示例#1
0
        /// <summary>
        /// Replaces all instructions between the last AND and the RET immediately after
        /// it with the specified method call. It should return bool and has the bits5 of A
        /// and B on the stack (in the OR case it also has the values from the previous
        /// bit compares).
        /// </summary>
        /// <param name="method">The method to transpile.</param>
        /// <param name="replacement">The method to call instead.</param>
        /// <returns>true if the method was transpiled, or false if the references were not
        /// found.</returns>
        private static bool ReplaceAnd(List <CodeInstruction> method, MethodBase replacement)
        {
            int  n          = method.Count;
            bool transpiled = false;

            for (int i = n - 1; i > 0; i--)
            {
                var instr = method[i];
                // LAST and
                if (instr.opcode == OpCodes.And)
                {
                    int and = ++i;
                    // Find the next RET or branch after it
                    for (; i < n && !transpiled; i++)
                    {
                        var ni     = method[i];
                        var opcode = ni.opcode;
                        // Ignore branches that branch to the immediately following statement
                        if (opcode == OpCodes.Ret || ((opcode == OpCodes.Br || opcode ==
                                                       OpCodes.Br_S) && (!(ni.operand is Label lbl) || i >= n ||
                                                                         !method[i + 1].labels.Contains(lbl))))
                        {
                            // Replace the AND with static tag compare
                            instr.opcode  = OpCodes.Call;
                            instr.operand = replacement;
                            // Remove everything in between
                            if (i > and)
                            {
                                method.RemoveRange(and, i - and);
                            }
                            transpiled = true;
                        }
                    }
                    break;
                }
            }
            if (!transpiled)
            {
                PUtil.LogWarning("Unable to transpile method {0}".F(replacement.Name));
            }
            return(transpiled);
        }
示例#2
0
        /// <summary>
        /// Creates an options entry if an attribute is a valid IOptionSpec or
        /// DynamicOptionAttribute.
        /// </summary>
        /// <param name="attribute">The attribute to parse.</param>
        /// <param name="prop">The property to inspect.</param>
        /// <param name="depth">The current depth of iteration to avoid infinite loops.</param>
        /// <returns>The OptionsEntry created from the attribute, or null if none was.</returns>
        private static IOptionsEntry TryCreateEntry(Attribute attribute, PropertyInfo prop,
                                                    int depth)
        {
            IOptionsEntry result = null;

            if (prop == null)
            {
                throw new ArgumentNullException(nameof(prop));
            }
            if (attribute is IOptionSpec spec)
            {
                if (string.IsNullOrEmpty(spec.Title))
                {
                    spec = HandleDefaults(spec, prop);
                }
                // Attempt to find a class that will represent it
                var type = prop.PropertyType;
                result = FindOptionClass(spec, prop);
                // See if it has entries that can themselves be added, ignore
                // value types and avoid infinite recursion
                if (result == null && !type.IsValueType && depth < 16 && type !=
                    prop.DeclaringType)
                {
                    result = CompositeOptionsEntry.Create(spec, prop, depth);
                }
            }
            else if (attribute is DynamicOptionAttribute doa &&
                     typeof(IOptionsEntry).IsAssignableFrom(doa.Handler))
            {
                try {
                    result = Activator.CreateInstance(doa.Handler) as IOptionsEntry;
                } catch (TargetInvocationException e) {
                    PUtil.LogError("Unable to create option handler for property " +
                                   prop.Name + ":");
                    PUtil.LogException(e.GetBaseException() ?? e);
                } catch (MissingMethodException) {
                    PUtil.LogWarning("Unable to create option handler for property " +
                                     prop.Name + ", it must have a public default constructor");
                }
            }
            return(result);
        }
示例#3
0
        /// <summary>
        /// Tries to move a Duplicant to a more sensible location when they are about to fall.
        /// </summary>
        /// <param name="instance">The fall monitor to update if successful.</param>
        /// <param name="navigator">The Duplicant to check.</param>
        /// <param name="layer">The location history of the Duplicant.</param>
        /// <returns>true if the Duplicant was successfully moved away from entombment, or
        /// false otherwise.</returns>
        private static bool TryEscapeFalling(LocationHistoryTransitionLayer layer,
                                             Navigator navigator, FallMonitor.Instance instance, ref bool flipEmote)
        {
            bool moved = false;

            for (int i = 0; i < LocationHistoryTransitionLayer.TRACK_CELLS; i++)
            {
                int last = layer.VisitedCells[i], above = Grid.CellAbove(last);
#if DEBUG
                PUtil.LogDebug("{0} is falling, trying to move to {1:D}".F(navigator.
                                                                           gameObject?.name, last));
#endif
                if (Grid.IsValidCell(last) && IsValidNavCell(navigator, last))
                {
                    ForceMoveTo(instance, last, navigator, ref flipEmote);
                    break;
                }
            }
            return(moved);
        }
示例#4
0
        /// <summary>
        /// Attempts to also patch the Decor Reimagined implementation of DecorProvider.
        /// Refresh.
        /// </summary>
        /// <param name="harmony">The Harmony instance to use for patching.</param>
        internal static void ApplyPatch(Harmony harmony)
        {
            var patchMethod = new HarmonyMethod(typeof(DecorProviderRefreshFix), nameof(
                                                    TranspileRefresh));
            var targetMethod = PPatchTools.GetTypeSafe(
                "ReimaginationTeam.DecorRework.DecorSplatNew", "DecorReimagined")?.
                               GetMethodSafe("RefreshDecor", false, PPatchTools.AnyArguments);

            if (targetMethod != null)
            {
                PUtil.LogDebug("Patching Decor Reimagined for DecorProvider.RefreshDecor");
                harmony.Patch(targetMethod, transpiler: patchMethod);
            }
            PUtil.LogDebug("Patching DecorProvider.Refresh");
            harmony.Patch(typeof(DecorProvider).GetMethodSafe(nameof(DecorProvider.Refresh),
                                                              false, PPatchTools.AnyArguments), transpiler: patchMethod);
            harmony.Patch(typeof(RoomProber), nameof(RoomProber.Sim1000ms), prefix:
                          new HarmonyMethod(typeof(DecorProviderRefreshFix), nameof(PrefixRoomProbe)));
            ROOMS_PENDING.Clear();
        }
            public static void OnLoad()
            {
                StartLogging();
                PUtil.InitLibrary(false);
                PUtil.RegisterPostload(CompatabilityPatches.DoPatches);
                POptions.RegisterOptions(typeof(CarbonOption));

                Traverse.Create <OilFloaterConfig>().Field <float>("KG_ORE_EATEN_PER_CYCLE").Value         = 40f;
                Traverse.Create <OilFloaterConfig>().Field <float>("CALORIES_PER_KG_OF_ORE").Value         = OilFloaterTuning.STANDARD_CALORIES_PER_CYCLE / 40f;
                Traverse.Create <OilFloaterHighTempConfig>().Field <float>("KG_ORE_EATEN_PER_CYCLE").Value = 40f;
                Traverse.Create <OilFloaterHighTempConfig>().Field <float>("CALORIES_PER_KG_OF_ORE").Value = OilFloaterTuning.STANDARD_CALORIES_PER_CYCLE / 40f;

                // Add Coalplant crop type
                TUNING.CROPS.CROP_TYPES.Add(
                    new Crop.CropVal("Carbon", CoalPlantConfig.LIFECYCLE, (int)CoalPlantConfig.COAL_PRODUCED));
                var RESONANT_NUM_SEEDS = ResonantPlantConfig.COAL_PRODUCED_TOTAL / ResonantPlantConfig.COAL_PER_SEED;

                TUNING.CROPS.CROP_TYPES.Add(
                    new Crop.CropVal(ResonantPlantConfig.SEED_ID, ResonantPlantConfig.LIFECYCLE, (int)RESONANT_NUM_SEEDS));
            }
示例#6
0
        /// <summary>
        /// Tries to move a Duplicant to a more sensible location when entombed or falling.
        /// </summary>
        /// <param name="layer">The location history of the Duplicant.</param>
        /// <param name="navigator">The Duplicant to check.</param>
        /// <param name="instance">The fall monitor to update if successful.</param>
        /// <returns>true if the Duplicant was successfully moved away, or false otherwise.</returns>
        private static bool TryEscape(LocationHistoryTransitionLayer layer,
                                      Navigator navigator, FallMonitor.Instance instance, ref bool flipEmote)
        {
            bool moved = false;

            for (int i = 0; i < LocationHistoryTransitionLayer.TRACK_CELLS; i++)
            {
                int last = layer.VisitedCells[i];
                if (Grid.IsValidCell(last) && IsValidNavCell(navigator, last))
                {
                    PUtil.LogDebug("{0} is in trouble, trying to escape to {1:D}".F(navigator.
                                                                                    gameObject?.name, last));
                    ForceMoveTo(instance, last, navigator, ref flipEmote);
                    // Prevents a loop back and forth between two cells in the history
                    layer.Reset();
                    break;
                }
            }
            return(moved);
        }
        /// <summary>
        /// Transpiles Render to disable the actual Graphics.DrawMesh call.
        /// </summary>
        internal static TranspiledMethod Transpiler(TranspiledMethod instructions)
        {
            var drawMesh = typeof(Graphics).GetMethodSafe(nameof(Graphics.DrawMesh),
                                                          true, typeof(Mesh), typeof(Vector3), typeof(Quaternion), typeof(Material),
                                                          typeof(int), typeof(Camera), typeof(int), typeof(MaterialPropertyBlock));
            var newMethod = instructions;

            if (drawMesh != null)
            {
                newMethod = PPatchTools.RemoveMethodCall(instructions, drawMesh);
            }
            else
            {
                PUtil.LogWarning("Unable to patch FallingWater.Render");
            }
            foreach (var instr in newMethod)
            {
                yield return(instr);
            }
        }
示例#8
0
        /// <summary>
        /// Avoids stacking up queues by waiting for the async path probe. Game updates almost
        /// all handlers that use pathfinding (including BrainScheduler) in a LateUpdate call,
        /// so ensure it ends in time.
        /// </summary>
        internal void EndJob()
        {
            var jobManager = AsyncJobManager.Instance;

            if (jobManager != null && running)
            {
                var now = Stopwatch.StartNew();
                if (onPathDone.WaitAndMeasure(FastTrackMod.MAX_TIMEOUT))
                {
                    Metrics.DebugMetrics.LogPathProbe(now.ElapsedTicks, totalRuntime);
                }
                else
                {
                    PUtil.LogWarning("Path probing did not complete within the timeout!");
                }
                jobCount     = 0;
                totalRuntime = 0L;
                running      = false;
            }
        }
示例#9
0
        /// <summary>
        /// Pan the camera's view from its current matrix when the activity starts to a new
        /// matrix so that the view bounds will contain (if possible, intersect if not
        /// possible) the new bounds in the camera layers' coordinate system.
        /// </summary>
        /// <param name="panToBounds">The bounds to pan the view to.</param>
        /// <param name="duration">The amount of time that the animation should take.</param>
        /// <returns>
        /// The newly scheduled activity, if the duration is greater than 0; else null.
        /// </returns>
        /// <remarks>
        /// If the duration is 0 then the view will be transformed immediately, and null will
        /// be returned. Else a new PTransformActivity will get returned that is set to
        /// animate the camera’s view matrix to the new bounds.
        /// </remarks>
        public virtual PTransformActivity AnimateViewToPanToBounds(RectangleF panToBounds, long duration)
        {
            SizeF delta = PUtil.DeltaRequiredToContain(ViewBounds, panToBounds);

            if (delta.Width != 0 || delta.Height != 0)
            {
                if (duration == 0)
                {
                    TranslateViewBy(-delta.Width, -delta.Height);
                }
                else
                {
                    PMatrix m = ViewMatrix;
                    m.TranslateBy(-delta.Width, -delta.Height);
                    return(AnimateViewToMatrix(m, duration));
                }
            }

            return(null);
        }
示例#10
0
        /// <summary>
        /// Checks for compatibility and applies fast fetch manager updates only if Efficient
        /// Supply is not enabled.
        /// </summary>
        /// <param name="harmony">The Harmony instance to use for patching.</param>
        private static void CheckFetchCompat(Harmony harmony)
        {
            if (PPatchTools.GetTypeSafe("PeterHan.EfficientFetch.EfficientFetchManager") ==
                null)
            {
                PathPatches.AsyncBrainGroupUpdater.AllowFastListSwap = true;
                harmony.Patch(typeof(FetchManager.FetchablesByPrefabId),
                              nameof(FetchManager.FetchablesByPrefabId.UpdatePickups),
                              prefix: new HarmonyMethod(typeof(GamePatches.FetchManagerFastUpdate),
                                                        nameof(GamePatches.FetchManagerFastUpdate.BeforeUpdatePickups)));
#if DEBUG
                PUtil.LogDebug("Patched FetchManager for fast pickup updates");
#endif
            }
            else
            {
                PUtil.LogWarning("Disabling fast pickup updates: Efficient Supply active");
                PathPatches.AsyncBrainGroupUpdater.AllowFastListSwap = false;
            }
        }
示例#11
0
        /// <summary>
        /// Transpiles AddTechItem to remove an Add call that duplicates every item, as it was
        /// already added to the constructor.
        /// </summary>
        internal static TranspiledMethod Transpiler(TranspiledMethod instructions)
        {
            var target = typeof(ResourceSet <TechItem>).GetMethodSafe(nameof(
                                                                          ResourceSet <TechItem> .Add), false, typeof(TechItem));

            foreach (var instr in instructions)
            {
                if (target != null && instr.Is(OpCodes.Callvirt, target))
                {
                    // Original method was 1 arg to 1 arg and result was ignored, so just rip
                    // it out
                    instr.opcode  = OpCodes.Nop;
                    instr.operand = null;
#if DEBUG
                    PUtil.LogDebug("Patched TechItems.AddTechItem");
#endif
                }
                yield return(instr);
            }
        }
示例#12
0
        public override void Process(uint when, object _)
        {
            if (patches.TryGetValue(when, out PrivateRunList atTime) && atTime != null &&
                atTime.Count > 0)
            {
                string stage = RunAt.ToString(when);
#if DEBUG
                PRegistry.LogPatchDebug("Executing {0:D} handler(s) from {1} for stage {2}".F(
                                            atTime.Count, Assembly.GetExecutingAssembly().GetNameSafe() ?? "?", stage));
#endif
                foreach (var patch in atTime)
                {
                    try {
                        patch.Run(harmony);
                    } catch (TargetInvocationException e) {
                        // Use the inner exception
                        PUtil.LogError("Error running patches for stage " + stage + ":");
                        PUtil.LogException(e.GetBaseException());
                    }
                }
示例#13
0
 /// <summary>
 /// Writes a mod's settings to its configuration file.
 /// </summary>
 /// <param name="settings">The settings to write.</param>
 /// <param name="path">The path to the settings file.</param>
 /// <param name="indent">true to indent the output, or false to leave it in one line.</param>
 internal static void WriteSettings(object settings, string path, bool indent = false)
 {
     if (settings != null)
     {
         try {
             // SharedConfigLocation
             Directory.CreateDirectory(Path.GetDirectoryName(path));
             using (var jw = new JsonTextWriter(File.CreateText(path))) {
                 var serializer = new JsonSerializer {
                     MaxDepth = MAX_SERIALIZATION_DEPTH
                 };
                 serializer.Formatting = indent ? Formatting.Indented : Formatting.None;
                 // Serialize from stream avoids creating file text in memory
                 serializer.Serialize(jw, settings);
             }
         } catch (UnauthorizedAccessException e) {
             // Options cannot be set
             PUtil.LogExcWarn(e);
         }
     }
示例#14
0
        /// <summary>
        /// Create a new dynamic options entry with no backing property. All handling of this
        /// entry must be done by the user options class.
        /// </summary>
        /// <param name="handler">The handler for that option.</param>
        /// <returns>A dynamic options entry for that type.</returns>
        internal static DynamicOptionsEntry Create(object handler)
        {
            string category = null;

            if (handler != null)
            {
                // Retrieve the category from the user handler
                var getter = handler.GetType().GetPropertySafe <string>(nameof(IDynamicOption.
                                                                               Category), false)?.GetGetMethod();
                if (getter != null)
                {
                    try {
                        category = getter.Invoke(handler, null)?.ToString();
                    } catch (TargetInvocationException e) {
                        PUtil.LogExcWarn(e);
                    }
                }
            }
            return(new DynamicOptionsEntry("Dynamic", category, handler));
        }
示例#15
0
        public override void OnLoad(Harmony harmony)
        {
            base.OnLoad(harmony);
            PUtil.InitLibrary();
            new PPatchManager(harmony).RegisterPatchClass(typeof(PipPlantOverlayPatches));
            LocString.CreateLocStringKeys(typeof(PipPlantOverlayStrings.INPUT_BINDINGS));
            PipPlantOverlayTests.SymmetricalRadius = false;
            OpenOverlay = new PActionManager().CreateAction(PipPlantOverlayStrings.
                                                            OVERLAY_ACTION, PipPlantOverlayStrings.INPUT_BINDINGS.ROOT.PIPPLANT);
            new PLocalization().Register();
            // If possible, make farming status items appear properly in pip plant mode
            var overlayBitsField = typeof(StatusItem).GetFieldSafe("overlayBitfieldMap", true);

            if (overlayBitsField != null && overlayBitsField.GetValue(null) is
                IDictionary <HashedString, StatusItemOverlays> overlayBits)
            {
                overlayBits.Add(PipPlantOverlay.ID, StatusItemOverlays.Farming);
            }
            new PVersionCheck().Register(this, new SteamVersionChecker());
        }
示例#16
0
文件: Mod.cs 项目: romen-h/ONI-Mods
        public override void OnLoad(Harmony harmony)
        {
            PUtil.InitLibrary();

            Options = new POptions();

            Settings = POptions.ReadSettings <ModSettings>();
            if (Settings == null)
            {
                Settings = new ModSettings();
                POptions.WriteSettings(Settings);
            }
            Options.RegisterOptions(this, typeof(ModSettings));

            ModAssets.LoadAssets();

            Registry = RomenHRegistry.Init();

            base.OnLoad(harmony);
        }
示例#17
0
        /// <summary>
        /// Copies data from the file system and source zip file to the new combined backup
        /// zip file.
        /// </summary>
        /// <param name="src">The source mod data with no configs.</param>
        /// <param name="dst">The temporary file destination.</param>
        /// <param name="toCopy">The files to copy from the source.</param>
        /// <param name="toAdd">The files to add from the current mod directory.</param>
        private void CopyFiles(ZipFile src, ZipFile dst, ISet <string> toCopy,
                               IDictionary <string, string> toAdd)
        {
            foreach (var entry in src)
            {
#if DEBUG
                PUtil.LogDebug("ConfigBackupUtility add existing file " + entry.FileName);
#endif
                dst.AddEntry(entry.FileName, (name, stream) => src[name].Extract(stream));
            }
            foreach (var pair in toAdd)
            {
                // Avoid capturing the wrong pair value
                string target = pair.Value, entryName = pair.Key;
                if (!dst.ContainsEntry(entryName))
                {
                    dst.AddEntry(entryName, (name, os) => CopyFromModFolder(name, target, os));
                }
            }
        }
示例#18
0
        // misc bookkeeping
        public static void OnLoad()
        {
            StartLogging();

            AddDiseaseName(SlimeLethalSickness.ID, DUPLICANTS.DISEASES.SLIMESICKNESS.NAME +
                           " (lethal)");
            AddDiseaseName(SlimeCoughSickness.ID, DUPLICANTS.DISEASES.SLIMESICKNESS.NAME +
                           " (cough)");
            AddDiseaseName(FoodPoisonVomiting.ID, DUPLICANTS.DISEASES.FOODSICKNESS.NAME +
                           " (vomiting)");

            SkipNotifications.Skip(SlimeLethalSickness.ID);
            SkipNotifications.Skip(SlimeCoughSickness.ID);
            SkipNotifications.Skip(FoodPoisonVomiting.ID);

            ImaginationLoader.Init(typeof(DiseasesPatch));
            PUtil.RegisterPostload(CompatPatch.CompatPatches);
            BuildingsPatch.uvlight = PLightShape.Register("SkyLib.LightShape.FixedSemi",
                                                          BuildingsPatch.SemicircleLight);
        }
示例#19
0
        public static void OnLoad(string path)
        {
            PUtil.InitLibrary();
            POptions.RegisterOptions(typeof(ModUpdateInfo));
            LocString.CreateLocStringKeys(typeof(ModUpdateDateStrings.UI));
            PLocalization.Register();
            // Try to read the backup config first
            string backupPath = ExtensionMethods.BackupConfigPath;

            if (File.Exists(backupPath))
            {
                try {
                    // Copy and overwrite our config if possible
                    File.Copy(backupPath, ExtensionMethods.ConfigPath, true);
                    File.Delete(backupPath);
                    PUtil.LogDebug("Restored configuration settings after self-update");
                } catch (IOException) {
                    PUtil.LogWarning("Unable to restore configuration for Mod Updater");
                }
            }
示例#20
0
        /// <summary>
        /// Creates a resource category header for critters.
        /// </summary>
        /// <param name="resList">The parent category screen for this header.</param>
        /// <param name="prefab">The prefab to use for creating the headers.</param>
        /// <param name="type">The critter type to create.</param>
        /// <returns>The heading for that critter type.</returns>
        public static ResourceCategoryHeader Create(ResourceCategoryScreen resList,
                                                    GameObject prefab, CritterType type)
        {
            var    tag     = GameTags.BagableCreature;
            string typeStr = type.GetDescription();

            // Create a heading for Critter (Type)
            PUtil.LogDebug("Creating Critter ({0}) category".F(typeStr));
            var gameObject = Util.KInstantiateUI(prefab, resList.CategoryContainer.gameObject,
                                                 false);

            gameObject.name = "CategoryHeader_{0}_{1}".F(tag.Name, type.ToString());
            var header = gameObject.GetComponent <ResourceCategoryHeader>();

            header.SetTag(tag, GameUtil.MeasureUnit.quantity);
            // Tag it with a wild/tame tag
            header.gameObject.AddComponent <CritterResourceInfo>().CritterType = type;
            header.elements.LabelText.SetText("{0} ({1})".F(tag.ProperName(), typeStr));
            return(header);
        }
示例#21
0
        /// <summary>
        /// Common transpiled target method for each use of PopFXManager.SpawnFX.
        /// </summary>
        private static PopFX SpawnFXShort(PopFXManager instance, Sprite icon, string text,
                                          Transform targetTransform, float lifetime, bool track_target, object source)
        {
            PopFX popup = null;
            bool  show  = true;

            try {
                // Parameter count cannot be reduced - in order to conform with Klei method
                show = ToastControlPopups.ShowPopup(source, text);
            } catch (Exception e) {
                // Sometimes this gets executed on a background thread and unhandled exceptions
                // cause a CTD
                PUtil.LogException(e);
            }
            if (show)
            {
                popup = instance.SpawnFX(icon, text, targetTransform, lifetime, track_target);
            }
            return(popup);
        }
示例#22
0
        /// <summary>
        /// Toggles empty storage on the object.
        /// </summary>
        /// <param name="cell">The cell this building occupies.</param>
        /// <param name="item">The item to toggle.</param>
        /// <param name="enable">true to schedule for emptying, or false to disable it.</param>
        /// <returns>true if changes were made, or false otherwise.</returns>
        private bool ToggleEmptyStorage(int cell, GameObject item, bool enable)
        {
            var  daw     = item.GetComponentSafe <DropAllWorkable>();
            bool changed = false;

            if (daw != null)
            {
                if ((Traverse.Create(daw).GetField <Chore>("chore") != null) != enable)
                {
                    daw.DropAll();
#if DEBUG
                    var xy = Grid.CellToXY(cell);
                    PUtil.LogDebug("Empty storage {3} @({0:D},{1:D}) = {2}".F(xy.X, xy.Y,
                                                                              enable, item.GetProperName()));
#endif
                    changed = true;
                }
            }
            return(changed);
        }
示例#23
0
        /// <summary>
        /// Loads all codex entries for all mods registered.
        /// </summary>
        /// <param name="lockKey">Key for shared data lock.</param>
        /// <param name="tableKey">Key for shared data table.</param>
        /// <param name="category">The codex category under which these data entries should be loaded.</param>
        /// <returns>The list of entries that were loaded.</returns>
        private static IList <CodexEntry> LoadEntries(string lockKey, string tableKey,
                                                      string category)
        {
            var entries = new List <CodexEntry>(32);

            lock (PSharedData.GetLock(lockKey)) {
                var table = PSharedData.GetData <IList <string> >(tableKey);
                if (table != null)
                {
                    foreach (string dir in table)
                    {
#if DEBUG
                        PUtil.LogDebug("Loaded codex entries from directory: {0}".F(dir));
#endif
                        LoadFromDirectory(entries, dir, category);
                    }
                }
            }
            return(entries);
        }
示例#24
0
            /// <summary>
            /// Applied before UpdatePickups runs.
            /// </summary>
            internal static bool Prefix(FetchManager.FetchablesByPrefabId __instance,
                                        Navigator worker_navigator, GameObject worker_go,
                                        Dictionary <int, int> ___cellCosts)
            {
                var  inst = EfficientFetchManager.Instance;
                bool cont = true;

                if (inst != null && options.MinimumAmountPercent > 0)
                {
                    try {
                        inst.UpdatePickups(__instance, worker_navigator, worker_go,
                                           ___cellCosts);
                        cont = false;
                    } catch (Exception e) {
                        // Crashing will bring down simdll with no stack trace
                        PUtil.LogException(e);
                    }
                }
                return(cont);
            }
示例#25
0
        public static void OnLoad(string path)
        {
            PUtil.InitLibrary();
            if (DebugNotIncludedOptions.Instance?.DetailedBacktrace ?? true)
            {
                DebugLogger.InstallExceptionLogger();
            }
            POptions.RegisterOptions(typeof(DebugNotIncludedOptions));
            if (DebugNotIncludedOptions.Instance?.LogAsserts ?? true)
            {
                LogAllFailedAsserts();
            }
            // Patch the exception logger for state machines
            var logException = typeof(DebugUtil).GetMethodSafe("LogException", true,
                                                               PPatchTools.AnyArguments);

            if (logException != null)
            {
                ModDebugRegistry.Instance.DebugInstance.Patch(logException, prefix:
                                                              new HarmonyMethod(typeof(DebugLogger), nameof(DebugLogger.LogException)));
            }
            foreach (var mod in Global.Instance.modManager?.mods)
            {
                if (mod.label.install_path == path)
                {
                    ThisMod = mod;
                    break;
                }
            }
            // Default UI debug key is ALT+U
            UIDebugAction = PAction.Register("DebugNotIncluded.UIDebugAction",
                                             DebugNotIncludedStrings.KEY_SNAPSHOT, new PKeyBinding(KKeyCode.U,
                                                                                                   Modifier.Alt));
            if (ThisMod == null)
            {
                DebugLogger.LogWarning("Unable to determine KMod instance!");
            }
            // Must postload the mods dialog to come out after aki's mods, ony's mods, PLib
            // options, and so forth
            PUtil.RegisterPostload(PostloadHandler);
        }
        /// <summary>
        /// Overridden.  Do auto-panning even when the mouse is not moving.
        /// </summary>
        /// <param name="sender">The source of the drag event.</param>
        /// <param name="e">A PInputEventArgs that contains the event data.</param>
        protected override void OnDragActivityStep(object sender, PInputEventArgs e)
        {
            base.OnDragActivityStep(sender, e);

            if (!autopan)
            {
                return;
            }

            PCamera     c = e.Camera;
            RectangleFx b = c.Bounds;
            PointFx     l = e.GetPositionRelativeTo(c);

            PUtil.OutCode outcode = PUtil.RectangleOutCode(l, b);
            SizeFx        delta   = SizeFx.Empty;

            if ((outcode & PUtil.OutCode.Top) != 0)
            {
                delta.Height = ValidatePanningDelta(-1.0f - (0.5f * Math.Abs(l.Y - b.Y)));
            }
            else if ((outcode & PUtil.OutCode.Bottom) != 0)
            {
                delta.Height = ValidatePanningDelta(1.0f + (0.5f * Math.Abs(l.Y - (b.Y + b.Height))));
            }

            if ((outcode & PUtil.OutCode.Right) != 0)
            {
                delta.Width = ValidatePanningDelta(1.0f + (0.5f * Math.Abs(l.X - (b.X + b.Width))));
            }
            else if ((outcode & PUtil.OutCode.Left) != 0)
            {
                delta.Width = ValidatePanningDelta(-1.0f - (0.5f * Math.Abs(l.X - b.X)));
            }

            delta = c.LocalToView(delta);

            if (delta.Width != 0 || delta.Height != 0)
            {
                c.TranslateViewBy(delta.Width, delta.Height);
            }
        }
示例#27
0
        /// <summary>
        /// Transpiles OnPrefabInit to rotate the offset table before building it.
        /// </summary>
        internal static TranspiledMethod Transpiler(TranspiledMethod method,
                                                    MethodBase __originalMethod)
        {
            var target = typeof(OffsetGroups).GetMethodSafe(nameof(OffsetGroups.
                                                                   BuildReachabilityTable), true, typeof(CellOffset[]),
                                                            typeof(CellOffset[][]), typeof(CellOffset[]));
            var replacement = typeof(StockBugsPatches).GetMethodSafe(nameof(StockBugsPatches.
                                                                            RotateAndBuild), true, typeof(CellOffset[]), typeof(CellOffset[][]),
                                                                     typeof(CellOffset[]), typeof(KMonoBehaviour));
            bool   patched = false;
            string sig     = __originalMethod.DeclaringType.FullName + "." + __originalMethod.Name;

            if (target != null && replacement != null)
            {
                foreach (var instr in method)
                {
                    if (instr.Is(OpCodes.Call, target))
                    {
                        yield return(new CodeInstruction(OpCodes.Ldarg_0));

                        instr.operand = replacement;
#if DEBUG
                        PUtil.LogDebug("Patched " + sig);
#endif
                        patched = true;
                    }
                    yield return(instr);
                }
            }
            else
            {
                foreach (var instr in method)
                {
                    yield return(instr);
                }
            }
            if (!patched)
            {
                PUtil.LogWarning("Unable to patch " + sig);
            }
        }
示例#28
0
        /// <summary>
        /// Transpiles StopAnim to throw away the event handle properly.
        /// </summary>
        internal static TranspiledMethod Transpiler(TranspiledMethod instructions)
        {
            MethodInfo target = null, setData = null;

            target = typeof(KCompactedVector <IndirectionData>).GetMethodSafe(nameof(
                                                                                  KCompactedVector <IndirectionData> .Free), false, typeof(HandleVector <int> .
                                                                                                                                           Handle));
            setData = typeof(KCompactedVector <IndirectionData>).GetMethodSafe(nameof(
                                                                                   KCompactedVector <IndirectionData> .SetData), false, typeof(HandleVector <int> .
                                                                                                                                               Handle), typeof(IndirectionData));
            if (target != null && setData != null)
            {
                foreach (var instr in instructions)
                {
                    if (instr.Is(OpCodes.Callvirt, setData))
                    {
                        // Remove "data"
                        yield return(new CodeInstruction(OpCodes.Pop));

                        // Call Free
                        yield return(new CodeInstruction(OpCodes.Callvirt, target));

                        // Pop the retval
                        instr.opcode  = OpCodes.Pop;
                        instr.operand = null;
#if DEBUG
                        PUtil.LogDebug("Patched AnimEventManager.StopAnim");
#endif
                    }
                    yield return(instr);
                }
            }
            else
            {
                PUtil.LogWarning("Unable to patch AnimEventManager.StopAnim");
                foreach (var instr in instructions)
                {
                    yield return(instr);
                }
            }
        }
示例#29
0
        /// <summary>
        /// Invoked when the manual config button is pressed.
        /// </summary>
        private void OnManualConfig(GameObject _)
        {
            string uri = null;

            try {
                uri = new Uri(Path.GetDirectoryName(path)).AbsoluteUri;
            } catch (UriFormatException e) {
                PUtil.LogWarning("Unable to convert parent of " + path + " to a URI:");
                PUtil.LogExcWarn(e);
            }
            if (!string.IsNullOrEmpty(uri))
            {
                // Open the config folder, opening the file itself might start an unknown
                // editor which could execute the json somehow...
                WriteOptions();
                CloseDialog();
                PUtil.LogDebug("Opening config folder: " + uri);
                Application.OpenURL(uri);
                CheckForRestart();
            }
        }
示例#30
0
            /// <summary>
            /// Applied after OnButcherComplete runs.
            /// </summary>
            internal static void Postfix(Butcherable __instance)
            {
                var  obj     = __instance.gameObject;
                bool natural = false;

                if (obj != null)
                {
                    var smi = obj.GetSMI <AgeMonitor.Instance>();
                    if (smi != null)
                    {
                        natural = smi.CyclesUntilDeath < (1.0f / Constants.SECONDS_PER_CYCLE);
                    }
#if DEBUG
                    PUtil.LogDebug("Critter died: " + (natural ? "old age" : "prematurely"));
#endif
                    if (!natural)
                    {
                        AchievementStateComponent.OnCritterKilled();
                    }
                }
            }