diff --git a/Assemblies/Outfitter.dll b/Assemblies/Outfitter.dll index 51c1320..e49877c 100644 Binary files a/Assemblies/Outfitter.dll and b/Assemblies/Outfitter.dll differ diff --git a/Source/Outfitter/ApparelStatCache.cs b/Source/Outfitter/ApparelStatCache.cs index 6515ea7..ed822c4 100644 --- a/Source/Outfitter/ApparelStatCache.cs +++ b/Source/Outfitter/ApparelStatCache.cs @@ -407,7 +407,7 @@ public float ApparelScoreRaw([NotNull] Apparel ap) // equipped offsets, e.g. movement speeds if (equippedOffsets.Contains(statPriority.Stat)) { - float statValue = ap.GetEquippedStatValue(statPriority.Stat); + float statValue = ap.GetEquippedStatValue(this.pawn, statPriority.Stat); score += statValue * statPriority.Weight; @@ -423,7 +423,14 @@ public float ApparelScoreRaw([NotNull] Apparel ap) // float statInfused = StatInfused(infusionSet, statPriority, ref dontcare); DoApparelScoreRaw_PawnStatsHandlers(ap, statPriority.Stat, out float statInfused); - score += statInfused * statPriority.Weight; + // Bug with Infused and "Ancient", it completely kills the pawn's armor + if (statInfused < 0 && (statPriority.Stat == StatDefOf.ArmorRating_Blunt + || statPriority.Stat == StatDefOf.ArmorRating_Sharp)) + { + score = -2f; + return score; + } + score += statInfused * statPriority.Weight; } } @@ -470,7 +477,7 @@ public float ApparelScoreRaw([NotNull] Apparel ap) } private static readonly SimpleCurve curve = - new SimpleCurve { new CurvePoint(-10f, 0.1f), new CurvePoint(0f, 1f), new CurvePoint(60f, 2f) }; + new SimpleCurve { new CurvePoint(-5f, 0.1f), new CurvePoint(0f, 1f), new CurvePoint(100f, 2.5f) }; public float ApparelScoreRaw_Temperature([NotNull] Apparel apparel) { @@ -503,9 +510,9 @@ public float ApparelScoreRaw_Temperature([NotNull] Apparel apparel) insulationHeat += infInsulationHeat; } - // string log = apparel.LabelCap + " - InsCold: " + insulationCold + " - InsHeat: " + insulationHeat + " - TargTemp: " - // + targetTemperatures + "\nMinComfy: " + minComfyTemperature + " - MaxComfy: " - // + maxComfyTemperature; + string log = apparel.LabelCap + " - InsCold: " + insulationCold + " - InsHeat: " + insulationHeat + " - TargTemp: " + + targetTemperatures + "\nMinComfy: " + minComfyTemperature + " - MaxComfy: " + + maxComfyTemperature; // if this gear is currently worn, we need to make sure the contribution to the pawn's comfy temps is removed so the gear is properly scored List wornApparel = thisPawn.apparel.WornApparel; @@ -554,8 +561,7 @@ public float ApparelScoreRaw_Temperature([NotNull] Apparel apparel) } } - // log += "\nBasic stat - MinComfy: " + minComfyTemperature + " - MaxComfy: " + maxComfyTemperature; - + log += "\nBasic stat not worn - MinComfy: " + minComfyTemperature + " - MaxComfy: " + maxComfyTemperature; // now for the interesting bit. FloatRange temperatureScoreOffset = new FloatRange(0f, 0f); @@ -565,14 +571,23 @@ public float ApparelScoreRaw_Temperature([NotNull] Apparel apparel) // isolation_warm is given as positive numbers. float neededInsulation_Warmth = targetTemperatures.max - maxComfyTemperature; - // log += "\nWeight: " + tempWeight + " - NeedInsCold: " + neededInsulation_Cold + " - NeedInsWarmth: " - // + neededInsulation_Warmth; + FloatRange tempWeight = this.TemperatureWeight; + log += "\nWeight: " + tempWeight + " - NeedInsCold: " + neededInsulation_Cold + " - NeedInsWarmth: " + + neededInsulation_Warmth; if (neededInsulation_Cold < 0) { // currently too cold - temperatureScoreOffset.min += -insulationCold; + // caps ap to only consider the needed temp and don't give extra points + if (neededInsulation_Cold > insulationCold) + { + temperatureScoreOffset.min += neededInsulation_Cold; + } + else + { + temperatureScoreOffset.min += insulationCold; + } } else { @@ -585,11 +600,21 @@ public float ApparelScoreRaw_Temperature([NotNull] Apparel apparel) } } + // invert for scoring + temperatureScoreOffset.min *= -1; if (neededInsulation_Warmth > 0) { // currently too warm - temperatureScoreOffset.max += insulationHeat; + // caps ap to only consider the needed temp and don't give extra points + if (neededInsulation_Warmth < insulationHeat) + { + temperatureScoreOffset.max += neededInsulation_Warmth; + } + else + { + temperatureScoreOffset.max += insulationHeat; + } } else { @@ -597,7 +622,7 @@ public float ApparelScoreRaw_Temperature([NotNull] Apparel apparel) if (insulationHeat < neededInsulation_Warmth) { // this gear would make us too warm - temperatureScoreOffset.max += neededInsulation_Warmth - insulationHeat; + temperatureScoreOffset.max += insulationHeat - neededInsulation_Warmth; } } @@ -606,15 +631,15 @@ public float ApparelScoreRaw_Temperature([NotNull] Apparel apparel) // temperatureScoreOffset.max *= temperatureScoreOffset.max < 0 ? 2f : 1f; // New - FloatRange tempWeight = this.TemperatureWeight; + log += "\nPre-Evaluate: " + temperatureScoreOffset.min + " / " + temperatureScoreOffset.max; temperatureScoreOffset.min = curve.Evaluate(temperatureScoreOffset.min * tempWeight.min); temperatureScoreOffset.max = curve.Evaluate(temperatureScoreOffset.max * tempWeight.max); - // log += "\nScoreOffsetMin: " + temperatureScoreOffset.min + " - ScoreOffsetMax: " - // + temperatureScoreOffset.max + " => " + 1 + (temperatureScoreOffset.min + temperatureScoreOffset.max) / 25; - // Log.Message(log); + log += "\nScoreOffsetMin: " + temperatureScoreOffset.min + " - ScoreOffsetMax: " + + temperatureScoreOffset.max + " *= " + (temperatureScoreOffset.min * temperatureScoreOffset.max); + // Log.Message(log); return temperatureScoreOffset.min * temperatureScoreOffset.max; //return 1 + (temperatureScoreOffset.min + temperatureScoreOffset.max) / 15; @@ -638,55 +663,50 @@ public ApparelEntry GetAllOffsets([NotNull] Apparel ap) public void UpdateTemperatureIfNecessary(bool force = false, bool forceweight = false) { Pawn thisPawn = this.pawn; + if (Find.TickManager.TicksGame - this.lastTempUpdate > JobGiver_OutfitterOptimizeApparel.ApparelStatCheck || force) { // get desired temperatures if (!this.pawnSave.TargetTemperaturesOverride) { - float temp = GenTemperature.GetTemperatureAtTile(thisPawn.Map.Tile); - float lowest = this.LowestTemperatureComing(thisPawn.Map); - - float minTemp = Mathf.Min(lowest - 5f, temp - 15f); - this.pawnSave.TargetTemperatures = new FloatRange(minTemp, temp + 15f); + // float temp = GenTemperature.GetTemperatureAtTile(thisPawn.Map.Tile); + float lowest = this.LowestTemperatureComing(thisPawn.Map); + float highest = this.HighestTemperatureComing(thisPawn.Map); - if (this.pawnSave.TargetTemperatures.min >= 12) - { - this.pawnSave.TargetTemperatures.min = 12; - } + // float minTemp = Mathf.Min(lowest - 5f, temp - 15f); - if (this.pawnSave.TargetTemperatures.max <= 32) - { - this.pawnSave.TargetTemperatures.max = 32; - } + this.pawnSave.TargetTemperatures = new FloatRange(Mathf.Min(12, lowest - 5f), Mathf.Max(32, highest + 5f)); - if (thisPawn.workSettings.WorkIsActive(DefDatabase.GetNamed("Cooking"))) + WorkTypeDef cooking = DefDatabase.GetNamed("Cooking"); + if (thisPawn.workSettings.WorkIsActive(cooking) && thisPawn.workSettings.GetPriority(cooking) < 3) { - { - this.pawnSave.TargetTemperatures.min = Mathf.Min(this.pawnSave.TargetTemperatures.min, -3); - } + this.pawnSave.TargetTemperatures.min = Mathf.Min(this.pawnSave.TargetTemperatures.min, -3); } this.lastTempUpdate = Find.TickManager.TicksGame; } } - FloatRange RealComfyTemperatures = thisPawn.ComfortableTemperatureRange(); + // FloatRange RealComfyTemperatures = thisPawn.ComfortableTemperatureRange(); + + float min = thisPawn.def.statBases.GetStatValueFromList(StatDefOf.ComfyTemperatureMin, StatDefOf.ComfyTemperatureMin.defaultBaseValue); + float max = thisPawn.def.statBases.GetStatValueFromList(StatDefOf.ComfyTemperatureMax, StatDefOf.ComfyTemperatureMax.defaultBaseValue); if (Find.TickManager.TicksGame - this.lastWeightUpdate > JobGiver_OutfitterOptimizeApparel.ApparelStatCheck || forceweight) { FloatRange weight = new FloatRange(1f, 1f); - if (this.pawnSave.TargetTemperatures.min < RealComfyTemperatures.min) + if (this.pawnSave.TargetTemperatures.min < min) { - weight.min += Math.Abs((this.pawnSave.TargetTemperatures.min - RealComfyTemperatures.min) / 100); + weight.min += Math.Abs((this.pawnSave.TargetTemperatures.min - min) / 10); } - if (this.pawnSave.TargetTemperatures.max > RealComfyTemperatures.max) + if (this.pawnSave.TargetTemperatures.max > max) { - weight.max += Math.Abs((this.pawnSave.TargetTemperatures.max - RealComfyTemperatures.max) / 100); + weight.max += Math.Abs((this.pawnSave.TargetTemperatures.max - max) / 10); } this.pawnSave.Temperatureweight = weight; @@ -750,6 +770,19 @@ private float LowestTemperatureComing([NotNull] Map map) return Mathf.Min(a, map.mapTemperature.OutdoorTemp); } + private float HighestTemperatureComing([NotNull] Map map) + { + Twelfth twelfth = GenLocalDate.Twelfth(map); + float a = this.GetTemperature(twelfth, map); + for (int i = 0; i < 3; i++) + { + twelfth = twelfth.NextTwelfth(); + a = Mathf.Max(a, this.GetTemperature(twelfth, map)); + } + + return Mathf.Max(a, map.mapTemperature.OutdoorTemp); + } + // ReSharper disable once CollectionNeverUpdated.Global } } \ No newline at end of file diff --git a/Source/Outfitter/ApparelStatsHelper.cs b/Source/Outfitter/ApparelStatsHelper.cs index 4e148a0..6608c4b 100644 --- a/Source/Outfitter/ApparelStatsHelper.cs +++ b/Source/Outfitter/ApparelStatsHelper.cs @@ -12,6 +12,8 @@ namespace Outfitter using JetBrains.Annotations; + using Outfitter.WorkType; + using RimWorld; using UnityEngine; @@ -81,7 +83,30 @@ private static float NegMin(bool mainJob = false) }; [NotNull] - private static readonly List IgnoredWorktypeDefs = new List(); + private static readonly List IgnoredWorktypeDefs = + new List + { + "Firefighter", + "Patient", + "PatientBedRest", + "Flicker", + "HaulingUrgent", + "FinishingOff", + "Feeding", + "FSFRearming", + "Rearming", // Splitter + "FSFRefueling", + "Refueling", // Splitter + "FSFLoading", + "Loading", // Splitter + "FSFCremating", + "FSFDeconstruct", + "Demolition", // Splitter + "Nursing", // Splitter + "Mortician", // Splitter + "Preparing", //Splitter + "Therapist" + }; private static readonly Dictionary PawnApparelStatCaches = new Dictionary(); @@ -331,29 +356,29 @@ public static Dictionary GetWeightedApparelIndividualStats(this switch (pawn.story.traits.DegreeOfTrait(TraitDefOf.Nerves)) { case -1: - AddStatToDict(StatDefOf.MentalBreakThreshold, -0.75f, ref dict); + AddStatToDict(StatDefOf.MentalBreakThreshold, -0.075f, ref dict); break; case -2: - AddStatToDict(StatDefOf.MentalBreakThreshold, -1.5f, ref dict); + AddStatToDict(StatDefOf.MentalBreakThreshold, -0.15f, ref dict); break; } switch (pawn.story.traits.DegreeOfTrait(TraitDef.Named("Neurotic"))) { case 1: - AddStatToDict(StatDefOf.MentalBreakThreshold, -0.75f, ref dict); + AddStatToDict(StatDefOf.MentalBreakThreshold, -0.075f, ref dict); break; case 2: - AddStatToDict(StatDefOf.MentalBreakThreshold, -1.5f, ref dict); + AddStatToDict(StatDefOf.MentalBreakThreshold, -0.15f, ref dict); break; } if (pawn.story.traits.HasTrait(TraitDefOf.TooSmart)) { - AddStatToDict(StatDefOf.MentalBreakThreshold, -1f, ref dict); + AddStatToDict(StatDefOf.MentalBreakThreshold, -0.15f, ref dict); } // float v1 = pawn.GetStatValue(StatDefOf.MentalBreakThreshold); @@ -382,10 +407,6 @@ public static int GetWorkPriority(this Pawn pawn, WorkTypeDef workType) return pawn.workSettings.GetPriority(workType); } - private static List ignoreList = - new List { "Firefighter", "Patient", "PatientBedRest", "Flicker", "HaulingUrgent", "FinishingOff" }; - - [NotNull] public static Dictionary GetWeightedApparelStats(this Pawn pawn) { @@ -400,7 +421,7 @@ public static Dictionary GetWeightedApparelStats(this Pawn pawn) { // add weights for all worktypes, multiplied by job priority List workTypes = DefDatabase.AllDefsListForReading - .Where(def => pawn.workSettings.WorkIsActive(def) && !ignoreList.Contains(def.defName)).ToList(); + .Where(def => pawn.workSettings.WorkIsActive(def) && !IgnoredWorktypeDefs.Contains(def.defName)).ToList(); int maxPriority = workTypes.Aggregate( 1, @@ -635,16 +656,12 @@ private static IEnumerable> GetStatsOfWorkType([Not switch (worktype.defName) { - case "Firefighter": yield break; - - case "Patient": yield break; - - case "Doctor": + case Vanilla.Doctor: if (pawnSave.mainJob == MainJob.Doctor) { mainJob = true; } - if (!DefDatabase.AllDefsListForReading.Any(x => x.defName == "FSFSurgeon")) + if (!DefDatabase.AllDefsListForReading.Any(x => x.defName == FSF.Surgeon || x.defName == Splitter.Surgeon)) { yield return new KeyValuePair(StatDefOf.MedicalSurgerySuccessChance, PosMax(mainJob)); yield return new KeyValuePair(StatDefOf2.MedicalOperationSpeed, PosMax(mainJob)); @@ -654,11 +671,7 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.MedicalTendSpeed, PosMed(mainJob)); yield break; - case "PatientBedRest": yield break; - - case "Flicker": yield break; - - case "Warden": + case Vanilla.Warden: if (pawnSave.mainJob == MainJob.Warden) { mainJob = true; @@ -666,14 +679,16 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.RecruitPrisonerChance, PosMax(mainJob)); yield return new KeyValuePair(StatDefOf.SocialImpact, PosMed(mainJob)); + yield return new KeyValuePair(StatDefOf.TradePriceImprovement, PosMax(mainJob)); yield break; - case "Handling": + case Vanilla.Handling: if (pawnSave.mainJob == MainJob.Handler) { mainJob = true; } - if (!DefDatabase.AllDefsListForReading.Any(x => x.defName == "FSFTraining")) + if (!DefDatabase.AllDefsListForReading.Any( + x => x.defName == FSF.Training || x.defName == Splitter.Training)) { yield return new KeyValuePair(StatDefOf.TrainAnimalChance, PosMax(mainJob)); yield return new KeyValuePair(StatDefOf.TameAnimalChance, PosMax(mainJob)); @@ -684,8 +699,12 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.MoveSpeed, PosMin(mainJob)); yield return new KeyValuePair(StatDefOf.MeleeDPS, PosMin(mainJob)); yield return new KeyValuePair(StatDefOf.AccuracyTouch, PosMin(mainJob)); - yield return new KeyValuePair(StatDefOf.MeleeWeapon_CooldownMultiplier, NegMin(mainJob)); - yield return new KeyValuePair(StatDefOf.MeleeWeapon_DamageMultiplier, PosMin(mainJob)); + yield return new KeyValuePair( + StatDefOf.MeleeWeapon_CooldownMultiplier, + NegMin(mainJob)); + yield return new KeyValuePair( + StatDefOf.MeleeWeapon_DamageMultiplier, + PosMin(mainJob)); } yield return new KeyValuePair(StatDefOf.AnimalGatherYield, PosMax(mainJob)); @@ -693,7 +712,7 @@ private static IEnumerable> GetStatsOfWorkType([Not yield break; // yield return new KeyValuePair(StatDefOf.CarryingCapacity, PosMin(mainJob)); - case "Cooking": + case Vanilla.Cooking: if (pawnSave.mainJob == MainJob.Cook) { mainJob = true; @@ -702,14 +721,18 @@ private static IEnumerable> GetStatsOfWorkType([Not // yield return new KeyValuePair(StatDefOf.MoveSpeed, 0.05f); // yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, 0.2f); - if (!DefDatabase.AllDefsListForReading.Any(x => x.defName == "FSFBrewing")) + if (!DefDatabase.AllDefsListForReading.Any( + x => x.defName == FSF.Brewing || x.defName == Splitter.Brewing)) { yield return new KeyValuePair(StatDefOf2.BrewingSpeed, PosMax(mainJob)); } - if (!DefDatabase.AllDefsListForReading.Any(x => x.defName == "FSFButcher")) + if (!DefDatabase.AllDefsListForReading.Any( + x => x.defName == FSF.Butcher || x.defName == Splitter.Butcher)) { yield return new KeyValuePair(StatDefOf2.ButcheryFleshSpeed, PosMax(mainJob)); - yield return new KeyValuePair(StatDefOf2.ButcheryFleshEfficiency, PosMax(mainJob)); + yield return new KeyValuePair( + StatDefOf2.ButcheryFleshEfficiency, + PosMax(mainJob)); } yield return new KeyValuePair(StatDefOf2.CookSpeed, PosMax(mainJob)); yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMin(mainJob)); @@ -717,7 +740,7 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.FoodPoisonChance, NegMax(mainJob)); yield break; - case "Hunting": + case Vanilla.Hunting: if (pawnSave.mainJob == MainJob.Hunter) { mainJob = true; @@ -737,12 +760,12 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.PainShockThreshold, PosMax(mainJob)); yield break; - case "Construction": + case Vanilla.Construction: if (pawnSave.mainJob == MainJob.Constructor) { mainJob = true; } - if (!DefDatabase.AllDefsListForReading.Any(x => x.defName == "FSFRepair")) + if (!DefDatabase.AllDefsListForReading.Any(x => x.defName == FSF.Repair || x.defName == Splitter.Repair)) { yield return new KeyValuePair(StatDefOf.FixBrokenDownBuildingSuccessChance, PosMax(mainJob)); } @@ -754,25 +777,32 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMin(mainJob)); yield break; - case "Repair": - yield return new KeyValuePair(StatDefOf.FixBrokenDownBuildingSuccessChance, PosMax(mainJob)); + + + case Vanilla.Growing: + if (pawnSave.mainJob == MainJob.Grower) + { + mainJob = true; + } + if (!DefDatabase.AllDefsListForReading.Any(x => x.defName == Splitter.Harvesting)) + { + yield return new KeyValuePair(StatDefOf.PlantHarvestYield, PosMax(mainJob)); + } + yield return new KeyValuePair(StatDefOf.PlantWorkSpeed, PosMax(mainJob)); yield return new KeyValuePair(StatDefOf.MoveSpeed, PosMin(mainJob)); yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMin(mainJob)); yield break; - case "Growing": + case Splitter.Harvesting: if (pawnSave.mainJob == MainJob.Grower) { mainJob = true; } - yield return new KeyValuePair(StatDefOf.PlantHarvestYield, PosMax(mainJob)); - yield return new KeyValuePair(StatDefOf.PlantWorkSpeed, PosMax(mainJob)); - yield return new KeyValuePair(StatDefOf.MoveSpeed, PosMin(mainJob)); yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMin(mainJob)); yield break; - case "Mining": + case Vanilla.Mining: if (pawnSave.mainJob == MainJob.Miner) { mainJob = true; @@ -785,13 +815,13 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.MoveSpeed, PosMin(mainJob)); yield break; - case "PlantCutting": + case Vanilla.PlantCutting: // yield return new KeyValuePair(StatDefOf.PlantWorkSpeed, PosMin(mainJob)); // yield return new KeyValuePair(StatDefOf.PlantHarvestYield, PosMax(mainJob)); yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMin(mainJob)); yield break; - case "Smithing": + case Vanilla.Smithing: if (pawnSave.mainJob == MainJob.Smith) { mainJob = true; @@ -801,7 +831,7 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMin(mainJob)); yield break; - case "Tailoring": + case Vanilla.Tailoring: if (pawnSave.mainJob == MainJob.Tailor) { mainJob = true; @@ -811,7 +841,7 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMin(mainJob)); yield break; - case "Art": + case Vanilla.Art: if (pawnSave.mainJob == MainJob.Artist) { mainJob = true; @@ -821,19 +851,34 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMed(mainJob)); yield break; - case "Crafting": + case Vanilla.Crafting: if (pawnSave.mainJob == MainJob.Crafter) { mainJob = true; } - + if (!DefDatabase.AllDefsListForReading.Any(x => x.defName == Splitter.Smelting)) + { + yield return new KeyValuePair(StatDefOf2.SmeltingSpeed, PosMax(mainJob)); + } yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMed(mainJob)); - yield return new KeyValuePair(StatDefOf2.SmeltingSpeed, PosMax(mainJob)); yield return new KeyValuePair(StatDefOf2.ButcheryMechanoidSpeed, PosMed(mainJob)); yield return new KeyValuePair(StatDefOf2.ButcheryMechanoidEfficiency, PosMed(mainJob)); yield break; - case "Hauling": + case Splitter.Stonecutting: + if (pawnSave.mainJob == MainJob.Crafter) + { + mainJob = true; + } + yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMed(mainJob)); + yield break; + + case Splitter.Smelting: + yield return new KeyValuePair(StatDefOf2.SmeltingSpeed, PosMax(mainJob)); + yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMin(mainJob)); + yield break; + + case Vanilla.Hauling: if (pawnSave.mainJob == MainJob.Hauler) { mainJob = true; @@ -843,12 +888,12 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.CarryingCapacity, PosMin(mainJob)); yield break; - case "Cleaning": + case Vanilla.Cleaning: yield return new KeyValuePair(StatDefOf.MoveSpeed, PosMax(mainJob)); yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMin(mainJob)); yield break; - case "Research": + case Vanilla.Research: if (pawnSave.mainJob == MainJob.Researcher) { mainJob = true; @@ -859,33 +904,56 @@ private static IEnumerable> GetStatsOfWorkType([Not yield break; // Colony Manager - case "Managing": + case Other.FluffyManaging: yield return new KeyValuePair(StatDefOf.SocialImpact, PosMin(mainJob)); yield return new KeyValuePair(DefDatabase.GetNamed("ManagingSpeed"), PosMed(mainJob)); yield break; // Hospitality - case "Diplomat": + case Other.HospitalityDiplomat: yield return new KeyValuePair(StatDefOf.SocialImpact, PosMed(mainJob)); yield return new KeyValuePair(StatDefOf.DiplomacyPower, PosMax(mainJob)); - yield return new KeyValuePair(StatDefOf.TradePriceImprovement, PosMax(mainJob)); yield break; // Else - case "HaulingUrgent": yield break; - case "FinishingOff": yield break; - case "Feeding": yield break; + // Job Mods + case FSF.Training: + if (pawnSave.mainJob == MainJob.Handler) + { + mainJob = true; + } + + yield return new KeyValuePair(StatDefOf.TrainAnimalChance, PosMax(mainJob)); + yield return new KeyValuePair(StatDefOf.TameAnimalChance, PosMax(mainJob)); + yield return new KeyValuePair(StatDefOf.ArmorRating_Blunt, PosMin(mainJob)); + yield return new KeyValuePair(StatDefOf.ArmorRating_Sharp, PosMin(mainJob)); + yield return new KeyValuePair(StatDefOf.MeleeDodgeChance, PosMed(mainJob)); + yield return new KeyValuePair(StatDefOf.MeleeHitChance, PosMin(mainJob)); + yield return new KeyValuePair(StatDefOf.MoveSpeed, PosMin(mainJob)); + yield return new KeyValuePair(StatDefOf.MeleeDPS, PosMin(mainJob)); + yield return new KeyValuePair(StatDefOf.AccuracyTouch, PosMin(mainJob)); + yield return new KeyValuePair(StatDefOf.MeleeWeapon_CooldownMultiplier, NegMin(mainJob)); + yield return new KeyValuePair(StatDefOf.MeleeWeapon_DamageMultiplier, PosMin(mainJob)); + + yield break; - // FSF Complex Jobs - case "FSFTraining": + case Splitter.Training: if (pawnSave.mainJob == MainJob.Handler) { mainJob = true; } yield return new KeyValuePair(StatDefOf.TrainAnimalChance, PosMax(mainJob)); + yield break; + + case Splitter.Taming: + if (pawnSave.mainJob == MainJob.Handler) + { + mainJob = true; + } + yield return new KeyValuePair(StatDefOf.TameAnimalChance, PosMax(mainJob)); yield return new KeyValuePair(StatDefOf.ArmorRating_Blunt, PosMin(mainJob)); yield return new KeyValuePair(StatDefOf.ArmorRating_Sharp, PosMin(mainJob)); @@ -898,7 +966,9 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.MeleeWeapon_DamageMultiplier, PosMin(mainJob)); yield break; - case "FSFButcher": + + case Splitter.Butcher: + case FSF.Butcher: if (pawnSave.mainJob == MainJob.Cook) { mainJob = true; @@ -906,23 +976,27 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf2.ButcheryFleshSpeed, PosMax(mainJob)); yield return new KeyValuePair(StatDefOf2.ButcheryFleshEfficiency, PosMax(mainJob)); yield break; - case "FSFBrewing": + + case Splitter.Brewing: + case FSF.Brewing: if (pawnSave.mainJob == MainJob.Cook) { mainJob = true; } yield return new KeyValuePair(StatDefOf2.BrewingSpeed, PosMax(mainJob)); yield break; - case "FSFRepair": + + case Splitter.Repair: + case FSF.Repair: if (pawnSave.mainJob == MainJob.Constructor) { mainJob = true; } yield return new KeyValuePair(StatDefOf.FixBrokenDownBuildingSuccessChance, PosMax(mainJob)); yield break; - case "FSFRearming": yield break; - case "FSFRefueling": yield break; - case "FSFDrilling": + + case Splitter.Drilling: + case FSF.Drilling: if (pawnSave.mainJob == MainJob.Miner) { mainJob = true; @@ -930,22 +1004,24 @@ private static IEnumerable> GetStatsOfWorkType([Not yield return new KeyValuePair(StatDefOf.MiningSpeed, PosMax(mainJob)); yield return new KeyValuePair(StatDefOf.MiningYield, PosMax(mainJob)); yield break; - case "FSFDrugs": + + case Splitter.Drugs: + case FSF.Drugs: yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMin(mainJob)); yield break; - case "FSFComponents": + + case Splitter.Components: + case FSF.Components: yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMin(mainJob)); yield break; - case "FSFRefining": + + case Splitter.Refining: + case FSF.Refining: yield return new KeyValuePair(StatDefOf.WorkSpeedGlobal, PosMin(mainJob)); yield break; - case "FSFLoading": - yield break; - case "FSFCremating": - yield break; - case "FSFDeconstruct": - yield break; - case "FSFSurgeon": + + case Splitter.Surgeon: + case FSF.Surgeon: if (pawnSave.mainJob == MainJob.Doctor) { mainJob = true; diff --git a/Source/Outfitter/Cache.cs b/Source/Outfitter/Cache.cs index e76cf7c..99ed2f5 100644 --- a/Source/Outfitter/Cache.cs +++ b/Source/Outfitter/Cache.cs @@ -6,26 +6,43 @@ using RimWorld; + using Verse; + public static class Cache { // ReSharper disable once FieldCanBeMadeReadOnly.Global public static Dictionary ApparelEntries = new Dictionary(); - public static float GetEquippedStatValue([NotNull] this Apparel apparel, StatDef stat) + public static float GetEquippedStatValue([NotNull] this Apparel apparel, Pawn pawn, StatDef stat) { - float currentStat = apparel.def.equippedStatOffsets.GetStatOffsetFromList(stat); + float baseStat = pawn.def.statBases.GetStatValueFromList(stat, stat.defaultBaseValue); + float equippedStatValue = apparel.def.equippedStatOffsets.GetStatOffsetFromList(stat); - // { - // float baseStat = apparel.GetStatValue(stat, true); - // float currentStat = baseStat; - // currentStat += apparel.def.equippedStatOffsets.GetStatOffsetFromList(stat.); - // DoApparelScoreRaw_PawnStatsHandlers(apparel, StatDefOf.Insulation_Cold, out float infInsulationCold); - // DoApparelScoreRaw_PawnStatsHandlers(apparel, StatDefOf.Insulation_Heat, out float infInsulationHeat); - // if (baseStat == 0) - // return currentStat; - // else - // return currentStat / baseStat; - // } + // float currentStat = apparel.def.equippedStatOffsets.GetStatOffsetFromList(stat); + + + // ApparelStatCache.DoApparelScoreRaw_PawnStatsHandlers(apparel, stat, out float statFloat); + // equippedStatValue += statFloat; + + // if (pawn == apparel.Wearer) + // { + // baseStat -= equippedStatValue; + // } + + // if (pawn.story != null) + // { + // for (int k = 0; k < pawn.story.traits.allTraits.Count; k++) + // { + // baseStat += pawn.story.traits.allTraits[k].OffsetOfStat(stat); + // baseStat *= pawn.story.traits.allTraits[k].MultiplierOfStat(stat); + // } + // } + + if (baseStat != 0) + { + return ((baseStat + equippedStatValue) / baseStat) -1; + } + return equippedStatValue; // float pawnStat = p.GetStatValue(stat); // var x = pawnStat; @@ -39,7 +56,7 @@ public static float GetEquippedStatValue([NotNull] this Apparel apparel, StatDef // { // return apparel.def.equippedStatOffsets.GetStatOffsetFromList(stat.StatDef) - baseStat; // } - return currentStat; + return equippedStatValue; } } } \ No newline at end of file diff --git a/Source/Outfitter/HarmonyPatches.cs b/Source/Outfitter/HarmonyPatches.cs index b736a15..283a776 100644 --- a/Source/Outfitter/HarmonyPatches.cs +++ b/Source/Outfitter/HarmonyPatches.cs @@ -19,7 +19,7 @@ using Verse.AI; [StaticConstructorOnStartup] -internal class HarmonyPatches +internal static class HarmonyPatches { static HarmonyPatches() { diff --git a/Source/Outfitter/Outfitter.csproj b/Source/Outfitter/Outfitter.csproj index f6de17d..99b28cd 100644 --- a/Source/Outfitter/Outfitter.csproj +++ b/Source/Outfitter/Outfitter.csproj @@ -69,7 +69,12 @@ + + + + + diff --git a/Source/Outfitter/StatWorkerOF.cs b/Source/Outfitter/StatWorkerOF.cs new file mode 100644 index 0000000..87632bb --- /dev/null +++ b/Source/Outfitter/StatWorkerOF.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Outfitter +{ + using RimWorld; + + using UnityEngine; + + using Verse; + + public class StatWorkerOF : StatWorker + { + + + public float GetValueUnfinalized(StatRequest req, bool applyPostProcess = true) + { + if (Prefs.DevMode && this.IsDisabledFor(req.Thing)) + { + Log.ErrorOnce(string.Format("Attempted to calculate value for disabled stat {0}; this is meant as a consistency check, either set the stat to neverDisabled or ensure this pawn cannot accidentally use this stat (thing={1})", this.stat, req.Thing.ToStringSafe()), 75193282 + this.stat.index); + } + float num = this.GetBaseValueFor(req.Def); + Pawn pawn = req.Thing as Pawn; + if (pawn != null) + { + if (pawn.skills != null) + { + if (this.stat.skillNeedOffsets != null) + { + for (int i = 0; i < this.stat.skillNeedOffsets.Count; i++) + { + num += this.stat.skillNeedOffsets[i].ValueFor(pawn); + } + } + } + else + { + num += this.stat.noSkillOffset; + } + if (this.stat.capacityOffsets != null) + { + for (int j = 0; j < this.stat.capacityOffsets.Count; j++) + { + PawnCapacityOffset pawnCapacityOffset = this.stat.capacityOffsets[j]; + num += pawnCapacityOffset.GetOffset(pawn.health.capacities.GetLevel(pawnCapacityOffset.capacity)); + } + } + if (pawn.story != null) + { + for (int k = 0; k < pawn.story.traits.allTraits.Count; k++) + { + num += pawn.story.traits.allTraits[k].OffsetOfStat(this.stat); + } + } + List hediffs = pawn.health.hediffSet.hediffs; + for (int l = 0; l < hediffs.Count; l++) + { + HediffStage curStage = hediffs[l].CurStage; + if (curStage != null) + { + num += curStage.statOffsets.GetStatOffsetFromList(this.stat); + } + } + if (pawn.apparel != null) + { + for (int m = 0; m < pawn.apparel.WornApparel.Count; m++) + { + num += StatWorker.StatOffsetFromGear(pawn.apparel.WornApparel[m], this.stat); + } + } + if (pawn.equipment != null && pawn.equipment.Primary != null) + { + num += StatWorker.StatOffsetFromGear(pawn.equipment.Primary, this.stat); + } + if (pawn.story != null) + { + for (int n = 0; n < pawn.story.traits.allTraits.Count; n++) + { + num *= pawn.story.traits.allTraits[n].MultiplierOfStat(this.stat); + } + } + num *= pawn.ageTracker.CurLifeStage.statFactors.GetStatFactorFromList(this.stat); + } + if (req.StuffDef != null && (num > 0.0 || this.stat.applyFactorsIfNegative)) + { + num += req.StuffDef.stuffProps.statOffsets.GetStatOffsetFromList(this.stat); + num *= req.StuffDef.stuffProps.statFactors.GetStatFactorFromList(this.stat); + } + if (req.HasThing) + { + CompAffectedByFacilities compAffectedByFacilities = req.Thing.TryGetComp(); + if (compAffectedByFacilities != null) + { + num += compAffectedByFacilities.GetStatOffset(this.stat); + } + if (this.stat.statFactors != null) + { + for (int num2 = 0; num2 < this.stat.statFactors.Count; num2++) + { + num *= req.Thing.GetStatValue(this.stat.statFactors[num2], true); + } + } + if (pawn != null) + { + if (pawn.skills != null) + { + if (this.stat.skillNeedFactors != null) + { + for (int num3 = 0; num3 < this.stat.skillNeedFactors.Count; num3++) + { + num *= this.stat.skillNeedFactors[num3].ValueFor(pawn); + } + } + } + else + { + num *= this.stat.noSkillFactor; + } + if (this.stat.capacityFactors != null) + { + for (int num4 = 0; num4 < this.stat.capacityFactors.Count; num4++) + { + PawnCapacityFactor pawnCapacityFactor = this.stat.capacityFactors[num4]; + float factor = pawnCapacityFactor.GetFactor(pawn.health.capacities.GetLevel(pawnCapacityFactor.capacity)); + num = Mathf.Lerp(num, num * factor, pawnCapacityFactor.weight); + } + } + if (pawn.Inspired) + { + num += pawn.InspirationDef.statOffsets.GetStatOffsetFromList(this.stat); + num *= pawn.InspirationDef.statFactors.GetStatFactorFromList(this.stat); + } + } + } + return num; + } + + } +} diff --git a/Source/Outfitter/Window/Window_Pawn_ApparelDetail.cs b/Source/Outfitter/Window/Window_Pawn_ApparelDetail.cs index 9879bfc..aae8874 100644 --- a/Source/Outfitter/Window/Window_Pawn_ApparelDetail.cs +++ b/Source/Outfitter/Window/Window_Pawn_ApparelDetail.cs @@ -24,24 +24,24 @@ public class Window_Pawn_ApparelDetail : Verse.Window private readonly GUIStyle fontBold = new GUIStyle - { - fontStyle = FontStyle.Bold, - normal = { + { + fontStyle = FontStyle.Bold, + normal = { textColor = Color.white }, - padding = new RectOffset(0, 0, 12, 6) - }; + padding = new RectOffset(0, 0, 12, 6) + }; private readonly GUIStyle headline = new GUIStyle - { - fontStyle = FontStyle.Bold, - fontSize = 16, - normal = { + { + fontStyle = FontStyle.Bold, + fontSize = 16, + normal = { textColor = Color.white }, - padding = new RectOffset(0, 0, 12, 6) - }; + padding = new RectOffset(0, 0, 12, 6) + }; private readonly GUIStyle hoverBox = new GUIStyle { hover = { background = OutfitterTextures.BgColor } }; @@ -129,7 +129,7 @@ public override void DoWindowContents(Rect windowRect) // GUI.BeginGroup(contentRect); float labelWidth = conRect.width - baseValue - baseValue - baseValue - 48f; - this.DrawLine("Status", labelWidth, "Base", "Strength", "Score", this.fontBold); + this.DrawLine("Status", labelWidth, "BaseMod", "Strength", "Score", this.fontBold); Space(6f); Label(string.Empty, this.whiteLine, Height(1)); @@ -168,14 +168,14 @@ public override void DoWindowContents(Rect windowRect) this.DrawLine( statLabel, labelWidth, - statValue.ToString("N2"), + statValue.ToStringPercent("N1"), statPriority.Weight.ToString("N2"), statScore.ToString("N2")); } if (equippedOffsets.Contains(statPriority.Stat)) { - float statValue = this.apparel.GetEquippedStatValue(statPriority.Stat); + float statValue = this.apparel.GetEquippedStatValue(this.pawn, statPriority.Stat); // statValue += StatCache.StatInfused(infusionSet, statPriority, ref equippedInfused); float statScore = statValue * statPriority.Weight; @@ -184,7 +184,7 @@ public override void DoWindowContents(Rect windowRect) this.DrawLine( statLabel, labelWidth, - statValue.ToString("N2"), + statValue.ToStringPercent("N1"), statPriority.Weight.ToString("N2"), statScore.ToString("N2")); } @@ -206,20 +206,35 @@ public override void DoWindowContents(Rect windowRect) statPriority.Stat, out float statValue); + bool flag = true; + // Bug with Infused and "Ancient", it completely kills the pawn's armor + if (statValue < 0 && (statPriority.Stat == StatDefOf.ArmorRating_Blunt + || statPriority.Stat == StatDefOf.ArmorRating_Sharp)) + { + score = -2f; + flag = false; + } + float statScore = statValue * statPriority.Weight; this.DrawLine( statLabel, labelWidth, - statValue.ToString("N2"), + statValue.ToStringPercent("N1"), statPriority.Weight.ToString("N2"), statScore.ToString("N2")); - score += statScore; + if (flag) + { + score += statScore; + } + else + { + break; + } } } - - GUI.color = Color.white; } + GUI.color = Color.white; // end upper group EndScrollView(); diff --git a/Source/Outfitter/WorkType/FSF.cs b/Source/Outfitter/WorkType/FSF.cs new file mode 100644 index 0000000..7e0ee53 --- /dev/null +++ b/Source/Outfitter/WorkType/FSF.cs @@ -0,0 +1,23 @@ +namespace Outfitter.WorkType +{ + public static class FSF + { + public const string Butcher = "FSFButcher"; + + public const string Brewing = "FSFBrewing"; + + public const string Repair = "FSFRepair"; + + public const string Training = "FSFTraining"; + + public const string Drilling = "FSFDrilling"; + + public const string Drugs = "FSFDrugs"; + + public const string Components = "FSFComponents"; + + public const string Refining = "FSFRefining"; + + public const string Surgeon = "FSFSurgeon"; + } +} diff --git a/Source/Outfitter/WorkType/Other.cs b/Source/Outfitter/WorkType/Other.cs new file mode 100644 index 0000000..bae5a86 --- /dev/null +++ b/Source/Outfitter/WorkType/Other.cs @@ -0,0 +1,9 @@ +namespace Outfitter.WorkType +{ + public static class Other + { + public const string FluffyManaging = "Managing"; + + public const string HospitalityDiplomat = "Diplomat"; + } +} diff --git a/Source/Outfitter/WorkType/Splitter.cs b/Source/Outfitter/WorkType/Splitter.cs new file mode 100644 index 0000000..6b9d3ea --- /dev/null +++ b/Source/Outfitter/WorkType/Splitter.cs @@ -0,0 +1,32 @@ +namespace Outfitter.WorkType +{ + public static class Splitter + { + public const string Butcher = "Butchering"; + public const string Repair = "Repairing"; + public const string Drugs = "Drugs"; + + public const string Brewing = "Brewing"; + + public const string Drilling = "Drilling"; + + + public const string Taming = "Taming"; + + public const string Training = "Training"; + + public const string Refining = "Refining"; + + public const string Surgeon = "Surgeon"; + + public const string Harvesting = "Harvesting"; + + public const string Components = "Assembling"; + + public const string Stonecutting = "Stonecutting"; + + public const string Smelting = "Smelting"; + + + } +} diff --git a/Source/Outfitter/WorkType/Vanilla.cs b/Source/Outfitter/WorkType/Vanilla.cs new file mode 100644 index 0000000..d840341 --- /dev/null +++ b/Source/Outfitter/WorkType/Vanilla.cs @@ -0,0 +1,37 @@ +namespace Outfitter.WorkType +{ + public static class Vanilla + { + public const string Doctor = "Doctor"; + + public const string Warden = "Warden"; + + public const string Handling = "Handling"; + + public const string Cooking = "Cooking"; + + public const string Hunting = "Hunting"; + + public const string Construction = "Construction"; + + public const string Growing = "Growing"; + + public const string Mining = "Mining"; + + public const string PlantCutting = "PlantCutting"; + + public const string Smithing = "Smithing"; + + public const string Tailoring = "Tailoring"; + + public const string Art = "Art"; + + public const string Crafting = "Crafting"; + + public const string Hauling = "Hauling"; + + public const string Cleaning = "Cleaning"; + + public const string Research = "Research"; + } +}