From 2b95ab32f29ab286c120c8420a0a7c350be00034 Mon Sep 17 00:00:00 2001 From: Jaxe-Dev <42095078+Jaxe-Dev@users.noreply.github.com> Date: Sun, 16 Sep 2018 01:53:24 +0800 Subject: [PATCH] v1.1.3 - More fixes to animal training food handling - Added support for non-Latin characters in preset names --- About/About.xml | 2 +- About/ModSync.xml | 2 +- Defs/UpdateFeaturesDef/UpdateFeatures.xml | 8 +-- README.md | 4 +- Source/Data/Persistent.cs | 3 +- Source/Data/Presetable.cs | 5 +- Source/Data/Registry.cs | 18 +++--- Source/Interface/Dialog_Global.cs | 2 +- Source/Interface/Dialog_Plans.cs | 2 +- Source/Interface/WindowPlus.cs | 1 - Source/Mod.cs | 23 ++++---- Source/Patch/Access.cs | 2 - Source/Patch/Extensions.cs | 4 +- ...imWorld_FoodUtility_BestFoodInInventory.cs | 12 +++- ...imWorld_FoodUtility_BestFoodSourceOnMap.cs | 16 +++-- ...ld_FoodUtility_TryFindBestFoodSourceFor.cs | 4 +- ...obDriver_InteractAnimal_StartFeedAnimal.cs | 58 +++++++++++++++++++ ...JobGiver_PackFood_IsGoodPackableFoodFor.cs | 6 +- ...imWorld_JoyGiver_Ingest_CanIngestForJoy.cs | 3 +- ...mWorld_Pawn_GuestTracker_SetGuestStatus.cs | 4 +- ..._InteractAnimal_HasFoodToInteractAnimal.cs | 50 ++++++++++++++++ ...actAnimal_TakeFoodForAnimalInteractJob.cs} | 2 +- Source/PawnRules.csproj | 6 +- Source/Properties/AssemblyInfo.cs | 2 +- 24 files changed, 175 insertions(+), 64 deletions(-) create mode 100644 Source/Patch/RimWorld_JobDriver_InteractAnimal_StartFeedAnimal.cs create mode 100644 Source/Patch/RimWorld_WorkGiver_InteractAnimal_HasFoodToInteractAnimal.cs rename Source/Patch/{RimWorld_WorkGiver_InteractAnimal.cs => RimWorld_WorkGiver_InteractAnimal_TakeFoodForAnimalInteractJob.cs} (93%) diff --git a/About/About.xml b/About/About.xml index b3166ff..7136ebb 100644 --- a/About/About.xml +++ b/About/About.xml @@ -4,6 +4,6 @@ Pawn Rules Jaxe 0.19.0 - Mod Version: 1.1.2\n\n\nPawn Rules is a mod that allows custom rules to be assigned individually to your colonists, animals, guests and prisoners.\n\nCurrently the following rules can be applied:\n\n- Disallow certain foods\n- Disallow bonding with certain animals\n- Disallow new romances\n- Disallow constructing items that have a quality level\n\nAny of these rules can be disabled and hidden from the rules window. Rules presets and defaults can be imported and exported between games. + Mod Version: 1.1.3\n\n\nPawn Rules is a mod that allows custom rules to be assigned individually to your colonists, animals, guests and prisoners.\n\nCurrently the following rules can be applied:\n\n- Disallow certain foods\n- Disallow bonding with certain animals\n- Disallow new romances\n- Disallow constructing items that have a quality level\n\nAny of these rules can be disabled and hidden from the rules window. Rules presets and defaults can be imported and exported between games. https://github.com/Jaxe-Dev/PawnRules diff --git a/About/ModSync.xml b/About/ModSync.xml index 765d319..e3d5ad1 100644 --- a/About/ModSync.xml +++ b/About/ModSync.xml @@ -3,7 +3,7 @@ 59f538ed-f86d-4506-a4a5-7e9faaa37508 Pawn Rules - v1.1.2 + v1.1.3 False Jaxe-Dev diff --git a/Defs/UpdateFeaturesDef/UpdateFeatures.xml b/Defs/UpdateFeaturesDef/UpdateFeatures.xml index 8826a2b..b525e81 100644 --- a/Defs/UpdateFeaturesDef/UpdateFeatures.xml +++ b/Defs/UpdateFeaturesDef/UpdateFeatures.xml @@ -10,13 +10,7 @@ PawnRules_1_1_1 1.1.1 - - Re-fixed "not allowed artisan builds" tooltip\n\n- Fixed "Could not find player faction" error on new game\n\n- Fixed ability to train animals if an animal has a food policy set\n\n- New Global option: Allow food if malnourished (if a pawn is is suffering from malnutrition they will ignore all food rules) [Default: False]\n\n- New Global option: Allow food if training (if an animal is being trained they ignore all food rules) [Default: False]\n\n- Plans can be imported/exported between games. A plan consists of all rule presets and defaults bundled into one file. - - - - PawnRules_1_1_2 - 1.1.2 - - Fixed null reference error when a pawn without rules attempts to build + - New Global option: Allow food if malnourished (if a pawn is is suffering from malnutrition they will ignore all food rules) [Default: False]\n\n- New Global option: Allow food if training (if an animal is being trained they ignore all food rules) [Default: False]\n\n- Plans can be imported/exported between games. A plan consists of all rule presets and defaults bundled into one file. diff --git a/README.md b/README.md index ed86ef4..b0e527b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Pawn Rules -![](https://img.shields.io/badge/Mod_Version-1.1.2-blue.svg) +![](https://img.shields.io/badge/Mod_Version-1.1.3-blue.svg) ![](https://img.shields.io/badge/Built_for_RimWorld-B19-blue.svg) ![](https://img.shields.io/badge/Powered_by_Harmony-1.2.0.1-blue.svg) @@ -53,11 +53,13 @@ Prefix : RimWorld.FoodUtility.TryFindBestFoodSourceFor Postfix : RimWorld.GenConstruct.CanConstruct Prefix : RimWorld.InteractionWorker_RomanceAttempt.RandomSelectionWeight Prefix : RimWorld.InteractionWorker_RomanceAttempt.SuccessChance +Prefix : RimWorld.JobDriver_InteractAnimal.StartFeedAnimal Postfix : RimWorld.JobGiver_PackFood.IsGoodPackableFoodFor Prefix : RimWorld.JoyGiver_Ingest.CanIngestForJoy Prefix : RimWorld.Pawn_GuestTracker.SetGuestStatus Postfix : RimWorld.PawnUtility.TrySpawnHatchedOrBornPawn Prefix : RimWorld.RelationsUtility.TryDevelopBondRelation +Prefix : RimWorld.WorkGiver_InteractAnimal.HasFoodToInteractAnimal Prefix : RimWorld.WorkGiver_InteractAnimal.TakeFoodForAnimalInteractJob Postfix : Verse.Game.FinalizeInit Prefix : Verse.Game.InitNewGame diff --git a/Source/Data/Persistent.cs b/Source/Data/Persistent.cs index 17f5074..3d8567a 100644 --- a/Source/Data/Persistent.cs +++ b/Source/Data/Persistent.cs @@ -15,7 +15,7 @@ internal static class Persistent private const string ExportsExtension = ".xml"; private const string ExportPrefix = "Plan_"; - private static readonly Regex ValidNameRegex = new Regex("^(?:[a-zA-Z0-9_\\-]|[a-zA-Z0-9_\\-]+[a-zA-Z0-9_\\- ]*[a-zA-Z0-9_\\-]+)$"); + private static readonly Regex ValidNameRegex = new Regex("^(?:[\\p{L}\\p{N}_\\-]|[\\p{L}\\p{N}_\\-]+[\\p{L}\\p{N}_\\- ]*[\\p{L}\\p{N}_\\-]+)$"); private static readonly DirectoryInfo ExportsDirectory = Mod.ConfigDirectory.CreateSubdirectory(ExportsDirectoryName); @@ -28,7 +28,6 @@ public static void DeletePlan(string name) { var file = GetPlanFile(name); - Mod.Warning($"Delete => {file.FullName}"); if (!file.Exists) { return; } file.Delete(); diff --git a/Source/Data/Presetable.cs b/Source/Data/Presetable.cs index fe0c9fc..82b84a3 100644 --- a/Source/Data/Presetable.cs +++ b/Source/Data/Presetable.cs @@ -2,6 +2,7 @@ using System.Text.RegularExpressions; using System.Xml.Linq; using PawnRules.Interface; +using PawnRules.Patch; using Verse; namespace PawnRules.Data @@ -12,7 +13,7 @@ internal abstract class Presetable : IExposable, ILoadReferenceable public static readonly string VoidName = Lang.Get("Preset.None"); - private static readonly Regex ValidNameRegex = new Regex("^(?:[a-zA-Z0-9]|[a-zA-Z0-9]+[a-zA-Z0-9 ]*[a-zA-Z0-9]+)$"); + private static readonly Regex ValidNameRegex = new Regex("^(?:[\\p{L}\\p{N}]|[\\p{L}\\p{N}]+[\\p{L}\\p{N} ]*[\\p{L}\\p{N}])$"); private static int _count; protected readonly int Id; @@ -54,7 +55,7 @@ public static void SetName(T preset, Action onRename) where T : Presetable var localPreset = preset; void OnCommit(string name) => onRename(Registry.RenamePreset(localPreset, name)); - Dialog_SetName.Open(Lang.Get("Dialog_SetName.PresetTitle", preset.Name), Lang.Get("Dialog_SetName.PresetLabel"), OnCommit, name => NameIsValid(preset.Type, name), preset.Name); + Dialog_SetName.Open(Lang.Get("Dialog_SetName.PresetTitle", preset.Name.Bold()), Lang.Get("Dialog_SetName.PresetLabel"), OnCommit, name => NameIsValid(preset.Type, name), preset.Name); } public static bool NameIsValid(IPresetableType type, string name) => (name.Length <= MaxIdLength) && !string.Equals(name, Lang.Get("Preset.None"), StringComparison.OrdinalIgnoreCase) && !string.Equals(name, Lang.Get("Preset.Personalized"), StringComparison.OrdinalIgnoreCase) && ValidNameRegex.IsMatch(name) && !Registry.PresetNameExists(type, name); diff --git a/Source/Data/Registry.cs b/Source/Data/Registry.cs index 3a18d76..8cb2e82 100644 --- a/Source/Data/Registry.cs +++ b/Source/Data/Registry.cs @@ -23,14 +23,16 @@ internal class Registry : WorldObject private static bool _isDeactivating; - public static bool AllowEmergencyFood { get => _instance._allowEmergencyFood; set => _instance._allowEmergencyFood = value; } - public static bool AllowTrainingFood { get => _instance._allowTrainingFood; set => _instance._allowTrainingFood = value; } public static bool ShowFoodPolicy { get => _instance._showFoodPolicy; set => _instance._showFoodPolicy = value; } public static bool ShowBondingPolicy { get => _instance._showBondingPolicy; set => _instance._showBondingPolicy = value; } public static bool ShowAllowCourting { get => _instance._showAllowCourting; set => _instance._showAllowCourting = value; } public static bool ShowAllowArtisan { get => _instance._showAllowArtisan; set => _instance._showAllowArtisan = value; } + public static bool AllowEmergencyFood { get => _instance._allowEmergencyFood; set => _instance._allowEmergencyFood = value; } + public static bool AllowTrainingFood { get => _instance._allowTrainingFood; set => _instance._allowTrainingFood = value; } + public static Pawn ExemptedTrainer { get; set; } + private string _loadedVersion; private readonly Dictionary> _voidPresets = new Dictionary>(); @@ -42,14 +44,14 @@ internal class Registry : WorldObject private List _savedBindings = new List(); private List _savedDefaults = new List(); - private bool _allowEmergencyFood; - private bool _allowTrainingFood; - private bool _showFoodPolicy = true; private bool _showBondingPolicy = true; private bool _showAllowCourting = true; private bool _showAllowArtisan = true; + private bool _allowEmergencyFood; + private bool _allowTrainingFood; + public static void Initialize() { var worldObjects = Current.Game.World.worldObjects; @@ -316,14 +318,14 @@ public override void ExposeData() _savedBindings.AddRange(_rules.Where(rules => rules.Key.CanHaveRules()).Select(rules => new Binding(rules.Key, rules.Value.IsIgnored() ? null : rules.Value)).ToArray()); } - Scribe_Values.Look(ref _allowEmergencyFood, "allowEmergencyFood"); - Scribe_Values.Look(ref _allowTrainingFood, "allowTrainingFood"); - Scribe_Values.Look(ref _showFoodPolicy, "showFoodPolicy", true); Scribe_Values.Look(ref _showBondingPolicy, "showBondingPolicy", true); Scribe_Values.Look(ref _showAllowCourting, "showAllowCourting", true); Scribe_Values.Look(ref _showAllowArtisan, "showAllowArtisan", true); + Scribe_Values.Look(ref _allowEmergencyFood, "allowEmergencyFood"); + Scribe_Values.Look(ref _allowTrainingFood, "allowTrainingFood"); + Scribe_Collections.Look(ref _savedPresets, "presets", LookMode.Deep); Scribe_Collections.Look(ref _savedBindings, "bindings", LookMode.Deep); Scribe_Collections.Look(ref _savedDefaults, "defaults", LookMode.Deep); diff --git a/Source/Interface/Dialog_Global.cs b/Source/Interface/Dialog_Global.cs index 207df76..9efea8e 100644 --- a/Source/Interface/Dialog_Global.cs +++ b/Source/Interface/Dialog_Global.cs @@ -7,7 +7,7 @@ namespace PawnRules.Interface { internal class Dialog_Global : WindowPlus { - private Dialog_Global() : base(Lang.Get("Dialog_Global.Title"), new Vector2(300f, 400f)) + private Dialog_Global() : base(Lang.Get("Dialog_Global.Title").Bold(), new Vector2(300f, 400f)) { } public static void Open() => Find.WindowStack.Add(new Dialog_Global()); diff --git a/Source/Interface/Dialog_Plans.cs b/Source/Interface/Dialog_Plans.cs index 5d03736..069e249 100644 --- a/Source/Interface/Dialog_Plans.cs +++ b/Source/Interface/Dialog_Plans.cs @@ -13,7 +13,7 @@ internal class Dialog_Plans : WindowPlus private IEnumerable _plans; private string _selected; - private Dialog_Plans() : base(Lang.Get("Dialog_Plans.Title"), new Vector2(500f, 600f)) + private Dialog_Plans() : base(Lang.Get("Dialog_Plans.Title").Bold(), new Vector2(500f, 600f)) { doCloseButton = false; diff --git a/Source/Interface/WindowPlus.cs b/Source/Interface/WindowPlus.cs index 3983408..ad3a6f2 100644 --- a/Source/Interface/WindowPlus.cs +++ b/Source/Interface/WindowPlus.cs @@ -43,7 +43,6 @@ private Rect DoTitle(Rect rect) var header = new Listing_StandardPlus(); header.Begin(rect); - Text.Font = GameFont.Medium; header.LabelMedium(Title); header.GapLine(); header.End(); diff --git a/Source/Mod.cs b/Source/Mod.cs index 9ad7de2..798c3e8 100644 --- a/Source/Mod.cs +++ b/Source/Mod.cs @@ -6,6 +6,7 @@ using PawnRules.Data; using PawnRules.Interface; using PawnRules.Patch; +using RimWorld; using UnityEngine; using Verse; @@ -15,19 +16,22 @@ internal class Mod : Verse.Mod { public const string Id = "PawnRules"; public const string Name = "Pawn Rules"; - public const string Author = "Jaxe"; - public const string Version = "1.1.2"; + public const string Version = "1.1.3"; - public static readonly DirectoryInfo ConfigDirectory = new DirectoryInfo(GenFilePaths.ConfigFolderPath).CreateSubdirectory(Id); + public static readonly DirectoryInfo ConfigDirectory = new DirectoryInfo(Path.Combine(GenFilePaths.ConfigFolderPath, Id)); public static Mod Instance { get; private set; } + public static bool FirstTimeUser { get; private set; } public Mod(ModContentPack contentPack) : base(contentPack) { Instance = this; Log("Loaded"); - TryRegisterHugsLibUpdateFeature(); + FirstTimeUser = !ConfigDirectory.Exists; + ConfigDirectory.Create(); + + if (!FirstTimeUser) { TryRegisterHugsLibUpdateFeature(); } } private static void TryRegisterHugsLibUpdateFeature() @@ -35,19 +39,16 @@ private static void TryRegisterHugsLibUpdateFeature() var hugsLib = (from assembly in AppDomain.CurrentDomain.GetAssemblies() from type in assembly.GetTypes() where type.Name == "HugsLibController" select type).FirstOrDefault(); if (hugsLib == null) { return; } - var controllerField = AccessTools.Field(hugsLib, "instance"); - var controller = controllerField.GetValue(null); - - var updateFeaturesField = AccessTools.Property(controller.GetType(), "UpdateFeatures"); - var updateFeatures = updateFeaturesField.GetValue(controller, null); + var updateFeatures = Traverse.Create(hugsLib)?.Field("instance")?.Property("UpdateFeatures")?.GetValue(); + if (updateFeatures == null) { return; } - var inspectActiveModMethod = AccessTools.Method(updateFeatures.GetType(), "InspectActiveMod"); - inspectActiveModMethod.Invoke(updateFeatures, new object[] { Id, Assembly.GetExecutingAssembly().GetName().Version }); + AccessTools.Method(updateFeatures.GetType(), "InspectActiveMod")?.Invoke(updateFeatures, new object[] { Id, Assembly.GetExecutingAssembly().GetName().Version }); } public static void Log(string message) => Verse.Log.Message(PrefixMessage(message)); public static void Warning(string message) => Verse.Log.Warning(PrefixMessage(message)); public static void Error(string message) => Verse.Log.Error(PrefixMessage(message)); + public static void Message(string message) => Messages.Message(message, MessageTypeDefOf.TaskCompletion, false); public static string PrefixMessage(string message) => $"[{Name} v{Version}] {message}"; public override string SettingsCategory() => Name; diff --git a/Source/Patch/Access.cs b/Source/Patch/Access.cs index b2efeea..a4a32f7 100644 --- a/Source/Patch/Access.cs +++ b/Source/Patch/Access.cs @@ -15,7 +15,6 @@ internal static class Access private static readonly MethodInfo Method_RimWorld_FoodUtility_IsFoodSourceOnMapSociallyProper = AccessTools.Method(typeof(FoodUtility), "IsFoodSourceOnMapSociallyProper"); private static readonly MethodInfo Method_RimWorld_FoodUtility_SpawnedFoodSearchInnerScan = AccessTools.Method(typeof(FoodUtility), "SpawnedFoodSearchInnerScan"); private static readonly FieldInfo Field_RimWorld_FoodUtility_Filtered = AccessTools.Field(typeof(FoodUtility), "filtered"); - private static readonly FieldInfo Field_RimWorld_Pawn_GuestTracker_Pawn = AccessTools.Field(typeof(Pawn_GuestTracker), "pawn"); private static readonly FieldInfo Field_Verse_LoadedModManager_RunningMods = AccessTools.Field(typeof(LoadedModManager), "runningMods"); public static Pawn Method_RimWorld_FoodUtility_BestPawnToHuntForPredator_Call(Pawn predator, bool forceScanWholeMap) => (Pawn) Method_RimWorld_FoodUtility_BestPawnToHuntForPredator.Invoke(null, new object[] { predator, forceScanWholeMap }); @@ -23,7 +22,6 @@ internal static class Access public static bool Method_RimWorld_FoodUtility_IsFoodSourceOnMapSociallyProper_Call(Thing thing, Pawn getter, Pawn eater, bool allowSociallyImproper) => (bool) Method_RimWorld_FoodUtility_IsFoodSourceOnMapSociallyProper.Invoke(null, new object[] { thing, getter, eater, allowSociallyImproper }); public static Thing Method_RimWorld_FoodUtility_SpawnedFoodSearchInnerScan_Call(Pawn eater, IntVec3 root, List searchSet, PathEndMode peMode, TraverseParms traverseParams, float maxDistance = 9999f, Predicate validator = null) => (Thing) Method_RimWorld_FoodUtility_SpawnedFoodSearchInnerScan.Invoke(null, new object[] { eater, root, searchSet, peMode, traverseParams, maxDistance, validator }); public static HashSet Field_RimWorld_FoodUtility_Filtered_Get() => (HashSet) Field_RimWorld_FoodUtility_Filtered.GetValue(null); - public static Pawn Field_RimWorld_Pawn_GuestTracker_Pawn_Get(Pawn_GuestTracker instance) => (Pawn) Field_RimWorld_Pawn_GuestTracker_Pawn.GetValue(instance); public static List Field_Verse_LoadedModManager_RunningMods_Get() => (List) Field_Verse_LoadedModManager_RunningMods.GetValue(null); } } diff --git a/Source/Patch/Extensions.cs b/Source/Patch/Extensions.cs index 688dea4..83c6238 100644 --- a/Source/Patch/Extensions.cs +++ b/Source/Patch/Extensions.cs @@ -1,4 +1,4 @@ -using System; +using System.Collections; using PawnRules.Data; using RimWorld; using UnityEngine; @@ -11,7 +11,7 @@ internal static class Extensions public static string Italic(this string self) => "" + self + ""; public static string Bold(this string self) => "" + self + ""; - public static int LastIndex(this Array self) => self.Length - 1; + public static int LastIndex(this IList self) => self.Count - 1; public static int ToInt(this string self, int defaultValue = 0) => int.TryParse(self, out var result) ? result : defaultValue; public static float ToFloat(this string self, float defaultValue = 0f) => float.TryParse(self, out var result) ? result : defaultValue; diff --git a/Source/Patch/RimWorld_FoodUtility_BestFoodInInventory.cs b/Source/Patch/RimWorld_FoodUtility_BestFoodInInventory.cs index 7a3a162..628b7ff 100644 --- a/Source/Patch/RimWorld_FoodUtility_BestFoodInInventory.cs +++ b/Source/Patch/RimWorld_FoodUtility_BestFoodInInventory.cs @@ -11,7 +11,13 @@ internal static class RimWorld_FoodUtility_BestFoodInInventory { private static bool Prefix(ref Thing __result, Pawn holder, Pawn eater = null, FoodPreferability minFoodPref = FoodPreferability.NeverForNutrition, FoodPreferability maxFoodPref = FoodPreferability.MealLavish, float minStackNutrition = 0.0f, bool allowDrug = false) { + if (Registry.ExemptedTrainer != null) + { + Registry.ExemptedTrainer = null; + return true; + } if (!Registry.IsActive) { return true; } + if (holder.inventory == null) { __result = null; @@ -20,14 +26,14 @@ private static bool Prefix(ref Thing __result, Pawn holder, Pawn eater = null, F if (eater == null) { eater = holder; } - var rules = Registry.GetRules(eater); - if (eater.InMentalState || (rules == null) || rules.GetRestriction(RestrictionType.Food).IsVoid) { return true; } + var restriction = Registry.GetRules(eater)?.GetRestriction(RestrictionType.Food); + if (eater.InMentalState || (restriction == null) || restriction.IsVoid) { return true; } var innerContainer = holder.inventory.innerContainer; foreach (var thing in innerContainer.ToArray()) { // Pawn Rules - Food check below - if (!thing.def.IsNutritionGivingIngestible || !thing.IngestibleNow || !eater.RaceProps.CanEverEat(thing) || (thing.def.ingestible.preferability < minFoodPref) || (thing.def.ingestible.preferability > maxFoodPref) || (!allowDrug && thing.def.IsDrug) || !(thing.GetStatValue(StatDefOf.Nutrition) * thing.stackCount >= (double) minStackNutrition) || !rules.GetRestriction(RestrictionType.Food).AllowsFood(thing.def, eater)) { continue; } + if (!thing.def.IsNutritionGivingIngestible || !thing.IngestibleNow || !eater.RaceProps.CanEverEat(thing) || (thing.def.ingestible.preferability < minFoodPref) || (thing.def.ingestible.preferability > maxFoodPref) || (!allowDrug && thing.def.IsDrug) || !(thing.GetStatValue(StatDefOf.Nutrition) * thing.stackCount >= (double) minStackNutrition) || !restriction.AllowsFood(thing.def, eater)) { continue; } __result = thing; return false; diff --git a/Source/Patch/RimWorld_FoodUtility_BestFoodSourceOnMap.cs b/Source/Patch/RimWorld_FoodUtility_BestFoodSourceOnMap.cs index c5b43ae..827b75f 100644 --- a/Source/Patch/RimWorld_FoodUtility_BestFoodSourceOnMap.cs +++ b/Source/Patch/RimWorld_FoodUtility_BestFoodSourceOnMap.cs @@ -10,21 +10,19 @@ namespace PawnRules.Patch [HarmonyPatch(typeof(FoodUtility), "BestFoodSourceOnMap")] internal static class RimWorld_FoodUtility_BestFoodSourceOnMap { - public static Pawn ExemptTrainer { get; set; } - private static bool Prefix(ref Thing __result, Pawn getter, Pawn eater, bool desperate, out ThingDef foodDef, FoodPreferability maxPref = FoodPreferability.MealLavish, bool allowPlant = true, bool allowDrug = true, bool allowCorpse = true, bool allowDispenserFull = true, bool allowDispenserEmpty = true, bool allowForbidden = false, bool allowSociallyImproper = false, bool allowHarvest = false, bool forceScanWholeMap = false) { foodDef = null; - if (!Registry.IsActive) { return true; } - if (ExemptTrainer != null) + if (Registry.ExemptedTrainer != null) { - ExemptTrainer = null; + Registry.ExemptedTrainer = null; return true; } + if (!Registry.IsActive) { return true; } - var rules = Registry.GetRules(eater); - if (eater.InMentalState || (rules == null) || rules.GetRestriction(RestrictionType.Food).IsVoid) { return true; } + var restriction = Registry.GetRules(eater)?.GetRestriction(RestrictionType.Food); + if (eater.InMentalState || (restriction == null) || restriction.IsVoid) { return true; } var filtered = Access.Field_RimWorld_FoodUtility_Filtered_Get(); @@ -45,7 +43,7 @@ private static bool Prefix(ref Thing __result, Pawn getter, Pawn eater, bool des else if ((thing.def.ingestible.preferability < minPref) || (thing.def.ingestible.preferability > maxPref) || !eater.RaceProps.WillAutomaticallyEat(thing) || !thing.def.IsNutritionGivingIngestible || !thing.IngestibleNow || (!allowCorpse && thing is Corpse) || (!allowDrug && thing.def.IsDrug) || (!allowForbidden && thing.IsForbidden(getter)) || (!desperate && thing.IsNotFresh()) || thing.IsDessicated() || !Access.Method_RimWorld_FoodUtility_IsFoodSourceOnMapSociallyProper_Call(thing, getter, eater, allowSociallyImproper) || (!getter.AnimalAwareOf(thing) && !forceScanWholeMap) || !getter.CanReserve(thing)) { return false; } // Pawn Rules - Food check below - return rules.GetRestriction(RestrictionType.Food).AllowsFood(thing.def, eater); + return restriction.AllowsFood(thing.def, eater); }); var req = ((eater.RaceProps.foodType & (FoodTypeFlags.Plant | FoodTypeFlags.Tree)) == FoodTypeFlags.None) || !allowPlant ? ThingRequest.ForGroup(ThingRequestGroup.FoodSourceNotPlantOrTree) : ThingRequest.ForGroup(ThingRequestGroup.FoodSource); @@ -64,7 +62,7 @@ bool Validator(Thing thing) var harvestedThingDef = plant.def.plant.harvestedThingDef; // Pawn Rules - Food check below - return harvestedThingDef.IsNutritionGivingIngestible && eater.RaceProps.CanEverEat(harvestedThingDef) && getter.CanReserve(plant) && (allowForbidden || !plant.IsForbidden(getter)) && ((bestThing == null) || (FoodUtility.GetFinalIngestibleDef(bestThing).ingestible.preferability < harvestedThingDef.ingestible.preferability)) && rules.GetRestriction(RestrictionType.Food).AllowsFood(plant.def, eater); + return harvestedThingDef.IsNutritionGivingIngestible && eater.RaceProps.CanEverEat(harvestedThingDef) && getter.CanReserve(plant) && (allowForbidden || !plant.IsForbidden(getter)) && ((bestThing == null) || (FoodUtility.GetFinalIngestibleDef(bestThing).ingestible.preferability < harvestedThingDef.ingestible.preferability)) && restriction.AllowsFood(plant.def, eater); } var foodSource = GenClosest.ClosestThingReachable(getter.Position, getter.Map, ThingRequest.ForGroup(ThingRequestGroup.HarvestablePlant), PathEndMode.Touch, TraverseParms.For(getter), 9999f, Validator, null, 0, searchRegionsMax); diff --git a/Source/Patch/RimWorld_FoodUtility_TryFindBestFoodSourceFor.cs b/Source/Patch/RimWorld_FoodUtility_TryFindBestFoodSourceFor.cs index 75af585..9aca22f 100644 --- a/Source/Patch/RimWorld_FoodUtility_TryFindBestFoodSourceFor.cs +++ b/Source/Patch/RimWorld_FoodUtility_TryFindBestFoodSourceFor.cs @@ -15,8 +15,8 @@ private static bool Prefix(ref bool __result, Pawn getter, Pawn eater, bool desp if (!Registry.IsActive) { return true; } - var rules = Registry.GetRules(eater); - if (eater.InMentalState || (rules == null) || rules.GetRestriction(RestrictionType.Food).IsVoid) { return true; } + var restriction = Registry.GetRules(eater)?.GetRestriction(RestrictionType.Food); + if (eater.InMentalState || (restriction == null) || restriction.IsVoid) { return true; } var hasInventory = getter.RaceProps.ToolUser && getter.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation); var allowDrug = !eater.IsTeetotaler(); diff --git a/Source/Patch/RimWorld_JobDriver_InteractAnimal_StartFeedAnimal.cs b/Source/Patch/RimWorld_JobDriver_InteractAnimal_StartFeedAnimal.cs new file mode 100644 index 0000000..42c3a75 --- /dev/null +++ b/Source/Patch/RimWorld_JobDriver_InteractAnimal_StartFeedAnimal.cs @@ -0,0 +1,58 @@ +using Harmony; +using PawnRules.Data; +using RimWorld; +using UnityEngine; +using Verse; +using Verse.AI; + +namespace PawnRules.Patch +{ + [HarmonyPatch(typeof(JobDriver_InteractAnimal), "StartFeedAnimal")] + internal static class RimWorld_JobDriver_InteractAnimal_StartFeedAnimal + { + private static bool Prefix(ref Toil __result, JobDriver_InteractAnimal __instance, TargetIndex tameeInd) + { + var toil = new Toil(); + toil.initAction = () => + { + var feedNutritionLeft = Traverse.Create(__instance).Field("feedNutritionLeft"); + + var actor = toil.GetActor(); + var target = (Pawn) (Thing) actor.CurJob.GetTarget(tameeInd); + + PawnUtility.ForceWait(target, 270, actor); + + Registry.ExemptedTrainer = actor; + var thing1 = FoodUtility.BestFoodInInventory(actor, target, FoodPreferability.NeverForNutrition, FoodPreferability.RawTasty); + if (thing1 == null) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); } + else + { + actor.mindState.lastInventoryRawFoodUseTick = Find.TickManager.TicksGame; + + var stackCountForNutrition = FoodUtility.StackCountForNutrition(feedNutritionLeft.Value, thing1.GetStatValue(StatDefOf.Nutrition)); + var stackCount = thing1.stackCount; + var thing2 = actor.inventory.innerContainer.Take(thing1, Mathf.Min(stackCountForNutrition, stackCount)); + + actor.carryTracker.TryStartCarry(thing2); + actor.CurJob.SetTarget(TargetIndex.B, thing2); + + var nutrition = thing2.stackCount * thing2.GetStatValue(StatDefOf.Nutrition); + __instance.ticksLeftThisToil = Mathf.CeilToInt((270f * (nutrition / JobDriver_InteractAnimal.RequiredNutritionPerFeed(target)))); + + if (stackCountForNutrition <= stackCount) { Traverse.Create(__instance).Field("feedNutritionLeft").Value = 0f; } + else + { + feedNutritionLeft.Value -= nutrition; + if (feedNutritionLeft.Value >= 0.001f) { return; } + feedNutritionLeft.Value = 0f; + } + } + }; + + toil.defaultCompleteMode = ToilCompleteMode.Delay; + + __result = toil; + return false; + } + } +} diff --git a/Source/Patch/RimWorld_JobGiver_PackFood_IsGoodPackableFoodFor.cs b/Source/Patch/RimWorld_JobGiver_PackFood_IsGoodPackableFoodFor.cs index ffa7937..36f503f 100644 --- a/Source/Patch/RimWorld_JobGiver_PackFood_IsGoodPackableFoodFor.cs +++ b/Source/Patch/RimWorld_JobGiver_PackFood_IsGoodPackableFoodFor.cs @@ -12,10 +12,10 @@ private static void Postfix(ref bool __result, Thing food, Pawn forPawn) { if (!Registry.IsActive) { return; } - var rules = Registry.GetRules(forPawn); - if (forPawn.InMentalState || (rules == null) || rules.GetRestriction(RestrictionType.Food).IsVoid) { return; } + var restriction = Registry.GetRules(forPawn)?.GetRestriction(RestrictionType.Food); + if (forPawn.InMentalState || (restriction == null) || restriction.IsVoid) { return; } - __result = __result && rules.GetRestriction(RestrictionType.Food).AllowsFood(food.def, forPawn); + __result = __result && restriction.AllowsFood(food.def, forPawn); } } } diff --git a/Source/Patch/RimWorld_JoyGiver_Ingest_CanIngestForJoy.cs b/Source/Patch/RimWorld_JoyGiver_Ingest_CanIngestForJoy.cs index ec59b02..962ff3f 100644 --- a/Source/Patch/RimWorld_JoyGiver_Ingest_CanIngestForJoy.cs +++ b/Source/Patch/RimWorld_JoyGiver_Ingest_CanIngestForJoy.cs @@ -10,8 +10,7 @@ internal static class RimWorld_JoyGiver_Ingest_CanIngestForJoy { private static bool Prefix(ref bool __result, Pawn pawn, Thing t) { - var rules = Registry.GetRules(pawn); - var restriction = rules?.GetRestriction(RestrictionType.Food); + var restriction = Registry.GetRules(pawn)?.GetRestriction(RestrictionType.Food); if (pawn.InMentalState || (restriction == null) || restriction.IsVoid || restriction.AllowsFood(t.def, pawn)) { return true; } __result = false; diff --git a/Source/Patch/RimWorld_Pawn_GuestTracker_SetGuestStatus.cs b/Source/Patch/RimWorld_Pawn_GuestTracker_SetGuestStatus.cs index 9a839de..22d7a57 100644 --- a/Source/Patch/RimWorld_Pawn_GuestTracker_SetGuestStatus.cs +++ b/Source/Patch/RimWorld_Pawn_GuestTracker_SetGuestStatus.cs @@ -1,6 +1,7 @@ using Harmony; using PawnRules.Data; using RimWorld; +using Verse; namespace PawnRules.Patch { @@ -11,7 +12,8 @@ private static void Prefix(Pawn_GuestTracker __instance, Faction newHost, bool p { if (!Registry.IsActive) { return; } - Registry.FactionUpdate(Access.Field_RimWorld_Pawn_GuestTracker_Pawn_Get(__instance), newHost, !prisoner); + Registry.FactionUpdate(Traverse.Create(__instance).Field("pawn").Value, newHost, !prisoner); + //Registry.FactionUpdate(Access.Field_RimWorld_Pawn_GuestTracker_Pawn_Get(__instance), newHost, !prisoner); } } } diff --git a/Source/Patch/RimWorld_WorkGiver_InteractAnimal_HasFoodToInteractAnimal.cs b/Source/Patch/RimWorld_WorkGiver_InteractAnimal_HasFoodToInteractAnimal.cs new file mode 100644 index 0000000..923cd30 --- /dev/null +++ b/Source/Patch/RimWorld_WorkGiver_InteractAnimal_HasFoodToInteractAnimal.cs @@ -0,0 +1,50 @@ +using System.Linq; +using Harmony; +using PawnRules.Data; +using RimWorld; +using Verse; + +namespace PawnRules.Patch +{ + [HarmonyPatch(typeof(WorkGiver_InteractAnimal), "HasFoodToInteractAnimal")] + internal static class RimWorld_WorkGiver_InteractAnimal_HasFoodToInteractAnimal + { + private static bool Prefix(ref bool __result, Pawn pawn, Pawn tamee) + { + if (!Registry.IsActive || Registry.AllowTrainingFood) { return true; } + + var restriction = Registry.GetRules(tamee)?.GetRestriction(RestrictionType.Food); + if ((restriction == null) || restriction.IsVoid) { return true; } + + var innerContainer = pawn.inventory.innerContainer; + var requiredNutritionPerFeed = JobDriver_InteractAnimal.RequiredNutritionPerFeed(tamee); + + var count = 0; + var nutrition = 0.0f; + + foreach (var thing in innerContainer.ToArray()) + { + if (!tamee.RaceProps.CanEverEat(thing) || (thing.def.ingestible.preferability > FoodPreferability.RawTasty) || thing.def.IsDrug || !restriction.Allows(thing.def)) { continue; } + + for (var index = 0; index < thing.stackCount; ++index) + { + nutrition += thing.GetStatValue(StatDefOf.Nutrition); + + if (nutrition >= (double) requiredNutritionPerFeed) + { + count++; + nutrition = 0.0f; + } + + if (count < 2) { continue; } + + __result = true; + return false; + } + } + + __result = false; + return false; + } + } +} diff --git a/Source/Patch/RimWorld_WorkGiver_InteractAnimal.cs b/Source/Patch/RimWorld_WorkGiver_InteractAnimal_TakeFoodForAnimalInteractJob.cs similarity index 93% rename from Source/Patch/RimWorld_WorkGiver_InteractAnimal.cs rename to Source/Patch/RimWorld_WorkGiver_InteractAnimal_TakeFoodForAnimalInteractJob.cs index 80b1e0e..bd10837 100644 --- a/Source/Patch/RimWorld_WorkGiver_InteractAnimal.cs +++ b/Source/Patch/RimWorld_WorkGiver_InteractAnimal_TakeFoodForAnimalInteractJob.cs @@ -16,7 +16,7 @@ private static bool Prefix(ref Job __result, Pawn pawn, Pawn tamee) var required = JobDriver_InteractAnimal.RequiredNutritionPerFeed(tamee) * 2f * 4f; - RimWorld_FoodUtility_BestFoodSourceOnMap.ExemptTrainer = pawn; + Registry.ExemptedTrainer = pawn; var foodSource = FoodUtility.BestFoodSourceOnMap(pawn, tamee, false, out var foodDef, FoodPreferability.RawTasty, false, false, false, false, false); if (foodSource == null) diff --git a/Source/PawnRules.csproj b/Source/PawnRules.csproj index b2ec2a4..6159e7d 100644 --- a/Source/PawnRules.csproj +++ b/Source/PawnRules.csproj @@ -90,12 +90,14 @@ - + + - + + diff --git a/Source/Properties/AssemblyInfo.cs b/Source/Properties/AssemblyInfo.cs index ecce163..7321b07 100644 --- a/Source/Properties/AssemblyInfo.cs +++ b/Source/Properties/AssemblyInfo.cs @@ -3,5 +3,5 @@ [assembly: AssemblyTitle(Mod.Name)] [assembly: AssemblyProduct("RimWorld Mods by Jaxe")] -[assembly: AssemblyCopyright("© " + Mod.Author)] +[assembly: AssemblyCopyright("© Jaxe")] [assembly: AssemblyVersion(Mod.Version)]