Example #1
0
        private bool DoesTargetPawnAcceptAdvance()
        {
            //if (xxx.config.always_accept_whores)
            //	return true;

            //Log.Message("[RJW]JobDriver_InvitingVisitors::DoesTargetPawnAcceptAdvance() is called");

            if (PawnUtility.EnemiesAreNearby(TargetPawn))
            {
                //Log.Message("[RJW]JobDriver_InvitingVisitors::DoesTargetPawnAcceptAdvance() fail - enemy near");
                return(false);
            }
            if (!allowedJobs.Contains(TargetPawn.jobs.curJob.def))
            {
                //Log.Message("[RJW]JobDriver_InvitingVisitors::DoesTargetPawnAcceptAdvance() fail - not allowed job");
                return(false);
            }

            //Log.Message("Will try " + WhoringHelper.WillPawnTryHookup(TargetPawn));
            //Log.Message("Appeal  " + WhoringHelper.IsHookupAppealing(TargetPawn, W***e));
            //Log.Message("Afford " + WhoringHelper.CanAfford(TargetPawn, W***e));
            //Log.Message("Need sex " + (xxx.need_some_sex(TargetPawn) >= 1));
            W***e.skills.Learn(SkillDefOf.Social, 1.2f);
            return(WhoringHelper.WillPawnTryHookup(TargetPawn) && WhoringHelper.IsHookupAppealing(TargetPawn, W***e) && WhoringHelper.CanAfford(TargetPawn, W***e) && xxx.need_some_sex(TargetPawn) >= 1f);
        }
Example #2
0
        static string DrawWhoring(Pawn pawn, Rect row)
        {
            string price;

            if (pawn.ageTracker.AgeBiologicalYears < RJWSettings.sex_minimum_age)
            {
                price = "Inapplicable (too young)";
            }
            else if (pawn.ownership.OwnedRoom == null)
            {
                if (Current.ProgramState == ProgramState.Playing)
                {
                    price = WhoringHelper.WhoreMinPrice(pawn) + " - " + WhoringHelper.WhoreMaxPrice(pawn) + " (base, needs suitable bedroom)";
                }
                else
                {
                    price = WhoringHelper.WhoreMinPrice(pawn) + " - " + WhoringHelper.WhoreMaxPrice(pawn) + " (base, modified by bedroom quality)";
                }
            }
            else if (xxx.is_animal(pawn))
            {
                price = "Incapable of whoring";
            }
            else
            {
                price = WhoringHelper.WhoreMinPrice(pawn) + " - " + WhoringHelper.WhoreMaxPrice(pawn);
            }

            Widgets.Label(row, "WhorePrice".Translate() + price);
            if (Mouse.IsOver(row))
            {
                Widgets.DrawHighlight(row);
            }
            return(price);
        }
        private bool DoesTargetPawnAcceptAdvance()
        {
            if (RJWSettings.DebugWhoring)
            {
                Log.Message($"JobDriver_InvitingVisitors::DoesTargetPawnAcceptAdvance() - {xxx.get_pawnname(TargetPawn)}");
            }
            //if (RJWSettings.WildMode) return true;

            if (PawnUtility.EnemiesAreNearby(TargetPawn))
            {
                if (RJWSettings.DebugWhoring)
                {
                    Log.Message($" fail - enemy near");
                }
                return(false);
            }
            if (!allowedJobs.Contains(TargetPawn.jobs.curJob.def))
            {
                if (RJWSettings.DebugWhoring)
                {
                    Log.Message($" fail - not allowed job");
                }
                return(false);
            }

            if (RJWSettings.DebugWhoring)
            {
                Log.Message("Will try hookup " + WhoringHelper.WillPawnTryHookup(TargetPawn));
                Log.Message("Is w***e appealing  " + WhoringHelper.IsHookupAppealing(TargetPawn, W***e));
                Log.Message("Can afford w***e " + WhoringHelper.CanAfford(TargetPawn, W***e));
                Log.Message("Need sex " + (xxx.need_some_sex(TargetPawn) >= 1));
            }
            if (WhoringHelper.WillPawnTryHookup(TargetPawn) && WhoringHelper.IsHookupAppealing(TargetPawn, W***e) && WhoringHelper.CanAfford(TargetPawn, W***e) && xxx.need_some_sex(TargetPawn) >= 1f)
            {
                W***e.skills.Learn(SkillDefOf.Social, 1.2f);
                return(true);
            }
            return(false);
        }
 internal bool RelationCheckPass(Pawn client)
 {
     //Rand.PopState();
     //Rand.PushState(RJW_Multiplayer.PredictableSeed());
     if (xxx.isSingleOrPartnerNotHere(client) || xxx.is_lecher(client) || Rand.Value < 0.9f)
     {
         if (client != LovePartnerRelationUtility.ExistingLovePartner(w***e))
         {                     //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 != w***e) & (client.Map == w***e.Map) && (client.Faction == w***e.Faction || w***e.IsPrisoner) && (client.IsColonist || w***e.IsPrisoner) && WhoringHelper.IsHookupAppealing(w***e, client));
         }
     }
     return(false);
 }
        public static Pawn FindAttractivePawn(Pawn w***e, out int price)
        {
            price = 0;
            if (w***e == null || xxx.is_asexual(w***e))
            {
                if (RJWSettings.DebugWhoring)
                {
                    Log.Message($" {xxx.get_pawnname(w***e)} is asexual, abort");
                }
                return(null);
            }
            //Rand.PopState();
            //Rand.PushState(RJW_Multiplayer.PredictableSeed());

            FindAttractivePawnHelper client = new FindAttractivePawnHelper
            {
                w***e = w***e
            };

            price = WhoringHelper.PriceOfWhore(w***e);
            int priceOfWhore = price;

            IntVec3 pos = w***e.Position;

            IEnumerable <Pawn> potentialClients = w***e.Map.mapPawns.AllPawnsSpawned;

            potentialClients = potentialClients.Where(x
                                                      => x != w***e &&
                                                      !x.IsForbidden(w***e) &&
                                                      !x.HostileTo(w***e) &&
                                                      !x.IsPrisoner &&
                                                      x.Position.DistanceTo(pos) < 100 &&
                                                      w***e.CanReserveAndReach(x, PathEndMode.ClosestTouch, Danger.Some, 1) &&
                                                      xxx.is_healthy_enough(x));

            potentialClients = potentialClients.Except(potentialClients.Where(client.TraitCheckFail));

            if (!potentialClients.Any())
            {
                return(null);
            }

            if (RJWSettings.DebugWhoring)
            {
                Log.Message($" FindAttractivePawn number of all potential clients {potentialClients.Count()}");
            }

            List <Pawn> valid_targets = new List <Pawn>();

            foreach (Pawn target in potentialClients)
            {
                if (xxx.can_path_to_target(w***e, target.Position))
                {
                    valid_targets.Add(target);
                }
            }

            IEnumerable <Pawn> guestsSpawned = valid_targets.Where(x => x.Faction != w***e.Faction &&
                                                                   WhoringHelper.CanAfford(x, w***e, priceOfWhore) &&
                                                                   !MemoryChecker(x, ThoughtDef.Named("RJWFailedSolicitation")) &&
                                                                   x != LovePartnerRelationUtility.ExistingLovePartner(w***e));

            if (guestsSpawned.Any())
            {
                if (RJWSettings.DebugWhoring)
                {
                    Log.Message($" FindAttractivePawn number of all acceptable Guests {guestsSpawned.Count()}");
                }
                return(guestsSpawned.RandomElement());
            }

            return(null);

            //use casual sex for colonist hooking
            if (RJWSettings.DebugWhoring)
            {
                Log.Message($" FindAttractivePawn found no guests, trying colonists");
            }

            if (!WhoringHelper.WillPawnTryHookup(w***e))            // will hookup colonists?
            {
                return(null);
            }
            IEnumerable <Pawn> freeColonists = valid_targets.Where(x => x.Faction == w***e.Faction &&
                                                                   Roll_to_skip(x, w***e));

            if (RJWSettings.DebugWhoring)
            {
                Log.Message($" FindAttractivePawn number of free colonists {freeColonists.Count()}");
            }

            freeColonists = freeColonists.Where(x => client.RelationCheckPass(x) && !MemoryChecker(x, ThoughtDef.Named("RJWTurnedDownWhore")));

            if (freeColonists.Any())
            {
                if (RJWSettings.DebugWhoring)
                {
                    Log.Message($" FindAttractivePawn number of all acceptable Colonists {freeColonists.Count()}");
                }
                return(freeColonists.RandomElement());
            }

            return(null);
        }
        protected override IEnumerable <Toil> MakeNewToils()
        {
            //Log.Message("[RJW]JobDriver_WhoreIsServingVisitors::MakeNewToils() - making toils");
            this.FailOnDespawnedOrNull(PartnerInd);
            this.FailOnDespawnedNullOrForbidden(BedInd);
            //Log.Message("[RJW]JobDriver_WhoreIsServingVisitors::MakeNewToils() fail conditions check " + (Actor is null) + " " + !xxx.CanUse(Actor, Bed) + " " + !Actor.CanReserve(Partner));
            this.FailOn(() => Actor is null || !xxx.CanUse(Actor, Bed) || !Actor.CanReserve(Partner));
            this.FailOn(() => pawn.Drafted);
            int price = WhoringHelper.PriceOfWhore(Actor);

            yield return(Toils_Reserve.Reserve(PartnerInd, 1, 0));

            //yield return Toils_Reserve.Reserve(BedInd, Bed.SleepingSlotsCount, 0);
            bool partnerHasPenis = Genital_Helper.has_penis(Partner) || Genital_Helper.has_penis_infertile(Partner);

            Toil gotoWhoreBed = new Toil
            {
                initAction = delegate
                {
                    //Log.Message("[RJW]JobDriver_WhoreIsServingVisitors::MakeNewToils() - gotoWhoreBed initAction is called");
                    Actor.pather.StartPath(WhoreSleepSpot, PathEndMode.OnCell);
                    Partner.jobs.StopAll();
                    Partner.pather.StartPath(WhoreSleepSpot, PathEndMode.Touch);
                },
                tickAction = delegate
                {
                    if (Partner.IsHashIntervalTick(150))
                    {
                        Partner.pather.StartPath(Actor, PathEndMode.Touch);
                        //Log.Message(xxx.get_pawnname(Partner) + ": I'm following the w***e");
                    }
                },
                defaultCompleteMode = ToilCompleteMode.PatherArrival
            };

            gotoWhoreBed.FailOnWhorebedNoLongerUsable(BedInd, Bed);
            yield return(gotoWhoreBed);

            Toil waitInBed = new Toil
            {
                initAction = delegate
                {
                    //Rand.PopState();
                    //Rand.PushState(RJW_Multiplayer.PredictableSeed());
                    //Log.Message("JobDriver_WhoreIsServingVisitors::MakeNewToils() - waitInBed, initAction is called");
                    ticksLeftThisToil = 5000;
                    ticks_left        = (int)(2000.0f * Rand.Range(0.30f, 1.30f));
                    //Actor.pather.StopDead();  //Let's just make w****s standing at the bed
                    //JobDriver curDriver = Actor.jobs.curDriver;
                    //curDriver.layingDown = LayingDownState.LayingInBed;
                    //curDriver.asleep = false;
                    var gettin_loved = new Job(xxx.gettin_loved, Actor, Bed);
                    Partner.jobs.StartJob(gettin_loved, JobCondition.InterruptForced);
                },
                tickAction = delegate
                {
                    Actor.GainComfortFromCellIfPossible();
                    if (IsInOrByBed(Bed, Partner))
                    {
                        //Log.Message("JobDriver_WhoreIsServingVisitors::MakeNewToils() - waitInBed, tickAction pass");
                        ticksLeftThisToil = 0;
                    }
                },
                defaultCompleteMode = ToilCompleteMode.Delay,
            };

            waitInBed.FailOn(() => pawn.GetRoom() == null);
            yield return(waitInBed);

            bool canAfford = WhoringHelper.CanAfford(Partner, Actor, price);

            if (canAfford)
            {
                Toil loveToil = new Toil
                {
                    initAction = delegate
                    {
                        //Actor.jobs.curDriver.ticksLeftThisToil = 1200;
                        //Using ticks_left to control the time of sex
                        //--Log.Message("JobDriver_WhoreIsServingVisitors::MakeNewToils() - loveToil, setting initAction");

                        /*
                         * //Hoge: W***e is just work. no feel cheatedOnMe.
                         * if (xxx.HasNonPolyPartner(Actor))
                         * {
                         *      Pawn pawn = LovePartnerRelationUtility.ExistingLovePartner(Actor);
                         *      if (((Partner != pawn) && !pawn.Dead) && ((pawn.Map == Actor.Map) || (Rand.Value < 0.15)))
                         *      {
                         *              pawn.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.CheatedOnMe, Actor);
                         *      }
                         * }
                         */
                        if (xxx.HasNonPolyPartnerOnCurrentMap(Partner))
                        {
                            Pawn lover = LovePartnerRelationUtility.ExistingLovePartner(Partner);
                            //Rand.PopState();
                            //Rand.PushState(RJW_Multiplayer.PredictableSeed());
                            // We have to do a few other checks because the pawn might have multiple lovers and ExistingLovePartner() might return the wrong one
                            if (lover != null && Actor != lover && !lover.Dead && (lover.Map == Partner.Map || Rand.Value < 0.25))
                            {
                                lover.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.CheatedOnMe, Partner);
                            }
                        }
                        if (!partnerHasPenis)
                        {
                            Actor.rotationTracker.Face(Partner.DrawPos);
                        }
                    },
                    defaultCompleteMode = ToilCompleteMode.Never,                     //Changed from Delay
                };
                loveToil.AddPreTickAction(delegate
                {
                    //Actor.Reserve(Partner, 1, 0);
                    --ticks_left;
                    xxx.reduce_rest(Partner);
                    xxx.reduce_rest(Actor, 2);

                    if (ticks_left <= 0)
                    {
                        ReadyForNextToil();
                    }
                    else if (pawn.IsHashIntervalTick(ticks_between_hearts))
                    {
                        MoteMaker.ThrowMetaIcon(Actor.Position, Actor.Map, ThingDefOf.Mote_Heart);
                    }
                    Actor.GainComfortFromCellIfPossible();
                    Partner.GainComfortFromCellIfPossible();
                });
                loveToil.AddFinishAction(delegate
                {
                    //Log.Message("[RJW] JobDriver_WhoreIsServingVisitors::MakeNewToils() - finished loveToil");
                    //// Trying to add some interactions and social logs
                    //xxx.processAnalSex(Partner, Actor, ref isAnalSex, partnerHasPenis);
                });
                loveToil.AddFailCondition(() => Partner.Dead || !IsInOrByBed(Bed, Partner));
                loveToil.socialMode = RandomSocialMode.Off;
                yield return(loveToil);

                Toil afterSex = new Toil
                {
                    initAction = delegate
                    {
                        // Adding interactions, social logs, etc
                        SexUtility.ProcessSex(Actor, Partner, false, false, true);

                        //--Log.Message("JobDriver_WhoreIsServingVisitors::MakeNewToils() - Partner should pay the price now in afterSex.initAction");
                        int remainPrice = WhoringHelper.PayPriceToWhore(Partner, price, Actor);

                        /*if (remainPrice <= 0)
                         * {
                         *      --Log.Message("JobDriver_WhoreIsServingVisitors::MakeNewToils() - Paying price is success");
                         * }
                         * else
                         * {
                         *      --Log.Message("JobDriver_WhoreIsServingVisitors::MakeNewToils() - Paying price failed");
                         * }*/
                        xxx.UpdateRecords(Actor, price - remainPrice);
                        var thought = (Actor.IsPrisoner) ? thought_captive : thought_free;
                        pawn.needs.mood.thoughts.memories.TryGainMemory(thought);
                        if (SexUtility.ConsiderCleaning(pawn))
                        {
                            LocalTargetInfo cum = pawn.PositionHeld.GetFirstThing <Filth>(pawn.Map);

                            Job clean = new Job(JobDefOf.Clean);
                            clean.AddQueuedTarget(TargetIndex.A, cum);

                            pawn.jobs.jobQueue.EnqueueFirst(clean);
                        }
                    },
                    defaultCompleteMode = ToilCompleteMode.Instant
                };
                yield return(afterSex);
            }
        }
        public void SexualityCard(Rect rect, Pawn pawn)
        {
            CompRJW comp = pawn.TryGetComp <CompRJW>();

            if (pawn == null || comp == null)
            {
                return;
            }

            Text.Font = GameFont.Medium;
            Rect rect1 = new Rect(8f, 4f, rect.width - 8f, rect.height - 20f);

            Widgets.Label(rect1, "RJW");            //rjw

            Text.Font = GameFont.Tiny;
            float num  = rect1.y + 40f;
            Rect  row1 = new Rect(10f, num, rect.width - 8f, 24f);           //sexuality
            Rect  row2 = new Rect(10f, num + 24, rect.width - 8f, 24f);      //quirks
            Rect  row3 = new Rect(10f, num + 48, rect.width - 8f, 24f);      //w***e price

            //Rect sexuality_button = new Rect(10f, rect1.height - 0f, rect.width - 8f, 24f);//change sex pref
            Rect button1 = new Rect(10f, rect1.height - 10f, rect.width - 8f, 24f);            //re sexualize
            Rect button2 = new Rect(10f, rect1.height - 34f, rect.width - 8f, 24f);            //archtech toggle
            Rect button3 = new Rect(10f, rect1.height - 58f, rect.width - 8f, 24f);            //breast
            Rect button4 = new Rect(10f, rect1.height - 82f, rect.width - 8f, 24f);            //anus
            Rect button5 = new Rect(10f, rect1.height - 106f, rect.width - 8f, 24f);           //v****a
            Rect button6 = new Rect(10f, rect1.height - 130f, rect.width - 8f, 24f);           //penis 1
            Rect button7 = new Rect(10f, rect1.height - 154f, rect.width - 8f, 24f);           //penis 2

            string price;
            string sexuality;

            // Check for Rational Romance consistency, in case the player adds it mid-game or adds traits (such as with Prepare Carefully)
            if (xxx.RomanceDiversifiedIsActive || pawn.story.traits.HasTrait(TraitDefOf.Gay))
            {
                if (RJWPreferenceSettings.sexuality_distribution == RJWPreferenceSettings.Rjw_sexuality.RationalRomance)
                {
                    CompRJW.RRTraitCheck(pawn);
                }
            }

            switch (CompRJW.Comp(pawn).orientation)
            {
            case Orientation.Asexual:
                sexuality = "Asexual";
                break;

            case Orientation.Bisexual:
                sexuality = "Bisexual";
                break;

            case Orientation.Heterosexual:
                sexuality = "Hetero";
                break;

            case Orientation.Homosexual:
                sexuality = "Gay";
                break;

            case Orientation.LeaningHeterosexual:
                sexuality = "Bisexual, leaning hetero";
                break;

            case Orientation.LeaningHomosexual:
                sexuality = "Bisexual, leaning gay";
                break;

            case Orientation.MostlyHeterosexual:
                sexuality = "Mostly hetero";
                break;

            case Orientation.MostlyHomosexual:
                sexuality = "Mostly gay";
                break;

            case Orientation.Pansexual:
                sexuality = "Pansexual";
                break;

            default:
                sexuality = "None";
                break;
            }

            //allow to change own hero sexuality
            if (RJWPreferenceSettings.sexuality_distribution == RJWPreferenceSettings.Rjw_sexuality.RimJobWorld &&
                Current.ProgramState == ProgramState.Playing &&
                pawn.IsDesignatedHero() && pawn.IsHeroOwner())

            {
                if (Widgets.ButtonText(row1, "Sexuality: " + sexuality))
                {
                    Find.WindowStack.Add(new FloatMenu(new List <FloatMenuOption>()
                    {
                        new FloatMenuOption("Asexual", (() => Change_orientation(pawn, Orientation.Asexual)), MenuOptionPriority.Default),
                        new FloatMenuOption("Pansexual", (() => Change_orientation(pawn, Orientation.Pansexual)), MenuOptionPriority.Default),
                        new FloatMenuOption("Heterosexual", (() => Change_orientation(pawn, Orientation.Heterosexual)), MenuOptionPriority.Default),
                        new FloatMenuOption("MostlyHeterosexual", (() => Change_orientation(pawn, Orientation.MostlyHeterosexual)), MenuOptionPriority.Default),
                        new FloatMenuOption("LeaningHeterosexual", (() => Change_orientation(pawn, Orientation.LeaningHeterosexual)), MenuOptionPriority.Default),
                        new FloatMenuOption("Bisexual", (() => Change_orientation(pawn, Orientation.Bisexual)), MenuOptionPriority.Default),
                        new FloatMenuOption("LeaningHomosexual", (() => Change_orientation(pawn, Orientation.LeaningHomosexual)), MenuOptionPriority.Default),
                        new FloatMenuOption("MostlyHomosexual", (() => Change_orientation(pawn, Orientation.MostlyHomosexual)), MenuOptionPriority.Default),
                        new FloatMenuOption("Homosexual", (() => Change_orientation(pawn, Orientation.Homosexual)), MenuOptionPriority.Default),
                    }));
                }
            }
            else
            {
                Widgets.Label(row1, "Sexuality: " + sexuality);
                if (Mouse.IsOver(row1))
                {
                    Widgets.DrawHighlight(row1);
                }
            }

            string quirklist = CompRJW.Comp(pawn).quirks.ToString();

            Widgets.Label(row2, "Quirks".Translate() + quirklist);
            if (Mouse.IsOver(row2))
            {
                Widgets.DrawHighlight(row2);
                if (quirklist == "None")
                {
                    TooltipHandler.TipRegion(row2, "NoQuirks".Translate());
                }
                else
                {
                    StringBuilder tooltip = new StringBuilder();

                    if (quirklist.Contains("Breeder"))
                    {
                        tooltip.AppendLine("BreederQuirk".Translate());
                    }

                    if (quirklist.Contains("Endytophile"))
                    {
                        tooltip.AppendLine("EndytophileQuirk".Translate());
                    }

                    if (quirklist.Contains("Exhibitionist"))
                    {
                        tooltip.AppendLine("ExhibitionistQuirk".Translate());
                    }

                    if (quirklist.Contains("Fertile"))
                    {
                        tooltip.AppendLine("FertileQuirk".Translate());
                    }

                    if (quirklist.Contains("Gerontophile"))
                    {
                        tooltip.AppendLine("GerontophileQuirk".Translate());
                    }

                    if (quirklist.Contains("Impregnation fetish"))
                    {
                        tooltip.AppendLine("ImpregnationFetishQuirk".Translate());
                    }

                    if (quirklist.Contains("Incubator"))
                    {
                        tooltip.AppendLine("IncubatorQuirk".Translate());
                    }

                    if (quirklist.Contains("Infertile"))
                    {
                        tooltip.AppendLine("InfertileQuirk".Translate());
                    }

                    if (quirklist.Contains("Messy"))
                    {
                        tooltip.AppendLine("MessyQuirk".Translate());
                    }

                    if (quirklist.Contains("Podophile"))
                    {
                        tooltip.AppendLine("PodophileQuirk".Translate());
                    }

                    if (quirklist.Contains("Pregnancy fetish"))
                    {
                        tooltip.AppendLine("PregnancyFetishQuirk".Translate());
                    }

                    if (quirklist.Contains("Sapiosexual"))
                    {
                        tooltip.AppendLine("SapiosexualQuirk".Translate());
                    }

                    if (quirklist.Contains("Somnophile"))
                    {
                        tooltip.AppendLine("SomnophileQuirk".Translate());
                    }

                    if (quirklist.Contains("Teratophile"))
                    {
                        tooltip.AppendLine("TeratophileQuirk".Translate());
                    }

                    if (quirklist.Contains("Vigorous"))
                    {
                        tooltip.AppendLine("VigorousQuirk".Translate());
                    }

                    TooltipHandler.TipRegion(row2, tooltip.ToString());
                }
            }

            if (RJWSettings.sex_minimum_age > pawn.ageTracker.AgeBiologicalYears)
            {
                price = "Inapplicable (too young)";
            }
            else if (pawn.ownership.OwnedRoom == null)
            {
                if (Current.ProgramState == ProgramState.Playing)
                {
                    price = WhoringHelper.WhoreMinPrice(pawn) + " - " + WhoringHelper.WhoreMaxPrice(pawn) + " (base, needs suitable bedroom)";
                }
                else
                {
                    price = WhoringHelper.WhoreMinPrice(pawn) + " - " + WhoringHelper.WhoreMaxPrice(pawn) + " (base, modified by bedroom quality)";
                }
            }
            else if (xxx.is_animal(pawn))
            {
                price = "Incapable of whoring";
            }
            else
            {
                price = WhoringHelper.WhoreMinPrice(pawn) + " - " + WhoringHelper.WhoreMaxPrice(pawn);
            }

            Widgets.Label(row3, "WhorePrice".Translate() + price);
            if (Mouse.IsOver(row3))
            {
                Widgets.DrawHighlight(row3);
            }

            // TODO: Add translations. or not
            if (Prefs.DevMode || Current.ProgramState != ProgramState.Playing)
            {
                if (Widgets.ButtonText(button1, Current.ProgramState != ProgramState.Playing ? "Reroll sexuality" : "[DEV] Reroll sexuality"))
                {
                    Re_sexualize(pawn);
                }
            }
            if (pawn.health.hediffSet.HasHediff(Genital_Helper.archotech_penis) || pawn.health.hediffSet.HasHediff(Genital_Helper.archotech_vagina))
            {
                if (pawn.health.hediffSet.HasHediff(HediffDef.Named("ImpregnationBlocker")))
                {
                    if (Widgets.ButtonText(button2, "[Archotech genitalia] Enable reproduction"))
                    {
                        Change_Archotechmode(pawn);
                    }
                }
                else if (!pawn.health.hediffSet.HasHediff(HediffDef.Named("FertilityEnhancer")))
                {
                    if (Widgets.ButtonText(button2, "[Archotech genitalia] Enchance fertility"))
                    {
                        Change_Archotechmode(pawn);
                    }
                }
                else if (Widgets.ButtonText(button2, "[Archotech genitalia] Disable reproduction"))
                {
                    Change_Archotechmode(pawn);
                }
            }
            // TODO: add mp synchronizers
            // TODO: clean that mess
            // TODO: add demon toggles
            if (MP.IsInMultiplayer)
            {
                return;
            }
            if (xxx.is_slime(pawn) && (pawn.IsColonistPlayerControlled || pawn.IsPrisonerOfColony))
            {
                BodyPartRecord bpr_genitalia = Genital_Helper.get_genitals(pawn);
                BodyPartRecord bpr_breasts   = Genital_Helper.get_breasts(pawn);
                BodyPartRecord bpr_anus      = Genital_Helper.get_anus(pawn);
                BodyPartRecord bpr           = null;
                HediffDef      hed           = null;

                if (Widgets.ButtonText(button3, "Morph breasts"))
                {
                    Find.WindowStack.Add(new FloatMenu(new List <FloatMenuOption>()
                    {
                        new FloatMenuOption("none", (() => Breasts = breasts.none), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.featureless_chest.label.CapitalizeFirst(), (() => Breasts = breasts.featureless_chest), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.flat_breasts.label.CapitalizeFirst(), (() => Breasts = breasts.flat_breasts), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.small_breasts.label.CapitalizeFirst(), (() => Breasts = breasts.small_breasts), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.average_breasts.label.CapitalizeFirst(), (() => Breasts = breasts.average_breasts), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.large_breasts.label.CapitalizeFirst(), (() => Breasts = breasts.large_breasts), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.huge_breasts.label.CapitalizeFirst(), (() => Breasts = breasts.huge_breasts), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.slime_breasts.label.CapitalizeFirst(), (() => Breasts = breasts.slime_breasts), MenuOptionPriority.Default),
                    }));
                }
                switch (Breasts)
                {
                case breasts.none:
                    bpr = bpr_breasts;
                    break;

                case breasts.featureless_chest:
                    bpr = bpr_breasts;
                    hed = Genital_Helper.featureless_chest;
                    break;

                case breasts.flat_breasts:
                    bpr = bpr_breasts;
                    hed = Genital_Helper.flat_breasts;
                    break;

                case breasts.small_breasts:
                    bpr = bpr_breasts;
                    hed = Genital_Helper.small_breasts;
                    break;

                case breasts.average_breasts:
                    bpr = bpr_breasts;
                    hed = Genital_Helper.average_breasts;
                    break;

                case breasts.large_breasts:
                    bpr = bpr_breasts;
                    hed = Genital_Helper.large_breasts;
                    break;

                case breasts.huge_breasts:
                    bpr = bpr_breasts;
                    hed = Genital_Helper.huge_breasts;
                    break;

                case breasts.slime_breasts:
                    bpr = bpr_breasts;
                    hed = Genital_Helper.slime_breasts;
                    break;

                default:
                    break;
                }

                if (Widgets.ButtonText(button4, "Morph anus"))
                {
                    Find.WindowStack.Add(new FloatMenu(new List <FloatMenuOption>()
                    {
                        new FloatMenuOption("none", (() => Anuses = anuses.none), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.micro_anus.label.CapitalizeFirst(), (() => Anuses = anuses.micro_anus), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.tight_anus.label.CapitalizeFirst(), (() => Anuses = anuses.tight_anus), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.average_anus.label.CapitalizeFirst(), (() => Anuses = anuses.average_anus), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.loose_anus.label.CapitalizeFirst(), (() => Anuses = anuses.loose_anus), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.gaping_anus.label.CapitalizeFirst(), (() => Anuses = anuses.gaping_anus), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.slime_anus.label.CapitalizeFirst(), (() => Anuses = anuses.slime_anus), MenuOptionPriority.Default),
                    }));
                }
                switch (Anuses)
                {
                case anuses.none:
                    bpr = bpr_anus;
                    break;

                case anuses.micro_anus:
                    bpr = bpr_anus;
                    hed = Genital_Helper.micro_anus;
                    break;

                case anuses.tight_anus:
                    bpr = bpr_anus;
                    hed = Genital_Helper.tight_anus;
                    break;

                case anuses.average_anus:
                    bpr = bpr_anus;
                    hed = Genital_Helper.average_anus;
                    break;

                case anuses.loose_anus:
                    bpr = bpr_anus;
                    hed = Genital_Helper.loose_anus;
                    break;

                case anuses.gaping_anus:
                    bpr = bpr_anus;
                    hed = Genital_Helper.gaping_anus;
                    break;

                case anuses.slime_anus:
                    bpr = bpr_anus;
                    hed = Genital_Helper.slime_anus;
                    break;

                default:
                    break;
                }

                if (Widgets.ButtonText(button5, "Morph v****a"))
                {
                    Find.WindowStack.Add(new FloatMenu(new List <FloatMenuOption>()
                    {
                        new FloatMenuOption("none", (() => Vaginas = vaginas.none), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.micro_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.micro_vagina), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.tight_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.tight_vagina), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.average_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.average_vagina), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.loose_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.loose_vagina), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.gaping_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.gaping_vagina), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.slime_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.slime_vagina), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.feline_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.feline_vagina), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.canine_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.canine_vagina), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.equine_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.equine_vagina), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.dragon_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.dragon_vagina), MenuOptionPriority.Default),
                    }));
                }
                switch (Vaginas)
                {
                case vaginas.none:
                    bpr = bpr_genitalia;
                    break;

                case vaginas.micro_vagina:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.micro_vagina;
                    break;

                case vaginas.tight_vagina:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.tight_vagina;
                    break;

                case vaginas.average_vagina:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.average_vagina;
                    break;

                case vaginas.loose_vagina:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.loose_vagina;
                    break;

                case vaginas.gaping_vagina:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.gaping_vagina;
                    break;

                case vaginas.slime_vagina:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.slime_vagina;
                    break;

                case vaginas.feline_vagina:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.feline_vagina;
                    break;

                case vaginas.canine_vagina:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.canine_vagina;
                    break;

                case vaginas.equine_vagina:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.equine_vagina;
                    break;

                case vaginas.dragon_vagina:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.dragon_vagina;
                    break;

                default:
                    break;
                }

                if (Widgets.ButtonText(button6, "Morph penis"))
                {
                    Find.WindowStack.Add(new FloatMenu(new List <FloatMenuOption>()
                    {
                        new FloatMenuOption("none", (() => Penises = penises.none), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.micro_penis.label.CapitalizeFirst(), (() => Penises = penises.micro_penis), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.small_penis.label.CapitalizeFirst(), (() => Penises = penises.small_penis), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.average_penis.label.CapitalizeFirst(), (() => Penises = penises.average_penis), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.big_penis.label.CapitalizeFirst(), (() => Penises = penises.big_penis), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.huge_penis.label.CapitalizeFirst(), (() => Penises = penises.huge_penis), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.slime_penis.label.CapitalizeFirst(), (() => Penises = penises.slime_penis), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.feline_penis.label.CapitalizeFirst(), (() => Penises = penises.feline_penis), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.canine_penis.label.CapitalizeFirst(), (() => Penises = penises.canine_penis), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.equine_penis.label.CapitalizeFirst(), (() => Penises = penises.equine_penis), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.dragon_penis.label.CapitalizeFirst(), (() => Penises = penises.dragon_penis), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.raccoon_penis.label.CapitalizeFirst(), (() => Penises = penises.raccoon_penis), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.hemipenis.label.CapitalizeFirst(), (() => Penises = penises.hemipenis), MenuOptionPriority.Default),
                        new FloatMenuOption(Genital_Helper.crocodilian_penis.label.CapitalizeFirst(), (() => Penises = penises.crocodilian_penis), MenuOptionPriority.Default),
                    }));
                }
                switch (Penises)
                {
                case penises.none:
                    bpr = bpr_genitalia;
                    break;

                case penises.micro_penis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.micro_penis;
                    break;

                case penises.small_penis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.small_penis;
                    break;

                case penises.average_penis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.average_penis;
                    break;

                case penises.big_penis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.big_penis;
                    break;

                case penises.huge_penis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.huge_penis;
                    break;

                case penises.slime_penis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.slime_penis;
                    break;

                case penises.feline_penis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.feline_penis;
                    break;

                case penises.canine_penis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.canine_penis;
                    break;

                case penises.equine_penis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.equine_penis;
                    break;

                case penises.dragon_penis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.dragon_penis;
                    break;

                case penises.raccoon_penis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.raccoon_penis;
                    break;

                case penises.hemipenis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.hemipenis;
                    break;

                case penises.crocodilian_penis:
                    bpr = bpr_genitalia;
                    hed = Genital_Helper.crocodilian_penis;
                    break;

                default:
                    break;
                }

                if (bpr != null)
                {
                    //Log.Message("start ");
                    if (bpr != bpr_genitalia)
                    {
                        if (pawn.needs.TryGetNeed <Need_Food>().CurLevel > 0.5f)
                        {
                            pawn.needs.food.CurLevel -= 0.5f;
                            pawn.health.RestorePart(bpr);
                            if (hed != null)
                            {
                                pawn.health.AddHediff(hed, bpr);
                            }
                        }
                        Anuses  = anuses.selectone;
                        Breasts = breasts.selectone;
                    }
                    else if (bpr == bpr_genitalia && Vaginas != vaginas.selectone)
                    {
                        if (pawn.needs.TryGetNeed <Need_Food>().CurLevel > 0.5f)
                        {
                            pawn.needs.food.CurLevel -= 0.5f;
                            List <Hediff> list = new List <Hediff>();
                            foreach (Hediff heddif in pawn.health.hediffSet.hediffs.Where(x =>
                                                                                          x.Part == bpr &&
                                                                                          x.def.defName.ToLower().Contains("v****a")))
                            {
                                list.Add(heddif);
                            }

                            foreach (Hediff heddif in list)
                            {
                                pawn.health.hediffSet.hediffs.Remove(heddif);
                            }

                            if (hed != null)
                            {
                                pawn.health.AddHediff(hed, bpr);
                            }
                        }
                        Vaginas = vaginas.selectone;
                    }
                    else if (bpr == bpr_genitalia && Penises != penises.selectone)
                    {
                        if (pawn.needs.TryGetNeed <Need_Food>().CurLevel > 0.5f)
                        {
                            pawn.needs.food.CurLevel -= 0.5f;
                            List <Hediff> list = new List <Hediff>();
                            foreach (Hediff heddif in pawn.health.hediffSet.hediffs.Where(x =>
                                                                                          x.Part == bpr &&
                                                                                          x.def.defName.ToLower().Contains("penis") ||
                                                                                          x.def.defName.ToLower().Contains("tentacle")))
                            {
                                list.Add(heddif);
                            }

                            foreach (Hediff heddif in list)
                            {
                                pawn.health.hediffSet.hediffs.Remove(heddif);
                            }

                            if (hed != null)
                            {
                                pawn.health.AddHediff(hed, bpr);
                            }
                        }
                        Penises = penises.selectone;
                    }
                    //Log.Message("end ");
                }
            }
        }
        protected override IEnumerable <Toil> MakeNewToils()
        {
            //Log.Message("[RJW]JobDriver_WhoreIsServingVisitors::MakeNewToils() - making toils");
            setup_ticks();

            this.FailOnDespawnedOrNull(iTarget);
            this.FailOnDespawnedNullOrForbidden(iBed);
            //Log.Message("[RJW]JobDriver_WhoreIsServingVisitors::MakeNewToils() fail conditions check " + !xxx.CanUse(pawn, Bed) + " " + !pawn.CanReserve(Partner));
            this.FailOn(() => !xxx.CanUse(pawn, Bed) || !pawn.CanReserve(Partner));
            this.FailOn(() => pawn.Drafted);
            this.FailOn(() => Partner.IsFighting());
            this.FailOn(() => !Partner.CanReach(pawn, PathEndMode.Touch, Danger.Deadly));

            yield return(Toils_Reserve.Reserve(iTarget, 1, 0));

            //yield return Toils_Reserve.Reserve(BedInd, Bed.SleepingSlotsCount, 0);

            //Log.Message("[RJW]JobDriver_WhoreIsServingVisitors::MakeNewToils() - generate toils");
            Toil gotoBed = new Toil();

            gotoBed.defaultCompleteMode = ToilCompleteMode.PatherArrival;
            gotoBed.FailOnWhorebedNoLongerUsable(iBed, Bed);
            gotoBed.AddFailCondition(() => Partner.Downed);
            gotoBed.FailOn(() => !Partner.CanReach(Bed, PathEndMode.Touch, Danger.Deadly));
            gotoBed.initAction = delegate
            {
                //Log.Message("[RJW]JobDriver_WhoreIsServingVisitors::MakeNewToils() - gotoWhoreBed initAction is called");
                pawn.pather.StartPath(SleepSpot, PathEndMode.OnCell);
                Partner.jobs.StopAll();
                Job job = JobMaker.MakeJob(JobDefOf.GotoMindControlled, SleepSpot);
                Partner.jobs.StartJob(job, JobCondition.InterruptForced);
            };
            yield return(gotoBed);

            ticks_left = (int)(2000.0f * Rand.Range(0.30f, 1.30f));

            Toil waitInBed = new Toil();

            waitInBed.initAction = delegate
            {
                ticksLeftThisToil = 5000;
            };
            waitInBed.tickAction = delegate
            {
                pawn.GainComfortFromCellIfPossible();
                if (IsInOrByBed(Bed, Partner) && pawn.PositionHeld == Partner.PositionHeld)
                {
                    ReadyForNextToil();
                }
            };
            waitInBed.defaultCompleteMode = ToilCompleteMode.Delay;
            yield return(waitInBed);

            Toil StartPartnerJob = new Toil();

            StartPartnerJob.defaultCompleteMode = ToilCompleteMode.Instant;
            StartPartnerJob.socialMode          = RandomSocialMode.Off;
            StartPartnerJob.initAction          = delegate
            {
                //Log.Message("[RJW]JobDriver_WhoreIsServingVisitors::MakeNewToils() - StartPartnerJob");
                var gettin_loved = JobMaker.MakeJob(xxx.gettin_loved, pawn, Bed);
                Partner.jobs.StartJob(gettin_loved, JobCondition.InterruptForced);
            };
            yield return(StartPartnerJob);

            Toil loveToil = new Toil();

            loveToil.AddFailCondition(() => Partner.Dead || Partner.CurJobDef != xxx.gettin_loved);
            loveToil.defaultCompleteMode = ToilCompleteMode.Never;
            loveToil.socialMode          = RandomSocialMode.Off;
            loveToil.handlingFacing      = true;
            loveToil.initAction          = delegate
            {
                //Log.Message("[RJW]JobDriver_WhoreIsServingVisitors::MakeNewToils() - loveToil");
                // TODO: replace this quick n dirty way
                CondomUtility.GetCondomFromRoom(pawn);

                // Try to use w***e's condom first, then client's
                usedCondom = CondomUtility.TryUseCondom(pawn) || CondomUtility.TryUseCondom(Partner);

                Start();

                if (xxx.HasNonPolyPartnerOnCurrentMap(Partner))
                {
                    Pawn lover = LovePartnerRelationUtility.ExistingLovePartner(Partner);
                    // We have to do a few other checks because the pawn might have multiple lovers and ExistingLovePartner() might return the wrong one
                    if (lover != null && pawn != lover && !lover.Dead && (lover.Map == Partner.Map || Rand.Value < 0.25))
                    {
                        lover.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.CheatedOnMe, Partner);
                    }
                }
            };
            loveToil.AddPreTickAction(delegate
            {
                --ticks_left;
                if (pawn.IsHashIntervalTick(ticks_between_hearts))
                {
                    if (xxx.is_nympho(pawn))
                    {
                        ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
                    }
                    else
                    {
                        ThrowMetaIcon(pawn.Position, pawn.Map, xxx.mote_noheart);
                    }
                }
                SexUtility.reduce_rest(Partner, 1);
                SexUtility.reduce_rest(pawn, 2);
                if (ticks_left <= 0)
                {
                    ReadyForNextToil();
                }
            });
            loveToil.AddFinishAction(delegate
            {
                End();
            });
            yield return(loveToil);

            Toil afterSex = new Toil
            {
                initAction = delegate
                {
                    // Adding interactions, social logs, etc
                    SexUtility.ProcessSex(pawn, Partner, usedCondom: usedCondom, whoring: isWhoring, sextype: sexType);

                    if (!(Partner.IsColonist && (pawn.IsPrisonerOfColony || pawn.IsColonist)))
                    {
                        int price = WhoringHelper.PriceOfWhore(pawn);
                        //--Log.Message("JobDriver_WhoreIsServingVisitors::MakeNewToils() - Partner should pay the price now in afterSex.initAction");
                        int remainPrice = WhoringHelper.PayPriceToWhore(Partner, price, pawn);

                        /*if (remainPrice <= 0)
                         * {
                         *      --Log.Message("JobDriver_WhoreIsServingVisitors::MakeNewToils() - Paying price is success");
                         * }
                         * else
                         * {
                         *      --Log.Message("JobDriver_WhoreIsServingVisitors::MakeNewToils() - Paying price failed");
                         * }*/
                        xxx.UpdateRecords(pawn, price - remainPrice);
                    }
                    var thought = (pawn.IsPrisoner || xxx.is_slave(pawn)) ? thought_captive : thought_free;
                    pawn.needs.mood.thoughts.memories.TryGainMemory(thought);
                    if (SexUtility.ConsiderCleaning(pawn))
                    {
                        LocalTargetInfo cum = pawn.PositionHeld.GetFirstThing <Filth>(pawn.Map);

                        Job clean = JobMaker.MakeJob(JobDefOf.Clean);
                        clean.AddQueuedTarget(TargetIndex.A, cum);

                        pawn.jobs.jobQueue.EnqueueFirst(clean);
                    }
                },
                defaultCompleteMode = ToilCompleteMode.Instant
            };

            yield return(afterSex);
        }