32 for (
int i = a_Width * a_Height - 1; i >= 0; i--)
70 LOGERROR(
"Attempted to get an invalid item from a crafting grid: (%d, %d), grid dimensions: (%d, %d).",
87 LOGERROR(
"Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d).",
105 LOGERROR(
"Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d).",
134 LOGWARNING(
"Consuming a grid of different dimensions: (%d, %d) vs (%d, %d)",
140 for (
int y = 0; y < MinY; y++)
for (
int x = 0; x < MinX; x++)
142 int ThatIdx = x + a_Grid.
m_Width * y;
150 LOGWARNING(
"Consuming incompatible grids: item at (%d, %d) is %d in grid and %d in ingredients. Item not consumed.",
156 if (NumWantedItems >
m_Items[ThisIdx].m_ItemCount)
158 LOGWARNING(
"Consuming more items than there actually are in slot (%d, %d), item %d (want %d, have %d). Item zeroed out.",
159 x, y,
m_Items[ThisIdx].m_ItemType,
160 NumWantedItems,
m_Items[ThisIdx].m_ItemCount
165 if (
m_Items[ThisIdx].m_ItemCount == 0)
199 [[maybe_unused]]
const int idx = x +
m_Width * y;
200 LOGD(
"Slot (%d, %d): Type %d, health %d, count %d",
214 m_Ingredients(a_CraftingGrid)
252 LOGD(
"Recipe ingredients:");
254 LOGD(
"Result: Type %d, health %d, count %d",
287 bool ContainsNewItem =
false;
291 (Ingredient.m_Item.m_ItemType == a_Item.
m_ItemType) &&
293 (Ingredient.m_Item.m_ItemDamage == a_Item.
m_ItemDamage) ||
294 (Ingredient.m_Item.m_ItemDamage == -1)
298 ContainsNewItem =
true;
300 if (a_KnownItems.find(Ingredient.m_Item) == a_KnownItems.end())
305 return ContainsNewItem;
314 std::vector<UInt32> Recipes;
323 Recipes.push_back(i);
354 if (
cRoot::Get()->GetPluginManager()->CallHookPreCrafting(a_Player, a_CraftingGrid, a_Recipe))
362 if (Recipe.get() ==
nullptr)
368 for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != Recipe->m_Ingredients.end(); ++itr)
384 LOGD(
"Loading crafting recipes from crafting.txt...");
391 LOGWARNING(
"Cannot open file \"crafting.txt\", no crafting recipes will be available!");
397 LOGWARNING(
"Cannot read file \"crafting.txt\", no crafting recipes will be available!");
405 for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr, ++LineNum)
454 AString RecipeLine(a_RecipeLine);
455 RecipeLine.erase(std::remove_if(RecipeLine.begin(), RecipeLine.end(), isspace), RecipeLine.end());
458 if (Sides.size() != 2)
460 LOGWARNING(
"crafting.txt: line %d: A single '=' was expected, got %zu", a_LineNum, Sides.size() - 1);
461 LOGINFO(
"Offending line: \"%s\"", a_RecipeLine.c_str());
465 std::unique_ptr<cCraftingRecipes::cRecipe> Recipe = std::make_unique<cCraftingRecipes::cRecipe>();
468 const auto * resultPart = &RecipeSplit[0];
469 if (RecipeSplit.size() > 1)
471 resultPart = &RecipeSplit[1];
472 Recipe->m_RecipeName = RecipeSplit[0];
476 if (ResultSplit.empty())
478 LOGWARNING(
"crafting.txt: line %d: Result is empty, ignoring the recipe.", a_LineNum);
479 LOGINFO(
"Offending line: \"%s\"", a_RecipeLine.c_str());
482 if (!
ParseItem(ResultSplit[0], Recipe->m_Result))
484 LOGWARNING(
"crafting.txt: line %d: Cannot parse result item, ignoring the recipe.", a_LineNum);
485 LOGINFO(
"Offending line: \"%s\"", a_RecipeLine.c_str());
488 if (ResultSplit.size() > 1)
490 if (!StringToInteger<char>(ResultSplit[1], Recipe->m_Result.m_ItemCount))
492 LOGWARNING(
"crafting.txt: line %d: Cannot parse result count, ignoring the recipe.", a_LineNum);
493 LOGINFO(
"Offending line: \"%s\"", a_RecipeLine.c_str());
499 Recipe->m_Result.m_ItemCount = 1;
505 for (AStringVector::const_iterator itr = Ingredients.begin(); itr != Ingredients.end(); ++itr, ++Num)
509 LOGWARNING(
"crafting.txt: line %d: Cannot parse ingredient #%d, ignoring the recipe.", a_LineNum, Num);
510 LOGINFO(
"Offending line: \"%s\"", a_RecipeLine.c_str());
539 if (Split.size() > 1)
542 if (!StringToInteger<short>(Damage, a_Item.
m_ItemDamage))
561 if (Split.size() < 2)
571 Item.m_ItemCount = 1;
574 for (AStringVector::const_iterator itr = Split.begin() + 1; itr != Split.end(); ++itr)
578 if ((Coords.size() == 1) && (
TrimString(Coords[0]) ==
"*"))
584 TempSlots.push_back(Slot);
587 if (Coords.size() != 2)
593 if (Coords[0].empty() || Coords[1].empty())
599 switch (Coords[0][0])
601 case '1': Slot.
x = 0;
break;
602 case '2': Slot.
x = 1;
break;
603 case '3': Slot.
x = 2;
break;
604 case '*': Slot.
x = -1;
break;
610 switch (Coords[1][0])
612 case '1': Slot.
y = 0;
break;
613 case '2': Slot.
y = 1;
break;
614 case '3': Slot.
y = 2;
break;
615 case '*': Slot.
y = -1;
break;
621 TempSlots.push_back(Slot);
642 MinX = std::min(itr->x, MinX);
643 MaxX = std::max(itr->x, MaxX);
647 MinY = std::min(itr->y, MinY);
648 MaxY = std::max(itr->y, MaxY);
664 a_Recipe->
m_Width = std::max(MaxX - MinX + 1, 1);
665 a_Recipe->
m_Height = std::max(MaxY - MinY + 1, 1);
681 int GridRight = 0, GridBottom = 0;
682 for (
int y = 0; y < a_GridHeight; y++)
for (
int x = 0; x < a_GridWidth; x++)
684 if (!a_CraftingGrid[x + y * a_GridWidth].IsEmpty())
686 GridRight = std::max(x, GridRight);
687 GridBottom = std::max(y, GridBottom);
688 GridLeft = std::min(x, GridLeft);
689 GridTop = std::min(y, GridTop);
692 int GridWidth = GridRight - GridLeft + 1;
693 int GridHeight = GridBottom - GridTop + 1;
696 const cItem * Grid = a_CraftingGrid + GridLeft + (a_GridWidth * GridTop);
698 if (Recipe ==
nullptr)
704 for (cRecipeSlots::iterator itrS = Recipe->m_Ingredients.begin(); itrS != Recipe->m_Ingredients.end(); ++itrS)
727 int MaxOfsX = a_GridWidth - (*itr)->
m_Width;
728 int MaxOfsY = a_GridHeight - (*itr)->m_Height;
729 for (
int x = 0; x <= MaxOfsX; x++)
for (
int y = 0; y <= MaxOfsY; y++)
731 cRecipe * Recipe =
MatchRecipe(a_CraftingGrid, a_GridWidth, a_GridHeight, a_GridStride, *itr, x, y);
732 if (Recipe !=
nullptr)
751 memset(HasMatched, 0,
sizeof(HasMatched));
754 if ((itrS->x < 0) || (itrS->y < 0))
759 ASSERT(itrS->x + a_OffsetX < a_GridWidth);
760 ASSERT(itrS->y + a_OffsetY < a_GridHeight);
761 int GridID = (itrS->x + a_OffsetX) + a_GridStride * (itrS->y + a_OffsetY);
765 (itrS->x >= a_GridWidth) ||
766 (itrS->y >= a_GridHeight) ||
770 (
Item.m_ItemDamage >= 0) &&
778 HasMatched[itrS->x + a_OffsetX][itrS->y + a_OffsetY] =
true;
787 if ((itrS->x >= 0) && (itrS->y >= 0))
792 int StartX = 0, EndX = a_GridWidth - 1;
793 int StartY = 0, EndY = a_GridHeight - 1;
799 else if (itrS->y >= 0)
805 for (
int x = StartX; x <= EndX; x++)
807 for (
int y = StartY; y <= EndY; y++)
809 if (HasMatched[x][y])
814 int GridIdx = x + a_GridStride * y;
816 (a_CraftingGrid[GridIdx].m_ItemType == itrS->m_Item.
m_ItemType) &&
818 (itrS->m_Item.m_ItemDamage < 0) ||
819 (itrS->m_Item.m_ItemDamage == a_CraftingGrid[GridIdx].
m_ItemDamage)
823 HasMatched[x][y] =
true;
825 MatchedSlots.push_back(*itrS);
826 MatchedSlots.back().x = x;
827 MatchedSlots.back().y = y;
843 for (
int x = 0; x < a_GridWidth; x++)
for (
int y = 0; y < a_GridHeight; y++)
845 if (!HasMatched[x][y] && !a_CraftingGrid[x + a_GridStride * y].IsEmpty())
853 std::unique_ptr<cRecipe> Recipe = std::make_unique<cRecipe>();
854 Recipe->m_Result = a_Recipe->
m_Result;
855 Recipe->m_Width = a_Recipe->
m_Width;
856 Recipe->m_Height = a_Recipe->
m_Height;
859 if ((itrS->x < 0) || (itrS->y < 0))
864 Recipe->m_Ingredients.push_back(*itrS);
865 Recipe->m_Ingredients.back().x += a_OffsetX;
866 Recipe->m_Ingredients.back().y += a_OffsetY;
868 Recipe->m_Ingredients.insert(Recipe->m_Ingredients.end(), MatchedSlots.begin(), MatchedSlots.end());
872 HandleFireworks(a_CraftingGrid, Recipe.get(), a_GridStride, a_OffsetX, a_OffsetY);
875 HandleDyedLeather(a_CraftingGrid, Recipe.get(), a_GridStride, a_GridWidth, a_GridHeight);
877 return Recipe.release();
893 switch (itr->m_Item.m_ItemType)
898 int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
909 default:
LOG(
"Unexpected item in firework rocket recipe, was the crafting file's fireworks section changed?");
break;
915 std::vector<int> DyeColours;
916 bool FoundStar =
false;
920 switch (itr->m_Item.m_ItemType)
926 int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
932 int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
944 default:
LOG(
"Unexpected item in firework star recipe, was the crafting file's fireworks section changed?");
break;
948 if (FoundStar && (!DyeColours.empty()))
953 else if (!DyeColours.empty())
978 for (
int x = 0; x < a_GridWidth; ++x)
980 for (
int y = 0; y < a_GridHeight; ++y)
982 int GridIdx = x + a_GridStride * y;
983 if ((a_CraftingGrid[GridIdx].m_ItemType == result_type) && !found)
986 temp = a_CraftingGrid[GridIdx].
CopyOne();
996 else if (a_CraftingGrid[GridIdx].m_ItemType ==
E_ITEM_DYE)
998 switch (a_CraftingGrid[GridIdx].m_ItemDamage)
1115 else if (a_CraftingGrid[GridIdx].m_ItemType !=
E_ITEM_EMPTY)
1128 double maximum =
static_cast<double>(std::max({red, green, blue}));
1130 double average_red = red / dye_count;
1131 double average_green = green / dye_count;
1132 double average_blue = blue / dye_count;
1133 double average_max = maximum / dye_count;
1135 double max_average = std::max({average_red, average_green, average_blue});
1137 double gain_factor = average_max / max_average;
1140 unsigned char result_red =
static_cast<unsigned char>(average_red * gain_factor);
1141 unsigned char result_green =
static_cast<unsigned char>(average_green * gain_factor);
1142 unsigned char result_blue =
static_cast<unsigned char>(average_blue * gain_factor);
bool StringToItem(const AString &a_ItemTypeString, cItem &a_Item)
Translates an itemtype string into an item.
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
void LOGERROR(std::string_view a_Format, const Args &... args)
void LOGWARNING(std::string_view a_Format, const Args &... args)
void LOG(std::string_view a_Format, const Args &... args)
void LOGINFO(std::string_view a_Format, const Args &... args)
AString TrimString(const AString &str)
Trims whitespace at both ends of the string.
AStringVector StringSplit(const AString &str, const AString &delim)
Split the string at any of the listed delimiters.
std::vector< AString > AStringVector
bool CallHookPostCrafting(cPlayer &a_Player, cCraftingGrid &a_Grid, cCraftingRecipe &a_Recipe)
bool CallHookCraftingNoRecipe(cPlayer &a_Player, cCraftingGrid &a_Grid, cCraftingRecipe &a_Recipe)
void SetColor(unsigned char a_Red, unsigned char a_Green, unsigned char a_Blue)
Changes the color.
unsigned char GetRed() const
Returns the red value of the color.
unsigned char GetGreen() const
Returns the green value of the color.
unsigned char GetBlue() const
Returns the blue value of the color.
bool IsValid() const
Returns whether the color is a valid color.
cCraftingGrid(const cCraftingGrid &a_Original)
void SetItem(int x, int y, ENUM_ITEM_TYPE a_ItemType, char a_ItemCount, short a_ItemHealth)
cItem * GetItems(void) const
void Dump(void)
Dumps the entire crafting grid using LOGD()
void CopyToItems(cItem *a_Items) const
Copies internal contents into the item array specified.
void ConsumeGrid(const cCraftingGrid &a_Grid)
Removes items in a_Grid from m_Items[] (used by cCraftingRecipe::ConsumeIngredients())
int GetHeight(void) const
cItem & GetItem(int x, int y) const
void Dump(void)
Dumps the entire recipe using LOGD()
void SetIngredient(int x, int y, ENUM_ITEM_TYPE a_ItemType, char a_ItemCount, short a_ItemHealth)
void ConsumeIngredients(cCraftingGrid &a_CraftingGrid)
Consumes ingredients from the crafting grid specified.
void SetResult(ENUM_ITEM_TYPE a_ItemType, char a_ItemCount, short a_ItemHealth)
cCraftingRecipe(const cCraftingGrid &a_CraftingGrid)
cCraftingGrid m_Ingredients
static const int MAX_GRID_HEIGHT
std::vector< cRecipeSlot > cRecipeSlots
void HandleFireworks(const cItem *a_CraftingGrid, cCraftingRecipes::cRecipe *a_Recipe, int a_GridStride, int a_OffsetX, int a_OffsetY)
Searches for anything firework related, and does the data setting if appropriate.
static const int MAX_GRID_WIDTH
void GetRecipe(cPlayer &a_Player, cCraftingGrid &a_CraftingGrid, cCraftingRecipe &a_Recipe)
Returns the recipe for current crafting grid.
void NormalizeIngredients(cRecipe *a_Recipe)
Moves the recipe to top-left corner, sets its MinWidth / MinHeight.
const std::map< AString, UInt32 > & GetRecipeNameMap()
Gets a map of all recipes with name and recipe id.
std::map< AString, UInt32 > m_RecipeNameMap
Mapping the minecraft recipe names to the internal cuberite recipe Ids.
bool IsNewCraftableRecipe(const cRecipe *a_Recipe, const cItem &a_NewItem, const std::set< cItem, cItem::sItemCompare > &a_KnownItems)
Checks if all ingredients of the a_Recipe are within the a_KnownItems list and if the a_NewItem is pa...
cRecipe * MatchRecipe(const cItem *a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe *a_Recipe, int a_OffsetX, int a_OffsetY)
Checks if the grid matches the specified recipe, offset by the specified offsets.
cRecipe * GetRecipeById(UInt32 a_RecipeId)
Returns the recipe by id.
cRecipe * FindRecipeCropped(const cItem *a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride)
Same as FindRecipe, but the grid is guaranteed to be of minimal dimensions needed.
void HandleDyedLeather(const cItem *a_CraftingGrid, cCraftingRecipes::cRecipe *a_Recipe, int a_GridStride, int a_GridWidth, int a_GridHeight)
Searches for anything dye related for leather, calculates the appropriate color value,...
cRecipe * FindRecipe(const cItem *a_CraftingGrid, int a_GridWidth, int a_GridHeight)
Finds a recipe matching the crafting grid.
bool ParseItem(const AString &a_String, cItem &a_Item)
Parses an item string in the format "<ItemType>[^<Damage>]", returns true if successful.
bool ParseIngredient(const AString &a_String, cRecipe *a_Recipe)
Parses one ingredient and adds it to the specified recipe.
std::vector< UInt32 > FindNewRecipesForItem(const cItem &a_Item, const std::set< cItem, cItem::sItemCompare > &a_KnownItems)
Find recipes and returns the RecipeIds which contain the new item and all ingredients are in the know...
void PopulateRecipeNameMap(void)
Populates the RecipeNameMap.
void AddRecipeLine(int a_LineNum, const AString &a_RecipeLine)
Parses the recipe line and adds it into m_Recipes.
cRecipeSlots m_Ingredients
bool IsEmpty(void) const
Returns true if the item represents an empty stack - either the type is invalid, or count is zero.
void Clear(void)
Empties the item and frees up any dynamic storage used by the internals.
void Empty(void)
Empties the item and frees up any dynamic storage used by the internals.
cFireworkItem m_FireworkItem
cItem CopyOne(void) const
Returns a copy of this item with m_ItemCount set to 1.
bool Open(const AString &iFileName, eMode iMode)
int ReadRestOfFile(AString &a_Contents)
Reads the file from current position till EOF into an AString; returns the number of bytes read or -1...
cPluginManager * GetPluginManager(void)
short m_FlightTimeInTicks
std::vector< int > m_FadeColours
std::vector< int > m_Colours
void CopyFrom(const cFireworkItem &a_Item)
static int GetVanillaColourCodeFromDye(NIBBLETYPE a_DyeMeta)
Returns a colour code for fireworks used by the network code.