private int DatabaseCount(out float databasePower) { int result = unlocked.TechsArchived.Count(); databasePower = TechTracker.BookResearchIncrement(result); return(result); }
//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); }
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)); }
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); }
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); } }
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 }); } }
//Thing public static ResearchProjectDef TryGetTech(this Thing book) { return((book.Stuff != null && book.Stuff.IsWithinCategory(TechDefOf.Knowledge)) ? TechTracker.FindTech(book.Stuff) : null); }
public static bool Allows(this Bill bill, ResearchProjectDef tech) { return(bill.ingredientFilter.Allows(TechTracker.FindTech(tech).Stuff)); }
private static bool SkillIsRelevant(SkillDef skill, TechLevel level) { return(TechTracker.FindSkills(x => x.Skill == skill && x.Techs.Any(y => y.techLevel == level)).Any()); }
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); }