Banners Complete!

Fixed improper behaviors of banners to adhere to LCE, as well as redid the banner recipes!
This commit is contained in:
Tranqlmao
2026-07-15 14:50:09 -04:00
parent f3a4f21029
commit 454da0ce3c
15 changed files with 389 additions and 47 deletions
+41 -16
View File
@@ -6,26 +6,27 @@
#include "ItemInstance.h"
#include "BannerItem.h"
#include "BannerTileEntity.h"
#include "com.mojang.nbt.h"
#include "Mth.h"
const unsigned int BannerItem::COLOR_DESCS[16] =
{
IDS_TILE_BANNER_BLACK,
IDS_TILE_BANNER_RED,
IDS_TILE_BANNER_GREEN,
IDS_TILE_BANNER_BROWN,
IDS_TILE_BANNER_BLUE,
IDS_TILE_BANNER_PURPLE,
IDS_TILE_BANNER_CYAN,
IDS_TILE_BANNER_SILVER,
IDS_TILE_BANNER_GRAY,
IDS_TILE_BANNER_PINK,
IDS_TILE_BANNER_LIME,
IDS_TILE_BANNER_YELLOW,
IDS_TILE_BANNER_LIGHT_BLUE,
IDS_TILE_BANNER_MAGENTA,
IDS_TILE_BANNER_ORANGE,
IDS_TILE_BANNER_WHITE,
IDS_TILE_BANNER_ORANGE,
IDS_TILE_BANNER_MAGENTA,
IDS_TILE_BANNER_LIGHT_BLUE,
IDS_TILE_BANNER_YELLOW,
IDS_TILE_BANNER_LIME,
IDS_TILE_BANNER_PINK,
IDS_TILE_BANNER_GRAY,
IDS_TILE_BANNER_SILVER,
IDS_TILE_BANNER_CYAN,
IDS_TILE_BANNER_PURPLE,
IDS_TILE_BANNER_BLUE,
IDS_TILE_BANNER_BROWN,
IDS_TILE_BANNER_GREEN,
IDS_TILE_BANNER_RED,
IDS_TILE_BANNER_BLACK,
};
BannerItem::BannerItem(int id) : Item(id)
@@ -64,14 +65,38 @@ bool BannerItem::useOn(shared_ptr<ItemInstance> instance, shared_ptr<Player> pla
}
else
{
level->setTileAndData(x, y, z, Tile::wall_banner_Id, face, Tile::UPDATE_CLIENTS);
level->setTileAndData(x, y, z, Tile::standing_banner_Id, face, Tile::UPDATE_CLIENTS);
}
shared_ptr<BannerTileEntity> bte = dynamic_pointer_cast<BannerTileEntity>(level->getTileEntity(x, y, z));
if (bte != nullptr)
{
if (face != 1) bte->setIsWall(true);
int color = 15 - (instance->getAuxValue() & 15);
bte->setBaseColor(color);
if (instance->hasTag())
{
CompoundTag* tag = instance->getTag();
if (tag->contains(L"BlockEntityTag", Tag::TAG_Compound))
{
CompoundTag* bet = tag->getCompound(L"BlockEntityTag");
if (bet->contains(L"Patterns"))
{
ListTag<CompoundTag>* list = static_cast<ListTag<CompoundTag>*>(
static_cast<void*>(bet->getList(L"Patterns")));
if (list)
{
for (int j = 0; j < list->size() && j < 6; j++)
{
CompoundTag* entry = list->get(j);
bte->addPattern(entry->getString(L"Pattern"), entry->getInt(L"Color"));
}
}
}
}
}
bte->setChanged();
}
+267
View File
@@ -0,0 +1,267 @@
#include "stdafx.h"
#include "net.minecraft.world.item.h"
#include "net.minecraft.world.level.tile.h"
#include "BannerItem.h"
#include "BannerPatternRecipe.h"
#include "Rose.h"
#include "com.mojang.nbt.h"
#include "ItemInstance.h"
#include "Recipes.h"
const BannerPatternRecipe::PatternInfo BannerPatternRecipe::PATTERNS[] =
{
{ L"bl", {" ", " ", "# "}, 0, -1 },
{ L"br", {" ", " ", " #"}, 0, -1 },
{ L"tl", {"# ", " ", " "}, 0, -1 },
{ L"tr", {" #", " ", " "}, 0, -1 },
{ L"bs", {" ", " ", "###"}, 0, -1 },
{ L"ts", {"###", " ", " "}, 0, -1 },
{ L"ls", {"# ", "# ", "# "}, 0, -1 },
{ L"rs", {" #", " #", " #"}, 0, -1 },
{ L"cs", {" # ", " # ", " # "}, 0, -1 },
{ L"ms", {" ", "###", " "}, 0, -1 },
{ L"drs", {"# ", " # ", " #"}, 0, -1 },
{ L"dls", {" #", " # ", "# "}, 0, -1 },
{ L"ss", {"# #", "# #", " "}, 0, -1 },
{ L"cr", {"# #", " # ", "# #"}, 0, -1 },
{ L"sc", {" # ", "###", " # "}, 0, -1 },
{ L"bt", {" ", " # ", "# #"}, 0, -1 },
{ L"tt", {"# #", " # ", " "}, 0, -1 },
{ L"bts", {" ", "# #", " # "}, 0, -1 },
{ L"tts", {" # ", "# #", " "}, 0, -1 },
{ L"ld", {"## ", "# ", " "}, 0, -1 },
{ L"rd", {" ", " #", " ##"}, 0, -1 },
{ L"lud", {" ", "# ", "## "}, 0, -1 },
{ L"rud", {" ##", " #", " "}, 0, -1 },
{ L"mc", {" ", " # ", " "}, 0, -1 },
{ L"mr", {" # ", "# #", " # "}, 0, -1 },
{ L"vh", {"## ", "## ", "## "}, 0, -1 },
{ L"hh", {"###", "###", " "}, 0, -1 },
{ L"vhr", {" ##", " ##", " ##"}, 0, -1 },
{ L"hhb", {" ", "###", "###"}, 0, -1 },
{ L"bo", {"###", "# #", "###"}, 0, -1 },
{ L"gra", {"# #", " # ", " # "}, 0, -1 },
{ L"gru", {" # ", " # ", "# #"}, 0, -1 },
{ L"cbo", {nullptr, nullptr, nullptr}, Tile::vine_Id, -1 },
{ L"bri", {nullptr, nullptr, nullptr}, Tile::brick_block_Id, -1 },
{ L"cre", {nullptr, nullptr, nullptr}, Item::skull_Id, 4 },
{ L"sku", {nullptr, nullptr, nullptr}, Item::skull_Id, 1 },
{ L"flo", {nullptr, nullptr, nullptr}, Tile::red_flower_Id, Rose::OXEYE_DAISY },
{ L"moj", {nullptr, nullptr, nullptr}, Item::golden_apple_Id, 1 },
};
const int BannerPatternRecipe::PATTERN_COUNT = static_cast<int>(sizeof(PATTERNS) / sizeof(PATTERNS[0]));
int BannerPatternRecipe::getBannerPatternCount(shared_ptr<ItemInstance> banner)
{
if (!banner->hasTag()) return 0;
CompoundTag* tag = banner->getTag();
if (!tag->contains(L"BlockEntityTag", Tag::TAG_Compound)) return 0;
CompoundTag* bet = tag->getCompound(L"BlockEntityTag");
if (!bet->contains(L"Patterns")) return 0;
ListTag<Tag>* list = bet->getList(L"Patterns");
return list ? list->size() : 0;
}
const wchar_t* BannerPatternRecipe::matchPattern(shared_ptr<CraftingContainer> craftSlots, int& outDyeColor)
{
int slots = craftSlots->getContainerSize();
for (int p = 0; p < PATTERN_COUNT; p++)
{
const PatternInfo& pat = PATTERNS[p];
bool gridBased = (pat.rows[0] != nullptr);
if (gridBased)
{
if (slots != 9) continue;
int dyeColor = -1;
bool ok = true;
for (int k = 0; k < 9 && ok; k++)
{
int row = k / 3;
int col = k % 3;
shared_ptr<ItemInstance> item = craftSlots->getItem(k);
char cell = pat.rows[row][col];
if (item != nullptr && dynamic_cast<BannerItem*>(item->getItem()) == nullptr)
{
if (item->id != Item::dye_Id) { ok = false; break; }
if (cell == ' ') { ok = false; break; }
if (dyeColor != -1 && dyeColor != item->getAuxValue()) { ok = false; break; }
dyeColor = item->getAuxValue();
}
else
{
if (cell != ' ') { ok = false; break; }
}
}
if (ok && dyeColor != -1)
{
outDyeColor = dyeColor;
return pat.id;
}
}
else
{
// item-based pattern: needs banner + dye + one special item, classic crafting only
if (slots != 9) continue;
bool hasSpecial = false;
int dyeColor = -1;
bool ok = true;
for (int k = 0; k < slots && ok; k++)
{
shared_ptr<ItemInstance> item = craftSlots->getItem(k);
if (item == nullptr) continue;
if (dynamic_cast<BannerItem*>(item->getItem()) != nullptr) continue;
if (item->id == Item::dye_Id)
{
if (dyeColor != -1) { ok = false; break; }
dyeColor = item->getAuxValue();
}
else if (item->id == pat.specialItemId &&
(pat.specialItemAux == -1 || item->getAuxValue() == pat.specialItemAux))
{
if (hasSpecial) { ok = false; break; }
hasSpecial = true;
}
else
{
ok = false; break;
}
}
if (ok && hasSpecial && dyeColor != -1)
{
outDyeColor = dyeColor;
return pat.id;
}
}
}
return nullptr;
}
bool BannerPatternRecipe::matches(shared_ptr<CraftingContainer> craftSlots, Level* level)
{
if (craftSlots->getContainerSize() != 9) return false;
shared_ptr<ItemInstance> banner = nullptr;
for (int i = 0; i < craftSlots->getContainerSize(); i++)
{
shared_ptr<ItemInstance> item = craftSlots->getItem(i);
if (item == nullptr) continue;
if (dynamic_cast<BannerItem*>(item->getItem()) != nullptr)
{
if (banner != nullptr) return false;
if (getBannerPatternCount(item) >= 6) return false;
banner = item;
}
}
if (banner == nullptr) return false;
int dyeColor = -1;
return matchPattern(craftSlots, dyeColor) != nullptr;
}
shared_ptr<ItemInstance> BannerPatternRecipe::assemble(shared_ptr<CraftingContainer> craftSlots)
{
if (craftSlots == nullptr)
return std::make_shared<ItemInstance>(Tile::standing_banner_Id, 1, 0);
shared_ptr<ItemInstance> banner = nullptr;
for (int i = 0; i < craftSlots->getContainerSize(); i++)
{
shared_ptr<ItemInstance> item = craftSlots->getItem(i);
if (item != nullptr && dynamic_cast<BannerItem*>(item->getItem()) != nullptr)
{
banner = item;
break;
}
}
if (banner == nullptr) return nullptr;
int dyeColor = -1;
const wchar_t* patternId = matchPattern(craftSlots, dyeColor);
if (patternId == nullptr || dyeColor == -1) return nullptr;
shared_ptr<ItemInstance> result = banner->copy();
result->count = 1;
if (!result->hasTag()) result->setTag(new CompoundTag());
CompoundTag* tag = result->getTag();
if (!tag->contains(L"BlockEntityTag", Tag::TAG_Compound))
tag->putCompound(L"BlockEntityTag", new CompoundTag());
CompoundTag* bet = tag->getCompound(L"BlockEntityTag");
ListTag<CompoundTag>* list;
if (bet->contains(L"Patterns"))
{
list = static_cast<ListTag<CompoundTag>*>(static_cast<void*>(bet->getList(L"Patterns")));
}
else
{
list = new ListTag<CompoundTag>();
bet->put(L"Patterns", list);
}
CompoundTag* entry = new CompoundTag();
entry->putString(L"Pattern", patternId);
entry->putInt(L"Color", dyeColor);
list->add(entry);
return result;
}
int BannerPatternRecipe::size()
{
return 10;
}
const ItemInstance* BannerPatternRecipe::getResultItem()
{
return nullptr;
}
const int BannerPatternRecipe::getGroup()
{
return eGroupType_Max;
}
bool BannerPatternRecipe::reqs(int iRecipe)
{
return false;
}
void BannerPatternRecipe::reqs(INGREDIENTS_REQUIRED* pIngReq)
{
pIngReq->iIngC = 0;
pIngReq->iType = RECIPE_TYPE_3x3;
pIngReq->iIngIDA = new int[1]();
pIngReq->iIngValA = new int[1]();
pIngReq->iIngAuxValA = new int[1]();
pIngReq->uiGridA = new unsigned int[9]();
pIngReq->pRecipy = this;
for (unsigned int i = 0; i < XUSER_MAX_COUNT; ++i)
pIngReq->bCanMake[i] = false;
}
void BannerPatternRecipe::writeToStream(DataOutputStream* dos)
{
dos->writeByte(99);
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "Recipy.h"
class BannerPatternRecipe : public Recipy
{
public:
bool matches(shared_ptr<CraftingContainer> craftSlots, Level* level) override;
shared_ptr<ItemInstance> assemble(shared_ptr<CraftingContainer> craftSlots) override;
int size() override;
const ItemInstance* getResultItem() override;
const int getGroup() override;
bool reqs(int iRecipe) override;
void reqs(INGREDIENTS_REQUIRED* pIngReq) override;
void writeToStream(DataOutputStream* dos) override;
private:
struct PatternInfo
{
const wchar_t* id;
const char* rows[3];
int specialItemId;
int specialItemAux;
};
static const PatternInfo PATTERNS[];
static const int PATTERN_COUNT;
static int getBannerPatternCount(shared_ptr<ItemInstance> banner);
const wchar_t* matchPattern(shared_ptr<CraftingContainer> craftSlots, int& outDyeColor);
};
+8 -3
View File
@@ -47,7 +47,10 @@ AABB *BannerTile::getTileAABB(Level *level, int x, int y, int z)
void BannerTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr<TileEntity> forceEntity)
{
if (onGround) return;
shared_ptr<BannerTileEntity> bte = forceEntity
? dynamic_pointer_cast<BannerTileEntity>(forceEntity)
: dynamic_pointer_cast<BannerTileEntity>(level->getTileEntity(x, y, z));
if (!bte || !bte->isWall()) return;
int face = level->getData(x, y, z);
@@ -96,9 +99,11 @@ int BannerTile::getResource(int data, Random *random, int playerBonusLevel)
void BannerTile::neighborChanged(Level *level, int x, int y, int z, int type)
{
shared_ptr<BannerTileEntity> bte = dynamic_pointer_cast<BannerTileEntity>(level->getTileEntity(x, y, z));
bool isWall = bte && bte->isWall();
bool remove = false;
if (onGround)
if (!isWall)
{
if (!level->getMaterial(x, y - 1, z)->isSolid()) remove = true;
}
@@ -144,7 +149,7 @@ void BannerTile::spawnResources(Level *level, int x, int y, int z, int data, flo
int color = m_dropColor;
shared_ptr<BannerTileEntity> bte = dynamic_pointer_cast<BannerTileEntity>(level->getTileEntity(x, y, z));
if (bte != nullptr) color = bte->getBaseColor() & 15;
if (bte != nullptr) color = 15 - (bte->getBaseColor() & 15);
int count = getResourceCountForLootBonus(playerBonusLevel, level->random);
for (int i = 0; i < count; i++)
+4
View File
@@ -7,12 +7,14 @@
BannerTileEntity::BannerTileEntity() : TileEntity()
{
baseColor = 15;
m_isWall = false;
}
void BannerTileEntity::save(CompoundTag *tag)
{
TileEntity::save(tag);
tag->putInt(L"Base", baseColor);
tag->putByte(L"IsWall", m_isWall ? 1 : 0);
if (!patterns.empty())
{
@@ -32,6 +34,7 @@ void BannerTileEntity::load(CompoundTag *tag)
{
TileEntity::load(tag);
baseColor = tag->getInt(L"Base");
m_isWall = tag->contains(L"IsWall") && tag->getByte(L"IsWall") != 0;
patterns.clear();
if (tag->contains(L"Patterns"))
@@ -65,6 +68,7 @@ shared_ptr<TileEntity> BannerTileEntity::clone()
shared_ptr<BannerTileEntity> result = std::make_shared<BannerTileEntity>();
TileEntity::clone(result);
result->baseColor = baseColor;
result->m_isWall = m_isWall;
result->patterns = patterns;
return result;
}
+4
View File
@@ -18,6 +18,7 @@ public:
private:
int baseColor;
bool m_isWall;
vector<BannerPattern> patterns;
public:
@@ -27,6 +28,9 @@ public:
int getBaseColor() const { return baseColor; }
void setBaseColor(int color) { baseColor = color; }
bool isWall() const { return m_isWall; }
void setIsWall(bool wall) { m_isWall = wall; }
const vector<BannerPattern> &getPatterns() const { return patterns; }
void addPattern(const wstring &pattern, int color) { patterns.push_back({ pattern, color }); }
void clearPatterns() { patterns.clear(); }
+4 -1
View File
@@ -48,6 +48,8 @@ void Recipes::_init()
void Recipes::_compileRecipes()
{
recipies->push_back(new BannerPatternRecipe());
addShapedRecipy(new ItemInstance(Tile::wood, 4, 0), //
L"sczg",
L"#", //
@@ -1634,6 +1636,7 @@ void Recipes::loadFromPacket(byteArray packetData)
DataInputStream input(&bais);
this->_wipeRecipes();
recipies->push_back(new BannerPatternRecipe());
{
int iCount = input.readInt();
for (int i = 0; i < iCount; i++) {
@@ -1662,4 +1665,4 @@ std::shared_ptr<CustomPayloadPacket> Recipes::createUpdatePacket()
}
return std::make_shared<CustomPayloadPacket>(CustomPayloadPacket::UPDATE_RECIPE_REGISTRY, baos.toByteArray());
}
}
+2 -1
View File
@@ -622,7 +622,8 @@ void Tile::staticCtor()
Tile::double_plant = static_cast<TallGrass2*>((new TallGrass2(175))->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_GRASS)->setIconName(L"tallgrass2_tall_grass_upper")->setDescriptionId(IDS_DESC_DOUBLE_TALL_GRASS)->setUseDescriptionId(IDS_DESC_TALL_GRASS)->disableMipmap()->sendTileData(0xFF));
Tile::standing_banner = static_cast<BannerTile*>((new BannerTile(176, true))->setDestroyTime(1.0f)->setSoundType(Tile::SOUND_WOOD)->setIconName(L"planks_oak")->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_SIGN));
Tile::wall_banner = static_cast<BannerTile*>((new BannerTile(177, false))->setDestroyTime(1.0f)->setSoundType(Tile::SOUND_WOOD)->setIconName(L"planks_oak")->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_SIGN));
Tile::wall_banner = Tile::standing_banner;
Tile::tiles[177] = Tile::standing_banner;
Item::items[standing_banner_Id] = (new BannerItem(standing_banner_Id - 256))->setIconName(L"sign")->setDescriptionId(IDS_TILE_BANNER)->setUseDescriptionId(IDS_DESC_SIGN);
+1 -1
View File
@@ -417,7 +417,7 @@ public:
static const int packed_ice_Id = 174;
static const int double_plant_Id = 175;
static const int standing_banner_Id = 176;
static const int wall_banner_Id = 177;
static const int wall_banner_Id = 176;
static const int daylight_detector_inverted_Id = 178;
static const int red_sandstone_Id = 179;
static const int red_sandstone_stairs_Id = 180;
@@ -1199,6 +1199,8 @@ source_group("net/minecraft/world/item/alchemy" FILES ${_MINECRAFT_WORLD_COMMON_
set(_MINECRAFT_WORLD_COMMON_NET_MINECRAFT_WORLD_ITEM_CRAFTING
"${CMAKE_CURRENT_SOURCE_DIR}/ArmorDyeRecipe.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/ArmorDyeRecipe.h"
"${CMAKE_CURRENT_SOURCE_DIR}/BannerPatternRecipe.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/BannerPatternRecipe.h"
"${CMAKE_CURRENT_SOURCE_DIR}/ArmorRecipes.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/ArmorRecipes.h"
"${CMAKE_CURRENT_SOURCE_DIR}/ClothDyeRecipes.cpp"
@@ -13,4 +13,5 @@
#include "ShapelessRecipy.h"
#include "StructureRecipies.h"
#include "ToolRecipies.h"
#include "WeaponRecipies.h"
#include "WeaponRecipies.h"
#include "BannerPatternRecipe.h"