using RimWorld; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using UnityEngine; using Verse.AI; namespace Verse { public class ThingDef : BuildableDef { public Type thingClass; public ThingCategory category; public TickerType tickerType; public int stackLimit = 1; public IntVec2 size = new IntVec2(1, 1); public bool destroyable = true; public bool rotatable = true; public bool smallVolume; public bool useHitPoints = true; public bool receivesSignals; public List comps = new List(); public List killedLeavings; public List butcherProducts; public List smeltProducts; public bool smeltable; public bool randomizeRotationOnSpawn; public List damageMultipliers; public bool isTechHediff; public RecipeMakerProperties recipeMaker; public ThingDef minifiedDef; public bool isUnfinishedThing; public bool leaveResourcesWhenKilled; public ThingDef slagDef; public bool isFrameInt; public IntVec3 interactionCellOffset = IntVec3.Zero; public bool hasInteractionCell; public ThingDef interactionCellIcon; public bool interactionCellIconReverse; public ThingDef filthLeaving; public bool forceDebugSpawnable; public bool intricate; public bool scatterableOnMapGen = true; public float deepCommonality; public int deepCountPerCell = 300; public int deepCountPerPortion = -1; public IntRange deepLumpSizeRange = IntRange.zero; public float generateCommonality = 1f; public float generateAllowChance = 1f; private bool canOverlapZones = true; public FloatRange startingHpRange = FloatRange.One; [NoTranslate] public List thingSetMakerTags; public bool alwaysFlee; public List recipes; public GraphicData graphicData; public DrawerType drawerType = DrawerType.RealtimeOnly; public bool drawOffscreen; public ColorGenerator colorGenerator; public float hideAtSnowDepth = 99999f; public bool drawDamagedOverlay = true; public bool castEdgeShadows; public float staticSunShadowHeight; public bool selectable; public bool neverMultiSelect; public bool isAutoAttackableMapObject; public bool hasTooltip; public List inspectorTabs; [Unsaved] public List inspectorTabsResolved; public bool seeThroughFog; public bool drawGUIOverlay; public ResourceCountPriority resourceReadoutPriority; public bool resourceReadoutAlwaysShow; public bool drawPlaceWorkersWhileSelected; public ConceptDef storedConceptLearnOpportunity; public float uiIconScale = 1f; public bool alwaysHaulable; public bool designateHaulable; public List thingCategories; public bool mineable; public bool socialPropernessMatters; public bool stealable = true; public SoundDef soundDrop; public SoundDef soundPickup; public SoundDef soundInteract; public SoundDef soundImpactDefault; public bool saveCompressible; public bool isSaveable = true; public bool holdsRoof; public float fillPercent; public bool coversFloor; public bool neverOverlapFloors; public SurfaceType surfaceType; public bool blockPlants; public bool blockLight; public bool blockWind; public Tradeability tradeability = Tradeability.All; [NoTranslate] public List tradeTags; public bool tradeNeverStack; public ColorGenerator colorGeneratorInTraderStock; private List verbs; public List tools; public float equippedAngleOffset; public EquipmentType equipmentType; public TechLevel techLevel; [NoTranslate] public List weaponTags; [NoTranslate] public List techHediffsTags; public bool destroyOnDrop; public List equippedStatOffsets; public BuildableDef entityDefToBuild; public ThingDef projectileWhenLoaded; public IngestibleProperties ingestible; public FilthProperties filth; public GasProperties gas; public BuildingProperties building; public RaceProperties race; public ApparelProperties apparel; public MoteProperties mote; public PlantProperties plant; public ProjectileProperties projectile; public StuffProperties stuffProps; public SkyfallerProperties skyfaller; [Unsaved] private string descriptionDetailedCached; [Unsaved] public Graphic interactionCellGraphic; public const int SmallUnitPerVolume = 10; public const float SmallVolumePerUnit = 0.1f; private List allRecipesCached; private static List EmptyVerbPropertiesList = new List(); private Dictionary concreteExamplesInt; public bool EverHaulable { get { return this.alwaysHaulable || this.designateHaulable; } } public float VolumePerUnit { get { return this.smallVolume ? 0.1f : 1f; } } public override IntVec2 Size { get { return this.size; } } public bool DiscardOnDestroyed { get { return this.race == null; } } public int BaseMaxHitPoints { get { return Mathf.RoundToInt(this.GetStatValueAbstract(StatDefOf.MaxHitPoints, null)); } } public float BaseFlammability { get { return this.GetStatValueAbstract(StatDefOf.Flammability, null); } } public float BaseMarketValue { get { return this.GetStatValueAbstract(StatDefOf.MarketValue, null); } set { this.SetStatBaseValue(StatDefOf.MarketValue, value); } } public float BaseMass { get { return this.GetStatValueAbstract(StatDefOf.Mass, null); } } public bool PlayerAcquirable { get { return !this.destroyOnDrop; } } public bool EverTransmitsPower { get { for (int i = 0; i < this.comps.Count; i++) { CompProperties_Power compProperties_Power = this.comps[i] as CompProperties_Power; if (compProperties_Power != null && compProperties_Power.transmitsPower) { return true; } } return false; } } public bool Minifiable { get { return this.minifiedDef != null; } } public bool HasThingIDNumber { get { return this.category != ThingCategory.Mote; } } public List AllRecipes { get { if (this.allRecipesCached == null) { this.allRecipesCached = new List(); if (this.recipes != null) { for (int i = 0; i < this.recipes.Count; i++) { this.allRecipesCached.Add(this.recipes[i]); } } List allDefsListForReading = DefDatabase.AllDefsListForReading; for (int j = 0; j < allDefsListForReading.Count; j++) { if (allDefsListForReading[j].recipeUsers != null && allDefsListForReading[j].recipeUsers.Contains(this)) { this.allRecipesCached.Add(allDefsListForReading[j]); } } } return this.allRecipesCached; } } public bool ConnectToPower { get { if (this.EverTransmitsPower) { return false; } for (int i = 0; i < this.comps.Count; i++) { if (this.comps[i].compClass == typeof(CompPowerBattery)) { return true; } if (this.comps[i].compClass == typeof(CompPowerTrader)) { return true; } } return false; } } public bool CoexistsWithFloors { get { return !this.neverOverlapFloors && !this.coversFloor; } } public FillCategory Fillage { get { if (this.fillPercent < 0.01f) { return FillCategory.None; } if (this.fillPercent > 0.99f) { return FillCategory.Full; } return FillCategory.Partial; } } public bool MakeFog { get { return this.Fillage == FillCategory.Full; } } public bool CanOverlapZones { get { if (this.building != null && this.building.SupportsPlants) { return false; } if (this.passability == Traversability.Impassable && this.category != ThingCategory.Plant) { return false; } if (this.surfaceType >= SurfaceType.Item) { return false; } if (typeof(ISlotGroupParent).IsAssignableFrom(this.thingClass)) { return false; } if (!this.canOverlapZones) { return false; } if (this.IsBlueprint || this.IsFrame) { ThingDef thingDef = this.entityDefToBuild as ThingDef; if (thingDef != null) { return thingDef.CanOverlapZones; } } return true; } } public bool CountAsResource { get { return this.resourceReadoutPriority != ResourceCountPriority.Uncounted; } } public bool BlockPlanting { get { return (this.building == null || !this.building.SupportsPlants) && (this.blockPlants || this.category == ThingCategory.Plant || this.Fillage > FillCategory.None || this.IsEdifice()); } } public List Verbs { get { if (this.verbs != null) { return this.verbs; } return ThingDef.EmptyVerbPropertiesList; } } public bool CanHaveFaction { get { if (this.IsBlueprint || this.IsFrame) { return true; } ThingCategory thingCategory = this.category; return thingCategory == ThingCategory.Pawn || thingCategory == ThingCategory.Building; } } public bool Claimable { get { return this.building != null && this.building.claimable && !this.building.isNaturalRock; } } public ThingCategoryDef FirstThingCategory { get { if (this.thingCategories.NullOrEmpty()) { return null; } return this.thingCategories[0]; } } public float MedicineTendXpGainFactor { get { return Mathf.Clamp(this.GetStatValueAbstract(StatDefOf.MedicalPotency, null) * 0.7f, 0.5f, 1f); } } public bool CanEverDeteriorate { get { return this.useHitPoints && (this.category == ThingCategory.Item || this == ThingDefOf.BurnedTree); } } public bool CanInteractThroughCorners { get { return this.category == ThingCategory.Building && this.holdsRoof && (this.building == null || !this.building.isNaturalRock || this.IsSmoothed); } } public bool AffectsRegions { get { return this.passability == Traversability.Impassable || this.IsDoor; } } public bool AffectsReachability { get { return this.AffectsRegions || (this.passability == Traversability.Impassable || this.IsDoor) || TouchPathEndModeUtility.MakesOccupiedCellsAlwaysReachableDiagonally(this); } } public string DescriptionDetailed { get { if (this.descriptionDetailedCached == null) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(this.description); if (this.IsApparel) { stringBuilder.AppendLine(); stringBuilder.AppendLine(string.Format("{0}: {1}", "Layer".Translate(), this.apparel.GetLayersString())); stringBuilder.AppendLine(string.Format("{0}: {1}", "Covers".Translate(), this.apparel.GetCoveredOuterPartsString(BodyDefOf.Human))); if (this.equippedStatOffsets != null && this.equippedStatOffsets.Count > 0) { stringBuilder.AppendLine(); foreach (StatModifier current in this.equippedStatOffsets) { stringBuilder.AppendLine(string.Format("{0}: {1}", current.stat.LabelCap, current.ValueToStringAsOffset)); } } } this.descriptionDetailedCached = stringBuilder.ToString(); } return this.descriptionDetailedCached; } } public bool IsApparel { get { return this.apparel != null; } } public bool IsBed { get { return typeof(Building_Bed).IsAssignableFrom(this.thingClass); } } public bool IsCorpse { get { return typeof(Corpse).IsAssignableFrom(this.thingClass); } } public bool IsFrame { get { return this.isFrameInt; } } public bool IsBlueprint { get { return this.entityDefToBuild != null && this.category == ThingCategory.Ethereal; } } public bool IsStuff { get { return this.stuffProps != null; } } public bool IsMedicine { get { return this.statBases.StatListContains(StatDefOf.MedicalPotency); } } public bool IsDoor { get { return typeof(Building_Door).IsAssignableFrom(this.thingClass); } } public bool IsFilth { get { return this.filth != null; } } public bool IsIngestible { get { return this.ingestible != null; } } public bool IsNutritionGivingIngestible { get { return this.IsIngestible && this.ingestible.CachedNutrition > 0f; } } public bool IsWeapon { get { return this.category == ThingCategory.Item && (!this.verbs.NullOrEmpty() || !this.tools.NullOrEmpty()); } } public bool IsCommsConsole { get { return typeof(Building_CommsConsole).IsAssignableFrom(this.thingClass); } } public bool IsOrbitalTradeBeacon { get { return typeof(Building_OrbitalTradeBeacon).IsAssignableFrom(this.thingClass); } } public bool IsFoodDispenser { get { return typeof(Building_NutrientPasteDispenser).IsAssignableFrom(this.thingClass); } } public bool IsDrug { get { return this.ingestible != null && this.ingestible.drugCategory != DrugCategory.None; } } public bool IsPleasureDrug { get { return this.IsDrug && this.ingestible.joy > 0f; } } public bool IsNonMedicalDrug { get { return this.IsDrug && this.ingestible.drugCategory != DrugCategory.Medical; } } public bool IsTable { get { return this.surfaceType == SurfaceType.Eat && this.HasComp(typeof(CompGatherSpot)); } } public bool IsWorkTable { get { return typeof(Building_WorkTable).IsAssignableFrom(this.thingClass); } } public bool IsShell { get { return this.projectileWhenLoaded != null; } } public bool IsArt { get { return this.IsWithinCategory(ThingCategoryDefOf.BuildingsArt); } } public bool IsSmoothable { get { return this.building != null && this.building.smoothedThing != null; } } public bool IsSmoothed { get { return this.building != null && this.building.unsmoothedThing != null; } } public bool IsMetal { get { return this.stuffProps != null && this.stuffProps.categories.Contains(StuffCategoryDefOf.Metallic); } } public bool IsAddictiveDrug { get { CompProperties_Drug compProperties = this.GetCompProperties(); return compProperties != null && compProperties.addictiveness > 0f; } } public bool IsMeat { get { return this.category == ThingCategory.Item && this.thingCategories != null && this.thingCategories.Contains(ThingCategoryDefOf.MeatRaw); } } public bool IsLeather { get { return this.category == ThingCategory.Item && this.thingCategories != null && this.thingCategories.Contains(ThingCategoryDefOf.Leathers); } } public bool IsRangedWeapon { get { if (!this.IsWeapon) { return false; } if (!this.verbs.NullOrEmpty()) { for (int i = 0; i < this.verbs.Count; i++) { if (!this.verbs[i].IsMeleeAttack) { return true; } } } return false; } } public bool IsMeleeWeapon { get { return this.IsWeapon && !this.IsRangedWeapon; } } public bool IsWeaponUsingProjectiles { get { if (!this.IsWeapon) { return false; } if (!this.verbs.NullOrEmpty()) { for (int i = 0; i < this.verbs.Count; i++) { if (this.verbs[i].LaunchesProjectile) { return true; } } } return false; } } public bool IsBuildingArtificial { get { return (this.category == ThingCategory.Building || this.IsFrame) && (this.building == null || (!this.building.isNaturalRock && !this.building.isResourceRock)); } } public string LabelAsStuff { get { if (!this.stuffProps.stuffAdjective.NullOrEmpty()) { return this.stuffProps.stuffAdjective; } return this.label; } } public bool EverStorable(bool willMinifyIfPossible) { if (typeof(MinifiedThing).IsAssignableFrom(this.thingClass)) { return true; } if (!this.thingCategories.NullOrEmpty()) { if (this.category == ThingCategory.Item) { return true; } if (willMinifyIfPossible && this.Minifiable) { return true; } } return false; } public Thing GetConcreteExample(ThingDef stuff = null) { if (this.concreteExamplesInt == null) { this.concreteExamplesInt = new Dictionary(); } if (stuff == null) { stuff = ThingDefOf.Steel; } if (!this.concreteExamplesInt.ContainsKey(stuff)) { if (this.race == null) { this.concreteExamplesInt[stuff] = ThingMaker.MakeThing(this, (!base.MadeFromStuff) ? null : stuff); } else { this.concreteExamplesInt[stuff] = PawnGenerator.GeneratePawn((from pkd in DefDatabase.AllDefsListForReading where pkd.race == this select pkd).FirstOrDefault(), null); } } return this.concreteExamplesInt[stuff]; } public CompProperties CompDefFor() where T : ThingComp { return this.comps.FirstOrDefault((CompProperties c) => c.compClass == typeof(T)); } public CompProperties CompDefForAssignableFrom() where T : ThingComp { return this.comps.FirstOrDefault((CompProperties c) => typeof(T).IsAssignableFrom(c.compClass)); } public bool HasComp(Type compType) { for (int i = 0; i < this.comps.Count; i++) { if (this.comps[i].compClass == compType) { return true; } } return false; } public T GetCompProperties() where T : CompProperties { for (int i = 0; i < this.comps.Count; i++) { T t = this.comps[i] as T; if (t != null) { return t; } } return (T)((object)null); } public override void PostLoad() { if (this.graphicData != null) { LongEventHandler.ExecuteWhenFinished(delegate { if (this.graphicData.shaderType == null) { this.graphicData.shaderType = ShaderTypeDefOf.Cutout; } this.graphic = this.graphicData.Graphic; }); } if (this.tools != null) { for (int i = 0; i < this.tools.Count; i++) { this.tools[i].id = i.ToString(); } } if (this.verbs != null && this.verbs.Count == 1) { this.verbs[0].label = this.label; } base.PostLoad(); if (this.category == ThingCategory.Building && this.building == null) { this.building = new BuildingProperties(); } if (this.building != null) { this.building.PostLoadSpecial(this); } if (this.plant != null) { this.plant.PostLoadSpecial(this); } } protected override void ResolveIcon() { base.ResolveIcon(); if (this.category == ThingCategory.Pawn) { if (!this.race.Humanlike) { PawnKindDef anyPawnKind = this.race.AnyPawnKind; if (anyPawnKind != null) { Material material = anyPawnKind.lifeStages.Last().bodyGraphicData.Graphic.MatAt(Rot4.East, null); this.uiIcon = (Texture2D)material.mainTexture; this.uiIconColor = material.color; } } } else { ThingDef thingDef = GenStuff.DefaultStuffFor(this); if (this.colorGenerator != null && (thingDef == null || thingDef.stuffProps.allowColorGenerators)) { this.uiIconColor = this.colorGenerator.ExemplaryColor; } else if (thingDef != null) { this.uiIconColor = thingDef.stuffProps.color; } else if (this.graphicData != null) { this.uiIconColor = this.graphicData.color; } if (this.rotatable && this.graphic != null && this.graphic != BaseContent.BadGraphic && this.graphic.ShouldDrawRotated && this.defaultPlacingRot == Rot4.South) { this.uiIconAngle = 180f + this.graphic.DrawRotatedExtraAngleOffset; } } } public override void ResolveReferences() { base.ResolveReferences(); if (this.ingestible != null) { this.ingestible.parent = this; } if (this.building != null) { this.building.ResolveReferencesSpecial(); } if (this.graphicData != null) { this.graphicData.ResolveReferencesSpecial(); } if (this.race != null) { this.race.ResolveReferencesSpecial(); } if (this.stuffProps != null) { this.stuffProps.ResolveReferencesSpecial(); } if (this.soundImpactDefault == null) { this.soundImpactDefault = SoundDefOf.BulletImpact_Ground; } if (this.soundDrop == null) { this.soundDrop = SoundDefOf.Standard_Drop; } if (this.soundPickup == null) { this.soundPickup = SoundDefOf.Standard_Pickup; } if (this.soundInteract == null) { this.soundInteract = SoundDefOf.Standard_Pickup; } if (this.inspectorTabs != null && this.inspectorTabs.Any()) { this.inspectorTabsResolved = new List(); for (int i = 0; i < this.inspectorTabs.Count; i++) { try { this.inspectorTabsResolved.Add(InspectTabManager.GetSharedInstance(this.inspectorTabs[i])); } catch (Exception ex) { Log.Error(string.Concat(new object[] { "Could not instantiate inspector tab of type ", this.inspectorTabs[i], ": ", ex }), false); } } } if (this.comps != null) { for (int j = 0; j < this.comps.Count; j++) { this.comps[j].ResolveReferences(this); } } } [DebuggerHidden] public override IEnumerable ConfigErrors() { foreach (string str in base.ConfigErrors()) { yield return str; } if (this.label.NullOrEmpty()) { yield return "no label"; } if (this.graphicData != null) { foreach (string err in this.graphicData.ConfigErrors(this)) { yield return err; } } if (this.projectile != null) { foreach (string err2 in this.projectile.ConfigErrors(this)) { yield return err2; } } if (this.statBases != null) { foreach (StatModifier statBase in this.statBases) { if ((from st in this.statBases where st.stat == statBase.stat select st).Count() > 1) { yield return "defines the stat base " + statBase.stat + " more than once."; } } } if (!BeautyUtility.BeautyRelevant(this.category) && this.StatBaseDefined(StatDefOf.Beauty)) { yield return "Beauty stat base is defined, but Things of category " + this.category + " cannot have beauty."; } if (char.IsNumber(this.defName[this.defName.Length - 1])) { yield return "ends with a numerical digit, which is not allowed on ThingDefs."; } if (this.thingClass == null) { yield return "has null thingClass."; } if (this.comps.Count > 0 && !typeof(ThingWithComps).IsAssignableFrom(this.thingClass)) { yield return "has components but it's thingClass is not a ThingWithComps"; } if (this.ConnectToPower && this.drawerType == DrawerType.RealtimeOnly && this.IsFrame) { yield return "connects to power but does not add to map mesh. Will not create wire meshes."; } if (this.costList != null) { foreach (ThingDefCountClass cost in this.costList) { if (cost.count == 0) { yield return "cost in " + cost.thingDef + " is zero."; } } } if (this.thingCategories != null) { ThingCategoryDef doubleCat = this.thingCategories.FirstOrDefault(delegate(ThingCategoryDef cat) { ThingDef $this = this.$this; return this.$this.thingCategories.Count((ThingCategoryDef c) => c == cat) > 1; }); if (doubleCat != null) { yield return "has duplicate thingCategory " + doubleCat + "."; } } if (this.Fillage == FillCategory.Full && this.category != ThingCategory.Building) { yield return "gives full cover but is not a building."; } if (this.comps.Any((CompProperties c) => c.compClass == typeof(CompPowerTrader)) && this.drawerType == DrawerType.MapMeshOnly) { yield return "has PowerTrader comp but does not draw real time. It won't draw a needs-power overlay."; } if (this.equipmentType != EquipmentType.None) { if (this.techLevel == TechLevel.Undefined) { yield return "is equipment but has no tech level."; } if (!this.comps.Any((CompProperties c) => c.compClass == typeof(CompEquippable))) { yield return "is equipment but has no CompEquippable"; } } if (this.thingClass == typeof(Bullet) && this.projectile.damageDef == null) { yield return " is a bullet but has no damageDef."; } if (this.destroyOnDrop) { if (!this.menuHidden) { yield return "destroyOnDrop but not menuHidden."; } if (this.tradeability != Tradeability.None) { yield return "destroyOnDrop but tradeability is " + this.tradeability; } } if (this.stackLimit > 1 && !this.drawGUIOverlay) { yield return "has stackLimit > 1 but also has drawGUIOverlay = false."; } if (this.damageMultipliers != null) { foreach (DamageMultiplier mult in this.damageMultipliers) { if ((from m in this.damageMultipliers where m.damageDef == mult.damageDef select m).Count() > 1) { yield return "has multiple damage multipliers for damageDef " + mult.damageDef; } } } if (this.Fillage == FillCategory.Full && !this.IsEdifice()) { yield return "fillPercent is 1.00 but is not edifice"; } if (base.MadeFromStuff && this.constructEffect != null) { yield return "madeFromStuff but has a defined constructEffect (which will always be overridden by stuff's construct animation)."; } if (base.MadeFromStuff && this.stuffCategories.NullOrEmpty()) { yield return "madeFromStuff but has no stuffCategories."; } if (this.costList.NullOrEmpty() && this.costStuffCount <= 0 && this.recipeMaker != null) { yield return "has a recipeMaker but no costList or costStuffCount."; } if (this.GetStatValueAbstract(StatDefOf.DeteriorationRate, null) > 1E-05f && !this.CanEverDeteriorate) { yield return "has >0 DeteriorationRate but can't deteriorate."; } if (this.drawerType == DrawerType.MapMeshOnly) { if (this.comps.Any((CompProperties c) => c.compClass == typeof(CompForbiddable))) { yield return "drawerType=MapMeshOnly but has a CompForbiddable, which must draw in real time."; } } if (this.smeltProducts != null && this.smeltable) { yield return "has smeltProducts but has smeltable=false"; } if (this.equipmentType != EquipmentType.None && this.verbs.NullOrEmpty() && this.tools.NullOrEmpty()) { yield return "is equipment but has no verbs or tools"; } if (this.Minifiable && this.thingCategories.NullOrEmpty()) { yield return "is minifiable but not in any thing category"; } if (this.category == ThingCategory.Building && !this.Minifiable && !this.thingCategories.NullOrEmpty()) { yield return "is not minifiable yet has thing categories (could be confusing in thing filters because it can't be moved/stored anyway)"; } if (this != ThingDefOf.MinifiedThing && (this.EverHaulable || this.Minifiable)) { if (!this.statBases.NullOrEmpty()) { if (this.statBases.Any((StatModifier s) => s.stat == StatDefOf.Mass)) { goto IL_D74; } } yield return "is haulable, but does not have an authored mass value"; } IL_D74: if (this.ingestible == null && this.GetStatValueAbstract(StatDefOf.Nutrition, null) != 0f) { yield return "has nutrition but ingestible properties are null"; } if (this.BaseFlammability != 0f && !this.useHitPoints && this.category != ThingCategory.Pawn) { yield return "flammable but has no hitpoints (will burn indefinitely)"; } if (this.graphicData != null && this.graphicData.shadowData != null && this.staticSunShadowHeight > 0f) { yield return "graphicData defines a shadowInfo but staticSunShadowHeight > 0"; } if (this.saveCompressible && this.Claimable) { yield return "claimable item is compressible; faction will be unset after load"; } if (this.deepCommonality > 0f != this.deepLumpSizeRange.TrueMax > 0) { yield return "if deepCommonality or deepLumpSizeRange is set, the other also must be set"; } if (this.deepCommonality > 0f && this.deepCountPerPortion <= 0) { yield return "deepCommonality > 0 but deepCountPerPortion is not set"; } if (this.verbs != null) { for (int k = 0; k < this.verbs.Count; k++) { foreach (string err3 in this.verbs[k].ConfigErrors(this)) { yield return string.Format("verb {0}: {1}", k, err3); } } } if (this.race != null && this.tools != null) { int i; for (i = 0; i < this.tools.Count; i++) { if (this.tools[i].linkedBodyPartsGroup != null && !this.race.body.AllParts.Any((BodyPartRecord part) => part.groups.Contains(this.$this.tools[i].linkedBodyPartsGroup))) { yield return string.Concat(new object[] { "has tool with linkedBodyPartsGroup ", this.tools[i].linkedBodyPartsGroup, " but body ", this.race.body, " has no parts with that group." }); } } } if (this.building != null) { foreach (string err4 in this.building.ConfigErrors(this)) { yield return err4; } } if (this.apparel != null) { foreach (string err5 in this.apparel.ConfigErrors(this)) { yield return err5; } } if (this.comps != null) { for (int j = 0; j < this.comps.Count; j++) { foreach (string err6 in this.comps[j].ConfigErrors(this)) { yield return err6; } } } if (this.race != null) { foreach (string e in this.race.ConfigErrors()) { yield return e; } } if (this.ingestible != null) { foreach (string e2 in this.ingestible.ConfigErrors()) { yield return e2; } } if (this.plant != null) { foreach (string e3 in this.plant.ConfigErrors()) { yield return e3; } } if (this.tools != null) { Tool dupeTool = this.tools.SelectMany(delegate(Tool lhs) { ThingDef $this = this.$this; return from rhs in this.$this.tools where lhs != rhs && lhs.id == rhs.id select rhs; }).FirstOrDefault(); if (dupeTool != null) { yield return string.Format("duplicate thingdef tool id {0}", dupeTool.id); } foreach (Tool t in this.tools) { foreach (string e4 in t.ConfigErrors()) { yield return e4; } } } } public static ThingDef Named(string defName) { return DefDatabase.GetNamed(defName, true); } public bool IsWithinCategory(ThingCategoryDef category) { if (this.thingCategories == null) { return false; } for (int i = 0; i < this.thingCategories.Count; i++) { for (ThingCategoryDef thingCategoryDef = this.thingCategories[i]; thingCategoryDef != null; thingCategoryDef = thingCategoryDef.parent) { if (thingCategoryDef == category) { return true; } } } return false; } [DebuggerHidden] public override IEnumerable SpecialDisplayStats(StatRequest req) { foreach (StatDrawEntry stat in base.SpecialDisplayStats(req)) { yield return stat; } if (this.apparel != null) { string coveredParts = this.apparel.GetCoveredOuterPartsString(BodyDefOf.Human); yield return new StatDrawEntry(StatCategoryDefOf.Apparel, "Covers".Translate(), coveredParts, 100, string.Empty); yield return new StatDrawEntry(StatCategoryDefOf.Apparel, "Layer".Translate(), this.apparel.GetLayersString(), 95, string.Empty); } if (this.IsMedicine && this.MedicineTendXpGainFactor != 1f) { yield return new StatDrawEntry(StatCategoryDefOf.Basics, "MedicineXpGainFactor".Translate(), this.MedicineTendXpGainFactor.ToStringPercent(), 0, string.Empty); } if (this.fillPercent > 0f && this.fillPercent < 1f && (this.category == ThingCategory.Item || this.category == ThingCategory.Building || this.category == ThingCategory.Plant)) { yield return new StatDrawEntry(StatCategoryDefOf.Basics, "CoverEffectiveness".Translate(), this.BaseBlockChance().ToStringPercent(), 0, string.Empty) { overrideReportText = "CoverEffectivenessExplanation".Translate() }; } if (this.constructionSkillPrerequisite > 0) { StatCategoryDef statCategoryDef = StatCategoryDefOf.Basics; string text = "ConstructionSkillRequired".Translate(); string valueString = this.constructionSkillPrerequisite.ToString(); string text2 = "ConstructionSkillRequiredExplanation".Translate(); yield return new StatDrawEntry(statCategoryDef, text, valueString, 0, text2); } if (!this.verbs.NullOrEmpty()) { VerbProperties verb = this.verbs.First((VerbProperties x) => x.isPrimary); StatCategoryDef verbStatCategory = (this.category != ThingCategory.Pawn) ? (verbStatCategory = StatCategoryDefOf.Weapon) : (verbStatCategory = StatCategoryDefOf.PawnCombat); float warmup = verb.warmupTime; if (warmup > 0f) { string warmupLabel = (this.category != ThingCategory.Pawn) ? "WarmupTime".Translate() : "MeleeWarmupTime".Translate(); yield return new StatDrawEntry(verbStatCategory, warmupLabel, warmup.ToString("0.##") + " s", 40, string.Empty); } if (verb.defaultProjectile != null) { StringBuilder damageAmountExplanation = new StringBuilder(); float dam = (float)verb.defaultProjectile.projectile.GetDamageAmount(req.Thing, damageAmountExplanation); yield return new StatDrawEntry(verbStatCategory, "Damage".Translate(), dam.ToString(), 50, damageAmountExplanation.ToString()); if (verb.defaultProjectile.projectile.damageDef.armorCategory != null) { StringBuilder armorPenetrationExplanation = new StringBuilder(); float ap = verb.defaultProjectile.projectile.GetArmorPenetration(req.Thing, armorPenetrationExplanation); string fullExplanation = "ArmorPenetrationExplanation".Translate(); if (armorPenetrationExplanation.Length != 0) { fullExplanation = fullExplanation + "\n\n" + armorPenetrationExplanation; } yield return new StatDrawEntry(verbStatCategory, "ArmorPenetration".Translate(), ap.ToStringPercent(), 49, fullExplanation); } } if (verb.LaunchesProjectile) { int burstShotCount = verb.burstShotCount; float burstShotFireRate = 60f / verb.ticksBetweenBurstShots.TicksToSeconds(); float range = verb.range; if (burstShotCount > 1) { yield return new StatDrawEntry(verbStatCategory, "BurstShotCount".Translate(), burstShotCount.ToString(), 20, string.Empty); yield return new StatDrawEntry(verbStatCategory, "BurstShotFireRate".Translate(), burstShotFireRate.ToString("0.##") + " rpm", 19, string.Empty); } yield return new StatDrawEntry(verbStatCategory, "Range".Translate(), range.ToString("F0"), 10, string.Empty); if (verb.defaultProjectile != null && verb.defaultProjectile.projectile != null && verb.defaultProjectile.projectile.stoppingPower != 0f) { StatCategoryDef statCategoryDef = verbStatCategory; string text2 = "StoppingPower".Translate(); string valueString = verb.defaultProjectile.projectile.stoppingPower.ToString("F1"); string text = "StoppingPowerExplanation".Translate(); yield return new StatDrawEntry(statCategoryDef, text2, valueString, 0, text); } } if (verb.forcedMissRadius > 0f) { yield return new StatDrawEntry(verbStatCategory, "MissRadius".Translate(), verb.forcedMissRadius.ToString("0.#"), 30, string.Empty); yield return new StatDrawEntry(verbStatCategory, "DirectHitChance".Translate(), (1f / (float)GenRadial.NumCellsInRadius(verb.forcedMissRadius)).ToStringPercent(), 29, string.Empty); } } if (this.plant != null) { foreach (StatDrawEntry s in this.plant.SpecialDisplayStats()) { yield return s; } } if (this.ingestible != null) { foreach (StatDrawEntry s2 in this.ingestible.SpecialDisplayStats()) { yield return s2; } } if (this.race != null) { foreach (StatDrawEntry s3 in this.race.SpecialDisplayStats(this)) { yield return s3; } } if (this.building != null) { foreach (StatDrawEntry s4 in this.building.SpecialDisplayStats(this, req)) { yield return s4; } } if (this.isTechHediff) { foreach (RecipeDef def in from x in DefDatabase.AllDefs where x.IsIngredient(this.$this) select x) { HediffDef diff = def.addsHediff; if (diff != null) { if (diff.addedPartProps != null) { yield return new StatDrawEntry(StatCategoryDefOf.Basics, "BodyPartEfficiency".Translate(), diff.addedPartProps.partEfficiency.ToStringByStyle(ToStringStyle.PercentZero, ToStringNumberSense.Absolute), 0, string.Empty); } foreach (StatDrawEntry s5 in diff.SpecialDisplayStats(StatRequest.ForEmpty())) { yield return s5; } HediffCompProperties_VerbGiver vg = diff.CompProps(); if (vg != null) { if (!vg.verbs.NullOrEmpty()) { VerbProperties verb2 = vg.verbs[0]; if (!verb2.IsMeleeAttack) { if (verb2.defaultProjectile != null) { int projDamage = verb2.defaultProjectile.projectile.GetDamageAmount(null, null); yield return new StatDrawEntry(StatCategoryDefOf.Basics, "Damage".Translate(), projDamage.ToString(), 0, string.Empty); } } else { int meleeDamage = verb2.meleeDamageBaseAmount; if (verb2.meleeDamageDef.armorCategory != null) { float armorPenetration = verb2.meleeArmorPenetrationBase; if (armorPenetration < 0f) { armorPenetration = (float)meleeDamage * 0.015f; } StatCategoryDef statCategoryDef = StatCategoryDefOf.Weapon; string text2 = "ArmorPenetration".Translate(); string valueString = armorPenetration.ToStringPercent(); string text = "ArmorPenetrationExplanation".Translate(); yield return new StatDrawEntry(statCategoryDef, text2, valueString, 0, text); } } } else if (!vg.tools.NullOrEmpty()) { Tool tool = vg.tools[0]; if (ThingUtility.PrimaryMeleeWeaponDamageType(vg.tools).armorCategory != null) { float armorPenetration2 = tool.armorPenetration; if (armorPenetration2 < 0f) { armorPenetration2 = tool.power * 0.015f; } StatCategoryDef statCategoryDef = StatCategoryDefOf.Weapon; string text = "ArmorPenetration".Translate(); string valueString = armorPenetration2.ToStringPercent(); string text2 = "ArmorPenetrationExplanation".Translate(); yield return new StatDrawEntry(statCategoryDef, text, valueString, 0, text2); } } } ThoughtDef thought = DefDatabase.AllDefs.FirstOrDefault((ThoughtDef x) => x.hediff == diff); if (thought != null && thought.stages != null && thought.stages.Any()) { yield return new StatDrawEntry(StatCategoryDefOf.Basics, "MoodChange".Translate(), thought.stages.First().baseMoodEffect.ToStringByStyle(ToStringStyle.Integer, ToStringNumberSense.Offset), 0, string.Empty); } } } } for (int i = 0; i < this.comps.Count; i++) { foreach (StatDrawEntry s6 in this.comps[i].SpecialDisplayStats(req)) { yield return s6; } } } } }