public override void DoWindowContents(Rect inRect) { var conf = _pawn.GetApparelStatCache(); var conRect = new Rect(inRect); conRect.height -= 50f; BeginArea(conRect); // begin main group BeginVertical(); Label(GetTitle(), _headline); Text.Font = GameFont.Small; // GUI.BeginGroup(contentRect); var labelWidth = conRect.width - BaseValue - BaseValue - BaseValue - 48f; DrawLine("Status", labelWidth, "BaseMod", "Strength", "Score", _fontBold); Space(6f); Label(string.Empty, _whiteLine, Height(1)); Space(6f); var apparelEntry = conf.GetAllOffsets(_apparel); var equippedOffsets = apparelEntry.EquippedOffsets; var statBases = apparelEntry.StatBases; var infusedOffsets = apparelEntry.InfusedOffsets; _scrollPosition = BeginScrollView(_scrollPosition, Width(conRect.width)); // relevant apparel stats // start score at 1 float score = 1; // add values for each statdef modified by the apparel foreach (var statPriority in _pawn.GetApparelStatCache().StatCache .OrderBy(i => i.Stat.LabelCap)) { var stat = statPriority.Stat; string statLabel = stat.LabelCap; // statbases, e.g. armor // StatCache.DoApparelScoreRaw_PawnStatsHandlers(_pawn, _apparel, statPriority.Stat, ref currentStat); if (statBases.Contains(stat)) { var statValue = _apparel.GetStatValue(stat); var statScore = 0f; if (ApparelStatCache.SpecialStats.Contains(stat)) { ApparelStatCache.CalculateScoreForSpecialStats(_apparel, statPriority, _pawn, statValue, ref statScore); } else { // statValue += StatCache.StatInfused(infusionSet, statPriority, ref baseInfused); statScore = statValue * statPriority.Weight; } score += statScore; DrawLine( statLabel, labelWidth, statValue.ToStringPercent("N1"), statPriority.Weight.ToString("N2"), statScore.ToString("N2")); } if (equippedOffsets.Contains(stat)) { var statValue = _apparel.GetEquippedStatValue(_pawn, stat); // statValue += StatCache.StatInfused(infusionSet, statPriority, ref equippedInfused); var statScore = 0f; if (ApparelStatCache.SpecialStats.Contains(stat)) { ApparelStatCache.CalculateScoreForSpecialStats(_apparel, statPriority, _pawn, statValue, ref statScore); } else { statScore = statValue * statPriority.Weight; } score += statScore; DrawLine( statLabel, labelWidth, statValue.ToStringPercent("N1"), statPriority.Weight.ToString("N2"), statScore.ToString("N2")); } if (!infusedOffsets.Contains(stat)) { continue; } { GUI.color = Color.green; // new Color(0.5f, 1f, 1f, 1f); // float statInfused = StatCache.StatInfused(infusionSet, statPriority, ref dontcare); ApparelStatCache.DoApparelScoreRaw_PawnStatsHandlers(_apparel, stat, out var statValue); var flag = true; var statScore = 0f; if (ApparelStatCache.SpecialStats.Contains(stat)) { ApparelStatCache.CalculateScoreForSpecialStats(_apparel, statPriority, _pawn, statValue, ref statScore); } else { // Bug with Infused and "Ancient", it completely kills the pawn's armor if (statValue < 0 && (stat == StatDefOf.ArmorRating_Blunt || stat == StatDefOf.ArmorRating_Sharp)) { score = -2f; flag = false; } statScore = statValue * statPriority.Weight; } DrawLine( statLabel, labelWidth, statValue.ToStringPercent("N1"), statPriority.Weight.ToString("N2"), statScore.ToString("N2")); GUI.color = Color.white; if (flag) { score += statScore; } else { break; } } } GUI.color = Color.white; // end upper group EndScrollView(); // begin lower group FlexibleSpace(); Space(6f); Label(string.Empty, _whiteLine, Height(1)); Space(6f); DrawLine(string.Empty, labelWidth, "Modifier", string.Empty, "Subtotal"); DrawLine("BasicStatusOfApparel".Translate(), labelWidth, "1.00", "+", score.ToString("N2")); var special = _apparel.GetSpecialApparelScoreOffset(); if (Math.Abs(special) > 0f) { score += special; DrawLine( "OutfitterSpecialScore".Translate(), labelWidth, special.ToString("N2"), "+", score.ToString("N2")); } var armor = ApparelStatCache.ApparelScoreRaw_ProtectionBaseStat(_apparel); if (Math.Abs(armor) > 0.01f) { score += armor; DrawLine("OutfitterArmor".Translate(), labelWidth, armor.ToString("N2"), "+", score.ToString("N2")); } if (_apparel.def.useHitPoints) { // durability on 0-1 scale var x = _apparel.HitPoints / (float)_apparel.MaxHitPoints; score *= ApparelStatsHelper.HitPointsPercentScoreFactorCurve.Evaluate(x); DrawLine( "OutfitterHitPoints".Translate(), labelWidth, x.ToString("N2"), "weighted", score.ToString("N2")); GUI.color = Color.white; } if (_apparel.WornByCorpse && ThoughtUtility.CanGetThought_NewTemp(_pawn, ThoughtDefOf.DeadMansApparel)) { score -= 0.5f; if (score > 0f) { score *= 0.1f; } DrawLine( "OutfitterWornByCorpse".Translate(), labelWidth, "modified", "weighted", score.ToString("N2")); } if (_apparel.Stuff == ThingDefOf.Human.race.leatherDef) { if (ThoughtUtility.CanGetThought_NewTemp(_pawn, ThoughtDefOf.HumanLeatherApparelSad)) { score -= 0.5f; if (score > 0f) { score *= 0.1f; } } if (ThoughtUtility.CanGetThought_NewTemp(_pawn, ThoughtDefOf.HumanLeatherApparelHappy)) { score *= 2f; } DrawLine( "OutfitterHumanLeather".Translate(), labelWidth, "modified", "weighted", score.ToString("N2")); } var temperature = conf.ApparelScoreRaw_Temperature(_apparel); if (Math.Abs(temperature - 1f) > 0) { score *= temperature; DrawLine( "OutfitterTemperature".Translate(), labelWidth, temperature.ToString("N2"), "*", score.ToString("N2")); } DrawLine( "OutfitterTotal".Translate(), labelWidth, string.Empty, "=", conf.ApparelScoreRaw(_apparel).ToString("N2")); GUI.color = Color.white; Text.Anchor = TextAnchor.UpperLeft; // end main group EndVertical(); EndArea(); }
// private static NeededWarmth neededWarmth; // ReSharper disable once InconsistentNaming public static bool TryGiveJob_Prefix([CanBeNull] ref Job __result, Pawn pawn) { __result = null; if (pawn.outfits == null) { Log.ErrorOnce( pawn + " tried to run JobGiver_OutfitterOptimizeApparel without an OutfitTracker", 5643897); return(false); } if (pawn.Faction != Faction.OfPlayer) { Log.ErrorOnce("Non-colonist " + pawn + " tried to optimize apparel.", 764323); return(false); } if (!DebugViewSettings.debugApparelOptimize) { if (Find.TickManager.TicksGame < pawn.mindState.nextApparelOptimizeTick) { return(false); } } else { _debugSb = new StringBuilder(); _debugSb.AppendLine(string.Concat("Outfiter scanning for ", pawn, " at ", pawn.Position)); } Outfit currentOutfit = pawn.outfits.CurrentOutfit; List <Apparel> wornApparel = pawn.apparel.WornApparel; for (int i = 0; i < wornApparel.Count; i++) { Apparel ap = wornApparel[i]; ApparelStatCache conf = pawn.GetApparelStatCache(); bool notAllowed = !currentOutfit.filter.Allows(ap) && pawn.outfits.forcedHandler.AllowedToAutomaticallyDrop(ap); bool shouldDrop = conf.ApparelScoreRaw(ap) < 0f && pawn.outfits.forcedHandler.AllowedToAutomaticallyDrop(ap); bool someoneWantsIt = pawn.GetApparelStatCache().ToDropList.ContainsKey(ap); if (notAllowed || shouldDrop || someoneWantsIt) { __result = new Job(JobDefOf.RemoveApparel, ap) { haulDroppedApparel = true }; if (someoneWantsIt) { pawn.GetApparelStatCache().ToDropList[ap].mindState.nextApparelOptimizeTick = -5000; pawn.GetApparelStatCache().ToDropList[ap].mindState.Notify_OutfitChanged(); pawn.GetApparelStatCache().ToDropList.Remove(ap); } return(false); } } Thing thing = null; float score = 0f; List <Thing> list = pawn.Map.listerThings.ThingsInGroup(ThingRequestGroup.Apparel); if (list.Count == 0) { SetNextOptimizeTick(pawn); return(false); } for (int i = 0; i < list.Count; i++) { Thing t = list[i]; Apparel apparel = (Apparel)t; // Not allowed if (!currentOutfit.filter.Allows(apparel)) { continue; } // Not in store if (apparel.Map.haulDestinationManager.SlotGroupAt(apparel.Position) == null) { continue; } // Forbidden if (apparel.IsForbidden(pawn)) { continue; } float gain = pawn.ApparelScoreGain(apparel); // this blocks pawns constantly switching between the recent apparel, due to shifting calculations // not very elegant but working // if (pawn.GetApparelStatCache().recentApparel.Contains(apparel)) // { // gain *= 0.01f; // } if (DebugViewSettings.debugApparelOptimize) { _debugSb.AppendLine(apparel.LabelCap + ": " + gain.ToString("F2")); } // float otherGain = 0f; // Pawn otherPawn = null; // foreach (Pawn otherP in pawn.Map.mapPawns.FreeColonistsSpawned.ToList()) // { // if (otherP == pawn) // { // continue; // } // if (otherP.ApparelScoreGain(apparel) >= MinScoreGainToCare) // { // if (ApparelUtility.HasPartsToWear(pawn, apparel.def)) // { // if (pawn.CanReserveAndReach(apparel, PathEndMode.OnCell, pawn.NormalMaxDanger(), 1)) // { // thing = apparel; // score = gain; // } // } // otherPawn = otherP; // otherGain = Mathf.Max(otherGain, otherP.ApparelScoreGain(apparel)); // } // } if (gain >= MinScoreGainToCare && gain >= score) { if (ApparelUtility.HasPartsToWear(pawn, apparel.def)) { if (pawn.CanReserveAndReach(apparel, PathEndMode.OnCell, pawn.NormalMaxDanger())) { thing = apparel; score = gain; } } } } if (DebugViewSettings.debugApparelOptimize) { _debugSb.AppendLine("BEST: " + thing); //Log.Message(_debugSb.ToString()); _debugSb = null; } // New stuff if (false) { IEnumerable <Pawn> list2 = pawn.Map.mapPawns.FreeColonistsSpawned.Where(x => x.IsColonistPlayerControlled); foreach (Apparel ap in wornApparel) { foreach (Pawn otherPawn in list2) { foreach (Apparel otherAp in otherPawn.apparel.WornApparel.Where( x => !ApparelUtility.CanWearTogether(ap.def, x.def, pawn.RaceProps.body))) { float gain = pawn.ApparelScoreGain(otherAp); float otherGain = otherPawn.ApparelScoreGain(ap); if (gain > MinScoreGainToCare && gain >= score && otherGain > MinScoreGainToCare) { score = gain; Log.Message( "OUTFITTER: " + pawn + " wants " + otherAp + " currently worn by " + otherPawn + ", scores: " + gain + " - " + otherGain + " - " + score); if (!otherPawn.GetApparelStatCache().ToDropList.ContainsKey(ap)) { otherPawn.GetApparelStatCache().ToDropList.Add(otherAp, otherPawn); otherPawn.mindState.nextApparelOptimizeTick = -5000; otherPawn.mindState.Notify_OutfitChanged(); } } } } } } if (thing == null) { SetNextOptimizeTick(pawn); return(false); } // foreach (Apparel apparel in wornApparel) // { // pawn.GetApparelStatCache().recentApparel.Add(apparel); // } __result = new Job(JobDefOf.Wear, thing); pawn.Reserve(thing, __result, 1, 1); return(false); }
private void DrawTemperatureStats([NotNull] SaveablePawn pawnSave, ref Vector2 cur, Rect canvas) { // header Rect tempHeaderRect = new Rect(cur.x, cur.y, canvas.width, 30f); cur.y += 30f; Text.Anchor = TextAnchor.LowerLeft; Widgets.Label(tempHeaderRect, "PreferedTemperature".Translate()); Text.Anchor = TextAnchor.UpperLeft; // line GUI.color = Color.grey; Widgets.DrawLineHorizontal(cur.x, cur.y, canvas.width); GUI.color = Color.white; // some padding cur.y += Margin; // temperature slider // SaveablePawn pawnStatCache = MapComponent_Outfitter.Get.GetSaveablePawn(SelPawn); ApparelStatCache pawnStatCache = this.SelPawnForGear.GetApparelStatCache(); FloatRange targetTemps = pawnStatCache.TargetTemperatures; FloatRange minMaxTemps = ApparelStatsHelper.MinMaxTemperatureRange; Rect sliderRect = new Rect(cur.x, cur.y, canvas.width - 20f, 40f); Rect tempResetRect = new Rect(sliderRect.xMax + 4f, cur.y + Margin, 16f, 16f); cur.y += 40f; // includes padding // current temperature settings GUI.color = pawnSave.TargetTemperaturesOverride ? Color.white : Color.grey; Widgets_FloatRange.FloatRange( sliderRect, 123123123, ref targetTemps, minMaxTemps, ToStringStyle.Temperature); GUI.color = Color.white; if (Math.Abs(targetTemps.min - pawnStatCache.TargetTemperatures.min) > 1e-4 || Math.Abs(targetTemps.max - pawnStatCache.TargetTemperatures.max) > 1e-4) { pawnStatCache.TargetTemperatures = targetTemps; } if (pawnSave.TargetTemperaturesOverride) { if (Widgets.ButtonImage(tempResetRect, OutfitterTextures.ResetButton)) { pawnSave.TargetTemperaturesOverride = false; // var saveablePawn = MapComponent_Outfitter.Get.GetSaveablePawn(SelPawn); // saveablePawn.targetTemperaturesOverride = false; pawnStatCache.UpdateTemperatureIfNecessary(true); } TooltipHandler.TipRegion(tempResetRect, "TemperatureRangeReset".Translate()); } Text.Font = GameFont.Small; this.TryDrawComfyTemperatureRange(ref cur.y, canvas.width); }
private void DrawThingRowModded(ref float y, float width, Apparel apparel) { if (apparel == null) { this.DrawThingRowVanilla(ref y, width, apparel); return; } Rect rect = new Rect(0f, y, width, ThingRowHeight); if (Mouse.IsOver(rect)) { GUI.color = HighlightColor; GUI.DrawTexture(rect, TexUI.HighlightTex); } GUI.color = ThingLabelColor; // LMB doubleclick if (Widgets.ButtonInvisible(rect)) { // Left Mouse Button Menu if (Event.current.button == 0) { Find.WindowStack.Add(new Window_Pawn_ApparelDetail(this.SelPawn, apparel)); } // RMB menu if (Event.current.button == 1) { List <FloatMenuOption> floatOptionList = new List <FloatMenuOption> { new FloatMenuOption( "ThingInfo".Translate(), delegate { Find.WindowStack.Add(new Dialog_InfoCard(apparel)); }) }; if (this.CanControl) { floatOptionList.Add( new FloatMenuOption( "OutfitterComparer".Translate(), delegate { Find.WindowStack.Add( new Dialog_PawnApparelComparer(this.SelPawnForGear, apparel)); })); Action dropApparel = delegate { SoundDefOf.Tick_High.PlayOneShotOnCamera(); this.InterfaceDrop(apparel); }; Action dropApparelHaul = delegate { SoundDefOf.Tick_High.PlayOneShotOnCamera(); this.InterfaceDropHaul(apparel); }; floatOptionList.Add(new FloatMenuOption("DropThing".Translate(), dropApparel)); floatOptionList.Add(new FloatMenuOption("DropThingHaul".Translate(), dropApparelHaul)); } FloatMenu window = new FloatMenu(floatOptionList, string.Empty); Find.WindowStack.Add(window); } } if (apparel.def.DrawMatSingle != null && apparel.def.DrawMatSingle.mainTexture != null) { Widgets.ThingIcon(new Rect(4f, y + 5f, ThingIconSize, ThingIconSize), apparel); } Text.Anchor = TextAnchor.MiddleLeft; GUI.color = ThingLabelColor; Rect textRect = new Rect(ThingLeftX, y, width - ThingLeftX, ThingRowHeight - Text.LineHeight); Rect scoreRect = new Rect(ThingLeftX, textRect.yMax, width - ThingLeftX, Text.LineHeight); ApparelStatCache conf = this.SelPawn.GetApparelStatCache(); string text = apparel.LabelCap; string textScore = Math.Round(conf.ApparelScoreRaw(apparel), 2).ToString("N2"); if (apparel is Apparel && this.SelPawn.outfits != null && this.SelPawn.outfits.forcedHandler.IsForced(apparel)) { text = text + ", " + "ApparelForcedLower".Translate(); Widgets.Label(textRect, text); } else { GUI.color = new Color(0.75f, 0.75f, 0.75f); if (apparel.def.useHitPoints) { float x = apparel.HitPoints / (float)apparel.MaxHitPoints; if (x < 0.5f) { GUI.color = Color.yellow; } if (x < 0.2f) { GUI.color = Color.red; } } Widgets.Label(textRect, text); GUI.color = Color.white; Widgets.Label(scoreRect, textScore); } y += ThingRowHeight; }
protected override void FillTab() { // main canvas Rect canvas = new Rect(0f, 0f, size.x, size.y).ContractedBy(20f); GUI.BeginGroup(canvas); Vector2 cur = Vector2.zero; // header Rect tempHeaderRect = new Rect(cur.x, cur.y, canvas.width, 30f); cur.y += 30f; Text.Anchor = TextAnchor.LowerLeft; Widgets.Label(tempHeaderRect, "PreferedTemperature".Translate()); Text.Anchor = TextAnchor.UpperLeft; // line GUI.color = Color.grey; Widgets.DrawLineHorizontal(cur.x, cur.y, canvas.width); GUI.color = Color.white; // some padding cur.y += 10f; // temperature slider ApparelStatCache pawnStatCache = SelPawn.GetApparelStatCache(); FloatRange targetTemps = pawnStatCache.TargetTemperatures; FloatRange minMaxTemps = ApparelStatsHelper.MinMaxTemperatureRange; Rect sliderRect = new Rect(cur.x, cur.y, canvas.width - 20f, 40f); Rect tempResetRect = new Rect(sliderRect.xMax + 4f, cur.y + 10f, 16f, 16f); cur.y += 60f; // includes padding // current temperature settings GUI.color = pawnStatCache.targetTemperaturesOverride ? Color.white : Color.grey; Widgets_FloatRange.FloatRange(sliderRect, 123123123, ref targetTemps, minMaxTemps, ToStringStyle.Temperature); GUI.color = Color.white; if (Math.Abs(targetTemps.min - SelPawn.GetApparelStatCache().TargetTemperatures.min) > 1e-4 || Math.Abs(targetTemps.max - SelPawn.GetApparelStatCache().TargetTemperatures.max) > 1e-4) { SelPawn.GetApparelStatCache().TargetTemperatures = targetTemps; } if (pawnStatCache.targetTemperaturesOverride) { if (Widgets.ImageButton(tempResetRect, resetButton)) { pawnStatCache.targetTemperaturesOverride = false; pawnStatCache.UpdateTemperatureIfNecessary(true); } TooltipHandler.TipRegion(tempResetRect, "TemperatureRangeReset".Translate()); } // header Rect statsHeaderRect = new Rect(cur.x, cur.y, canvas.width, 30f); cur.y += 30f; Text.Anchor = TextAnchor.LowerLeft; Text.Font = GameFont.Small; Widgets.Label(statsHeaderRect, "PreferredStats".Translate()); Text.Anchor = TextAnchor.UpperLeft; // add button Rect addStatRect = new Rect(statsHeaderRect.xMax - 16f, statsHeaderRect.yMin + 10f, 16f, 16f); if (Widgets.ImageButton(addStatRect, addButton)) { List <FloatMenuOption> options = new List <FloatMenuOption>(); foreach (StatDef def in SelPawn.NotYetAssignedStatDefs()) { options.Add(new FloatMenuOption(def.LabelCap, delegate { SelPawn.GetApparelStatCache() .StatCache.Insert(0, new ApparelStatCache.StatPriority(def, 0f, StatAssignment.Manual)); })); } Find.WindowStack.Add(new FloatMenu(options)); } TooltipHandler.TipRegion(addStatRect, "StatPriorityAdd".Translate()); // line GUI.color = Color.grey; Widgets.DrawLineHorizontal(cur.x, cur.y, canvas.width); GUI.color = Color.white; // some padding cur.y += 10f; // main content in scrolling view Rect contentRect = new Rect(cur.x, cur.y, canvas.width, canvas.height - cur.y); Rect viewRect = contentRect; viewRect.height = SelPawn.GetApparelStatCache().StatCache.Count * 30f + 10f; if (viewRect.height > contentRect.height) { viewRect.width -= 20f; } Widgets.BeginScrollView(contentRect, ref _scrollPosition, viewRect); GUI.BeginGroup(viewRect); cur = Vector2.zero; // none label if (!SelPawn.GetApparelStatCache().StatCache.Any()) { Rect noneLabel = new Rect(cur.x, cur.y, viewRect.width, 30f); GUI.color = Color.grey; Text.Anchor = TextAnchor.MiddleCenter; Widgets.Label(noneLabel, "None".Translate()); Text.Anchor = TextAnchor.UpperLeft; GUI.color = Color.white; cur.y += 30f; } else { // legend kind of thingy. Rect legendRect = new Rect(cur.x + (viewRect.width - 24) / 2, cur.y, (viewRect.width - 24) / 2, 20f); Text.Font = GameFont.Tiny; GUI.color = Color.grey; Text.Anchor = TextAnchor.LowerLeft; Widgets.Label(legendRect, "-10"); Text.Anchor = TextAnchor.LowerRight; Widgets.Label(legendRect, "10"); Text.Anchor = TextAnchor.UpperLeft; Text.Font = GameFont.Small; GUI.color = Color.white; cur.y += 15f; // stat weight sliders foreach (ApparelStatCache.StatPriority stat in SelPawn.GetApparelStatCache().StatCache) { bool stop_UI; ApparelStatCache.DrawStatRow(ref cur, viewRect.width, stat, SelPawn, out stop_UI); if (stop_UI) { // DrawStatRow can change the StatCache, invalidating the loop. So if it does that, stop looping - we'll redraw on the next tick. break; } } } GUI.EndGroup(); Widgets.EndScrollView(); GUI.EndGroup(); }