Exemplo n.º 1
0
 void AddAllMutations()
 {
     MutationUtilities.AddMutation(_pawn, _mutationDef);
 }
Exemplo n.º 2
0
        private void DrawPartButtons(ref float curY, Rect partListViewRect, List <Hediff_AddedMutation> mutations, List <BodyPartRecord> parts, MutationLayer layer, string label)
        {
            // Draw the main mutation selection button. It should take up the whole width if there are no mutations, otherwise it will leave a space for the edit button.
            float partButtonWidth = partListViewRect.width - (mutations.NullOrEmpty() ? 0 : editButtonWidth);
            Rect  partButtonRect  = new Rect(partListViewRect.x, curY, partButtonWidth, Text.CalcHeight(label, partButtonWidth - BUTTON_HORIZONTAL_PADDING));

            if (Widgets.ButtonText(partButtonRect, label))
            {
                List <FloatMenuOption> options = new List <FloatMenuOption>();
                void removeMutations()
                {
                    foreach (Hediff_AddedMutation mutation in mutations)
                    {
                        addedMutations.RemoveByPartAndLayer(mutation.Part, layer);
                        if (cachedInitialHediffs.Select(m => m.hediff).Contains(mutation))
                        {
                            addedMutations.AddData(mutation.Def, mutation.Part, mutation.Severity, mutation.ProgressionHalted, true);
                        }
                        pawn.health.RemoveHediff(mutation);
                    }
                    recachePreview = true;
                    RecachePawnMutations();
                }

                options.Add(new FloatMenuOption(NO_MUTATIONS_LOC_STRING.Translate(), removeMutations));


                List <MutationDef> mutationDefs = cachedMutationDefsByPartDef[parts.FirstOrDefault().def];
                foreach (MutationDef mutationDef in mutationDefs.Where(m => m.RemoveComp.layer == layer && (DebugSettings.godMode || m.IsTagged())))
                {
                    void addMutation()
                    {
                        foreach (Hediff_AddedMutation mutation in mutations)
                        {
                            pawn.health.RemoveHediff(mutation);
                        }
                        foreach (BodyPartRecord part in parts)
                        {
                            addedMutations.RemoveByPartAndLayer(part, layer);
                            addedMutations.AddData(mutationDef, part, mutationDef.initialSeverity, false, false);
                            MutationUtilities.AddMutation(pawn, mutationDef, part, ancillaryEffects: MutationUtilities.AncillaryMutationEffects.None); //don't give the green puffs
                        }
                        recachePreview = true;
                        RecachePawnMutations();
                    }

                    options.Add(new FloatMenuOption(mutationDef.LabelCap, addMutation));
                }
                Find.WindowStack.Add(new FloatMenu(options));
            }
            curY += partButtonRect.height;

            // If there are actually mutations, draw the edit button.
            if (!mutations.NullOrEmpty())
            {
                Rect editButtonRect = new Rect(partButtonWidth, partButtonRect.y, editButtonWidth, partButtonRect.height);
                if (Widgets.ButtonText(editButtonRect, editButtonText))
                {
                    detailPart = (detailPart.Item1 == parts.FirstOrDefault() && detailPart.Item2 == layer) ? new Tuple <BodyPartRecord, MutationLayer>(new BodyPartRecord(), 0) : new Tuple <BodyPartRecord, MutationLayer>(parts.FirstOrDefault(), layer);
                }
            }

            // If the currently selected part and layer match up with the part to give details for, draw the edit area below the buttons.
            if (detailPart.Item1 == parts.FirstOrDefault() && detailPart.Item2 == layer)
            {
                foreach (MutationDef mutationDef in mutations.Select(m => m.Def).Distinct())
                {
                    List <Hediff_AddedMutation> mutationsOfDef = mutations.Where(m => m.Def == mutationDef).ToList();

                    // Draw the LabelCap of the current Def if there is more than one type of mutation in the current list.
                    if (mutations.Select(m => m.Def).Distinct().Count() > 1)
                    {
                        Widgets.ListSeparator(ref curY, partListViewRect.width, mutationDef.LabelCap);
                    }

                    if (mutationDef.HasComp(typeof(Comp_MutationSeverityAdjust)))
                    {
                        DrawSeverityBar(ref curY, ref partListViewRect, layer, mutationDef, mutationsOfDef);
                    }

                    // If the mutation has the ability to be paused, show the toggle for it.
                    // This is a CheckboxMulti to handle edge cases, but likely could be replaced with a simple Checkbox.
                    if (mutationDef.CompProps <CompProperties_MutationSeverityAdjust>() != null)
                    {
                        float pauseLabelWidth           = partListViewRect.width - IS_PAUSED_CHECKBOX_SIZE.x;
                        Rect  pauseLabelRect            = new Rect(partListViewRect.x, curY, pauseLabelWidth, Text.CalcHeight(IS_PAUSED_LOC_STRING.Translate(), partListViewRect.width));
                        Rect  checkBoxRect              = new Rect(partListViewRect.x + pauseLabelWidth, curY, IS_PAUSED_CHECKBOX_SIZE.x, IS_PAUSED_CHECKBOX_SIZE.y);
                        MultiCheckboxState initialState = !mutationsOfDef.Select(n => n.ProgressionHalted).Contains(true) ? MultiCheckboxState.Off : !mutationsOfDef.Select(n => n.ProgressionHalted).Contains(false) ? MultiCheckboxState.On : MultiCheckboxState.Partial;
                        Widgets.Label(pauseLabelRect, IS_PAUSED_LOC_STRING.Translate());
                        MultiCheckboxState newState = Widgets.CheckboxMulti(checkBoxRect, initialState);
                        if (initialState != newState)
                        {
                            initialState = newState;
                            mutationsOfDef.FirstOrDefault().SeverityAdjust.Halted = !mutationsOfDef.FirstOrDefault().SeverityAdjust.Halted;
                            foreach (Hediff_AddedMutation mutationOfDef in mutationsOfDef)
                            {
                                MutationData relevantEntry = addedMutations.MutationsByPartAndLayer(mutationOfDef.Part, layer);
                                if (cachedInitialHediffs.Select(m => m.hediff).Contains(mutationOfDef))
                                {
                                    bool initialHediffIsHalted = cachedInitialHediffs.Where(m => m.hediff == mutationOfDef).FirstOrDefault().isHalted;
                                    if (newState == MultiCheckboxState.On == initialHediffIsHalted)
                                    {
                                        addedMutations.RemoveByPartAndLayer(mutationOfDef.Part, layer);
                                    }
                                }
                                if (relevantEntry != null)
                                {
                                    relevantEntry.isHalted = newState == MultiCheckboxState.On;
                                }
                                else
                                {
                                    addedMutations.AddData(mutationOfDef.Def, mutationOfDef.Part, mutationOfDef.Severity, newState == MultiCheckboxState.On, false);
                                }
                            }
                        }
                        curY += Math.Max(pauseLabelRect.height, checkBoxRect.height);
                    }
                }
            }

            // Create a zone for updating the lower description box (The one that shows details based on the currently hovered over mutation).
            Rect descriptionUpdateRect = new Rect(partListViewRect.x, partButtonRect.y, partListViewRect.width, curY - partButtonRect.y);

            if (Mouse.IsOver(descriptionUpdateRect))
            {
                foreach (MutationDef mutation in mutations.Select(m => m.def).Distinct())
                {
                    Hediff_AddedMutation firstMutationOfDef = mutations.Where(m => m.def == mutation).FirstOrDefault();
                    partDescBuilder.AppendLine(firstMutationOfDef.LabelCap);
                    partDescBuilder.AppendLine(firstMutationOfDef.Description);
                    partDescBuilder.AppendLine(firstMutationOfDef.TipStringExtra);
                    partDescBuilder.AppendLine();
                }
            }
        }
Exemplo n.º 3
0
        private static void ApplyMutations([NotNull] Pawn pawn, bool canApplyRestricted, bool setAtMaxStage,
                                           [NotNull] MorphPawnKindExtension kindExtension)
        {
            List <MutationDef> mutations;
            var addedPartsSet = new HashSet <BodyPartDef>();

            if (!canApplyRestricted)
            {
                canApplyRestricted = pawn.CanReceiveRareMutations();
            }


            if (canApplyRestricted)
            {
                mutations = kindExtension.GetRandomMutations(pawn.thingIDNumber).ToList();
            }
            else
            {
                mutations = kindExtension.GetRandomMutations(pawn.thingIDNumber)
                            .Where(g => !g.IsRestricted)              //only keep the unrestricted mutations
                            .ToList();
            }

            if (mutations.Count == 0)
            {
                Warning($"could not get any mutations for {pawn.Name} using extension\n{kindExtension.ToStringFull()}");
            }

            var toGive    = new List <MutationDef>();
            var addedList = new List <BodyPartRecord>();

            int toGiveCount = kindExtension.hediffRange.RandomInRange; //get a random number of mutations to add
            int max         = Mathf.Min(mutations.Count, toGiveCount);
            var i           = 0;

            while (i < max)
            {
                if (mutations.Count == 0)
                {
                    break;
                }
                while (true)
                {
                    if (mutations.Count == 0)
                    {
                        break;
                    }
                    int         rI     = Rand.Range(0, mutations.Count);
                    MutationDef mGiver = mutations[rI];

                    mutations.RemoveAt(rI); //remove the entry so we don't pull duplicates
                    if (mGiver.parts.Any(p => p != null && addedPartsSet.Contains(p))
                        )                   //make sure its for a part we haven't encountered yet
                    {
                        continue;
                    }

                    foreach (BodyPartDef part in mGiver.parts)
                    {
                        addedPartsSet.Add(part);
                    }
                    toGive.Add(mGiver);
                    i++;  //only count 1 regardless of what was added
                    break;
                }
            }


            foreach (MutationDef giver in toGive)
            {
                giver.ClearOverlappingMutations(pawn); // make sure to remove any overlapping hediffs added during a different stage

                var result = MutationUtilities.AddMutation(pawn, giver, int.MaxValue, MutationUtilities.AncillaryMutationEffects.None);
                addedList.AddRange(result.Parts);
            }

            if (toGive.Count > 0 && (addedList.Count == 0 || !pawn.health.hediffSet.hediffs.OfType <Hediff_AddedMutation>().Any()))
            {
                LogMsg(LogLevel.Warnings, $"could not add mutations to pawn {pawn.Name} with ext\n{kindExtension}");
            }

            if (setAtMaxStage)
            {
                var addedMutations = new List <Hediff_AddedMutation>();
                List <Hediff_AddedMutation> allMutationsOnPawn =
                    pawn.health.hediffSet.hediffs.OfType <Hediff_AddedMutation>().ToList(); //save these
                foreach (BodyPartRecord bodyPartRecord in addedList)                        //get a list of all mutations we just added
                {
                    foreach (Hediff_AddedMutation mutation in allMutationsOnPawn)
                    {
                        if (mutation.Part == bodyPartRecord && toGive.Contains(mutation.def as MutationDef))
                        {
                            if (!addedMutations.Contains(mutation))
                            {
                                addedMutations.Add(mutation);
                            }
                        }
                    }
                }


                foreach (Hediff_AddedMutation hediff in addedMutations)
                {
                    if (hediff.pawn == null)
                    {
                        continue;                      //sometimes the hediffs are removed by other mutations
                    }
                    if (hediff.TryGetComp <HediffComp_Production>() != null)
                    {
                        continue;                                                     //do not increase production hediffs
                    }
                    var comp = hediff.TryGetComp <Comp_MutationSeverityAdjust>();
                    if (comp != null)
                    {
                        hediff.Severity = comp.NaturalSeverityLimit;
                        continue;
                    }

                    HediffStage lastStage = hediff.def.stages?.LastOrDefault();
                    if (lastStage == null)
                    {
                        continue;
                    }

                    float severity = lastStage.minSeverity + 0.01f;
                    hediff.Severity = severity;
                }
            }


            pawn.CheckRace(false); //don't apply missing mutations to avoid giving restricted mutations and to respect the limit
        }
Exemplo n.º 4
0
        private void AddPartMutations()
        {
            var mutationsAdded = 0;

            while (_curIndex < _checkList.Count)
            {
                BodyPartRecord part = _checkList[_curIndex];
                if (!pawn.RaceProps.body.AllParts.Contains(part))
                {
                    //if the pawn's race changes the mutation order may no longer be valid
                    //need to reset it and try again later
                    break;
                }

                var lst = _scratchDict.TryGetValue(part.def);

                if (lst == null) //end check early if there are no parts that can be added
                {
                    _curIndex++;
                    _curMutationIndex = 0;
                    continue;
                }
                //make sure we try each mutation per part

                while (_curMutationIndex < lst.Count)
                {
                    var mutation = lst[_curMutationIndex];

                    //check if the mutation can actually be added
                    if (!mutation.mutation.CanApplyMutations(pawn, part))
                    {
                        _curMutationIndex++;
                        continue;
                    }
                    else if (Rand.Value < mutation.addChance)
                    {
                        var result = MutationUtilities.AddMutation(pawn, mutation.mutation, part);
                        if (result) //make sure the mutation was actually added before doing this
                        {
                            mutationsAdded = NewMethod(mutationsAdded);
                        }
                    }
                    else if (mutation.blocks)
                    {
                        return; //wait here until the blocking mutation is added
                    }

                    _curMutationIndex++;

                    //check if we added enough mutations to break early
                    if (mutationsAdded >= MinMutationsPerCheck)
                    {
                        goto loopBreak;//break both loops
                    }
                }

                _curIndex++;           //increment after so mutations can 'block'
                _curMutationIndex = 0; //reset the mutation index every time we move up a part

                //check if we have added enough mutations to end the loop early
                if (mutationsAdded >= MinMutationsPerCheck)
                {
                    break;
                }
            }

loopBreak:  //label so we can break both loops

            if (mutationsAdded > 0)
            {
                OnMutationsAdded(mutationsAdded);
            }
        }
Exemplo n.º 5
0
        public override void DoWindowContents(Rect inRect)
        {
            // Step 1 - Gather and set relevent information.
            float col1, col2, col3;
            List <MutationDef> allMutations = DefDatabase <MutationDef> .AllDefs.ToList();

            List <BodyPartRecord>       mutableParts  = pawn.RaceProps.body.AllParts.Where(m => DefDatabase <MutationDef> .AllDefs.SelectMany(n => n.parts).Distinct().Contains(m.def)).ToList();
            List <Hediff_AddedMutation> pawnMutations = pawn.health.hediffSet.hediffs.Where(m => m.def.GetType() == typeof(MutationDef)).Cast <Hediff_AddedMutation>().ToList();

            // Step 2 - Draw the title of the window.
            Text.Font = GameFont.Medium;
            string title       = $"{WINDOW_TITLE_LOC_STRING.Translate()} - {pawn.Name.ToStringShort} ({pawn.def.LabelCap})";
            float  titleHeight = Text.CalcHeight(title, inRect.width);

            Widgets.Label(new Rect(inRect.x, inRect.y, inRect.width, Text.CalcHeight(title, inRect.width)), title);
            Text.Font = GameFont.Small;
            col1      = col2 = col3 = titleHeight;

            // Step 3 - Determine vewing areas for body part list and description.
            float drawableWidth        = (inRect.width - PREVIEW_SIZE.x - 2 * SPACER_SIZE) / 2;
            float drawableHeight       = inRect.height - titleHeight - Math.Max(APPLY_BUTTON_SIZE.y, Math.Max(RESET_BUTTON_SIZE.y, CANCEL_BUTTON_SIZE.y)) - 2 * SPACER_SIZE;
            Rect  partListOutRect      = new Rect(inRect.x, titleHeight, drawableWidth, drawableHeight);
            Rect  partListViewRect     = new Rect(partListOutRect.x, partListOutRect.y, partListScrollSize.x, partListScrollSize.y - titleHeight);
            Rect  previewRect          = new Rect(inRect.x + SPACER_SIZE + drawableWidth, titleHeight, PREVIEW_SIZE.x, PREVIEW_SIZE.y);
            Rect  descriptionOutRect   = new Rect(inRect.x + 2 * SPACER_SIZE + PREVIEW_SIZE.x, titleHeight, drawableWidth, drawableHeight);
            Rect  descriptiontViewRect = new Rect(descriptionOutRect.x, descriptionOutRect.y, descriptionOutRect.width - 16f, descriptionOutRect.height);

            // Step 4 - Draw the body part list, selection buttons and edit buttons.
            string editButtonText  = EDIT_PARAMS_LOC_STRING.Translate();
            float  editButtonWidth = Text.CalcSize(editButtonText).x + BUTTON_HORIZONTAL_PADDING;

            Widgets.BeginScrollView(partListOutRect, ref partListScrollPos, partListViewRect);
            if (doSymmetry)
            {
                List <BodyPartDef> uniqueMutablePartDefs = mutableParts.Select(m => m.def).Distinct().ToList();
                foreach (BodyPartDef part in uniqueMutablePartDefs)
                {
                    List <Hediff_AddedMutation> mutationsOnPart = pawnMutations.Where(m => m.Part.def == part).ToList();
                    string text       = part.LabelCap;
                    float  textHeight = Text.CalcHeight(text, partListViewRect.width);
                    Widgets.Label(new Rect(0f, col1, partListViewRect.width, textHeight), text);
                    col1 += textHeight;
                    foreach (MutationLayer layer in Enum.GetValues(typeof(MutationLayer)))
                    {
                        List <MutationDef> applicableMutations = allMutations.Where(m => m.parts.Contains(part) && m.comps.Find(n => n.GetType() == typeof(RemoveFromPartCompProperties)).ChangeType <RemoveFromPartCompProperties>().layer == layer).ToList();
                        if (!applicableMutations.NullOrEmpty())
                        {
                            List <Hediff_AddedMutation> mutationsOnLayer = mutationsOnPart.Where(m => m.TryGetComp <RemoveFromPartComp>().Layer == layer).ToList();
                            string buttonText   = $"{layer}: {(mutationsOnLayer.NullOrEmpty() ? NO_MUTATIONS_LOC_STRING.Translate().ToString() : string.Join(", ", mutationsOnLayer.Select(m => m.LabelCap).Distinct()))}";
                            float  buttonHeight = Text.CalcHeight(buttonText, partListViewRect.width);
                            if (Widgets.ButtonText(new Rect(0f, col1, partListViewRect.width - editButtonWidth, buttonHeight), buttonText))
                            {
                                List <FloatMenuOption> options = new List <FloatMenuOption>();
                                Action removeAction            = delegate()
                                {
                                    if (!mutationsOnLayer.NullOrEmpty())
                                    {
                                        foreach (Hediff_AddedMutation hediff in mutationsOnLayer)
                                        {
                                            pawn.health.RemoveHediff(hediff);
                                        }
                                    }
                                    forceRecachePreview = true;
                                };
                                options.Add(new FloatMenuOption(NO_MUTATIONS_LOC_STRING.Translate(), removeAction));
                                foreach (MutationDef mutationDef in applicableMutations)
                                {
                                    Action action = delegate()
                                    {
                                        if (!mutationsOnLayer.NullOrEmpty())
                                        {
                                            foreach (Hediff_AddedMutation hediff in mutationsOnLayer)
                                            {
                                                pawn.health.RemoveHediff(hediff);
                                            }
                                        }
                                        foreach (BodyPartRecord bpr in pawn.RaceProps.body.AllParts.Where(m => m.def == part))
                                        {
                                            MutationUtilities.AddMutation(pawn, mutationDef, bpr);
                                        }
                                        forceRecachePreview = true;
                                    };
                                    options.Add(new FloatMenuOption(mutationDef.LabelCap, action));
                                }
                                Find.WindowStack.Add(new FloatMenu(options));
                            }
                            if (Widgets.ButtonText(new Rect(partListViewRect.width - editButtonWidth, col1, editButtonWidth, buttonHeight), editButtonText))
                            {
                                // Edit the paramaters of the relevant mutations, such as current stage, if it's halted, etc. (Check for full list of what can be modified later)
                            }
                            col1 += buttonHeight;
                        }
                    }
                }
            }
            else
            {
                foreach (BodyPartRecord part in mutableParts)
                {
                    string text       = part.LabelCap;
                    float  textHeight = Text.CalcHeight(text, partListViewRect.width);
                    Widgets.Label(new Rect(0f, col1, partListViewRect.width, textHeight), text);
                    col1 += textHeight;
                    foreach (MutationLayer layer in Enum.GetValues(typeof(MutationLayer)))
                    {
                        List <MutationDef> applicableMutations = allMutations.Where(m => m.parts.Contains(part.def) && m.comps.Find(n => n.GetType() == typeof(RemoveFromPartCompProperties)).ChangeType <RemoveFromPartCompProperties>().layer == layer).ToList();
                        if (!applicableMutations.NullOrEmpty())
                        {
                            Hediff_AddedMutation mutationOnPartAndLayer = pawnMutations.Find(m => m.TryGetComp <Hediffs.RemoveFromPartComp>().Layer == layer && m.Part == part);
                            string buttonText   = $"{layer}: {(mutationOnPartAndLayer == null ? NO_MUTATIONS_LOC_STRING.Translate().ToString() : mutationOnPartAndLayer.LabelCap)}";
                            float  buttonHeight = Text.CalcHeight(buttonText, partListViewRect.width);
                            if (Widgets.ButtonText(new Rect(0f, col1, partListViewRect.width - editButtonWidth, buttonHeight), buttonText))
                            {
                                List <FloatMenuOption> options = new List <FloatMenuOption>();
                                Action removeAction            = delegate()
                                {
                                    if (mutationOnPartAndLayer != null)
                                    {
                                        pawn.health.RemoveHediff(mutationOnPartAndLayer);
                                    }
                                    forceRecachePreview = true;
                                };
                                options.Add(new FloatMenuOption(NO_MUTATIONS_LOC_STRING.Translate(), removeAction));
                                foreach (MutationDef mutationDef in applicableMutations)
                                {
                                    Action action = delegate()
                                    {
                                        if (mutationOnPartAndLayer != null)
                                        {
                                            pawn.health.RemoveHediff(mutationOnPartAndLayer);
                                        }
                                        MutationUtilities.AddMutation(pawn, mutationDef, part);
                                        forceRecachePreview = true;
                                    };
                                    options.Add(new FloatMenuOption(mutationDef.LabelCap, action));
                                }
                                Find.WindowStack.Add(new FloatMenu(options));
                            }
                            if (Widgets.ButtonText(new Rect(partListViewRect.width - editButtonWidth, col1, editButtonWidth, buttonHeight), editButtonText))
                            {
                                // Edit the paramaters of the mutation on the current part and layer, such as its current stage, if it's halted, etc. (Check for full list of what can be modified later)
                            }
                            col1 += buttonHeight;
                        }
                    }
                }
            }
            if (Event.current.type == EventType.Layout)
            {
                partListScrollSize.x = partListOutRect.width - 16f;
                partListScrollSize.y = col1;
            }
            Widgets.EndScrollView();

            // Step 5 - Draw the preview area then rotation and clothes buttons then symmetry toggle.
            if (forceRecachePreview || previewImage == null)
            {
                setPawnPreview();
            }
            GUI.DrawTexture(previewRect, previewImage);
            col2 += previewRect.height;
            float rotCWHorPos          = previewRect.x + previewRect.width / 2 - TOGGLE_CLOTHES_BUTTON_SIZE.x / 2 - ROTATE_CW_BUTTON_SIZE.x - SPACER_SIZE;
            float toggleClothingHorPos = previewRect.x + previewRect.width / 2 - TOGGLE_CLOTHES_BUTTON_SIZE.x / 2;
            float rotCCWHorPos         = previewRect.x + previewRect.width / 2 + TOGGLE_CLOTHES_BUTTON_SIZE.x / 2 + SPACER_SIZE;

            col2 += SPACER_SIZE;
            if (Widgets.ButtonImageFitted(new Rect(rotCWHorPos, col2, ROTATE_CW_BUTTON_SIZE.x, ROTATE_CW_BUTTON_SIZE.y), ButtonTexturesPM.rotCW, Color.white, Color.blue))
            {
                previewRot.Rotate(RotationDirection.Clockwise);
                SoundDefOf.Tick_Tiny.PlayOneShotOnCamera();
                forceRecachePreview = true;
            }
            if (Widgets.ButtonImageFitted(new Rect(toggleClothingHorPos, col2, TOGGLE_CLOTHES_BUTTON_SIZE.x, TOGGLE_CLOTHES_BUTTON_SIZE.y), ButtonTexturesPM.toggleClothes, Color.white, Color.blue))
            {
                toggleClothesEnabled = !toggleClothesEnabled;
                (toggleClothesEnabled ? SoundDefOf.Checkbox_TurnedOn : SoundDefOf.Checkbox_TurnedOff).PlayOneShotOnCamera();
                forceRecachePreview = true;
            }
            if (Widgets.ButtonImageFitted(new Rect(rotCCWHorPos, col2, ROTATE_CCW_BUTTON_SIZE.x, ROTATE_CCW_BUTTON_SIZE.y), ButtonTexturesPM.rotCCW, Color.white, Color.blue))
            {
                previewRot.Rotate(RotationDirection.Counterclockwise);
                SoundDefOf.Tick_Tiny.PlayOneShotOnCamera();
                forceRecachePreview = true;
            }
            col2 += Math.Max(TOGGLE_CLOTHES_BUTTON_SIZE.y, Math.Max(ROTATE_CW_BUTTON_SIZE.y, ROTATE_CCW_BUTTON_SIZE.y));
            string toggleText       = DO_SYMMETRY_LOC_STRING.Translate();
            float  toggleTextHeight = Text.CalcHeight(toggleText, PREVIEW_SIZE.x);

            Widgets.CheckboxLabeled(new Rect(inRect.x + SPACER_SIZE + drawableWidth, col2, PREVIEW_SIZE.x, toggleTextHeight), DO_SYMMETRY_LOC_STRING.Translate(), ref doSymmetry);
            col2 += toggleTextHeight;

            // Step 6 - Draw description box.

            // Step 7 - Draw the apply, reset and cancel buttons.
            float buttonVertPos = titleHeight + drawableHeight + SPACER_SIZE;
            float applyHorPos   = inRect.width / 2 - APPLY_BUTTON_SIZE.x - RESET_BUTTON_SIZE.x / 2 - SPACER_SIZE;
            float resetHorPos   = inRect.width / 2 - RESET_BUTTON_SIZE.x / 2;
            float cancelHorPos  = inRect.width / 2 + RESET_BUTTON_SIZE.x / 2 + SPACER_SIZE;

            if (Widgets.ButtonText(new Rect(applyHorPos, buttonVertPos, APPLY_BUTTON_SIZE.x, APPLY_BUTTON_SIZE.y), APPLY_BUTTON_LOC_STRING.Translate()))
            {
                OnAcceptKeyPressed();
            }
            if (Widgets.ButtonText(new Rect(resetHorPos, buttonVertPos, RESET_BUTTON_SIZE.x, RESET_BUTTON_SIZE.y), RESET_BUTTON_LOC_STRING.Translate()))
            {
                SoundDefOf.Checkbox_TurnedOff.PlayOneShotOnCamera(null);
                ResetPawnHealth();
                forceRecachePreview = true;
            }
            if (Widgets.ButtonText(new Rect(cancelHorPos, buttonVertPos, CANCEL_BUTTON_SIZE.x, CANCEL_BUTTON_SIZE.y), CANCEL_BUTTON_LOC_STRING.Translate()))
            {
                OnCancelKeyPressed();
            }
        }