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); }
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; }
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; }
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); }
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); }
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); } }
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; } } }
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); }
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."); } }
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(); } }