From 561296f2699bcfff93d22b8a67182308998cfb31 Mon Sep 17 00:00:00 2001 From: Samuel Barney Date: Mon, 13 Jul 2015 18:15:37 -0600 Subject: Leather Armor can now be dyed. * Created new color class to handle dye-related coloring --- MCServer/crafting.txt | 38 +++++++++ src/CMakeLists.txt | 1 + src/Color.cpp | 76 +++++++++++++++++ src/Color.h | 58 +++++++++++++ src/CraftingRecipes.cpp | 192 +++++++++++++++++++++++++++++++++++++++++++ src/CraftingRecipes.h | 3 + src/Item.cpp | 19 +++++ src/Item.h | 11 ++- src/Protocol/Protocol17x.cpp | 14 +++- src/Protocol/Protocol18x.cpp | 14 +++- 10 files changed, 420 insertions(+), 6 deletions(-) create mode 100644 src/Color.cpp create mode 100644 src/Color.h diff --git a/MCServer/crafting.txt b/MCServer/crafting.txt index b08a82f2f..8adc00fa2 100644 --- a/MCServer/crafting.txt +++ b/MCServer/crafting.txt @@ -552,6 +552,44 @@ GlisteringMelon = MelonSlice, 2:2 | GoldNugget, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1 GoldNugget, 9 = GoldIngot, * EnchantmentTable = Obsidian, 1:3, 2:3, 3:3, 2:2 | Diamond, 1:2, 3:2 | Book, 2:1 +#******************************************************# +# Dyed Armor +# Do not modify +LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * +LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * +LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherHelmet = LeatherHelmet^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * + +LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * +LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * +LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherChestplate = LeatherChestplate^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * + +LeatherPants = LeatherPants^-1, * | Dye^-1, * +LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * +LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherPants = LeatherPants^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * + +LeatherBoots = LeatherBoots^-1, * | Dye^-1, * +LeatherBoots = LeatherBoots^-1, * | Dye^-1, * | Dye^-1, * +LeatherBoots = LeatherBoots^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherBoots = LeatherBoots^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherBoots = LeatherBoots^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherBoots = LeatherBoots^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherBoots = LeatherBoots^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * +LeatherBoots = LeatherBoots^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * | Dye^-1, * #******************************************************# # Fireworks & Co. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6c8fe7d0c..d941b7d4c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,6 +28,7 @@ SET (SRCS ChunkSender.cpp ChunkStay.cpp ClientHandle.cpp + Color.cpp CommandOutput.cpp CompositeChat.cpp CraftingRecipes.cpp diff --git a/src/Color.cpp b/src/Color.cpp new file mode 100644 index 000000000..f2180e2d9 --- /dev/null +++ b/src/Color.cpp @@ -0,0 +1,76 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Color.h" + + + + + +#define COLOR_RED_BITS 0x00FF0000 +#define COLOR_GREEN_BITS 0x0000FF00 +#define COLOR_BLUE_BITS 0x000000FF +#define COLOR_RED_OFFSET 16 +#define COLOR_GREEN_OFFSET 8 + + + + + +void cColor::SetColor(unsigned char a_Red, unsigned char a_Green, unsigned char a_Blue) +{ + m_Color = (static_cast(a_Red) << COLOR_RED_OFFSET) + (static_cast(a_Green) << COLOR_GREEN_OFFSET) + (static_cast(a_Blue)); +} + + + + + +void cColor::SetRed(unsigned char a_Red) +{ + m_Color = (static_cast(a_Red) << COLOR_RED_OFFSET) + ((COLOR_GREEN_BITS | COLOR_BLUE_BITS) & m_Color); +} + + + + + +void cColor::SetGreen(unsigned char a_Green) +{ + m_Color = (static_cast(a_Green) << COLOR_GREEN_OFFSET) + ((COLOR_RED_BITS | COLOR_BLUE_BITS) & m_Color); +} + + + + + +void cColor::SetBlue(unsigned char a_Blue) +{ + m_Color = static_cast(a_Blue) + ((COLOR_RED_BITS | COLOR_GREEN_BITS) & m_Color); +} + + + + + +unsigned char cColor::GetRed() const +{ + return (m_Color & COLOR_RED_BITS) >> COLOR_RED_OFFSET; +} + + + + + +unsigned char cColor::GetGreen() const +{ + return (m_Color & COLOR_GREEN_BITS) >> COLOR_GREEN_OFFSET; +} + + + + + +unsigned char cColor::GetBlue() const +{ + return m_Color & COLOR_BLUE_BITS; +} diff --git a/src/Color.h b/src/Color.h new file mode 100644 index 000000000..a90e8372b --- /dev/null +++ b/src/Color.h @@ -0,0 +1,58 @@ + +// Color.h + +// Declares a class to handle item color related code + + + + + +#pragma once + + // tolua_begin + +class cColor +{ +public: + + enum + { + COLOR_MIN = 0, + COLOR_MAX = 255, + COLOR_LIMIT = 256, + COLOR_NONE = 0xFFFFFFFF, + }; + cColor() { m_Color = COLOR_NONE;} + cColor(unsigned char a_Red, unsigned char a_Green, unsigned char a_Blue) { SetColor(a_Red, a_Green, a_Blue); } + + /// Returns whether the color is a valid color + bool IsValid() const { return m_Color != COLOR_NONE; } + + /// Changes the color + void SetColor(unsigned char a_Red, unsigned char a_Green, unsigned char a_Blue); + + /// Alters the red value of the color + void SetRed(unsigned char a_Red); + + /// Alters the green value of the color + void SetGreen(unsigned char a_Red); + + /// Alters the blue value of the color + void SetBlue(unsigned char a_Red); + + /// Returns the red value of the color + unsigned char GetRed() const; + + /// Returns the green value of the color + unsigned char GetGreen() const; + + /// Returns the blue value of the color + unsigned char GetBlue() const; + + /// Resets the color + void Clear() { m_Color = COLOR_NONE; } + // tolua_end + + unsigned int m_Color; + +}; diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp index 0bb77d341..d3a28292a 100644 --- a/src/CraftingRecipes.cpp +++ b/src/CraftingRecipes.cpp @@ -787,6 +787,9 @@ cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_Crafti // We use Recipe instead of a_Recipe because we want the wildcard ingredients' slot numbers as well, which was just added previously HandleFireworks(a_CraftingGrid, Recipe.get(), a_GridStride, a_OffsetX, a_OffsetY); + // Handle Dyed Leather + HandleDyedLeather(a_CraftingGrid, Recipe.get(), a_GridStride, a_GridWidth, a_GridHeight); + return Recipe.release(); } @@ -874,3 +877,192 @@ void cCraftingRecipes::HandleFireworks(const cItem * a_CraftingGrid, cCraftingRe + +void cCraftingRecipes::HandleDyedLeather(const cItem * a_CraftingGrid, cCraftingRecipes::cRecipe * a_Recipe, int a_GridStride, int a_GridWidth, int a_GridHeight) +{ + short result_type = a_Recipe->m_Result.m_ItemType; + if ((result_type == E_ITEM_LEATHER_CAP) || (result_type == E_ITEM_LEATHER_TUNIC) || (result_type == E_ITEM_LEATHER_PANTS) || (result_type == E_ITEM_LEATHER_BOOTS)) + { + bool found = false; + cItem temp; + + float red = 0; + float green = 0; + float blue = 0; + float dye_count = 0; + + for (int x = 0; x < a_GridWidth; ++x) + { + for (int y = 0; y < a_GridHeight; ++y) + { + int GridIdx = x + a_GridStride * y; + if ((a_CraftingGrid[GridIdx].m_ItemType == result_type) && (found == false)) + { + found = true; + temp = a_CraftingGrid[GridIdx].CopyOne(); + // The original color of the item affects the result + if (temp.m_ItemColor.IsValid()) + { + red += temp.m_ItemColor.GetRed(); + green += temp.m_ItemColor.GetGreen(); + blue += temp.m_ItemColor.GetBlue(); + ++dye_count; + } + } + else if (a_CraftingGrid[GridIdx].m_ItemType == E_ITEM_DYE) + { + switch (a_CraftingGrid[GridIdx].m_ItemDamage) + { + case E_META_DYE_BLACK: + { + red += 23; + green += 23; + blue += 23; + break; + } + case E_META_DYE_RED: + { + red += 142; + green += 47; + blue += 47; + break; + } + case E_META_DYE_GREEN: + { + red += 95; + green += 118; + blue += 47; + break; + } + case E_META_DYE_BROWN: + { + red += 95; + green += 71; + blue += 47; + break; + } + case E_META_DYE_BLUE: + { + red += 47; + green += 71; + blue += 165; + break; + } + case E_META_DYE_PURPLE: + { + red += 118; + green += 59; + blue += 165; + break; + } + case E_META_DYE_CYAN: + { + red += 71; + green += 118; + blue += 142; + break; + } + case E_META_DYE_LIGHTGRAY: + { + red += 142; + green += 142; + blue += 142; + break; + } + case E_META_DYE_GRAY: + { + red += 71; + green += 71; + blue += 71; + break; + } + case E_META_DYE_PINK: + { + red += 225; + green += 118; + blue += 153; + break; + } + case E_META_DYE_LIGHTGREEN: + { + red += 118; + green += 190; + blue += 23; + break; + } + case E_META_DYE_YELLOW: + { + red += 213; + green += 213; + blue += 47; + break; + } + case E_META_DYE_LIGHTBLUE: + { + red += 95; + green += 142; + blue += 201; + break; + } + case E_META_DYE_MAGENTA: + { + red += 165; + green += 71; + blue += 201; + break; + } + case E_META_DYE_ORANGE: + { + red += 201; + green += 118; + blue += 47; + break; + } + case E_META_DYE_WHITE: + { + red += 237; + green += 237; + blue += 237; + break; + } + } + ++dye_count; + } + else if (a_CraftingGrid[GridIdx].m_ItemType != E_ITEM_EMPTY) + { + return; + } + } + } + + if (!found) + { + return; + } + + // Calculate the rgb values + double maximum = static_cast(std::max({red, green, blue})); + + double average_red = red / dye_count; + double average_green = green / dye_count; + double average_blue = blue / dye_count; + double average_max = maximum / dye_count; + + double max_average = std::max({average_red, average_green, average_blue}); + + double gain_factor = average_max / max_average; + + + unsigned char result_red = static_cast(average_red * gain_factor); + unsigned char result_green = static_cast(average_green * gain_factor); + unsigned char result_blue = static_cast(average_blue * gain_factor); + + // Set the results values + a_Recipe->m_Result = temp; + a_Recipe->m_Result.m_ItemColor.SetColor(result_red, result_green, result_blue); + } +} + + + + diff --git a/src/CraftingRecipes.h b/src/CraftingRecipes.h index 44444d42e..778dd495a 100644 --- a/src/CraftingRecipes.h +++ b/src/CraftingRecipes.h @@ -168,6 +168,9 @@ protected: /** Searches for anything firework related, and does the data setting if appropriate */ void HandleFireworks(const cItem * a_CraftingGrid, cCraftingRecipes::cRecipe * a_Recipe, int a_GridStride, int a_OffsetX, int a_OffsetY); + + /// Searches for anything dye related for leather, calculates the appropriate color value, and sets the resulting value. + void HandleDyedLeather(const cItem * a_CraftingGrid, cCraftingRecipes::cRecipe * a_Recipe, int a_GridStride, int a_GridWidth, int a_GridHeight); } ; diff --git a/src/Item.cpp b/src/Item.cpp index 36c444328..7bd344ae8 100644 --- a/src/Item.cpp +++ b/src/Item.cpp @@ -142,6 +142,13 @@ void cItem::GetJson(Json::Value & a_OutValue) const a_OutValue["Lore"] = m_Lore; } + if (m_ItemColor.IsValid()) + { + a_OutValue["Color_Red"] = m_ItemColor.GetRed(); + a_OutValue["Color_Green"] = m_ItemColor.GetGreen(); + a_OutValue["Color_Blue"] = m_ItemColor.GetBlue(); + } + if ((m_ItemType == E_ITEM_FIREWORK_ROCKET) || (m_ItemType == E_ITEM_FIREWORK_STAR)) { a_OutValue["Flicker"] = m_FireworkItem.m_HasFlicker; @@ -172,6 +179,18 @@ void cItem::FromJson(const Json::Value & a_Value) m_CustomName = a_Value.get("Name", "").asString(); m_Lore = a_Value.get("Lore", "").asString(); + int red = a_Value.get("Color_Red", -1).asInt(); + int green = a_Value.get("Color_Green", -1).asInt(); + int blue = a_Value.get("Color_Blue", -1).asInt(); + if ((red > -1) && (red < static_cast(cColor::COLOR_LIMIT)) && (green > -1) && (green < static_cast(cColor::COLOR_LIMIT)) && (blue > -1) && (blue < static_cast(cColor::COLOR_LIMIT))) + { + m_ItemColor.SetColor(static_cast(red), static_cast(green), static_cast(blue)); + } + else if ((red != -1) || (blue != -1) || (green != -1)) + { + LOGWARNING("Item with invalid red, green, and blue values read in from json file."); + } + if ((m_ItemType == E_ITEM_FIREWORK_ROCKET) || (m_ItemType == E_ITEM_FIREWORK_STAR)) { m_FireworkItem.m_HasFlicker = a_Value.get("Flicker", false).asBool(); diff --git a/src/Item.h b/src/Item.h index 056b5eb8a..8f47c4177 100644 --- a/src/Item.h +++ b/src/Item.h @@ -12,6 +12,7 @@ #include "Defines.h" #include "Enchantments.h" #include "WorldStorage/FireworksSerializer.h" +#include "Color.h" @@ -19,6 +20,7 @@ // fwd: class cItemHandler; +class cColor; namespace Json { @@ -41,7 +43,8 @@ public: m_CustomName(""), m_Lore(""), m_RepairCost(0), - m_FireworkItem() + m_FireworkItem(), + m_ItemColor() { } @@ -62,7 +65,8 @@ public: m_CustomName (a_CustomName), m_Lore (a_Lore), m_RepairCost (0), - m_FireworkItem() + m_FireworkItem(), + m_ItemColor() { if (!IsValidItem(m_ItemType)) { @@ -105,6 +109,7 @@ public: m_Lore = ""; m_RepairCost = 0; m_FireworkItem.EmptyData(); + m_ItemColor.Clear(); } @@ -114,6 +119,7 @@ public: m_ItemCount = 0; m_ItemDamage = 0; m_RepairCost = 0; + m_ItemColor.Clear(); } @@ -206,6 +212,7 @@ public: int m_RepairCost; cFireworkItem m_FireworkItem; + cColor m_ItemColor; }; // tolua_end diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index c5c0f4a03..ff134c754 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -2646,6 +2646,10 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) a_Item.m_Lore = Lore; } + else if ((NBT.GetType(displaytag) == TAG_Int) && (NBT.GetName(displaytag) == "color")) + { + a_Item.m_ItemColor.m_Color = static_cast(NBT.GetInt(displaytag)); + } } } else if ((TagName == "Fireworks") || (TagName == "Explosion")) @@ -2732,7 +2736,7 @@ void cProtocol172::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item) a_Pkt.WriteBEInt8(a_Item.m_ItemCount); a_Pkt.WriteBEInt16(a_Item.m_ItemDamage); - if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (a_Item.m_ItemType != E_ITEM_FIREWORK_ROCKET) && (a_Item.m_ItemType != E_ITEM_FIREWORK_STAR)) + if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (a_Item.m_ItemType != E_ITEM_FIREWORK_ROCKET) && (a_Item.m_ItemType != E_ITEM_FIREWORK_STAR) && !a_Item.m_ItemColor.IsValid()) { a_Pkt.WriteBEInt16(-1); return; @@ -2749,9 +2753,15 @@ void cProtocol172::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item) const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, Writer, TagName); } - if (!a_Item.IsBothNameAndLoreEmpty()) + if (!a_Item.IsBothNameAndLoreEmpty() || a_Item.m_ItemColor.IsValid()) { Writer.BeginCompound("display"); + + if (a_Item.m_ItemColor.IsValid()) + { + Writer.AddInt("color", static_cast(a_Item.m_ItemColor.m_Color)); + } + if (!a_Item.IsCustomNameEmpty()) { Writer.AddString("Name", a_Item.m_CustomName.c_str()); diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index 99bba63e3..c435f6e40 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -2898,6 +2898,10 @@ void cProtocol180::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) a_Item.m_Lore = Lore; } + else if ((NBT.GetType(displaytag) == TAG_Int) && (NBT.GetName(displaytag) == "color")) + { + a_Item.m_ItemColor.m_Color = static_cast(NBT.GetInt(displaytag)); + } } } else if ((TagName == "Fireworks") || (TagName == "Explosion")) @@ -3047,12 +3051,13 @@ void cProtocol180::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item) a_Pkt.WriteBEInt8(a_Item.m_ItemCount); a_Pkt.WriteBEInt16(a_Item.m_ItemDamage); - if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (a_Item.m_ItemType != E_ITEM_FIREWORK_ROCKET) && (a_Item.m_ItemType != E_ITEM_FIREWORK_STAR)) + if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (a_Item.m_ItemType != E_ITEM_FIREWORK_ROCKET) && (a_Item.m_ItemType != E_ITEM_FIREWORK_STAR) && !a_Item.m_ItemColor.IsValid()) { a_Pkt.WriteBEInt8(0); return; } + // Send the enchantments and custom names: cFastNBTWriter Writer; if (a_Item.m_RepairCost != 0) @@ -3064,9 +3069,14 @@ void cProtocol180::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item) const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, Writer, TagName); } - if (!a_Item.IsBothNameAndLoreEmpty()) + if (!a_Item.IsBothNameAndLoreEmpty() || a_Item.m_ItemColor.IsValid()) { Writer.BeginCompound("display"); + if (a_Item.m_ItemColor.IsValid()) + { + Writer.AddInt("color", static_cast(a_Item.m_ItemColor.m_Color)); + } + if (!a_Item.IsCustomNameEmpty()) { Writer.AddString("Name", a_Item.m_CustomName.c_str()); -- cgit v1.2.3