예제 #1
0
파일: Painting.cs 프로젝트: THDigi/PaintGun
        public void ToolPaintBlock(IMyCubeGrid grid, Vector3I gridPosition, PaintMaterial paint, bool useMirroring)
        {
            if (paint.Skin.HasValue)
            {
                // vanilla DLC-locked skins should use API, mod-added should use the packet to force skin change.
                SkinInfo skin = Main.Palette.GetSkinInfo(paint.Skin.Value);
                if (!skin.AlwaysOwned)
                {
                    if (useMirroring && (grid.XSymmetryPlane.HasValue || grid.YSymmetryPlane.HasValue || grid.ZSymmetryPlane.HasValue))
                    {
                        MirrorData mirrorData = new MirrorData(grid);
                        PaintBlockSymmetry(true, grid, gridPosition, paint, mirrorData, MyAPIGateway.Multiplayer.MyId);
                        // no ammo consumption, it's creative usage anyway
                    }
                    else
                    {
                        PaintBlock(true, grid, gridPosition, paint, MyAPIGateway.Multiplayer.MyId);
                        Main.NetworkLibHandler.PacketConsumeAmmo.Send();
                    }

                    return;
                }
            }

            // for mod-added skins:
            Main.NetworkLibHandler.PacketPaint.Send(grid, gridPosition, paint, useMirroring);
        }
예제 #2
0
        void DrawSkinNameText(SkinInfo selectedSkin)
        {
            if (!Main.TextAPI.IsEnabled)
            {
                return;
            }

            PlayerInfo localInfo = Main.Palette.LocalInfo;

            float    scale    = Main.Settings.paletteScale;
            Vector2D labelPos = Main.Settings.paletteScreenPos + new Vector2D(0, 0.06 * 2 * scale); // TODO FIX: needs to move relatively with the scale of the elements below it, but it doesn't...

            if (localInfo.ApplyColor)
            {
                labelPos += new Vector2D(0, 0.08); // this needs fixing too
            }
            const double TextScaleOffset = 1.8;

            if (skinLabel == null)
            {
                skinLabelSB = new StringBuilder(64).Append(selectedSkin.Name);

                skinLabelShadow = new HudAPIv2.HUDMessage(skinLabelSB, labelPos, Scale: TextScaleOffset, HideHud: true, Blend: BlendTypeEnum.PostPP);
                skinLabel       = new HudAPIv2.HUDMessage(skinLabelSB, labelPos, Scale: TextScaleOffset, HideHud: true, Blend: BlendTypeEnum.PostPP);

                skinLabelShadow.InitialColor = Color.Black;
                skinLabel.InitialColor       = Color.White;

                skinLabelUpdate = true;
            }

            if (skinLabelUpdate)
            {
                skinLabelUpdate = false;

                skinLabelSB.Clear().Append(selectedSkin.Name);

                skinLabel.Origin       = labelPos;
                skinLabelShadow.Origin = labelPos;

                skinLabel.Scale       = scale * TextScaleOffset;
                skinLabelShadow.Scale = scale * TextScaleOffset;

                Vector2D textLen = skinLabel.GetTextLength();
                skinLabel.Offset       = new Vector2D(textLen.X * -0.5, 0);                // centered
                skinLabelShadow.Offset = skinLabel.Offset + new Vector2D(0.0015, -0.0015); // centered + small offset
            }

            if (!skinLabelVisible)
            {
                skinLabelVisible        = true;
                skinLabel.Visible       = true;
                skinLabelShadow.Visible = true;
            }

            drawSkinLabel = true;
        }
예제 #3
0
        public BlockMaterial(SerializedBlockMaterial material)
        {
            ColorMask = ColorExtensions.UnpackHSVFromUint(material.ColorMaskPacked);

            SkinInfo skin = PaintGunMod.Instance.Palette.GetSkinInfo(material.Skin);

            if (skin == null)
            {
                throw new ArgumentException($"BlockMaterial :: Unknown skin={material.Skin}");
            }

            Skin = skin.SubtypeId;
        }
예제 #4
0
파일: Painting.cs 프로젝트: THDigi/PaintGun
        public void ToolReplacePaint(IMyCubeGrid grid, BlockMaterial oldPaint, PaintMaterial paint, bool includeSubgrids)
        {
            if (paint.Skin.HasValue)
            {
                // vanilla DLC-locked skins should use API, mod-added should use the packet to force skin change.
                SkinInfo skin = Main.Palette.GetSkinInfo(paint.Skin.Value);
                if (!skin.AlwaysOwned)
                {
                    ReplaceColorInGrid(true, grid, oldPaint, paint, includeSubgrids, MyAPIGateway.Multiplayer.MyId);
                    return;
                }
            }

            // for mod-added skins:
            Main.NetworkLibHandler.PacketReplacePaint.Send(grid, oldPaint, paint, includeSubgrids);
        }
예제 #5
0
파일: Palette.cs 프로젝트: THDigi/PaintGun
 public bool ValidateSkinOwnership(MyStringHash?skinId, ulong steamId, bool notifySender = true)
 {
     if (skinId.HasValue && MyAPIGateway.Multiplayer.IsServer)
     {
         SkinInfo skinInfo = GetSkinInfo(skinId.Value);
         if (skinInfo == null)
         {
             Main.NetworkLibHandler.PacketWarningMessage.Send(steamId, $"Failed to apply skin server side, skin {skinId.Value.String} does not exist.");
             return(false);
         }
         else if (!MyAPIGateway.DLC.HasDefinitionDLC(skinInfo.Definition, steamId))
         {
             Main.NetworkLibHandler.PacketWarningMessage.Send(steamId, $"Failed to apply skin server side, skin {skinId.Value.String} not owned.");
             return(false);
         }
     }
     return(true);
 }
예제 #6
0
파일: Painting.cs 프로젝트: THDigi/PaintGun
        protected override void UpdateAfterSim(int tick)
        {
            if (tick % 30 != 0)
            {
                return;
            }

            // see if skin was applied and warn accordingly.
            foreach (KeyValuePair <IMySlimBlock, CheckData> kv in CheckSkinned)
            {
                IMySlimBlock slim = kv.Key;
                CheckData    data = kv.Value;

                if (data.ReadAtTick > tick)
                {
                    continue;
                }

                if (slim.SkinSubtypeId != data.SkinId)
                {
                    SkinInfo skinInfo = Main.Palette.GetSkinInfo(data.SkinId);
                    string   name     = skinInfo?.Name ?? $"Unknown:{data.SkinId.String}";
                    Main.Notifications.Show(3, $"[{name}] skin not applied, likely not owned. Consider hiding it in PaintGun's config (Chat then F2).", MyFontEnum.Red, 5000);
                }

                RemoveCheckKeys.Add(slim);
            }

            foreach (IMySlimBlock key in RemoveCheckKeys)
            {
                CheckSkinned.Remove(key);
            }

            RemoveCheckKeys.Clear();

            if (CheckSkinned.Count <= 0)
            {
                SetUpdateMethods(UpdateFlags.UPDATE_AFTER_SIM, false);
            }
        }
예제 #7
0
        void DrawSkinSelector(MatrixD camMatrix, Vector3D worldPos, float scaleFOV, float bgAlpha)
        {
            PlayerInfo      localInfo  = Main.Palette.LocalInfo;
            List <SkinInfo> shownSkins = Main.Palette.SkinsForHUD;

            if (localInfo == null || shownSkins == null || !localInfo.ApplySkin || !Main.Palette.HasAnySkin)
            {
                return;
            }

            float        iconSize               = 0.0024f * scaleFOV;
            float        selectedIconSize       = 0.003f * scaleFOV;
            MyStringHash selectedSkinId         = localInfo.SelectedSkin;
            double       iconSpacingAdd         = (selectedIconSize - iconSize); // 0.0012 * scaleFOV;
            double       iconSpacingWidth       = (iconSize * 2) + iconSpacingAdd;
            float        iconBgSpacingAddWidth  = 0.0004f * scaleFOV;
            float        iconBgSpacingAddHeight = 0.0006f * scaleFOV;
            //float iconBgSpacingAddWidth = 0.0006f * scaleFOV;
            //float iconBgSpacingAddHeight = 0.0008f * scaleFOV;

            Vector3D pos = worldPos;

            if (localInfo.ApplyColor)
            {
                pos += camMatrix.Up * (0.0075f * scaleFOV);
            }

            DrawSkinNameText(localInfo.SelectedSkinInfo);

            const int    MAX_VIEW_SKINS        = 7; // must be an odd number.
            const int    MAX_VIEW_SKINS_HALF   = ((MAX_VIEW_SKINS - 1) / 2);
            const double MAX_VIEW_SKINS_HALF_D = (MAX_VIEW_SKINS / 2d);
            //const double MAX_VIEW_SKINS_HALF_D_BG = ((MAX_VIEW_SKINS - 4) / 2d);

            int showSkinsCount = shownSkins.Count;

            if (showSkinsCount >= MAX_VIEW_SKINS) // scrolling skin bar
            {
                //var bgPos = pos + camMatrix.Right * ((iconSpacingWidth * 0.5) - (iconSpacingWidth * 0.5));
                //MyTransparentGeometry.AddBillboardOriented(MATERIAL_PALETTE_BACKGROUND, PALETTE_COLOR_BG * bgAlpha, bgPos, camMatrix.Left, camMatrix.Up, (float)(iconSpacingWidth * MAX_VIEW_SKINS_HALF_D_BG) + iconBgSpacingAddWidth, iconSize + iconBgSpacingAddHeight, Vector2.Zero, UI_BG_BLENDTYPE);

                pos += camMatrix.Left * ((iconSpacingWidth * MAX_VIEW_SKINS_HALF_D) - (iconSpacingWidth * 0.5));

                int skinIndex = 0;

                for (int i = 0; i < showSkinsCount; ++i)
                {
                    SkinInfo skin = shownSkins[i];
                    if (skin.SubtypeId == selectedSkinId)
                    {
                        skinIndex = i;
                        break;
                    }
                }

                const float MIN_ALPHA         = 0.5f; // alpha of the skin icon on the edge of the scrollable bar
                float       alphaSubtractStep = ((1f - MIN_ALPHA) / MAX_VIEW_SKINS_HALF);

                int index = skinIndex - MAX_VIEW_SKINS_HALF;

                for (int a = -MAX_VIEW_SKINS_HALF; a <= MAX_VIEW_SKINS_HALF; ++a)
                {
                    if (index < 0)
                    {
                        index = showSkinsCount + index;
                    }
                    if (index >= showSkinsCount)
                    {
                        index %= showSkinsCount;
                    }

                    SkinInfo skin = shownSkins[index];

                    float alpha = 1f - (Math.Abs(a) * alphaSubtractStep);

                    if (selectedSkinId == skin.SubtypeId)
                    {
                        MyTransparentGeometry.AddBillboardOriented(MATERIAL_PALETTE_COLOR, Color.White, pos, camMatrix.Left, camMatrix.Up, selectedIconSize, selectedIconSize, Vector2.Zero, GUI_FG_BLENDTYPE);
                    }

                    MyTransparentGeometry.AddBillboardOriented(skin.Icon, Color.White * alpha, pos, camMatrix.Left, camMatrix.Up, iconSize, iconSize, Vector2.Zero, GUI_FG_BLENDTYPE);

                    pos += camMatrix.Right * iconSpacingWidth;

                    index++;
                }
            }
            else // static skin bar with moving selection
            {
                double halfOwnedSkins = showSkinsCount * 0.5;

                //MyTransparentGeometry.AddBillboardOriented(MATERIAL_PALETTE_BACKGROUND, PALETTE_COLOR_BG * bgAlpha, pos, camMatrix.Left, camMatrix.Up, (float)(iconSpacingWidth * halfOwnedSkins) + iconBgSpacingAddWidth, iconSize + iconBgSpacingAddHeight, Vector2.Zero, UI_BG_BLENDTYPE);

                pos += camMatrix.Left * ((iconSpacingWidth * halfOwnedSkins) - (iconSpacingWidth * 0.5));

                foreach (SkinInfo skin in shownSkins)
                {
                    if (selectedSkinId == skin.SubtypeId)
                    {
                        MyTransparentGeometry.AddBillboardOriented(MATERIAL_PALETTE_COLOR, Color.White, pos, camMatrix.Left, camMatrix.Up, selectedIconSize, selectedIconSize, Vector2.Zero, GUI_FG_BLENDTYPE);
                    }

                    MyTransparentGeometry.AddBillboardOriented(skin.Icon, Color.White, pos, camMatrix.Left, camMatrix.Up, iconSize, iconSize, Vector2.Zero, GUI_FG_BLENDTYPE);

                    pos += camMatrix.Right * iconSpacingWidth;
                }
            }
        }
예제 #8
0
파일: Palette.cs 프로젝트: THDigi/PaintGun
        bool SetColorAndSkin(PaintMaterial pickedMaterial, bool changeApply)
        {
            // in case of players with both ApplyColor and ApplySkin turned off.
            if (!pickedMaterial.ColorMask.HasValue && !pickedMaterial.Skin.HasValue)
            {
                Main.Notifications.Show(0, $"Target has no color or skin enabled.", MyFontEnum.Red, 3000);
                return(false);
            }

            PaintMaterial currentMaterial = GetLocalPaintMaterial();

            if (currentMaterial.PaintEquals(pickedMaterial))
            {
                Main.Notifications.Show(0, $"Color and skin already selected.", MyFontEnum.Debug, 2000);
                return(false);
            }

            bool success = false;

            if (pickedMaterial.ColorMask.HasValue)
            {
                if (changeApply)
                {
                    LocalInfo.ApplyColor = true;
                }

                if (Utils.ColorMaskEquals(LocalInfo.SelectedColorMask, pickedMaterial.ColorMask.Value))
                {
                    Main.Notifications.Show(0, $"Color already selected.", MyFontEnum.Debug, 2000);
                }
                else
                {
                    bool inPalette = false;

                    for (int i = 0; i < LocalInfo.ColorsMasks.Count; i++)
                    {
                        if (Utils.ColorMaskEquals(LocalInfo.ColorsMasks[i], pickedMaterial.ColorMask.Value))
                        {
                            inPalette = true;
                            LocalInfo.SelectedColorSlot = i;
                            MyAPIGateway.Session.Player.SelectedBuildColorSlot = i;

                            Main.Notifications.Show(0, $"Color exists in slot [{(i + 1).ToString()}], selected.", MyFontEnum.Debug, 2000);
                            break;
                        }
                    }

                    if (!inPalette)
                    {
                        MyAPIGateway.Session.Player.ChangeOrSwitchToColor(pickedMaterial.ColorMask.Value);

                        Main.NetworkLibHandler.PacketPaletteSetColor.Send(LocalInfo.SelectedColorSlot, pickedMaterial.ColorMask.Value);

                        Main.Notifications.Show(0, $"Color slot [{(LocalInfo.SelectedColorSlot + 1).ToString()}] set to [{Utils.ColorMaskToString(pickedMaterial.ColorMask.Value)}]", MyFontEnum.Debug, 2000);
                    }

                    success = true;
                }
            }
            else
            {
                if (changeApply)
                {
                    LocalInfo.ApplyColor = false;
                }
            }

            if (pickedMaterial.Skin.HasValue)
            {
                if (changeApply)
                {
                    LocalInfo.ApplySkin = true;
                }

                if (LocalInfo.SelectedSkin != pickedMaterial.Skin.Value)
                {
                    SkinInfo skin = GetSkinInfo(pickedMaterial.Skin.Value);
                    if (skin != null)
                    {
                        if (!skin.LocallyOwned)
                        {
                            Main.Notifications.Show(1, $"Skin [{skin.Name}] is not owned, not selected.", MyFontEnum.Red, 2000);
                        }
                        else
                        {
                            LocalInfo.SelectedSkin = skin.SubtypeId;
                            success = true;

                            Main.Notifications.Show(1, $"Selected skin: [{skin.Name}]", MyFontEnum.Debug, 2000);
                        }
                    }
                }
            }
            else
            {
                if (changeApply)
                {
                    LocalInfo.ApplySkin = false;
                }
            }

            return(success);
        }
예제 #9
0
파일: Palette.cs 프로젝트: THDigi/PaintGun
        void InitBlockSkins()
        {
            if (Constants.SKIN_INIT_LOGGING)
            {
                Log.Info("Finding block skins...");
            }

            HashSet <string> definedIcons = new HashSet <string>();

            foreach (MyTransparentMaterialDefinition def in MyDefinitionManager.Static.GetTransparentMaterialDefinitions())
            {
                if (def.Id.SubtypeName.StartsWith(SKIN_ICON_PREFIX))
                {
                    definedIcons.Add(def.Id.SubtypeName);
                }
            }

            int foundSkins = 0;

            foreach (MyAssetModifierDefinition assetDef in MyDefinitionManager.Static.GetAssetModifierDefinitions())
            {
                if (IsSkinAsset(assetDef))
                {
                    foundSkins++;
                }
            }

            int capacity = foundSkins + 1; // include "No Skin" too.

            Skins       = new SortedDictionary <MyStringHash, SkinInfo>(new SkinSorter());
            SkinsForHUD = new List <SkinInfo>(capacity);

            StringBuilder sb = new StringBuilder(256);

            foreach (MyAssetModifierDefinition assetDef in MyDefinitionManager.Static.GetAssetModifierDefinitions())
            {
                if (assetDef.Id.SubtypeId.String == "RustNonColorable_Armor")
                {
                    continue; // HACK: DLC-less skin that has no steam item, not sure what to do about this so I'm just gonna make it not exist for now
                }
                if (IsSkinAsset(assetDef))
                {
                    bool isCustomSkin = (!assetDef.Context.IsBaseGame && (assetDef.DLCs == null || assetDef.DLCs.Length == 0));

                    #region Generate user friendly name
                    string name = assetDef.Id.SubtypeName;
                    sb.Clear();
                    sb.Append(name);

                    if (name.EndsWith(ARMOR_SUFFIX))
                    {
                        sb.Length -= ARMOR_SUFFIX.Length;
                    }

                    string nameId = sb.ToString();

                    // Add spaces before upper case letters except first.
                    // Or replace _ with space.
                    for (int i = 1; i < sb.Length; ++i)
                    {
                        char c = sb[i];

                        if (c == '_')
                        {
                            sb[i] = ' ';
                        }

                        if (char.IsUpper(c) && sb[i - 1] != ' ')
                        {
                            sb.Insert(i, ' ');
                        }
                    }

                    name = sb.ToString();
                    #endregion

                    string icon = SKIN_ICON_PREFIX + nameId;

                    if (!definedIcons.Contains(icon))
                    {
                        if (isCustomSkin || Utils.IsLocalMod())
                        {
                            Log.Error($"'{icon}' not found in transparent materials definitions.", Log.PRINT_MESSAGE);
                        }

                        icon = SKIN_ICON_UNKNOWN;
                    }

                    if (isCustomSkin)
                    {
                        FixModTexturePaths(assetDef);
                    }

                    SkinInfo skinInfo = new SkinInfo(assetDef, name, icon);
                    Skins[skinInfo.SubtypeId] = skinInfo;

                    // HACK: this skin isn't colorable, so might as well make it ignore color palette like gold and silver
                    if (assetDef.Id.SubtypeId.String == "RustNonColorable_Armor")
                    {
                        assetDef.DefaultColor = Color.Gray;
                    }
                }
            }

            SkinInfo noSkin = new SkinInfo(null, "No Skin", SKIN_ICON_PREFIX + "NoSkin");
            Skins[noSkin.SubtypeId] = noSkin;

            CleanUpFixMods();

            bool neonSkinExists = false;

            const int SubtypeWidth = -26;
            const int NameWidth    = -26;
            const int DLCsWidth    = -20;

            Log.Info($"{"Skin SubtypeId",SubtypeWidth} {"Name",NameWidth} {"DLCs",DLCsWidth} Mod");

            foreach (SkinInfo skin in Skins.Values)
            {
                if (skin.SubtypeId.String == "Neon_Colorable_Surface")
                {
                    neonSkinExists = true;
                }

                if (Constants.SKIN_INIT_LOGGING)
                {
                    string dlcList = "";
                    if (skin.Definition?.DLCs != null && skin.Definition.DLCs.Length > 0)
                    {
                        dlcList = String.Join(",", skin.Definition.DLCs);
                    }

                    string modName = skin.Definition?.Context?.ModName ?? "";

                    Log.Info($"{$"'{skin.SubtypeId.String}'",SubtypeWidth} {skin.Name,NameWidth} {dlcList,DLCsWidth} {modName}");

#if false
                    sb.Clear();
                    sb.Append("Defined skin id='").Append(skin.SubtypeId.String).Append("'; Name='").Append(skin.Name).Append("'");

                    if (skin.Icon.String == SKIN_ICON_UNKNOWN)
                    {
                        sb.Append("; No Icon!");
                    }

                    if (skin.Definition?.DLCs != null && skin.Definition.DLCs.Length > 0)
                    {
                        sb.Append("; DLC=");

                        int preLen = sb.Length;

                        foreach (string dlcId in skin.Definition.DLCs)
                        {
                            MyDLCs.MyDLC dlc;
                            if (!MyDLCs.TryGetDLC(dlcId, out dlc))
                            {
                                Log.Error($"Skin '{skin.SubtypeId.String}' uses unknown DLC={dlcId}");
                                continue;
                            }

                            sb.Append(dlc.Name).Append(", ");
                        }

                        if (sb.Length > preLen)
                        {
                            sb.Length -= 2; // remove last comma
                        }
                    }

                    if (skin.Definition?.Context != null && !skin.Definition.Context.IsBaseGame)
                    {
                        sb.Append("; Mod='").Append(skin.Definition.Context.ModName).Append("'");
                    }

                    Log.Info(sb.ToString());
#endif
                }
            }

            if (!neonSkinExists)
            {
                Log.Error("WARNING: Expected to find 'Neon_Colorable_Surface' but did not!\nCheck the skins list in the mod's log to ensure all of them are there.");
            }
        }
예제 #10
0
        void HandleInputs_CyclePalette()
        {
            if (LocalInfo == null)
            {
                return;
            }

            if (!LocalInfo.ApplySkin && !LocalInfo.ApplyColor)
            {
                return;
            }

            int  cycleDir   = 0;
            bool cycleSkins = false;

            if (Main.GameConfig.UsingGamepad)
            {
                // x button is used for both, if it's not pressed then ignore
                if (!MyAPIGateway.Input.IsJoystickButtonNewPressed(Constants.GamepadBind_CyclePalette))
                {
                    return;
                }

                cycleDir   = -1; // cycle right
                cycleSkins = MyAPIGateway.Input.IsJoystickButtonPressed(Constants.GamepadBind_CycleSkinsModifier);

                if (!cycleSkins)
                {
                    IMyUseObject useObject = MyAPIGateway.Session?.Player?.Character?.Components?.Get <MyCharacterDetectorComponent>()?.UseObject;
                    if (useObject != null)
                    {
                        return; // aiming at an interactive object while pressing only X, ignore
                    }
                }
            }
            else // keyboard & mouse
            {
                if (MyAPIGateway.Input.IsAnyAltKeyPressed())
                {
                    return; // ignore combos with alt to allow other systems to use these controls with alt
                }
                cycleDir = MyAPIGateway.Input.DeltaMouseScrollWheelValue();
                if (cycleDir == 0)
                {
                    if (MyAPIGateway.Input.IsNewGameControlPressed(MyControlsSpace.SWITCH_RIGHT))
                    {
                        cycleDir = -1;
                    }
                    else if (MyAPIGateway.Input.IsNewGameControlPressed(MyControlsSpace.SWITCH_LEFT))
                    {
                        cycleDir = 1;
                    }
                }

                if (cycleDir == 0)
                {
                    return;
                }

                cycleSkins = MyAPIGateway.Input.IsAnyShiftKeyPressed();
                bool ctrl = MyAPIGateway.Input.IsAnyCtrlKeyPressed();

                // ignore ctrl+shift+scroll combo, leave it to other systems
                if (cycleSkins && ctrl)
                {
                    return;
                }

                // color cycling requires it pressed or explicitly requires it not pressed depending on user pref
                if (!cycleSkins && Main.Settings.requireCtrlForColorCycle != ctrl)
                {
                    return;
                }
            }

            if (cycleDir == 0)
            {
                return;
            }

            if (cycleSkins && !Main.Palette.HasAnySkin)
            {
                return;
            }

            // skin or color applying is off, can't cycle turned off palette
            if (cycleSkins ? !LocalInfo.ApplySkin : !LocalInfo.UseColor)
            {
                Main.HUDSounds.PlayUnable();

                // there's no gamepad equivalent to toggle palettes so it'll just show kb/m binds for both.
                string assigned = InputHandler.GetFriendlyStringForControl(MyAPIGateway.Input.GetGameControl(MyControlsSpace.CUBE_COLOR_CHANGE));

                bool   showBind = true;
                string message  = cycleSkins ? "Skin applying is turned off." : "Color applying is turned off.";

                if (!cycleSkins && !LocalInfo.SkinAllowsColor)
                {
                    message  = $"[{LocalInfo.SelectedSkinInfo.Name}] skin cannot be colored.";
                    showBind = false;
                }

                Main.Notifications.Show(0, message, MyFontEnum.Red, 1000);

                if (showBind)
                {
                    Main.Notifications.Show(1, cycleSkins ? $"Press [Shift] + [{assigned}] to enable" : $"Press [{assigned}] to enable", MyFontEnum.Debug, 1000);
                }
                return;
            }

            if (cycleDir < 0)
            {
                if (cycleSkins)
                {
                    for (int i = 0; i < Main.Palette.SkinsForHUD.Count; i++)
                    {
                        SkinInfo skin = Main.Palette.SkinsForHUD[i];
                        if (skin.SubtypeId == LocalInfo.SelectedSkin)
                        {
                            i++;
                            if (i < Main.Palette.SkinsForHUD.Count)
                            {
                                LocalInfo.SelectedSkin = Main.Palette.SkinsForHUD[i].SubtypeId;
                            }
                            else
                            {
                                LocalInfo.SelectedSkin = MyStringHash.NullOrEmpty;
                            }
                            break;
                        }
                    }
                }
                else
                {
                    int slot = LocalInfo.SelectedColorSlot;
                    if (Main.Settings.selectColorZigZag)
                    {
                        if (slot >= 13)
                        {
                            slot = 0;
                        }
                        else if (slot >= 7)
                        {
                            slot -= 6;
                        }
                        else
                        {
                            slot += 7;
                        }
                    }
                    else
                    {
                        if (++slot >= LocalInfo.ColorsMasks.Count)
                        {
                            slot = 0;
                        }
                    }

                    LocalInfo.SelectedColorSlot = slot;
                }
            }
            else
            {
                if (cycleSkins)
                {
                    for (int i = (Main.Palette.SkinsForHUD.Count - 1); i >= 0; i--)
                    {
                        SkinInfo skin = Main.Palette.SkinsForHUD[i];
                        if (skin.SubtypeId == LocalInfo.SelectedSkin)
                        {
                            i--;
                            if (i >= 0)
                            {
                                LocalInfo.SelectedSkin = Main.Palette.SkinsForHUD[i].SubtypeId;
                            }
                            else
                            {
                                LocalInfo.SelectedSkin = Main.Palette.SkinsForHUD[Main.Palette.SkinsForHUD.Count - 1].SubtypeId;
                            }
                            break;
                        }
                    }
                }
                else
                {
                    int slot = LocalInfo.SelectedColorSlot;
                    if (Main.Settings.selectColorZigZag)
                    {
                        if (slot >= 7)
                        {
                            slot -= 7;
                        }
                        else
                        {
                            slot += 6;
                        }
                    }
                    else
                    {
                        if (--slot < 0)
                        {
                            slot = (LocalInfo.ColorsMasks.Count - 1);
                        }
                    }

                    LocalInfo.SelectedColorSlot = slot;
                }
            }

            if (cycleSkins)
            {
                Main.HUDSounds.PlayItem();
            }
            else
            {
                MyAPIGateway.Session.Player.SelectedBuildColorSlot = LocalInfo.SelectedColorSlot;
                Main.HUDSounds.PlayClick();
            }
        }