diff --git a/Languages/English/Keyed/RJW_Sexperience.xml b/Languages/English/Keyed/RJW_Sexperience.xml index 92fe2bc..83d2026 100644 --- a/Languages/English/Keyed/RJW_Sexperience.xml +++ b/Languages/English/Keyed/RJW_Sexperience.xml @@ -9,6 +9,16 @@ not animal capable of sex is required + Enable romance patch for incest precepts* + Patch for incest precepts to affect RomanceChanceFactor. May conflict with romance mods./n/n* Requires a game restart to apply changes + Enable manual romance patch for incest precepts* + Patch for incest precepts to affect manual romance options./n/n* Requires a game restart to apply changes + + Time between bestiality attempts x{0} + Time between masturbation attempts x{0} + Time between necrophilia attempts x{0} + Time between rape attempts x{0} + - forbidden by ideology + \ No newline at end of file diff --git a/Source/IdeologyAddon/IdeologyAddon.csproj b/Source/IdeologyAddon/IdeologyAddon.csproj index 9f06941..f791556 100644 --- a/Source/IdeologyAddon/IdeologyAddon.csproj +++ b/Source/IdeologyAddon/IdeologyAddon.csproj @@ -54,6 +54,7 @@ + @@ -87,6 +88,8 @@ + + @@ -97,7 +100,7 @@ - 1.4.3555 + 1.4.3704 2.2.2 diff --git a/Source/IdeologyAddon/Keyed.cs b/Source/IdeologyAddon/Keyed.cs index 8a98c01..4083479 100644 --- a/Source/IdeologyAddon/Keyed.cs +++ b/Source/IdeologyAddon/Keyed.cs @@ -4,11 +4,17 @@ namespace RJWSexperience.Ideology { public static class Keyed { + public static readonly string ModTitle = "RSI_Mod_Title".Translate(); public static readonly string MemeStatFactor = "MemeStatFactor".Translate(); public static readonly string RSVictimCondition = "RSVictimCondition".Translate(); public static readonly string RSBreederCondition = "RSBreederCondition".Translate(); public static readonly string RSNotHuman = "RSNotHuman".Translate(); public static readonly string RSNotAnimal = "RSNotAnimal".Translate(); public static readonly string RSShouldCanFuck = "RSShouldCanFuck".Translate(); + + public static readonly string PatchRomanceChanceFactor = "RSI_PatchRomanceChanceFactor".Translate(); + public static readonly string PatchRomanceChanceFactorTip = "RSI_PatchRomanceChanceFactorTip".Translate(); + public static readonly string PatchIncestuousManualRomance = "RSI_PatchIncestuousManualRomance".Translate(); + public static readonly string PatchIncestuousManualRomanceTip = "RSI_PatchIncestuousManualRomanceTip".Translate(); } } diff --git a/Source/IdeologyAddon/Patches/Rimworld_Patch.cs b/Source/IdeologyAddon/Patches/Rimworld_Patch.cs index 5bd03b3..8d4498a 100644 --- a/Source/IdeologyAddon/Patches/Rimworld_Patch.cs +++ b/Source/IdeologyAddon/Patches/Rimworld_Patch.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; +using System.Text; using Verse; namespace RJWSexperience.Ideology.Patches @@ -60,6 +61,8 @@ namespace RJWSexperience.Ideology.Patches [HarmonyPatch(typeof(RelationsUtility), "Incestuous")] public static class Rimworld_Patch_IncestuousManualRomance { + public static bool Prepare() => RsiMod.Prefs.patchIncestuousManualRomance; + /// /// Override incestuous check in the manual romance /// @@ -97,6 +100,8 @@ namespace RJWSexperience.Ideology.Patches [HarmonyPatch(typeof(Pawn_RelationsTracker), nameof(Pawn_RelationsTracker.SecondaryRomanceChanceFactor))] public static class Rimworld_Patch_SecondaryRomanceChanceFactor { + public static bool Prepare() => RsiMod.Prefs.patchRomanceChanceFactor; + /// /// /// Replace @@ -124,9 +129,6 @@ namespace RJWSexperience.Ideology.Patches typeof(RomanceChanceFactorHelpers), nameof(RomanceChanceFactorHelpers.GetRomanceChanceFactor), new[] { typeof(Pawn), typeof(Pawn) }); - bool skipping = false; - bool yieldNext = false; - bool finished = false; // Original IL looks something like this: // Load this.pawn @@ -142,41 +144,103 @@ namespace RJWSexperience.Ideology.Patches // Idea is to substitute GetRelations call with a method with the same signature, // store results in the same place original loop does and remove everything else until Endfinaly - foreach (CodeInstruction instruction in instructions) + IEnumerator enumerator = instructions.GetEnumerator(); + bool endOfInstructions = enumerator.MoveNext(); + + // Find and replace GetRelations + while (!endOfInstructions) { - if (finished) - { - yield return instruction; - continue; - } - - if (skipping) - { - if (yieldNext) - { - yield return instruction; - yieldNext = false; - } - else if (instruction.opcode == OpCodes.Mul) - { - yieldNext = true; - } - else if (instruction.opcode == OpCodes.Endfinally) - { - finished = true; - } - - continue; - } + var instruction = enumerator.Current; if (instruction.Calls(getRelationsInfo)) { yield return new CodeInstruction(OpCodes.Call, helperInfo); - skipping = true; - continue; + enumerator.MoveNext(); // skip original method call + break; } yield return instruction; + endOfInstructions = enumerator.MoveNext(); + } + + if (endOfInstructions) + { + Log.Error("[RSI] Failed to transpile Pawn_RelationsTracker.SecondaryRomanceChanceFactor: PawnRelationUtility.GetRelations call not found"); + yield break; + } + + // Skip everything until Mul + while (!endOfInstructions) + { + if (enumerator.Current.opcode == OpCodes.Mul) + { + enumerator.MoveNext(); // skip Mul + yield return enumerator.Current; // return next op, it should be "store result" + break; + } + + endOfInstructions = enumerator.MoveNext(); + } + + if (endOfInstructions) + { + Log.Error("[RSI] Failed to transpile Pawn_RelationsTracker.SecondaryRomanceChanceFactor: Mul not found. This error means half of SecondaryRomanceChanceFactor was erased. Very not good"); + yield break; + } + + // Skip the rest of the loop + while (!endOfInstructions) + { + if (enumerator.Current.opcode == OpCodes.Endfinally) + { + // Endfinally will be skipped by the next MoveNext() + break; + } + + endOfInstructions = enumerator.MoveNext(); + } + + if (endOfInstructions) + { + Log.Error("[RSI] Failed to transpile Pawn_RelationsTracker.SecondaryRomanceChanceFactor: Endfinally not found. This error means half of SecondaryRomanceChanceFactor was erased. Very not good"); + yield break; + } + + // Return the rest of the method + while (enumerator.MoveNext()) + { + yield return enumerator.Current; + } + } + + [HarmonyPatch(typeof(Precept), nameof(Precept.GetTip))] + public static class Rimworld_Patch_PreceptTip + { + public static void Postfix(ref string __result, Precept __instance) + { + if (__instance.def.modExtensions.NullOrEmpty()) + { + return; + } + + bool tipChanged = false; + StringBuilder tipBuilder = new StringBuilder(__result); + tipBuilder.AppendLine(); + tipBuilder.AppendInNewLine((Keyed.ModTitle + ":").Colorize(ColoredText.TipSectionTitleColor)); + + for (int i = 0; i < __instance.def.modExtensions.Count; i++) + { + if (__instance.def.modExtensions[i] is IPreceptTipPostfix tipPostfix) + { + tipBuilder.AppendInNewLine(" - " + tipPostfix.GetTip()); + tipChanged = true; + } + } + + if (tipChanged) + { + __result = tipBuilder.ToString(); + } } } } diff --git a/Source/IdeologyAddon/Precepts/DefExtension_ModifyBestialityMtb.cs b/Source/IdeologyAddon/Precepts/DefExtension_ModifyBestialityMtb.cs index 1d97595..111ee69 100644 --- a/Source/IdeologyAddon/Precepts/DefExtension_ModifyBestialityMtb.cs +++ b/Source/IdeologyAddon/Precepts/DefExtension_ModifyBestialityMtb.cs @@ -2,5 +2,6 @@ { public class DefExtension_ModifyBestialityMtb : DefExtension_ModifyMtb { + protected override string TipTemplateKey => "RSI_PreceptTipModifyBestialityMtb"; } } diff --git a/Source/IdeologyAddon/Precepts/DefExtension_ModifyFappinMtb.cs b/Source/IdeologyAddon/Precepts/DefExtension_ModifyFappinMtb.cs index 8596b28..f01c4ed 100644 --- a/Source/IdeologyAddon/Precepts/DefExtension_ModifyFappinMtb.cs +++ b/Source/IdeologyAddon/Precepts/DefExtension_ModifyFappinMtb.cs @@ -2,5 +2,6 @@ { public class DefExtension_ModifyFappinMtb : DefExtension_ModifyMtb { + protected override string TipTemplateKey => "RSI_PreceptTipModifyFappinMtb"; } } diff --git a/Source/IdeologyAddon/Precepts/DefExtension_ModifyMtb.cs b/Source/IdeologyAddon/Precepts/DefExtension_ModifyMtb.cs index e1553f3..361ceb1 100644 --- a/Source/IdeologyAddon/Precepts/DefExtension_ModifyMtb.cs +++ b/Source/IdeologyAddon/Precepts/DefExtension_ModifyMtb.cs @@ -1,11 +1,28 @@ -using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Verse; namespace RJWSexperience.Ideology.Precepts { - public abstract class DefExtension_ModifyMtb : DefModExtension + [SuppressMessage("Minor Code Smell", "S1104:Fields should not have public accessibility", Justification = "Field values are loaded from XML")] + public abstract class DefExtension_ModifyMtb : DefModExtension, IPreceptTipPostfix { - [SuppressMessage("Minor Code Smell", "S1104:Fields should not have public accessibility", Justification = "Field value loaded from XML")] + protected abstract string TipTemplateKey { get; } + public float multiplier = 1f; + + public string GetTip() => TipTemplateKey.Translate(multiplier.ToString()); + + public override IEnumerable ConfigErrors() + { + if (multiplier == 1f) + { + yield return "There is no point if is 1"; + } + else if (multiplier <= 0f) + { + yield return " must be > 0"; + } + } } } diff --git a/Source/IdeologyAddon/Precepts/DefExtension_ModifyNecroMtb.cs b/Source/IdeologyAddon/Precepts/DefExtension_ModifyNecroMtb.cs index 3e544f7..ce35b27 100644 --- a/Source/IdeologyAddon/Precepts/DefExtension_ModifyNecroMtb.cs +++ b/Source/IdeologyAddon/Precepts/DefExtension_ModifyNecroMtb.cs @@ -2,5 +2,6 @@ { public class DefExtension_ModifyNecroMtb : DefExtension_ModifyMtb { + protected override string TipTemplateKey => "RSI_PreceptTipModifyNecroMtb"; } } diff --git a/Source/IdeologyAddon/Precepts/DefExtension_ModifyRapeCPMtb.cs b/Source/IdeologyAddon/Precepts/DefExtension_ModifyRapeCPMtb.cs index e83be70..ff09728 100644 --- a/Source/IdeologyAddon/Precepts/DefExtension_ModifyRapeCPMtb.cs +++ b/Source/IdeologyAddon/Precepts/DefExtension_ModifyRapeCPMtb.cs @@ -2,5 +2,6 @@ { public class DefExtension_ModifyRapeCPMtb : DefExtension_ModifyMtb { + protected override string TipTemplateKey => "RSI_PreceptTipModifyRapeCPMtb"; } } diff --git a/Source/IdeologyAddon/Precepts/IPreceptTipPostfix.cs b/Source/IdeologyAddon/Precepts/IPreceptTipPostfix.cs new file mode 100644 index 0000000..85411fb --- /dev/null +++ b/Source/IdeologyAddon/Precepts/IPreceptTipPostfix.cs @@ -0,0 +1,7 @@ +namespace RJWSexperience.Ideology.Precepts +{ + public interface IPreceptTipPostfix + { + string GetTip(); + } +} diff --git a/Source/IdeologyAddon/RsiMod.cs b/Source/IdeologyAddon/RsiMod.cs new file mode 100644 index 0000000..66dab60 --- /dev/null +++ b/Source/IdeologyAddon/RsiMod.cs @@ -0,0 +1,26 @@ +using UnityEngine; +using Verse; + +namespace RJWSexperience.Ideology +{ + public class RsiMod : Mod + { + public static RsiSettings Prefs { get; private set; } + + public RsiMod(ModContentPack content) : base(content) + { + Prefs = GetSettings(); + } + + public override string SettingsCategory() => Keyed.ModTitle; + + public override void DoSettingsWindowContents(Rect inRect) + { + Listing_Standard listmain = new Listing_Standard(); + listmain.Begin(inRect); + listmain.CheckboxLabeled(Keyed.PatchRomanceChanceFactor, ref Prefs.patchRomanceChanceFactor, Keyed.PatchRomanceChanceFactorTip); + listmain.CheckboxLabeled(Keyed.PatchIncestuousManualRomance, ref Prefs.patchIncestuousManualRomance, Keyed.PatchIncestuousManualRomanceTip); + listmain.End(); + } + } +} diff --git a/Source/IdeologyAddon/RsiSettings.cs b/Source/IdeologyAddon/RsiSettings.cs new file mode 100644 index 0000000..215b625 --- /dev/null +++ b/Source/IdeologyAddon/RsiSettings.cs @@ -0,0 +1,17 @@ +using Verse; + +namespace RJWSexperience.Ideology +{ + public class RsiSettings : ModSettings + { + public bool patchRomanceChanceFactor; + public bool patchIncestuousManualRomance; + + public override void ExposeData() + { + base.ExposeData(); + Scribe_Values.Look(ref patchRomanceChanceFactor, "patchSecondaryRomanceChanceFactor", true); + Scribe_Values.Look(ref patchIncestuousManualRomance, "patchIncestuousManualRomance", true); + } + } +}