public string GetSettingsString(bool comments) { sb.Clear(); if (comments) { sb.Append("ConfigVersion=").Append(CFG_VERSION).AppendLine(comments ? " // Do not edit. This is used by the mod to reset settings when their defaults change without changing them if they're custom." : ""); sb.AppendLine("// Paint Gun mod config; this file gets automatically overwritten after being loaded so don't leave custom comments."); sb.AppendLine("// You can reload this while the game is running by typing in chat: /pg reload"); sb.AppendLine("// Lines starting with // are comments. All values are case insensitive unless otherwise specified."); sb.AppendLine(); } sb.Append("ExtraSounds=").Append(extraSounds).AppendLine(comments ? " // toggle all HUD-sounds for this mod's actions, like cycling colors/skins, alerts when can't do something, etc. Default: true" : ""); sb.Append("SprayParticles=").Append(sprayParticles).AppendLine(comments ? " // toggles the spray particles. Default: true" : ""); sb.Append("SpraySoundVolume=").Append(spraySoundVolume).AppendLine(comments ? " // paint gun spraying sound volume. Default: 0.8" : ""); sb.Append("SelectColorZigZag=").Append(selectColorZigZag).AppendLine(comments ? " // type of scrolling through colors in the palette, false is each row at a time, true is in zig-zag. Default: false" : ""); sb.Append("HidePaletteWithHUD=").Append(hidePaletteWithHUD).AppendLine(comments ? " // wether to hide the color palette and aim info along with the HUD. Set to false to always show regardless of the HUD being visible or not. Default: true" : ""); sb.Append("PaletteScreenPos=").Append(Math.Round(paletteScreenPos.X, 5)).Append(", ").Append(Math.Round(paletteScreenPos.Y, 5)).AppendLine(comments ? $" // color palette screen position in X and Y coordinates where 0,0 is the screen center. Positive values are right and up and negative ones are opposite of that. Default: {paletteScreenPos.X.ToString("0.#####")}, {paletteScreenPos.Y.ToString("0.#####")}" : ""); sb.Append("PaletteScale=").Append(Math.Round(paletteScale, 5)).AppendLine(comments ? $" // color palette overall scale. Default: {paletteScaleDefault.ToString("0.#####")}" : ""); sb.Append("PaletteBackgroundOpacity=").Append(paletteBackgroundOpacity < 0 ? "HUD" : Math.Round(paletteBackgroundOpacity, 5).ToString()).AppendLine(comments ? " // palette's background opacity percent scalar (0 to 1 value) or set to HUD to use the game's HUD opacity. Default: HUD" : ""); sb.Append("AimInfoScreenPos=").Append(Math.Round(aimInfoScreenPos.X, 5)).Append(", ").Append(Math.Round(aimInfoScreenPos.Y, 5)).AppendLine(comments ? $" // aim info's screen position in X and Y coordinates where 0,0 is the screen center. Positive values are right and up and negative ones are opposite of that. Default: {aimInfoScreenPos.X.ToString("0.#####")}, {aimInfoScreenPos.Y.ToString("0.#####")}" : ""); // TODO: make it work with scale? //str.Append("AimInfoScale=").Append(Math.Round(aimInfoScale, 5)).AppendLine(comments ? $" // aiming info box overall scale. Default: {aimInfoScaleDefault:0.#####}" : ""); sb.Append("AimInfoBackgroundOpacity=").Append(aimInfoBackgroundOpacity < 0 ? "HUD" : Math.Round(aimInfoBackgroundOpacity, 5).ToString()).AppendLine(comments ? " // aim info's background opacity percent scalar (0 to 1 value) or set to HUD to use the game's HUD opacity. Default: HUD" : ""); sb.Append("RequireCtrlForColorCycle=").Append(requireCtrlForColorCycle).AppendLine(comments ? " // Whether color cycling requires ctrl+scroll (true) or just scroll (false). Skin cycling (shift+scroll) is unaffected. Default: false" : ""); if (comments) { sb.AppendLine(); sb.AppendLine("// This allows you to hide certain skin IDs from the HUD palette, separated by comma and case-sensitive!"); sb.Append("// All detected skins, sorted by DLC: "); int widestName = 0; // using dlcId so they get sorted by order DLCs were introduced SortedDictionary <uint, List <SkinInfo> > skinsByDLC = new SortedDictionary <uint, List <SkinInfo> >(); foreach (SkinInfo skin in Main.Palette.Skins.Values) { if (skin.SubtypeId == MyStringHash.NullOrEmpty) { continue; } uint dlcId = 0; MyDLCs.MyDLC dlc; if (skin.Definition?.DLCs != null && skin.Definition.DLCs.Length > 0 && MyDLCs.TryGetDLC(skin.Definition.DLCs[0], out dlc)) { dlcId = dlc.AppId; widestName = Math.Max(widestName, dlc.Name.Length); } // get or add list List <SkinInfo> skins; if (!skinsByDLC.TryGetValue(dlcId, out skins)) { skinsByDLC[dlcId] = skins = new List <SkinInfo>(); } skins.Add(skin); } foreach (KeyValuePair <uint, List <SkinInfo> > kv in skinsByDLC) { uint dlcId = kv.Key; string dlcName = "(No DLC)"; MyDLCs.MyDLC dlc; if (dlcId > 0 && MyDLCs.TryGetDLC(dlcId, out dlc)) { dlcName = dlc.Name; } sb.AppendLine().Append("// ").Append(' ', widestName - dlcName.Length).Append(dlcName).Append(": "); foreach (SkinInfo skin in kv.Value) { sb.Append(skin.SubtypeId.String).Append(", "); } sb.Length -= 2; // remove last comma } sb.AppendLine(); } sb.Append("HideSkinsFromPalette=").Append(string.Join(", ", hideSkinsFromPalette)).AppendLine(); if (comments) { sb.AppendLine(); sb.AppendLine("// Key/mouse/gamepad combination to trigger '/pg pick' command."); sb.AppendLine("// Separate multiple keys/buttons/controls with spaces. For gamepad add " + InputHandler.GAMEPAD_PREFIX + " prefix, for mouse add " + InputHandler.MOUSE_PREFIX + " prefix and for game controls add " + InputHandler.CONTROL_PREFIX + " prefix."); sb.AppendLine("// All keys, mouse buttons, gamepad buttons/axes and control names are at the bottom of this file."); } sb.Append("PickColorMode-input1=").Append(colorPickMode1?.GetStringCombination() ?? "").AppendLine(comments ? " // Default: " + (default_colorPickMode1?.GetStringCombination() ?? "") : ""); sb.Append("PickColorMode-input2=").Append(colorPickMode2?.GetStringCombination() ?? "").AppendLine(comments ? " // Default: " + (default_colorPickMode2?.GetStringCombination() ?? "") : ""); if (comments) { sb.AppendLine(); sb.AppendLine("// Key/mouse/gamepad combination to instantly get the aimed block/player's color into the selected slot."); sb.AppendLine("// Same input rules as above."); } sb.Append("InstantPickColor-input1=").Append(instantColorPick1?.GetStringCombination() ?? "").AppendLine(comments ? " // Default: " + (default_instantColorPick1?.GetStringCombination() ?? "") : ""); sb.Append("InstantPickColor-input2=").Append(instantColorPick2?.GetStringCombination() ?? "").AppendLine(comments ? " // Default: " + (default_instantColorPick2?.GetStringCombination() ?? "") : ""); if (comments) { sb.AppendLine(); sb.AppendLine("// Key/mouse/gamepad combination to toggle the replace color mode, which only works in creative."); sb.AppendLine("// Same input rules as above."); } sb.Append("ReplaceColorMode-input1=").Append(replaceColorMode1?.GetStringCombination() ?? "").AppendLine(comments ? " // Default: " + (default_replaceColorMode1?.GetStringCombination() ?? "") : ""); sb.Append("ReplaceColorMode-input2=").Append(replaceColorMode2?.GetStringCombination() ?? "").AppendLine(comments ? " // Default: " + (default_replaceColorMode2?.GetStringCombination() ?? "") : ""); if (comments) { sb.AppendLine(); sb.AppendLine("// List of inputs, generated from game data."); const int NEWLINE_EVERY_CHARACTERS = 130; int characters = 0; sb.Append("// Key names: ").AppendLine().Append("// "); foreach (KeyValuePair <string, object> kv in InputHandler.inputs) { if (kv.Key.StartsWith(InputHandler.MOUSE_PREFIX, StringComparison.Ordinal) || kv.Key.StartsWith(InputHandler.GAMEPAD_PREFIX, StringComparison.Ordinal) || kv.Key.StartsWith(InputHandler.CONTROL_PREFIX, StringComparison.Ordinal)) { continue; } int prevLen = sb.Length; sb.Append(kv.Key).Append(", "); characters += (sb.Length - prevLen); if (characters >= NEWLINE_EVERY_CHARACTERS) { sb.AppendLine().Append("// "); characters = 0; } } sb.AppendLine(); characters = 0; sb.Append("// Mouse button names: ").AppendLine().Append("// "); foreach (KeyValuePair <string, object> kv in InputHandler.inputs) { if (kv.Key.StartsWith(InputHandler.MOUSE_PREFIX, StringComparison.Ordinal)) { int prevLen = sb.Length; sb.Append(kv.Key).Append(", "); characters += (sb.Length - prevLen); if (characters >= NEWLINE_EVERY_CHARACTERS) { sb.AppendLine().Append("// "); characters = 0; } } } sb.AppendLine(); characters = 0; sb.Append("// Gamepad button/axes names: ").AppendLine().Append("// "); foreach (KeyValuePair <string, object> kv in InputHandler.inputs) { if (kv.Key.StartsWith(InputHandler.GAMEPAD_PREFIX, StringComparison.Ordinal)) { int prevLen = sb.Length; sb.Append(kv.Key).Append(", "); characters += (sb.Length - prevLen); if (characters >= NEWLINE_EVERY_CHARACTERS) { sb.AppendLine().Append("// "); characters = 0; } } } sb.AppendLine(); characters = 0; sb.Append("// Control names: ").AppendLine().Append("// "); foreach (KeyValuePair <string, object> kv in InputHandler.inputs) { if (kv.Key.StartsWith(InputHandler.CONTROL_PREFIX, StringComparison.Ordinal)) { int prevLen = sb.Length; sb.Append(kv.Key).Append(", "); characters += (sb.Length - prevLen); if (characters >= NEWLINE_EVERY_CHARACTERS) { sb.AppendLine().Append("// "); characters = 0; } } } sb.AppendLine(); } return(sb.ToString()); }
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."); } }