Example #1
0
            /// <summary>
            /// Checks if the given source mutation blocks the given otherMutation being added at the given part
            /// </summary>
            /// <param name="sourceMutation">The source mutation.</param>
            /// <param name="otherMutation">The other mutation.</param>
            /// <param name="addPart">The add part.</param>
            /// <returns></returns>
            public bool Blocks([NotNull] Hediff_AddedMutation sourceMutation, [NotNull] MutationDef otherMutation, [CanBeNull] BodyPartRecord addPart)
            {
                if (sourceMutation == null)
                {
                    throw new ArgumentNullException(nameof(sourceMutation));
                }
                if (otherMutation == null)
                {
                    throw new ArgumentNullException(nameof(otherMutation));
                }

                if (otherMutation != mutation)
                {
                    return(false);
                }

                return(blockOnAnyPart || addPart == sourceMutation.Part);
            }
Example #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);
                    }

                    // Draw the various labels for the severity bar (need to refine this later).
                    string stageLabelText     = $"Stage {mutationsOfDef.FirstOrDefault().CurStageIndex}: {mutationsOfDef.FirstOrDefault().LabelCap}";
                    Rect   severityLabelsRect = new Rect(partListViewRect.x, curY, partListViewRect.width, Text.CalcHeight(stageLabelText, partListViewRect.width));
                    Text.Anchor = TextAnchor.MiddleLeft;
                    Widgets.Label(severityLabelsRect, stageLabelText);
                    Text.Anchor = TextAnchor.MiddleRight;
                    Widgets.Label(severityLabelsRect, mutationsOfDef.FirstOrDefault().Severity.ToString("n2"));
                    Text.Anchor = TextAnchor.UpperLeft;
                    curY       += severityLabelsRect.height;

                    // Draw the severity slider
                    float curSeverity = mutationsOfDef.Select(n => n.Severity).Average();
                    float newSeverity = Widgets.HorizontalSlider(new Rect(partListViewRect.x, curY, partListViewRect.width, SLIDER_HEIGHT), curSeverity, mutationDef.minSeverity, mutationDef.maxSeverity);
                    if (curSeverity != newSeverity)
                    {
                        curSeverity = newSeverity;
                        foreach (Hediff_AddedMutation mutationOfDef in mutationsOfDef)
                        {
                            MutationData relevantEntry = addedMutations.MutationsByPartAndLayer(mutationOfDef.Part, layer);
                            if (relevantEntry != null)
                            {
                                relevantEntry.severity = newSeverity;
                            }
                            else
                            {
                                addedMutations.AddData(mutationOfDef.Def, mutationOfDef.Part, newSeverity, mutationOfDef.ProgressionHalted, false);
                            }
                            mutationOfDef.Severity = newSeverity;
                        }
                        recachePreview = true;
                    }
                    curY += SLIDER_HEIGHT;

                    // 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();
                }
            }
        }
Example #3
0
 void IMutationEventReceiver.MutationRemoved(Hediff_AddedMutation mutation, MutationTracker tracker)
 {
     RefreshGraphics(tracker, Pawn);
 }
 void IMutationEventReceiver.MutationRemoved(Hediff_AddedMutation mutation, MutationTracker tracker)
 {
     IsDirty = true;
 }
Example #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();
            }
        }