using HarmonyLib; using Verse; using System; using RimWorld; using System.Collections.Generic; using System.Linq; using UnityEngine; using rjw; /// /// patches Building_Bed to add stuff for WhoreBeds /// /// Also contains smaller patches for RoomRoleWorker_Barracks (don't count whore beds) (disabled), Toils_LayDown.ApplyBedThoughts (slept in brothel thought) and RestUtility (don't automatically claim brothel beds) /// namespace rjwwhoring { public static class harmony_Building_BedPatches { private static readonly Color sheetColorForWhores = new Color(181 / 255f, 55 / 255f, 109 / 255f); // Set color for whore beds [HarmonyPatch(typeof(Building_Bed))] [HarmonyPatch(nameof(Building_Bed.DrawColorTwo), MethodType.Getter)] public static class Building_Bed_DrawColor_Patch { [HarmonyPostfix] public static void Postfix(Building_Bed __instance, ref Color __result) { if (__instance.IsAllowedForWhoringAll()) { __result = sheetColorForWhores; } } } // add whoring toggles to beds [HarmonyPatch(typeof(Building_Bed), nameof(Building_Bed.GetGizmos))] public static class Building_Bed_GetGizmos_Patch { [HarmonyPostfix] public static void Postfix(Building_Bed __instance, ref IEnumerable __result) { if (!WhoringBase.show_whore_widgets_on_bed) { return; } __result = Process(__instance, __result); } private static IEnumerable Process(Building_Bed __instance, IEnumerable __result) { var isPrisonCell = __instance.GetRoom()?.IsPrisonCell == true; if (!__instance.ForPrisoners && !__instance.Medical && __instance.def.building.bed_humanlike && __instance.Faction == Faction.OfPlayerSilentFail && !__instance.def.defName.Contains("Guest") && !__instance.def.defName.Contains("Android") && !isPrisonCell) { yield return new Command_Toggle { defaultLabel = "CommandBedAllowWhoringLabel".Translate(), defaultDesc = "CommandBedAllowWhoringDesc".Translate(), icon = ContentFinder.Get("UI/Commands/AsWhore"), isActive = __instance.IsAllowedForWhoringOwner, toggleAction = __instance.ToggleAllowedForWhoringOwner, hotKey = KeyBindingDefOf.Misc5, // Guest Beds uses Misc4 Disabled = !__instance.def.HasAssignableCompFrom(typeof(CompAssignableToPawn_Bed)), disabledReason = "This bed type is not assignable to pawns." }; yield return new Command_Toggle { defaultLabel = "CommandBedSetAsWhoreBedLabel".Translate(), defaultDesc = "CommandBedSetAsWhoreBedDesc".Translate(), icon = ContentFinder.Get("UI/Commands/AsWhoreMany"), isActive = __instance.IsAllowedForWhoringAll, toggleAction = __instance.ToggleAllowedForWhoringAll, hotKey = KeyBindingDefOf.Misc6, // Guest Beds uses Misc4 Disabled = !__instance.def.HasAssignableCompFrom(typeof(CompAssignableToPawn_Bed)), disabledReason = "This bed type is not assignable to pawns." }; } foreach (var gizmo in __result) { if (__instance.IsAllowedForWhoringAll()) { if (gizmo is Command_Toggle && ((Command_Toggle)gizmo).defaultLabel == "CommandBedSetAsGuestLabel".Translate()) { // hide set as guest bed continue; }; // old: instead of hiding, just disable /*switch (gizmo) { case Command_Toggle toggle: { // Disable prisoner and medical, and guest buttons if (//toggle.defaultLabel == "CommandBedSetForPrisonersLabel".Translate() || //toggle.defaultLabel == "CommandBedSetAsMedicalLabel".Translate() || toggle.defaultLabel == "CommandBedSetAsGuestLabel".Translate()) gizmo.Disable(); break; } }//*/ } yield return gizmo; } } } // add description of whore price factor to inspect string (bottom left corner if item selected) [HarmonyPatch(typeof(Building_Bed), nameof(Building_Bed.GetInspectString))] public static class Building_Bed_GetInspectString_Patch { [HarmonyPostfix] public static void Postfix(Building_Bed __instance, ref string __result) { if (__instance.def.building.bed_humanlike && __instance.Faction == Faction.OfPlayerSilentFail && (__instance.IsAllowedForWhoringAll() || __instance.IsAllowedForWhoringOwner())) { __result = __result + "\n" + "WhorePriceCalcDesc".Translate(WhoreBed_Utility.CalculatePriceFactor(__instance).ToString("F2")); if (WhoringBase.DebugWhoring) { __result = __result + "\nbed.thingIDNumber: " + __instance.thingIDNumber.ToString(); __result = __result + "\nscoreUpdateTickDelay: " + WhoringBase.DataStore.GetBedData(__instance).scoreUpdateTickDelay.ToString(); if (WhoringBase.DataStore.GetBedData(__instance).reservedUntilGameTick > GenTicks.TicksGame) { __result = __result + "\nreserved by pawn id: " + WhoringBase.DataStore.GetBedData(__instance).reservedForPawnID.ToString(); } } } } } // add whore price factor as overlay [HarmonyPatch(typeof(Building_Bed), nameof(Building_Bed.DrawGUIOverlay))] public static class Building_Bed_DrawGUIOverlay_Patch { [HarmonyPrefix] public static bool Prefix(Building_Bed __instance) { if (WhoringBase.show_whore_price_factor_on_bed && __instance.def.building.bed_humanlike && __instance.Faction == Faction.OfPlayerSilentFail) { // if whore bed, print price factor on it if (Find.CameraDriver.CurrentZoom == CameraZoomRange.Closest && ((__instance.IsAllowedForWhoringOwner() && __instance.OwnersForReading.Any()) || __instance.IsAllowedForWhoringAll())) { Color defaultThingLabelColor = GenMapUI.DefaultThingLabelColor; // make string float whore_price_factor = WhoreBed_Utility.CalculatePriceFactor(__instance); string wpf; if (Math.Abs(whore_price_factor) >= 100) { wpf = ((int)whore_price_factor).ToString("D"); } else if (Math.Abs(whore_price_factor) >= 10) { wpf = whore_price_factor.ToString("F1"); } else { wpf = whore_price_factor.ToString("F2"); } // get dimensions of text and make it appear above names Vector2 textsize = Text.CalcSize(wpf); Vector2 baseLabelPos = GenMapUI.LabelDrawPosFor(__instance, -0.4f); // -0.4f is copied from vanilla code baseLabelPos.y -= textsize.y * 0.75f; GenMapUI.DrawThingLabel(baseLabelPos, wpf, defaultThingLabelColor); if (__instance.IsAllowedForWhoringAll() && !__instance.OwnersForReading.Any()) { // hide "Unowned" if whore bed with no owner return false; } } } // after drawing whore price factor, draw the actual names // could have been done as a postfix, but I started with a prefix, hoping I could get by with only one draw call return true; } } // barracks don't count whore beds, so room type switches to brothel sooner // disabled - barracks have their own slept in ~ debuff; doesn't really matter; put some effort in your brothels! /*[HarmonyPatch(typeof(RoomRoleWorker_Barracks), nameof(RoomRoleWorker_Barracks.GetScore))] public static class RoomRoleWorker_Barracks_GetScore_Patch { public static bool Prefix(Room room, ref float __result) { int num = 0; int num2 = 0; List containedAndAdjacentThings = room.ContainedAndAdjacentThings; for (int i = 0; i < containedAndAdjacentThings.Count; i++) { Building_Bed building_Bed = containedAndAdjacentThings[i] as Building_Bed; if (building_Bed != null && building_Bed.def.building.bed_humanlike) { if (building_Bed.ForPrisoners) { __result = 0f; return false; } num++; if (!building_Bed.Medical && !building_Bed.IsAllowedForWhoringAll()) { num2++; } } } if (num <= 1) { __result = 0f; return false; } __result = (float)num2 * 100100f; return false; } }*/ // if pawns sleep in a brothel or a whoring bed, they get a thought [HarmonyPatch(typeof(Toils_LayDown), "ApplyBedThoughts")] public class Toils_LayDown_ApplyBedThoughts_Patch { [HarmonyPostfix] public static void Postfix(Pawn actor) { if (actor?.needs?.mood == null) return; Building_Bed building_Bed = actor.CurrentBed(); actor?.needs?.mood?.thoughts?.memories?.RemoveMemoriesOfDef(ThoughtDefOf.SleptInBrothel); if (building_Bed == null) return; if (building_Bed?.GetRoom()?.Role == WhoreBed_Utility.roleDefBrothel || building_Bed.IsAllowedForWhoringAll()) { var memoryHandler = actor.needs.mood.thoughts.memories; int thoughtStage = 0; foreach (var thoughtDef in DefDatabase.AllDefsListForReading) { var memory = memoryHandler.GetFirstMemoryOfDef(thoughtDef); if (memory?.CurStageIndex >= thoughtDef.stages.Count - 1) { thoughtStage = 1; break; } } memoryHandler.TryGainMemory(ThoughtMaker.MakeThought(ThoughtDefOf.SleptInBrothel, thoughtStage)); } } } // if room stats are updated, update beds within // "necessary" if beds are toggled during pause [HarmonyPatch(typeof(Room), "UpdateRoomStatsAndRole")] public class Room_UpdateRoomStatsAndRole_Patch { [HarmonyPostfix] public static void Postfix(Room __instance) { // note: with room stat display enabled, this get's called quite often for whatever room the mouse hovers over // even large outdoor areas // where iterating over all things to find all beds (even if there are none) is expensive // for now, skip doing anything if even the game decides it's not worth it // (game sets role to None if region count >36 or something) // - the beds will update eventually if (/*Find.PlaySettings.showRoomStats && */__instance.Role == RoomRoleDefOf.None) return; if (Find.TickManager.Paused) { // if paused, update immediately WhoreBed_Utility.CalculateBedFactorsForRoom(__instance); } else { // else, just make beds update as soon as needed WhoreBed_Utility.ResetTicksUntilUpdate(__instance); } } } /// ///Prevents automatic claiming of brothel beds (beds that allow anyone to use for whoring) ///Note, that intent is not verified here, and this works because bed usage for actual whoring does not rely on IsValidBedFor call. ///Should above change in future, this patch needs to be removed or adjusted ///If the bed is already claimed (for example - assigned manually to pawn), it will still be used. ///Modifies __result to false, if bed is set to allow anyone for whroing AND is not already claimed. /// [HarmonyPatch(typeof(RestUtility), nameof(RestUtility.IsValidBedFor))] public class RestUtility_IsValidBedFor_Patch { [HarmonyPostfix] public static void Postfix(Pawn sleeper, Thing bedThing, ref bool __result) { if (!__result) return; Building_Bed building_Bed = bedThing as Building_Bed; bool isOwner = sleeper.ownership != null && sleeper.ownership.OwnedBed == bedThing; if (building_Bed.IsAllowedForWhoringAll() && !isOwner ) __result = false; } } } }