fix: enchantment table + frost walker (kinda)
fixes: - treasure enchants being selectable within the enchantment table menu - frost walker resemblance to decompiled legacy console edition - enchantment table featuring hot-swappable abilities where the player could replace one tool with another whilst keeping the exact same potential enchantments in the enchant menu
This commit is contained in:
@@ -94,7 +94,7 @@ void Enchantment::staticCtor()
|
||||
for(unsigned int i = 0; i < 256; ++i)
|
||||
{
|
||||
Enchantment *enchantment = enchantments[i];
|
||||
if (enchantment != nullptr && !enchantment->isTreasureEnchantment())
|
||||
if (enchantment != nullptr && !enchantment->isTreasureOnly())
|
||||
{
|
||||
validEnchantments.push_back(enchantment);
|
||||
}
|
||||
|
||||
@@ -85,9 +85,9 @@ public:
|
||||
virtual int getDamageProtection(int level, DamageSource *source);
|
||||
virtual float getDamageBonus(int level, shared_ptr<LivingEntity> target);
|
||||
virtual bool isCompatibleWith(Enchantment *other) const;
|
||||
virtual bool isTreasureEnchantment() const { return false; }
|
||||
virtual Enchantment *setDescriptionId(int id);
|
||||
virtual int getDescriptionId();
|
||||
virtual bool isTreasureOnly() { return false; }
|
||||
virtual HtmlString getFullname(int level);
|
||||
virtual bool canEnchant(shared_ptr<ItemInstance> item);
|
||||
// 4J Added
|
||||
|
||||
@@ -481,6 +481,11 @@ unordered_map<int, EnchantmentInstance *> *EnchantmentHelper::getAvailableEnchan
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e->isTreasureOnly())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only picks "normal" enchantments, no specialcases
|
||||
if (!e->category->canEnchant(item) && !isBook)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ class Inventory;
|
||||
class DamageSource;
|
||||
class Enchantment;
|
||||
class EnchantmentInstance;
|
||||
class LivingEntity;
|
||||
|
||||
class EnchantmentHelper
|
||||
{
|
||||
@@ -50,6 +51,7 @@ private:
|
||||
*/
|
||||
public:
|
||||
static int getDamageProtection(ItemInstanceArray armor, DamageSource *source);
|
||||
static bool hasFrostWalker(shared_ptr<LivingEntity> living);
|
||||
|
||||
private:
|
||||
class GetDamageBonusIteration : public EnchantmentIterationMethod
|
||||
|
||||
@@ -98,9 +98,36 @@ vector<EnchantmentInstance*> EnchantmentMenu::getEnchantment() {
|
||||
void EnchantmentMenu::slotsChanged(int a) // 4J used to take a shared_ptr<Container> container but wasn't using it, so removed to simplify things
|
||||
{
|
||||
shared_ptr<ItemInstance> item = enchantSlots->getItem(0);
|
||||
|
||||
shared_ptr<ItemInstance> lapis = lapisSlot->getItem(1);
|
||||
|
||||
bool itemChanged = false;
|
||||
if (item == nullptr || lastEnchantmentItem == nullptr)
|
||||
{
|
||||
itemChanged = (item == nullptr) != (lastEnchantmentItem == nullptr);
|
||||
}
|
||||
else if (item->id != lastEnchantmentItem->id || item->getDamageValue() != lastEnchantmentItem->getDamageValue())
|
||||
{
|
||||
itemChanged = true;
|
||||
}
|
||||
|
||||
if (itemChanged)
|
||||
{
|
||||
alreadyRan = false;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (cachedEnchantments[i] != nullptr)
|
||||
{
|
||||
for (EnchantmentInstance *cached : *cachedEnchantments[i])
|
||||
{
|
||||
delete cached;
|
||||
}
|
||||
cachedEnchantments[i]->clear();
|
||||
delete cachedEnchantments[i];
|
||||
cachedEnchantments[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item == nullptr || !item->isEnchantable())
|
||||
{
|
||||
if (!level->isClientSide)
|
||||
@@ -220,6 +247,7 @@ void EnchantmentMenu::slotsChanged(int a) // 4J used to take a shared_ptr<Contai
|
||||
}
|
||||
|
||||
wasLapis = lapis != nullptr;
|
||||
lastEnchantmentItem = item;
|
||||
}
|
||||
|
||||
bool EnchantmentMenu::clickMenuButton(shared_ptr<Player> player, int i)
|
||||
@@ -257,6 +285,7 @@ bool EnchantmentMenu::clickMenuButton(shared_ptr<Player> player, int i)
|
||||
delete e;
|
||||
}
|
||||
delete newEnchantment;
|
||||
cachedEnchantments[i] = nullptr;
|
||||
slotsChanged(1);// Removed enchantSlots parameter as the function can reference it directly
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ public:
|
||||
public:
|
||||
shared_ptr<Container> enchantSlots;
|
||||
shared_ptr<Container> lapisSlot;
|
||||
shared_ptr<ItemInstance> lastEnchantmentItem;
|
||||
bool alreadyRan = false;
|
||||
bool en = false;
|
||||
// Header
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
#include "net.minecraft.world.level.h"
|
||||
#include "net.minecraft.world.level.tile.h"
|
||||
#include "net.minecraft.world.entity.h"
|
||||
#include "net.minecraft.world.entity.player.h"
|
||||
#include "Material.h"
|
||||
#include "Mth.h"
|
||||
#include "BlockPos.h"
|
||||
#include "FrostWalkerEnchantment.h"
|
||||
#include "EnchantmentHelper.h"
|
||||
|
||||
FrostWalkerEnchantment::FrostWalkerEnchantment(int id, int frequency) : Enchantment(id, frequency, EnchantmentCategory::armor_feet)
|
||||
{
|
||||
@@ -26,38 +29,56 @@ int FrostWalkerEnchantment::getMaxLevel()
|
||||
return 2;
|
||||
}
|
||||
|
||||
void FrostWalkerEnchantment::freezeNearby(shared_ptr<LivingEntity> living, Level *level, int px, int py, int pz, int enchLevel)
|
||||
bool FrostWalkerEnchantment::isCompatibleWith(Enchantment *other) const
|
||||
{
|
||||
if (!Enchantment::isCompatibleWith(other)) return false;
|
||||
return other != Enchantment::waterWalker;
|
||||
}
|
||||
|
||||
bool EnchantmentHelper::hasFrostWalker(shared_ptr<LivingEntity> living)
|
||||
{
|
||||
return getFrostWalker(living) != 0;
|
||||
}
|
||||
|
||||
void FrostWalkerEnchantment::onEntityMoved(shared_ptr<LivingEntity> living, Level *level, BlockPos pos, int enchLevel)
|
||||
{
|
||||
if (!living->onGround) return;
|
||||
|
||||
int radius = 2 + enchLevel;
|
||||
Player *player = dynamic_cast<Player *>(living.get());
|
||||
if (player != nullptr && player->abilities.flying) return;
|
||||
|
||||
int radius = enchLevel + 2;
|
||||
if (radius > 16) radius = 16;
|
||||
float f = (float)radius;
|
||||
int r = radius;
|
||||
float f = static_cast<float>(radius);
|
||||
|
||||
for (int dx = -r; dx <= r; dx++)
|
||||
BlockPos minPos = pos.offset(-radius, -1, -radius);
|
||||
BlockPos maxPos = pos.offset(radius, -1, radius);
|
||||
|
||||
for (int x = minPos.getX(); x <= maxPos.getX(); ++x)
|
||||
{
|
||||
for (int dz = -r; dz <= r; dz++)
|
||||
for (int y = minPos.getY(); y <= maxPos.getY(); ++y)
|
||||
{
|
||||
int bx = px + dx;
|
||||
int by = py - 1;
|
||||
int bz = pz + dz;
|
||||
|
||||
double ddx = (bx + 0.5) - living->x;
|
||||
double ddy = (by + 0.5) - living->y;
|
||||
double ddz = (bz + 0.5) - living->z;
|
||||
if (ddx * ddx + ddy * ddy + ddz * ddz > (double)(f * f)) continue;
|
||||
|
||||
if (level->getTile(bx, by + 1, bz) != 0) continue;
|
||||
|
||||
Material *ground = level->getMaterial(bx, by, bz);
|
||||
if (ground != Material::water) continue;
|
||||
if (level->getData(bx, by, bz) != 0) continue;
|
||||
|
||||
if (Tile::frosted_ice->mayPlace(level, bx, by, bz))
|
||||
for (int z = minPos.getZ(); z <= maxPos.getZ(); ++z)
|
||||
{
|
||||
level->setTileAndData(bx, by, bz, Tile::frosted_ice_Id, 0, Tile::UPDATE_ALL);
|
||||
double dx = (x + 0.5) - living->x;
|
||||
double dy = (y + 0.5) - living->y;
|
||||
double dz = (z + 0.5) - living->z;
|
||||
if (dx * dx + dy * dy + dz * dz > static_cast<double>(f * f)) continue;
|
||||
|
||||
if (level->getTile(x, y + 1, z) != 0) continue;
|
||||
|
||||
Material *ground = level->getMaterial(x, y, z);
|
||||
if (ground != Material::water) continue;
|
||||
if (level->getData(x, y, z) != 0) continue;
|
||||
|
||||
bool canPlace = level->mayPlace(Tile::frosted_ice_Id, x, y, z, false, 0, nullptr, nullptr);
|
||||
if (!canPlace) continue;
|
||||
|
||||
level->setTileAndData(x, y, z, Tile::frosted_ice_Id, 0, Tile::UPDATE_ALL);
|
||||
|
||||
int ticks = Mth::nextInt(living->getRandom(), 60, 120);
|
||||
level->addToTickNextTick(x, y, z, Tile::frosted_ice_Id, ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
class Level;
|
||||
class LivingEntity;
|
||||
class BlockPos;
|
||||
|
||||
class FrostWalkerEnchantment : public Enchantment
|
||||
{
|
||||
@@ -13,7 +14,9 @@ public:
|
||||
virtual int getMinCost(int level) override;
|
||||
virtual int getMaxCost(int level) override;
|
||||
virtual int getMaxLevel() override;
|
||||
virtual bool isTreasureEnchantment() const override { return true; }
|
||||
virtual bool isCompatibleWith(Enchantment *other) const override;
|
||||
virtual bool isTreasureOnly() override { return true; }
|
||||
|
||||
static void freezeNearby(shared_ptr<LivingEntity> living, Level *level, int px, int py, int pz, int enchLevel);
|
||||
static void freezeNearby(shared_ptr<LivingEntity> living, Level *level, int x, int y, int z, int enchLevel);
|
||||
static void onEntityMoved(shared_ptr<LivingEntity> living, Level *level, BlockPos pos, int enchLevel);
|
||||
};
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include "Dimension.h"
|
||||
#include "GenericStats.h"
|
||||
#include "ItemEntity.h"
|
||||
#include "TilePos.h"
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
#include "../Minecraft.Server/FourKitBridge.h"
|
||||
#endif
|
||||
@@ -296,17 +298,7 @@ void LivingEntity::baseTick()
|
||||
tickEffects();
|
||||
|
||||
animStepO = animStep;
|
||||
|
||||
if (!level->isClientSide && isAlive())
|
||||
{
|
||||
int frostWalkerLevel = EnchantmentHelper::getFrostWalker(dynamic_pointer_cast<LivingEntity>(shared_from_this()));
|
||||
if (frostWalkerLevel > 0)
|
||||
{
|
||||
FrostWalkerEnchantment::freezeNearby(dynamic_pointer_cast<LivingEntity>(shared_from_this()), level,
|
||||
Mth::floor(x), Mth::floor(y), Mth::floor(z), frostWalkerLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
yBodyRotO = yBodyRot;
|
||||
yHeadRotO = yHeadRot;
|
||||
yRotO = yRot;
|
||||
@@ -1075,6 +1067,21 @@ int LivingEntity::getDeathSound()
|
||||
return eSoundType_DAMAGE_HURT;
|
||||
}
|
||||
|
||||
// frost walker-specific
|
||||
|
||||
void LivingEntity::onChangedBlock(BlockPos pos)
|
||||
{
|
||||
shared_ptr<LivingEntity> self = dynamic_pointer_cast<LivingEntity>(shared_from_this());
|
||||
|
||||
int frostWalkerLevel = EnchantmentHelper::getEnchantmentLevel(Enchantment::frostWalker->id, getEquipmentSlots());
|
||||
if (frostWalkerLevel < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FrostWalkerEnchantment::onEntityMoved(self, level, pos, frostWalkerLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop extra rare loot. Only occurs roughly 5% of the time, rareRootLevel
|
||||
* is set to 1 (otherwise 0) 1% of the time.
|
||||
|
||||
@@ -203,6 +203,7 @@ public:
|
||||
virtual bool onLadder();
|
||||
virtual bool isShootable();
|
||||
virtual bool isAlive();
|
||||
virtual void onChangedBlock(BlockPos pos);
|
||||
virtual void causeFallDamage(float distance);
|
||||
virtual void animateHurt();
|
||||
virtual int getArmorValue();
|
||||
|
||||
@@ -26,4 +26,4 @@ int MendingEnchantment::getMaxLevel()
|
||||
bool MendingEnchantment::canEnchant(shared_ptr<ItemInstance> item)
|
||||
{
|
||||
return item->isDamageableItem();
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,6 @@ public:
|
||||
virtual int getMinCost(int level) override;
|
||||
virtual int getMaxCost(int level) override;
|
||||
virtual int getMaxLevel() override;
|
||||
virtual bool isTreasureEnchantment() const override { return true; }
|
||||
virtual bool isTreasureOnly() override { return true; };
|
||||
virtual bool canEnchant(shared_ptr<ItemInstance> item) override;
|
||||
};
|
||||
|
||||
@@ -145,6 +145,7 @@ Player::Player(Level *level, const wstring &name) : LivingEntity( level )
|
||||
Pos *spawnPos = level->getSharedSpawnPos();
|
||||
moveTo(spawnPos->x + 0.5, spawnPos->y + 1, spawnPos->z + 0.5, 0, 0);
|
||||
delete spawnPos;
|
||||
lastChangedBlockPos = BlockPos(x, y, z);
|
||||
|
||||
rotOffs = 180;
|
||||
flameTime = 20;
|
||||
@@ -330,6 +331,13 @@ void Player::tick()
|
||||
|
||||
LivingEntity::tick();
|
||||
|
||||
BlockPos currentBlockPos(Mth::floor(x), Mth::floor(y), Mth::floor(z));
|
||||
if (currentBlockPos != lastChangedBlockPos)
|
||||
{
|
||||
lastChangedBlockPos = currentBlockPos;
|
||||
onChangedBlock(currentBlockPos);
|
||||
}
|
||||
|
||||
if (!level->isClientSide)
|
||||
{
|
||||
if (containerMenu != nullptr && !containerMenu->stillValid( dynamic_pointer_cast<Player>( shared_from_this() ) ))
|
||||
@@ -624,6 +632,14 @@ bool Player::isImmobile()
|
||||
return getHealth() <= 0 || isSleeping();
|
||||
}
|
||||
|
||||
void Player::onChangedBlock(BlockPos pos)
|
||||
{
|
||||
if (true /*!isSpectator()*/) // spectator is not a thing yet
|
||||
{
|
||||
LivingEntity::onChangedBlock(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::closeContainer()
|
||||
{
|
||||
containerMenu = inventoryMenu;
|
||||
|
||||
@@ -8,6 +8,7 @@ using namespace std;
|
||||
#include "PlayerEnderChestContainer.h"
|
||||
#include "CommandSender.h"
|
||||
#include "ScoreHolder.h"
|
||||
#include "BlockPos.h"
|
||||
|
||||
class AbstractContainerMenu;
|
||||
class Stats;
|
||||
@@ -133,6 +134,7 @@ public:
|
||||
private:
|
||||
int sleepCounter; // animation timer
|
||||
int deathFadeCounter; // animation timer
|
||||
BlockPos lastChangedBlockPos;
|
||||
|
||||
public:
|
||||
float bedOffsetX, bedOffsetY, bedOffsetZ;
|
||||
@@ -207,6 +209,7 @@ protected:
|
||||
|
||||
public:
|
||||
virtual void handleEntityEvent(byte id);
|
||||
void onChangedBlock(BlockPos pos);
|
||||
|
||||
protected:
|
||||
bool isImmobile();
|
||||
|
||||
Reference in New Issue
Block a user