private void Finished() { StringBuilder attendedString = new StringBuilder(); foreach (Pawn p in attendees) { attendedString.AppendLine(p.Name.ToString()); if (PsycheHelper.PsychologyEnabled(p)) { ThoughtDef def = new ThoughtDef(); def.defName = p.GetHashCode() + "AttendedFuneral" + dead.GetHashCode(); def.durationDays = 20f; def.nullifyingTraits = new List <TraitDef>(); def.nullifyingTraits.Add(TraitDefOf.Psychopath); def.nullifyingTraits.Add(TraitDefOfPsychology.Desensitized); def.thoughtClass = typeof(Thought_MemoryDynamic); ThoughtStage stage = new ThoughtStage(); stage.label = "AttendedFuneralThought".Translate(dead); stage.baseMoodEffect = Mathf.RoundToInt((p.relations.OpinionOf(dead) / 15f) * (0.33f + PsycheHelper.Comp(p).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Nostalgic))); stage.description = "AttendedFuneralDesc".Translate().AdjustedFor(dead); def.stages.Add(stage); p.needs.mood.thoughts.memories.TryGainMemory(def); } else { p.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOfPsychology.AttendedFuneral); } } if (attendees.Count == 0) { attendedString.AppendLine("No one."); } Find.LetterStack.ReceiveLetter("LetterLabelFuneralEnded".Translate(dead), "LetterFuneralEnded".Translate(dead, attendedString), LetterDefOf.NeutralEvent, null); }
private static void AddBrokeUpMood(Pawn lover, Pawn ex, float intensity = 1f) { ThoughtDef brokeUpMoodDef = new ThoughtDef(); brokeUpMoodDef.defName = "BrokeUpWithMeMood" + lover.LabelShort + Find.TickManager.TicksGame; if (intensity < 1f) { brokeUpMoodDef.durationDays = 10f; } else { brokeUpMoodDef.durationDays = 40f; } brokeUpMoodDef.thoughtClass = typeof(Thought_MemoryDynamic); brokeUpMoodDef.stackedEffectMultiplier = 1f; brokeUpMoodDef.stackLimit = 999; ThoughtStage brokeUpStage = new ThoughtStage(); brokeUpStage.label = "Broke up with {0}"; brokeUpStage.baseMoodEffect = Mathf.RoundToInt(-4f * intensity * Mathf.InverseLerp(0.25f, 0.75f, PsycheHelper.Comp(lover).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic)) * Mathf.InverseLerp(-20f, 100f, lover.relations.OpinionOf(ex))); if (brokeUpStage.baseMoodEffect < -5f) { brokeUpStage.description = "{0} and I parted ways amicably, but it's still a little sad."; } else { brokeUpStage.description = "I'm going through a bad break-up right now."; } brokeUpMoodDef.stages.Add(brokeUpStage); if (brokeUpStage.baseMoodEffect > 0f) { lover.needs.mood.thoughts.memories.TryGainMemory(brokeUpMoodDef, ex); } }
public override void ExposeData() { if (this.def != null) { this.def.defName = "Dynamic"; } base.ExposeData(); Scribe_Values.Look(ref this.topic, "topic", "Dynamic"); Scribe_Values.Look(ref this.label, "label", "dynamic thought"); Scribe_Values.Look(ref this.description, "description", "a dynamic thought."); Scribe_Values.Look(ref this.duration, "duration", 5f); Scribe_Values.Look(ref this.baseMoodEffect, "realMoodEffect", 5f); ThoughtDef def = new ThoughtDef(); def.defName = this.topic; def.label = "dynamic thought"; def.description = this.description; def.durationDays = this.duration; def.thoughtClass = typeof(Thought_MemoryDynamic); def.stackedEffectMultiplier = 1f; def.stackLimit = 999; ThoughtStage stage = new ThoughtStage(); stage.label = this.label; stage.baseMoodEffect = this.baseMoodEffect; def.stages.Add(stage); this.def = def; }
public ThoughtStageWidget(ThoughtStage stage) { this.stage = stage; this.inputWidgets = new List <IInputWidget>() { new FloatInputWidget <ThoughtStage>(this.stage, "Base Mood Effect", s => s.baseMoodEffect, (s, v) => s.baseMoodEffect = v), new FloatInputWidget <ThoughtStage>(this.stage, "Base Opinion Offset", s => s.baseOpinionOffset, (s, v) => s.baseOpinionOffset = v), new BoolInputWidget <ThoughtStage>(this.stage, "Visible", s => s.visible, (s, v) => s.visible = v) }; }
public static void Thoughts() { Func <ThoughtDef, string> stagesText = delegate(ThoughtDef t) { string text = ""; if (t.stages == null) { return(null); } for (int i = 0; i < t.stages.Count; i++) { ThoughtStage thoughtStage = t.stages[i]; text = text + "[" + i + "] "; if (thoughtStage == null) { text += "null"; } else { if (thoughtStage.label != null) { text += thoughtStage.label; } if (thoughtStage.labelSocial != null) { if (thoughtStage.label != null) { text += "/"; } text += thoughtStage.labelSocial; } text += " "; if (thoughtStage.baseMoodEffect != 0f) { text = text + "[" + thoughtStage.baseMoodEffect.ToStringWithSign() + " Mo]"; } if (thoughtStage.baseOpinionOffset != 0f) { text = text + "(" + thoughtStage.baseOpinionOffset.ToStringWithSign() + " Op)"; } } if (i < t.stages.Count - 1) { text += "\n"; } } return(text); }; DebugTables.MakeTablesDialog(DefDatabase <ThoughtDef> .AllDefs, new TableDataGetter <ThoughtDef>("defName", (ThoughtDef d) => d.defName), new TableDataGetter <ThoughtDef>("type", (ThoughtDef d) => (!d.IsMemory) ? "situ" : "mem"), new TableDataGetter <ThoughtDef>("social", (ThoughtDef d) => (!d.IsSocial) ? "mood" : "soc"), new TableDataGetter <ThoughtDef>("stages", (ThoughtDef d) => stagesText(d)), new TableDataGetter <ThoughtDef>("best\nmood", (ThoughtDef d) => d.stages.Where((ThoughtStage st) => st != null).Max((ThoughtStage st) => st.baseMoodEffect)), new TableDataGetter <ThoughtDef>("worst\nmood", (ThoughtDef d) => d.stages.Where((ThoughtStage st) => st != null).Min((ThoughtStage st) => st.baseMoodEffect)), new TableDataGetter <ThoughtDef>("stack\nlimit", (ThoughtDef d) => d.stackLimit.ToString()), new TableDataGetter <ThoughtDef>("stack\nlimit\nper o. pawn", (ThoughtDef d) => (d.stackLimitForSameOtherPawn >= 0) ? d.stackLimitForSameOtherPawn.ToString() : ""), new TableDataGetter <ThoughtDef>("stacked\neffect\nmultiplier", (ThoughtDef d) => (d.stackLimit != 1) ? d.stackedEffectMultiplier.ToStringPercent() : ""), new TableDataGetter <ThoughtDef>("duration\n(days)", (ThoughtDef d) => d.durationDays.ToString()), new TableDataGetter <ThoughtDef>("effect\nmultiplying\nstat", (ThoughtDef d) => (d.effectMultiplyingStat != null) ? d.effectMultiplyingStat.defName : ""), new TableDataGetter <ThoughtDef>("game\ncondition", (ThoughtDef d) => (d.gameCondition != null) ? d.gameCondition.defName : ""), new TableDataGetter <ThoughtDef>("hediff", (ThoughtDef d) => (d.hediff != null) ? d.hediff.defName : ""), new TableDataGetter <ThoughtDef>("lerp opinion\nto zero\nafter duration pct", (ThoughtDef d) => d.lerpOpinionToZeroAfterDurationPct.ToStringPercent()), new TableDataGetter <ThoughtDef>("max cumulated\nopinion\noffset", (ThoughtDef d) => (!(d.maxCumulatedOpinionOffset > 99999f)) ? d.maxCumulatedOpinionOffset.ToString() : ""), new TableDataGetter <ThoughtDef>("next\nthought", (ThoughtDef d) => (d.nextThought != null) ? d.nextThought.defName : ""), new TableDataGetter <ThoughtDef>("nullified\nif not colonist", (ThoughtDef d) => d.nullifiedIfNotColonist.ToStringCheckBlank()), new TableDataGetter <ThoughtDef>("show\nbubble", (ThoughtDef d) => d.showBubble.ToStringCheckBlank())); }
public static void AddBrokeUpOpinion(PsychologyPawn lover, PsychologyPawn ex) { ThoughtDef brokeUpDef = new ThoughtDef(); brokeUpDef.defName = "BrokeUpWithMe" + lover.LabelShort + Find.TickManager.TicksGame; brokeUpDef.durationDays = 40f; brokeUpDef.thoughtClass = typeof(Thought_MemorySocialDynamic); ThoughtStage brokeUpStage = new ThoughtStage(); brokeUpStage.label = "broke up with me"; brokeUpStage.baseOpinionOffset = Mathf.RoundToInt(-50f * lover.psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic) * Mathf.InverseLerp(5f, 100f, lover.relations.OpinionOf(ex))); brokeUpDef.stages.Add(brokeUpStage); lover.needs.mood.thoughts.memories.TryGainMemory(brokeUpDef, ex); }
public ThoughtStageStats(ThoughtStage ts) { if (ts != null) { this.label = ts.label; this.baseMoodEffect = ts.baseMoodEffect; this.baseOpinionOffset = ts.baseOpinionOffset; this.visible = ts.visible; this.isNull = false; } else { this.isNull = true; } }
private bool TryGainThought(ThoughtDef def, int opinionOffset) { ThoughtStage stage = def.stages.First(); IEnumerable <Thought_MemorySocialDynamic> convoMemories; /* The more they know about someone, the less likely small thoughts are to have an impact on their opinion. * This helps declutter the Social card without preventing pawns from having conversations. * They just won't change their mind about the colonist as a result. */ if (Rand.Value < Mathf.InverseLerp(0f, PsycheHelper.Comp(pawn).Psyche.TotalThoughtOpinion(this.otherPawn, out convoMemories), 250f + Mathf.Abs(opinionOffset)) && opinionOffset != 0) { this.pawn.needs.mood.thoughts.memories.TryGainMemory(def, this.otherPawn); return(true); } return(false); }
private static void RenderThoughtStage(ThoughtStage stg) { CONS.WriteLine(" li"); CONS.WriteLine($" label: {stg.Label.ToStringOrDefault()}"); if (stg.LabelSocial != null) { CONS.WriteLine($" labelSocial: {stg.LabelSocial.ToStringOrDefault()}"); } CONS.WriteLine($" description: {stg.Description.ToStringOrDefault()}"); if (stg.BaseMoodEffect.HasValue) { CONS.WriteLine($" baseMoodEffect: {stg.BaseMoodEffect.Value}"); } if (stg.BaseOpinionOffset.HasValue) { CONS.WriteLine($" baseOpinionOffset: {stg.BaseOpinionOffset.Value}"); } }
//Modified directly from Psychology. private static void AddBrokeUpOpinion(Pawn lover, Pawn ex, float intensity = 1f) { ThoughtDef brokeUpDef = new ThoughtDef(); brokeUpDef.defName = "BrokeUpWithMe" + lover.LabelShort + Find.TickManager.TicksGame; if (intensity < 1f) { brokeUpDef.durationDays = 10f; } else { brokeUpDef.durationDays = 40f; } brokeUpDef.thoughtClass = typeof(Thought_MemorySocialDynamic); ThoughtStage brokeUpStage = new ThoughtStage(); brokeUpStage.label = "broke up with me"; brokeUpStage.baseOpinionOffset = Mathf.RoundToInt(-10 * intensity * PsycheHelper.Comp(lover).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic) * Mathf.InverseLerp(5f, 100f, lover.relations.OpinionOf(ex))); brokeUpDef.stages.Add(brokeUpStage); lover.needs.mood.thoughts.memories.TryGainMemory(brokeUpDef, ex); }
public static void FindAllTODOThoughts() { var builder = new StringBuilder(); var defNames = new List <string>(); foreach (ThoughtDef thoughtDef in DefDatabase <ThoughtDef> .AllDefs) { var addedHeader = false; for (var index = 0; index < (thoughtDef?.stages?.Count ?? 0); index++) { ThoughtStage stage = thoughtDef?.stages?[index]; if (stage == null) { continue; } if (string.IsNullOrEmpty(stage.label) || string.IsNullOrEmpty(stage.description)) { continue; } if (stage.label == "TODO" || stage.description == "TODO" || stage.description.StartsWith("!!!") || stage.label.StartsWith("!!!")) { if (!addedHeader) { builder.AppendLine($"In {thoughtDef.defName}:"); addedHeader = true; } defNames.Add(thoughtDef.defName); builder.AppendLine($"{index}) label:{stage.label} description:\"{stage.description}\"".Indented()); } } } builder.AppendLine(defNames.Distinct().Join(d => d, "\n")); Log.Message(builder.ToString()); }
private static void Postfix(IngestionOutcomeDoer_GiveHediff __instance, Pawn pawn, Thing ingested) { ThoughtStage ts = new ThoughtStage() { label = "took antidepressants", description = "I feel calm. LOL", baseMoodEffect = -50, }; ThoughtDef td = new ThoughtDef() { defName = "AteAntiDepressant", durationDays = 0.5f, stackLimit = 1, stages = new List <ThoughtStage>() { ts }, }; pawn.needs.mood.thoughts.memories.TryGainMemory(td, null); Debug.Log("debug : " + ingested.def.defName + " " + pawn.Name); }
public override void ExposeData() { if (this.def != null) { this.def.defName = "DynamicSocial"; } base.ExposeData(); Scribe_Values.Look(ref this.topic, "topic", "DynamicSocial"); Scribe_Values.Look(ref this.label, "label", "conversation"); Scribe_Values.Look(ref this.baseOpinionOffset, "realOpinionOffset", 5); ThoughtDef def = new ThoughtDef(); def.defName = this.topic; def.label = "conversation"; def.durationDays = 60f; def.thoughtClass = typeof(Thought_MemorySocialDynamic); ThoughtStage stage = new ThoughtStage(); stage.label = this.label; stage.baseOpinionOffset = this.baseOpinionOffset; def.stages.Add(stage); this.def = def; }
public override void PostRemoved() { base.PostRemoved(); if (this.realPawn == null) { this.realPawn = this.pawn as PsychologyPawn; } if (this.realPawn != null && this.otherPawn != null) { Hediff otherConvo = otherPawn.health.hediffSet.hediffs.Find(h => h is Hediff_Conversation && ((Hediff_Conversation)h).otherPawn == this.realPawn); if (otherConvo != null) { this.otherPawn.health.RemoveHediff(otherConvo); } string talkDesc; if (this.ageTicks < 500) { int numShortTalks = int.Parse("NumberOfShortTalks".Translate()); talkDesc = "ShortTalk" + Rand.RangeInclusive(1, numShortTalks); } else if (this.ageTicks < 1500) { int numNormalTalks = int.Parse("NumberOfNormalTalks".Translate()); talkDesc = "NormalTalk" + Rand.RangeInclusive(1, numNormalTalks); } else if (this.ageTicks < 5000) { int numLongTalks = int.Parse("NumberOfLongTalks".Translate()); talkDesc = "LongTalk" + Rand.RangeInclusive(1, numLongTalks); } else { int numEpicTalks = int.Parse("NumberOfEpicTalks".Translate()); talkDesc = "EpicTalk" + Rand.RangeInclusive(1, numEpicTalks); } //We create a dynamic def to hold this thought so that the game won't worry about it being used anywhere else. ThoughtDef def = new ThoughtDef(); def.defName = this.pawn.GetHashCode() + "Conversation" + topic.defName; def.label = topic.defName; def.durationDays = 60f; def.nullifyingTraits = new List <TraitDef>(); def.nullifyingTraits.Add(TraitDefOf.Psychopath); def.thoughtClass = typeof(Thought_MemorySocialDynamic); ThoughtStage stage = new ThoughtStage(); //Base opinion mod is 5 to the power of controversiality. float opinionMod = Mathf.Pow(5f, topic.controversiality); //Multiplied by difference between their personality ratings, on an exponential scale. opinionMod *= Mathf.Lerp(-1.25f, 1.25f, Mathf.Pow(1f - Mathf.Abs(this.realPawn.psyche.GetPersonalityRating(topic) - this.otherPawn.psyche.GetPersonalityRating(topic)), 3)); //Cool pawns are liked more. opinionMod += Mathf.Pow(2f, topic.controversiality) * (0.5f - this.otherPawn.psyche.GetPersonalityRating(PersonalityNodeDefOf.Cool)); //The length of the talk has a large impact on how much the pawn cares. opinionMod *= 5f * (this.ageTicks / (GenDate.TicksPerHour * 2.25f)); //talkModifier[talkLength] //If they had a bad experience, the more polite the pawn is, the less they're bothered by it. opinionMod *= (opinionMod < 0f ? 0.5f + (1f - this.otherPawn.psyche.GetPersonalityRating(PersonalityNodeDefOf.Polite)) : 1f); //The more judgmental the pawn, the more they're affected by all conversations. opinionMod *= 0.5f + this.realPawn.psyche.GetPersonalityRating(PersonalityNodeDefOf.Judgmental); if (opinionMod < 0f) { opinionMod *= PopulationModifier; } stage.label = "ConversationStage".Translate() + " " + topic.conversationTopic; stage.baseOpinionOffset = Mathf.RoundToInt(opinionMod); def.stages.Add(stage); /* The more they know about someone, the less likely small thoughts are to have an impact on their opinion. * This helps declutter the Social card without preventing pawns from having conversations. * They just won't change their mind about the colonist as a result. */ if (Rand.Value < Mathf.InverseLerp(0f, this.realPawn.psyche.TotalThoughtOpinion(this.otherPawn), 250f + Mathf.Abs(stage.baseOpinionOffset)) && stage.baseOpinionOffset != 0) { this.pawn.needs.mood.thoughts.memories.TryGainMemory(def, this.otherPawn); } if (this.waveGoodbye && this.pawn.Map != null) { InteractionDef endConversation = new InteractionDef(); endConversation.defName = "EndConversation"; RulePack goodbyeText = new RulePack(); FieldInfo RuleStrings = typeof(RulePack).GetField("rulesStrings", BindingFlags.Instance | BindingFlags.NonPublic); List <string> text = new List <string>(1); text.Add("logentry->" + talkDesc.Translate(topic.conversationTopic)); RuleStrings.SetValue(goodbyeText, text); endConversation.logRulesInitiator = goodbyeText; endConversation.logRulesRecipient = goodbyeText; FieldInfo Symbol = typeof(InteractionDef).GetField("symbol", BindingFlags.Instance | BindingFlags.NonPublic); Symbol.SetValue(endConversation, Symbol.GetValue(InteractionDefOf.DeepTalk)); PlayLogEntry_InteractionConversation log = new PlayLogEntry_InteractionConversation(endConversation, realPawn, this.otherPawn, new List <RulePackDef>()); Find.PlayLog.Add(log); MoteMaker.MakeInteractionBubble(this.pawn, this.otherPawn, InteractionDefOf.Chitchat.interactionMote, InteractionDefOf.Chitchat.Symbol); } } }
private void Finished() { if (PsycheHelper.PsychologyEnabled(mayor) && PsycheHelper.PsychologyEnabled(constituent)) { if (this.ticksInSameRoom > 0) { if (this.complaint) { ThoughtDef complaintDef = new ThoughtDef(); complaintDef.label = "MayorComplaint"; complaintDef.durationDays = 1f + 4f * this.mayor.GetStatValue(StatDefOf.SocialImpact); //Constituent thought duration affected by mayor's Social stat complaintDef.thoughtClass = typeof(Thought_MemoryDynamic); complaintDef.stackedEffectMultiplier = 1f; complaintDef.stackLimit = 999; ThoughtStage complaintStage = new ThoughtStage(); float complaintMood = 18f * (PsycheHelper.Comp(mayor).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Empathetic) - 0.33f); //Base complaint mood determined by mayor's Empathetic trait complaintMood *= (float)this.ticksInSameRoom / (float)GenDate.TicksPerHour; //Length of meeting also affects mood complaintMood *= (complaintMood < 0f ? Mathf.Lerp(1.25f, 0.75f, PsycheHelper.Comp(constituent).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Polite)) : 1f); //Negative meeting thoughts (unempathetic mayors) mitigated by mayor's politeness complaintMood += (BeautyUtility.AverageBeautyPerceptible(this.constituent.Position, this.constituent.Map) / 10f); //Beauty of the room has a net positive effect on the thought complaintMood *= 0.75f + (PsycheHelper.Comp(constituent).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Judgmental) / 2f); //Constituent's Judgmental trait changes how much the thought affects them complaintStage.label = "ComplaintLabel".Translate(); complaintStage.description = "ComplaintDesc".Translate(); complaintStage.baseMoodEffect = Mathf.RoundToInt(complaintMood); complaintDef.defName = this.constituent.GetHashCode() + "MayorComplaint" + complaintStage.baseMoodEffect; complaintDef.stages.Add(complaintStage); if (complaintStage.baseMoodEffect != 0) { this.constituent.needs.mood.thoughts.memories.TryGainMemory(complaintDef, this.mayor); } } ThoughtDef visitDef = new ThoughtDef(); visitDef.label = "MayorVisited"; visitDef.durationDays = 0.75f + 2f * (1f - PsycheHelper.Comp(mayor).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Independent)); //Mayor thought duration affected by mayor's Independent trait visitDef.thoughtClass = typeof(Thought_MemoryDynamic); visitDef.stackedEffectMultiplier = 1f; visitDef.stackLimit = 999; ThoughtStage stage = new ThoughtStage(); float mood = 7f * (complaint ? -0.5f - (1f - this.constituent.needs.mood.CurLevel) : 0.1f + (this.constituent.needs.mood.CurLevel * 0.65f)); //Base visit mood determined by the mood level of the constituent mood *= (float)this.ticksInSameRoom / (float)GenDate.TicksPerHour; //Length of meeting also affects mood mood *= (mood < 0f ? Mathf.Lerp(1.25f, 0.75f, PsycheHelper.Comp(constituent).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Polite)) : 1f); //Negative meeting thoughts (unhappy constituents) mitigated by constituent's politeness mood *= 0.5f + (1f - PsycheHelper.Comp(this.mayor).Psyche.GetPersonalityRating(PersonalityNodeDefOf.LaidBack)); //Mayor's Laid-Back trait strongly impacts how much the thought affects them stage.label = "VisitLabel".Translate(); stage.description = "VisitDesc".Translate(); stage.baseMoodEffect = Mathf.RoundToInt(mood); visitDef.defName = this.mayor.GetHashCode() + "MayorVisited" + stage.baseMoodEffect; visitDef.stages.Add(stage); if (stage.baseMoodEffect != 0) { this.mayor.needs.mood.thoughts.memories.TryGainMemory(visitDef, this.constituent); } InteractionDef endConversation = new InteractionDef(); endConversation.defName = "EndConversation"; FieldInfo RuleStrings = typeof(RulePack).GetField("rulesStrings", BindingFlags.Instance | BindingFlags.NonPublic); RulePack goodbyeTextInit = new RulePack(); List <string> text = new List <string>(1); if (complaint) { text.Add("r_logentry->" + "Complained".Translate()); } else { text.Add("r_logentry->" + "Supported".Translate()); } RuleStrings.SetValue(goodbyeTextInit, text); endConversation.logRulesInitiator = goodbyeTextInit; FieldInfo Symbol = typeof(InteractionDef).GetField("symbol", BindingFlags.Instance | BindingFlags.NonPublic); Symbol.SetValue(endConversation, Symbol.GetValue(InteractionDefOfPsychology.HangOut)); PlayLogEntry_InteractionConversation log = new PlayLogEntry_InteractionConversation(endConversation, this.constituent, this.mayor, new List <RulePackDef>()); Find.PlayLog.Add(log); MoteMaker.MakeInteractionBubble(this.mayor, this.constituent, InteractionDefOf.Chitchat.interactionMote, InteractionDefOf.Chitchat.Symbol); } } }
public static void AddPsychRejectedThoughts(Pawn initiator, Pawn recipient) { if (PsycheHelper.PsychologyEnabled(initiator)) { ThoughtDef rejectedProposalDef = new ThoughtDef(); rejectedProposalDef.defName = "RejectedMyProposal" + initiator.LabelShort + Find.TickManager.TicksGame; rejectedProposalDef.durationDays = 60f * PsycheHelper.Comp(initiator).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic); rejectedProposalDef.thoughtClass = typeof(Thought_MemorySocialDynamic); ThoughtStage rejectedProposalStage = new ThoughtStage(); rejectedProposalStage.label = "rejected my proposal"; rejectedProposalStage.baseOpinionOffset = Mathf.RoundToInt(-40f * PsycheHelper.Comp(initiator).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic) * (PsychologyBase.ActivateKinsey() ? PsycheHelper.Comp(initiator).Sexuality.AdjustedRomanticDrive : 1f)); rejectedProposalDef.stages.Add(rejectedProposalStage); ThoughtDef rejectedProposalMoodDef = new ThoughtDef(); rejectedProposalMoodDef.defName = "RejectedMyProposalMood" + initiator.LabelShort + Find.TickManager.TicksGame; rejectedProposalMoodDef.durationDays = 25f; rejectedProposalMoodDef.thoughtClass = typeof(Thought_MemoryDynamic); rejectedProposalMoodDef.stackLimit = 999; rejectedProposalMoodDef.stackedEffectMultiplier = 1f; ThoughtStage rejectedProposalMoodStage = new ThoughtStage(); rejectedProposalMoodStage.label = "proposal rejected by {0}"; rejectedProposalMoodStage.baseMoodEffect = Mathf.RoundToInt(-25f * PsycheHelper.Comp(initiator).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic) * Mathf.InverseLerp(100f, 5f, initiator.relations.OpinionOf(recipient))); if (rejectedProposalMoodStage.baseMoodEffect < -5f) { rejectedProposalMoodStage.description = "My lover isn't ready for that kind of commitment right now, and I understand, but rejection is hard to take."; } else { rejectedProposalMoodStage.description = "I can't believe I got turned down. Maybe we're not meant to be together after all?"; } rejectedProposalMoodDef.stages.Add(rejectedProposalMoodStage); if (rejectedProposalMoodStage.baseMoodEffect < 0f) { initiator.needs.mood.thoughts.memories.TryGainMemory(rejectedProposalMoodDef, recipient); } initiator.needs.mood.thoughts.memories.TryGainMemory(rejectedProposalDef, recipient); } else { initiator.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.RejectedMyProposal, recipient); } if (PsycheHelper.PsychologyEnabled(recipient)) { ThoughtDef rejectedTheirProposalDef = new ThoughtDef(); rejectedTheirProposalDef.defName = "IRejectedTheirProposal" + recipient.LabelShort + Find.TickManager.TicksGame; rejectedTheirProposalDef.durationDays = 60f * PsycheHelper.Comp(recipient).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic); rejectedTheirProposalDef.thoughtClass = typeof(Thought_MemorySocialDynamic); ThoughtStage rejectedTheirProposalStage = new ThoughtStage(); rejectedTheirProposalStage.label = "I rejected their proposal"; rejectedTheirProposalStage.baseOpinionOffset = Mathf.RoundToInt(-30f * PsycheHelper.Comp(recipient).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic) * (PsychologyBase.ActivateKinsey() ? 1.75f - PsycheHelper.Comp(recipient).Sexuality.AdjustedRomanticDrive : 1f)); rejectedTheirProposalDef.stages.Add(rejectedTheirProposalStage); ThoughtDef rejectedTheirProposalMoodDef = new ThoughtDef(); rejectedTheirProposalMoodDef.defName = "IRejectedTheirProposalMood" + recipient.LabelShort + Find.TickManager.TicksGame; rejectedTheirProposalMoodDef.durationDays = 25f; rejectedTheirProposalMoodDef.thoughtClass = typeof(Thought_MemoryDynamic); rejectedTheirProposalMoodDef.stackLimit = 999; rejectedTheirProposalMoodDef.stackedEffectMultiplier = 1f; ThoughtStage rejectedTheirProposalMoodStage = new ThoughtStage(); rejectedTheirProposalMoodStage.label = "rejected {0}'s proposal"; rejectedTheirProposalMoodStage.baseMoodEffect = Mathf.RoundToInt(-25f * PsycheHelper.Comp(recipient).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic) * Mathf.InverseLerp(100f, 5f, recipient.relations.OpinionOf(initiator))); if (rejectedTheirProposalMoodStage.baseMoodEffect < -5f) { rejectedTheirProposalMoodStage.description = "I wish they wouldn't spring something like that on me."; } else { rejectedTheirProposalMoodStage.description = "I'm not ready for that kind of commitment. If they don't know that, maybe we're not meant to be together after all?"; } rejectedTheirProposalMoodDef.stages.Add(rejectedTheirProposalMoodStage); if (rejectedTheirProposalMoodStage.baseMoodEffect < 0f) { recipient.needs.mood.thoughts.memories.TryGainMemory(rejectedTheirProposalMoodDef, initiator); } recipient.needs.mood.thoughts.memories.TryGainMemory(rejectedTheirProposalDef, initiator); } else { recipient.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.IRejectedTheirProposal, initiator); } }
private ThoughtDef CreateSocialThought(out float opinionMod) { //We create a dynamic def to hold this thought so that the game won't worry about it being used anywhere else. ThoughtDef def = new ThoughtDef(); def.defName = this.pawn.GetHashCode() + "Conversation" + topic.defName; def.label = topic.defName; def.durationDays = PsychologyBase.ConvoDuration(); def.nullifyingTraits = new List <TraitDef>(); def.nullifyingTraits.Add(TraitDefOf.Psychopath); def.thoughtClass = typeof(Thought_MemorySocialDynamic); ThoughtStage stage = new ThoughtStage(); /* Base opinion mod is 5 to the power of controversiality. * Controversiality varies from 0.7 (~3.09) to 1.5 (~11.18). */ opinionMod = Mathf.Pow(5f, topic.controversiality); /* That opinion mod is weighted by the difference in their personality. 1 is identical, 0 is polar opposites. * Here's the math on how this works: * * The weighting is on a heavily customized cubic curve. It looks like this: https://www.wolframalpha.com/input/?i=(9.5((x-0.5)%5E3))%2B(2x%2F3)%5E2-0.3+from+0+to+1 * The maximum positive weight is 1.27x. The maximum negative weight is -1.4875x. The neutral weight (0x) is at an opinion diff of 0.706. * This means that to have a positive thought from a conversation, pawns need to have a <31% difference in personality on that topic. * However, since it's a cubic curve, the negative modifier builds gradually. Pawns will need a >79% difference in personality to have a -0.5x weight. * But they'll also need a <23% difference to have a 0.5x weight. Normal agreement is at ~0.962, and normal disagreement is at ~0.073. * Pawns are unlikely to have huge differences in opinion. There are more ways for them to be close to each other than far apart. Here's the proof: https://anydice.com/program/1177b4 * The approximate likelihood for them to agree on something with this weighting is 49.89%. * * Pawns' differences are exacerbated when they are in a relationship, but their similarities are also magnified. * The neutral conversation weight (0x) moves from 0.706 to 0.759, so the threshold for agreement is ~5% higher. * Normal disagreement moves from 0.073 to 0.127, and normal agreement moves from 0.962 to 0.946. * The maximum positive weight moves from 1.27x to 1.4986x. The maximum negative weight moves from -1.425x to -1.9875x. * The approximate likelihood for them to agree on something with this weighting is 42.63%. */ float rawOpinionDiff = 1f - Mathf.Abs(PsycheHelper.Comp(pawn).Psyche.GetPersonalityRating(topic) - PsycheHelper.Comp(otherPawn).Psyche.GetPersonalityRating(topic)); if (LovePartnerRelationUtility.LovePartnerRelationExists(this.pawn, this.otherPawn)) { opinionMod *= (13.5f * (Mathf.Pow(rawOpinionDiff - 0.5f, 3))) + Mathf.Pow(((3f * rawOpinionDiff) / 9f), 2) - 0.3f; } else { opinionMod *= (9.5f * (Mathf.Pow(rawOpinionDiff - 0.5f, 3))) + Mathf.Pow(((2f * rawOpinionDiff) / 3f), 2) - 0.3f; } //Old cubic interpolation weighting. //opinionMod *= Mathf.Lerp((LovePartnerRelationUtility.LovePartnerRelationExists(pawn, otherPawn) ? -2f : -1.5f), (LovePartnerRelationUtility.LovePartnerRelationExists(pawn, otherPawn) ? 1.5f : 1.25f), weightedOpinionDiff); /* The Cool modifier ranges from 3^(0.7) ~ 2.16 to 3^(1.5) ~ 5.2. * If a pawn is Cool, that modifier is a positive one added to all conversational thoughts. Otherwise, it's subtracted. * On average, Cool pawns will be liked better, and non-Cool pawns will be disliked more. */ opinionMod += Mathf.Pow(3f, topic.controversiality) * (PsycheHelper.Comp(otherPawn).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Cool) - 0.5f); /* The length of the talk has a large impact on how much the pawn cares. * A conversation is considered "full impact" at 1,125 ticks, or less than half an hour in-game. * A short talk (500 ticks or less) has a maximum 0.53x impact. The max opinion this could give (for non-lovers) is 7.5/-8.8. * A normal talk (half an hour or less) has a maximum 1.33x impact. The max opinion this could give is 18.9/-22.1. * A long talk (2.5 hours or less) has a maximum 6.67x impact. The max opinion this could give is 94.7/-110.9. * An epic talk (2.5+ hours) has no maximum impact, but after 2 hours the MTB to end the conversation becomes half an hour, so it's unlikely they will ever have an epic talk. * An average conversation is 1-2 hours, so on average the max opinion (not counting Cool modifier) is 37.9/-44.3 to 75.7/-88.7. * Again, it's unlikely the numbers will get that high. This is assuming identical or polar opposite personalities. */ opinionMod *= 6f * ((float)this.ageTicks / (float)(GenDate.TicksPerHour * 2.25f)); // Negative opinions are tempered by how Polite the other pawn is. An extremely impolite pawn will make a bad opinion 1.5x worse. A very polite pawn will make it half as bad. opinionMod *= (opinionMod < 0f ? 0.5f + (1f - PsycheHelper.Comp(otherPawn).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Polite)) : 1f); // Positive opinions are bolstered by how Friendly the other pawn is. An extremely friendly pawn will make a positive opinion 1.5x better. A very unfriendly pawn will halve it. opinionMod *= (opinionMod > 0f ? 0.5f + PsycheHelper.Comp(otherPawn).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Friendly) : 1f); // The more Judgmental the pawn, the more they're affected by all conversations. opinionMod *= 0.5f + PsycheHelper.Comp(pawn).Psyche.GetPersonalityRating(PersonalityNodeDefOf.Judgmental); // In low-population colonies, pawns will put aside their differences. if (opinionMod < 0f) { opinionMod *= PopulationModifier; } else if (LovePartnerRelationUtility.LovePartnerRelationExists(this.pawn, this.otherPawn) && this.pawn.story.traits.HasTrait(TraitDefOfPsychology.Codependent)) { //If it's a positive thought about their lover, Codependent pawns are always 1.25x as affected by it. opinionMod *= 1.25f; } stage.label = "ConversationStage".Translate() + " " + convoTopic; stage.baseOpinionOffset = Mathf.RoundToInt(opinionMod); def.stages.Add(stage); return(def); }
static void ListThoughtStageInfo([NotNull] ThoughtStage stage, [NotNull] StringBuilder builder) { builder.AppendLine($"{nameof(stage.label)}:{stage.label}\t{nameof(stage.labelSocial)}:{stage.labelSocial}"); builder.AppendLine($"{nameof(stage.visible)}:{stage.visible}\t{nameof(stage.baseMoodEffect)}:{stage.baseMoodEffect}\t{nameof(stage.baseOpinionOffset)}:{stage.baseOpinionOffset}"); builder.AppendLine($"description:\t{stage.description}"); }
public static bool NewInteracted(InteractionWorker_MarriageProposal __instance, Pawn initiator, Pawn recipient, List <RulePackDef> extraSentencePacks) { //TODO: Turn this into a transpihahaha no. float num = __instance.AcceptanceChance(initiator, recipient); bool flag = Rand.Value < num; bool brokeUp = false; if (flag) { initiator.relations.RemoveDirectRelation(PawnRelationDefOf.Lover, recipient); initiator.relations.AddDirectRelation(PawnRelationDefOf.Fiance, recipient); initiator.needs.mood.thoughts.memories.RemoveMemoriesOfDefWhereOtherPawnIs(ThoughtDefOf.RejectedMyProposal, recipient); recipient.needs.mood.thoughts.memories.RemoveMemoriesOfDefWhereOtherPawnIs(ThoughtDefOf.RejectedMyProposal, initiator); /* Remove custom Psychology rejection thoughts */ foreach (ThoughtDef d in (from tgt in initiator.needs.mood.thoughts.memories.Memories where tgt.def.defName.Contains("RejectedMyProposal") select tgt.def)) { initiator.needs.mood.thoughts.memories.RemoveMemoriesOfDefWhereOtherPawnIs(d, recipient); } foreach (ThoughtDef d in (from tgt in recipient.needs.mood.thoughts.memories.Memories where tgt.def.defName.Contains("RejectedMyProposal") select tgt.def)) { recipient.needs.mood.thoughts.memories.RemoveMemoriesOfDefWhereOtherPawnIs(d, initiator); } initiator.needs.mood.thoughts.memories.RemoveMemoriesOfDefWhereOtherPawnIs(ThoughtDefOf.IRejectedTheirProposal, recipient); recipient.needs.mood.thoughts.memories.RemoveMemoriesOfDefWhereOtherPawnIs(ThoughtDefOf.IRejectedTheirProposal, initiator); extraSentencePacks.Add(RulePackDefOf.Sentence_MarriageProposalAccepted); } else { PsychologyPawn realInitiator = initiator as PsychologyPawn; PsychologyPawn realRecipient = recipient as PsychologyPawn; if (realInitiator != null) { ThoughtDef rejectedProposalDef = new ThoughtDef(); rejectedProposalDef.defName = "RejectedMyProposal" + realInitiator.LabelShort + Find.TickManager.TicksGame; rejectedProposalDef.durationDays = 40f; rejectedProposalDef.thoughtClass = typeof(Thought_MemorySocialDynamic); ThoughtStage rejectedProposalStage = new ThoughtStage(); rejectedProposalStage.label = "rejected my proposal"; rejectedProposalStage.baseOpinionOffset = Mathf.RoundToInt(-30f * realInitiator.psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic) * Mathf.InverseLerp(100f, 5f, realInitiator.relations.OpinionOf(realRecipient))); rejectedProposalDef.stages.Add(rejectedProposalStage); ThoughtDef rejectedProposalMoodDef = new ThoughtDef(); rejectedProposalMoodDef.defName = "RejectedMyProposalMood" + realInitiator.LabelShort + Find.TickManager.TicksGame; rejectedProposalMoodDef.durationDays = 25f; rejectedProposalMoodDef.thoughtClass = typeof(Thought_MemoryDynamic); rejectedProposalMoodDef.stackedEffectMultiplier = 1f; ThoughtStage rejectedProposalMoodStage = new ThoughtStage(); rejectedProposalMoodStage.label = "proposal rejected by {0}"; rejectedProposalMoodStage.baseMoodEffect = Mathf.RoundToInt(-25f * realInitiator.psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic) * Mathf.InverseLerp(100f, 5f, realInitiator.relations.OpinionOf(realRecipient))); if (rejectedProposalMoodStage.baseMoodEffect < -5f) { rejectedProposalMoodStage.description = "My lover isn't ready for that kind of commitment right now, and I understand, but rejection is hard to take."; } else { rejectedProposalMoodStage.description = "I can't believe I got turned down. Maybe we're not meant to be together after all?"; } rejectedProposalMoodDef.stages.Add(rejectedProposalMoodStage); if (rejectedProposalMoodStage.baseMoodEffect > 0) { realInitiator.needs.mood.thoughts.memories.TryGainMemory(rejectedProposalMoodDef, realRecipient); } realInitiator.needs.mood.thoughts.memories.TryGainMemory(rejectedProposalDef, realRecipient); } else { initiator.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.RejectedMyProposal, recipient); } recipient.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.IRejectedTheirProposal, initiator); extraSentencePacks.Add(RulePackDefOf.Sentence_MarriageProposalRejected); if (realRecipient != null && !recipient.story.traits.HasTrait(TraitDefOfPsychology.Codependent) && Rand.Value > 2f * realRecipient.psyche.GetPersonalityRating(PersonalityNodeDefOf.Romantic)) { recipient.interactions.TryInteractWith(initiator, DefDatabase <InteractionDef> .GetNamed("Breakup")); } else if (realRecipient == null && !recipient.story.traits.HasTrait(TraitDefOfPsychology.Codependent) && Rand.Value < 0.4f) { initiator.relations.RemoveDirectRelation(PawnRelationDefOf.Lover, recipient); initiator.relations.AddDirectRelation(PawnRelationDefOf.ExLover, recipient); brokeUp = true; extraSentencePacks.Add(RulePackDefOf.Sentence_MarriageProposalRejectedBrokeUp); } } if (initiator.IsColonist || recipient.IsColonist) { Traverse.Create(__instance).Method("SendLetter", new[] { typeof(Pawn), typeof(Pawn), typeof(bool), typeof(bool) }).GetValue(new object[] { initiator, recipient, flag, brokeUp }); } return(false); }