static void DrawCloneButton(ExtendedOutfit selectedOutfit) { Rect rect = new Rect(480, 0f, 150f, 35f); if (Widgets.ButtonText(rect, "CommandCopyZoneSettingsLabel".Translate(), true, true, true)) { if (selectedOutfit == null) { Messages.Message("NoOutfitSelected".Translate(), MessageTypeDefOf.RejectInput, false); return; } List <FloatMenuOption> list2 = new List <FloatMenuOption>(); foreach (Outfit outfit in Current.Game.outfitDatabase.AllOutfits) { if (outfit == selectedOutfit) { continue; } list2.Add(new FloatMenuOption(outfit.label, () => selectedOutfit.CopyFrom((ExtendedOutfit)outfit), MenuOptionPriority.Default, null, null, 0f, null, null)); } Find.WindowStack.Add(new FloatMenu(list2)); } }
static void DrawTemperatureStats(ExtendedOutfit selectedOutfit, 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, ResourceBank.Strings.PreferedTemperature); 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 += marginVertical; // temperature slider Rect sliderRect = new Rect(cur.x, cur.y, canvas.width - 20f, 40f); Rect tempResetRect = new Rect(sliderRect.xMax + 4f, cur.y + marginVertical, 16f, 16f); cur.y += 40f; // includes padding FloatRange targetTemps; if (selectedOutfit.targetTemperaturesOverride) { targetTemps = selectedOutfit.targetTemperatures; GUI.color = Color.white; } else { targetTemps = MinMaxTemperatureRange; GUI.color = Color.grey; } FloatRange minMaxTemps = MinMaxTemperatureRange; Widgets_FloatRange.FloatRange(sliderRect, 123123123, ref targetTemps, minMaxTemps, ToStringStyle.Temperature); GUI.color = Color.white; if (Math.Abs(targetTemps.min - selectedOutfit.targetTemperatures.min) > 1e-4 || Math.Abs(targetTemps.max - selectedOutfit.targetTemperatures.max) > 1e-4) { selectedOutfit.targetTemperatures = targetTemps; selectedOutfit.targetTemperaturesOverride = true; } if (selectedOutfit.targetTemperaturesOverride) { if (Widgets.ButtonImage(tempResetRect, ResourceBank.Textures.ResetButton)) { selectedOutfit.targetTemperaturesOverride = false; selectedOutfit.targetTemperatures = MinMaxTemperatureRange; } TooltipHandler.TipRegion(tempResetRect, ResourceBank.Strings.TemperatureRangeReset); } }
static void DrawAutoWorkPrioritiesToggle(ExtendedOutfit outfit, ref Vector2 pos, Rect canvas) { Rect rect = new Rect(pos.x, pos.y, canvas.width, 30f); Widgets.CheckboxLabeled(rect, ResourceBank.Strings.AutoWorkPriorities, ref outfit.AutoWorkPriorities); TooltipHandler.TipRegion(rect, ResourceBank.Strings.AutoWorkPrioritiesTooltip); pos.y += rect.height; }
static void DrawDeadmanToogle(ExtendedOutfit selectedOutfit, ref Vector2 cur, Rect canvas) { Rect rect = new Rect(cur.x, cur.y, canvas.width, 30f); Widgets.CheckboxLabeled(rect, ResourceBank.Strings.PenaltyWornByCorpse, ref selectedOutfit.PenaltyWornByCorpse); TooltipHandler.TipRegion(rect, ResourceBank.Strings.PenaltyWornByCorpseTooltip); cur.y += rect.height; }
public static void Watch(ref ExtendedOutfit outfit) { selectedOutfitId = outfit.uniqueId; targetTemperaturesOverride.Watch(outfit); targetTemperatures.Watch(outfit); PenaltyWornByCorpse.Watch(outfit); AutoWorkPriorities.Watch(outfit); selectedStatPrioritySF.Watch(Instance); }
static void DrawAutoTempOffsetInput(ExtendedOutfit outfit, ref Vector2 pos, Rect canvas) { Rect rect = new Rect(pos.x, pos.y, canvas.width, 30f); //TODO: input label var autoTempOffsetString = outfit.autoTempOffset.ToString(); Widgets.IntEntry(rect, ref outfit.autoTempOffset, ref autoTempOffsetString); TooltipHandler.TipRegion(rect, ResourceBank.Strings.AutoTempOffsetTooltip); pos.y += rect.height; }
static void DrawAutoTempToggle(ExtendedOutfit outfit, ref Vector2 pos, Rect canvas) { Rect rect = new Rect(pos.x, pos.y, canvas.width, 30f); var autoTemp = outfit.AutoTemp; Widgets.CheckboxLabeled(rect, ResourceBank.Strings.AutoTemp, ref autoTemp); outfit.AutoTemp = autoTemp; TooltipHandler.TipRegion(rect, ResourceBank.Strings.AutoTempTooltip); pos.y += rect.height; }
public void CopyFrom(ExtendedOutfit outfit) { filter.CopyAllowancesFrom(outfit.filter); targetTemperaturesOverride = outfit.targetTemperaturesOverride; targetTemperatures = outfit.targetTemperatures; PenaltyWornByCorpse = outfit.PenaltyWornByCorpse; statPriorities.Clear(); statPriorities.AddRange(outfit.statPriorities); AutoWorkPriorities = outfit.AutoWorkPriorities; _autoTemp = outfit._autoTemp; autoTempOffset = outfit.autoTempOffset; }
static float ApparelScoreRawPriorities(Apparel apparel, ExtendedOutfit outfit) { if (outfit?.StatPriorities.Any() != true) { return(0f); } return(outfit.StatPriorities .Select(sp => new { weight = sp.Weight, value = apparel.def.equippedStatOffsets.GetStatOffsetFromList(sp.Stat) + apparel.GetStatValue(sp.Stat), def = sp.Stat.defaultBaseValue, }) .Average(sp => (Math.Abs(sp.def) < 0.001f ? sp.value : (sp.value - sp.def) / sp.def) * Mathf.Pow(sp.weight, 3))); }
static void ExtendedOutfitSyncer(SyncWorker sync, ref ExtendedOutfit outfit) { if (sync.isWriting) { sync.Bind(ref outfit.uniqueId); } else { int uid = 0; sync.Bind(ref uid); var currentOutfit = Current.Game.outfitDatabase.AllOutfits.Find(o => o.uniqueId == uid); if (currentOutfit is ExtendedOutfit extendedOutfit) { outfit = extendedOutfit; } } }
public static void Watch(ref ExtendedOutfit outfit) { ProxyFields.Watch(); ExtendedOutfitFields.Watch(outfit); }
static float ApparelScoreRawInsulation(Pawn pawn, Apparel apparel, ExtendedOutfit outfit, NeededWarmth neededWarmth) { float insulation; if (outfit.targetTemperaturesOverride) { // NOTE: We can't rely on the vanilla check for taking off gear for temperature, because // we need to consider all the wardrobe changes taken together; each individual change may // note push us over the thresholds, but several changes together may. // Return 1 for temperature offsets here, we'll look at the effects of any gear we have to // take off below. // NOTE: This is still suboptimal, because we're still only considering one piece of apparel // to wear at each time. A better solution would be reducing the problem to a series of linear // equations, and then solving that system. // I'm not sure that's feasible at all; first off for simple computational reasons: the linear // system to solve would be fairly massive, optimizing for dozens of pawns and hundreds of pieces // of gear simultaneously. Second, many of the stat functions aren't actually linear, and would // have to be made to be linear. bool currentlyWorn = pawn.apparel.WornApparel.Contains(apparel); var currentRange = pawn.ComfortableTemperatureRange(); var candidateRange = currentRange; if (outfit.AutoTemp) { var seasonalTemp = pawn.Map.mapTemperature.SeasonalTemp; outfit.targetTemperatures = new FloatRange(seasonalTemp - outfit.autoTempOffset, seasonalTemp + outfit.autoTempOffset); } var targetRange = outfit.targetTemperatures; var apparelOffset = GetInsulationStats(apparel); // effect of this piece of apparel candidateRange.min += apparelOffset.min; candidateRange.max += apparelOffset.max; if (!currentlyWorn) { foreach (var otherApparel in pawn.apparel.WornApparel) { // effect of taking off any other apparel that is incompatible if (!ApparelUtility.CanWearTogether(apparel.def, otherApparel.def, pawn.RaceProps.body)) { var otherInsulationRange = GetInsulationStats(otherApparel); candidateRange.min -= otherInsulationRange.min; candidateRange.max -= otherInsulationRange.max; } } } // did we get any closer to our target range? (smaller distance is better, negative values are overkill). var currentDistance = new FloatRange(Mathf.Max(currentRange.min - targetRange.min, 0f), Mathf.Max(targetRange.max - currentRange.max, 0f)); var candidateDistance = new FloatRange(Mathf.Max(candidateRange.min - targetRange.min, 0f), Mathf.Max(targetRange.max - candidateRange.max, 0f)); // improvement in distances insulation = InsulationFactorCurve.Evaluate(currentDistance.min - candidateDistance.min) + InsulationFactorCurve.Evaluate(currentDistance.max - candidateDistance.max); #if DEBUG Log.Message($"{pawn.Name.ToStringShort} :: {apparel.LabelCap}\n" + $"\ttarget range: {targetRange}, current range: {currentRange}, candidate range {candidateRange}\n" + $"\tcurrent distance: {currentDistance}, candidate distance: {candidateDistance}\n" + $"\timprovement: {(currentDistance.min - candidateDistance.min) + (currentDistance.max - candidateDistance.max)}, insulation score: {insulation}\n"); #endif } else { float statValue; if (neededWarmth == NeededWarmth.Warm) { statValue = apparel.GetStatValue(StatDefOf.Insulation_Cold, true); insulation = InsulationTemperatureScoreFactorCurve_Need.Evaluate(statValue); } else if (neededWarmth == NeededWarmth.Cool) { statValue = apparel.GetStatValue(StatDefOf.Insulation_Heat, true); insulation = InsulationTemperatureScoreFactorCurve_Need.Evaluate(statValue); } else { insulation = 1f; } } return(insulation); }
static void DrawStatRow(ExtendedOutfit selectedOutfit, StatPriority statPriority, ref Vector2 cur, float width) { // set up rects Rect labelRect = new Rect(cur.x, cur.y, (width - 24) / 2f, 30f); Rect sliderRect = new Rect(labelRect.xMax + 4f, cur.y + 5f, labelRect.width, 25f); Rect buttonRect = new Rect(sliderRect.xMax + 4f, cur.y + 3f, 16f, 16f); // draw label Text.Font = Text.CalcHeight(statPriority.Stat.LabelCap, labelRect.width) > labelRect.height ? GameFont.Tiny : GameFont.Small; GUI.color = AssigmentColor(statPriority); Widgets.Label(labelRect, statPriority.Stat.LabelCap); Text.Font = GameFont.Small; // draw button // if manually added, delete the priority string buttonTooltip = string.Empty; if (statPriority.IsManual) { buttonTooltip = ResourceBank.Strings.StatPriorityDelete(statPriority.Stat.LabelCap); if (Widgets.ButtonImage(buttonRect, ResourceBank.Textures.DeleteButton)) { selectedOutfit.RemoveStatPriority(statPriority.Stat); } } // if overridden auto assignment, reset to auto else if (statPriority.IsOverride) { buttonTooltip = ResourceBank.Strings.StatPriorityReset(statPriority.Stat.LabelCap); if (Widgets.ButtonImage(buttonRect, ResourceBank.Textures.ResetButton)) { statPriority.Weight = statPriority.Default; if (MPApi.IsInMultiplayer) { ExtendedOutfitProxy.SetStatPriority(statPriority.Stat, statPriority.Default); } } } // draw line behind slider GUI.color = new Color(.3f, .3f, .3f); for (int y = (int)cur.y; y < cur.y + 30; y += 5) { Widgets.DrawLineVertical((sliderRect.xMin + sliderRect.xMax) / 2f, y, 3f); } // draw slider GUI.color = AssigmentColor(statPriority); float weight = GUI.HorizontalSlider(sliderRect, statPriority.Weight, -MaxValue, MaxValue); if (Mathf.Abs(weight - statPriority.Weight) > 1e-4) { statPriority.Weight = weight; if (MPApi.IsInMultiplayer) { ExtendedOutfitProxy.SetStatPriority(statPriority.Stat, weight); } } GUI.color = Color.white; // tooltips TooltipHandler.TipRegion(labelRect, statPriority.Stat.LabelCap + "\n\n" + statPriority.Stat.description); if (buttonTooltip != string.Empty) { TooltipHandler.TipRegion(buttonRect, buttonTooltip); } TooltipHandler.TipRegion(sliderRect, statPriority.Weight.ToStringByStyle(ToStringStyle.FloatTwo)); // advance row cur.y += 30f; }
static void DrawApparelStats(ExtendedOutfit selectedOutfit, Vector2 cur, Rect canvas) { // 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, ResourceBank.Strings.PreferedStats); Text.Anchor = TextAnchor.UpperLeft; // add button Rect addStatRect = new Rect(statsHeaderRect.xMax - 16f, statsHeaderRect.yMin + marginVertical, 16f, 16f); if (Widgets.ButtonImage(addStatRect, ResourceBank.Textures.AddButton)) { var options = new List <FloatMenuOption>(); foreach (var def in selectedOutfit.UnassignedStats.OrderBy(i => i.label).OrderBy(i => i.category.displayOrder)) { FloatMenuOption option = new FloatMenuOption(def.LabelCap, delegate { selectedOutfit.AddStatPriority(def, 0f); }); options.Add(option); } Find.WindowStack.Add(new FloatMenu(options)); } TooltipHandler.TipRegion(addStatRect, ResourceBank.Strings.StatPriorityAdd); // line GUI.color = Color.grey; Widgets.DrawLineHorizontal(cur.x, cur.y, canvas.width); GUI.color = Color.white; // some padding cur.y += marginVertical; var stats = selectedOutfit.StatPriorities.ToList(); // main content in scrolling view Rect contentRect = new Rect(cur.x, cur.y, canvas.width, canvas.height - cur.y); Rect viewRect = new Rect(contentRect) { height = 30f * stats.Count }; if (viewRect.height > contentRect.height) { viewRect.width -= 20f; } Widgets.BeginScrollView(contentRect, ref scrollPosition, viewRect); GUI.BeginGroup(viewRect); cur = Vector2.zero; // none label if (stats.Count > 0) { // 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, "-" + MaxValue.ToString("N1")); Text.Anchor = TextAnchor.LowerRight; Widgets.Label(legendRect, MaxValue.ToString("N1")); Text.Anchor = TextAnchor.UpperLeft; Text.Font = GameFont.Small; GUI.color = Color.white; cur.y += 15f; // statPriority weight sliders foreach (var stat in stats) { DrawStatRow(selectedOutfit, stat, ref cur, viewRect.width); } } else { Rect noneLabel = new Rect(cur.x, cur.y, viewRect.width, 30f); GUI.color = Color.grey; Text.Anchor = TextAnchor.MiddleCenter; Widgets.Label(noneLabel, ResourceBank.Strings.None); Text.Anchor = TextAnchor.UpperLeft; GUI.color = Color.white; cur.y += 30f; } GUI.EndGroup(); Widgets.EndScrollView(); }