}//end JobOnThing()

        /// <summary>
        /// Checks if a pawn can be assigned to a bill based on factors including PawnRestrictions, workSkills, etc.
        /// If a bill passes all the checks, it's assigned as the ActiveBill in the billProcessor.
        /// </summary>
        /// <param name="p"></param>
        /// <param name="grower"></param>
        /// <param name="reason"></param>
        /// <returns></returns>
        public Bill GetBillPawnCanDo(Pawn p, Building_GrowerBase_WorkTable grower, out string reason)
        {
            reason = GenericFailReasonTrans;

            BillStack       bills  = grower.billStack;
            LocalTargetInfo target = grower;

            //loop through bills
            for (int i = 0; i < bills.Count; i++)
            {
                Bill_Production theBill = bills[i] as Bill_Production;

                if (!PawnCanDoThisBill(p, theBill, target, out reason))
                {
                    continue;
                }

                //check if cached ingredients search found ingredients for this bill
                Thing cachedThing;
                grower.billProc.ingredientsAvailableNow.TryGetValue(theBill.GetUniqueLoadID(), out cachedThing);
                if (cachedThing == null)
                {
                    QEEMod.TryLog("GetBillPawnCanDo - no ingredients available");
                    reason = NoIngredientsTrans;
                    continue;
                }

                grower.billProc.ActiveBill = theBill;
                QEEMod.TryLog(p.LabelShort + " can do bill " + theBill.GetUniqueLoadID());
                return(theBill);
            }
            return(null);
        } //end GetBillPawnCanDo()
Beispiel #2
0
        /// <summary>
        /// Copy of vanilla's Recipe_InstallNaturalBodyPart.GetPartsToApplyOn() plus the addition of code that respects the addsHediff node
        /// in the recipe (vanilla does not). The addsHediff node is currently used in the RBSE compatibility patch to add organ rejection.
        /// </summary>
        /// <param name="pawn"></param>
        /// <param name="part"></param>
        /// <param name="billDoer"></param>
        /// <param name="ingredients"></param>
        /// <param name="bill"></param>
        public override void ApplyOnPawn(Pawn pawn, BodyPartRecord part, Pawn billDoer, List <Thing> ingredients, Bill bill)
        {
            if (billDoer != null && !CheckSurgeryFail(billDoer, pawn, ingredients, part, bill))
            {
                TaleRecorder.RecordTale(TaleDefOf.DidSurgery, billDoer, pawn);
                MedicalRecipesUtility.RestorePartAndSpawnAllPreviousParts(pawn, part, billDoer.Position, billDoer.Map);
            }

            //add any hediffs in the <addsHediff> element of the recipe, after surgery involving natural body parts completes.
            if (bill.recipe.addsHediff != null && RBSECompat.GetOrganRejectionSetting() == true)
            {
                if (part.LabelShort == "shoulder")
                {
                    //add hediff to arm bodypart
                    foreach (BodyPartRecord childPart in part.parts)
                    {
                        if (childPart.def == BodyPartDefOf.Arm)
                        {
                            QEEMod.TryLog("Adding hediff " + bill.recipe.addsHediff.label + " to installed BodyPart " + childPart.Label);
                            pawn.health.AddHediff(bill.recipe.addsHediff, childPart);
                            return;
                        }
                    }

                    QEEMod.TryLog("No arm found in shoulder replacement!");
                }
                else
                {
                    QEEMod.TryLog("Adding hediff " + bill.recipe.addsHediff.label + " to installed BodyPart " + part.Label);
                    pawn.health.AddHediff(bill.recipe.addsHediff, part);
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Instantiate any Types that exist in the other mod for later use in the QEE codebase.
        /// </summary>
        /// <param name="assemblies">The list of the other mod's loaded assemblies</param>
        /// <returns>Indicates whether all types and members were loaded successfully from the other mod</returns>
        public static bool Init(List <Assembly> assemblies)
        {
            HediffPsychicAwakenedType = assemblies.Select(assembly => assembly.GetType("RimWorld.HediffPsychicAwakened")).FirstOrDefault(type => type != null);

            if (HediffPsychicAwakenedType != null)
            {
                PsychicPowerDefType = HediffPsychicAwakenedType.Assembly.GetType("RimWorld.PsychicPowerDef");

                if (PsychicPowerDefType == null)
                {
                    QEEMod.TryLog("Psychic Awakened mod detected, but 'PsychicPowerDef' type missing. Psychic Awakening functionality disabled.");
                    return(false);
                }

                powersKnownField = HediffPsychicAwakenedType.GetField("powersKnown", BindingFlags.Public | BindingFlags.Instance);

                if (powersKnownField == null)
                {
                    QEEMod.TryLog("Psychic Awakened mod detected, but 'powersKnown' field missing. Psychic Awakening functionality disabled.");
                    return(false);
                }
            }
            else
            {
                QEEMod.TryLog("Psychic Awakened mod detected, but attempts to get HediffPsychicAwakened type failed. Psychic Awakening functionality disabled.");
                return(false);
            }

            return(true);
        }
Beispiel #4
0
        public static Thing MakeGenomeSequence(Pawn pawn, ThingDef genomeDef)
        {
            Thing          genomeThing    = ThingMaker.MakeThing(genomeDef);
            GenomeSequence genomeSequence = genomeThing as GenomeSequence;

            if (genomeSequence != null)
            {
                //Standard.
                genomeSequence.sourceName = pawn?.Name?.ToStringFull ?? null;
                if (genomeSequence.sourceName == null)
                {
                    genomeSequence.sourceName = pawn.def.LabelCap;
                }
                genomeSequence.pawnKindDef = pawn.kindDef;
                genomeSequence.gender      = pawn.gender;

                if (pawn?.health?.hediffSet?.hediffs != null)
                {
                    List <Hediff> pawnHediffs = pawn.health.hediffSet.hediffs;
                    if (pawnHediffs.Count > 0)
                    {
                        foreach (Hediff h in pawnHediffs)
                        {
                            if (GeneralCompatibility.includedGenomeTemplateHediffs.Any(hediffDef => h.def.defName == hediffDef.defName))
                            {
                                QEEMod.TryLog("Hediff " + h.def.defName + " will be added to genome template");

                                genomeSequence.hediffInfos.Add(new HediffInfo(h));
                            }
                        }
                    }
                }

                //Humanoid only.
                Pawn_StoryTracker story = pawn.story;
                if (story != null)
                {
                    genomeSequence.bodyType        = story.bodyType;
                    genomeSequence.crownType       = story.crownType;
                    genomeSequence.hairColor       = story.hairColor;
                    genomeSequence.skinMelanin     = story.melanin;
                    genomeSequence.hair            = story.hairDef;
                    genomeSequence.headGraphicPath = story.HeadGraphicPath;

                    foreach (Trait trait in story.traits.allTraits)
                    {
                        genomeSequence.traits.Add(new ExposedTraitEntry(trait));
                    }
                }

                //Alien Races compatibility.
                if (CompatibilityTracker.AlienRacesActive)
                {
                    AlienRaceCompat.GetFieldsFromAlienComp(pawn, genomeSequence);
                }
            }

            return(genomeThing);
        }
Beispiel #5
0
            static void SpawnNaturalPartIfCleanPostfix(ref Thing __result, Pawn pawn, BodyPartRecord part, IntVec3 pos, Map map)
            {
                bool isOrganTransplant         = false;
                bool shouldDropTransplantOrgan = false;
                int  badHediffCount            = 0;

                foreach (Hediff currHediff in pawn.health.hediffSet.hediffs)
                {
                    if (currHediff.Part == part)
                    {
                        QEEMod.TryLog("Hediff on this body part: " + currHediff.def.defName + " isBad: " + currHediff.def.isBad);
                        if (currHediff.def == QEHediffDefOf.QE_OrganRejection)
                        {
                            isOrganTransplant = true;
                        }
                        else if (currHediff.def.isBad)
                        {
                            badHediffCount++;
                        }
                    }
                }

                //if the only hediff on bodypart is organ rejection, that organ should spawn
                //vanilla game would not spawn it, because part hediffs > 0
                if (isOrganTransplant && badHediffCount == 0 && part.def.spawnThingOnRemoved != null)
                {
                    shouldDropTransplantOrgan = true;
                }

                QEEMod.TryLog("shouldDropTransplantOrgan: " + shouldDropTransplantOrgan + " [isOrganTransplant: " +
                              isOrganTransplant + " " + part.LabelShort + " bad hediffs: " + badHediffCount + "]");

                //spawn a biological arm when a shoulder is removed with a healthy arm attached
                if (part.LabelShort == "shoulder")
                {
                    foreach (BodyPartRecord childPart in part.parts)
                    {
                        bool isHealthy = MedicalRecipesUtility.IsClean(pawn, childPart);
                        QEEMod.TryLog("body part: " + childPart.LabelShort + " defName: " + childPart.def.defName +
                                      " healthy: " + isHealthy);

                        if (childPart.def == BodyPartDefOf.Arm && isHealthy && (shouldDropTransplantOrgan = true ||
                                                                                                            isOrganTransplant == false))
                        {
                            QEEMod.TryLog("Spawn natural arm from shoulder replacement");
                            __result = GenSpawn.Spawn(QEThingDefOf.QE_Organ_Arm, pos, map);
                        }
                    }
                }
                else if (shouldDropTransplantOrgan)
                {
                    __result = GenSpawn.Spawn(part.def.spawnThingOnRemoved, pos, map);
                }
            }
        public static bool GetOrganRejectionSetting()
        {
            //only set rejectionEnabled to true if the mod loads successfully and we can retrieve the value from settings
            bool rejectionEnabled = false;

            var rbseModType = GenTypes.GetTypeInAnyAssembly("RBSE.Mod", "RBSE");

            if (rbseModType != null)
            {
                var rbseMod = LoadedModManager.GetMod(rbseModType);

                if (rbseMod != null)
                {
                    //The RBSE.Settings class is not available w/ public modifier. Use Reflection to get the Type
                    var rbseSettingsType = rbseModType.Assembly.GetType("RBSE.Settings");
                    if (rbseSettingsType != null)
                    {
                        var settings = typeof(Mod)?.GetMethod(nameof(Mod.GetSettings), Type.EmptyTypes)?.MakeGenericMethod(rbseSettingsType)?.Invoke(rbseMod, new object[0]);
                        if (settings != null)
                        {
                            var toggleActiveRejection = settings?.GetType()?.GetField("ToggleActiveRejection", AccessTools.all)?.GetValue(settings);
                            if (toggleActiveRejection != null)
                            {
                                QEEMod.TryLog("toggleActiveRejection value: " + toggleActiveRejection);
                                rejectionEnabled = (bool)toggleActiveRejection;
                            }
                            else
                            {
                                QEEMod.TryLog("Problem retrieving toggleActiveRejection ModSetting value from RBSE mod. Organ rejection hediff will not be added.");
                            }
                        }
                        else
                        {
                            QEEMod.TryLog("Error making generic GetSettings() method for RBSE compatibility. Organ rejection hediff will not be added.");
                        }
                    }
                    else
                    {
                        QEEMod.TryLog("RBSE class 'RBSE.Settings' not found. Organ rejection hediff will not be added.");
                    }
                }
                else
                {
                    QEEMod.TryLog("GetMod() returned null for RBSE. Organ rejection hediff will not be added.");
                }
            }
            else
            {
                QEEMod.TryLog("Class 'RBSE.Mod' not found. Organ rejection hediff will not be added.");
            }

            return(rejectionEnabled);
        }
        public override void ExposeData()
        {
            base.ExposeData();

            Scribe_Values.Look(ref sourceName, "sourceName");
            Scribe_Defs.Look(ref kindDef, "kindDef");

            string childhoodIdentifier = (backStoryChild == null) ? null : backStoryChild.identifier;

            Scribe_Values.Look(ref childhoodIdentifier, "backStoryChild");
            if (Scribe.mode == LoadSaveMode.LoadingVars && !childhoodIdentifier.NullOrEmpty())
            {
                if (!BackstoryDatabase.TryGetWithIdentifier(childhoodIdentifier, out backStoryChild, true))
                {
                    Log.Error("Couldn't load child backstory with identifier " + childhoodIdentifier + ". Giving random.", false);
                    backStoryChild = BackstoryDatabase.RandomBackstory(BackstorySlot.Childhood);
                }
            }

            string adulthoodIdentifier = (backStoryAdult == null) ? null : backStoryAdult.identifier;

            Scribe_Values.Look(ref adulthoodIdentifier, "backStoryAdult");
            if (Scribe.mode == LoadSaveMode.LoadingVars && !adulthoodIdentifier.NullOrEmpty())
            {
                if (!BackstoryDatabase.TryGetWithIdentifier(adulthoodIdentifier, out backStoryAdult, true))
                {
                    Log.Error("Couldn't load adult backstory with identifier " + adulthoodIdentifier + ". Giving random.", false);
                    backStoryAdult = BackstoryDatabase.RandomBackstory(BackstorySlot.Adulthood);
                }
            }

            Scribe_Collections.Look(ref skills, "skills", LookMode.Deep);
            Scribe_Values.Look(ref isAnimal, "isAnimal");
            Scribe_Deep.Look(ref trainingLearned, "trainingLearned");
            Scribe_Deep.Look(ref trainingSteps, "trainingSteps");
            Scribe_Collections.Look(ref hediffInfos, "hediffInfos", LookMode.Deep);

            if (Scribe.mode == LoadSaveMode.LoadingVars && hediffInfos != null)
            {
                //remove any hediffs where the def is missing. Most commonly occurs when a mod is removed from a save.
                int removed = hediffInfos.RemoveAll(h => h.def == null);
                if (removed > 0)
                {
                    QEEMod.TryLog("Removed " + removed + " null hediffs from hediffInfo list for " + sourceName + "'s brain template ");
                }
            }
        }
        static CompatibilityTracker()
        {
            foreach (string s in incompatibleModArr)
            {
                if (ModsConfig.ActiveModsInLoadOrder.Any((ModMetaData m) => m.Name == s))
                {
                    Log.Error("Questionable Ethics Enhanced is incompatible with " + s);
                }
            }

            //Check for Alien Races Compatiblity
            if (GenTypes.AllTypes.Any(type => type.FullName == "AlienRace.ThingDef_AlienRace"))
            {
                alienRacesActiveInt = true;
                QEEMod.TryLog("Humanoid Alien Races detected");
            }
        }
Beispiel #9
0
            static void SpawnNaturalPartIfCleanPostfix(ref Thing __result, Pawn pawn, BodyPartRecord part, IntVec3 pos, Map map)
            {
                //spawn a biological arm when a shoulder is removed with a healthy arm attached (e.g. from installing a prosthetic on a healthy arm)
                if (part.LabelShort == "shoulder")
                {
                    foreach (BodyPartRecord childPart in part.parts)
                    {
                        bool isHealthy = MedicalRecipesUtility.IsClean(pawn, childPart);
                        QEEMod.TryLog("body part: " + childPart.LabelShort + " IsClean: " + isHealthy);

                        if (childPart.def == BodyPartDefOf.Arm && isHealthy)
                        {
                            QEEMod.TryLog("Spawn natural arm from shoulder replacement");
                            __result = GenSpawn.Spawn(QEThingDefOf.QE_Organ_Arm, pos, map);
                        }
                    }
                }
            }
        public HediffInfo(Hediff h)
        {
            def  = h.def;
            part = h.Part;

            if (CompatibilityTracker.PsychicAwakeningActive)
            {
                //check if this is a Psychic Awakened hediff
                if (PsychicAwakeningCompat.HediffPsychicAwakenedType.IsInstanceOfType(h))
                {
                    //get the value of the 'powersKnown' field from the Hediff
                    var powersValue = PsychicAwakeningCompat.powersKnownField.GetValue(h);

                    if (powersValue != null)
                    {
                        if (powersValue is System.Collections.IEnumerable)
                        {
                            psychicAwakeningPowersKnownDefNames = new List <string>();

                            //loop through each object in the 'powersKnown' List
                            foreach (object o in (powersValue as System.Collections.IEnumerable))
                            {
                                //PsychicPowerDef is derived from class Def, so this cast should work
                                Def theDef = o as Def;
                                if (theDef != null)
                                {
                                    QEEMod.TryLog("Adding PsychicPower " + theDef.defName + " to psychicAwakeningPowersKnownDefNames");
                                    psychicAwakeningPowersKnownDefNames.Add(theDef.defName);
                                }
                            }
                        }
                        else
                        {
                            QEEMod.TryLog("'powersKnown' field from Psychic hediff is not System.Collections.IEnumerable");
                        }
                    }
                    else
                    {
                        QEEMod.TryLog("Unable to retrieve value of 'powersKnown' field from Psychic hediff");
                    }
                }
            }
        }
            static void IsCleanPostfix(ref bool __result, Pawn pawn, BodyPartRecord part)
            {
                if (pawn.Dead)
                {
                    __result = false;
                    return;
                }

                BodyPartRecord diseasedPart = null;

                if (PawnUtility.bodyPartOrChildHasHediffs(pawn, part, out diseasedPart))
                {
                    QEEMod.TryLog("IsClean() false for " + part.Label + " because " + diseasedPart.Label + " has bad Hediffs");
                    __result = false;
                    return;
                }

                __result = true;
            } //end postfix
        static CompatibilityTracker()
        {
            foreach (ModContentPack m in LoadedModManager.RunningMods)
            {
                string modName = m.Name;

                //Fully incompatible mods
                if (modName == "Questionable Ethics")
                {
                    Log.Error("Questionable Ethics Enhanced is incompatible with " + modName);
                    continue;
                }

                //Partially incompatible mods
                else if (modName == "Multiplayer")
                {
                    Log.Warning("Questionable Ethics Enhanced works with the Multiplayer mod, but is incompatible with the Multiplayer game mode.");
                    continue;
                }

                //Enhanced compatibility mods
                else if (modName.Contains("Humanoid Alien Races 2.0"))
                {
                    alienRacesActiveInt = true;
                    QEEMod.TryLog("Humanoid Alien Races detected");
                }

                else if (modName.Contains("Psychic Awakening"))
                {
                    QEEMod.TryLog("Psychic Awakening detected");

                    psychicAwakeningActiveInt = PsychicAwakeningCompat.Init(m.assemblies.loadedAssemblies);
                }
                else if (modName.Contains("Rah's Bionics and Surgery Expansion") || modName.Contains("RBSE Hardcore Edition"))
                {
                    rbseActiveInt = true;
                    QEEMod.TryLog("RBSE detected");
                }
            }
        }
Beispiel #13
0
        /// <summary>
        /// Finds the closest Thing on the map to a pawn that matches the ThingDef needed by the Bill.
        /// The available ingredients are cached in the BillProcessor variable in Building_GrowerBase_WorkTable.
        /// If the cached ThingDef is no longer available, it will look for another ThingDef that's needed.
        /// </summary>
        /// <param name="theBill"></param>
        /// <param name="finder"></param>
        /// <param name="countForVat"></param>
        /// <returns></returns>
        public static Thing ThingPawnShouldRetrieveForBill(Bill theBill, Pawn finder, ref int countForVat)
        {
            Building_GrowerBase_WorkTable vat = theBill.billStack.billGiver as Building_GrowerBase_WorkTable;
            ThingOwner vatStoredIngredients   = vat.GetDirectlyHeldThings();

            Thing cachedThing     = null;
            bool  ingAreAvailable = vat.billProc.ingredientsAvailableNow.TryGetValue(theBill.GetUniqueLoadID(), out cachedThing);

            if (cachedThing == null)
            {
                QEEMod.TryLog("ThingPawnShouldRetrieveForBill() returning null. Reason - cachedThing is null");
                return(null);
            }

            if (ingAreAvailable == false)
            {
                QEEMod.TryLog("ThingPawnShouldRetrieveForBill() returning null. Reason - ingAreAvailable is false");
                return(null);
            }

            ThingRequest      tRequest;
            ThingOrderRequest desiredRequest;

            vat.billProc.desiredRequests.TryGetValue(cachedThing.def.defName, out desiredRequest);

            //check that the vat still needs the cached ingredient before searching the map for the same ThingDef
            if (desiredRequest == null || desiredRequest.amount <= 0)
            {
                QEEMod.TryLog("Cached ingredient " + cachedThing.LabelShort + " is already fulfilled in vat. Looking for next ingredient in recipe");

                //this ingredient isn't in desiredIngredients or the vat has the full amount. Refresh desiredIngredients and try once more
                vat.billProc.UpdateDesiredRequests();

                //now get a random item in the dictionary of desired requests
                foreach (ThingOrderRequest value in vat.billProc.desiredRequests.Values)
                {
                    desiredRequest = value;
                }

                //return if there's no thingDef or desiredRequest is null
                if (desiredRequest == null)
                {
                    QEEMod.TryLog("ThingPawnShouldRetrieveForBill() returning null. Reason - Desired ThingOrderRequest is null");
                    return(null);
                }

                //return if the amount for this request is 0
                if (desiredRequest.amount <= 0)
                {
                    QEEMod.TryLog("ThingPawnShouldRetrieveForBill() returning null. Reason - Desired Thing " + desiredRequest.Label + " has 0 amount");
                    return(null);
                }
            }

            tRequest = desiredRequest.GetThingRequest();

            if (tRequest.IsUndefined)
            {
                QEEMod.TryLog("ThingPawnShouldRetrieveForBill() returning null. Reason - ThingRequest for " + desiredRequest.Label +
                              " returned undefined ThingRequest");
                return(null);
            }

            countForVat = desiredRequest.amount;


            QEEMod.TryLog("Searching map for closest " + desiredRequest.Label + " to " + finder.LabelShort);

            //search the map for the closest Thing to the pawn that matches the ThingDef in 'tRequest'
            Thing result = GenClosest.ClosestThingReachable(finder.Position, finder.Map, tRequest,
                                                            PathEndMode.OnCell, TraverseParms.For(finder),
                                                            validator :
                                                            delegate(Thing testThing)
            {
                if (tRequest.Accepts(testThing))
                {
                    if (testThing.IsForbidden(finder))
                    {
                        return(false);
                    }

                    if (!finder.CanReserve(testThing))
                    {
                        return(false);
                    }
                    return(true);
                }

                return(false);
            });

            if (result != null)
            {
                QEEMod.TryLog(finder.LabelShort + " should retrieve: " + result.Label + " | stackCount: " + result.stackCount +
                              " | countForVat: " + countForVat);
            }

            return(result);
        } //end FindClosestIngForBill
        //this is a modified version of vanilla's RestUtility.IsValidBedFor() that adds medical bed checks
        public static bool IsValidMedicalBed(Thing bedThing, Pawn sleeper, Pawn traveler, bool sleeperWillBePrisoner,
                                             bool checkSocialProperness, bool ignoreOtherReservations = false)
        {
            Building_Bed building_Bed = bedThing as Building_Bed;

            if (building_Bed == null)
            {
                QEEMod.TryLog("QE_BedInvalid".Translate(bedThing.def.defName));
                return(false);
            }
            LocalTargetInfo target             = building_Bed;
            PathEndMode     peMode             = PathEndMode.OnCell;
            Danger          maxDanger          = Danger.Some;
            int             sleepingSlotsCount = building_Bed.SleepingSlotsCount;

            if (!building_Bed.Medical && !sleeper.RaceProps.Animal)
            {
                QEEMod.TryLog("QE_BedIsNotMedical".Translate(building_Bed.def.defName));
                return(false);
            }
            else if (!traveler.CanReserveAndReach(target, peMode, maxDanger, sleepingSlotsCount, -1, null, ignoreOtherReservations))
            {
                QEEMod.TryLog("QE_BedUnreachableBySurgeon".Translate(building_Bed.def.defName, traveler.Named("SURGEON")));
                return(false);
            }
            else if (building_Bed.Position.GetDangerFor(sleeper, sleeper.Map) > maxDanger)
            {
                QEEMod.TryLog("QE_BedTooDangerous".Translate(building_Bed.def.defName, traveler.Named("PATIENT")));
                return(false);
            }
            else if (traveler.HasReserved <JobDriver_TakeToBed>(building_Bed, sleeper))
            {
                QEEMod.TryLog("QE_BedSurgeonAlreadyReserved".Translate(building_Bed.def.defName, traveler.Named("SURGEON")));
                return(false);
            }
            else if (!building_Bed.AnyUnoccupiedSleepingSlot && (!sleeper.InBed() || sleeper.CurrentBed() != building_Bed) &&
                     !building_Bed.CompAssignableToPawn.AssignedPawns.Contains(sleeper))
            {
                QEEMod.TryLog("QE_BedOccupied".Translate(building_Bed.def.defName));
                return(false);
            }
            else if (building_Bed.IsForbidden(traveler))
            {
                QEEMod.TryLog("QE_BedForbidden".Translate(building_Bed.def.defName));
                return(false);
            }
            else if (checkSocialProperness && !building_Bed.IsSociallyProper(sleeper, sleeperWillBePrisoner))
            {
                QEEMod.TryLog("QE_BedFailsSocialChecks".Translate(building_Bed.def.defName));
                return(false);
            }
            else if (building_Bed.IsBurning())
            {
                QEEMod.TryLog("QE_BedIsBurning".Translate(building_Bed.def.defName));
                return(false);
            }
            else if (sleeperWillBePrisoner)
            {
                if (!building_Bed.ForPrisoners)
                {
                    QEEMod.TryLog("QE_BedImproperForPrisoner".Translate(building_Bed.def.defName, traveler.Named("PATIENT")));
                    return(false);
                }
                if (!building_Bed.Position.IsInPrisonCell(building_Bed.Map))
                {
                    QEEMod.TryLog("QE_BedNotInPrisonCell".Translate(building_Bed.def.defName, traveler.Named("PATIENT")));
                    return(false);
                }
            }
            else
            {
                if (building_Bed.Faction != traveler.Faction && (traveler.HostFaction == null || building_Bed.Faction != traveler.HostFaction))
                {
                    QEEMod.TryLog("QE_BedImproperFaction".Translate(building_Bed.def.defName, traveler.Named("SURGEON")));
                    return(false);
                }
                if (building_Bed.ForPrisoners)
                {
                    QEEMod.TryLog("QE_PrisonerBedForNonPrisoner".Translate(building_Bed.def.defName, traveler.Named("PATIENT")));
                    return(false);
                }
            }
            //fail if bed is owned
            if (building_Bed.OwnersForReading.Any() && !building_Bed.OwnersForReading.Contains(sleeper) && !building_Bed.AnyUnownedSleepingSlot)
            {
                QEEMod.TryLog("QE_BedHasAnotherOwner".Translate(building_Bed.def.defName));
                return(false);
            }
            return(true);
        }
Beispiel #15
0
        public static Thing MakeBrainScan(Pawn pawn, ThingDef genomeDef)
        {
            Thing             brainScanThing = ThingMaker.MakeThing(genomeDef);
            BrainScanTemplate brainScan      = brainScanThing as BrainScanTemplate;

            if (brainScan != null)
            {
                //Standard.
                brainScan.sourceName = pawn?.Name?.ToStringFull ?? null;
                brainScan.kindDef    = pawn?.kindDef ?? null;

                //Backgrounds
                Pawn_StoryTracker story = pawn.story;
                if (story != null)
                {
                    brainScan.backStoryChild = story.childhood;
                    brainScan.backStoryAdult = story.adulthood;
                }

                //Skills
                Pawn_SkillTracker skillTracker = pawn.skills;
                if (skillTracker != null)
                {
                    foreach (SkillRecord skill in skillTracker.skills)
                    {
                        brainScan.skills.Add(new ComparableSkillRecord()
                        {
                            def     = skill.def,
                            level   = skill.Level,
                            passion = skill.passion
                        });
                    }
                }

                //Animal
                brainScan.isAnimal = pawn.RaceProps.Animal;

                //Training
                Pawn_TrainingTracker trainingTracker = pawn.training;
                if (trainingTracker != null)
                {
                    DefMap <TrainableDef, bool> learned = (DefMap <TrainableDef, bool>)AccessTools.Field(typeof(Pawn_TrainingTracker), "learned").GetValue(trainingTracker);
                    DefMap <TrainableDef, int>  steps   = (DefMap <TrainableDef, int>)AccessTools.Field(typeof(Pawn_TrainingTracker), "steps").GetValue(trainingTracker);

                    //Copy
                    foreach (var item in learned)
                    {
                        brainScan.trainingLearned[item.Key] = item.Value;
                    }
                    foreach (var item in steps)
                    {
                        brainScan.trainingSteps[item.Key] = item.Value;
                    }
                }

                //Hediffs
                if (pawn?.health?.hediffSet?.hediffs != null)
                {
                    List <Hediff> pawnHediffs = pawn.health.hediffSet.hediffs;
                    if (pawnHediffs.Count > 0)
                    {
                        foreach (Hediff h in pawnHediffs)
                        {
                            if (GeneralCompatibility.includedBrainTemplateHediffs.Any(hediffDef => h.def.defName == hediffDef.defName))
                            {
                                QEEMod.TryLog("Hediff " + h.def.defName + " will be added to brain template");

                                brainScan.hediffInfos.Add(new HediffInfo(h));
                            }
                        }
                    }
                }
            }

            return(brainScanThing);
        }
Beispiel #16
0
        public static Pawn MakePawnFromGenomeSequence(GenomeSequence genomeSequence, Thing creator)
        {
            //int adultAge = (int)genome.pawnKindDef.RaceProps.lifeStageAges.Last().minAge;

            QEEMod.TryLog("Generating pawn...");
            PawnGenerationRequest request = new PawnGenerationRequest(
                genomeSequence.pawnKindDef,
                faction: creator.Faction,
                forceGenerateNewPawn: true,
                canGeneratePawnRelations: false,
                fixedGender: genomeSequence.gender,
                fixedBiologicalAge: 0,
                fixedChronologicalAge: 0,
                allowFood: false);
            Pawn pawn = PawnGenerator.GeneratePawn(request);

            //No pregenerated equipment.
            pawn?.equipment?.DestroyAllEquipment();
            pawn?.apparel?.DestroyAll();
            pawn?.inventory?.DestroyAll();

            //No pregenerated hediffs.
            pawn.health.hediffSet.Clear();

            //Add Hediff marking them as a clone.
            QEEMod.TryLog("Adding hediffs to generated pawn");
            pawn.health.AddHediff(QEHediffDefOf.QE_CloneStatus);

            if (genomeSequence.hediffInfos != null && genomeSequence.hediffInfos.Count > 0)
            {
                //add hediffs to pawn from defs in HediffInfo class
                foreach (HediffInfo h in genomeSequence.hediffInfos)
                {
                    pawn.health.AddHediff(h.def, h.part);
                }
            }

            //Set everything else.
            if (pawn.story is Pawn_StoryTracker storyTracker)
            {
                QEEMod.TryLog("Setting Pawn_StoryTracker attributes for generated pawn...");
                storyTracker.bodyType = genomeSequence.bodyType;
                //sanity check to remove possibility of an Undefined crownType
                if (genomeSequence.crownType == CrownType.Undefined)
                {
                    storyTracker.crownType = CrownType.Average;
                }
                else
                {
                    storyTracker.crownType = genomeSequence.crownType;
                }

                storyTracker.hairColor = genomeSequence.hairColor;
                storyTracker.hairDef   = genomeSequence.hair ?? PawnHairChooser.RandomHairDefFor(pawn, pawn.Faction.def);
                storyTracker.melanin   = genomeSequence.skinMelanin;

                //headGraphicPath is private, so we need Harmony to set its value
                if (genomeSequence.headGraphicPath != null)
                {
                    QEEMod.TryLog("Setting headGraphicPath for generated pawn");
                    AccessTools.Field(typeof(Pawn_StoryTracker), "headGraphicPath").SetValue(storyTracker, genomeSequence.headGraphicPath);
                }
                else
                {
                    //could use this code to make a random head, instead of the static graphic paths.
                    //AccessTools.Field(typeof(Pawn_StoryTracker), "headGraphicPath").SetValue(storyTracker,
                    //GraphicDatabaseHeadRecords.GetHeadRandom(genomeSequence.gender, PawnSkinColors.GetSkinColor(genomeSequence.skinMelanin), genomeSequence.crownType).GraphicPath);
                    QEEMod.TryLog("No headGraphicPath in genome template, setting to default head");
                    string path = genomeSequence.gender == Gender.Male ? "Things/Pawn/Humanlike/Heads/Male/Male_Average_Normal" :
                                  "Things/Pawn/Humanlike/Heads/Female/Female_Narrow_Normal";
                    AccessTools.Field(typeof(Pawn_StoryTracker), "headGraphicPath").SetValue(storyTracker, path);
                }

                storyTracker.traits.allTraits.Clear();
                QEEMod.TryLog("Setting traits for generated pawn");
                foreach (ExposedTraitEntry trait in genomeSequence.traits)
                {
                    //storyTracker.traits.GainTrait(new Trait(trait.def, trait.degree));
                    storyTracker.traits.allTraits.Add(new Trait(trait.def, trait.degree));
                    if (pawn.workSettings != null)
                    {
                        pawn.workSettings.Notify_DisabledWorkTypesChanged();
                    }
                    if (pawn.skills != null)
                    {
                        pawn.skills.Notify_SkillDisablesChanged();
                    }
                    if (!pawn.Dead && pawn.RaceProps.Humanlike)
                    {
                        pawn.needs.mood.thoughts.situational.Notify_SituationalThoughtsDirty();
                    }
                }

                QEEMod.TryLog("Setting backstory for generated pawn");
                //Give random vatgrown backstory.
                storyTracker.childhood = DefDatabase <BackstoryDef> .GetNamed("Backstory_ColonyVatgrown").GetFromDatabase();

                storyTracker.adulthood = null;
            }

            if (pawn.skills is Pawn_SkillTracker skillsTracker)
            {
                foreach (SkillRecord skill in skillsTracker.skills)
                {
                    skill.Level   = 0;
                    skill.passion = Passion.None;
                    skill.Notify_SkillDisablesChanged();
                }


                List <SkillRecord> skillPassions = new List <SkillRecord>();
                int skillsPicked = 0;
                int iterations   = 0;
                //Pick 4 random skills to give minor passions to.
                while (skillsPicked < 4 && iterations < 1000)
                {
                    SkillRecord randomSkill = skillsTracker.skills.RandomElement();
                    if (!skillPassions.Contains(randomSkill))
                    {
                        skillPassions.Add(randomSkill);
                        randomSkill.passion = Passion.Minor;
                        skillsPicked++;
                    }

                    iterations++;
                }

                skillsPicked = 0;
                iterations   = 0;
                //Pick 2 random skills to give major passions to.
                while (skillsPicked < 2 && iterations < 1000)
                {
                    SkillRecord randomSkill = skillsTracker.skills.RandomElement();
                    if (!skillPassions.Contains(randomSkill))
                    {
                        skillPassions.Add(randomSkill);
                        randomSkill.passion = Passion.Major;
                        skillsPicked++;
                    }

                    iterations++;
                }
            }

            if (pawn.workSettings is Pawn_WorkSettings workSettings)
            {
                workSettings.EnableAndInitialize();
            }

            //Alien Races compatibility.
            if (CompatibilityTracker.AlienRacesActive)
            {
                AlienRaceCompat.SetFieldsToAlienComp(pawn, genomeSequence);
            }

            PortraitsCache.SetDirty(pawn);
            PortraitsCache.PortraitsCacheUpdate();

            return(pawn);
        }
Beispiel #17
0
        static PostDefFixer()
        {
            //Add recipes to valid Genome Sequencing targets.
            foreach (ThingDef def in DefDatabase <ThingDef> .AllDefs.Where(def => def.category == ThingCategory.Pawn))
            {
                if (def.GetModExtension <RaceExclusionProperties>() is RaceExclusionProperties props)
                {
                    if (props.excludeThisRace)
                    {
                        GeneralCompatibility.excludedRaces.Add(def);
                    }

                    if (props.excludeTheseHediffs.Count > 0)
                    {
                        GeneralCompatibility.excludedHediffs.AddRange(props.excludeTheseHediffs);
                    }
                }

                if (def.IsValidGenomeSequencingTargetDef())
                {
                    if (def.recipes == null)
                    {
                        def.recipes = new List <RecipeDef>();
                    }
                    if (def.recipes.Count > 0)
                    {
                        def.recipes.Insert(0, QERecipeDefOf.QE_GenomeSequencing);
                    }
                    else
                    {
                        def.recipes.Add(QERecipeDefOf.QE_GenomeSequencing);
                    }
                }

                if (def.IsValidBrainScanningDef())
                {
                    if (def.recipes == null)
                    {
                        def.recipes = new List <RecipeDef>();
                    }
                    if (def.recipes.Count > 0)
                    {
                        def.recipes.Insert(0, QERecipeDefOf.QE_BrainScanning);
                    }
                    else
                    {
                        def.recipes.Add(QERecipeDefOf.QE_BrainScanning);
                    }
                }
            }

            //Inject our own backstories.
            foreach (BackstoryDef def in DefDatabase <BackstoryDef> .AllDefs)
            {
                Backstory backstory = new Backstory();
                backstory.slot             = def.slot;
                backstory.title            = def.title;
                backstory.titleShort       = def.titleShort;
                backstory.titleFemale      = def.titleFemale;
                backstory.titleShortFemale = def.titleShortFemale;
                backstory.baseDesc         = def.baseDesc;
                AccessTools.Field(typeof(Backstory), "bodyTypeFemale").SetValue(backstory, def.bodyTypeFemale);
                AccessTools.Field(typeof(Backstory), "bodyTypeMale").SetValue(backstory, def.bodyTypeMale);
                AccessTools.Field(typeof(Backstory), "bodyTypeGlobal").SetValue(backstory, def.bodyTypeGlobal);
                backstory.spawnCategories.AddRange(def.spawnCategories);
                backstory.PostLoad();
                backstory.ResolveReferences();

                BackstoryDatabase.AddBackstory(backstory);

                def.identifier = backstory.identifier;
                //Log.Message("'" + def.defName + "' identifier is '" + backstory.identifier + "'");
            }

            foreach (HediffDef def in DefDatabase <HediffDef> .AllDefs)
            {
                if (def.GetModExtension <HediffTemplateProperties>() is HediffTemplateProperties props)
                {
                    if (props.includeInBrainTemplate)
                    {
                        QEEMod.TryLog(def.defName + " added to list of Hediffs applied to brain templates");
                        GeneralCompatibility.includedBrainTemplateHediffs.Add(def);
                    }

                    if (props.includeInGenomeTemplate)
                    {
                        QEEMod.TryLog(def.defName + " added to list of Hediffs applied to genome templates");
                        GeneralCompatibility.includedGenomeTemplateHediffs.Add(def);
                    }
                }
            }
        }
Beispiel #18
0
        public bool UpdateAvailIngredientsCache()
        {
            anyBillIngredientsAvailable = false;
            ingredientsAvailableNow.Clear();

            if (AnyPendingRequests == false)
            {
                return(false);
            }

            //Thing giver = (observedThingHolder as Thing);
            //QEEMod.TryLog("Updating available ingredients cache for " + giver.ThingID);

            List <Bill> bills = new List <Bill>();

            if (_activeBill != null)
            {
                bills.Add(_activeBill);
            }
            else
            {
                bills = (observedThingHolder as IBillGiver)?.BillStack.Bills;
            }

            Dictionary <string, Thing> ingsFoundOnMap = new Dictionary <string, Thing>();
            int checkIntervalTicks = QEESettings.instance.ingredientCheckIntervalSeconds * 60;

            for (int i = 0; i < bills.Count; i++)
            {
                Bill_Production curBill = bills[i] as Bill_Production;

                if (Find.TickManager.TicksGame < curBill.lastIngredientSearchFailTicks + checkIntervalTicks)
                {
                    QEEMod.TryLog("checked " + curBill.GetUniqueLoadID() + " for avail. ingredients recently, skipping");
                    continue;
                }
                curBill.lastIngredientSearchFailTicks = 0;

                if (curBill.ShouldDoNow())
                {
                    //loop through ingredients
                    foreach (IngredientCount curIng in curBill.recipe.ingredients)
                    {
                        Thing ing;

                        //if same ingredient was found on map in previous loop iteration, don't search map again
                        ingsFoundOnMap.TryGetValue(curIng.FixedIngredient.defName, out ing);
                        if (ing != null)
                        {
                            ingredientsAvailableNow[curBill.GetUniqueLoadID()] = ing;
                            //QEEMod.TryLog("Already found " + ing.Label + " on previous iteration. Skipping");
                            break;
                        }

                        //check if this ingredient is on the map
                        ing = IngredientUtility.FindClosestIngToBillGiver(curBill, curIng);
                        if (ing != null)
                        {
                            QEEMod.TryLog("Found " + ing.Label + " on map, adding to ingredient cache");
                            ingsFoundOnMap[curIng.FixedIngredient.defName]     = ing;
                            ingredientsAvailableNow[curBill.GetUniqueLoadID()] = ing;
                            anyBillIngredientsAvailable = true;
                            break;
                        }
                        else
                        {
                            //QEEMod.TryLog("no ingredients available");
                        }
                    }

                    Thing dummy;
                    if (!ingredientsAvailableNow.TryGetValue(curBill.GetUniqueLoadID(), out dummy))
                    {
                        curBill.lastIngredientSearchFailTicks = Find.TickManager.TicksGame;
                    }
                }
            }

            return(anyBillIngredientsAvailable);
        }
        }//end HasJobOnThing()

        public override Job JobOnThing(Pawn p, Thing t, bool forced = false)
        {
            IBillGiver billGiver = t as IBillGiver;
            Building_GrowerBase_WorkTable grower = t as Building_GrowerBase_WorkTable;
            BillProcessor processor = grower.billProc;

            //if null is returned from this function, the game will throw an error. Instead,
            //return this simple wait job to avoid unnecessary errors.
            Job returnJobOnFailure = new Job(JobDefOf.Wait, 5);

            //check if there are any pending requests (unfulfilled bills)
            if (!processor.AnyPendingRequests)
            {
                QEEMod.TryLog("AnyPendingRequests is false inside JobOnThing()!");
                return(returnJobOnFailure);
            }

            //check if there are ingredients available for those bills
            if (processor.ingredientsAvailableNow.Count <= 0)
            {
                QEEMod.TryLog("ingredientsAvailableNow.Count is 0 inside JobOnThing()!");
                return(returnJobOnFailure);
            }

            //get the reference to the currently active bill
            Bill theBill = processor.ActiveBill;

            if (theBill == null)
            {
                QEEMod.TryLog("Attempt to get ActiveBill failed inside JobOnThing()!");
                return(returnJobOnFailure);
            }

            //get the cached ingredient that's available. This Thing is not always going to be used in the Job,
            //but we know there's at least one stack of this Thing available on the map
            Thing cachedThing;

            grower.billProc.ingredientsAvailableNow.TryGetValue(theBill.GetUniqueLoadID(), out cachedThing);
            if (cachedThing == null)
            {
                QEEMod.TryLog("Attempt to retrieve cached ingredients failed for " + theBill.GetUniqueLoadID());
                return(returnJobOnFailure);
            }

            //get the nearest Thing to the Pawn with the same ThingDef
            int   countForVat = 0;
            Thing thingToFill = IngredientUtility.ThingPawnShouldRetrieveForBill(theBill, p, ref countForVat);

            if (thingToFill == null)
            {
                grower.billProc.anyBillIngredientsAvailable = false;
                QEEMod.TryLog("ThingPawnShouldRetrieveForBill() is null for " + theBill.GetUniqueLoadID());

                return(returnJobOnFailure);
            }

            //all checks have passed! Return the Job and notify the grower that it's time to start Filling
            grower.Notify_FillingStarted(theBill);

            Job returnJob = new Job(QEJobDefOf.QE_LoadGrowerJob, t, thingToFill);

            returnJob.count = countForVat;
            returnJob.bill  = theBill;
            return(returnJob);
        }//end JobOnThing()
Beispiel #20
0
        /// <summary>
        /// Checks if Bill specifies to drop product on ground, and if so, creates the product and drops it.
        /// The current grower's active Bill is retrieved programmatically via grower members.
        /// Heavily modified version of the Toils_Recipe.FinishRecipeAndStartStoringProduct() toil in vanilla.
        /// </summary>
        /// <returns></returns>
        public static Toil TryDropProductOnFloor(Building_GrowerBase_WorkTable vat)
        {
            Toil toil = new Toil();

            toil.initAction = delegate
            {
                Pawn            actor      = toil.actor;
                Bill_Production activeBill = vat.billProc.ActiveBill;

                if (vat?.activeRecipe?.products[0]?.thingDef == null)
                {
                    if (vat?.activeRecipe?.label != null)
                    {
                        QEEMod.TryLog("No product found in recipe " + vat.activeRecipe.label + ".Ending extract Job.");
                    }
                    else
                    {
                        QEEMod.TryLog("No product found in recipe. Ending extract Job.");
                    }

                    //decrement billstack count
                    if (activeBill.repeatMode == BillRepeatModeDefOf.RepeatCount)
                    {
                        if (activeBill.repeatCount > 0)
                        {
                            activeBill.repeatCount--;
                        }
                    }

                    actor.jobs.EndCurrentJob(JobCondition.Succeeded);
                }
                else if (activeBill == null || activeBill.GetStoreMode() == BillStoreModeDefOf.DropOnFloor)
                {
                    //true if no output stockpile specified in bill options

                    Thing product = ThingMaker.MakeThing(vat.activeRecipe.products[0].thingDef);
                    product.stackCount = vat.activeRecipe.products[0].count;

                    if (activeBill?.GetUniqueLoadID() != null)
                    {
                        QEEMod.TryLog("activeBill: " + activeBill?.GetUniqueLoadID() + " specifies dropping " + product.Label + " on floor.");
                    }
                    else
                    {
                        QEEMod.TryLog("activeBill is null, dropping " + product.Label + " on floor.");
                    }

                    //put the product on the ground on the same cell as the pawn
                    if (GenPlace.TryPlaceThing(product, actor.Position, actor.Map, ThingPlaceMode.Near))
                    {
                        vat.Notify_ProductExtracted(actor);
                    }
                    else
                    {
                        QEEMod.TryLog(actor + " could not drop recipe product " + product + " near " + actor.Position + ". Ending extract job.");
                    }

                    //decrement billstack count
                    if (activeBill.repeatMode == BillRepeatModeDefOf.RepeatCount)
                    {
                        if (activeBill.repeatCount > 0)
                        {
                            activeBill.repeatCount--;
                        }
                    }

                    actor.jobs.EndCurrentJob(JobCondition.Succeeded);
                }
            };
            return(toil);
        } // end TryDropProductOnFloor
        protected override IEnumerable <Toil> MakeNewToils()
        {
            //A - Sleeper   B - Brain template   C - Bed

            this.FailOnDestroyedNullOrForbidden(TargetIndex.A);
            this.FailOnDestroyedNullOrForbidden(TargetIndex.B);
            this.FailOnDestroyedNullOrForbidden(TargetIndex.C);

            yield return(Toils_Reserve.Reserve(TargetIndex.B));

            if (Patient.CurJobDef != JobDefOf.LayDown || Patient.CurrentBed() != Bed)
            {
                QEEMod.TryLog("Patient not in bed, carrying them to bed");
                //get the patient and carry them to the bed
                yield return(Toils_Reserve.Reserve(TargetIndex.A));

                yield return(Toils_Reserve.Reserve(TargetIndex.C));

                yield return(Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.InteractionCell));

                yield return(Toils_Haul.StartCarryThing(TargetIndex.A));

                yield return(Toils_Goto.GotoThing(TargetIndex.C, PathEndMode.InteractionCell));

                yield return(Toils_Haul.PlaceHauledThingInCell(TargetIndex.C, null, false));

                yield return(Toils_Reserve.Release(TargetIndex.C));
            }

            //anesthetize the patient
            Toil anesthetizePatient = new Toil()
            {
                initAction = delegate()
                {
                    Toils_Reserve.Reserve(TargetIndex.A);

                    Patient.health.AddHediff(HediffDefOf.Anesthetic);

                    if (Patient.CurJobDef != JobDefOf.LayDown)
                    {
                        Patient.pather.StopDead();
                        Patient.jobs.StartJob(new Job(JobDefOf.LayDown, TargetC)
                        {
                            forceSleep = true
                        });
                    }
                },
            };

            yield return(anesthetizePatient);

            //Surgeon gets the brain template, carries it, then travel to bed
            QEEMod.TryLog("Carrying brain template to bed");
            yield return(Toils_Goto.GotoThing(TargetIndex.B, PathEndMode.OnCell));

            yield return(Toils_Haul.StartCarryThing(TargetIndex.B));

            yield return(Toils_Goto.GotoThing(TargetIndex.C, PathEndMode.ClosestTouch));

            Toil applyBrainTemplateToil = new Toil()
            {
                defaultCompleteMode = ToilCompleteMode.Never,
                tickAction          = delegate()
                {
                    ticksWork -= StatDefOf.ResearchSpeed.Worker.IsDisabledFor(pawn) ? 1f : 1f * pawn.GetStatValue(StatDefOf.ResearchSpeed);

                    if (ticksWork <= 0)
                    {
                        BrainManipUtility.ApplyBrainScanTemplateOnPawn(Patient, BrainTemplate);

                        //Give headache
                        Patient.health.AddHediff(QEHediffDefOf.QE_Headache, Patient.health.hediffSet.GetBrain());
                    }
                }
            }.WithProgressBar(TargetIndex.A, () => (workRequired - ticksWork) / workRequired).WithEffect(EffecterDefOf.Research, TargetIndex.A);

            applyBrainTemplateToil.AddPreInitAction(delegate()
            {
                ticksWork   = workRequired;
                workStarted = true;
                //Log.Message("preInitAction: ticksWork = " + ticksWork);
            });
            applyBrainTemplateToil.AddEndCondition(delegate()
            {
                if (workStarted && ticksWork <= 0)
                {
                    //Log.Message("Succeeded: ticksWork = " + ticksWork);
                    return(JobCondition.Succeeded);
                }

                //Log.Message("Ongoing: ticksWork = " + ticksWork);
                return(JobCondition.Ongoing);
            });
            applyBrainTemplateToil.AddFailCondition(() => workStarted && Patient.CurJobDef != JobDefOf.LayDown);

            yield return(applyBrainTemplateToil);
        }
        } //end GetBillPawnCanDo()

        /// <summary>
        /// Checks if a pawn can be assigned to a bill based on factors including PawnRestrictions, workSkills, etc.
        /// Does not check if ingredients are nearby.
        /// </summary>
        /// <param name="p"></param>
        /// <param name="theBill"></param>
        /// <param name="reason"></param>
        /// <returns></returns>
        public bool PawnCanDoThisBill(Pawn p, Bill theBill, LocalTargetInfo target, out string reason)
        {
            reason = GenericFailReasonTrans;
            if (Find.TickManager.TicksGame < theBill.lastIngredientSearchFailTicks + (QEESettings.instance.ingredientCheckIntervalSeconds * 60))
            {
                reason = RecentIngSearchTrans;
                return(false);
            }

            if (theBill?.recipe?.requiredGiverWorkType != null && theBill.recipe.requiredGiverWorkType != def.workType)
            {
                reason = WorkTypeMismatchTrans;
                return(false);
            }

            bool shouldDo = theBill.ShouldDoNow();

            if (!shouldDo)
            {
                reason = ShouldDoFalseTrans;
                return(false);
            }

            if (theBill.pawnRestriction != null && theBill.pawnRestriction != p)
            {
                reason = string.Format(PawnRestrictionTrans, theBill.pawnRestriction.LabelShort);

                return(false);
            }

            if (theBill.recipe.workSkill != null)
            {
                int level = p.skills.GetSkill(theBill.recipe.workSkill).Level;
                if (level < theBill.allowedSkillRange.min)
                {
                    reason = string.Format(UnderAllowedSkillTrans, theBill.allowedSkillRange.min);
                    return(false);
                }
                if (level > theBill.allowedSkillRange.max)
                {
                    reason = string.Format(AboveAllowedSkillTrans, theBill.allowedSkillRange.max);
                    return(false);
                }
            }

            if (!p.Map.reservationManager.CanReserve(p, target, 1, -1, null, false))
            {
                Pawn pawn2 = p.Map.reservationManager.FirstRespectedReserver(target, p);

                if (pawn2 != null)
                {
                    reason = string.Format(ReservedByTrans, theBill.billStack.billGiver.LabelShort, pawn2.LabelShort);
                }
                else
                {
                    reason = string.Format(ReservedTrans);
                }

                return(false);
            }

            QEEMod.TryLog(p.LabelShort + " can do bill " + theBill.GetUniqueLoadID());
            return(true);
        } //end PawnCanDoThisBill()
Beispiel #23
0
        protected override IEnumerable <Toil> MakeNewToils()
        {
            this.FailOnBurningImmobile(BillGiverInd);
            this.FailOnDestroyedNullOrForbidden(BillGiverInd);
            this.FailOnForbidden(IngredientInd);
            this.FailOn(delegate
            {
                Thing building       = job.GetTarget(BillGiverInd).Thing;
                IBillGiver billGiver = building as IBillGiver;
                if (building == null || billGiver == null || job.bill == null)
                {
                    QEEMod.TryLog("something is null, failing job");
                    return(true);
                }

                Building_GrowerBase_WorkTable grower = job.GetTarget(BillGiverInd).Thing as Building_GrowerBase_WorkTable;
                if (job.bill.DeletedOrDereferenced)
                {
                    QEEMod.TryLog("Bill deleted, failing job");

                    //refund ingredients if player cancels bill during Filling phase
                    if (grower != null && grower.status == CrafterStatus.Filling)
                    {
                        QEEMod.TryLog("Bill cancelled, refunding ingredients");
                        grower.StopCrafting(true);
                    }

                    return(true);
                }

                if (grower.status != CrafterStatus.Filling)
                {
                    QEEMod.TryLog("Crafter is not 'filling', ending job");
                    return(true);
                }

                return(false);
            });


            Toil logToil = new Toil()
            {
                initAction = delegate()
                {
                    QEEMod.TryLog("Pawn " + GetActor().LabelShort + " | Toil: " + logCounter + " | Job: " + job.GetUniqueLoadID());
                    logCounter++;
                }
            };
            ////yield return logToil;

            //travel to ingredient and carry it
            Toil reserveIng = Toils_Reserve.Reserve(IngredientInd);

            yield return(reserveIng);

            //yield return logToil;

            Toil travelToil = Toils_Goto.GotoThing(IngredientInd, PathEndMode.OnCell);

            yield return(travelToil);

            //yield return logToil;

            Toil carryThing = Toils_Haul.StartCarryThing(IngredientInd, subtractNumTakenFromJobCount: true);

            yield return(carryThing);

            //yield return logToil;

            //Opportunistically haul a nearby ingredient of same ThingDef. Checks 8 square radius.
            yield return(Toils_Haul.CheckForGetOpportunityDuplicate(reserveIng, IngredientInd, TargetIndex.None, takeFromValidStorage: true));

            //yield return logToil;


            //head back to grower
            yield return(Toils_Goto.GotoThing(BillGiverInd, PathEndMode.InteractionCell).FailOnDestroyedOrNull(IngredientInd));

            //yield return logToil;

            //deposit into grower
            Toil depositIntoGrower = new Toil()
            {
                initAction = delegate()
                {
                    Building_GrowerBase_WorkTable grower = TargetThingA as Building_GrowerBase_WorkTable;
                    if (grower != null)
                    {
                        grower.FillThing(GetActor().carryTracker.CarriedThing);
                    }
                }
            };

            yield return(depositIntoGrower);

            Toil tryStartCrafting = new Toil()
            {
                initAction = delegate()
                {
                    Building_GrowerBase_WorkTable grower = job.GetTarget(BillGiverInd).Thing as Building_GrowerBase_WorkTable;
                    Pawn actor = GetActor();

                    //if all ingredients have been loaded, start crafting
                    if (grower?.billProc != null)
                    {
                        if (grower.billProc.AnyPendingRequests == false)
                        {
                            grower.Notify_CraftingStarted();
                        }
                    }

                    actor.jobs.EndCurrentJob(JobCondition.Succeeded);
                }
            };

            yield return(tryStartCrafting);
        } //end function MakeNewToils()
        public override void ExposeData()
        {
            base.ExposeData();

            //Basic.
            Scribe_Values.Look(ref sourceName, "sourceName");
            Scribe_Defs.Look(ref pawnKindDef, "pawnKindDef");
            Scribe_Values.Look(ref gender, "gender");

            //Load first humanoid value
            Scribe_Values.Look(ref crownType, "crownType");

            Scribe_Collections.Look(ref hediffInfos, "hediffInfos", LookMode.Deep);

            //Save/Load rest of the humanoid values. CrownType will be Undefined for animals.
            if (crownType != CrownType.Undefined)
            {
                Scribe_Defs.Look(ref bodyType, "bodyType");
                Scribe_Values.Look(ref hairColor, "hairColor");
                Scribe_Values.Look(ref skinMelanin, "skinMelanin");
                Scribe_Collections.Look(ref traits, "traits", LookMode.Deep);
                Scribe_Defs.Look(ref hair, "hair");
                Scribe_Values.Look(ref headGraphicPath, "headGraphicPath");

                //Humanoid values that could be null in save file go here
                if (Scribe.mode == LoadSaveMode.PostLoadInit)
                {
                    if (hair == null)
                    {
                        //hair = DefDatabase<HairDef>.AllDefs.RandomElement();
                        hair = DefDatabase <HairDef> .GetNamed("Shaved");
                    }
                    if (headGraphicPath == null)
                    {
                        //*slaps roof of car* this bad code can crash your game!
                        //headGraphicPath = GraphicDatabaseHeadRecords.GetHeadRandom(gender, PawnSkinColors.GetSkinColor(skinMelanin), crownType).GraphicPath;

                        headGraphicPath = gender == Gender.Male ? "Things/Pawn/Humanlike/Heads/Male/Male_Average_Normal" :
                                          "Things/Pawn/Humanlike/Heads/Female/Female_Narrow_Normal";
                    }
                }
            }

            if (Scribe.mode == LoadSaveMode.PostLoadInit && hediffInfos != null)
            {
                //remove any hediffs where the def is missing. Most commonly occurs when a mod is removed from a save.
                int removed = hediffInfos.RemoveAll(h => h.def == null);
                if (removed > 0)
                {
                    QEEMod.TryLog("Removed " + removed + " null hediffs from hediffInfo list for " + sourceName + "'s genome template ");
                }
            }

            //Alien Compat.
            Scribe_Values.Look(ref isAlien, "isAlien");
            Scribe_Values.Look(ref skinColor, "skinColor");
            Scribe_Values.Look(ref skinColorSecond, "skinColorSecond");
            Scribe_Values.Look(ref hairColorSecond, "hairColorSecond");
            Scribe_Values.Look(ref crownTypeAlien, "crownTypeAlien");
            Scribe_Collections.Look(ref addonVariants, "addonVariants");
        }
Beispiel #25
0
        } // end TryDropProductOnFloor

        /// <summary>
        /// Generates the recipe product, then looks for a valid cell in the Bill's output stockpile.
        /// If a valid cell is found, puts the product into the pawn's arms for transport in a future Toil.
        /// Uses code from the Toils_Recipe.FinishRecipeAndStartStoringProduct() toil in vanilla.
        /// </summary>
        /// <param name="vat"></param>
        /// <returns></returns>
        public static Toil StartCarryProductToStockpile(Building_GrowerBase_WorkTable vat)
        {
            Toil toil = new Toil();

            toil.initAction = delegate
            {
                Pawn actor  = toil.actor;
                Job  curJob = actor.jobs.curJob;

                //Skip null checking, as it was done in previous toil

                Thing product = ThingMaker.MakeThing(vat.activeRecipe.products[0].thingDef);
                product.stackCount = vat.activeRecipe.products[0].count;
                Bill_Production activeBill = vat.billProc.ActiveBill;

                //decrement billstack count
                if (activeBill.repeatMode == BillRepeatModeDefOf.RepeatCount)
                {
                    if (activeBill.repeatCount > 0)
                    {
                        activeBill.repeatCount--;
                    }
                }

                IntVec3 foundCell = IntVec3.Invalid;

                //find the best cell to put the product in
                if (activeBill.GetStoreMode() == BillStoreModeDefOf.BestStockpile)
                {
                    StoreUtility.TryFindBestBetterStoreCellFor(product, actor, actor.Map, StoragePriority.Unstored, actor.Faction, out foundCell);
                }
                else if (activeBill.GetStoreMode() == BillStoreModeDefOf.SpecificStockpile)
                {
                    StoreUtility.TryFindBestBetterStoreCellForIn(product, actor, actor.Map, StoragePriority.Unstored, actor.Faction,
                                                                 activeBill.GetStoreZone().slotGroup, out foundCell);
                }
                else
                {
                    Log.ErrorOnce("Unknown bill store mode", 9158246);
                }

                //if a cell was found in a stockpile, start a hauling job to move the product from the ground to that cell
                if (foundCell.IsValid)
                {
                    bool tryCarrySuccess = actor.carryTracker.TryStartCarry(product);
                    QEEMod.TryLog("Valid stockpile found - haul product to cell " + foundCell + ". TryStartCarry() result for "
                                  + actor.LabelShort + ": " + tryCarrySuccess);

                    curJob.targetB = foundCell;
                    curJob.targetA = product;
                    curJob.count   = 99999;

                    vat.Notify_ProductExtracted(actor);

                    //the next toil in the JobDriver will now haul the carried object to the stockpile
                }
                else
                {
                    QEEMod.TryLog("No stockpile found to haul " + product.Label + " to. Dropping product on ground.");
                    if (GenPlace.TryPlaceThing(product, actor.Position, actor.Map, ThingPlaceMode.Near))
                    {
                        vat.Notify_ProductExtracted(actor);
                    }
                    else
                    {
                        QEEMod.TryLog(actor + " could not drop recipe product " + product + " near " + actor.Position + ". Ending extract job.");
                    }

                    actor.jobs.EndCurrentJob(JobCondition.Succeeded);
                }
            };
            return(toil);
        } // end StartCarryProductToStockpile
        public static void ApplyBrainScanTemplateOnPawn(Pawn thePawn, BrainScanTemplate brainScan, float efficency = 1f)
        {
            if (thePawn.IsValidBrainScanningTarget())
            {
                //Backgrounds
                Pawn_StoryTracker storyTracker = thePawn.story;
                if (storyTracker != null)
                {
                    //story.childhood = brainScan.backStoryChild;
                    storyTracker.adulthood = brainScan.backStoryAdult;
                }

                //Skills
                Pawn_SkillTracker skillTracker = thePawn.skills;
                if (skillTracker != null)
                {
                    foreach (ComparableSkillRecord skill in brainScan.skills)
                    {
                        SkillRecord pawnSkill = skillTracker.GetSkill(skill.def);
                        pawnSkill.Level   = (int)Math.Floor((float)skill.level * efficency);
                        pawnSkill.passion = skill.passion;
                        pawnSkill.Notify_SkillDisablesChanged();
                    }
                }

                //Training
                Pawn_TrainingTracker trainingTracker = thePawn.training;
                if (trainingTracker != null)
                {
                    DefMap <TrainableDef, bool> learned = (DefMap <TrainableDef, bool>)AccessTools.Field(typeof(Pawn_TrainingTracker), "learned").GetValue(trainingTracker);
                    DefMap <TrainableDef, int>  steps   = (DefMap <TrainableDef, int>)AccessTools.Field(typeof(Pawn_TrainingTracker), "steps").GetValue(trainingTracker);

                    //Copy
                    foreach (var item in brainScan.trainingLearned)
                    {
                        learned[item.Key] = item.Value;
                    }
                    foreach (var item in brainScan.trainingSteps)
                    {
                        steps[item.Key] = (int)Math.Floor((float)item.Value * efficency);
                    }
                }

                //Add Hediffs
                thePawn.health.AddHediff(QEHediffDefOf.QE_BrainTemplated);
                if (brainScan.hediffInfos != null && brainScan.hediffInfos?.Count > 0)
                {
                    //add hediffs to pawn from defs in HediffInfo class
                    foreach (HediffInfo h in brainScan.hediffInfos)
                    {
                        Hediff addedHediff = thePawn.health.AddHediff(h.def, h.part);

                        //Psychic Awakened compatibility
                        if (h.psychicAwakeningPowersKnownDefNames != null && h.psychicAwakeningPowersKnownDefNames?.Count > 0)
                        {
                            //create a List of the type PsychicPowerDef via Reflection. Cast it as IList to interact with it.
                            var   listType        = typeof(List <>).MakeGenericType(PsychicAwakeningCompat.PsychicPowerDefType);
                            var   powers          = Activator.CreateInstance(listType);
                            IList powersInterface = (IList)powers;

                            //iterate through the defNames saved in the list inside HediffInfo
                            foreach (string defName in h.psychicAwakeningPowersKnownDefNames)
                            {
                                //look for this PsychicPowerDef in the DefDatabase
                                var psychicPowerDef = GenDefDatabase.GetDef(PsychicAwakeningCompat.PsychicPowerDefType, defName, false);

                                if (psychicPowerDef != null)
                                {
                                    //add this to the list
                                    powersInterface.Add(psychicPowerDef);
                                }
                                else
                                {
                                    QEEMod.TryLog("Psychic Power def " + defName + " not loaded in database of Rimworld Defs. This power will not be applied.");
                                }
                            }

                            if (powersInterface.Count > 0)
                            {
                                QEEMod.TryLog("assigning " + powersInterface.Count + " psychic powers to " + thePawn.LabelCap + " from brain template");

                                PsychicAwakeningCompat.powersKnownField.SetValue(addedHediff, powers);
                            }
                        }
                    }
                }

                Messages.Message("QE_BrainTemplatingComplete".Translate(thePawn.Named("PAWN")), MessageTypeDefOf.PositiveEvent, false);
            }
        }