rjw-whoring/1.5/Source/Mod/JobGivers/JobGiver_WhoreInvitingVisitors.cs
2024-05-24 16:03:45 +03:00

300 lines
No EOL
10 KiB
C#

using System.Collections.Generic;
using System.Linq;
using HugsLib.Utils;
using RimWorld;
using rjw;
using Verse;
using Verse.AI;
//using Multiplayer.API;
namespace rjwwhoring
{
public class JobGiver_WhoreInvitingVisitors : ThinkNode_JobGiver
{
// Checks if pawn has a memory.
// Maybe not the best place for function, might be useful elsewhere too.
public static bool MemoryChecker(Pawn pawn, ThoughtDef thought)
{
Thought_Memory val = pawn.needs.mood.thoughts.memories.Memories.Find((Thought_Memory x) => (object)x.def == thought);
return val == null ? false : true;
}
public static Thought_Memory GetMemory(Pawn pawn, Pawn target, ThoughtDef thought)
{
Thought_Memory val = pawn.needs.mood.thoughts.memories.Memories.Find(
(Thought_Memory x) =>
{
if (x.def != thought)
return false;
if (x.otherPawn == null || x.otherPawn != target)
return false;
return true;
}
);
return val;
}
//[SyncMethod]
private static bool Roll_to_skip(Pawn client, Pawn whore)
{
//Rand.PopState();
//Rand.PushState(RJW_Multiplayer.PredictableSeed());
float fuckability = SexAppraiser.would_fuck(client, whore); // 0.0 to 1.
// More likely to skip other whores, because they're supposed to be working.
if (client.IsDesignatedService())
fuckability *= 0.6f;
if (WhoringBase.ClientAlwaysAccept)
{
return fuckability >= 0.1f && xxx.need_some_sex(client) >= 1f;
}
else
return fuckability >= 0.1f && xxx.need_some_sex(client) >= 1f && Rand.Chance(0.5f);
}
/*
public static Pawn Find_pawn_to_fuck(Pawn whore, Map map)
{
Pawn best_fuckee = null;
float best_distance = 1.0e6f;
foreach (Pawn q in map.mapPawns.FreeColonists)
if ((q != whore) &&
xxx.need_some_sex(q)>0 &&
whore.CanReserve(q, 1, 0) &&
q.CanReserve(whore, 1, 0) &&
Roll_to_skip(whore, q) &&
(!q.Position.IsForbidden(whore)) &&
xxx.is_healthy(q))
{
var dis = whore.Position.DistanceToSquared(q.Position);
if (dis < best_distance)
{
best_fuckee = q;
best_distance = dis;
}
}
return best_fuckee;
}
*/
private sealed class FindAttractivePawnHelper
{
internal Pawn whore;
internal bool TraitCheckFail(Pawn client)
{
if (!xxx.is_human(client))
return true;
if (!xxx.has_traits(client))
return true;
if (!(xxx.can_fuck(client) || xxx.can_be_fucked(client)) || !xxx.IsTargetPawnOkay(client))
return true;
//Log.Message("client:" + client + " whore:" + whore);
if (CompRJW.CheckPreference(client, whore) == false)
return true;
return false; // Everything ok.
}
//Use this check when client is not in the same faction as the whore
internal bool FactionCheckPass(Pawn client)
{
return ((client.Map == whore.Map) && (client.Faction != null && client.Faction != whore.Faction) && !client.IsPrisoner && !xxx.is_slave(client) && !client.HostileTo(whore));
}
//Use this check when client is in the same faction as the whore
//[SyncMethod]
internal bool RelationCheckPass(Pawn client)
{
//Rand.PopState();
//Rand.PushState(RJW_Multiplayer.PredictableSeed());
if (xxx.IsSingleOrPartnersNotHere(client) || xxx.is_lecher(client) || Rand.Value < 0.9f)
{
if (client != LovePartnerRelationUtility.ExistingLovePartner(whore))
{ //Exception for prisoners to account for PrisonerWhoreSexualEmergencyTree, which allows prisoners to try to hook up with anyone who's around (mostly other prisoners or warden)
return (client != whore) & (client.Map == whore.Map) && (client.Faction == whore.Faction || whore.IsPrisoner) && (client.IsColonist || whore.IsPrisoner) && WhoringHelper.IsHookupAppealing(whore, client);
}
}
return false;
}
}
//[SyncMethod]
public static Pawn FindAttractivePawn(Pawn whore, out int price)
{
price = 0;
if (whore == null || xxx.is_asexual(whore))
{
if (WhoringBase.DebugWhoring) ModLog.Message($" {xxx.get_pawnname(whore)} is asexual, abort");
return null;
}
//Rand.PopState();
//Rand.PushState(RJW_Multiplayer.PredictableSeed());
FindAttractivePawnHelper client = new FindAttractivePawnHelper
{
whore = whore
};
price = WhoringHelper.PriceOfWhore(whore);
int priceOfWhore = price;
IntVec3 pos = whore.Position;
IEnumerable<Pawn> potentialClients = whore.Map.mapPawns.AllPawnsSpawned;
potentialClients = potentialClients.Where(x
=> x != whore
&& !x.IsForbidden(whore)
&& !x.HostileTo(whore)
&& !x.IsPrisoner
&& !xxx.is_slave(x)
&& !x.IsColonist
//&& (!x.IsColonist || x.guest?.GuestStatus == GuestStatus.Guest)
&& x.Position.DistanceTo(pos) < 100
&& xxx.is_healthy_enough(x));
potentialClients = potentialClients.Except(potentialClients.Where(client.TraitCheckFail));
if (!whore.IsPrisoner)
potentialClients = potentialClients.Except(potentialClients.Where(x => !whore.CanReserveAndReach(x, PathEndMode.ClosestTouch, Danger.Some, 1)));
else
potentialClients = potentialClients.Except(potentialClients.Where(x => !x.CanReserveAndReach(whore, PathEndMode.ClosestTouch, Danger.Some, 1)));
if (!potentialClients.Any()) return null;
if (WhoringBase.DebugWhoring) ModLog.Message($" FindAttractivePawn number of all potential clients {potentialClients.Count()}");
//if (WhoringBase.DebugWhoring) ModLog.Message($" FindAttractivePawn number of all potential clients {potentialClients.ListElements()}");
List<Pawn> valid_targets = new List<Pawn>();
if (!whore.IsPrisoner)
foreach (Pawn target in potentialClients)
{
if(Pather_Utility.cells_to_target_casual(whore, target.Position))
if (Pather_Utility.can_path_to_target(whore, target.Position))
valid_targets.Add(target);
}
else
foreach (Pawn target in potentialClients)
{
if (Pather_Utility.cells_to_target_casual(target, whore.Position))
if (Pather_Utility.can_path_to_target(target, whore.Position))
valid_targets.Add(target);
}
if (WhoringBase.DebugWhoring) ModLog.Message($" number of reachable clients {valid_targets.Count()}");
//if (WhoringBase.DebugWhoring) ModLog.Message($" number of reachable clients {valid_targets.ListElements()}");
//IEnumerable<Pawn> guestsSpawned = valid_targets.Where(x => x.Faction != whore.Faction
// && WhoringHelper.CanAfford(x, whore, priceOfWhore));
//if (RJWSettings.DebugWhoring) ModLog.Message($" number of clients can afford {guestsSpawned.Count()}");
//guestsSpawned = valid_targets.Where(x => x.Faction != whore.Faction
// && x != LovePartnerRelationUtility.ExistingLovePartner(whore));
//if (RJWSettings.DebugWhoring) ModLog.Message($" number of relations OK {guestsSpawned.Count()}");
//guestsSpawned = valid_targets.Where(x => x.Faction != whore.Faction
// && !MemoryChecker(x, ThoughtDef.Named("RJWFailedSolicitation")));
//if (RJWSettings.DebugWhoring) ModLog.Message($" number of clients can memory OK {guestsSpawned.Count()}");
List<Pawn> guestsSpawned = new List<Pawn>();
foreach(Pawn x in valid_targets)
{
bool canAfford = WhoringHelper.CanAfford(x, whore, priceOfWhore);
Thought_Memory refusedMmeory = GetMemory(x, whore, ThoughtDef.Named("RJWFailedSolicitation"));
bool refused = refusedMmeory != null;
DirectPawnRelation relationship = LovePartnerRelationUtility.ExistingLoveRealtionshipBetween(whore, x);
bool relation = relationship != null;
bool differentFaction = x.Faction != whore.Faction;
bool finalResult = canAfford && !refused && !relation && differentFaction;
if (WhoringBase.DebugWhoring)
{
ModLog.Message($"Pawn {x.Name} is an {(finalResult ? "acceptable" : "unacceptable")} client for {whore.Name}. Explanation: canAfford {canAfford.ToString()} refused: {refused.ToString()} relation: {relation.ToString()} differentFaction: {differentFaction.ToString()}");
}
if (canAfford && !refused && !relation && differentFaction)
guestsSpawned.Add(x);
}
if (guestsSpawned.Any())
{
if (WhoringBase.DebugWhoring) ModLog.Message($" number of all acceptable Guests {guestsSpawned.Count()}");
return guestsSpawned.RandomElement();
}
return null;
//use casual sex for colonist hooking
if (WhoringBase.DebugWhoring) ModLog.Message($" found no guests, trying colonists");
if (!WhoringHelper.WillPawnTryHookup(whore))// will hookup colonists?
{
return null;
}
IEnumerable<Pawn> freeColonists = valid_targets.Where(x => x.Faction == whore.Faction
&& Roll_to_skip(x, whore));
if (WhoringBase.DebugWhoring) ModLog.Message($" number of free colonists {freeColonists.Count()}");
freeColonists = freeColonists.Where(x => client.RelationCheckPass(x) && !MemoryChecker(x, ThoughtDef.Named("RJWTurnedDownWhore")));
if (freeColonists.Any())
{
if (WhoringBase.DebugWhoring) ModLog.Message($" number of all acceptable Colonists {freeColonists.Count()}");
return freeColonists.RandomElement();
}
return null;
}
protected override Job TryGiveJob(Pawn pawn)
{
// Most things are now checked in the ThinkNode_ConditionalWhore.
if (pawn.Drafted) return null;
//if (MP.IsInMultiplayer) return null; //fix error someday, maybe
if (pawn.jobs.curDriver is JobDriver_Sex || pawn.jobs.curDriver is JobDriver_WhoreInvitingVisitors) return null; // already having sex
if (!SexUtility.ReadyForLovin(pawn))
{
//Whores need rest too, but this'll reduxe the wait a bit every it triggers.
pawn.mindState.canLovinTick -= 40;
return null;
}
if (WhoringBase.DebugWhoring) ModLog.Message($"JobGiver_WhoreInvitingVisitors.TryGiveJob:({xxx.get_pawnname(pawn)})");
int price;
Pawn client = FindAttractivePawn(pawn, out price);
if (client == null)
{
if (WhoringBase.DebugWhoring) ModLog.Message($" no clients found");
return null;
}
if (WhoringBase.DebugWhoring) ModLog.Message($" {xxx.get_pawnname(client)} is client");
Building_Bed whorebed = WhoreBed_Utility.FindBed(pawn, client);
if (whorebed == null)
{
if (WhoringBase.DebugWhoring) ModLog.Message($" {xxx.get_pawnname(pawn)} + {xxx.get_pawnname(client)} - no usable bed found");
return null;
}
whorebed.ReserveForWhoring(pawn, 600); // reserve for a short while until whore can actually ask customer
return JobMaker.MakeJob(xxx.whore_inviting_visitors, client, whorebed);
}
}
}