예제 #1
0
        private int DatabaseCount(out float databasePower)
        {
            int result = unlocked.TechsArchived.Count();

            databasePower = TechTracker.BookResearchIncrement(result);
            return(result);
        }
예제 #2
0
        //Bill
        public static bool Allows(this Bill bill, IEnumerable <ResearchProjectDef> homework)
        {
            var  textBooks = homework.Select(x => TechTracker.FindTech(x).Stuff);
            bool result    = bill.ingredientFilter.AllowedThingDefs.Intersect(textBooks).Any();

            return(result);
        }
예제 #3
0
 public override bool TryMakePreToilReservations(bool errorOnFailed)
 {
     project = techComp.homework?.Where(x => job.bill.Allows(x)).Intersect(techComp.knownTechs).Reverse().FirstOrDefault();
     if (project == null)
     {
         return(false);
     }
     techStuff = TechTracker.FindTech(project).Stuff;
     return(base.TryMakePreToilReservations(errorOnFailed));
 }
예제 #4
0
        private int LibraryCount(List <Thing> library, out float libraryPower)
        {
            int result = 0;

            foreach (Thing t in library)
            {
                if (t is Building_BookStore shelf)
                {
                    result += shelf.innerContainer.Count;
                }
            }
            libraryPower = TechTracker.BookResearchIncrement(result);
            return(result);
        }
예제 #5
0
        public static IEnumerable <StatDrawEntry> Postfix(IEnumerable <StatDrawEntry> values, Thing thing)
        {
            foreach (StatDrawEntry entry in values)
            {
                yield return(entry);
            }
            if (thing.def.IsWeapon)
            {
                string tech = TechTracker.FindTech(thing.def)?.Tech.LabelCap ?? "None".Translate();
                yield return(new StatDrawEntry(StatCategoryDefOf.Weapon, "WeaponAssociatedTech".Translate(), tech, "WeaponAssociatedTechDesc".Translate(), 10000, null, null, false));

                bool known = unlocked.weapons.Contains(thing.def);
                yield return(new StatDrawEntry(StatCategoryDefOf.Weapon, "WeaponKnown".Translate(), known.ToStringYesNo(), "WeaponKnownDesc".Translate(), 9999, null, null, false));

                bool free = SimpleWeapons.Contains(thing.def) || UniversalWeapons.Contains(thing.def);
                yield return(new StatDrawEntry(StatCategoryDefOf.Weapon, "WeaponRequiresTraining".Translate(), (!free).ToStringYesNo(), "WeaponRequiresTrainingDesc".Translate(), 9998, null, null, false));
            }
        }
        protected void LearnWeaponGroup(ThingDef weapon, Pawn pawn, CompKnowledge techComp)
        {
            bool groupRanged = ModBaseHumanResources.LearnRangedWeaponsByGroup && weapon.IsRangedWeapon;
            bool groupMelee  = ModBaseHumanResources.LearnMeleeWeaponsByGroup && weapon.IsMeleeWeapon;

            if (TechTracker.FindTechs(weapon).Any() && (groupRanged || groupMelee))
            {
                foreach (ThingDef sister in TechTracker.FindTech(weapon).Weapons)
                {
                    if ((groupRanged && sister.IsRangedWeapon) || (groupMelee && sister.IsMeleeWeapon))
                    {
                        techComp.LearnWeapon(sister);
                        Messages.Message("MessageTrainingComplete".Translate(pawn, sister), MessageTypeDefOf.TaskCompletion);
                    }
                }
            }
            else
            {
                techComp.LearnWeapon(weapon);
                Messages.Message("MessageTrainingComplete".Translate(pawn, weapon), MessageTypeDefOf.TaskCompletion);
            }
        }
예제 #7
0
        private Vector2 DrawGroupedNodes(IEnumerable <ExpertiseNode> expertiseList, float max, ref Vector2 pos)
        {
            Vector2 maxView   = pos;
            var     techsList = expertiseList.Select(x => x.Tech);
            var     skillSet  = TechTracker.FindSkills(x => x.Techs.Any(y => techsList.Contains(y))).OrderByDescending(x => PawnToShowInfoAbout.skills.GetSkill(x).Level);

            foreach (var skill in skillSet)
            {
                var filtered = expertiseList.Where(x => skill.Techs.Contains(x.Tech)).ToList();
                if (filtered.NullOrEmpty())
                {
                    continue;
                }
                var  skillRatio = (float)PawnToShowInfoAbout.skills.GetSkill(skill).Level / SkillRecord.MaxLevel;
                Rect groupTitle = DrawGroupTitle(pos, skill, skillRatio, filtered);
                pos.y += groupTitle.height + margin;
                DrawResearchNodes(filtered, max, ref pos, true);
                if (!fullTechs)
                {
                    pos.y += margin;
                }
                if (maxView.y < pos.y)
                {
                    maxView.y = pos.y;
                }
                if (fullTechs)
                {
                    breakColumn(ref pos);
                }
                if (pos.x > maxView.x)
                {
                    maxView.x = fullTechs ? pos.x : pos.x + columnWidth;
                }
            }
            return(maxView);
        }
        public override void DefsLoaded()
        {
            //1. Preparing settings
            UpdateSettings();

            //2. Adding Tech Tab to Pawns
            //ThingDef injection stolen from the work of notfood for Psychology
            var zombieThinkTree = DefDatabase <ThinkTreeDef> .GetNamedSilentFail("Zombie");

            IEnumerable <ThingDef> things = (from def in DefDatabase <ThingDef> .AllDefs
                                             where def.race?.intelligence == Intelligence.Humanlike && (zombieThinkTree == null || def.race.thinkTreeMain != zombieThinkTree)
                                             select def);
            List <string> registered = new List <string>();

            foreach (ThingDef t in things)
            {
                if (t.inspectorTabsResolved == null)
                {
                    t.inspectorTabsResolved = new List <InspectTabBase>(1);
                }
                t.inspectorTabsResolved.Add(InspectTabManager.GetSharedInstance(typeof(ITab_PawnKnowledge)));
                if (t.comps == null)
                {
                    t.comps = new List <CompProperties>(1);
                }
                t.comps.Add(new CompProperties_Knowledge());
                registered.Add(t.defName);
            }
            InspectPaneUtility.Reset();

            //3. Preparing knowledge support infrastructure

            //a. Things everyone knows
            UniversalWeapons.AddRange(DefDatabase <ThingDef> .AllDefs.Where(x => x.IsWeapon));
            UniversalCrops.AddRange(DefDatabase <ThingDef> .AllDefs.Where(x => x.plant != null && x.plant.Sowable));

            //b. Minus things unlocked on research
            ThingFilter lateFilter = new ThingFilter();

            foreach (ResearchProjectDef tech in DefDatabase <ResearchProjectDef> .AllDefs)
            {
                tech.InferSkillBias();
                tech.CreateStuff(lateFilter, unlocked);
                foreach (ThingDef weapon in tech.UnlockedWeapons())
                {
                    UniversalWeapons.Remove(weapon);
                }
                foreach (ThingDef plant in tech.UnlockedPlants())
                {
                    UniversalCrops.Remove(plant);
                }
            }
            ;

            //c. Also removing atipical weapons
            List <string> ForbiddenWeaponTags = TechDefOf.WeaponsNotBasic.weaponTags;

            UniversalWeapons.RemoveAll(x => SplitSimpleWeapons(x, ForbiddenWeaponTags));
            List <ThingDef> garbage = new List <ThingDef>();

            garbage.Add(TechDefOf.WeaponsNotBasic);

            //d. Classifying pawn backstories
            PawnBackgroundUtility.BuildCache();

            //e. Telling humans what's going on
            ThingCategoryDef       knowledgeCat = TechDefOf.Knowledge;
            IEnumerable <ThingDef> codifiedTech = DefDatabase <ThingDef> .AllDefs.Where(x => x.IsWithinCategory(knowledgeCat));

            if (Prefs.LogVerbose || FullStartupReport)
            {
                Log.Message($"[HumanResources] Codified technologies: {codifiedTech.Select(x => x.label).ToStringSafeEnumerable()}");
                Log.Message($"[HumanResources] Basic crops: {UniversalCrops.ToStringSafeEnumerable()}");
                Log.Message($"[HumanResources] Basic weapons: {UniversalWeapons.ToStringSafeEnumerable()}");
                Log.Message($"[HumanResources] Basic weapons that require training: {SimpleWeapons.ToStringSafeEnumerable()}");
                Log.Warning($"[HumanResources] Basic weapons tags: {SimpleWeapons.Where(x => !x.weaponTags.NullOrEmpty()).SelectMany(x => x.weaponTags).Distinct().ToStringSafeEnumerable()}");
                if (FullStartupReport)
                {
                    Log.Warning("[HumanResources] Backstories classified by TechLevel:");
                    for (int i = 0; i < 8; i++)
                    {
                        TechLevel            level = (TechLevel)i;
                        IEnumerable <string> found = PawnBackgroundUtility.TechLevelByBackstory.Where(e => e.Value == level).Select(e => e.Key);
                        if (!found.EnumerableNullOrEmpty())
                        {
                            Log.Message($"- {level.ToString().CapitalizeFirst()} ({found.EnumerableCount()}): {found.ToStringSafeEnumerable()}");
                        }
                    }
                    Log.Warning("[HumanResources] Techs classified by associated skill:");
                    var skills = DefDatabase <SkillDef> .AllDefsListForReading.GetEnumerator();

                    while (skills.MoveNext())
                    {
                        SkillDef             skill = skills.Current;
                        IEnumerable <string> found = TechTracker.FindTechs(skill).Select(x => x.Tech.label);
                        Log.Message($"- {skill.LabelCap} ({found.EnumerableCount()}): {found.ToStringSafeEnumerable()}");
                    }
                }
            }
            else
            {
                Log.Message($"[HumanResources] This is what we know: {codifiedTech.EnumerableCount()} technologies processed, {UniversalCrops.Count()} basic crops, {UniversalWeapons.Count()} basic weapons + {SimpleWeapons.Count()} that require training.");
            }

            //4. Filling gaps on the database

            //a. TechBook dirty trick, but only now this is possible!
            TechDefOf.TechBook.stuffCategories  = TechDefOf.UnfinishedTechBook.stuffCategories = TechDefOf.LowTechCategories.stuffCategories;
            TechDefOf.TechDrive.stuffCategories = TechDefOf.HiTechCategories.stuffCategories;
            garbage.Add(TechDefOf.LowTechCategories);
            garbage.Add(TechDefOf.HiTechCategories);

            //b. Filling main tech category with subcategories
            foreach (ThingDef t in lateFilter.AllowedThingDefs.Where(t => !t.thingCategories.NullOrEmpty()))
            {
                foreach (ThingCategoryDef c in t.thingCategories)
                {
                    c.childThingDefs.Add(t);
                    if (!knowledgeCat.childCategories.NullOrEmpty() && !knowledgeCat.childCategories.Contains(c))
                    {
                        knowledgeCat.childCategories.Add(c);
                    }
                }
            }

            //c. Populating knowledge recipes and book shelves
            foreach (RecipeDef r in DefDatabase <RecipeDef> .AllDefs.Where(x => x.ingredients.Count == 1 && x.fixedIngredientFilter.AnyAllowedDef == null))
            {
                r.fixedIngredientFilter.ResolveReferences();
                r.defaultIngredientFilter.ResolveReferences();
            }
            foreach (ThingDef t in DefDatabase <ThingDef> .AllDefs.Where(x => x.thingClass == typeof(Building_BookStore)))
            {
                t.building.fixedStorageSettings.filter.ResolveReferences();
                t.building.defaultStorageSettings.filter.ResolveReferences();
            }

            //d. Removing temporary defs from database.
            foreach (ThingDef def in garbage)
            {
                AccessTools.Method(typeof(DefDatabase <ThingDef>), "Remove").Invoke(this, new object[] { def });
            }
        }
예제 #9
0
 //Thing
 public static ResearchProjectDef TryGetTech(this Thing book)
 {
     return((book.Stuff != null && book.Stuff.IsWithinCategory(TechDefOf.Knowledge)) ? TechTracker.FindTech(book.Stuff) : null);
 }
예제 #10
0
 public static bool Allows(this Bill bill, ResearchProjectDef tech)
 {
     return(bill.ingredientFilter.Allows(TechTracker.FindTech(tech).Stuff));
 }
예제 #11
0
 private static bool SkillIsRelevant(SkillDef skill, TechLevel level)
 {
     return(TechTracker.FindSkills(x => x.Skill == skill && x.Techs.Any(y => y.techLevel == level)).Any());
 }
예제 #12
0
        public IEnumerable <ResearchProjectDef> GetExpertiseDefsFor(Pawn pawn, FactionDef faction)
        {
            //1. Gather info on that pawn

            //a. tech level
            TechLevel factionTechLevel = faction?.techLevel ?? 0;
            TechLevel childhoodLevel   = 0;
            SkillDef  childhoodSkill   = null;
            bool      isPlayer         = pawn.Faction?.IsPlayer ?? false;

            techLevel = TechPoolIncludesBackground || !isPlayer?FindBGTechLevel(pawn, out childhoodLevel, out childhoodSkill) : factionTechLevel;

            TechLevel workingTechLevel = startingTechLevel = techLevel;

            //b. higest skills
            SkillRecord highestSkillRecord             = pawn.skills.skills.Aggregate(AccessHighestSkill);
            SkillDef    highestSkill                   = highestSkillRecord.def;
            IEnumerable <SkillRecord> secondCandidates = pawn.skills.skills.Except(highestSkillRecord).Where(x => SkillIsRelevant(x.def, techLevel));
            SkillDef secondSkill = secondCandidates.Aggregate(AccessHighestSkill).def;

            //c. age
            float middleAge    = pawn.RaceProps.lifeExpectancy / 2;
            int   matureAge    = pawn.RaceProps.lifeStageAges.FindLastIndex(x => x.minAge < middleAge); //not always the last possible age because there are mods with an "eldery" stage
            int   growthAdjust = 0;
            int   oldBonus     = 0;

            if (pawn.ageTracker.CurLifeStageIndex < matureAge)
            {
                growthAdjust = matureAge - pawn.ageTracker.CurLifeStageIndex;
            }
            else if (pawn.ageTracker.AgeBiologicalYears > middleAge)
            {
                oldBonus = 1;
            }

            //d. special cases
            isFighter = highestSkill == SkillDefOf.Melee;
            isShooter = highestSkill == SkillDefOf.Shooting;
            int  fighterHandicap = (isFighter | isShooter) ? 1 : 0;
            bool guru            = techLevel < TechLevel.Archotech && highestSkill == SkillDefOf.Intellectual && highestSkillRecord.Level >= Rand.Range(7, 10);

            //2. Calculate how many techs he should know
            int minSlots = techLevel > TechLevel.Medieval ? 1 : oldBonus;
            int slots    = Mathf.Max(minSlots, FactionExpertiseRange(techLevel) - growthAdjust + oldBonus - fighterHandicap);

            if (slots == 0)
            {
                if (Prefs.LogVerbose)
                {
                    Log.Warning($"... No slots for {pawn.gender.GetObjective()}, returning null. (StartingTechLevel is {techLevel}, CurLifeStageIndex is {pawn.ageTracker.CurLifeStageIndex}, fighterHandicap is {fighterHandicap})");
                }
                return(null);
            }

            //3. Info for debugging.

            if (Prefs.LogVerbose)
            {
                StringBuilder stringBuilder = new StringBuilder();
                string        factionName   = faction.label.ToLower() ?? pawn.Possessive().ToLower() + faction;
                if (TechPoolIncludesStarting)
                {
                    stringBuilder.Append($"default for {factionName}");
                }
                if (TechPoolIncludesTechLevel)
                {
                    stringBuilder.AppendWithComma($"{factionTechLevel.ToString().ToLower()} age");
                }
                if (TechPoolIncludesScenario)
                {
                    stringBuilder.AppendWithComma($"{Find.Scenario.name.ToLower()} scenario");
                }
                if (TechPoolIncludesBackground)
                {
                    stringBuilder.AppendWithComma($"{childhoodLevel.ToString().ToLower()} childhood & {techLevel.ToString().ToLower()} background");
                }
                Log.Message($"... Including technologies from: " + stringBuilder.ToString() + ".");
                stringBuilder.Clear();
                string guruText = guru ? " (allowing advanced knowledge)" : "";
                stringBuilder.Append($"... As {pawn.ageTracker.CurLifeStage.label}, {pawn.ProSubj()} gets {slots} slots. {pawn.Possessive().CapitalizeFirst()} highest relevant skills are {highestSkill.label}{guruText} & {secondSkill.label}.");
                Log.Message(stringBuilder.ToString());
            }

            //4. Finally, Distribute knowledge
            bool strict       = false;
            bool useChildhood = childhoodSkill != null && TechPoolIncludesBackground && SkillIsRelevant(childhoodSkill, childhoodLevel) && slots > 1;
            var  filtered     = TechTracker.FindTechs(x => TechPool(isPlayer, x, workingTechLevel, strict));
            int  pass         = 0;
            List <ResearchProjectDef> result = new List <ResearchProjectDef>();

            if (guru)
            {
                workingTechLevel++;
            }
            while (result.Count() < slots)
            {
                pass++;
                filtered.ExecuteEnumerable();
                if (filtered.EnumerableNullOrEmpty())
                {
                    Log.Warning("[HumanResources] Empty technology pool!");
                }
                var remaining = filtered.Where(x => !result.Contains(x));
                if (remaining.EnumerableNullOrEmpty())
                {
                    break;
                }
                SkillDef skill = null;
                if (pass == 1 && remaining.Any(x => x.Skills.Contains(highestSkill)))
                {
                    skill = highestSkill;
                }
                else if (pass == 2 && remaining.Any(x => x.Skills.Contains(secondSkill)))
                {
                    skill = useChildhood ? childhoodSkill : secondSkill;
                }
                ResearchProjectDef selected = remaining.RandomElementByWeightWithDefault(x => TechLikelihoodForSkill(pawn, x.Skills, slots, pass, skill), 1f) ?? remaining.RandomElement();
                result.Add(selected);

                //prepare next pass:
                strict = false;
                if ((guru && pass == 1) | result.NullOrEmpty())
                {
                    workingTechLevel--;
                }
                if (useChildhood)
                {
                    if (pass == 1)
                    {
                        strict           = true;
                        workingTechLevel = childhoodLevel;
                    }
                    if (pass == 2)
                    {
                        workingTechLevel = techLevel;
                    }
                }
                if (workingTechLevel == 0)
                {
                    break;
                }
            }
            if (!result.NullOrEmpty())
            {
                return(result);
            }
            Log.Error($"[HumanResources] Couldn't calculate any expertise for {pawn}");
            return(null);
        }