diff --git a/About/About.xml b/About/About.xml index 11c9d18..56a295e 100644 --- a/About/About.xml +++ b/About/About.xml @@ -4,5 +4,5 @@ XeoNovaDan https://ludeon.com/forums/index.php?topic=29503.0 0.19.0 - Current Version: v1.0.6 (24th September 2018)\n\nV.A.T.S for RimWorld! Allow your colonists, obedient animals and even some raiders to prioritize targeting certain body part groups of their enemies. Shoot those heads off in style! + Current Version: v1.1 (25th September 2018)\n\nV.A.T.S for RimWorld! Allow your colonists, obedient animals and even some raiders to prioritize targeting certain body part groups of their enemies. Shoot those heads off in style! \ No newline at end of file diff --git a/About/ModSync.xml b/About/ModSync.xml index 07981d4..1c65652 100644 --- a/About/ModSync.xml +++ b/About/ModSync.xml @@ -2,7 +2,7 @@ 6ff764b2-16e6-400f-be30-c004b847f984 [XND] Targeting Modes - 1.0.6 + 1.1 False XeoNovaDan diff --git a/Assemblies/1SettingsHelper.dll b/Assemblies/1SettingsHelper.dll new file mode 100644 index 0000000..a11a867 Binary files /dev/null and b/Assemblies/1SettingsHelper.dll differ diff --git a/Assemblies/TargetingModes.dll b/Assemblies/TargetingModes.dll index df9ff41..146a708 100644 Binary files a/Assemblies/TargetingModes.dll and b/Assemblies/TargetingModes.dll differ diff --git a/Languages/English/Keyed/GameplayCommands.xml b/Languages/English/Keyed/GameplayCommands.xml deleted file mode 100644 index 19b28bf..0000000 --- a/Languages/English/Keyed/GameplayCommands.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - Set targeting mode - Targeting: {0} - Determine which targeting mode the pawn should use. - - \ No newline at end of file diff --git a/Languages/English/Keyed/Keys.xml b/Languages/English/Keyed/Keys.xml new file mode 100644 index 0000000..9e814bb --- /dev/null +++ b/Languages/English/Keyed/Keys.xml @@ -0,0 +1,27 @@ + + + + Targeting Modes + Accuracy penalties + Whether or not targeting modes have an effect on the accuracy of a pawn's attacks. + + Targeting mode resetting frequency + Never + 1d + 12h + 6h + 3h + 1h + + Hostile NPCs use Targeting Modes + Whether or not any hostile NPCs are able to use targeting modes other than general. + Minimum weapon skill for raiders + Chance for mechanoids + Base chance for manhunters + + + Set targeting mode + Targeting: {0} + Determine which targeting mode the pawn should use. + + \ No newline at end of file diff --git a/Source/TargetingModes/.vs/TargetingModes/v15/.suo b/Source/TargetingModes/.vs/TargetingModes/v15/.suo index 29e3d85..da2e976 100644 Binary files a/Source/TargetingModes/.vs/TargetingModes/v15/.suo and b/Source/TargetingModes/.vs/TargetingModes/v15/.suo differ diff --git a/Source/TargetingModes/.vs/TargetingModes/v15/Server/sqlite3/storage.ide b/Source/TargetingModes/.vs/TargetingModes/v15/Server/sqlite3/storage.ide index 5f90bbb..4071c93 100644 Binary files a/Source/TargetingModes/.vs/TargetingModes/v15/Server/sqlite3/storage.ide and b/Source/TargetingModes/.vs/TargetingModes/v15/Server/sqlite3/storage.ide differ diff --git a/Source/TargetingModes/.vs/TargetingModes/v15/Server/sqlite3/storage.ide-shm b/Source/TargetingModes/.vs/TargetingModes/v15/Server/sqlite3/storage.ide-shm index 4cd7655..e1c3bc3 100644 Binary files a/Source/TargetingModes/.vs/TargetingModes/v15/Server/sqlite3/storage.ide-shm and b/Source/TargetingModes/.vs/TargetingModes/v15/Server/sqlite3/storage.ide-shm differ diff --git a/Source/TargetingModes/.vs/TargetingModes/v15/Server/sqlite3/storage.ide-wal b/Source/TargetingModes/.vs/TargetingModes/v15/Server/sqlite3/storage.ide-wal index 1d5c122..3f4da50 100644 Binary files a/Source/TargetingModes/.vs/TargetingModes/v15/Server/sqlite3/storage.ide-wal and b/Source/TargetingModes/.vs/TargetingModes/v15/Server/sqlite3/storage.ide-wal differ diff --git a/Source/TargetingModes/Command_SetTargetingMode.cs b/Source/TargetingModes/Command_SetTargetingMode.cs index c75d8ac..3ad7b82 100644 --- a/Source/TargetingModes/Command_SetTargetingMode.cs +++ b/Source/TargetingModes/Command_SetTargetingMode.cs @@ -59,8 +59,8 @@ public override void ProcessInput(Event ev) private string FloatMenuLabel(TargetingModeDef targetingMode) { string label = targetingMode.LabelCap; - if (targetingMode.hitChanceFactor != 1f) - label += $" (x{targetingMode.hitChanceFactor.ToStringPercent()} Acc)"; + if (targetingMode.HitChanceFactor != 1f) + label += $" (x{targetingMode.HitChanceFactor.ToStringPercent()} Acc)"; return label; } diff --git a/Source/TargetingModes/CompTargetingMode.cs b/Source/TargetingModes/CompTargetingMode.cs index 98bccc5..b254f69 100644 --- a/Source/TargetingModes/CompTargetingMode.cs +++ b/Source/TargetingModes/CompTargetingMode.cs @@ -4,6 +4,7 @@ using System.Text; using UnityEngine; using Verse; +using Verse.AI; using RimWorld; namespace TargetingModes @@ -25,13 +26,64 @@ public override void CompTick() { base.CompTick(); // Debugging - //if (Find.TickManager.TicksGame % (GenTicks.TicksPerRealSecond * 4) == 0) - // Log.Message(this.ToString()); + if (Find.TickManager.TicksGame % (GenTicks.TicksPerRealSecond * 4) == 0) + Log.Message(this.ToString()); // For compatibility with existing saves - if (_targetingMode == null) + if (_targetingMode == null || CanResetTargetingMode()) _targetingMode = TargetingModesUtility.DefaultTargetingMode; } + public bool CanResetTargetingMode() + { + // Non-player world pawns are exempt to player restrictions + if (parent.Faction != Faction.OfPlayer && parent.Map == null) + return Find.TickManager.TicksGame % TargModeResetAttemptInterval(3) == 0; + + // If player's set it to never reset, if it isn't time to update or if it's already been reset + if (TargetingModesSettings.TargModeResetFrequencyInt == 0 || + Find.TickManager.TicksGame % TargModeResetAttemptInterval(TargetingModesSettings.TargModeResetFrequencyInt) != 0 || + _targetingMode == TargetingModesUtility.DefaultTargetingMode) + return false; + + // Not super efficient code, but legibility's the priority + if (Pawn != null) + { + if (Pawn.drafter?.Drafted == false) + return true; + if (!GenAI.InDangerousCombat(Pawn)) + return true; + } + if (parent is Building_TurretGun turret) + { + if (turret.CurrentTarget == null) + return true; + } + + return false; + } + + private static int TargModeResetAttemptInterval(int freqInt) + { + switch (freqInt) + { + // 1 = 1d + case 1: + return GenDate.TicksPerDay; + // 2 = 12h + case 2: + return GenDate.TicksPerHour * 12; + // 3 = 6h + case 3: + return GenDate.TicksPerHour * 6; + // 4 = 3h + case 4: + return GenDate.TicksPerHour * 3; + // other (5) = 1h + default: + return GenDate.TicksPerHour; + }; + } + public override void PostExposeData() { base.PostExposeData(); diff --git a/Source/TargetingModes/HarmonyPatches.cs b/Source/TargetingModes/HarmonyPatches.cs index 83447f3..8461b04 100644 --- a/Source/TargetingModes/HarmonyPatches.cs +++ b/Source/TargetingModes/HarmonyPatches.cs @@ -22,9 +22,13 @@ static HarmonyPatches() { HarmonyInstance h = HarmonyInstance.Create("XeoNovaDan.TargetingModes"); + #region Patches h.Patch(AccessTools.Method(typeof(PawnGroupMakerUtility), nameof(PawnGroupMakerUtility.GeneratePawns)), postfix: new HarmonyMethod(patchType, nameof(Postfix_GeneratePawns))); + h.Patch(AccessTools.Method(typeof(ManhunterPackIncidentUtility), nameof(ManhunterPackIncidentUtility.GenerateAnimals)), + postfix: new HarmonyMethod(patchType, nameof(Postfix_GenerateAnimals))); + h.Patch(AccessTools.Method(typeof(Pawn), nameof(Pawn.GetGizmos)), postfix: new HarmonyMethod(patchType, nameof(Postfix_GetGizmos))); @@ -54,6 +58,7 @@ static HarmonyPatches() h.Patch(AccessTools.Method(typeof(CompSpawnerMechanoidsOnDamaged), "TrySpawnMechanoids"), transpiler: new HarmonyMethod(patchType, nameof(Transpile_TrySpawnMechanoids))); + #endregion } @@ -69,7 +74,7 @@ private static IEnumerable ModifiedPawnGroup(IEnumerable result, Paw foreach (Pawn pawn in result) { if ((pawn.RaceProps.Humanlike && parms.raidStrategy == TM_RaidStrategyDefOf.ImmediateAttackSmart && pawn.IsCompetentWithWeapon()) || - (pawn.RaceProps.IsMechanoid && Rand.Chance(TargetingModesUtility.MechanoidRandomTargetingModeChance))) + (pawn.RaceProps.IsMechanoid && Rand.Chance(TargetingModesSettings.mechanoidTargModeChance))) { // Validation for CompTargetingMode is done within this method pawn.TryAssignRandomTargetingMode(); @@ -79,6 +84,26 @@ private static IEnumerable ModifiedPawnGroup(IEnumerable result, Paw } #endregion + #region Postfix_GenerateAnimals + public static void Postfix_GenerateAnimals(ref List __result) + { + __result = ModifiedAnimalGroup(__result).ToList(); + } + + private static IEnumerable ModifiedAnimalGroup(List result) + { + if (!result.NullOrEmpty()) + foreach (Pawn pawn in result) + { + if (Rand.Chance(TargetingModesUtility.AdjustedChanceForAnimal(pawn))) + { + pawn.TryAssignRandomTargetingMode(); + } + yield return pawn; + } + } + #endregion + #region Postfix_GetGizmos public static void Postfix_GetGizmos(Pawn __instance, ref IEnumerable __result) { @@ -103,13 +128,13 @@ private static bool IsPlayerControlledAnimal(this Pawn pawn) => public static void Postfix_HitFactorFromShooter(ref float __result, Thing caster) { if (caster.TryGetComp() is CompTargetingMode targetingComp) - __result = Mathf.Max(__result * targetingComp.GetTargetingMode().hitChanceFactor, ShootTuning.MinAccuracyFactorFromShooterAndDistance); + __result = Mathf.Max(__result * targetingComp.GetTargetingMode().HitChanceFactor, ShootTuning.MinAccuracyFactorFromShooterAndDistance); } public static void Postfix_GetNonMissChance(Verb_MeleeAttack __instance, ref float __result) { if (__instance.caster is Thing caster && caster.TryGetComp() is CompTargetingMode targetingComp && __result == caster.GetStatValue(StatDefOf.MeleeHitChance)) - __result *= __result * targetingComp.GetTargetingMode().hitChanceFactor; + __result *= __result * targetingComp.GetTargetingMode().HitChanceFactor; } #endregion @@ -196,7 +221,7 @@ private static bool IsTheInstructionIAmLookingFor(this CodeInstruction instructi private static void TryAssignRandomTargetingModeMechanoid(this Pawn pawn) { - if (Rand.Chance(TargetingModesUtility.MechanoidRandomTargetingModeChance)) + if (Rand.Chance(TargetingModesSettings.mechanoidTargModeChance)) pawn.TryAssignRandomTargetingMode(); } #endregion diff --git a/Source/TargetingModes/Properties/AssemblyInfo.cs b/Source/TargetingModes/Properties/AssemblyInfo.cs index 2a24a21..7ed5ddd 100644 --- a/Source/TargetingModes/Properties/AssemblyInfo.cs +++ b/Source/TargetingModes/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // 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("1.0.6.0")] +[assembly: AssemblyVersion("1.1.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Source/TargetingModes/TargetingModeDef.cs b/Source/TargetingModes/TargetingModeDef.cs index 56107af..ba367fb 100644 --- a/Source/TargetingModes/TargetingModeDef.cs +++ b/Source/TargetingModes/TargetingModeDef.cs @@ -14,6 +14,9 @@ public class TargetingModeDef : Def public bool HasNoSpecifiedPartDetails => parts.NullOrEmpty() && partsOrAnyChildren.NullOrEmpty() && tags.NullOrEmpty(); + public float HitChanceFactor => + (TargetingModesSettings.accuracyPenalties) ? hitChanceFactor : 1f; + public bool PartsListContains(BodyPartDef def) => !parts.NullOrEmpty() && parts.Contains(def); public bool PartsOrAnyChildrenListContains(BodyPartRecord part) diff --git a/Source/TargetingModes/TargetingModes.csproj b/Source/TargetingModes/TargetingModes.csproj index 561be9a..81e7b0b 100644 --- a/Source/TargetingModes/TargetingModes.csproj +++ b/Source/TargetingModes/TargetingModes.csproj @@ -34,6 +34,11 @@ ..\..\Assemblies\0Harmony.dll False + + False + ..\..\Assemblies\1SettingsHelper.dll + False + references\AbilityUser.dll False @@ -61,6 +66,7 @@ + diff --git a/Source/TargetingModes/TargetingModesSettings.cs b/Source/TargetingModes/TargetingModesSettings.cs new file mode 100644 index 0000000..a981b2c --- /dev/null +++ b/Source/TargetingModes/TargetingModesSettings.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using Verse; +using RimWorld; +using SettingsHelper; + +namespace TargetingModes +{ + public class TargetingModesSettings : ModSettings + { + + public static bool accuracyPenalties = true; + #region TargModeResetFrequencyInt + private static float targModeResetFrequencyInt = 3f; + public static int TargModeResetFrequencyInt => + (int)targModeResetFrequencyInt; + #endregion + public static bool raidersUseTargModes = true; + #region MinimumSkillForRandomTargetingMode + private static float raiderMinSkillForTargMode = 8f; + public static int MinimumSkillForRandomTargetingMode => + (int)raiderMinSkillForTargMode; + #endregion + public static float mechanoidTargModeChance = 0.35f; + public static float baseManhunterTargModeChance = 0.2f; + + public void DoWindowContents(Rect wrect) + { + Listing_Standard options = new Listing_Standard(); + Color defaultColor = GUI.color; + options.Begin(wrect); + + GUI.color = defaultColor; + Text.Font = GameFont.Small; + Text.Anchor = TextAnchor.UpperLeft; + options.Gap(); + // General settings + options.CheckboxLabeled("Settings_AccuracyPenalties".Translate(), ref accuracyPenalties, "Settings_AccuracyPenalties_Tooltip".Translate()); + options.Gap(); + options.AddLabeledSlider("Settings_TargModeResetFrequency".Translate(), ref targModeResetFrequencyInt, 0, 5, + rightAlignedLabel: $"Settings_TargModeResetFrequency_{targModeResetFrequencyInt}".Translate(), roundTo: 1); + options.GapLine(24); + + // Settings for AI + options.CheckboxLabeled("Settings_RaidersUseTargetingModes".Translate(), ref raidersUseTargModes, "Settings_RaidersUseTargetingModes_Tooltip".Translate()); + options.Gap(); + + // Grey out this section if raiders can't use targeting modes + if (!raidersUseTargModes) + GUI.color = Color.grey; + + options.AddLabeledSlider("Settings_MinRaiderWeaponSkill".Translate(), ref raiderMinSkillForTargMode, 0, 20, + rightAlignedLabel: raiderMinSkillForTargMode.ToString(), roundTo: 1); + options.Gap(); + options.AddLabeledSlider("Settings_MechTargModeChance".Translate(), ref mechanoidTargModeChance, 0, 1, + rightAlignedLabel: mechanoidTargModeChance.ToStringPercent(), roundTo: 0.01f); + options.Gap(); + options.AddLabeledSlider("Settings_BaseManhunterTargModeChance".Translate(), ref baseManhunterTargModeChance, 0, 1, + rightAlignedLabel: baseManhunterTargModeChance.ToStringPercent(), roundTo: 0.01f); + // End of section + GUI.color = defaultColor; + + options.End(); + + Mod.GetSettings().Write(); + + } + + public override void ExposeData() + { + Scribe_Values.Look(ref accuracyPenalties, "accuracyPenalties", true); + Scribe_Values.Look(ref raidersUseTargModes, "raidersUseTargModes", true); + Scribe_Values.Look(ref raiderMinSkillForTargMode, "raiderMinSkillForTargMode", 8f); + Scribe_Values.Look(ref mechanoidTargModeChance, "mechanoidTargModeChance", 0.35f); + Scribe_Values.Look(ref baseManhunterTargModeChance, "baseManhunterTargModeChance", 0.2f); + } + + } + + public class TargetingModes : Mod + { + public TargetingModesSettings settings; + + public TargetingModes(ModContentPack content) : base(content) + { + GetSettings(); + } + + public override string SettingsCategory() => "TargetingModesSettingsCategory".Translate(); + + public override void DoSettingsWindowContents(Rect inRect) + { + GetSettings().DoWindowContents(inRect); + } + } +} diff --git a/Source/TargetingModes/TargetingModesUtility.cs b/Source/TargetingModes/TargetingModesUtility.cs index 6064bd6..696eafc 100644 --- a/Source/TargetingModes/TargetingModesUtility.cs +++ b/Source/TargetingModes/TargetingModesUtility.cs @@ -5,7 +5,6 @@ using UnityEngine; using Verse; using RimWorld; -using AbilityUser; namespace TargetingModes { @@ -13,8 +12,7 @@ public static class TargetingModesUtility { public static TargetingModeDef DefaultTargetingMode = TargetingModeDefOf.Standard; - public const int MinimumSkillForRandomTargetingMode = 8; - public const float MechanoidRandomTargetingModeChance = 0.35f; + private const float TargModeChanceFactorOffsetPerTrainabilityOrder = 0.05f; public static Command_SetTargetingMode SetTargetModeCommand(ITargetModeSettable settable) => new Command_SetTargetingMode @@ -36,7 +34,7 @@ public static bool CanUseTargetingModes(this ThingDef weapon, Thing instigator) if (instigator == null || !instigator.def.HasComp(typeof(CompTargetingMode)) || weapon == null) return false; - if (weapon.GetType().IsAssignableFrom(typeof(ProjectileDef_Ability))) + if (weapon.GetType().IsAssignableFrom(typeof(AbilityUser.ProjectileDef_Ability))) return true; if (weapon.thingClass.IsAssignableFrom(typeof(Pawn)) || weapon.IsMeleeWeapon) return true; @@ -98,19 +96,29 @@ public static bool IsCompetentWithWeapon(this Pawn pawn) if (pawn.skills == null || pawn.equipment == null) return false; - if (pawn.equipment.Primary?.def.IsRangedWeapon == true && pawn.skills.GetSkill(SkillDefOf.Shooting).Level >= MinimumSkillForRandomTargetingMode) + if (pawn.equipment.Primary?.def.IsRangedWeapon == true && pawn.skills.GetSkill(SkillDefOf.Shooting).Level >= TargetingModesSettings.MinimumSkillForRandomTargetingMode) return true; - return pawn.skills.GetSkill(SkillDefOf.Melee).Level >= MinimumSkillForRandomTargetingMode; + return pawn.skills.GetSkill(SkillDefOf.Melee).Level >= TargetingModesSettings.MinimumSkillForRandomTargetingMode; } public static void TryAssignRandomTargetingMode(this Pawn pawn) { - if (pawn.TryGetComp() is CompTargetingMode targetingComp) + if (TargetingModesSettings.raidersUseTargModes && pawn.TryGetComp() is CompTargetingMode targetingComp) { TargetingModeDef newTargetingMode = DefDatabase.AllDefsListForReading.RandomElementByWeight(t => t.commonality); targetingComp.SetTargetingMode(newTargetingMode); } } + public static float AdjustedChanceForAnimal(Pawn animal) + { + int orderOverIntermediate = animal.RaceProps.trainability.intelligenceOrder - TrainabilityDefOf.Intermediate.intelligenceOrder; + + // Animal needs at least intermediate intelligence to use targeting modes + if (orderOverIntermediate >= 0) + return TargetingModesSettings.baseManhunterTargModeChance * (1 + orderOverIntermediate * TargModeChanceFactorOffsetPerTrainabilityOrder); + return 0f; + } + } } diff --git a/Source/TargetingModes/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/Source/TargetingModes/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache index 16f88f0..fcaa9df 100644 Binary files a/Source/TargetingModes/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache and b/Source/TargetingModes/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/Source/TargetingModes/obj/Debug/TargetingModes.csproj.CoreCompileInputs.cache b/Source/TargetingModes/obj/Debug/TargetingModes.csproj.CoreCompileInputs.cache index f9d1cce..5095a41 100644 --- a/Source/TargetingModes/obj/Debug/TargetingModes.csproj.CoreCompileInputs.cache +++ b/Source/TargetingModes/obj/Debug/TargetingModes.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -c45965a061fe9b29e936a9d08b6a29933a79350e +74f536ccffc18782bd1182be9cd04286731b609e diff --git a/Source/TargetingModes/obj/Debug/TargetingModes.csprojAssemblyReference.cache b/Source/TargetingModes/obj/Debug/TargetingModes.csprojAssemblyReference.cache index ea626a9..055f5ae 100644 Binary files a/Source/TargetingModes/obj/Debug/TargetingModes.csprojAssemblyReference.cache and b/Source/TargetingModes/obj/Debug/TargetingModes.csprojAssemblyReference.cache differ diff --git a/Source/TargetingModes/obj/Debug/TargetingModes.dll b/Source/TargetingModes/obj/Debug/TargetingModes.dll index df9ff41..146a708 100644 Binary files a/Source/TargetingModes/obj/Debug/TargetingModes.dll and b/Source/TargetingModes/obj/Debug/TargetingModes.dll differ