using RimWorld; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection.Emit; using System.Text; using System.Threading.Tasks; using Verse; namespace RJW_Genes { /// /// DevNote: Issue #37 came along because I checked for getMother() and getFather(), but it can happen that a pawn has two mothers. /// They are called Mother if they have a ParentRelation and are female. /// New behaviour iterates over all parents and returns the first queen/drone or null. /// public class HiveBirthLogic { /// /// Central function for the Hive-Birth logic used in Patches. /// *Only* run this, if the pawn has a queen parent (either as mother/father, or as implanter in case of egg-logic). /// Covers the following behavior: /// 1. look up the Defs for the mother and HiveOffspringChances (or defaults) /// 2. If there is no drone involved, default to worker /// 3. Roll a random dice /// 3.1 Make a queen /// 3.2 Make a drone /// 3.3 Make a worker /// /// The pawn born, that maybe becomes a hive-xenotype. /// whether there was a drone parent involved public static void ManageHiveBirth(Pawn pawn, bool hasDroneParent = false, Either fallbackQueenDef = null, Either fallbackDroneDef = null) { Either queenDef = TryFindParentQueenXenotype(pawn); if (queenDef == null) queenDef = fallbackQueenDef; HiveOffspringChanceDef hiveOffspringChanceDef = HiveUtility.LookupHiveInheritanceChances(queenDef); // Case 1: Mother is Queen, Father is something else. Produce Worker. if (!hasDroneParent) { if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"{pawn} was born as a worker, as it did not have Drone Father ({100}% chance)"); MakeWorker(pawn, queenDef); } // Case 2: Mother is Queen, Father is drone. Apply xenotype as per chance. else { double roll = (new Random()).NextDouble(); // Case 2.a: New Queen born if (roll < hiveOffspringChanceDef.queenChance) { MakeQueen(pawn, queenDef); if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"Queen Chance: {hiveOffspringChanceDef.queenChance * 100}% chance,rolled { roll}"); } // Case 2.b: New Drone born else if (roll < hiveOffspringChanceDef.droneChance + hiveOffspringChanceDef.queenChance) { var droneDef = TryFindParentDroneXenotype(pawn); MakeDrone(pawn,droneDef); if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"Drone Chance ({(hiveOffspringChanceDef.droneChance + hiveOffspringChanceDef.queenChance) * 100}% chance,rolled {roll}))"); } // Case 2.c: Worker else { if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"{pawn} born as a worker ({(hiveOffspringChanceDef.workerChance) * 100}% chance,rolled {roll}))"); MakeWorker(pawn, queenDef); } } } private static void MakeQueen(Pawn pawnToBeQueen, Either queenDef) { if (queenDef == null && pawnToBeQueen == null) return; if (queenDef.isLeft) { var xenotype = queenDef.left; pawnToBeQueen.genes.SetXenotype(xenotype); if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"{pawnToBeQueen} born as a new queen with Xenotype {xenotype.defName}"); } else { var customXenotype = queenDef.right; foreach (var gene in customXenotype.genes) pawnToBeQueen.genes.AddGene(gene, true); pawnToBeQueen.genes.xenotypeName = customXenotype.name; pawnToBeQueen.genes.iconDef = customXenotype.iconDef; if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"{pawnToBeQueen} born as a new queen with custom Xenotype {customXenotype.name}"); } MakeQueenBornLetter(pawnToBeQueen); } private static void MakeDrone(Pawn pawnToBeDrone, Either droneDef) { if (droneDef == null && pawnToBeDrone == null) return; if (droneDef.isLeft) { var xenotype = droneDef.left; pawnToBeDrone.genes.SetXenotype(xenotype); if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"{pawnToBeDrone} born as a new drone with Xenotype {xenotype.defName}"); } else { var customXenotype = droneDef.right; foreach (var gene in customXenotype.genes) pawnToBeDrone.genes.AddGene(gene, true); pawnToBeDrone.genes.xenotypeName = customXenotype.name; pawnToBeDrone.genes.iconDef = customXenotype.iconDef; if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"{pawnToBeDrone} born as a new drone with custom Xenotype {customXenotype.name}"); } } /// /// Turns a given pawn into a worker, by looking up the relevant genes as per queen. /// /// If the queen xenotype has no mapping, the "rjw_genes_default_worker_xenotype" are used instead. /// The genes are added as endogenes, so the worker can still become a xenotype. /// /// The pawn for which the genes are added. /// The xenotype of the queen, used for lookup. private static void MakeWorker(Pawn pawnTobeWorker, Either queenDef) { if (pawnTobeWorker == null) return; var mappings = HiveUtility.GetQueenWorkerMappings(); String queenDefName = HiveUtility.GetXenotypeDefName(queenDef); if (queenDef == null || mappings.NullOrEmpty()) return; var genes = mappings.TryGetValue(queenDefName, HiveUtility.LookupDefaultWorkerGenes()); if (genes == null) return; foreach (var gene in genes) pawnTobeWorker.genes.AddGene(gene, false); pawnTobeWorker.genes.xenotypeName = "Worker"; } /// /// Looks up if there is a Xenotype with Drone-Gene for the pawns parents. /// This is to account that maybe father or mother are the drone (instead of hardcoding things for father). /// If both are drones, the mothers is returned. /// /// The pawn for whichs parent the xenotypes is looked up. /// The Drone-Xenotype of a parent or null. If both are drones, mothers are preferred. public static Either TryFindParentDroneXenotype(Pawn pawn) { if (pawn == null) return null; List parentRelations = pawn.relations.DirectRelations.FindAll(rel => rel.def.Equals(PawnRelationDefOf.Parent)); foreach (DirectPawnRelation parent in parentRelations) { var xenotype = HiveUtility.TryGetDroneXenotype(parent.otherPawn); if (xenotype != null) return xenotype; } return null; } public static void MakeQueenBornLetter(Pawn bornQueen) { if (bornQueen == null) return; var letter= LetterMaker.MakeLetter( "New Queen", "A new Queen was born! Make sure to adress succession before the new queen reaches adolesence.", LetterDefOf.NeutralEvent, bornQueen ); Find.LetterStack.ReceiveLetter(letter); } /// /// Looks up if there is a Xenotype with Queen-Gene for the pawns parents. /// This is to account that maybe father or mother are the queen (instead of hardcoding things for father). /// If both are queens, the first is returned. /// /// The pawn for whichs parent the xenotypes is looked up. /// The Queen-Xenotype of a parent or null. If both are queens, mothers are preferred. public static Either TryFindParentQueenXenotype(Pawn pawn) { if (pawn == null) return null; List parentRelations = pawn.relations.DirectRelations.FindAll(rel => rel.def.Equals(PawnRelationDefOf.Parent)); foreach (var parent in parentRelations) { var xenotype = HiveUtility.TryGetQueenXenotype(parent.otherPawn); if (xenotype != null) return xenotype; } return null; } } }