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;
}
}
}
}