21 m_Items(new
cItem[a_Width * a_Height])
34 for (
int i = a_Width * a_Height - 1; i >= 0; i--)
74 LOGERROR(
"Attempted to get an invalid item from a crafting grid: (%d, %d), grid dimensions: (%d, %d).",
91 LOGERROR(
"Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d).",
109 LOGERROR(
"Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d).",
138 LOGWARNING(
"Consuming a grid of different dimensions: (%d, %d) vs (%d, %d)",
144 for (
int y = 0; y < MinY; y++)
for (
int x = 0; x < MinX; x++)
146 int ThatIdx = x + a_Grid.
m_Width * y;
154 LOGWARNING(
"Consuming incompatible grids: item at (%d, %d) is %d in grid and %d in ingredients. Item not consumed.",
160 if (NumWantedItems >
m_Items[ThisIdx].m_ItemCount)
162 LOGWARNING(
"Consuming more items than there actually are in slot (%d, %d), item %d (want %d, have %d). Item zeroed out.",
163 x, y,
m_Items[ThisIdx].m_ItemType,
164 NumWantedItems,
m_Items[ThisIdx].m_ItemCount
169 if (
m_Items[ThisIdx].m_ItemCount == 0)
204 int idx = x + m_Width * y;
206 LOGD(
"Slot (%d, %d): Type %d, health %d, count %d",
220 m_Ingredients(a_CraftingGrid)
258 LOGD(
"Recipe ingredients:");
260 LOGD(
"Result: Type %d, health %d, count %d",
293 if (
cRoot::Get()->GetPluginManager()->CallHookPreCrafting(a_Player, a_CraftingGrid, a_Recipe))
299 std::unique_ptr<cRecipe> Recipe(FindRecipe(a_CraftingGrid.
GetItems(), a_CraftingGrid.
GetWidth(), a_CraftingGrid.
GetHeight()));
301 if (Recipe.get() ==
nullptr)
307 for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != Recipe->m_Ingredients.end(); ++itr)
323 LOGD(
"Loading crafting recipes from crafting.txt...");
330 LOGWARNING(
"Cannot open file \"crafting.txt\", no crafting recipes will be available!");
336 LOGWARNING(
"Cannot read file \"crafting.txt\", no crafting recipes will be available!");
344 for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr, ++LineNum)
353 AddRecipeLine(LineNum, Recipe);
355 LOG(
"Loaded %zu crafting recipes", m_Recipes.size());
364 for (cRecipes::iterator itr = m_Recipes.begin(); itr != m_Recipes.end(); ++itr)
378 AString RecipeLine(a_RecipeLine);
379 RecipeLine.erase(std::remove_if(RecipeLine.begin(), RecipeLine.end(), isspace), RecipeLine.end());
382 if (Sides.size() != 2)
384 LOGWARNING(
"crafting.txt: line %d: A single '=' was expected, got %zu", a_LineNum, Sides.size() - 1);
385 LOGINFO(
"Offending line: \"%s\"", a_RecipeLine.c_str());
389 std::unique_ptr<cCraftingRecipes::cRecipe> Recipe = cpp14::make_unique<cCraftingRecipes::cRecipe>();
393 if (ResultSplit.empty())
395 LOGWARNING(
"crafting.txt: line %d: Result is empty, ignoring the recipe.", a_LineNum);
396 LOGINFO(
"Offending line: \"%s\"", a_RecipeLine.c_str());
399 if (!ParseItem(ResultSplit[0], Recipe->m_Result))
401 LOGWARNING(
"crafting.txt: line %d: Cannot parse result item, ignoring the recipe.", a_LineNum);
402 LOGINFO(
"Offending line: \"%s\"", a_RecipeLine.c_str());
405 if (ResultSplit.size() > 1)
407 if (!StringToInteger<char>(ResultSplit[1].c_str(), Recipe->m_Result.m_ItemCount))
409 LOGWARNING(
"crafting.txt: line %d: Cannot parse result count, ignoring the recipe.", a_LineNum);
410 LOGINFO(
"Offending line: \"%s\"", a_RecipeLine.c_str());
416 Recipe->m_Result.m_ItemCount = 1;
422 for (AStringVector::const_iterator itr = Ingredients.begin(); itr != Ingredients.end(); ++itr, ++Num)
424 if (!ParseIngredient(*itr, Recipe.get()))
426 LOGWARNING(
"crafting.txt: line %d: Cannot parse ingredient #%d, ignoring the recipe.", a_LineNum, Num);
427 LOGINFO(
"Offending line: \"%s\"", a_RecipeLine.c_str());
432 NormalizeIngredients(Recipe.get());
434 m_Recipes.push_back(Recipe.release());
456 if (Split.size() > 1)
459 if (!StringToInteger<short>(Damage.c_str(), a_Item.
m_ItemDamage))
478 if (Split.size() < 2)
484 if (!ParseItem(Split[0], Item))
491 for (AStringVector::const_iterator itr = Split.begin() + 1; itr != Split.end(); ++itr)
495 if ((Coords.size() == 1) && (
TrimString(Coords[0]) ==
"*"))
501 TempSlots.push_back(Slot);
504 if (Coords.size() != 2)
510 if (Coords[0].empty() || Coords[1].empty())
516 switch (Coords[0][0])
518 case '1': Slot.
x = 0;
break;
519 case '2': Slot.
x = 1;
break;
520 case '3': Slot.
x = 2;
break;
521 case '*': Slot.
x = -1;
break;
527 switch (Coords[1][0])
529 case '1': Slot.
y = 0;
break;
530 case '2': Slot.
y = 1;
break;
531 case '3': Slot.
y = 2;
break;
532 case '*': Slot.
y = -1;
break;
538 TempSlots.push_back(Slot);
553 int MinX = MAX_GRID_WIDTH, MaxX = 0;
554 int MinY = MAX_GRID_HEIGHT, MaxY = 0;
559 MinX = std::min(itr->x, MinX);
560 MaxX = std::max(itr->x, MaxX);
564 MinY = std::min(itr->y, MinY);
565 MaxY = std::max(itr->y, MaxY);
581 a_Recipe->
m_Width = std::max(MaxX - MinX + 1, 1);
582 a_Recipe->
m_Height = std::max(MaxY - MinY + 1, 1);
593 ASSERT(a_GridWidth <= MAX_GRID_WIDTH);
594 ASSERT(a_GridHeight <= MAX_GRID_HEIGHT);
597 int GridLeft = MAX_GRID_WIDTH, GridTop = MAX_GRID_HEIGHT;
598 int GridRight = 0, GridBottom = 0;
599 for (
int y = 0; y < a_GridHeight; y++)
for (
int x = 0; x < a_GridWidth; x++)
601 if (!a_CraftingGrid[x + y * a_GridWidth].IsEmpty())
603 GridRight = std::max(x, GridRight);
604 GridBottom = std::max(y, GridBottom);
605 GridLeft = std::min(x, GridLeft);
606 GridTop = std::min(y, GridTop);
609 int GridWidth = GridRight - GridLeft + 1;
610 int GridHeight = GridBottom - GridTop + 1;
613 const cItem * Grid = a_CraftingGrid + GridLeft + (a_GridWidth * GridTop);
614 cRecipe * Recipe = FindRecipeCropped(Grid, GridWidth, GridHeight, a_GridWidth);
615 if (Recipe ==
nullptr)
621 for (cRecipeSlots::iterator itrS = Recipe->m_Ingredients.begin(); itrS != Recipe->m_Ingredients.end(); ++itrS)
636 for (cRecipes::const_iterator itr = m_Recipes.begin(); itr != m_Recipes.end(); ++itr)
644 int MaxOfsX = a_GridWidth - (*itr)->
m_Width;
645 int MaxOfsY = a_GridHeight - (*itr)->m_Height;
646 for (
int x = 0; x <= MaxOfsX; x++)
for (
int y = 0; y <= MaxOfsY; y++)
648 cRecipe * Recipe = MatchRecipe(a_CraftingGrid, a_GridWidth, a_GridHeight, a_GridStride, *itr, x, y);
649 if (Recipe !=
nullptr)
667 bool HasMatched[MAX_GRID_WIDTH][MAX_GRID_HEIGHT];
668 memset(HasMatched, 0,
sizeof(HasMatched));
671 if ((itrS->x < 0) || (itrS->y < 0))
676 ASSERT(itrS->x + a_OffsetX < a_GridWidth);
677 ASSERT(itrS->y + a_OffsetY < a_GridHeight);
678 int GridID = (itrS->x + a_OffsetX) + a_GridStride * (itrS->y + a_OffsetY);
680 const cItem & Item = itrS->m_Item;
682 (itrS->x >= a_GridWidth) ||
683 (itrS->y >= a_GridHeight) ||
695 HasMatched[itrS->x + a_OffsetX][itrS->y + a_OffsetY] =
true;
704 if ((itrS->x >= 0) && (itrS->y >= 0))
709 int StartX = 0, EndX = a_GridWidth - 1;
710 int StartY = 0, EndY = a_GridHeight - 1;
716 else if (itrS->y >= 0)
722 for (
int x = StartX; x <= EndX; x++)
724 for (
int y = StartY; y <= EndY; y++)
726 if (HasMatched[x][y])
731 int GridIdx = x + a_GridStride * y;
733 (a_CraftingGrid[GridIdx].m_ItemType == itrS->m_Item.
m_ItemType) &&
735 (itrS->m_Item.m_ItemDamage < 0) ||
736 (itrS->m_Item.m_ItemDamage == a_CraftingGrid[GridIdx].
m_ItemDamage)
740 HasMatched[x][y] =
true;
742 MatchedSlots.push_back(*itrS);
743 MatchedSlots.back().x = x;
744 MatchedSlots.back().y = y;
760 for (
int x = 0; x < a_GridWidth; x++)
for (
int y = 0; y < a_GridHeight; y++)
762 if (!HasMatched[x][y] && !a_CraftingGrid[x + a_GridStride * y].IsEmpty())
770 std::unique_ptr<cRecipe> Recipe = cpp14::make_unique<cRecipe>();
771 Recipe->m_Result = a_Recipe->
m_Result;
772 Recipe->m_Width = a_Recipe->
m_Width;
773 Recipe->m_Height = a_Recipe->
m_Height;
776 if ((itrS->x < 0) || (itrS->y < 0))
781 Recipe->m_Ingredients.push_back(*itrS);
782 Recipe->m_Ingredients.back().x += a_OffsetX;
783 Recipe->m_Ingredients.back().y += a_OffsetY;
785 Recipe->m_Ingredients.insert(Recipe->m_Ingredients.end(), MatchedSlots.begin(), MatchedSlots.end());
789 HandleFireworks(a_CraftingGrid, Recipe.get(), a_GridStride, a_OffsetX, a_OffsetY);
792 HandleDyedLeather(a_CraftingGrid, Recipe.get(), a_GridStride, a_GridWidth, a_GridHeight);
794 return Recipe.release();
810 switch (itr->m_Item.m_ItemType)
815 int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
826 default:
LOG(
"Unexpected item in firework rocket recipe, was the crafting file's fireworks section changed?");
break;
832 std::vector<int> DyeColours;
833 bool FoundStar =
false;
837 switch (itr->m_Item.m_ItemType)
843 int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
849 int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
861 default:
LOG(
"Unexpected item in firework star recipe, was the crafting file's fireworks section changed?");
break;
865 if (FoundStar && (!DyeColours.empty()))
870 else if (!DyeColours.empty())
895 for (
int x = 0; x < a_GridWidth; ++x)
897 for (
int y = 0; y < a_GridHeight; ++y)
899 int GridIdx = x + a_GridStride * y;
900 if ((a_CraftingGrid[GridIdx].m_ItemType == result_type) && (found ==
false))
903 temp = a_CraftingGrid[GridIdx].
CopyOne();
913 else if (a_CraftingGrid[GridIdx].m_ItemType ==
E_ITEM_DYE)
915 switch (a_CraftingGrid[GridIdx].m_ItemDamage)
1032 else if (a_CraftingGrid[GridIdx].m_ItemType !=
E_ITEM_EMPTY)
1045 double maximum =
static_cast<double>(std::max({red, green, blue}));
1047 double average_red = red / dye_count;
1048 double average_green = green / dye_count;
1049 double average_blue = blue / dye_count;
1050 double average_max = maximum / dye_count;
1052 double max_average = std::max({average_red, average_green, average_blue});
1054 double gain_factor = average_max / max_average;
1057 unsigned char result_red =
static_cast<unsigned char>(average_red * gain_factor);
1058 unsigned char result_green =
static_cast<unsigned char>(average_green * gain_factor);
1059 unsigned char result_blue =
static_cast<unsigned char>(average_blue * gain_factor);
cRecipeSlots m_Ingredients
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. ...
std::vector< cRecipeSlot > cRecipeSlots
void ConsumeGrid(const cCraftingGrid &a_Grid)
Removes items in a_Grid from m_Items[] (used by cCraftingRecipe::ConsumeIngredients()) ...
bool ParseIngredient(const AString &a_String, cRecipe *a_Recipe)
Parses one ingredient and adds it to the specified recipe.
void SetIngredient(int x, int y, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth)
std::vector< int > m_FadeColours
bool CallHookCraftingNoRecipe(cPlayer &a_Player, cCraftingGrid &a_Grid, cCraftingRecipe &a_Recipe)
cFireworkItem m_FireworkItem
cCraftingRecipe(const cCraftingGrid &a_CraftingGrid)
void SetResult(ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth)
cItem * GetItems(void) const
bool IsValid() const
Returns whether the color is a valid color.
int ReadRestOfFile(AString &a_Contents)
Reads the file from current position till EOF into an AString; returns the number of bytes read or -1...
void LOGERROR(const char *a_Format, fmt::ArgList a_ArgList)
short m_FlightTimeInTicks
cItem CopyOne(void) const
Returns a copy of this item with m_ItemCount set to 1.
void SetColor(unsigned char a_Red, unsigned char a_Green, unsigned char a_Blue)
Changes the color.
void GetRecipe(cPlayer &a_Player, cCraftingGrid &a_CraftingGrid, cCraftingRecipe &a_Recipe)
Returns the recipe for current crafting grid.
void AddRecipeLine(int a_LineNum, const AString &a_RecipeLine)
Parses the recipe line and adds it into m_Recipes.
void SetItem(int x, int y, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth)
std::vector< AString > AStringVector
void CopyToItems(cItem *a_Items) const
Copies internal contents into the item array specified.
int GetHeight(void) const
static int GetVanillaColourCodeFromDye(NIBBLETYPE a_DyeMeta)
Returns a colour code for fireworks used by the network code.
bool Open(const AString &iFileName, eMode iMode)
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. ...
cItem & GetItem(int x, int y) const
bool ParseItem(const AString &a_String, cItem &a_Item)
Parses an item string in the format "<ItemType>[^<Damage>]", returns true if successful.
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 LOGINFO(const char *a_Format, fmt::ArgList a_ArgList)
unsigned char GetRed() const
Returns the red value of the color.
unsigned char GetBlue() const
Returns the blue value of the color.
void LOGWARNING(const char *a_Format, fmt::ArgList a_ArgList)
void CopyFrom(const cFireworkItem &a_Item)
AString TrimString(const AString &str)
Trims whitespace at both ends of the string.
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, and sets the resulting value.
void Dump(void)
Dumps the entire recipe using LOGD()
unsigned char GetGreen() const
Returns the green value of the color.
void LOG(const char *a_Format, fmt::ArgList a_ArgList)
AStringVector StringSplit(const AString &str, const AString &delim)
Split the string at any of the listed delimiters.
bool CallHookPostCrafting(cPlayer &a_Player, cCraftingGrid &a_Grid, cCraftingRecipe &a_Recipe)
void ConsumeIngredients(cCraftingGrid &a_CraftingGrid)
Consumes ingredients from the crafting grid specified.
std::vector< int > m_Colours
cRecipe * FindRecipe(const cItem *a_CraftingGrid, int a_GridWidth, int a_GridHeight)
Finds a recipe matching the crafting grid.
cCraftingGrid(const cCraftingGrid &a_Original)
bool StringToItem(const AString &a_ItemTypeString, cItem &a_Item)
Translates an itemtype string into an item.
void NormalizeIngredients(cRecipe *a_Recipe)
Moves the recipe to top-left corner, sets its MinWidth / MinHeight.
void Dump(void)
Dumps the entire crafting grid using LOGD()
cPluginManager * GetPluginManager(void)
cCraftingGrid m_Ingredients