├── templates ├── malebody.pdn ├── malebody.png ├── sleeves.pdn ├── sleeves.png ├── body_blank.png ├── femalebody.pdn ├── femalebody.png ├── sleeves_blank.png ├── novakid_sleeves.png └── unxie_template.png ├── OutfitGenerator ├── Resources │ ├── animatedBackTemplate.png │ ├── animatedPantsTemplate.png │ ├── animatedSleevesTemplate.png │ ├── invisibleAnimatedPantsTemplate.png │ ├── HatConfig.json │ ├── HidingHatConfig.json │ ├── MaskedHatConfig.json │ ├── HidingPantsConfig.json │ ├── PantsConfig.json │ ├── BackConfig.json │ └── SleevesConfig.json ├── packages.config ├── App.config ├── Mergers │ ├── ISpriteMerger.cs │ ├── SleevesMerger.cs │ └── ChestPantsMerger.cs ├── Generators │ ├── HidingHatGenerator.cs │ ├── GeneratorException.cs │ ├── HidingPantsGenerator.cs │ ├── MaskedHatGenerator.cs │ ├── BackGenerator.cs │ ├── SleeveGenerator.cs │ ├── PantsGenerator.cs │ ├── IClothingGenerator.cs │ ├── ClothingGenerator.cs │ ├── HatGenerator.cs │ └── DirectiveGenerator.cs ├── ItemDescriptor.cs ├── Util │ ├── CommandGenerator.cs │ ├── JsonResourceManager.cs │ ├── FileSaver.cs │ └── ConsoleWriter.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── OutfitGenerator.csproj └── Program.cs ├── LICENSE ├── Outfit Generator.sln ├── README.md └── .gitignore /templates/malebody.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/templates/malebody.pdn -------------------------------------------------------------------------------- /templates/malebody.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/templates/malebody.png -------------------------------------------------------------------------------- /templates/sleeves.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/templates/sleeves.pdn -------------------------------------------------------------------------------- /templates/sleeves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/templates/sleeves.png -------------------------------------------------------------------------------- /templates/body_blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/templates/body_blank.png -------------------------------------------------------------------------------- /templates/femalebody.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/templates/femalebody.pdn -------------------------------------------------------------------------------- /templates/femalebody.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/templates/femalebody.png -------------------------------------------------------------------------------- /templates/sleeves_blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/templates/sleeves_blank.png -------------------------------------------------------------------------------- /templates/novakid_sleeves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/templates/novakid_sleeves.png -------------------------------------------------------------------------------- /templates/unxie_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/templates/unxie_template.png -------------------------------------------------------------------------------- /OutfitGenerator/Resources/animatedBackTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/OutfitGenerator/Resources/animatedBackTemplate.png -------------------------------------------------------------------------------- /OutfitGenerator/Resources/animatedPantsTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/OutfitGenerator/Resources/animatedPantsTemplate.png -------------------------------------------------------------------------------- /OutfitGenerator/Resources/animatedSleevesTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/OutfitGenerator/Resources/animatedSleevesTemplate.png -------------------------------------------------------------------------------- /OutfitGenerator/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /OutfitGenerator/Resources/invisibleAnimatedPantsTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Silverfeelin/Starbound-OutfitGenerator/HEAD/OutfitGenerator/Resources/invisibleAnimatedPantsTemplate.png -------------------------------------------------------------------------------- /OutfitGenerator/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /OutfitGenerator/Mergers/ISpriteMerger.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace OutfitGenerator.Mergers 4 | { 5 | public interface ISpriteMerger 6 | { 7 | Bitmap Merge(string pathA, string pathB); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /OutfitGenerator/Generators/HidingHatGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace OutfitGenerator.Generators 2 | { 3 | class HidingHatGenerator : HatGenerator 4 | { 5 | public override byte[] Config => Properties.Resources.HidingHatConfig; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /OutfitGenerator/Generators/GeneratorException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OutfitGenerator 4 | { 5 | public class GeneratorException : Exception 6 | { 7 | public GeneratorException(string message) : base(message) { } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /OutfitGenerator/Generators/HidingPantsGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace OutfitGenerator.Generators 4 | { 5 | public class HidingPantsGenerator : PantsGenerator 6 | { 7 | public override Bitmap Template => Properties.Resources.invisibleAnimatedPantsTemplate; 8 | 9 | public override byte[] Config => Properties.Resources.HidingPantsConfig; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /OutfitGenerator/Generators/MaskedHatGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace OutfitGenerator.Generators 8 | { 9 | public class MaskedHatGenerator : HatGenerator 10 | { 11 | public override byte[] Config => Properties.Resources.MaskedHatConfig; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /OutfitGenerator/Resources/HatConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "descriptor": { 3 | "name": "eyepatchhead", 4 | "count": 1, 5 | "parameters": { 6 | "shortdescription": "Custom Hat", 7 | "description": "Made using the Outfit Generator.", 8 | "inventoryIcon": "head.png", 9 | "directives": "", 10 | "maleFrames": "head.png", 11 | "femaleFrames": "head.png", 12 | "mask": "mask.png", 13 | "statusEffects": [] 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /OutfitGenerator/Resources/HidingHatConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "descriptor": { 3 | "name": "frogghead", 4 | "count": 1, 5 | "parameters": { 6 | "shortdescription": "Custom Hat", 7 | "description": "Made using the Outfit Generator.", 8 | "inventoryIcon": "head.png", 9 | "directives": "", 10 | "maleFrames": "head.png", 11 | "femaleFrames": "head.png", 12 | "mask": "mask.png", 13 | "statusEffects": [] 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /OutfitGenerator/ItemDescriptor.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | namespace OutfitGenerator 4 | { 5 | [JsonObject] 6 | public class ItemDescriptor 7 | { 8 | [JsonProperty("name")] 9 | public string Name { get; set; } 10 | 11 | [JsonProperty("count")] 12 | public int Count { get; set; } = 1; 13 | 14 | [JsonProperty("parameters")] 15 | public JObject Parameters { get; set; } = new JObject(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /OutfitGenerator/Util/CommandGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace OutfitGenerator.Util 2 | { 3 | public static class CommandGenerator 4 | { 5 | public static string SpawnItem(ItemDescriptor descriptor) 6 | { 7 | string parameters = descriptor.Parameters.ToString(Newtonsoft.Json.Formatting.None); 8 | string command = string.Format("/spawnitem {0} {1} '{2}'", descriptor.Name, descriptor.Count, parameters); 9 | return command; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /OutfitGenerator/Resources/MaskedHatConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "descriptor": { 3 | "name": "eyepatchhead", 4 | "count": 1, 5 | "parameters": { 6 | "shortdescription": "Custom Hat", 7 | "description": "Made using the Outfit Generator.", 8 | "inventoryIcon": "head.png", 9 | "directives": "", 10 | "maleFrames": "head.png", 11 | "femaleFrames": "head.png", 12 | "mask": "?submask=/items/armors/decorative/hats/eyepatch/mask.png", 13 | "statusEffects": [] 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /OutfitGenerator/Util/JsonResourceManager.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace OutfitGenerator.Util 5 | { 6 | public static class JsonResourceManager 7 | { 8 | public static string GetResource(byte[] resource) 9 | { 10 | return Encoding.UTF8.GetString(resource); 11 | } 12 | 13 | public static JObject GetJsonObject(byte[] resource) 14 | { 15 | return JObject.Parse(GetResource(resource)); 16 | } 17 | 18 | public static JArray GetJsonArray(byte[] resource) 19 | { 20 | return JArray.Parse(GetResource(resource)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /OutfitGenerator/Generators/BackGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Drawing; 3 | 4 | namespace OutfitGenerator.Generators 5 | { 6 | public class BackGenerator : ClothingGenerator 7 | { 8 | public override string FileName => "back"; 9 | 10 | private readonly ISet _supportedDimensions = new HashSet() 11 | { 12 | new Size(387,301) 13 | }; 14 | 15 | public override ISet SupportedDimensions => _supportedDimensions; 16 | 17 | public override Bitmap Template => Properties.Resources.animatedBackTemplate; 18 | 19 | public override byte[] Config => Properties.Resources.BackConfig; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /OutfitGenerator/Util/FileSaver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace OutfitGenerator.Util 5 | { 6 | public static class FileSaver 7 | { 8 | /// 9 | /// Saves the given content to the given directory, using a generated file name. 10 | /// 11 | /// 12 | /// 13 | /// Generated file name. 14 | public static string Save(DirectoryInfo directory, string content, string fileNamePrefix = "generatedPants") 15 | { 16 | string file = string.Format("{0}-{1}.txt", fileNamePrefix, DateTime.Now.ToString("h-mm-ss")); 17 | string path = Path.Combine(directory.FullName, file); 18 | File.WriteAllText(path, content); 19 | return file; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /OutfitGenerator/Generators/SleeveGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | 5 | namespace OutfitGenerator.Generators 6 | { 7 | public class SleeveGenerator : ClothingGenerator 8 | { 9 | public override string FileName => "sleeves"; 10 | 11 | private readonly ISet _supportedDimensions = new HashSet() 12 | { 13 | new Size(387,602) 14 | }; 15 | 16 | public override ISet SupportedDimensions => _supportedDimensions; 17 | 18 | public override Bitmap Template => Properties.Resources.animatedSleevesTemplate; 19 | 20 | public override byte[] Config => Properties.Resources.SleevesConfig; 21 | 22 | public override ItemDescriptor Generate(Bitmap bitmap) 23 | { 24 | return base.Generate(bitmap); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /OutfitGenerator/Generators/PantsGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Drawing; 3 | 4 | namespace OutfitGenerator.Generators 5 | { 6 | public class PantsGenerator : ClothingGenerator 7 | { 8 | public override string FileName => "pants"; 9 | 10 | private readonly ISet _supportedDimensions = new HashSet() 11 | { 12 | new Size(387, 258), 13 | new Size(387, 301) 14 | }; 15 | 16 | public override ISet SupportedDimensions => _supportedDimensions; 17 | 18 | public override Bitmap Template => Properties.Resources.animatedPantsTemplate; 19 | 20 | public override byte[] Config => Properties.Resources.PantsConfig; 21 | 22 | public override ItemDescriptor Generate(Bitmap bitmap) 23 | { 24 | if (bitmap?.Height == 301) 25 | { 26 | bitmap = Crop(bitmap, 0, 0, bitmap.Width, 258); 27 | } 28 | 29 | return base.Generate(bitmap); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Silverfeelin, KrashV 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The contents of the folder(s) listed below are property of Chucklefish LTD. or adapted versions thereof. As such, they do not fall under this specific license. 13 | - /templates/ 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | -------------------------------------------------------------------------------- /OutfitGenerator/Generators/IClothingGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Drawing; 3 | 4 | namespace OutfitGenerator.Generators 5 | { 6 | public interface IClothingGenerator 7 | { 8 | /// 9 | /// Format for the exported file name. 10 | /// 11 | string FileName { get; } 12 | /// 13 | /// Gets the supported Bitmap dimensions for the generator, in pixels. 14 | /// 15 | ISet SupportedDimensions { get; } 16 | 17 | /// 18 | /// Gets the template used to generate directives. 19 | /// 20 | Bitmap Template { get; } 21 | 22 | /// 23 | /// Gets the template configuration used to generate item descriptors. 24 | /// 25 | byte[] Config { get; } 26 | 27 | /// 28 | /// Generates an item descriptor from the given image. 29 | /// 30 | /// Expected input and generated output differ per implementation. 31 | /// 32 | /// 33 | /// Non-null Bitmap matching any of . 34 | /// Spawnable Item Descriptor 35 | ItemDescriptor Generate(Bitmap bitmap); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /OutfitGenerator/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("OutfitGenerator")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("OutfitGenerator")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c0bdd3a6-b257-416e-b550-638f65594676")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /OutfitGenerator/Util/ConsoleWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OutfitGenerator 4 | { 5 | /// 6 | /// Simple helper class to write colored text to the standard output. 7 | /// 8 | public class ConsoleWriter 9 | { 10 | public ConsoleColor DefaultColor { get; set; } = ConsoleColor.Cyan; 11 | 12 | public ConsoleWriter() { } 13 | 14 | public ConsoleWriter(ConsoleColor defaultColor) 15 | { 16 | DefaultColor = defaultColor; 17 | } 18 | 19 | public void Write(string str, params object[] args) 20 | { 21 | WriteColored(Console.Write, DefaultColor, str, args); 22 | } 23 | 24 | public void Write(ConsoleColor color, string str, params object[] args) 25 | { 26 | WriteColored(Console.Write, color, str, args); 27 | } 28 | 29 | public void WriteLine(string str, params object[] args) 30 | { 31 | WriteColored(Console.WriteLine, DefaultColor, str, args); 32 | } 33 | 34 | public void WriteLine(ConsoleColor color, string str, params object[] args) 35 | { 36 | WriteColored(Console.WriteLine, color, str, args); 37 | } 38 | 39 | private static void WriteColored(Action writeMethod, ConsoleColor color, string str, params object[] args) 40 | { 41 | ConsoleColor old = Console.ForegroundColor; 42 | Console.ForegroundColor = color; 43 | writeMethod(str, args); 44 | 45 | Console.ResetColor(); 46 | Console.ForegroundColor = old; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Outfit Generator.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OutfitGenerator", "OutfitGenerator\OutfitGenerator.csproj", "{C0BDD3A6-B257-416E-B550-638F65594676}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {C0BDD3A6-B257-416E-B550-638F65594676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {C0BDD3A6-B257-416E-B550-638F65594676}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {C0BDD3A6-B257-416E-B550-638F65594676}.Debug|x64.ActiveCfg = Debug|Any CPU 21 | {C0BDD3A6-B257-416E-B550-638F65594676}.Debug|x64.Build.0 = Debug|Any CPU 22 | {C0BDD3A6-B257-416E-B550-638F65594676}.Debug|x86.ActiveCfg = Debug|Any CPU 23 | {C0BDD3A6-B257-416E-B550-638F65594676}.Debug|x86.Build.0 = Debug|Any CPU 24 | {C0BDD3A6-B257-416E-B550-638F65594676}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {C0BDD3A6-B257-416E-B550-638F65594676}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {C0BDD3A6-B257-416E-B550-638F65594676}.Release|x64.ActiveCfg = Release|Any CPU 27 | {C0BDD3A6-B257-416E-B550-638F65594676}.Release|x64.Build.0 = Release|Any CPU 28 | {C0BDD3A6-B257-416E-B550-638F65594676}.Release|x86.ActiveCfg = Release|Any CPU 29 | {C0BDD3A6-B257-416E-B550-638F65594676}.Release|x86.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {26701B08-65BA-4FE6-9F55-D2379F567715} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /OutfitGenerator/Generators/ClothingGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using Newtonsoft.Json.Linq; 5 | using OutfitGenerator.Util; 6 | 7 | namespace OutfitGenerator.Generators 8 | { 9 | public abstract class ClothingGenerator : IClothingGenerator 10 | { 11 | public abstract string FileName {get;} 12 | 13 | public abstract ISet SupportedDimensions { get; } 14 | 15 | public abstract Bitmap Template { get; } 16 | 17 | public abstract byte[] Config { get; } 18 | 19 | public virtual ItemDescriptor Generate(Bitmap bitmap) 20 | { 21 | // Parse arguments 22 | if (bitmap == null) 23 | { 24 | throw new ArgumentNullException("Bitmap may not be null."); 25 | } 26 | 27 | if (!SupportedDimensions.Contains(bitmap.Size)) 28 | { 29 | throw new ArgumentException("Bitmap does not match any of the expected dimensions: " + String.Join(", ", SupportedDimensions)); 30 | } 31 | 32 | // Load descriptor 33 | JObject config = JsonResourceManager.GetJsonObject(Config); 34 | ItemDescriptor descriptor = config["descriptor"].ToObject(); 35 | 36 | // Generate and apply directives 37 | string directives = descriptor.Parameters["directives"].Value(); 38 | directives = directives.Replace("{directives}", DirectiveGenerator.Generate(Template, bitmap)); 39 | descriptor.Parameters["directives"] = directives; 40 | 41 | return descriptor; 42 | } 43 | 44 | /// 45 | /// Crops out the bottom (unused) row for larger pants sprites. 46 | /// It's not really necessary, but whatever. 47 | /// 48 | public static Bitmap Crop(Bitmap bmp, int x, int y, int width, int height) 49 | { 50 | return bmp.Clone(new Rectangle(x, y, width, height), bmp.PixelFormat); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /OutfitGenerator/Mergers/SleevesMerger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace OutfitGenerator.Mergers 9 | { 10 | public class SleevesMerger : ISpriteMerger 11 | { 12 | private const int SLEEVES_WIDTH = 387; 13 | private const int SLEEVES_HEIGHT = 301; 14 | private static Size sleevesSize = new Size(SLEEVES_WIDTH, SLEEVES_HEIGHT); 15 | 16 | public Bitmap Merge(string firstPath, string secondPath) 17 | { 18 | Bitmap frontSleeves; 19 | Bitmap backSleeves; 20 | 21 | // Predict the correct order: 22 | if (firstPath.ToLower().Contains("fsleeve") && secondPath.ToLower().Contains("bsleeve")) 23 | { 24 | frontSleeves = new Bitmap(firstPath); 25 | backSleeves = new Bitmap(secondPath); 26 | } 27 | else if (firstPath.ToLower().Contains("bsleeve") && secondPath.ToLower().Contains("fsleeve")) 28 | { 29 | frontSleeves = new Bitmap(secondPath); 30 | backSleeves = new Bitmap(firstPath); 31 | } 32 | else //prediction failed 33 | { 34 | Console.WriteLine("Is the order correct?"); 35 | Console.WriteLine("Front sleeve: " + firstPath); 36 | Console.WriteLine("Back sleeve: " + secondPath); 37 | Console.WriteLine("Press Enter if it is, press any key otherwise"); 38 | 39 | if (Console.ReadKey(true).Key == ConsoleKey.Enter) 40 | { 41 | frontSleeves = new Bitmap(firstPath); 42 | backSleeves = new Bitmap(secondPath); 43 | } 44 | else 45 | { 46 | frontSleeves = new Bitmap(secondPath); 47 | backSleeves = new Bitmap(firstPath); 48 | } 49 | } 50 | 51 | return ApllyMultingSleeves(frontSleeves, backSleeves); 52 | } 53 | 54 | private static Bitmap ApllyMultingSleeves(Bitmap frontSleeves, Bitmap backSleeves) 55 | { 56 | Bitmap result = new Bitmap(SLEEVES_WIDTH, SLEEVES_HEIGHT * 2); 57 | 58 | Superimpose(result, frontSleeves, 0, 0); 59 | Superimpose(result, backSleeves, 0, SLEEVES_HEIGHT); 60 | 61 | return result; 62 | } 63 | 64 | private static void Superimpose(Bitmap largeBmp, Bitmap smallBmp, int x, int y) 65 | { 66 | Graphics g = Graphics.FromImage(largeBmp); 67 | g.DrawImage(smallBmp, x, y, smallBmp.Width, smallBmp.Height); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /OutfitGenerator/Resources/HidingPantsConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "descriptor": { 3 | "name": "froggmerchantlegs", 4 | "count": 1, 5 | "parameters": { 6 | "shortdescription": "Custom Pants", 7 | "description": "Made using the Outfit Generator.", 8 | "inventoryIcon": "pantsm.png:idle.2", 9 | "hideBody": true, 10 | "directives": "?scale=0.4?scale=0.7?scale=0.85?crop;6;1;7;2?replace;45572af4=00a10000;4e6530f8=00a20000;4f6631f7=00a30000;445729f7=00a40000;4f6531f5=00a50000;487035fc=00a60000;445e2df8=00a70000;556733f4=00b10000;4a4122f6=00b20000;425929f0=00b30000;3e2f1cb7=00b40000;664d39bb=00b50000;425b2df2=00b60000;54201e7f=00b70000;56322780=00b80000;675831bb=00c10000;5916192a=00c20000;463c20c0=00c30000;36311bb7=00c40000;313c1ebb=00c50000;561b1a29=00c60000;38642bf4=00c70000;60493180=00c80000;415128bb=00d10000;47562ab9=00d20000;4939207f=00d30000;4f432681=00d40000;5a1e1b25=00d50000;5a1e1c25=00d60000;571a1a25=00d70000;62212413=00d80000;29452424=00e10000;96918a25=00e20000;4567347f=00e30000;5a755681=00e40000;427235f3=00e50000?scale=2?blendscreen=/objects/outpost/customsign/signplaceholder.png?replace;01a10000=2ea10000;00a10100=00a12e00;01a10100=2ea12e00;01a20000=2ea20000;00a20100=00a22e00;01a20100=2ea22e00;01a30000=2ea30000;00a30100=00a32e00;01a30100=2ea32e00;01a40000=2ea40000;00a40100=00a42e00;01a40100=2ea42e00;01a50000=2ea50000;00a50100=00a52e00;01a50100=2ea52e00;01a60000=2ea60000;00a60100=00a62e00;01a60100=2ea62e00;01a70000=2ea70000;00a70100=00a72e00;01a70100=2ea72e00;01b10000=2eb10000;00b10100=00b12e00;01b10100=2eb12e00;01b20000=2eb20000;00b20100=00b22e00;01b20100=2eb22e00;01b30000=2eb30000;00b30100=00b32e00;01b30100=2eb32e00;01b40000=2eb40000;00b40100=00b42e00;01b40100=2eb42e00;01b50000=2eb50000;00b50100=00b52e00;01b50100=2eb52e00;01b60000=2eb60000;00b60100=00b62e00;01b60100=2eb62e00;01b70000=2eb70000;00b70100=00b72e00;01b70100=2eb72e00;01b80000=2eb80000;00b80100=00b82e00;01b80100=2eb82e00;01c10000=2ec10000;00c10100=00c12e00;01c10100=2ec12e00;01c20000=2ec20000;00c20100=00c22e00;01c20100=2ec22e00;01c30000=2ec30000;00c30100=00c32e00;01c30100=2ec32e00;01c40000=2ec40000;00c40100=00c42e00;01c40100=2ec42e00;01c50000=2ec50000;00c50100=00c52e00;01c50100=2ec52e00;01c60000=2ec60000;00c60100=00c62e00;01c60100=2ec62e00;01c70000=2ec70000;00c70100=00c72e00;01c70100=2ec72e00;01c80000=2ec80000;00c80100=00c82e00;01c80100=2ec82e00;01d10000=2ed10000;00d10100=00d12e00;01d10100=2ed12e00;01d20000=2ed20000;00d20100=00d22e00;01d20100=2ed22e00;01d30000=2ed30000;00d30100=00d32e00;01d30100=2ed32e00;01d40000=2ed40000;00d40100=00d42e00;01d40100=2ed42e00;01d50000=2ed50000;00d50100=00d52e00;01d50100=2ed52e00;01d60000=2ed60000;00d60100=00d62e00;01d60100=2ed62e00;01d70000=2ed70000;00d70100=00d72e00;01d70100=2ed72e00;01d80000=2ed80000;00d80100=00d82e00;01d80100=2ed82e00;01e10000=2ee10000;00e10100=00e12e00;01e10100=2ee12e00;01e20000=2ee20000;00e20100=00e22e00;01e20100=2ee22e00;01e30000=2ee30000;00e30100=00e32e00;01e30100=2ee32e00;01e40000=2ee40000;00e40100=00e42e00;01e40100=2ee42e00;01e50000=2ee50000;00e50100=00e52e00;01e50100=2ee52e00?scale=47?crop;1;1;44;44{directives}" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Starbound Outfit Generator 2 | 3 | >

You can now use the online Outfit Generator!

4 | 5 | A tool that specializes in creating multiplayer compatible custom animated clothing using [Nettle Boy's directives](http://ilovebacons.com/threads/guide-to-re-animating-clothes-with-json.12019/page-5#post-92288) methods. 6 | 7 | #### Why pants? 8 | 9 | Nettle Boy has found a way to create multiplayer-compatible custom clothing that is fully animated. Hats aren't animated, tops contain only a few frames and back items contain a lot of duplicate frames. All of these options don't work all that well with the method. 10 | 11 | To limit the amount of sprites we need to modify, the pants should be used for the main parts of your outfit (chest, pants, back item). 12 | This does come with a limitation; the sprites are rendered between the front arm and body layer of your character. If your outfit comes with sleeves, you'll still need a chest item for this purpose. 13 | 14 | #### Why not put the entire outfit on the chest piece, then? 15 | 16 | The frames on chest pieces are limited. If you compare any chest sprite sheet with any pants sprite sheet, you'll notice the difference. Putting pants on a chest piece would lead to staggered animations. 17 | 18 | #### But I want a custom outfit, not just pants! 19 | 20 | Since the frames cover your full character, you can draw an entire outfit and put them on one item (which are, in fact, simply pants). It is highly recommended to use the [Starbound Hatter](https://silverfeelin.github.io/Starbound-Hatter/) for custom hats, to conserve data. This should increase overall performance for you and other players. 21 | 22 | For the sleeves of your outfit; these should be drawn on separate sprite sheets. The sleeve generator takes care of this part. 23 | 24 | ## Installation 25 | 26 | This tool requires [.NET Framework 4.5](https://www.microsoft.com/en-US/download/details.aspx?id=30653). 27 | 28 | * Download and extract the [latest release](https://github.com/Silverfeelin/Starbound-OutfitGenerator/releases). 29 | * Make sure both files are in the same folder. The sleeves generator depends on the pants generator to work. 30 | 31 | ## Usage 32 | 33 | * Draw your outfit, or use an existing sprite sheet. Templates can be found in the [templates](https://github.com/Silverfeelin/Starbound-OutfitGenerator/tree/master/templates) folder. 34 | * The climb frames are not used, so it is highly recommended to keep them out of your sprite sheet. 35 | * Drag your sprite sheet (preferably a 32-bit depth PNG) on top of the `OutfitGenerator` executable. 36 | * Select whether you want to make pants, sleeves or a back item by pressing the according key. 37 | * Use the generated `/spawnitem` command in-game. 38 | * The command is saved to a file and also copied directly to your clipboard. 39 | 40 | For custom sleeves, it is highly recommended to use a provided template. The front and back sleeve sheets have to be combined into one sheet for the tool. You can also sprite the front and back sleeves separately and merge them later by dragging both files on top of the application. 41 | -------------------------------------------------------------------------------- /OutfitGenerator/Resources/PantsConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "descriptor": { 3 | "name": "florantier6apants", 4 | "count": 1, 5 | "parameters": { 6 | "shortdescription": "Custom Pants", 7 | "description": "Made using the Outfit Generator.", 8 | "inventoryIcon": "pants.png:idle.2", 9 | "directives": "?replace;ffffff00=ffffff;00000000=ffffff;ffca8a00=ffffff;e0975c00=ffffff;a8563600=ffffff;6f291900=ffffff;9bba3d00=ffffff;48832f00=ffffff;1b4c2a00=ffffff;a4784400=ffffff;754c2300=ffffff;472b1300=ffffff;e7dfbd00=ffffff;320c4000=ffffff?scale=0.4?scale=0.7?replace;a0b03e=00a10000;7e9b35=00a20000;a5ba92=00a30000;769441=00a40000;557743=00a50000;83715c=00a60000;b19c82=00a70000;7c9036=00b10000;757a32=00b20000;91a638=00b30000;748e37=00b40000;746f2c=00b50000;7a8a31=00b60000;608333=00b70000;8f953a=00b80000;736f2f=00c10000;c2d1b4=00c20000;94ae76=00c30000;788e35=00c40000;6f602f=00c50000;6a846e=00c60000;617e34=00c70000;829935=00c80000;d5ddd7=00d10000;a5b4a7=00d20000;a4945f=00d30000;a57f58=00d40000;a98c6c=00d50000;ccc0a4=00d60000;cabb9d=00d70000;a5a381=00d80000;66714c=00e10000;ada788=00e20000;778c34=00e30000;b1917c=00e40000;95ad8f=00e50000?crop;6;2;7;3?scalenearest=2?blendscreen=/objects/outpost/customsign/signplaceholder.png?replace;01a10000=3fa10000;00a10100=00a13f00;01a10100=3fa13f00;01a20000=3fa20000;00a20100=00a23f00;01a20100=3fa23f00;01a30000=3fa30000;00a30100=00a33f00;01a30100=3fa33f00;01a40000=3fa40000;00a40100=00a43f00;01a40100=3fa43f00;01a50000=3fa50000;00a50100=00a53f00;01a50100=3fa53f00;01a60000=3fa60000;00a60100=00a63f00;01a60100=3fa63f00;01a70000=3fa70000;00a70100=00a73f00;01a70100=3fa73f00;01b10000=3fb10000;00b10100=00b13f00;01b10100=3fb13f00;01b20000=3fb20000;00b20100=00b23f00;01b20100=3fb23f00;01b30000=3fb30000;00b30100=00b33f00;01b30100=3fb33f00;01b40000=3fb40000;00b40100=00b43f00;01b40100=3fb43f00;01b50000=3fb50000;00b50100=00b53f00;01b50100=3fb53f00;01b60000=3fb60000;00b60100=00b63f00;01b60100=3fb63f00;01b70000=3fb70000;00b70100=00b73f00;01b70100=3fb73f00;01b80000=3fb80000;00b80100=00b83f00;01b80100=3fb83f00;01c10000=3fc10000;00c10100=00c13f00;01c10100=3fc13f00;01c20000=3fc20000;00c20100=00c23f00;01c20100=3fc23f00;01c30000=3fc30000;00c30100=00c33f00;01c30100=3fc33f00;01c40000=3fc40000;00c40100=00c43f00;01c40100=3fc43f00;01c50000=3fc50000;00c50100=00c53f00;01c50100=3fc53f00;01c60000=3fc60000;00c60100=00c63f00;01c60100=3fc63f00;01c70000=3fc70000;00c70100=00c73f00;01c70100=3fc73f00;01c80000=3fc80000;00c80100=00c83f00;01c80100=3fc83f00;01d10000=3fd10000;00d10100=00d13f00;01d10100=3fd13f00;01d20000=3fd20000;00d20100=00d23f00;01d20100=3fd23f00;01d30000=3fd30000;00d30100=00d33f00;01d30100=3fd33f00;01d40000=3fd40000;00d40100=00d43f00;01d40100=3fd43f00;01d50000=3fd50000;00d50100=00d53f00;01d50100=3fd53f00;01d60000=3fd60000;00d60100=00d63f00;01d60100=3fd63f00;01d70000=3fd70000;00d70100=00d73f00;01d70100=3fd73f00;01d80000=3fd80000;00d80100=00d83f00;01d80100=3fd83f00;01e10000=3fe10000;00e10100=00e13f00;01e10100=3fe13f00;01e20000=3fe20000;00e20100=00e23f00;01e20100=3fe23f00;01e30000=3fe30000;00e30100=00e33f00;01e30100=3fe33f00;01e40000=3fe40000;00e40100=00e43f00;01e40100=3fe43f00;01e50000=3fe50000;00e50100=00e53f00;01e50100=3fe53f00?scale=64?crop;1;1;44;44{directives}" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /OutfitGenerator/Generators/HatGenerator.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using OutfitGenerator.Util; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Drawing; 6 | using System.Text; 7 | 8 | namespace OutfitGenerator.Generators 9 | { 10 | public class HatGenerator : ClothingGenerator 11 | { 12 | public override string FileName => "hat"; 13 | 14 | private readonly ISet _supportedDimensions = new HashSet() 15 | { 16 | new Size(86, 215), 17 | new Size(43, 43) 18 | }; 19 | 20 | public override ISet SupportedDimensions => _supportedDimensions; 21 | 22 | public override Bitmap Template => null; 23 | 24 | public override byte[] Config => Properties.Resources.HatConfig; 25 | 26 | public override ItemDescriptor Generate(Bitmap bitmap) 27 | { 28 | // Parse arguments 29 | if (bitmap == null) 30 | { 31 | throw new ArgumentNullException("Bitmap may not be null."); 32 | } 33 | 34 | if (!SupportedDimensions.Contains(bitmap.Size)) 35 | { 36 | throw new ArgumentException("Bitmap does not match any of the expected dimensions: " + String.Join(", ", SupportedDimensions)); 37 | } 38 | 39 | // Crop 40 | if (bitmap?.Height == 215) 41 | { 42 | bitmap = Crop(bitmap, 43, 0, 43, 43); 43 | } 44 | 45 | // Generate 46 | bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); 47 | 48 | // 01002B00 2B002B00 49 | // 01000100 2B000100 50 | StringBuilder dir = new StringBuilder("?crop;0;0;2;2?replace;fff0=fff;0000=fff?setcolor=fff" + 51 | "?blendmult=/objects/outpost/customsign/signplaceholder.png" + 52 | "?replace;01000101=01000100;02000101=2B000100?replace;01000201=01002B00;02000201=2B002B00" + 53 | "?scale=43?crop=0;0;43;43"); 54 | 55 | dir.Append("?replace"); 56 | for (int x = 0; x < 43; x++) 57 | { 58 | for (int y = 0; y < 43; y++) 59 | { 60 | Color pixel = bitmap.GetPixel(x, y); 61 | if (pixel.A == 0) continue; 62 | 63 | dir.AppendFormat(";{0}00{1}00={2}", 64 | x.ToString("X2"), 65 | y.ToString("X2"), 66 | ColorToString(pixel)); 67 | } 68 | } 69 | 70 | // Load descriptor 71 | JObject config = JsonResourceManager.GetJsonObject(Config); 72 | ItemDescriptor descriptor = config["descriptor"].ToObject(); 73 | 74 | // Generate and apply directives 75 | descriptor.Parameters["directives"] = dir.ToString(); 76 | 77 | return descriptor; 78 | } 79 | 80 | public string ColorToString(Color color) 81 | { 82 | return color.R.ToString("X2") + 83 | color.G.ToString("X2") + 84 | color.B.ToString("X2") + 85 | color.A.ToString("X2"); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /OutfitGenerator/Resources/BackConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "descriptor": { 3 | "name": "tigertailback", 4 | "count": 1, 5 | "parameters": { 6 | "shortdescription": "Custom Back", 7 | "description": "Made using the Outfit Generator.", 8 | "inventoryIcon": "back.png:idle.1", 9 | "directives": "?scale=0.4?scale=0.7?scale=0.84?crop;4;2;5;3?replace;aa836459=00a10000;bb885e4e=00a20000;cb926431=00a30000;cb95693b=00a40000;cf91601c=00a50000;ce966b4b=00a60000;e6c2a50c=00a70000;cc93662e=00b10000;cc8c5921=00b20000;c1895c3d=00b30000;bf885c41=00b40000;cb8e5d2d=00b50000;c7895728=00b60000;ac7c5558=00b70000;b8a99d5d=00b80000;d796610f=00c10000;dc955b0a=00c20000;de965b06=00c30000;c3885730=00c40000;d9945b0d=00c50000;dc955b08=00c60000;da945b0a=00c70000;dfbca11e=00c80000;ce8e5b23=00d10000;dc945b06=00d20000;cf8f5b23=00d30000;9d74526f=00d40000;d28f5916=00d50000;de965b02=00d60000;e0975c00=00d70000;ecc3a200=00d80000;d08f5b1e=00e10000;da945b08=00e20000;cd905f2f=00e30000;d8955e14=00e40000;cc8d5920=00e50000;59504932=00f10000;655c5509=00f20000;7369631b=00f30000;756c665a=00f40000;62574f32=00f50000;877d7782=00f60000;63595277=00f70000;a19d9959=00f80000?scale=2?blendscreen=/objects/outpost/customsign/signplaceholder.png?replace;01a10000=2ea10000;00a10100=00a12e00;01a10100=2ea12e00;01a20000=2ea20000;00a20100=00a22e00;01a20100=2ea22e00;01a30000=2ea30000;00a30100=00a32e00;01a30100=2ea32e00;01a40000=2ea40000;00a40100=00a42e00;01a40100=2ea42e00;01a50000=2ea50000;00a50100=00a52e00;01a50100=2ea52e00;01a60000=2ea60000;00a60100=00a62e00;01a60100=2ea62e00;01a70000=2ea70000;00a70100=00a72e00;01a70100=2ea72e00;01b10000=2eb10000;00b10100=00b12e00;01b10100=2eb12e00;01b20000=2eb20000;00b20100=00b22e00;01b20100=2eb22e00;01b30000=2eb30000;00b30100=00b32e00;01b30100=2eb32e00;01b40000=2eb40000;00b40100=00b42e00;01b40100=2eb42e00;01b50000=2eb50000;00b50100=00b52e00;01b50100=2eb52e00;01b60000=2eb60000;00b60100=00b62e00;01b60100=2eb62e00;01b70000=2eb70000;00b70100=00b72e00;01b70100=2eb72e00;01b80000=2eb80000;00b80100=00b82e00;01b80100=2eb82e00;01c10000=2ec10000;00c10100=00c12e00;01c10100=2ec12e00;01c20000=2ec20000;00c20100=00c22e00;01c20100=2ec22e00;01c30000=2ec30000;00c30100=00c32e00;01c30100=2ec32e00;01c40000=2ec40000;00c40100=00c42e00;01c40100=2ec42e00;01c50000=2ec50000;00c50100=00c52e00;01c50100=2ec52e00;01c60000=2ec60000;00c60100=00c62e00;01c60100=2ec62e00;01c70000=2ec70000;00c70100=00c72e00;01c70100=2ec72e00;01c80000=2ec80000;00c80100=00c82e00;01c80100=2ec82e00;01d10000=2ed10000;00d10100=00d12e00;01d10100=2ed12e00;01d20000=2ed20000;00d20100=00d22e00;01d20100=2ed22e00;01d30000=2ed30000;00d30100=00d32e00;01d30100=2ed32e00;01d40000=2ed40000;00d40100=00d42e00;01d40100=2ed42e00;01d50000=2ed50000;00d50100=00d52e00;01d50100=2ed52e00;01d60000=2ed60000;00d60100=00d62e00;01d60100=2ed62e00;01d70000=2ed70000;00d70100=00d72e00;01d70100=2ed72e00;01d80000=2ed80000;00d80100=00d82e00;01d80100=2ed82e00;01e10000=2ee10000;00e10100=00e12e00;01e10100=2ee12e00;01e20000=2ee20000;00e20100=00e22e00;01e20100=2ee22e00;01e30000=2ee30000;00e30100=00e32e00;01e30100=2ee32e00;01e40000=2ee40000;00e40100=00e42e00;01e40100=2ee42e00;01e50000=2ee50000;00e50100=00e52e00;01e50100=2ee52e00;01f10000=2ef10000;00f10100=00f12e00;01f10100=2ef12e00;01f20000=2ef20000;00f20100=00f22e00;01f20100=2ef22e00;01f30000=2ef30000;00f30100=00f32e00;01f30100=2ef32e00;01f40000=2ef40000;00f40100=00f42e00;01f40100=2ef42e00;01f50000=2ef50000;00f50100=00f52e00;01f50100=2ef52e00;01f60000=2ef60000;00f60100=00f62e00;01f60100=2ef62e00;01f70000=2ef70000;00f70100=00f72e00;01f70100=2ef72e00;01f80000=2ef80000;00f80100=00f82e00;01f80100=2ef82e00?scale=47?crop;1;1;44;44{directives}" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /OutfitGenerator/Generators/DirectiveGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Text; 5 | 6 | namespace OutfitGenerator.Generators 7 | { 8 | public class DirectiveGenerator 9 | { 10 | /// 11 | /// Alpha 0/1 are considered transparent, but #FFFFFF00 on the source bitmap is ignored regardless of the target color. 12 | /// 13 | public static readonly Color TRANSPARENT = Color.FromArgb(0, 255, 255, 255); 14 | 15 | /// 16 | /// Generates a replace directive to change from into to. 17 | /// If the same color is replaced by multiple different colors, only the last one will be remembered (going row by row, left to right, top to bottom). 18 | /// 19 | /// Source image. 20 | /// Target image. 21 | /// Directive string. 22 | public static string Generate(Bitmap from, Bitmap to) 23 | { 24 | if (from == null || to == null) 25 | { 26 | throw new ArgumentNullException("Bitmaps may not be null."); 27 | } 28 | 29 | if (from.Size != to.Size) 30 | { 31 | throw new ArgumentException(string.Format("Bitmap sizes must match.\n" + 32 | "Source: {0}" + 33 | "Target: {1}", 34 | from.Size, 35 | to.Size)); 36 | } 37 | 38 | var size = from.Size; 39 | 40 | Dictionary conversions = new Dictionary(); 41 | 42 | for (int y = 0; y < size.Height; y++) 43 | { 44 | for (int x = 0; x < size.Width; x++) 45 | { 46 | Color colorFrom = from.GetPixel(x, y), 47 | colorTo = to.GetPixel(x, y); 48 | 49 | if (colorFrom.Equals(TRANSPARENT)) continue; 50 | if (colorFrom.Equals(colorTo)) continue; 51 | if (colorTo.A == 0) continue; 52 | 53 | conversions[colorFrom] = colorTo; 54 | } 55 | } 56 | 57 | string directives = CreateDirectives(conversions); 58 | return directives; 59 | } 60 | 61 | /// 62 | /// Converts a Color to a hexidecimal color code, formatted RRGGBBAA. 63 | /// 64 | /// Color to convert. 65 | /// Hexadecimal RRGGBBAA color code. 66 | public static string ColorToString(Color c) 67 | { 68 | string r = c.R.ToString("X2"), 69 | g = c.G.ToString("X2"), 70 | b = c.B.ToString("X2"), 71 | a = c.A.ToString("X2"); 72 | 73 | return (r + g + b + a).ToLower(); 74 | } 75 | 76 | /// 77 | /// Creates a replace directives string that converts all color keys to their respective color values. 78 | /// The order of directives can not be guaranteed, and may not match the order in which entries were added to the dictionary. 79 | /// 80 | /// Table containing all color conversions. Keys are converted to their value counterpart. 81 | /// Replace directives string. 82 | public static string CreateDirectives(Dictionary conversions) 83 | { 84 | StringBuilder directives = new StringBuilder("?replace"); 85 | foreach (KeyValuePair conversion in conversions) 86 | { 87 | directives.AppendFormat(";{0}={1}", ColorToString(conversion.Key), ColorToString(conversion.Value)); 88 | } 89 | return directives.ToString(); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /OutfitGenerator/OutfitGenerator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C0BDD3A6-B257-416E-B550-638F65594676} 8 | Exe 9 | OutfitGenerator 10 | OutfitGenerator 11 | v4.6.1 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | True 69 | True 70 | Resources.resx 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | ResXFileCodeGenerator 97 | Resources.Designer.cs 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /OutfitGenerator/Resources/SleevesConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "descriptor": { 3 | "name": "aviantier6schest", 4 | "count": 1, 5 | "parameters": { 6 | "shortdescription": "Custom Sleeves", 7 | "description": "Made using the Outfit Generator.", 8 | "inventoryIcon": "fsleeve.png:idle.1", 9 | "directives": "?replace;0000=000;fff0=ffff;1b4c2a00=1b4c2a;d1e16000=d1e160;9bba3d00=9bba3d;d93a3a00=d93a3a;93262500=932625;60111900=601119?scale=0.4?blendscreen=/objects/outpost/customsign/signplaceholder.png;0;-4?replace;9c4f32=e10000;702450=e10000;a95b3b=008787;c5764b=008787;837655=008787;6d2785=00c3c3;8939a6=00c3c3;aa5538=00a5a5;bab350=00a5a5;8c3b5f=00a5a5;927d48=00a5a5;aa5536=878700;bab34f=878700;8c3b5e=878700;927d47=878700;7a516b=878700?scale=0.7?replace;c6ca57=a50000;9a6534=a50000;cdd95d=a50000;c6ca58=a50000;cbc359=a50000;cdd95e=a50000;b4a84b=00c3c3;c59550=00c3c3;c5ca66=00c3c3;998367=00c3c3;c7d360=00c3c3;625684=00c3c3;afdc72=00c3c3;96d87f=00c3c3;95d87f=00c3c3?scale=0.85?replace;cfd75c=a50000;c1913e=a50000;cbc454=a50000;ccc655=a50000;cad05d=a50000;9e6740=a50000;be9643=a50000;ccc957=a50000?scale=0.925?scale=0.9625?crop;5;4;6;5?replace;ccd85f=00a10000;cdd85f=00a20000;cddb5f=00a30000;c29f45=00a40000;cdda5f=00a50000;a6ba53=00a60000;bcbc5d=00b10000;c6cd61=00b20000;c3ca5e=00b30000;cfdb61=00b40000;d0de5f=00b50000;989558=00b60000;ad9b5a=00c10000;b7a256=00c20000;c6c861=00c30000;cfdd61=00c40000;cfdb60=00c50000;bfc05a=00d10000;d0dd61=00d20000;cadd64=00d30000;ccdc61=00d40000;cfdc60=00d50000;d1de61=00d60000;d1df61=00d70000;a6bb52=00d80000;c1bf5c=00e10000;bfbd58=00e20000;cbd35d=00e30000;b3a051=00e40000;cad15f=00e50000;c6c25f=00e60000;a5af55=00e70000;cdda60=00f10000;a4b754=00f20000;5a4837=00600000;b9b263=00610000;b2a660=00620000;b9b663=00630000;b4ab64=00640000;b5b958=00650000;b2a36c=00660000;b7b068=00670000;bcb768=00680000;a89e5d=00690000;b4ac65=006a0000;bebe60=006b0000;c2c166=006c0000;b9b464=006d0000;a89e5e=006e0000;c0bf61=006f0000;b6b65a=00700000;6e603e=00710000;a58c59=00720000;b6b45b=00730000;c2c062=00740000;b9b85a=00750000;b9bc5b=00760000;c4cb5d=00770000;b7bd57=00780000;70673a=00790000;babb5b=007a0000;b7b65a=007b0000;b8ab50=007c0000;c9cb68=007d0000;b9b75f=007e0000;bec060=007f0000;a89e5c=00800000;b6bb59=00810000;5e3834=ffffff00;aa8344=ffffff00;7e6646=ffffff00;828349=ffffff00;886a48=ffffff00;8d6d50=ffffff00;754f43=ffffff00;9a6639=ffffff00;825e46=ffffff00;848248=ffffff00;9a7051=ffffff00;85644e=ffffff00?scalenearest=2?blendscreen=/objects/outpost/customsign/signplaceholder.png?replace;01a10000=2ea10000;00a10100=00a12e00;01a10100=2ea12e00;01a20000=2ea20000;00a20100=00a22e00;01a20100=2ea22e00;01a30000=2ea30000;00a30100=00a32e00;01a30100=2ea32e00;01a40000=2ea40000;00a40100=00a42e00;01a40100=2ea42e00;01a50000=2ea50000;00a50100=00a52e00;01a50100=2ea52e00;01a60000=2ea60000;00a60100=00a62e00;01a60100=2ea62e00;01b10000=2eb10000;00b10100=00b12e00;01b10100=2eb12e00;01b20000=2eb20000;00b20100=00b22e00;01b20100=2eb22e00;01b30000=2eb30000;00b30100=00b32e00;01b30100=2eb32e00;01b40000=2eb40000;00b40100=00b42e00;01b40100=2eb42e00;01b50000=2eb50000;00b50100=00b52e00;01b50100=2eb52e00;01b60000=2eb60000;00b60100=00b62e00;01b60100=2eb62e00;01c10000=2ec10000;00c10100=00c12e00;01c10100=2ec12e00;01c20000=2ec20000;00c20100=00c22e00;01c20100=2ec22e00;01c30000=2ec30000;00c30100=00c32e00;01c30100=2ec32e00;01c40000=2ec40000;00c40100=00c42e00;01c40100=2ec42e00;01c50000=2ec50000;00c50100=00c52e00;01c50100=2ec52e00;01d10000=2ed10000;00d10100=00d12e00;01d10100=2ed12e00;01d20000=2ed20000;00d20100=00d22e00;01d20100=2ed22e00;01d30000=2ed30000;00d30100=00d32e00;01d30100=2ed32e00;01d40000=2ed40000;00d40100=00d42e00;01d40100=2ed42e00;01d50000=2ed50000;00d50100=00d52e00;01d50100=2ed52e00;01d60000=2ed60000;00d60100=00d62e00;01d60100=2ed62e00;01d70000=2ed70000;00d70100=00d72e00;01d70100=2ed72e00;01d80000=2ed80000;00d80100=00d82e00;01d80100=2ed82e00;01e10000=2ee10000;00e10100=00e12e00;01e10100=2ee12e00;01e20000=2ee20000;00e20100=00e22e00;01e20100=2ee22e00;01e30000=2ee30000;00e30100=00e32e00;01e30100=2ee32e00;01e40000=2ee40000;00e40100=00e42e00;01e40100=2ee42e00;01e50000=2ee50000;00e50100=00e52e00;01e50100=2ee52e00;01e60000=2ee60000;00e60100=00e62e00;01e60100=2ee62e00;01e70000=2ee70000;00e70100=00e72e00;01e70100=2ee72e00;01f10000=2ef10000;00f10100=00f12e00;01f10100=2ef12e00;01f20000=2ef20000;00f20100=00f22e00;01f20100=2ef22e00;01600000=2e600000;00600100=00602e00;01600100=2e602e00;01610000=2e610000;00610100=00612e00;01610100=2e612e00;01620000=2e620000;00620100=00622e00;01620100=2e622e00;01630000=2e630000;00630100=00632e00;01630100=2e632e00;01640000=2e640000;00640100=00642e00;01640100=2e642e00;01650000=2e650000;00650100=00652e00;01650100=2e652e00;01660000=2e660000;00660100=00662e00;01660100=2e662e00;01670000=2e670000;00670100=00672e00;01670100=2e672e00;01680000=2e680000;00680100=00682e00;01680100=2e682e00;01690000=2e690000;00690100=00692e00;01690100=2e692e00;016a0000=2e6a0000;006a0100=006a2e00;016a0100=2e6a2e00;016b0000=2e6b0000;006b0100=006b2e00;016b0100=2e6b2e00;016c0000=2e6c0000;006c0100=006c2e00;016c0100=2e6c2e00;016d0000=2e6d0000;006d0100=006d2e00;016d0100=2e6d2e00;016e0000=2e6e0000;006e0100=006e2e00;016e0100=2e6e2e00;016f0000=2e6f0000;006f0100=006f2e00;016f0100=2e6f2e00;01700000=2e700000;00700100=00702e00;01700100=2e702e00;01710000=2e710000;00710100=00712e00;01710100=2e712e00;01720000=2e720000;00720100=00722e00;01720100=2e722e00;01730000=2e730000;00730100=00732e00;01730100=2e732e00;01740000=2e740000;00740100=00742e00;01740100=2e742e00;01750000=2e750000;00750100=00752e00;01750100=2e752e00;01760000=2e760000;00760100=00762e00;01760100=2e762e00;01770000=2e770000;00770100=00772e00;01770100=2e772e00;01780000=2e780000;00780100=00782e00;01780100=2e782e00;01790000=2e790000;00790100=00792e00;01790100=2e792e00;017a0000=2e7a0000;007a0100=007a2e00;017a0100=2e7a2e00;017b0000=2e7b0000;007b0100=007b2e00;017b0100=2e7b2e00;017c0000=2e7c0000;007c0100=007c2e00;017c0100=2e7c2e00;017d0000=2e7d0000;007d0100=007d2e00;017d0100=2e7d2e00;017e0000=2e7e0000;007e0100=007e2e00;017e0100=2e7e2e00;017f0000=2e7f0000;007f0100=007f2e00;017f0100=2e7f2e00;01800000=2e800000;00800100=00802e00;01800100=2e802e00;01810000=2e810000;00810100=00812e00;01810100=2e812e00?scale=47?crop;1;1;44;44{directives}" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /OutfitGenerator/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using Newtonsoft.Json; 11 | using OutfitGenerator.Generators; 12 | using OutfitGenerator.Mergers; 13 | using OutfitGenerator.Util; 14 | 15 | namespace OutfitGenerator 16 | { 17 | class Program 18 | { 19 | private static ConsoleWriter writer; 20 | private static readonly Dictionary visualNames = new Dictionary() 21 | { 22 | { typeof(HatGenerator), "Hat" }, 23 | { typeof(MaskedHatGenerator), "Hat (Hide hair)" }, 24 | { typeof(HidingHatGenerator), "Hat (Hide body)" }, 25 | { typeof(SleeveGenerator), "Sleeves" }, 26 | { typeof(PantsGenerator), "Pants" }, 27 | { typeof(HidingPantsGenerator), "Pants (Hide body)" }, 28 | { typeof(BackGenerator), "Back Item" }, 29 | 30 | { typeof(ChestPantsMerger), "Merge Chest & Pants" }, 31 | { typeof(SleevesMerger), "Merge Sleeves" } 32 | }; 33 | 34 | // TODO: Clean this method up a bit more. 35 | [STAThread] 36 | static void Main(string[] args) 37 | { 38 | writer = new ConsoleWriter(ConsoleColor.Cyan); 39 | writer.WriteLine("= Outfit Generator"); 40 | Console.WriteLine(); 41 | 42 | writer.WriteLine("Select the desired generator:"); 43 | 44 | if (args.Length > 2) 45 | { 46 | WaitAndExit($"Invalid number of parameters: {args.Length}. Expected 1 for outfit generation or 2 for sprite merging."); 47 | return; 48 | } 49 | 50 | // Read images. 51 | Bitmap[] images = new Bitmap[args.Length]; 52 | try 53 | { 54 | for (int i = 0; i < args.Length; i++) 55 | { 56 | images[i] = new Bitmap(args[i]); 57 | } 58 | } 59 | catch (Exception exc) 60 | { 61 | WaitAndExit("Couldn't read image file: {0}", exc.Message); 62 | return; 63 | } 64 | 65 | // Prompt generator/merge type. 66 | Type generatorType; 67 | switch (args.Length) 68 | { 69 | case 0: 70 | WaitAndExit("Please drag and drop a valid image on the application (not this window)."); 71 | break; 72 | case 1: 73 | // Prompt clothing to generate 74 | generatorType = SelectGenerator(typeof(HatGenerator), typeof(MaskedHatGenerator), typeof(HidingHatGenerator), typeof(SleeveGenerator), typeof(PantsGenerator), typeof(HidingPantsGenerator), typeof(BackGenerator)); 75 | 76 | // Generate clothing 77 | IClothingGenerator generator = (IClothingGenerator)Activator.CreateInstance(generatorType); 78 | GenerateClothing(generator, images[0]); 79 | break; 80 | case 2: 81 | // Prompt sprites to merge 82 | generatorType = SelectGenerator(typeof(ChestPantsMerger), typeof(SleevesMerger)); 83 | 84 | // Merge sprites 85 | ISpriteMerger merger = (ISpriteMerger)Activator.CreateInstance(generatorType); 86 | MergeSprites(merger, args[0], args[1]); 87 | break; 88 | } 89 | } 90 | 91 | private static Type SelectGenerator(params Type[] typeNames) 92 | { 93 | for (int i = 0; i < typeNames.Length; i++) 94 | { 95 | string visualName = visualNames[typeNames[i]]; 96 | writer.WriteLine($"[{i + 1}]: {visualName}"); 97 | } 98 | 99 | while (true) 100 | { 101 | ConsoleKeyInfo cki = Console.ReadKey(true); 102 | 103 | if (char.IsNumber(cki.KeyChar)) 104 | { 105 | int choice = (cki.KeyChar - '0'); 106 | 107 | if (--choice < typeNames.Length) 108 | return typeNames[choice]; 109 | } 110 | } 111 | } 112 | 113 | private static void GenerateClothing(IClothingGenerator generator, Bitmap bmp) 114 | { 115 | try 116 | { 117 | ItemDescriptor item = generator.Generate(bmp); 118 | string s = CommandGenerator.SpawnItem(item); 119 | string file = string.Format("{0} {1}.txt", generator.FileName, DateTime.Now.ToString("MM-dd h.mm.ss")); 120 | File.WriteAllText(file, s); 121 | Clipboard.SetText(s); 122 | WaitAndExit("Saved command to {0} and copied into clipboard", file); 123 | } 124 | catch (Exception exc) 125 | { 126 | WaitAndExit("Failed to create clothing: {0}", exc.Message); 127 | } 128 | } 129 | 130 | private static void MergeSprites(ISpriteMerger merger, string a, string b) 131 | { 132 | try 133 | { 134 | Bitmap merged = merger.Merge(a, b); 135 | string file = string.Format("merged {0}.png", DateTime.Now.ToString("MM-dd h.mm.ss")); 136 | merged.Save(file); 137 | Console.WriteLine("Saved image to {0}", file); 138 | 139 | //Propt to work with the saved image 140 | if (PromptWorking()) 141 | { 142 | // Prompt clothing to generate 143 | Type generatorType = SelectGenerator(typeof(HatGenerator), typeof(MaskedHatGenerator), typeof(HidingHatGenerator), typeof(SleeveGenerator), typeof(PantsGenerator), typeof(HidingPantsGenerator), typeof(BackGenerator)); 144 | 145 | // Generate clothing 146 | IClothingGenerator generator = (IClothingGenerator)Activator.CreateInstance(generatorType); 147 | GenerateClothing(generator, merged); 148 | } 149 | else 150 | { 151 | WaitAndExit("Saved image to {0}", file); 152 | } 153 | } 154 | catch (Exception exc) 155 | { 156 | WaitAndExit("Failed to merge sprites: {0}", exc.Message); 157 | } 158 | } 159 | 160 | private static bool PromptWorking() 161 | { 162 | writer.WriteLine("Would you like to proceed the result? y/n"); 163 | string answer = Console.ReadLine(); 164 | return answer.ToLower().Equals("y") || answer.ToLower().Equals("yes"); 165 | } 166 | 167 | public static void WaitAndExit(string message = null, params object[] args) 168 | { 169 | if (!string.IsNullOrEmpty(message)) 170 | { 171 | Console.WriteLine(message, args); 172 | } 173 | writer.WriteLine("Press any key to exit..."); 174 | 175 | Console.ReadKey(true); 176 | 177 | Environment.Exit(0); 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /OutfitGenerator/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace OutfitGenerator.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OutfitGenerator.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap animatedBackTemplate { 67 | get { 68 | object obj = ResourceManager.GetObject("animatedBackTemplate", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Bitmap. 75 | /// 76 | internal static System.Drawing.Bitmap animatedPantsTemplate { 77 | get { 78 | object obj = ResourceManager.GetObject("animatedPantsTemplate", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Looks up a localized resource of type System.Drawing.Bitmap. 85 | /// 86 | internal static System.Drawing.Bitmap animatedSleevesTemplate { 87 | get { 88 | object obj = ResourceManager.GetObject("animatedSleevesTemplate", resourceCulture); 89 | return ((System.Drawing.Bitmap)(obj)); 90 | } 91 | } 92 | 93 | /// 94 | /// Looks up a localized resource of type System.Byte[]. 95 | /// 96 | internal static byte[] BackConfig { 97 | get { 98 | object obj = ResourceManager.GetObject("BackConfig", resourceCulture); 99 | return ((byte[])(obj)); 100 | } 101 | } 102 | 103 | /// 104 | /// Looks up a localized resource of type System.Byte[]. 105 | /// 106 | internal static byte[] HatConfig { 107 | get { 108 | object obj = ResourceManager.GetObject("HatConfig", resourceCulture); 109 | return ((byte[])(obj)); 110 | } 111 | } 112 | 113 | /// 114 | /// Looks up a localized resource of type System.Byte[]. 115 | /// 116 | internal static byte[] HidingHatConfig { 117 | get { 118 | object obj = ResourceManager.GetObject("HidingHatConfig", resourceCulture); 119 | return ((byte[])(obj)); 120 | } 121 | } 122 | 123 | /// 124 | /// Looks up a localized resource of type System.Byte[]. 125 | /// 126 | internal static byte[] HidingPantsConfig { 127 | get { 128 | object obj = ResourceManager.GetObject("HidingPantsConfig", resourceCulture); 129 | return ((byte[])(obj)); 130 | } 131 | } 132 | 133 | /// 134 | /// Looks up a localized resource of type System.Drawing.Bitmap. 135 | /// 136 | internal static System.Drawing.Bitmap invisibleAnimatedPantsTemplate { 137 | get { 138 | object obj = ResourceManager.GetObject("invisibleAnimatedPantsTemplate", resourceCulture); 139 | return ((System.Drawing.Bitmap)(obj)); 140 | } 141 | } 142 | 143 | /// 144 | /// Looks up a localized resource of type System.Byte[]. 145 | /// 146 | internal static byte[] MaskedHatConfig { 147 | get { 148 | object obj = ResourceManager.GetObject("MaskedHatConfig", resourceCulture); 149 | return ((byte[])(obj)); 150 | } 151 | } 152 | 153 | /// 154 | /// Looks up a localized resource of type System.Byte[]. 155 | /// 156 | internal static byte[] PantsConfig { 157 | get { 158 | object obj = ResourceManager.GetObject("PantsConfig", resourceCulture); 159 | return ((byte[])(obj)); 160 | } 161 | } 162 | 163 | /// 164 | /// Looks up a localized resource of type System.Byte[]. 165 | /// 166 | internal static byte[] SleevesConfig { 167 | get { 168 | object obj = ResourceManager.GetObject("SleevesConfig", resourceCulture); 169 | return ((byte[])(obj)); 170 | } 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /OutfitGenerator/Mergers/ChestPantsMerger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Drawing.Drawing2D; 4 | 5 | namespace OutfitGenerator.Mergers 6 | { 7 | public class ChestPantsMerger : ISpriteMerger 8 | { 9 | private const int FRAME_SIZE = 43; 10 | private const int PANTS_WIDTH = 387; 11 | private const int PANTS_HEIGHT = 258; 12 | private const int PANTS_OLD_HEIGHT = 301; 13 | private const int CHEST_WIDTH = 86; 14 | private const int CHEST_HEIGHT = 258; 15 | 16 | private static Size chestSize = new Size(CHEST_WIDTH, CHEST_HEIGHT); 17 | private static Size pantsSize = new Size(PANTS_WIDTH, PANTS_HEIGHT); 18 | private static Size pantsOldSize = new Size(PANTS_WIDTH, PANTS_OLD_HEIGHT); 19 | 20 | public Bitmap Merge(string firstPath, string secondPath) 21 | { 22 | Bitmap chestBitmap; 23 | Bitmap pantsBitmap; 24 | 25 | // Predict the correct order: 26 | if (firstPath.ToLower().Contains("chest") && secondPath.ToLower().Contains("pants")) 27 | { 28 | chestBitmap = new Bitmap(firstPath); 29 | pantsBitmap = new Bitmap(secondPath); 30 | } 31 | else if (firstPath.ToLower().Contains("pants") && secondPath.ToLower().Contains("chest")) 32 | { 33 | chestBitmap = new Bitmap(secondPath); 34 | pantsBitmap = new Bitmap(firstPath); 35 | } 36 | else //prediction failed 37 | { 38 | Console.WriteLine("Is the order correct?"); 39 | Console.WriteLine("Chest image: " + firstPath); 40 | Console.WriteLine("Pants image: " + secondPath); 41 | Console.WriteLine("Press Enter if it is, press any key otherwise"); 42 | 43 | if (Console.ReadKey(true).Key == ConsoleKey.Enter) 44 | { 45 | chestBitmap = new Bitmap(firstPath); 46 | pantsBitmap = new Bitmap(secondPath); 47 | } 48 | else 49 | { 50 | chestBitmap = new Bitmap(secondPath); 51 | pantsBitmap = new Bitmap(firstPath); 52 | } 53 | } 54 | 55 | return ApplyMultingChestPants(chestBitmap, pantsBitmap); 56 | } 57 | 58 | private static Bitmap ApplyMultingChestPants(Bitmap chest, Bitmap pants) 59 | { 60 | Bitmap result = new Bitmap(pants); 61 | 62 | Bitmap chestIdle = chest.Clone(new Rectangle(FRAME_SIZE, 0, FRAME_SIZE, FRAME_SIZE), chest.PixelFormat); 63 | Bitmap chestIdle2 = chest.Clone(new Rectangle(0, FRAME_SIZE, FRAME_SIZE, FRAME_SIZE), chest.PixelFormat); 64 | Bitmap chestIdle3 = chest.Clone(new Rectangle(FRAME_SIZE, FRAME_SIZE, FRAME_SIZE, FRAME_SIZE), chest.PixelFormat); 65 | 66 | Bitmap chestRun = chest.Clone(new Rectangle(FRAME_SIZE, FRAME_SIZE * 2, FRAME_SIZE, FRAME_SIZE), chest.PixelFormat); 67 | 68 | Bitmap chestDuck = chest.Clone(new Rectangle(FRAME_SIZE, FRAME_SIZE * 3, FRAME_SIZE, FRAME_SIZE), chest.PixelFormat); 69 | 70 | Bitmap chestClimb = chest.Clone(new Rectangle(FRAME_SIZE, FRAME_SIZE * 4, FRAME_SIZE, FRAME_SIZE), chest.PixelFormat); 71 | Bitmap chestSwim = chest.Clone(new Rectangle(FRAME_SIZE, FRAME_SIZE * 5, FRAME_SIZE, FRAME_SIZE), chest.PixelFormat); 72 | 73 | // Personality 1,5 74 | SuperImpose(result, chestIdle, FRAME_SIZE, 0); 75 | SuperImpose(result, chestIdle, FRAME_SIZE * 5, 0); 76 | 77 | // Personality 2,4 78 | SuperImpose(result, chestIdle2, FRAME_SIZE * 2, 0); 79 | SuperImpose(result, chestIdle2, FRAME_SIZE * 4, 0); 80 | 81 | // Personality 3 82 | SuperImpose(result, chestIdle3, FRAME_SIZE * 3, 0); 83 | 84 | // Duck 85 | SuperImpose(result, chestDuck, 344, 0); 86 | 87 | // Sit 88 | SuperImpose(result, chestIdle, 258, 1); 89 | 90 | // Walking 91 | SuperImpose(result, chestIdle, FRAME_SIZE, FRAME_SIZE + 1); 92 | SuperImpose(result, chestIdle, FRAME_SIZE * 2, FRAME_SIZE + 2); 93 | SuperImpose(result, chestIdle, FRAME_SIZE * 3, FRAME_SIZE + 1); 94 | SuperImpose(result, chestIdle, FRAME_SIZE * 4, FRAME_SIZE); 95 | SuperImpose(result, chestIdle, FRAME_SIZE * 5, FRAME_SIZE + 1); 96 | SuperImpose(result, chestIdle, FRAME_SIZE * 6, FRAME_SIZE + 2); 97 | SuperImpose(result, chestIdle, FRAME_SIZE * 7, FRAME_SIZE + 1); 98 | SuperImpose(result, chestIdle, FRAME_SIZE * 8, FRAME_SIZE); 99 | 100 | // Running 101 | SuperImpose(result, chestRun, FRAME_SIZE, FRAME_SIZE * 2); 102 | SuperImpose(result, chestRun, FRAME_SIZE * 2, FRAME_SIZE * 2 - 1); 103 | SuperImpose(result, chestRun, FRAME_SIZE * 3, FRAME_SIZE * 2); 104 | SuperImpose(result, chestRun, FRAME_SIZE * 4, FRAME_SIZE * 2 + 1); 105 | SuperImpose(result, chestRun, FRAME_SIZE * 5, FRAME_SIZE * 2); 106 | SuperImpose(result, chestRun, FRAME_SIZE * 6, FRAME_SIZE * 2 - 1); 107 | SuperImpose(result, chestRun, FRAME_SIZE * 7, FRAME_SIZE * 2); 108 | SuperImpose(result, chestRun, FRAME_SIZE * 8, FRAME_SIZE * 2 + 1); 109 | 110 | // Jumping 111 | SuperImpose(result, chestIdle, FRAME_SIZE, FRAME_SIZE * 3 - 1); 112 | SuperImpose(result, chestIdle, FRAME_SIZE * 2, FRAME_SIZE * 3 - 1); 113 | SuperImpose(result, chestIdle, FRAME_SIZE * 3, FRAME_SIZE * 3 - 1); 114 | SuperImpose(result, chestIdle, FRAME_SIZE * 4, FRAME_SIZE * 3 - 1); 115 | 116 | // Falling 117 | SuperImpose(result, chestIdle, FRAME_SIZE * 5, FRAME_SIZE * 3 - 1); 118 | SuperImpose(result, chestIdle, FRAME_SIZE * 6, FRAME_SIZE * 3 - 1); 119 | SuperImpose(result, chestIdle, FRAME_SIZE * 7, FRAME_SIZE * 3 - 1); 120 | SuperImpose(result, chestIdle, FRAME_SIZE * 8, FRAME_SIZE * 3 - 1); 121 | 122 | // Climbing 123 | SuperImpose(result, chestClimb, FRAME_SIZE, FRAME_SIZE * 4); 124 | SuperImpose(result, chestClimb, FRAME_SIZE * 2, FRAME_SIZE * 4); 125 | SuperImpose(result, chestClimb, FRAME_SIZE * 3, FRAME_SIZE * 4); 126 | SuperImpose(result, chestClimb, FRAME_SIZE * 4, FRAME_SIZE * 4); 127 | SuperImpose(result, chestClimb, FRAME_SIZE * 5, FRAME_SIZE * 4); 128 | SuperImpose(result, chestClimb, FRAME_SIZE * 6, FRAME_SIZE * 4); 129 | SuperImpose(result, chestClimb, FRAME_SIZE * 7, FRAME_SIZE * 4); 130 | SuperImpose(result, chestClimb, FRAME_SIZE * 8, FRAME_SIZE * 4); 131 | 132 | // Swimming 133 | SuperImpose(result, chestSwim, FRAME_SIZE, FRAME_SIZE * 5); 134 | SuperImpose(result, chestSwim, FRAME_SIZE * 4, FRAME_SIZE * 5); 135 | SuperImpose(result, chestSwim, FRAME_SIZE * 5, FRAME_SIZE * 5 + 1); 136 | SuperImpose(result, chestSwim, FRAME_SIZE * 6, FRAME_SIZE * 5 + 2); 137 | SuperImpose(result, chestSwim, FRAME_SIZE * 7, FRAME_SIZE * 5 + 1); 138 | 139 | return result; 140 | } 141 | 142 | private static void SuperImpose( Bitmap largeBmp, Bitmap smallBmp, int x, int y) 143 | { 144 | Graphics g = Graphics.FromImage(largeBmp); 145 | g.DrawImage(smallBmp, x, y, smallBmp.Width, smallBmp.Height); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /OutfitGenerator/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\animatedBackTemplate.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\Resources\animatedPantsTemplate.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | 128 | ..\Resources\animatedSleevesTemplate.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | 131 | ..\Resources\BackConfig.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 132 | 133 | 134 | ..\Resources\HatConfig.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 135 | 136 | 137 | ..\Resources\invisibleAnimatedPantsTemplate.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 138 | 139 | 140 | ..\Resources\PantsConfig.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 141 | 142 | 143 | ..\Resources\SleevesConfig.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 144 | 145 | 146 | ..\Resources\HidingPantsConfig.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 147 | 148 | 149 | ..\Resources\HidingHatConfig.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 150 | 151 | 152 | ..\Resources\MaskedHatConfig.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 153 | 154 | --------------------------------------------------------------------------------