diff --git a/.gitignore b/.gitignore index f652b45..5bf56e8 100644 --- a/.gitignore +++ b/.gitignore @@ -297,3 +297,4 @@ __pycache__/ *.btm.cs *.odx.cs *.xsd.cs +/Assemblies/0Harmony.xml diff --git a/Assemblies/0Harmony.dll b/1.0/Assemblies/0Harmony.dll similarity index 100% rename from Assemblies/0Harmony.dll rename to 1.0/Assemblies/0Harmony.dll diff --git a/Assemblies/IHoldMultipleThings.dll b/1.0/Assemblies/IHoldMultipleThings.dll similarity index 100% rename from Assemblies/IHoldMultipleThings.dll rename to 1.0/Assemblies/IHoldMultipleThings.dll diff --git a/Assemblies/PickUpAndHaul.dll b/1.0/Assemblies/PickUpAndHaul.dll similarity index 100% rename from Assemblies/PickUpAndHaul.dll rename to 1.0/Assemblies/PickUpAndHaul.dll diff --git a/1.0/Assemblies/THIS IS FOR v1.0 ONLY.txt b/1.0/Assemblies/THIS IS FOR v1.0 ONLY.txt new file mode 100644 index 0000000..e69de29 diff --git a/1.1/Assemblies/0Harmony.dll b/1.1/Assemblies/0Harmony.dll new file mode 100644 index 0000000..106a81a Binary files /dev/null and b/1.1/Assemblies/0Harmony.dll differ diff --git a/1.1/Assemblies/0Harmony.xml b/1.1/Assemblies/0Harmony.xml new file mode 100644 index 0000000..a17cc8f --- /dev/null +++ b/1.1/Assemblies/0Harmony.xml @@ -0,0 +1,2476 @@ + + + + 0Harmony + + + + A factory to create delegate types + + + Default constructor + + + Creates a delegate type for a method + The method + The new delegate type + + + + A getter delegate type + Type that getter gets field/property value from + Type of the value that getter gets + The instance get getter uses + An delegate + + + + A setter delegate type + Type that setter sets field/property value for + Type of the value that setter sets + The instance the setter uses + The value the setter uses + An delegate + + + + A constructor delegate type + Type that constructor creates + An delegate + + + + A helper class for fast access to getters and setters + + + Creates an instantiation delegate + Type that constructor creates + The new instantiation delegate + + + + Creates an getter delegate for a property + Type that getter reads property from + Type of the property that gets accessed + The property + The new getter delegate + + + + Creates an getter delegate for a field + Type that getter reads field from + Type of the field that gets accessed + The field + The new getter delegate + + + + Creates an getter delegate for a field (with a list of possible field names) + Type that getter reads field/property from + Type of the field/property that gets accessed + A list of possible field names + The new getter delegate + + + + Creates an setter delegate + Type that setter assigns property value to + Type of the property that gets assigned + The property + The new setter delegate + + + + Creates an setter delegate for a field + Type that setter assigns field value to + Type of the field that gets assigned + The field + The new getter delegate + + + + A delegate to invoke a method + The instance + The method parameters + The method result + + + A helper class to invoke method with delegates + + + Creates a fast invocation handler from a method + The method to invoke + Controls if boxed value object is accessed/updated directly + The + + + The directBoxValueAccess option controls how value types passed by reference (e.g. ref int, out my_struct) are handled in the arguments array + passed to the fast invocation handler. + Since the arguments array is an object array, any value types contained within it are actually references to a boxed value object. + Like any other object, there can be other references to such boxed value objects, other than the reference within the arguments array. + For example, + + var val = 5; + var box = (object)val; + var arr = new object[] { box }; + handler(arr); // for a method with parameter signature: ref/out/in int + + + + + If directBoxValueAccess is true, the boxed value object is accessed (and potentially updated) directly when the handler is called, + such that all references to the boxed object reflect the potentially updated value. + In the above example, if the method associated with the handler updates the passed (boxed) value to 10, both box and arr[0] + now reflect the value 10. Note that the original val is not updated, since boxing always copies the value into the new boxed value object. + + + If directBoxValueAccess is false (default), the boxed value object in the arguments array is replaced with a "reboxed" value object, + such that potential updates to the value are reflected only in the arguments array. + In the above example, if the method associated with the handler updates the passed (boxed) value to 10, only arr[0] now reflects the value 10. + + + + + A low level memory helper + + + Mark method for no inlining (currently only works on Mono) + The method/constructor to change + + + Detours a method + The original method/constructor + The replacement method/constructor + An error string + + + + Writes a jump to memory + The memory address + Jump destination + An error string + + + + Gets the start of a method in memory + The method/constructor + [out] Details of the exception + The method start address + + + + special parameter names that can be used in prefix and postfix methods + + + Patch function helpers + + + Adds a prefix + The patch info + The owner (Harmony ID) + The annotation info + + + + Removes a prefix + The patch info + The owner (Harmony ID) + + + + Adds a postfix + The patch info + The owner (Harmony ID) + The annotation info + + + + Removes a postfix + The patch info + The owner (Harmony ID) + + + + Adds a transpiler + The patch info + The owner (Harmony ID) + The annotation info + + + + Removes a transpiler + The patch info + The owner (Harmony ID) + + + + Adds a finalizer + The patch info + The owner (Harmony ID) + The annotation info + + + + Removes a finalizer + The patch info + The owner (Harmony ID) + + + + Removes a patch method + The patch info + The patch method + + + + Gets sorted patch methods + The original method + Patches to sort + Use debug mode + The sorted patch methods + + + + Creates new replacement method with the latest patches and detours the original method + The original method + Information describing the patches + The newly created replacement method + + + + Creates a patch sorter + Array of patches that will be sorted + Use debugging + + + Sorts internal PatchSortingWrapper collection and caches the results. + After first run the result is provided from the cache. + The original method + The sorted patch methods + + + Checks if the sorter was created with the same patch list and as a result can be reused to + get the sorted order of the patches. + List of patches to check against + true if equal + + + Removes one unresolved dependency from the least important patch. + + + Outputs all unblocked patches from the waiting list to results list + + + Adds patch to both results list and handled patches set + Patch to add + + + Wrapper used over the Patch object to allow faster dependency access and + dependency removal in case of cyclic dependencies + + + Create patch wrapper object used for sorting + Patch to wrap + + + Determines how patches sort + The other patch + integer to define sort order (-1, 0, 1) + + + Determines whether patches are equal + The other patch + true if equal + + + Hash function + A hash code + + + Bidirectionally registers Patches as after dependencies + List of dependencies to register + + + Bidirectionally registers Patches as before dependencies + List of dependencies to register + + + Bidirectionally removes Patch from after dependencies + Patch to remove + + + Bidirectionally removes Patch from before dependencies + Patch to remove + + + Specifies the type of method + + + + This is a normal method + + + This is a getter + + + This is a setter + + + This is a constructor + + + This is a static constructor + + + Specifies the type of argument + + + + This is a normal argument + + + This is a reference argument (ref) + + + This is an out argument (out) + + + This is a pointer argument (&) + + + Specifies the type of patch + + + + Any patch + + + A prefix patch + + + A postfix patch + + + A transpiler + + + A finalizer + + + A reverse patch + + + Specifies the type of reverse patch + + + + Use the unmodified original method (directly from IL) + + + Use the original as it is right now including previous patches but excluding future ones + + + The base class for all Harmony annotations (not meant to be used directly) + + + + The common information for all attributes + + + Annotation to define your Harmony patch methods + + + + An empty annotation can be used together with TargetMethod(s) + + + + An annotation that specifies a class to patch + The declaring class/type + + + + An annotation that specifies a method, property or constructor to patch + The declaring class/type + The argument types of the method or constructor to patch + + + + An annotation that specifies a method, property or constructor to patch + The declaring class/type + The name of the method, property or constructor to patch + + + + An annotation that specifies a method, property or constructor to patch + The declaring class/type + The name of the method, property or constructor to patch + An array of argument types to target overloads + + + + An annotation that specifies a method, property or constructor to patch + The declaring class/type + The name of the method, property or constructor to patch + An array of argument types to target overloads + Array of + + + + An annotation that specifies a method, property or constructor to patch + The declaring class/type + The + + + + An annotation that specifies a method, property or constructor to patch + The declaring class/type + The + An array of argument types to target overloads + + + + An annotation that specifies a method, property or constructor to patch + The declaring class/type + The + An array of argument types to target overloads + Array of + + + + An annotation that specifies a method, property or constructor to patch + The declaring class/type + The name of the method, property or constructor to patch + The + + + + An annotation that specifies a method, property or constructor to patch + The name of the method, property or constructor to patch + + + + An annotation that specifies a method, property or constructor to patch + The name of the method, property or constructor to patch + An array of argument types to target overloads + + + + An annotation that specifies a method, property or constructor to patch + The name of the method, property or constructor to patch + An array of argument types to target overloads + An array of + + + + An annotation that specifies a method, property or constructor to patch + The name of the method, property or constructor to patch + The + + + + An annotation that specifies a method, property or constructor to patch + The + + + + An annotation that specifies a method, property or constructor to patch + The + An array of argument types to target overloads + + + + An annotation that specifies a method, property or constructor to patch + The + An array of argument types to target overloads + An array of + + + + An annotation that specifies a method, property or constructor to patch + An array of argument types to target overloads + + + + An annotation that specifies a method, property or constructor to patch + An array of argument types to target overloads + An array of + + + + Annotation to define your standin methods for reverse patching + + + + An annotation that specifies the type of reverse patching + The of the reverse patch + + + + A Harmony annotation to define that all methods in a class are to be patched + + + + A Harmony annotation + + + + A Harmony annotation to define patch priority + The priority + + + + A Harmony annotation + + + + A Harmony annotation to define that a patch comes before another patch + The array of harmony IDs of the other patches + + + + A Harmony annotation + + + A Harmony annotation to define that a patch comes after another patch + The array of harmony IDs of the other patches + + + + A Harmony annotation + + + A Harmony annotation to debug a patch (output uses to log to your Desktop) + + + + Specifies the Prepare function in a patch class + + + + Specifies the Cleanup function in a patch class + + + + Specifies the TargetMethod function in a patch class + + + + Specifies the TargetMethods function in a patch class + + + + Specifies the Prefix function in a patch class + + + + Specifies the Postfix function in a patch class + + + + Specifies the Transpiler function in a patch class + + + + Specifies the Finalizer function in a patch class + + + + A Harmony annotation + + + + The name of the original argument + + + + The index of the original argument + + + + The new name of the original argument + + + + An annotation to declare injected arguments by name + + + + An annotation to declare injected arguments by index + Zero-based index + + + + An annotation to declare injected arguments by renaming them + Name of the original argument + New name + + + + An annotation to declare injected arguments by index and renaming them + Zero-based index + New name + + + + An abstract wrapper around OpCode and their operands. Used by transpilers + + + + The opcode + + + + The operand + + + + All labels defined on this instruction + + + + All exception block boundaries defined on this instruction + + + + Creates a new CodeInstruction with a given opcode and optional operand + The opcode + The operand + + + + Create a full copy (including labels and exception blocks) of a CodeInstruction + The to copy + + + + Clones a CodeInstruction and resets its labels and exception blocks + A lightweight copy of this code instruction + + + + Clones a CodeInstruction, resets labels and exception blocks and sets its opcode + The opcode + A copy of this CodeInstruction with a new opcode + + + + Clones a CodeInstruction, resets labels and exception blocks and sets its operand + The operand + A copy of this CodeInstruction with a new operand + + + + Returns a string representation of the code instruction + A string representation of the code instruction + + + + Exception block types + + + + The beginning of an exception block + + + + The beginning of a catch block + + + + The beginning of an except filter block + + + + The beginning of a fault block + + + + The beginning of a finally block + + + + The end of an exception block + + + + An exception block + + + + Block type + + + + Catch type + + + + Creates an exception block + The + The catch type + + + + The Harmony instance is the main entry to Harmony. After creating one with an unique identifier, it is used to patch and query the current application domain + + + + The unique identifier + + + + Set to true before instantiating Harmony to debug Harmony or use an environment variable to set HARMONY_DEBUG to '1' like this: cmd /C "set HARMONY_DEBUG=1 && game.exe" + This is for full debugging. To debug only specific patches, use the attribute + + + + Creates a new Harmony instance + A unique identifier (you choose your own) + A Harmony instance + + + + Searches the current assembly for Harmony annotations and uses them to create patches + + + + Creates a empty patch processor for an original method + The original method/constructor + A new instance + + + + Creates a patch class processor from an annotated class + The class/type + A new instance + + + + Creates a reverse patcher for one of your stub methods + The original method/constructor + The stand-in stub method as + A new instance + + + + Searches an assembly for Harmony annotations and uses them to create patches + The assembly + + + + Creates patches by manually specifying the methods + The original method/constructor + An optional prefix method wrapped in a object + An optional postfix method wrapped in a object + An optional transpiler method wrapped in a object + An optional finalizer method wrapped in a object + The replacement method that was created to patch the original method + + + + Patches a foreign method onto a stub method of yours and optionally applies transpilers during the process + The original method/constructor you want to duplicate + Your stub method as that will become the original. Needs to have the correct signature (either original or whatever your transpilers generates) + An optional transpiler as method that will be applied during the process + The replacement method that was created to patch the stub method + + + + Unpatches methods + The optional Harmony ID to restrict unpatching to a specific instance + This method could be static if it wasn't for the fact that unpatching creates a new replacement method that contains your harmony ID + + + + Unpatches a method + The original method/constructor + The + The optional Harmony ID to restrict unpatching to a specific instance + + + + Unpatches a method + The original method/constructor + The patch method as method to remove + + + + Test for patches from a specific Harmony ID + The Harmony ID + True if patches for this ID exist + + + + Gets patch information for a given original method + The original method/constructor + The patch information as + + + + Gets the methods this instance has patched + An enumeration of original methods/constructors + + + + Gets all patched original methods in the appdomain + An enumeration of patched original methods/constructors + + + + Gets Harmony version for all active Harmony instances + [out] The current Harmony version + A dictionary containing assembly versions keyed by Harmony IDs + + + + Under Mono, HarmonyException wraps IL compile errors with detailed information about the failure + + + + Default serialization constructor (not implemented) + The info + The context + + + + Get a list of IL instructions in pairs of offset+code + A list of key/value pairs which represent an offset and the code at that offset + + + + Get a list of IL instructions without offsets + A list of + + + + Get the error offset of the errornous IL instruction + The offset + + + + Get the index of the errornous IL instruction + The index into the list of instructions or -1 if not found + + + + A wrapper around a method to use it as a patch (for example a Prefix) + + + + The original method + + + + Class/type declaring this patch + + + + Patch method name + + + + Optional patch + + + + Array of argument types of the patch method + + + + of the patch + + + + Install this patch before patches with these Harmony IDs + + + + Install this patch after patches with these Harmony IDs + + + + Reverse patch type, see + + + + Create debug output for this patch + + + + Default constructor + + + + Creates a patch from a given method + The original method + + + + Creates a patch from a given method + The original method + The patch + A list of harmony IDs that should come after this patch + A list of harmony IDs that should come before this patch + Set to true to generate debug output + + + + Creates a patch from a given method + The patch class/type + The patch method name + The optional argument types of the patch method (for overloaded methods) + + + + Gets the names of all internal patch info fields + A list of field names + + + + Merges annotations + The list of to merge + The merged + + + + Returns a string that represents the annotation + A string representation + + + + Annotation extensions + + + + Copies annotation information + The source + The destination + + + + Clones an annotation + The to clone + A copied + + + + Merges annotations + The master + The detail + A new, merged + + + + Gets all annotations on a class/type + The class/type + A list of all + + + + Gets merged annotations on a class/type + The class/type + The merged + + + + Gets all annotations on a method + The method/constructor + A list of + + + + Gets merged annotations on a method + The method/constructor + The merged + + + + + A mutable representation of an inline signature, similar to Mono.Cecil's CallSite. + Used by the calli instruction, can be used by transpilers + + + + + See + + + + See + + + + See + + + + The list of all parameter types or function pointer signatures received by the call site + + + + The return type or function pointer signature returned by the call site + + + + Returns a string representation of the inline signature + A string representation of the inline signature + + + + + A mutable representation of a parameter type with an attached type modifier, + similar to Mono.Cecil's OptionalModifierType / RequiredModifierType and C#'s modopt / modreq + + + + + Whether this is a modopt (optional modifier type) or a modreq (required modifier type) + + + + The modifier type attached to the parameter type + + + + The modified parameter type + + + + Returns a string representation of the modifier type + A string representation of the modifier type + + + + Patch serialization + + + + Control the binding of a serialized object to a type + Specifies the assembly name of the serialized object + Specifies the type name of the serialized object + The type of the object the formatter creates a new instance of + + + + Serializes a patch info + The + The serialized data + + + + Deserialize a patch info + The serialized data + A + + + + Compare function to sort patch priorities + The patch + Zero-based index + The priority + A standard sort integer (-1, 0, 1) + + + + Serializable patch information + + + + Prefixes as an array of + + + + Postfixes as an array of + + + + Transpilers as an array of + + + + Finalizers as an array of + + + + Default constructor + + + + Returns if any of the patches wants debugging turned on + + + + Adds a prefix + + The prefix method + An owner (Harmony ID) + The priority, see + A list of Harmony IDs for prefixes that should run after this prefix + A list of Harmony IDs for prefixes that should run before this prefix + A flag that will log the replacement method via every time this prefix is used to build the replacement, even in the future + + + + Removes prefixes + The owner of the prefix or * for any prefix + + + + Adds a postfix + The postfix method + An owner (Harmony ID) + The priority, see + A list of Harmony IDs for postfixes that should run after this postfix + A list of Harmony IDs for postfixes that should run before this postfix + A flag that will log the replacement method via every time this postfix is used to build the replacement, even in the future + + + + Removes postfixes + The owner of the postfix or * for any postfix + + + + Adds a transpiler + The transpiler method + An owner (Harmony ID) + The priority, see + A list of Harmony IDs for transpilers that should run after this transpiler + A list of Harmony IDs for transpilers that should run before this transpiler + A flag that will log the replacement method via every time this patch is used to build the replacement, even in the future + + + + Removes transpilers + The owner of the transpiler or * for any transpiler + + + + Adds a finalizer + The finalizer method + An owner (Harmony ID) + The priority, see + A list of Harmony IDs for finalizers that should run after this finalizer + A list of Harmony IDs for finalizers that should run before this finalizer + A flag that will log the replacement method via every time this patch is used to build the replacement, even in the future + + + + Removes finalizers + The owner of the finalizer or * for any finalizer + + + + Removes a patch using its method + The method of the patch to remove + + + + A serializable patch + + + + Zero-based index + + + + The owner (Harmony ID) + + + + The priority, see + + + + Keep this patch before the patches indicated in the list of Harmony IDs + + + + Keep this patch after the patches indicated in the list of Harmony IDs + + + + A flag that will log the replacement method via every time this patch is used to build the replacement, even in the future + + + + The method of the static patch method + + + + Creates a patch + The method of the patch + Zero-based index + An owner (Harmony ID) + The priority, see + A list of Harmony IDs for patches that should run after this patch + A list of Harmony IDs for patches that should run before this patch + A flag that will log the replacement method via every time this patch is used to build the replacement, even in the future + + + + Get the patch method or a DynamicMethod if original patch method is a patch factory + The original method/constructor + The method of the patch + + + + Determines whether patches are equal + The other patch + true if equal + + + + Determines how patches sort + The other patch + integer to define sort order (-1, 0, 1) + + + + Hash function + A hash code + + + + A PatchClassProcessor used to turn on a class/type into patches + + + + Creates an empty patch class processor + The Harmony instance + The class to process + + + + Applies the patches + A list of all created replacement methods or null if patch class is not annotated + + + + A group of patches + + + + A collection of prefix + + + + A collection of postfix + + + + A collection of transpiler + + + + A collection of finalizer + + + + Gets all owners (Harmony IDs) or all known patches + The patch owners + + + + Creates a group of patches + An array of prefixes as + An array of postfixes as + An array of transpileres as + An array of finalizeres as + + + + A PatchProcessor handles patches on a method/constructor + + + + Creates an empty patch processor + The Harmony instance + The original method/constructor + + + + Adds a prefix + The prefix as a + A for chaining calls + + + + Adds a prefix + The prefix method + A for chaining calls + + + + Adds a postfix + The postfix as a + A for chaining calls + + + + Adds a postfix + The postfix method + A for chaining calls + + + + Adds a transpiler + The transpiler as a + A for chaining calls + + + + Adds a transpiler + The transpiler method + A for chaining calls + + + + Adds a finalizer + The finalizer as a + A for chaining calls + + + + Adds a finalizer + The finalizer method + A for chaining calls + + + + Gets all patched original methods in the appdomain + An enumeration of patched method/constructor + + + + Applies all registered patches + The generated replacement method + + + + Unpatches patches of a given type and/or Harmony ID + The patch type + Harmony ID or * for any + A for chaining calls + + + + Unpatches a specific patch + The method of the patch + A for chaining calls + + + + Gets patch information on an original + The original method/constructor + The patch information as + + + + Gets Harmony version for all active Harmony instances + [out] The current Harmony version + A dictionary containing assembly version keyed by Harmony ID + + + + Returns the methods unmodified list of code instructions + The original method/constructor + Optionally an existing generator that will be used to create all local variables and labels contained in the result (if not specified, an internal generator is used) + A list containing all the original + + + + Returns the methods unmodified list of code instructions + The original method/constructor + A new generator that now contains all local variables and labels contained in the result + A list containing all the original + + + + A low level way to read the body of a method. Used for quick searching in methods + The original method + All instructions as opcode/operand pairs + + + + A patch priority + + + + Patch last + + + + Patch with very low priority + + + + Patch with low priority + + + + Patch with lower than normal priority + + + + Patch with normal priority + + + + Patch with higher than normal priority + + + + Patch with high priority + + + + Patch with very high priority + + + + Patch first + + + + A reverse patcher + + + + Creates a reverse patcher + The Harmony instance + The original method/constructor + Your stand-in stub method as + + + + Applies the patch + The type of patch, see + The generated replacement method + + + + A collection of commonly used transpilers + + + + A transpiler that replaces all occurrences of a given method with another one + The enumeration of to act on + Method or constructor to search for + Method or constructor to replace with + Modified enumeration of + + + + A transpiler that alters instructions that match a predicate by calling an action + The enumeration of to act on + A predicate selecting the instructions to change + An action to apply to matching instructions + Modified enumeration of + + + + A transpiler that logs a text at the beginning of the method + The instructions to act on + The log text + Modified enumeration of + + + + A helper class for reflection related functions + + + + Shortcut for to simplify the use of reflections and make it work for any access level + + + + Shortcut for to simplify the use of reflections and make it work for any access level but only within the current type + + + + Gets a type by name. Prefers a full name with namespace but falls back to the first type matching the name otherwise + The name + A type or null if not found + + + + Gets all type by name from a given assembly. This is a wrapper that respects different .NET versions + The assembly + An array of types + + + + Applies a function going up the type hierarchy and stops at the first non null result + Result type of func() + The class/type to start with + The evaluation function returning T + Returns the first non null result or default(T) when reaching the top level type object + + + + Applies a function going into inner types and stops at the first non null result + Generic type parameter + The class/type to start with + The evaluation function returning T + Returns the first non null result or null with no match + + + + Gets the reflection information for a directly declared field + The class/type where the field is defined + The name of the field + A field or null when type/name is null or when the field cannot be found + + + + Gets the reflection information for a field by searching the type and all its super types + The class/type where the field is defined + The name of the field (case sensitive) + A field or null when type/name is null or when the field cannot be found + + + + Gets the reflection information for a field + The class/type where the field is declared + The zero-based index of the field inside the class definition + A field or null when type is null or when the field cannot be found + + + + Gets the reflection information for a directly declared property + The class/type where the property is declared + The name of the property (case sensitive) + A property or null when type/name is null or when the property cannot be found + + + + Gets the reflection information for the getter method of a directly declared property + The class/type where the property is declared + The name of the property (case sensitive) + A method or null when type/name is null or when the property cannot be found + + + + Gets the reflection information for the setter method of a directly declared property + The class/type where the property is declared + The name of the property (case sensitive) + A method or null when type/name is null or when the property cannot be found + + + + Gets the reflection information for a property by searching the type and all its super types + The class/type + The name + A property or null when type/name is null or when the property cannot be found + + + + Gets the reflection information for the getter method of a property by searching the type and all its super types + The class/type + The name + A method or null when type/name is null or when the property cannot be found + + + + Gets the reflection information for the setter method of a property by searching the type and all its super types + The class/type + The name + A method or null when type/name is null or when the property cannot be found + + + + Gets the reflection information for a directly declared method + The class/type where the method is declared + The name of the method (case sensitive) + Optional parameters to target a specific overload of the method + Optional list of types that define the generic version of the method + A method or null when type/name is null or when the method cannot be found + + + + Gets the reflection information for a method by searching the type and all its super types + The class/type where the method is declared + The name of the method (case sensitive) + Optional parameters to target a specific overload of the method + Optional list of types that define the generic version of the method + A method or null when type/name is null or when the method cannot be found + + + + Gets the reflection information for a method by searching the type and all its super types + The full name like Namespace.Type1.Type2:MethodName of the type where the method is declared + Optional parameters to target a specific overload of the method + Optional list of types that define the generic version of the method + A method or null when type/name is null or when the method cannot be found + + + + Gets the names of all method that are declared in a type + The declaring class/type + A list of method names + + + + Gets the names of all method that are declared in the type of the instance + An instance of the type to search in + A list of method names + + + + Gets the names of all fields that are declared in a type + The declaring class/type + A list of field names + + + + Gets the names of all fields that are declared in the type of the instance + An instance of the type to search in + A list of field names + + + + Gets the names of all properties that are declared in a type + The declaring class/type + A list of property names + + + + Gets the names of all properties that are declared in the type of the instance + An instance of the type to search in + A list of property names + + + + Gets the type of any class member of + A member + The class/type of this member + + + + Test if a class member is actually an concrete implementation + A member + True if the member is a declared + + + + Gets the real implementation of a class member + A member + The member itself if its declared. Otherwise the member that is actually implemented in some base type + + + + Gets the reflection information for a directly declared constructor + The class/type where the constructor is declared + Optional parameters to target a specific overload of the constructor + Optional parameters to only consider static constructors + A constructor info or null when type is null or when the constructor cannot be found + + + + Gets the reflection information for a constructor by searching the type and all its super types + The class/type where the constructor is declared + Optional parameters to target a specific overload of the method + Optional parameters to only consider static constructors + A constructor info or null when type is null or when the method cannot be found + + + + Gets reflection information for all declared constructors + The class/type where the constructors are declared + Optional parameters to only consider static constructors + A list of constructor infos + + + + Gets reflection information for all declared methods + The class/type where the methods are declared + A list of methods + + + + Gets reflection information for all declared properties + The class/type where the properties are declared + A list of properties + + + + Gets reflection information for all declared fields + The class/type where the fields are declared + A list of fields + + + + Gets the return type of a method or constructor + The method/constructor + The return type + + + + Given a type, returns the first inner type matching a recursive search by name + The class/type to start searching at + The name of the inner type (case sensitive) + The inner type or null if type/name is null or if a type with that name cannot be found + + + + Given a type, returns the first inner type matching a recursive search with a predicate + The class/type to start searching at + The predicate to search with + The inner type or null if type/predicate is null or if a type with that name cannot be found + + + + Given a type, returns the first method matching a predicate + The class/type to start searching at + The predicate to search with + The method or null if type/predicate is null or if a type with that name cannot be found + + + + Given a type, returns the first constructor matching a predicate + The class/type to start searching at + The predicate to search with + The constructor info or null if type/predicate is null or if a type with that name cannot be found + + + + Given a type, returns the first property matching a predicate + The class/type to start searching at + The predicate to search with + The property or null if type/predicate is null or if a type with that name cannot be found + + + + Returns an array containing the type of each object in the given array + An array of objects + An array of types or an empty array if parameters is null (if an object is null, the type for it will be object) + + + + Creates an array of input parameters for a given method and a given set of potential inputs + The method/constructor you are planing to call + The possible input parameters in any order + An object array matching the method signature + + + + A read/writable reference to an instance field + The class the field is defined in or "object" if type cannot be accessed at compile time + The type of the field + The runtime instance to access the field (leave empty for static fields) + An readable/assignable object representing the field + + + + Creates an instance field reference + The class the field is defined in or "object" if type cannot be accessed at compile time + The type of the field + The name of the field + A read and writable field reference delegate + + + + Creates an instance field reference for a specific instance + The class the field is defined in or "object" if type cannot be accessed at compile time + The type of the field + The instance + The name of the field + An readable/assignable object representing the field + + + + Creates an instance field reference delegate + The class the field is defined in or "object" if type cannot be accessed at compile time + The type of the field + The field of the field + A read and writable delegate + + + + A read/writable reference delegate to a static field + The type of the field + An readable/assignable object representing the static field + + + + Creates a static field reference + The class the field is defined in or "object" if type cannot be accessed at compile time + The type of the field + The name of the field + An readable/assignable object representing the static field + + + + Creates a static field reference delegate + The type of the field + The field + A read and writable delegate + + + + Returns who called the current method + The calling method/constructor (excluding the caller) + + + + Rethrows an exception while preserving its stack trace (throw statement typically clobbers existing stack traces) + The exception to rethrow + + + + Tells you if the current runtime is based on Mono + True if we are running under Mono, false otherwise (.NET) + + + + Throws a missing member runtime exception + The type that is involved + A list of names + + + + Gets default value for a specific type + The class/type + The default value + + + + Creates an (possibly uninitialized) instance of a given type + The class/type + The new instance + + + + Makes a deep copy of any object + The type of the instance that should be created + The original object + A copy of the original object but of type T + + + + Makes a deep copy of any object + The type of the instance that should be created + The original object + [out] The copy of the original object + Optional value transformation function (taking a field name and src/dst instances) + The optional path root to start with + + + + Makes a deep copy of any object + The original object + The type of the instance that should be created + Optional value transformation function (taking a field name and src/dst instances) + The optional path root to start with + The copy of the original object + + + + Tests if a type is a struct + The type + True if the type is a struct + + + + Tests if a type is a class + The type + True if the type is a class + + + + Tests if a type is a value type + The type + True if the type is a value type + + + + Tests if a type is an integer type + The type + True if the type represents some integer + + + + Tests if a type is a floating point type + The type + True if the type represents some floating point + + + + Tests if a type is a numerical type + The type + True if the type represents some number + + + + Tests if a type is void + The type + True if the type is void + + + + Test whether an instance is of a nullable type + Type of instance + An instance to test + True if instance is of nullable type, false if not + + + + Calculates a combined hash code for an enumeration of objects + The objects + The hash code + + + + General extensions for common cases + + + + Joins an enumeration with a value converter and a delimiter to a string + The inner type of the enumeration + The enumeration + An optional value converter (from T to string) + An optional delimiter + The values joined into a string + + + + Converts an array of types (for example methods arguments) into a human readable form + The array of types + A human readable description including brackets + + + + A full description of a type + The type + A human readable description + + + + A a full description of a method or a constructor without assembly details but with generics + The method/constructor + A human readable description + + + + A helper converting parameter infos to types + The array of parameter infos + An array of types + + + + A helper to access a value via key from a dictionary + The key type + The value type + The dictionary + The key + The value for the key or the default value (of T) if that key does not exist + + + + A helper to access a value via key from a dictionary with extra casting + The value type + The dictionary + The key + The value for the key or the default value (of T) if that key does not exist or cannot be cast to T + + + + Escapes Unicode and ASCII non printable characters + The string to convert + The string to convert + A string literal surrounded by + + + + Extensions for + + + + Shortcut for testing whether the operand is equal to a non-null value + The + The value + True if the operand has the same type and is equal to the value + + + + Shortcut for testing whether the operand is equal to a non-null value + The + The value + True if the operand is equal to the value + This is an optimized version of for + + + + Shortcut for code.opcode == opcode && code.OperandIs(operand) + The + The + The operand value + True if the opcode is equal to the given opcode and the operand has the same type and is equal to the given operand + + + + Shortcut for code.opcode == opcode && code.OperandIs(operand) + The + The + The operand value + True if the opcode is equal to the given opcode and the operand is equal to the given operand + This is an optimized version of for + + + + Tests for any form of Ldarg* + The + The (optional) index + True if it matches one of the variations + + + + Tests for Ldarga/Ldarga_S + The + The (optional) index + True if it matches one of the variations + + + + Tests for Starg/Starg_S + The + The (optional) index + True if it matches one of the variations + + + + Tests for any form of Ldloc* + The + The optional local variable + True if it matches one of the variations + + + + Tests for any form of Stloc* + The + The optional local variable + True if it matches one of the variations + + + + Tests if the code instruction branches + The + The label if the instruction is a branch operation or if not + True if the instruction branches + + + + Tests if the code instruction calls the method/constructor + The + The method + True if the instruction calls the method or constructor + + + + Tests if the code instruction loads a constant + The + True if the instruction loads a constant + + + + Tests if the code instruction loads an integer constant + The + The integer constant + True if the instruction loads the constant + + + + Tests if the code instruction loads a floating point constant + The + The floating point constant + True if the instruction loads the constant + + + + Tests if the code instruction loads an enum constant + The + The enum + True if the instruction loads the constant + + + + Tests if the code instruction loads a field + The + The field + Set to true if the address of the field is loaded + True if the instruction loads the field + + + + Tests if the code instruction stores a field + The + The field + True if the instruction stores this field + + + + General extensions for collections + + + + A simple way to execute code for every element in a collection + The inner type of the collection + The collection + The action to execute + + + + A simple way to execute code for elements in a collection matching a condition + The inner type of the collection + The collection + The predicate + The action to execute + + + + A helper to add an item to a collection + The inner type of the collection + The collection + The item to add + The collection containing the item + + + + A helper to add an item to an array + The inner type of the collection + The array + The item to add + The array containing the item + + + + A helper to add items to an array + The inner type of the collection + The array + The items to add + The array containing the items + + + + A file log for debugging + + + + Full pathname of the log file, defaults to a file called harmony.log.txt on your Desktop + + + + The indent character. The default is tab + + + + The current indent level + + + + Changes the indentation level + The value to add to the indentation level + + + + Log a string in a buffered way. Use this method only if you are sure that FlushBuffer will be called + or else logging information is incomplete in case of a crash + The string to log + + + + Logs a list of string in a buffered way. Use this method only if you are sure that FlushBuffer will be called + or else logging information is incomplete in case of a crash + A list of strings to log (they will not be re-indented) + + + + Returns the log buffer and optionally empties it + True to empty the buffer + The buffer. + + + + Replaces the buffer with new lines + The lines to store + + + + Flushes the log buffer to disk (use in combination with LogBuffered) + + + + Log a string directly to disk. Slower method that prevents missing information in case of a crash + The string to log. + + + + Resets and deletes the log + + + + Logs some bytes as hex values + The pointer to some memory + The length of bytes to log + + + + A helper class to retrieve reflection info for non-private methods + + + + Given a lambda expression that calls a method, returns the method info + The lambda expression using the method + The method in the lambda expression + + + + Given a lambda expression that calls a method, returns the method info + The generic type + The lambda expression using the method + The method in the lambda expression + + + + Given a lambda expression that calls a method, returns the method info + The generic type + The generic result type + The lambda expression using the method + The method in the lambda expression + + + + Given a lambda expression that calls a method, returns the method info + The lambda expression using the method + The method in the lambda expression + + + + A reflection helper to read and write private elements + The result type defined by GetValue() + + + + Creates a traverse instance from an existing instance + The existing instance + + + + Gets/Sets the current value + The value to read or write + + + + A reflection helper to read and write private elements + + + + Creates a new traverse instance from a class/type + The class/type + A instance + + + + Creates a new traverse instance from a class T + The class + A instance + + + + Creates a new traverse instance from an instance + The object + A instance + + + + Creates a new traverse instance from a named type + The type name, for format see + A instance + + + + Creates a new and empty traverse instance + + + + Creates a new traverse instance from a class/type + The class/type + + + + Creates a new traverse instance from an instance + The object + + + + Gets the current value + The value + + + + Gets the current value + The type of the value + The value + + + + Invokes the current method with arguments and returns the result + The method arguments + The value returned by the method + + + + Invokes the current method with arguments and returns the result + The type of the value + The method arguments + The value returned by the method + + + + Sets a value of the current field or property + The value + The same traverse instance + + + + Gets the type of the current field or property + The type + + + + Moves the current traverse instance to a inner type + The type name + A traverse instance + + + + Moves the current traverse instance to a field + The type name + A traverse instance + + + + Moves the current traverse instance to a field + The type of the field + The type name + A traverse instance + + + + Gets all fields of the current type + A list of field names + + + + Moves the current traverse instance to a property + The type name + Optional property index + A traverse instance + + + + Moves the current traverse instance to a field + The type of the property + The type name + Optional property index + A traverse instance + + + + Gets all properties of the current type + A list of property names + + + + Moves the current traverse instance to a method + The name of the method + The arguments defining the argument types of the method overload + A traverse instance + + + + Moves the current traverse instance to a method + The name of the method + The argument types of the method + The arguments for the method + A traverse instance + + + + Gets all methods of the current type + A list of method names + + + + Checks if the current traverse instance is for a field + True if its a field + + + + Checks if the current traverse instance is for a property + True if its a property + + + + Checks if the current traverse instance is for a method + True if its a method + + + + Checks if the current traverse instance is for a type + True if its a type + + + + Iterates over all fields of the current type and executes a traverse action + Original object + The action receiving a instance for each field + + + + Iterates over all fields of the current type and executes a traverse action + Original object + Target object + The action receiving a pair of instances for each field pair + + + + Iterates over all fields of the current type and executes a traverse action + Original object + Target object + The action receiving a dot path representing the field pair and the instances + + + + Iterates over all properties of the current type and executes a traverse action + Original object + The action receiving a instance for each property + + + + Iterates over all properties of the current type and executes a traverse action + Original object + Target object + The action receiving a pair of instances for each property pair + + + + Iterates over all properties of the current type and executes a traverse action + Original object + Target object + The action receiving a dot path representing the property pair and the instances + + + + A default field action that copies fields to fields + + + + Returns a string that represents the current traverse + A string representation + + + + diff --git a/1.1/Assemblies/IHoldMultipleThings.dll b/1.1/Assemblies/IHoldMultipleThings.dll new file mode 100644 index 0000000..bccd46a Binary files /dev/null and b/1.1/Assemblies/IHoldMultipleThings.dll differ diff --git a/1.1/Assemblies/PickUpAndHaul.dll b/1.1/Assemblies/PickUpAndHaul.dll new file mode 100644 index 0000000..40b7765 Binary files /dev/null and b/1.1/Assemblies/PickUpAndHaul.dll differ diff --git a/About/About.xml b/About/About.xml index 063797b..f5de76d 100644 --- a/About/About.xml +++ b/About/About.xml @@ -4,19 +4,31 @@ Mehni
  • 1.0
  • +
  • 1.1
  • + Mehni.PickUpAndHaul + + +
  • + brrainz.harmony + Harmony + steam://url/CommunityFilePage/2009463077 + https://github.com/pardeike/HarmonyRimWorld/releases/latest +
  • +
    +
    https://ludeon.com/forums/index.php?topic=35832 "Greatest hauling mod ever" - Chicken Plucker - v.1.0.5 - + v1.1.0⅓ + Colonists will gather stuff in their inventory, then haul it all to a stockpile. This hauling mod will greatly increase hauling efficiency, because pawns can now carry more than one gun or t-shirt. Those smart and intelligent colonists can use their inventory! -Works well in combination with Combat Extended, AllowTool's Haul Urgently, etc. No known conflicts. +Safe to add to existing games. -Safe to add to existing games. <size=20>Credits and thanks:</size> +<size=20>Credits and thanks:</size> - AlexTD, for his direct contributions - erdelf, Zorba, Why_is_that and Dingo for code and advice (yet again!) - Chicken Plucker, for the preview image diff --git a/Source/IHoldMultipleThings/IHoldMultipleThings.csproj b/Source/IHoldMultipleThings/IHoldMultipleThings.csproj index a9b4eb5..a3f2c6a 100644 --- a/Source/IHoldMultipleThings/IHoldMultipleThings.csproj +++ b/Source/IHoldMultipleThings/IHoldMultipleThings.csproj @@ -9,25 +9,28 @@ Properties IHoldMultipleThings IHoldMultipleThings - v3.5 + v4.7.2 512 + false none true - ..\..\Assemblies\ + ..\..\v1.1\Assemblies\ TRACE prompt 4 + false none true - ..\..\Assemblies\ + ..\..\1.1\Assemblies\ TRACE prompt 4 + false diff --git a/Source/IHoldMultipleThingsv1.0/IHoldMultipleThings.cs b/Source/IHoldMultipleThingsv1.0/IHoldMultipleThings.cs new file mode 100644 index 0000000..7adc3c4 --- /dev/null +++ b/Source/IHoldMultipleThingsv1.0/IHoldMultipleThings.cs @@ -0,0 +1,11 @@ +using Verse; + +namespace IHoldMultipleThings +{ + public interface IHoldMultipleThings + { + bool CapacityAt(Thing thing, IntVec3 storeCell, Map map, out int capacity); + + bool StackableAt(Thing thing, IntVec3 storeCell, Map map); + } +} diff --git a/Source/IHoldMultipleThingsv1.0/IHoldMultipleThingsv10.csproj b/Source/IHoldMultipleThingsv1.0/IHoldMultipleThingsv10.csproj new file mode 100644 index 0000000..265887c --- /dev/null +++ b/Source/IHoldMultipleThingsv1.0/IHoldMultipleThingsv10.csproj @@ -0,0 +1,53 @@ + + + + + Debug + AnyCPU + {0F7BA6C3-BF3C-42D5-9268-593B4AF8FC12} + Library + Properties + IHoldMultipleThings + IHoldMultipleThings + v3.5 + 512 + + + false + none + true + ..\..\Assemblies\ + TRACE + prompt + 4 + + + none + true + ..\..\Assemblies\ + TRACE + prompt + 4 + + + + ..\..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll + False + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/IHoldMultipleThingsv1.0/Properties/AssemblyInfo.cs b/Source/IHoldMultipleThingsv1.0/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..77b3e84 --- /dev/null +++ b/Source/IHoldMultipleThingsv1.0/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IHoldMultipleThings")] +[assembly: AssemblyDescription("Pick Up and Haul interface for multi-stack solutions")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("IHoldMultipleThings")] +[assembly: AssemblyCopyright("Copyright © Mehni 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e1536a54-d289-41fa-9d0b-8a2f6812c7fa")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.1.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Source/PickUpAndHaul.sln b/Source/PickUpAndHaul.sln index 4bf388e..1eef94f 100644 --- a/Source/PickUpAndHaul.sln +++ b/Source/PickUpAndHaul.sln @@ -1,12 +1,16 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2020 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29025.244 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PickUpAndHaul", "PickUpAndHaul\PickUpAndHaul.csproj", "{CCF8350B-E3FD-4693-9209-681E9C089097}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IHoldMultipleThings", "IHoldMultipleThings\IHoldMultipleThings.csproj", "{E1536A54-D289-41FA-9D0B-8A2F6812C7FA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PickUpAndHaulv10", "PickUpAndHaulv1.0\PickUpAndHaulv10.csproj", "{2480F13E-E9ED-44EC-A5DE-C821676904E7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IHoldMultipleThingsv10", "IHoldMultipleThingsv1.0\IHoldMultipleThingsv10.csproj", "{0F7BA6C3-BF3C-42D5-9268-593B4AF8FC12}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +25,10 @@ Global {E1536A54-D289-41FA-9D0B-8A2F6812C7FA}.Debug|Any CPU.Build.0 = Debug|Any CPU {E1536A54-D289-41FA-9D0B-8A2F6812C7FA}.Release|Any CPU.ActiveCfg = Release|Any CPU {E1536A54-D289-41FA-9D0B-8A2F6812C7FA}.Release|Any CPU.Build.0 = Release|Any CPU + {2480F13E-E9ED-44EC-A5DE-C821676904E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2480F13E-E9ED-44EC-A5DE-C821676904E7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0F7BA6C3-BF3C-42D5-9268-593B4AF8FC12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0F7BA6C3-BF3C-42D5-9268-593B4AF8FC12}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/PickUpAndHaul/DebugLog.cs b/Source/PickUpAndHaul/DebugLog.cs index 2744706..fb56ddf 100644 --- a/Source/PickUpAndHaul/DebugLog.cs +++ b/Source/PickUpAndHaul/DebugLog.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace PickUpAndHaul +namespace PickUpAndHaul { static class Log { diff --git a/Source/PickUpAndHaul/HarmonyPatches.cs b/Source/PickUpAndHaul/HarmonyPatches.cs index 63e0170..ed7fb91 100644 --- a/Source/PickUpAndHaul/HarmonyPatches.cs +++ b/Source/PickUpAndHaul/HarmonyPatches.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using RimWorld; using Verse; using UnityEngine; -using Harmony; +using HarmonyLib; using System.Reflection.Emit; using System.Reflection; using Verse.AI; @@ -17,7 +16,10 @@ static class HarmonyPatches { static HarmonyPatches() { - HarmonyInstance harmony = HarmonyInstance.Create(id: "mehni.rimworld.pickupandhaul.main"); + var harmony = new Harmony("mehni.rimworld.pickupandhaul.main"); +#if DEBUG + Harmony.DEBUG = true; +#endif harmony.Patch(original: AccessTools.Method(typeof(FloatMenuMakerMap), "AddHumanlikeOrders"), transpiler: new HarmonyMethod(typeof(HarmonyPatches), nameof(FloatMenuMakerMad_AddHumanlikeOrders_Transpiler))); @@ -40,7 +42,7 @@ static HarmonyPatches() harmony.Patch(original: AccessTools.Method(typeof(ITab_Pawn_Gear), "DrawThingRow"), transpiler: new HarmonyMethod(typeof(HarmonyPatches), nameof(GearTabHighlightTranspiler))); - Verse.Log.Message("PickUpAndHaul v0.1.0.5¼ welcomes you to RimWorld with pointless logspam.", true); + Verse.Log.Message("PickUpAndHaul v0.1.1.0⅓ welcomes you to RimWorld with pointless logspam.", true); } private static bool Drop_Prefix(Pawn pawn, Thing thing) @@ -101,7 +103,6 @@ public static void DropUnusedInventory_PostFix(Pawn pawn) public static IEnumerable FloatMenuMakerMad_AddHumanlikeOrders_Transpiler(IEnumerable instructions) { - MethodInfo playerHome = AccessTools.Property(typeof(Map), nameof(Map.IsPlayerHome)).GetGetMethod(); List instructionList = instructions.ToList(); @@ -109,7 +110,7 @@ public static IEnumerable FloatMenuMakerMad_AddHumanlikeOrders_ foreach (CodeInstruction instruction in instructionList) { - if (!patched && instruction.operand == playerHome && !ModCompatibilityCheck.CombatExtendedIsActive) + if (!patched && instruction.Calls(playerHome) && !ModCompatibilityCheck.CombatExtendedIsActive) { instruction.opcode = OpCodes.Ldc_I4_0; instruction.operand = null; @@ -122,26 +123,24 @@ public static IEnumerable FloatMenuMakerMad_AddHumanlikeOrders_ //ITab_Pawn_Gear //private void DrawThingRow(ref float y, float width, Thing thing, bool inventory = false) - public static IEnumerable GearTabHighlightTranspiler(IEnumerable instructions, ILGenerator il, MethodBase mb) + public static IEnumerable GearTabHighlightTranspiler(IEnumerable instructions) { - MethodInfo WidgetsButtonImageInfo = AccessTools.Method(typeof(Widgets), "ButtonImage", new Type[] { typeof(Rect), typeof(Texture2D) }); - MethodInfo WidgetsButtonImageColorInfo = AccessTools.Method(typeof(Widgets), "ButtonImage", new Type[] { typeof(Rect), typeof(Texture2D), typeof(Color) }); - MethodInfo SelPawnForGearInfo = AccessTools.Property(typeof(ITab_Pawn_Gear), "SelPawnForGear").GetGetMethod(true); MethodInfo GetColorForHauledInfo = AccessTools.Method(typeof(HarmonyPatches), nameof(GetColorForHauled)); + MethodInfo ColorWhite = AccessTools.Property(typeof(Color), nameof(Color.white)).GetGetMethod(); + bool done = false; foreach (CodeInstruction i in instructions) { - //if (Widgets.ButtonImage(rect2, TexButton.Drop)) - if (!done && i.opcode == OpCodes.Call && i.operand == WidgetsButtonImageInfo) + //// Color color = flag ? Color.grey : Color.white; + if (!done && i.Calls(ColorWhite)) { yield return new CodeInstruction(OpCodes.Ldarg_0); //this yield return new CodeInstruction(OpCodes.Call, SelPawnForGearInfo); //this.SelPawnForGearInfo yield return new CodeInstruction(OpCodes.Ldarg_3); //thing yield return new CodeInstruction(OpCodes.Call, GetColorForHauledInfo); //GetColorForHauledInfo(Pawn, Thing) - yield return new CodeInstruction(OpCodes.Call, WidgetsButtonImageColorInfo); //ButtonImage(rect, texture, color) done = true; } else diff --git a/Source/PickUpAndHaul/JobDriver_HaulToInventory.cs b/Source/PickUpAndHaul/JobDriver_HaulToInventory.cs index 1cad9b6..80fe3cb 100644 --- a/Source/PickUpAndHaul/JobDriver_HaulToInventory.cs +++ b/Source/PickUpAndHaul/JobDriver_HaulToInventory.cs @@ -145,7 +145,7 @@ protected override IEnumerable MakeNewToils() Job curJob = actor.jobs.curJob; LocalTargetInfo storeCell = curJob.targetB; - Job unloadJob = new Job(PickUpAndHaulJobDefOf.UnloadYourHauledInventory, storeCell); + Job unloadJob = JobMaker.MakeJob(PickUpAndHaulJobDefOf.UnloadYourHauledInventory, storeCell); if (unloadJob.TryMakePreToilReservations(actor, false)) { actor.jobs.jobQueue.EnqueueFirst(unloadJob, JobTag.Misc); diff --git a/Source/PickUpAndHaul/JobDriver_UnloadYourHauledInventory.cs b/Source/PickUpAndHaul/JobDriver_UnloadYourHauledInventory.cs index 309c2e6..0539126 100644 --- a/Source/PickUpAndHaul/JobDriver_UnloadYourHauledInventory.cs +++ b/Source/PickUpAndHaul/JobDriver_UnloadYourHauledInventory.cs @@ -158,7 +158,7 @@ from straggler in pawn.inventory.innerContainer } return new ThingCount(thing, thing.stackCount); } - return default(ThingCount); + return default; } } } \ No newline at end of file diff --git a/Source/PickUpAndHaul/PawnUnloadChecker.cs b/Source/PickUpAndHaul/PawnUnloadChecker.cs index 47ef7f2..519dacd 100644 --- a/Source/PickUpAndHaul/PawnUnloadChecker.cs +++ b/Source/PickUpAndHaul/PawnUnloadChecker.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Collections.Generic; using RimWorld; using Verse; using Verse.AI; @@ -12,7 +9,7 @@ public class PawnUnloadChecker { public static void CheckIfPawnShouldUnloadInventory(Pawn pawn, bool forced = false) { - Job job = new Job(PickUpAndHaulJobDefOf.UnloadYourHauledInventory, pawn); + Job job = JobMaker.MakeJob(PickUpAndHaulJobDefOf.UnloadYourHauledInventory, pawn); CompHauledToInventory itemsTakenToInventory = pawn.TryGetComp(); if (itemsTakenToInventory == null) diff --git a/Source/PickUpAndHaul/PickUpAndHaul.csproj b/Source/PickUpAndHaul/PickUpAndHaul.csproj index 35b6458..2e47ac0 100644 --- a/Source/PickUpAndHaul/PickUpAndHaul.csproj +++ b/Source/PickUpAndHaul/PickUpAndHaul.csproj @@ -9,45 +9,44 @@ Properties PickUpAndHaul PickUpAndHaul - v3.5 + v4.7.2 512 + false none true - ..\..\Assemblies\ + ..\..\v1.1\Assemblies\ DEBUG;TRACE prompt 4 + false none true - ..\..\Assemblies\ + ..\..\1.1\Assemblies\ TRACE prompt 4 + false - - False - ..\..\..\..\..\..\..\..\..\..\Users\Maniak\Documents\RimWorld Mods\0Harmony.dll + + ..\..\..\..\..\..\workshop\content\294100\2009463077\Assemblies\0Harmony.dll ..\..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll False - - ..\..\..\..\..\..\workshop\content\294100\731732064\Assemblies\ExtendedStorage.dll - False - False ..\..\Assemblies\IHoldMultipleThings.dll + @@ -56,13 +55,15 @@ ..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.dll False + + ..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll + False + - - diff --git a/Source/PickUpAndHaul/Properties/AssemblyInfo.cs b/Source/PickUpAndHaul/Properties/AssemblyInfo.cs index 7dab455..a040188 100644 --- a/Source/PickUpAndHaul/Properties/AssemblyInfo.cs +++ b/Source/PickUpAndHaul/Properties/AssemblyInfo.cs @@ -33,4 +33,4 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("0.1.0.5")] -[assembly: AssemblyFileVersion("0.1.0.853")] +[assembly: AssemblyFileVersion("0.1.1.0")] diff --git a/Source/PickUpAndHaul/WorkGiver_HaulToInventory.cs b/Source/PickUpAndHaul/WorkGiver_HaulToInventory.cs index 18979d0..479eb85 100644 --- a/Source/PickUpAndHaul/WorkGiver_HaulToInventory.cs +++ b/Source/PickUpAndHaul/WorkGiver_HaulToInventory.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using RimWorld; using Verse; using Verse.AI; @@ -91,7 +90,7 @@ public override Job JobOnThing(Pawn pawn, Thing thing, bool forced = false) if (capacityStoreCell == 0) return HaulAIUtility.HaulToStorageJob(pawn, thing); - Job job = new Job(PickUpAndHaulJobDefOf.HaulToInventory, null, storeCell); //Things will be in queues + Job job = JobMaker.MakeJob(PickUpAndHaulJobDefOf.HaulToInventory, null, storeCell); //Things will be in queues Log.Message($"-------------------------------------------------------------------"); Log.Message($"------------------------------------------------------------------");//different size so the log doesn't count it 2x Log.Message($"{pawn} job found to haul: {thing} to {storeCell}:{capacityStoreCell}, looking for more now"); @@ -99,7 +98,7 @@ public override Job JobOnThing(Pawn pawn, Thing thing, bool forced = false) //Find extra things than can be hauled to inventory, queue to reserve them bool isUrgent = ModCompatibilityCheck.AllowToolIsActive && pawn.Map.designationManager.DesignationOn(thing)?.def == haulUrgentlyDesignation; - Func validatorExtra = (Thing t) => + bool validatorExtra(Thing t) => (!isUrgent || pawn.Map.designationManager.DesignationOn(t)?.def == haulUrgentlyDesignation) && GoodThingToHaul(t, pawn) && HaulAIUtility.PawnCanAutomaticallyHaulFast(pawn, t, false);//forced is false, may differ from first thing @@ -221,19 +220,13 @@ public CellAllocation(Thing a, int c) public static int CapacityAt(Thing thing, IntVec3 storeCell, Map map) { - int capacity; - if (HoldMultipleThings_Support.CapacityAt(thing, storeCell, map, out capacity)) + if (HoldMultipleThings_Support.CapacityAt(thing, storeCell, map, out int capacity)) { Log.Message($"Found external capacity of {capacity}"); return capacity; } - if (ExtendedStorage_Support.CapacityAt(thing, storeCell, map, out capacity)) - { - return capacity; - } - capacity = thing.def.stackLimit; Thing preExistingThing = map.thingGrid.ThingAt(storeCell, thing.def); @@ -246,8 +239,7 @@ public static int CapacityAt(Thing thing, IntVec3 storeCell, Map map) public static bool Stackable(Thing nextThing, KeyValuePair allocation) => nextThing == allocation.Value.allocated || allocation.Value.allocated.CanStackWith(nextThing) - || HoldMultipleThings_Support.StackableAt(nextThing, allocation.Key, nextThing.Map) - || ExtendedStorage_Support.StackableAt(nextThing.def, allocation.Key, nextThing.Map); + || HoldMultipleThings_Support.StackableAt(nextThing, allocation.Key, nextThing.Map); public static bool AllocateThingAtCell(Dictionary storeCellCapacity, Pawn pawn, Thing nextThing, Job job) { @@ -258,7 +250,7 @@ public static bool AllocateThingAtCell(Dictionary store IntVec3 storeCell = allocation.Key; //Can't stack with allocated cells, find a new cell: - if (storeCell == default(IntVec3)) + if (storeCell == default) { StoragePriority currentPriority = StoreUtility.CurrentStoragePriorityOf(nextThing); if (TryFindBestBetterStoreCellFor(nextThing, pawn, map, currentPriority, pawn.Faction, out IntVec3 nextStoreCell)) @@ -326,7 +318,7 @@ public static bool TryFindBestBetterStoreCellFor(Thing thing, Pawn carrier, Map .Where(s => s.Settings.Priority > currentPriority && s.parent.Accepts(thing))) { if (slotGroup.CellsList.Except(skipCells).FirstOrDefault(c => StoreUtility.IsGoodStoreCell(c, map, thing, carrier, faction)) is IntVec3 cell - && cell != default(IntVec3)) + && cell != default) { foundCell = cell; diff --git a/Source/PickUpAndHaulv1.0/CompHauledToInventory.cs b/Source/PickUpAndHaulv1.0/CompHauledToInventory.cs new file mode 100644 index 0000000..8a75a28 --- /dev/null +++ b/Source/PickUpAndHaulv1.0/CompHauledToInventory.cs @@ -0,0 +1,27 @@ +namespace PickUpAndHaul +{ + using System.Collections.Generic; + using Verse; + + public class CompHauledToInventory : ThingComp + { + private HashSet takenToInventory = new HashSet(); + + public HashSet GetHashSet() + { + takenToInventory.RemoveWhere(x => x == null); + return takenToInventory; + } + + public void RegisterHauledItem(Thing thing) + { + this.takenToInventory.Add(thing); + } + + public override void PostExposeData() + { + base.PostExposeData(); + Scribe_Collections.Look(ref takenToInventory, "ThingsHauledToInventory", LookMode.Reference); + } + } +} diff --git a/Source/PickUpAndHaulv1.0/DebugLog.cs b/Source/PickUpAndHaulv1.0/DebugLog.cs new file mode 100644 index 0000000..2744706 --- /dev/null +++ b/Source/PickUpAndHaulv1.0/DebugLog.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PickUpAndHaul +{ + static class Log + { + [System.Diagnostics.Conditional("DEBUG")] + public static void Message(string x) + { + Verse.Log.Message(x, true); + } + } +} diff --git a/Source/PickUpAndHaul/ExtendedStorage_Support.cs b/Source/PickUpAndHaulv1.0/ExtendedStorage_Support.cs similarity index 100% rename from Source/PickUpAndHaul/ExtendedStorage_Support.cs rename to Source/PickUpAndHaulv1.0/ExtendedStorage_Support.cs diff --git a/Source/PickUpAndHaulv1.0/HarmonyPatches.cs b/Source/PickUpAndHaulv1.0/HarmonyPatches.cs new file mode 100644 index 0000000..63e0170 --- /dev/null +++ b/Source/PickUpAndHaulv1.0/HarmonyPatches.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using RimWorld; +using Verse; +using UnityEngine; +using Harmony; +using System.Reflection.Emit; +using System.Reflection; +using Verse.AI; + +namespace PickUpAndHaul +{ + [StaticConstructorOnStartup] + static class HarmonyPatches + { + static HarmonyPatches() + { + HarmonyInstance harmony = HarmonyInstance.Create(id: "mehni.rimworld.pickupandhaul.main"); + + harmony.Patch(original: AccessTools.Method(typeof(FloatMenuMakerMap), "AddHumanlikeOrders"), + transpiler: new HarmonyMethod(typeof(HarmonyPatches), nameof(FloatMenuMakerMad_AddHumanlikeOrders_Transpiler))); + + harmony.Patch(original: AccessTools.Method(typeof(JobGiver_DropUnusedInventory), "TryGiveJob"), + postfix: new HarmonyMethod(typeof(HarmonyPatches), nameof(DropUnusedInventory_PostFix))); + + harmony.Patch(original: AccessTools.Method(typeof(JobDriver_HaulToCell), "MakeNewToils"), + postfix: new HarmonyMethod(typeof(HarmonyPatches), nameof(JobDriver_HaulToCell_PostFix))); + + harmony.Patch(original: AccessTools.Method(typeof(Pawn_InventoryTracker), "Notify_ItemRemoved"), + postfix: new HarmonyMethod(typeof(HarmonyPatches), nameof(Pawn_InventoryTracker_PostFix))); + + harmony.Patch(original: AccessTools.Method(typeof(JobGiver_DropUnusedInventory), "Drop"), + prefix: new HarmonyMethod(typeof(HarmonyPatches), nameof(Drop_Prefix))); + + harmony.Patch(original: AccessTools.Method(typeof(JobGiver_Idle), "TryGiveJob"), + postfix: new HarmonyMethod(typeof(HarmonyPatches), nameof(IdleJoy_Postfix))); + + harmony.Patch(original: AccessTools.Method(typeof(ITab_Pawn_Gear), "DrawThingRow"), + transpiler: new HarmonyMethod(typeof(HarmonyPatches), nameof(GearTabHighlightTranspiler))); + + Verse.Log.Message("PickUpAndHaul v0.1.0.5¼ welcomes you to RimWorld with pointless logspam.", true); + } + + private static bool Drop_Prefix(Pawn pawn, Thing thing) + { + CompHauledToInventory takenToInventory = pawn.TryGetComp(); + if (takenToInventory == null) + return true; + + HashSet carriedThing = takenToInventory.GetHashSet(); + + return !carriedThing.Contains(thing); + } + + private static void Pawn_InventoryTracker_PostFix(Pawn_InventoryTracker __instance, Thing item) + { + CompHauledToInventory takenToInventory = __instance.pawn.TryGetComp(); + if (takenToInventory == null) + return; + + HashSet carriedThing = takenToInventory.GetHashSet(); + if (carriedThing?.Count > 0) + { + if (carriedThing.Contains(item)) + { + carriedThing.Remove(item); + } + } + } + + private static void JobDriver_HaulToCell_PostFix(JobDriver_HaulToCell __instance) + { + CompHauledToInventory takenToInventory = __instance.pawn.TryGetComp(); + if (takenToInventory == null) + return; + + HashSet carriedThing = takenToInventory.GetHashSet(); + + if (__instance.job.haulMode == HaulMode.ToCellStorage + && __instance.pawn.Faction == Faction.OfPlayer + && __instance.pawn.RaceProps.Humanlike + && __instance.pawn.carryTracker.CarriedThing is Corpse == false + && carriedThing != null + && carriedThing.Count != 0) //deliberate hauling job. Should unload. + { + PawnUnloadChecker.CheckIfPawnShouldUnloadInventory(__instance.pawn, true); + } + } + + public static void IdleJoy_Postfix(Pawn pawn) + { + PawnUnloadChecker.CheckIfPawnShouldUnloadInventory(pawn, true); + } + + public static void DropUnusedInventory_PostFix(Pawn pawn) + { + PawnUnloadChecker.CheckIfPawnShouldUnloadInventory(pawn); + } + + public static IEnumerable FloatMenuMakerMad_AddHumanlikeOrders_Transpiler(IEnumerable instructions) + { + + MethodInfo playerHome = AccessTools.Property(typeof(Map), nameof(Map.IsPlayerHome)).GetGetMethod(); + List instructionList = instructions.ToList(); + + bool patched = false; + + foreach (CodeInstruction instruction in instructionList) + { + if (!patched && instruction.operand == playerHome && !ModCompatibilityCheck.CombatExtendedIsActive) + { + instruction.opcode = OpCodes.Ldc_I4_0; + instruction.operand = null; + yield return instruction; + patched = true; + } + yield return instruction; + } + } + + //ITab_Pawn_Gear + //private void DrawThingRow(ref float y, float width, Thing thing, bool inventory = false) + public static IEnumerable GearTabHighlightTranspiler(IEnumerable instructions, ILGenerator il, MethodBase mb) + { + MethodInfo WidgetsButtonImageInfo = AccessTools.Method(typeof(Widgets), "ButtonImage", new Type[] { typeof(Rect), typeof(Texture2D) }); + MethodInfo WidgetsButtonImageColorInfo = AccessTools.Method(typeof(Widgets), "ButtonImage", new Type[] { typeof(Rect), typeof(Texture2D), typeof(Color) }); + + MethodInfo SelPawnForGearInfo = AccessTools.Property(typeof(ITab_Pawn_Gear), "SelPawnForGear").GetGetMethod(true); + + MethodInfo GetColorForHauledInfo = AccessTools.Method(typeof(HarmonyPatches), nameof(GetColorForHauled)); + + bool done = false; + foreach (CodeInstruction i in instructions) + { + //if (Widgets.ButtonImage(rect2, TexButton.Drop)) + if (!done && i.opcode == OpCodes.Call && i.operand == WidgetsButtonImageInfo) + { + yield return new CodeInstruction(OpCodes.Ldarg_0); //this + yield return new CodeInstruction(OpCodes.Call, SelPawnForGearInfo); //this.SelPawnForGearInfo + yield return new CodeInstruction(OpCodes.Ldarg_3); //thing + yield return new CodeInstruction(OpCodes.Call, GetColorForHauledInfo); //GetColorForHauledInfo(Pawn, Thing) + yield return new CodeInstruction(OpCodes.Call, WidgetsButtonImageColorInfo); //ButtonImage(rect, texture, color) + done = true; + } + else + yield return i; + } + } + + private static Color GetColorForHauled(Pawn pawn, Thing thing) + { + if (pawn.GetComp()?.GetHashSet().Contains(thing) ?? false) + return Color.Lerp(Color.grey, Color.red, 0.5f); + return Color.white; + } + } +} \ No newline at end of file diff --git a/Source/PickUpAndHaulv1.0/IHoldMultipleThings_Support.cs b/Source/PickUpAndHaulv1.0/IHoldMultipleThings_Support.cs new file mode 100644 index 0000000..016682c --- /dev/null +++ b/Source/PickUpAndHaulv1.0/IHoldMultipleThings_Support.cs @@ -0,0 +1,42 @@ +using Verse; + +namespace PickUpAndHaul +{ + using System.Linq; + + public class HoldMultipleThings_Support + { + // ReSharper disable SuspiciousTypeConversion.Global + public static bool CapacityAt(Thing thing, IntVec3 storeCell, Map map, out int capacity) + { + capacity = 0; + + var compOfHolding = (map.haulDestinationManager.SlotGroupParentAt(storeCell) as ThingWithComps)? + .AllComps.FirstOrDefault(x => x is IHoldMultipleThings.IHoldMultipleThings); + + if (compOfHolding is IHoldMultipleThings.IHoldMultipleThings holderOfThings) + return holderOfThings.CapacityAt(thing, storeCell, map, out capacity); + + foreach (Thing t in storeCell.GetThingList(map)) + if (t is IHoldMultipleThings.IHoldMultipleThings holderOfMultipleThings) + return holderOfMultipleThings.CapacityAt(thing, storeCell, map, out capacity); + + return false; + } + + public static bool StackableAt(Thing thing, IntVec3 storeCell, Map map) + { + var compOfHolding = (map.haulDestinationManager.SlotGroupParentAt(storeCell) as ThingWithComps)? + .AllComps.FirstOrDefault(x => x is IHoldMultipleThings.IHoldMultipleThings); + + if (compOfHolding is IHoldMultipleThings.IHoldMultipleThings holderOfThings) + return holderOfThings.StackableAt(thing, storeCell, map); + + foreach (Thing t in storeCell.GetThingList(map)) + if (t is IHoldMultipleThings.IHoldMultipleThings holderOfMultipleThings) + return holderOfMultipleThings.StackableAt(thing, storeCell, map); + + return false; + } + } +} diff --git a/Source/PickUpAndHaulv1.0/JobDriver_HaulToInventory.cs b/Source/PickUpAndHaulv1.0/JobDriver_HaulToInventory.cs new file mode 100644 index 0000000..1cad9b6 --- /dev/null +++ b/Source/PickUpAndHaulv1.0/JobDriver_HaulToInventory.cs @@ -0,0 +1,201 @@ +namespace PickUpAndHaul +{ + using System; + using System.Collections.Generic; + using System.Linq; + using RimWorld; + using UnityEngine; + using Verse; + using Verse.AI; + + public class JobDriver_HaulToInventory : JobDriver + { + public override bool TryMakePreToilReservations(bool errorOnFailed) + { + Log.Message($"{pawn} starting HaulToInventory job: {job.targetQueueA.ToStringSafeEnumerable()}:{job.countQueue.ToStringSafeEnumerable()}"); + pawn.ReserveAsManyAsPossible(job.targetQueueA, job); + pawn.ReserveAsManyAsPossible(job.targetQueueB, job); + return pawn.Reserve(job.targetQueueA[0], job) && pawn.Reserve(job.targetB, job); + } + + //get next, goto, take, check for more. Branches off to "all over the place" + protected override IEnumerable MakeNewToils() + { + CompHauledToInventory takenToInventory = pawn.TryGetComp(); + + Toil wait = Toils_General.Wait(2); + + Toil nextTarget = Toils_JobTransforms.ExtractNextTargetFromQueue(TargetIndex.A); //also does count + yield return nextTarget; + + //honestly the workgiver checks for encumbered, so until CE checks are in this is unnecessary + //yield return CheckForOverencumbered();//Probably redundant without CE checks + + Toil gotoThing = new Toil + { + initAction = () => + { + pawn.pather.StartPath(TargetThingA, PathEndMode.ClosestTouch); + }, + defaultCompleteMode = ToilCompleteMode.PatherArrival + }; + gotoThing.FailOnDespawnedNullOrForbidden(TargetIndex.A); + yield return gotoThing; + + Toil takeThing = new Toil + { + initAction = () => + { + Pawn actor = pawn; + Thing thing = actor.CurJob.GetTarget(TargetIndex.A).Thing; + Toils_Haul.ErrorCheckForCarry(actor, thing); + + //get max we can pick up + int countToPickUp = Mathf.Min(job.count, MassUtility.CountToPickUpUntilOverEncumbered(actor, thing)); + Log.Message($"{actor} is hauling to inventory {thing}:{countToPickUp}"); + + // yo dawg, I heard you like delegates so I put delegates in your delegate, so you can delegate your delegates. + // because compilers don't respect IF statements in delegates and toils are fully iterated over as soon as the job starts. + try + { + ((Action)(() => + { + if (ModCompatibilityCheck.CombatExtendedIsActive) + { + //CombatExtended.CompInventory ceCompInventory = actor.GetComp(); + //ceCompInventory.CanFitInInventory(thing, out countToPickUp); + } + }))(); + } + catch (TypeLoadException) { } + + if (countToPickUp > 0) + { + Thing splitThing = thing.SplitOff(countToPickUp); + bool shouldMerge = takenToInventory.GetHashSet().Any(x => x.def == thing.def); + actor.inventory.GetDirectlyHeldThings().TryAdd(splitThing, shouldMerge); + takenToInventory.RegisterHauledItem(splitThing); + + try + { + ((Action)(() => + { + if (ModCompatibilityCheck.CombatExtendedIsActive) + { + //CombatExtended.CompInventory ceCompInventory = actor.GetComp(); + //ceCompInventory.UpdateInventory(); + } + }))(); + } + catch (TypeLoadException) + { + } + } + + //thing still remains, so queue up hauling if we can + end the current job (smooth/instant transition) + //This will technically release the reservations in the queue, but what can you do + if (thing.Spawned) + { + Job haul = HaulAIUtility.HaulToStorageJob(actor, thing); + if (haul?.TryMakePreToilReservations(actor, false) ?? false) + { + actor.jobs.jobQueue.EnqueueFirst(haul, JobTag.Misc); + } + actor.jobs.curDriver.JumpToToil(wait); + } + } + }; + yield return takeThing; + yield return Toils_Jump.JumpIf(nextTarget, () => !job.targetQueueA.NullOrEmpty()); + + //Find more to haul, in case things spawned while this was in progess + yield return new Toil + { + initAction = () => + { + List haulables = pawn.Map.listerHaulables.ThingsPotentiallyNeedingHauling(); + WorkGiver_HaulToInventory haulMoreWork = DefDatabase.AllDefsListForReading.First(wg => wg.Worker is WorkGiver_HaulToInventory).Worker as WorkGiver_HaulToInventory; + Thing haulMoreThing = GenClosest.ClosestThing_Global(pawn.Position, haulables, 12, t => haulMoreWork.HasJobOnThing(pawn, t)); + + //WorkGiver_HaulToInventory found more work nearby + if (haulMoreThing != null) + { + Log.Message($"{pawn} hauling again : {haulMoreThing}"); + Job haulMoreJob = haulMoreWork.JobOnThing(pawn, haulMoreThing); + + if (haulMoreJob.TryMakePreToilReservations(pawn, false)) + { + pawn.jobs.jobQueue.EnqueueFirst(haulMoreJob, JobTag.Misc); + EndJobWith(JobCondition.Succeeded); + } + } + } + }; + + //maintain cell reservations on the trip back + //TODO: do that when we carry things + //I guess that means TODO: implement carrying the rest of the items in this job instead of falling back on HaulToStorageJob + yield return Toils_Goto.GotoCell(TargetIndex.B, PathEndMode.ClosestTouch); + + yield return new Toil //Queue next job + { + initAction = () => + { + Pawn actor = pawn; + Job curJob = actor.jobs.curJob; + LocalTargetInfo storeCell = curJob.targetB; + + Job unloadJob = new Job(PickUpAndHaulJobDefOf.UnloadYourHauledInventory, storeCell); + if (unloadJob.TryMakePreToilReservations(actor, false)) + { + actor.jobs.jobQueue.EnqueueFirst(unloadJob, JobTag.Misc); + EndJobWith(JobCondition.Succeeded); + //This will technically release the cell reservations in the queue, but what can you do + } + } + }; + yield return wait; + } + + public Toil CheckForOverencumbered() + { + Toil toil = new Toil(); + toil.initAction = delegate + { + Pawn actor = toil.actor; + Job curJob = actor.jobs.curJob; + Thing nextThing = curJob.targetA.Thing; + + //float usedBulkByPct = 1f; + //float usedWeightByPct = 1f; + + //try + //{ + // ((Action)(() => + // { + // if (ModCompatibilityCheck.CombatExtendedIsActive) + // { + // CompInventory ceCompInventory = actor.GetComp(); + // usedWeightByPct = ceCompInventory.currentWeight / ceCompInventory.capacityWeight; + // usedBulkByPct = ceCompInventory.currentBulk / ceCompInventory.capacityBulk; + // } + // }))(); + //} + //catch (TypeLoadException) { } + + + if (!(MassUtility.EncumbrancePercent(actor) <= 0.9f /*|| usedBulkByPct >= 0.7f || usedWeightByPct >= 0.8f*/)) + { + Job haul = HaulAIUtility.HaulToStorageJob(actor, nextThing); + if (haul?.TryMakePreToilReservations(actor, false) ?? false) + { + //note that HaulToStorageJob etc doesn't do opportunistic duplicate hauling for items in valid storage. REEEE + actor.jobs.jobQueue.EnqueueFirst(haul, JobTag.Misc); + EndJobWith(JobCondition.Succeeded); + } + } + }; + return toil; + } + } +} \ No newline at end of file diff --git a/Source/PickUpAndHaulv1.0/JobDriver_UnloadYourHauledInventory.cs b/Source/PickUpAndHaulv1.0/JobDriver_UnloadYourHauledInventory.cs new file mode 100644 index 0000000..309c2e6 --- /dev/null +++ b/Source/PickUpAndHaulv1.0/JobDriver_UnloadYourHauledInventory.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using Verse; +using Verse.AI; +using RimWorld; +using System.Linq; + +namespace PickUpAndHaul +{ + public class JobDriver_UnloadYourHauledInventory : JobDriver + { + private int countToDrop = -1; + private int unloadDuration = 3; + + public override void ExposeData() + { + base.ExposeData(); + Scribe_Values.Look(ref countToDrop, "countToDrop", -1); + } + + public override bool TryMakePreToilReservations(bool errorOnFailed) + => true; + + /// + /// Find spot, reserve spot, pull thing out of inventory, go to spot, drop stuff, repeat. + /// + /// + protected override IEnumerable MakeNewToils() + { + CompHauledToInventory takenToInventory = pawn.TryGetComp(); + HashSet carriedThing = takenToInventory.GetHashSet(); + + if (ModCompatibilityCheck.ExtendedStorageIsActive) + unloadDuration = 20; + + Toil wait = Toils_General.Wait(unloadDuration); + Toil celebrate = Toils_General.Wait(unloadDuration); + + yield return wait; + Toil findSpot = new Toil + { + initAction = () => + { + ThingCount unloadableThing = FirstUnloadableThing(pawn); + + if (unloadableThing.Count == 0 && carriedThing.Count == 0) + EndJobWith(JobCondition.Succeeded); + + if (unloadableThing.Count != 0) + { + //StoragePriority currentPriority = StoreUtility.StoragePriorityAtFor(pawn.Position, unloadableThing.Thing); + if (!StoreUtility.TryFindStoreCellNearColonyDesperate(unloadableThing.Thing, pawn, out IntVec3 c)) + { + pawn.inventory.innerContainer.TryDrop(unloadableThing.Thing, ThingPlaceMode.Near, unloadableThing.Thing.stackCount, out Thing _); + EndJobWith(JobCondition.Succeeded); + } + else + { + job.SetTarget(TargetIndex.A, unloadableThing.Thing); + job.SetTarget(TargetIndex.B, c); + countToDrop = unloadableThing.Thing.stackCount; + } + } + } + }; + yield return findSpot; + + yield return Toils_Reserve.Reserve(TargetIndex.B); + + yield return new Toil + { + initAction = delegate + { + Thing thing = job.GetTarget(TargetIndex.A).Thing; + if (thing == null || !pawn.inventory.innerContainer.Contains(thing)) + { + carriedThing.Remove(thing); + pawn.jobs.curDriver.JumpToToil(wait); + return; + } + if (!pawn.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation) || !thing.def.EverStorable(false)) + { + pawn.inventory.innerContainer.TryDrop(thing, ThingPlaceMode.Near, countToDrop, out thing); + EndJobWith(JobCondition.Succeeded); + carriedThing.Remove(thing); + } + else + { + pawn.inventory.innerContainer.TryTransferToContainer(thing, pawn.carryTracker.innerContainer, countToDrop, out thing); + job.count = countToDrop; + job.SetTarget(TargetIndex.A, thing); + carriedThing.Remove(thing); + } + try + { + ((Action)(() => + { + if (ModCompatibilityCheck.CombatExtendedIsActive) + { + //CombatExtended.CompInventory ceCompInventory = pawn.GetComp(); + //ceCompInventory.UpdateInventory(); + } + }))(); + } + catch (TypeLoadException) { } + thing.SetForbidden(false, false); + } + }; + + Toil carryToCell = Toils_Haul.CarryHauledThingToCell(TargetIndex.B); + yield return Toils_Goto.GotoCell(TargetIndex.B, PathEndMode.Touch); + yield return carryToCell; + yield return Toils_Haul.PlaceHauledThingInCell(TargetIndex.B, carryToCell, true); + + //If the original cell is full, PlaceHauledThingInCell will set a different TargetIndex resulting in errors on yield return Toils_Reserve.Release. + //We still gotta release though, mostly because of Extended Storage. + Toil releaseReservation = new Toil + { + initAction = () => + { + if (pawn.Map.reservationManager.ReservedBy(job.targetB, pawn, pawn.CurJob) + && !ModCompatibilityCheck.HCSKIsActive) + pawn.Map.reservationManager.Release(job.targetB, pawn, pawn.CurJob); + } + }; + yield return releaseReservation; + yield return Toils_Jump.Jump(wait); + yield return celebrate; + } + + private static ThingCount FirstUnloadableThing(Pawn pawn) + { + CompHauledToInventory itemsTakenToInventory = pawn.TryGetComp(); + HashSet carriedThings = itemsTakenToInventory.GetHashSet(); + + //find the overlap. + IEnumerable potentialThingsToUnload = + from t in pawn.inventory.innerContainer + where carriedThings.Contains(t) + select t; + + foreach (Thing thing in carriedThings.OrderBy(t => t.def.FirstThingCategory?.index)) + { + //merged partially picked up stacks get a different thingID in inventory + if (!potentialThingsToUnload.Contains(thing)) + { + ThingDef stragglerDef = thing.def; + //we have no method of grabbing the newly generated thingID. This is the solution to that. + IEnumerable dirtyStragglers = + from straggler in pawn.inventory.innerContainer + where straggler.def == stragglerDef + select straggler; + + carriedThings.Remove(thing); + + foreach (Thing dirtyStraggler in dirtyStragglers) + return new ThingCount(dirtyStraggler, dirtyStraggler.stackCount); + } + return new ThingCount(thing, thing.stackCount); + } + return default(ThingCount); + } + } +} \ No newline at end of file diff --git a/Source/PickUpAndHaulv1.0/ModCompatibilityCheck.cs b/Source/PickUpAndHaulv1.0/ModCompatibilityCheck.cs new file mode 100644 index 0000000..c640ce6 --- /dev/null +++ b/Source/PickUpAndHaulv1.0/ModCompatibilityCheck.cs @@ -0,0 +1,22 @@ +using System.Linq; +using Verse; + +namespace PickUpAndHaul +{ + public static class ModCompatibilityCheck + { + public static bool CombatExtendedIsActive + => ModsConfig.ActiveModsInLoadOrder.Any(m => m.Name == "Combat Extended"); + + public static bool AllowToolIsActive + => ModsConfig.ActiveModsInLoadOrder.Any(m => m.Name == "Allow Tool"); + + public static bool ExtendedStorageIsActive + => ModsConfig.ActiveModsInLoadOrder.Any(m => m.Name == "ExtendedStorageFluffyHarmonised") + || ModsConfig.ActiveModsInLoadOrder.Any(m => m.Name == "Extended Storage") + || ModsConfig.ActiveModsInLoadOrder.Any(m => m.Name == "Core SK"); + + public static bool HCSKIsActive + => ModsConfig.ActiveModsInLoadOrder.Any(m => m.Name == "Core SK"); + } +} diff --git a/Source/PickUpAndHaulv1.0/PawnUnloadChecker.cs b/Source/PickUpAndHaulv1.0/PawnUnloadChecker.cs new file mode 100644 index 0000000..47ef7f2 --- /dev/null +++ b/Source/PickUpAndHaulv1.0/PawnUnloadChecker.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using RimWorld; +using Verse; +using Verse.AI; + +namespace PickUpAndHaul +{ + public class PawnUnloadChecker + { + public static void CheckIfPawnShouldUnloadInventory(Pawn pawn, bool forced = false) + { + Job job = new Job(PickUpAndHaulJobDefOf.UnloadYourHauledInventory, pawn); + CompHauledToInventory itemsTakenToInventory = pawn.TryGetComp(); + + if (itemsTakenToInventory == null) + return; + + HashSet carriedThing = itemsTakenToInventory.GetHashSet(); + + if (pawn.Faction != Faction.OfPlayer || !pawn.RaceProps.Humanlike) + return; + if (carriedThing == null || carriedThing.Count == 0 || pawn.inventory.innerContainer.Count == 0) + return; + + if (forced) + { + if (job.TryMakePreToilReservations(pawn, false)) + { + pawn.jobs.jobQueue.EnqueueFirst(job, JobTag.Misc); + return; + } + } + + if (MassUtility.EncumbrancePercent(pawn) >= 0.90f || carriedThing.Count >= 1) + { + if (job.TryMakePreToilReservations(pawn, false)) + { + pawn.jobs.jobQueue.EnqueueFirst(job, JobTag.Misc); + return; + } + } + + if (pawn.inventory.innerContainer?.Count >= 1) + { + foreach (Thing rottable in pawn.inventory.innerContainer) + { + CompRottable compRottable = rottable.TryGetComp(); + + if (compRottable?.TicksUntilRotAtCurrentTemp < 30000) + { + pawn.jobs.jobQueue.EnqueueFirst(job, JobTag.Misc); + return; + } + } + } + + if (Find.TickManager.TicksGame % 50 == 0 && pawn.inventory.innerContainer.Count < carriedThing.Count) + { + Verse.Log.Warning("[PickUpAndHaul] " + pawn + " inventory was found out of sync with haul index. Pawn will drop their inventory."); + carriedThing.Clear(); + pawn.inventory.UnloadEverything = true; + } + } + } + + [DefOf] + public static class PickUpAndHaulJobDefOf + { + public static JobDef UnloadYourHauledInventory; + public static JobDef HaulToInventory; + } +} \ No newline at end of file diff --git a/Source/PickUpAndHaulv1.0/PickUpAndHaulv10.csproj b/Source/PickUpAndHaulv1.0/PickUpAndHaulv10.csproj new file mode 100644 index 0000000..747532d --- /dev/null +++ b/Source/PickUpAndHaulv1.0/PickUpAndHaulv10.csproj @@ -0,0 +1,74 @@ + + + + + Debug + AnyCPU + {2480F13E-E9ED-44EC-A5DE-C821676904E7} + Library + Properties + PickUpAndHaul + PickUpAndHaul + v3.5 + 512 + + + false + none + true + ..\..\Assemblies\ + DEBUG;TRACE + prompt + 4 + + + none + true + ..\..\Assemblies\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\..\..\..\..\..\Users\Maniak\Documents\RimWorld Mods\0Harmony.dll + + + ..\..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll + False + + + ..\..\..\..\..\..\workshop\content\294100\731732064\Assemblies\ExtendedStorage.dll + False + + + False + ..\..\Assemblies\IHoldMultipleThings.dll + + + + + + + + + ..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.dll + False + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/PickUpAndHaulv1.0/Properties/AssemblyInfo.cs b/Source/PickUpAndHaulv1.0/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7dab455 --- /dev/null +++ b/Source/PickUpAndHaulv1.0/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PickUpAndHaul")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PickUpAndHaul")] +[assembly: AssemblyCopyright("Copyright © Mehni 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ccf8350b-e3fd-4693-9209-681e9c089097")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.1.0.5")] +[assembly: AssemblyFileVersion("0.1.0.853")] diff --git a/Source/PickUpAndHaulv1.0/WorkGiver_HaulToInventory.cs b/Source/PickUpAndHaulv1.0/WorkGiver_HaulToInventory.cs new file mode 100644 index 0000000..18979d0 --- /dev/null +++ b/Source/PickUpAndHaulv1.0/WorkGiver_HaulToInventory.cs @@ -0,0 +1,348 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using RimWorld; +using Verse; +using Verse.AI; + +namespace PickUpAndHaul +{ + public class WorkGiver_HaulToInventory : WorkGiver_HaulGeneral + { + //Thanks to AlexTD for the more dynamic search range + //And queueing + //And optimizing + private const float SEARCH_FOR_OTHERS_RANGE_FRACTION = 0.5f; + + public override bool ShouldSkip(Pawn pawn, bool forced = false) => base.ShouldSkip(pawn, forced) + || pawn.Faction != Faction.OfPlayer + || !pawn.RaceProps.Humanlike + || pawn.TryGetComp() == null; + + public static bool GoodThingToHaul(Thing t, Pawn pawn) => t.Spawned + && !t.IsInValidBestStorage() + && !t.IsForbidden(pawn) + && !(t is Corpse) + && pawn.CanReserve(t); + + public override bool HasJobOnThing(Pawn pawn, Thing thing, bool forced = false) + { + //bulky gear (power armor + minigun) so don't bother. + if (MassUtility.GearMass(pawn) / MassUtility.Capacity(pawn) >= 0.8f) + return false; + + if (!GoodThingToHaul(thing, pawn) || !HaulAIUtility.PawnCanAutomaticallyHaulFast(pawn, thing, forced)) + return false; + + StoragePriority currentPriority = StoreUtility.CurrentStoragePriorityOf(thing); + return StoreUtility.TryFindBestBetterStoreCellFor(thing, pawn, pawn.Map, currentPriority, pawn.Faction, out IntVec3 _); + } + + //pick up stuff until you can't anymore, + //while you're up and about, pick up something and haul it + //before you go out, empty your pockets + + public override Job JobOnThing(Pawn pawn, Thing thing, bool forced = false) + { + //bulky gear (power armor + minigun) so don't bother. + if (MassUtility.GearMass(pawn) / MassUtility.Capacity(pawn) >= 0.8f) + return null; + + DesignationDef haulUrgentlyDesignation = DefDatabase.GetNamed("HaulUrgentlyDesignation", false); + + //This WorkGiver gets hijacked by AllowTool and expects us to urgently haul corpses. + if (ModCompatibilityCheck.AllowToolIsActive && thing is Corpse + && pawn.Map.designationManager.DesignationOn(thing)?.def == haulUrgentlyDesignation && HaulAIUtility.PawnCanAutomaticallyHaulFast(pawn, thing, forced)) + return HaulAIUtility.HaulToStorageJob(pawn, thing); + + if (!GoodThingToHaul(thing, pawn) || !HaulAIUtility.PawnCanAutomaticallyHaulFast(pawn, thing, forced)) + return null; + + StoragePriority currentPriority = StoreUtility.CurrentStoragePriorityOf(thing); + if (StoreUtility.TryFindBestBetterStoreCellFor(thing, pawn, pawn.Map, currentPriority, pawn.Faction, out IntVec3 storeCell, true)) + { + //since we've gone through all the effort of getting the loc, might as well use it. + //Don't multi-haul food to hoppers. + if (thing.def.IsNutritionGivingIngestible) + { + if (thing.def.ingestible.preferability == FoodPreferability.RawBad || thing.def.ingestible.preferability == FoodPreferability.RawTasty) + { + List thingList = storeCell.GetThingList(thing.Map); + + foreach (Thing t in thingList) + if (t.def == ThingDefOf.Hopper) + return HaulAIUtility.HaulToStorageJob(pawn, thing); + } + } + } + else + { + JobFailReason.Is("NoEmptyPlaceLower".Translate()); + return null; + } + + //https://github.com/Mehni/PickUpAndHaul/pull/18 + if (MassUtility.WillBeOverEncumberedAfterPickingUp(pawn, thing, 1)) + return HaulAIUtility.HaulToStorageJob(pawn, thing); + + //credit to Dingo + int capacityStoreCell = CapacityAt(thing, storeCell, pawn.Map); + + if (capacityStoreCell == 0) return HaulAIUtility.HaulToStorageJob(pawn, thing); + + Job job = new Job(PickUpAndHaulJobDefOf.HaulToInventory, null, storeCell); //Things will be in queues + Log.Message($"-------------------------------------------------------------------"); + Log.Message($"------------------------------------------------------------------");//different size so the log doesn't count it 2x + Log.Message($"{pawn} job found to haul: {thing} to {storeCell}:{capacityStoreCell}, looking for more now"); + + //Find extra things than can be hauled to inventory, queue to reserve them + bool isUrgent = ModCompatibilityCheck.AllowToolIsActive && pawn.Map.designationManager.DesignationOn(thing)?.def == haulUrgentlyDesignation; + + Func validatorExtra = (Thing t) => + (!isUrgent || pawn.Map.designationManager.DesignationOn(t)?.def == haulUrgentlyDesignation) && + GoodThingToHaul(t, pawn) && HaulAIUtility.PawnCanAutomaticallyHaulFast(pawn, t, false);//forced is false, may differ from first thing + + + //Find what fits in inventory, set nextThingLeftOverCount to be + int nextThingLeftOverCount = 0; + float encumberance = MassUtility.EncumbrancePercent(pawn); + job.targetQueueA = new List(); //more things + job.targetQueueB = new List(); //more storage; keep in mind the job doesn't use it, but reserve it so you don't over-haul + job.countQueue = new List();//thing counts + + + //TODO check CE along with encumberance + //float usedBulkByPct = 1f; + //float usedWeightByPct = 1f; + + //try + //{ + // ((Action)(() => + // { + // if (ModCompatibilityCheck.CombatExtendedIsActive) + // { + // CombatExtended.CompInventory ceCompInventory = pawn.GetComp(); + // usedWeightByPct = ceCompInventory.currentWeight / ceCompInventory.capacityWeight; + // usedBulkByPct = ceCompInventory.currentBulk / ceCompInventory.capacityBulk; + // } + // }))(); + //} + //catch (TypeLoadException) { } + + float distanceToHaul = (storeCell - thing.Position).LengthHorizontal * SEARCH_FOR_OTHERS_RANGE_FRACTION; + float distanceToSearchMore = Math.Max(12f, distanceToHaul); + + List haulables = pawn.Map.listerHaulables.ThingsPotentiallyNeedingHauling() + .Where(validatorExtra).ToList(); + + Thing nextThing = thing; + Thing lastThing = thing; + + Dictionary storeCellCapacity = new Dictionary + { + [storeCell] = new CellAllocation(nextThing, capacityStoreCell) + }; + skipCells = new HashSet() { storeCell }; + + do + { + haulables.Remove(nextThing); + if (AllocateThingAtCell(storeCellCapacity, pawn, nextThing, job)) + { + lastThing = nextThing; + encumberance += AddedEnumberance(pawn, nextThing); + + if (encumberance > 1)// || usedBulkByPct >= 0.7f || usedWeightByPct >= 0.8f))//TODO: CE also + { + //can't CountToPickUpUntilOverEncumbered here, pawn doesn't actually hold these things yet + nextThingLeftOverCount = CountPastCapacity(pawn, nextThing, encumberance); + Log.Message($"Inventory allocated, will carry {nextThing}:{nextThingLeftOverCount}"); + break; + } + } + } + while ((nextThing = GenClosest.ClosestThingReachable(lastThing.Position, thing.Map, ThingRequest.ForUndefined(), + PathEndMode.ClosestTouch, TraverseParms.For(pawn), distanceToSearchMore, null, haulables)) + is Thing); + + if (nextThing == null) + { + skipCells = null; + return job; + } + + //Find what can be carried + //this doesn't actually get pickupandhauled, but will hold the reservation so others don't grab what this pawn can carry + haulables.RemoveAll(t => !t.CanStackWith(nextThing)); + + int carryCapacity = pawn.carryTracker.MaxStackSpaceEver(nextThing.def) - nextThingLeftOverCount; + if (carryCapacity == 0) + { + Log.Message("Can't carry more, nevermind!"); + skipCells = null; + return job; + } + Log.Message($"Looking for more like {nextThing}"); + + while ((nextThing = GenClosest.ClosestThingReachable(nextThing.Position, thing.Map, ThingRequest.ForUndefined(), + PathEndMode.ClosestTouch, TraverseParms.For(pawn), 8f, null, haulables)) != null) + { + haulables.Remove(nextThing); + carryCapacity -= nextThing.stackCount; + + if (AllocateThingAtCell(storeCellCapacity, pawn, nextThing, job)) + break; + + if (carryCapacity <= 0) + { + int lastCount = job.countQueue.Pop() + carryCapacity; + job.countQueue.Add(lastCount); + Log.Message($"Nevermind, last count is {lastCount}"); + break; + } + } + + skipCells = null; + return job; + } + + public class CellAllocation + { + public Thing allocated; + public int capacity; + + public CellAllocation(Thing a, int c) + { + allocated = a; + capacity = c; + } + } + + public static int CapacityAt(Thing thing, IntVec3 storeCell, Map map) + { + int capacity; + + if (HoldMultipleThings_Support.CapacityAt(thing, storeCell, map, out capacity)) + { + Log.Message($"Found external capacity of {capacity}"); + return capacity; + } + + if (ExtendedStorage_Support.CapacityAt(thing, storeCell, map, out capacity)) + { + return capacity; + } + + capacity = thing.def.stackLimit; + + Thing preExistingThing = map.thingGrid.ThingAt(storeCell, thing.def); + if (preExistingThing != null) + capacity = thing.def.stackLimit - preExistingThing.stackCount; + + return capacity; + } + + public static bool Stackable(Thing nextThing, KeyValuePair allocation) + => nextThing == allocation.Value.allocated + || allocation.Value.allocated.CanStackWith(nextThing) + || HoldMultipleThings_Support.StackableAt(nextThing, allocation.Key, nextThing.Map) + || ExtendedStorage_Support.StackableAt(nextThing.def, allocation.Key, nextThing.Map); + + public static bool AllocateThingAtCell(Dictionary storeCellCapacity, Pawn pawn, Thing nextThing, Job job) + { + Map map = pawn.Map; + KeyValuePair allocation = storeCellCapacity.FirstOrDefault(kvp => + kvp.Key.GetSlotGroup(map).parent.Accepts(nextThing) && + Stackable(nextThing, kvp)); + IntVec3 storeCell = allocation.Key; + + //Can't stack with allocated cells, find a new cell: + if (storeCell == default(IntVec3)) + { + StoragePriority currentPriority = StoreUtility.CurrentStoragePriorityOf(nextThing); + if (TryFindBestBetterStoreCellFor(nextThing, pawn, map, currentPriority, pawn.Faction, out IntVec3 nextStoreCell)) + { + storeCell = nextStoreCell; + job.targetQueueB.Add(storeCell); + + storeCellCapacity[storeCell] = new CellAllocation(nextThing, CapacityAt(nextThing, storeCell, map)); + + Log.Message($"New cell for unstackable {nextThing} = {nextStoreCell}"); + } + else + { + Log.Message($"{nextThing} can't stack with allocated cells"); + + if (job.targetQueueA.NullOrEmpty()) + job.targetQueueA.Add(nextThing); + return false; + } + } + + job.targetQueueA.Add(nextThing); + int count = nextThing.stackCount; + storeCellCapacity[storeCell].capacity -= count; + Log.Message($"{pawn} allocating {nextThing}:{count}, now {storeCell}:{storeCellCapacity[storeCell].capacity}"); + + while (storeCellCapacity[storeCell].capacity <= 0) + { + int capacityOver = -storeCellCapacity[storeCell].capacity; + storeCellCapacity.Remove(storeCell); + + Log.Message($"{pawn} overdone {storeCell} by {capacityOver}"); + + if (capacityOver == 0) + break; //don't find new cell, might not have more of this thing to haul + + StoragePriority currentPriority = StoreUtility.CurrentStoragePriorityOf(nextThing); + if (TryFindBestBetterStoreCellFor(nextThing, pawn, map, currentPriority, pawn.Faction, out IntVec3 nextStoreCell)) + { + storeCell = nextStoreCell; + job.targetQueueB.Add(storeCell); + + int capacity = CapacityAt(nextThing, storeCell, map) - capacityOver; + storeCellCapacity[storeCell] = new CellAllocation(nextThing, capacity); + + Log.Message($"New cell {storeCell}:{capacity}, allocated extra {capacityOver}"); + } + else + { + count -= capacityOver; + job.countQueue.Add(count); + Log.Message($"Nowhere else to store, allocated {nextThing}:{count}"); + return false; + } + } + job.countQueue.Add(count); + Log.Message($"{nextThing}:{count} allocated"); + return true; + } + + public static HashSet skipCells; + public static bool TryFindBestBetterStoreCellFor(Thing thing, Pawn carrier, Map map, StoragePriority currentPriority, Faction faction, out IntVec3 foundCell) + { + foreach (SlotGroup slotGroup in map.haulDestinationManager.AllGroupsListInPriorityOrder + .Where(s => s.Settings.Priority > currentPriority && s.parent.Accepts(thing))) + { + if (slotGroup.CellsList.Except(skipCells).FirstOrDefault(c => StoreUtility.IsGoodStoreCell(c, map, thing, carrier, faction)) is IntVec3 cell + && cell != default(IntVec3)) + { + foundCell = cell; + + skipCells.Add(cell); + + return true; + } + } + foundCell = IntVec3.Invalid; + return false; + } + + public static float AddedEnumberance(Pawn pawn, Thing thing) + => thing.stackCount * thing.GetStatValue(StatDefOf.Mass) / MassUtility.Capacity(pawn); + + public static int CountPastCapacity(Pawn pawn, Thing thing, float encumberance) + => (int)Math.Ceiling((encumberance - 1) * MassUtility.Capacity(pawn) / thing.GetStatValue(StatDefOf.Mass)); + } +}