From f7b6b7f21e565ee302bbf74457da6ee8ae00b518 Mon Sep 17 00:00:00 2001 From: Sam Eads Date: Thu, 29 Jan 2026 20:27:54 -0500 Subject: [PATCH 1/2] Door lighting n/s check not to increment/decrement all coordinates --- source/client/renderer/TileRenderer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/client/renderer/TileRenderer.cpp b/source/client/renderer/TileRenderer.cpp index 5b104ad4d..037b0e802 100644 --- a/source/client/renderer/TileRenderer.cpp +++ b/source/client/renderer/TileRenderer.cpp @@ -1159,7 +1159,7 @@ bool TileRenderer::tesselateDoorInWorld(Tile* tile, const TilePos& pos) t.color(fBright, fBright, fBright); renderFaceDown(tile, pos, tile->getTexture(m_pTileSource, pos, Facing::UP)); - fBright = tile->getBrightness(m_pTileSource, pos - 1); + fBright = tile->getBrightness(m_pTileSource, pos.north()); if (tile->m_aabb.min.z > 0.0f) fBright = fBrightHere; if (Tile::lightEmission[tile->m_ID]) fBright = 1.0f; t.color(fBright * 0.8f, fBright * 0.8f, fBright * 0.8f); @@ -1168,7 +1168,7 @@ bool TileRenderer::tesselateDoorInWorld(Tile* tile, const TilePos& pos) renderNorth(tile, pos, texture); m_bXFlipTexture = false; - fBright = tile->getBrightness(m_pTileSource, pos + 1); + fBright = tile->getBrightness(m_pTileSource, pos.south()); if (tile->m_aabb.max.z < 1.0f) fBright = fBrightHere; if (Tile::lightEmission[tile->m_ID]) fBright = 1.0f; t.color(fBright * 0.8f, fBright * 0.8f, fBright * 0.8f); From 88743fe273b1c5adfb656d5160804a91321ac487 Mon Sep 17 00:00:00 2001 From: hellosammu Date: Fri, 30 Jan 2026 10:23:36 -0500 Subject: [PATCH 2/2] Digging tool strength & durability visuals --- source/CMakeLists.txt | 4 ++ .../client/renderer/entity/ItemRenderer.cpp | 23 +++++++- source/world/entity/Player.cpp | 36 ++++++++++- source/world/entity/Player.hpp | 2 +- source/world/gamemode/SurvivalMode.cpp | 2 +- source/world/item/HatchetItem.cpp | 16 +++++ source/world/item/HatchetItem.hpp | 9 +++ source/world/item/Item.cpp | 52 ++++++++-------- source/world/item/Item.hpp | 29 ++++----- source/world/item/ItemStack.cpp | 37 ++++++------ source/world/item/ItemStack.hpp | 8 +-- source/world/item/PickaxeItem.cpp | 59 +++++++++++++++++++ source/world/item/PickaxeItem.hpp | 10 ++++ source/world/item/ShovelItem.cpp | 27 +++++++++ source/world/item/ShovelItem.hpp | 11 ++++ source/world/item/ToolItem.cpp | 49 +++++++++++++++ source/world/item/ToolItem.hpp | 28 +++++++++ source/world/level/Material.cpp | 21 +++++-- source/world/level/Material.hpp | 3 + source/world/tile/Tile.cpp | 5 +- 20 files changed, 361 insertions(+), 70 deletions(-) create mode 100644 source/world/item/HatchetItem.cpp create mode 100644 source/world/item/HatchetItem.hpp create mode 100644 source/world/item/PickaxeItem.cpp create mode 100644 source/world/item/PickaxeItem.hpp create mode 100644 source/world/item/ShovelItem.cpp create mode 100644 source/world/item/ShovelItem.hpp create mode 100644 source/world/item/ToolItem.cpp create mode 100644 source/world/item/ToolItem.hpp diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 24ebc1bb5..af4e85c01 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -332,6 +332,10 @@ add_library(reminecraftpe-core STATIC world/item/ClothItem.cpp world/item/DyeColor.cpp world/item/SlabItem.cpp + world/item/ToolItem.cpp + world/item/HatchetItem.cpp + world/item/PickaxeItem.cpp + world/item/ShovelItem.cpp world/particle/RedDustParticle.cpp world/particle/TerrainParticle.cpp world/particle/BubbleParticle.cpp diff --git a/source/client/renderer/entity/ItemRenderer.cpp b/source/client/renderer/entity/ItemRenderer.cpp index 8eadcd4b2..2773df240 100644 --- a/source/client/renderer/entity/ItemRenderer.cpp +++ b/source/client/renderer/entity/ItemRenderer.cpp @@ -189,9 +189,30 @@ void ItemRenderer::renderGuiItemOverlay(Font* font, Textures* textures, ItemStac if (item.isEmpty()) return; - if (item.m_count == 1) + // Draw damage amount + if (item.isDamaged()) { + int duraWidth = ceilf(13.0 - static_cast(item.getDamageValue()) * 13.0 / static_cast(item.getMaxDamage())); + int duraPercent = ceilf(255.0 - static_cast(item.getDamageValue()) * 255.0 / static_cast(item.getMaxDamage())); + + + int duraBgColor = (((255 - duraPercent) / 4) << 16) | 0x3F00; + int duraColor = ((255 - duraPercent) << 16) | (duraPercent << 8); + + Tesselator& t = Tesselator::instance; + + blitRect(t, x + 2, y + 13, 13, 2, 0); + blitRect(t, x + 2, y + 13, 12, 1, duraBgColor); + blitRect(t, x + 2, y + 13, duraWidth, 1, duraColor); + + + glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + } + + if (item.m_count <= 1) { return; + } + // Draw num items std::stringstream ss; ss << item.m_count; std::string amtstr = ss.str(); diff --git a/source/world/entity/Player.cpp b/source/world/entity/Player.cpp index f9d5ea840..fba4388d8 100644 --- a/source/world/entity/Player.cpp +++ b/source/world/entity/Player.cpp @@ -334,7 +334,15 @@ void Player::useItem(ItemStack& item) const bool Player::canDestroy(const Tile* pTile) const { - return true; + // If the tile's material does not need tool check then allow destroy regardless of equipped item + if (pTile->m_pMaterial->isMineable()) + return true; + + ItemStack& item = getSelectedItem(); + if (!item.isEmpty()) + return item.canDestroySpecial(pTile); + + return false; } void Player::closeContainer() @@ -347,6 +355,32 @@ void Player::displayClientMessage(const std::string& msg) } +float Player::getDestroySpeed(const Tile* tile) const +{ + float speed = 1.0f; + + ItemStack& item = getSelectedItem(); + if (!item.isEmpty()) + { + // Original multiplies but there's no need since you're just multiplying by 1 on the first check. + speed = item.getDestroySpeed(tile); + } + + // Speed penalty for being underwater + if (isUnderLiquid(Material::water)) + { + speed /= 5.0F; + } + + // Speed penalty for jumping/falling + if (!m_bOnGround) + { + speed /= 5.0F; + } + + return speed; +} + int Player::getInventorySlot(int x) const { return 0; diff --git a/source/world/entity/Player.hpp b/source/world/entity/Player.hpp index 11f088424..d5186e141 100644 --- a/source/world/entity/Player.hpp +++ b/source/world/entity/Player.hpp @@ -71,7 +71,7 @@ class Player : public Mob bool canDestroy(const Tile*) const; void closeContainer(); void displayClientMessage(const std::string& msg); - float getDestroySpeed() const { return 1.0f; } + float getDestroySpeed(const Tile* tile) const; int getInventorySlot(int x) const; TilePos getRespawnPosition() const { return m_respawnPos; } int getScore() const { return m_score; } diff --git a/source/world/gamemode/SurvivalMode.cpp b/source/world/gamemode/SurvivalMode.cpp index 8a86fd1e6..e7040e001 100644 --- a/source/world/gamemode/SurvivalMode.cpp +++ b/source/world/gamemode/SurvivalMode.cpp @@ -66,7 +66,7 @@ bool SurvivalMode::destroyBlock(Player* player, const TilePos& pos, Facing::Name ItemStack& item = player->getSelectedItem(); if (!item.isEmpty()) { - item.mineBlock(pos, face); + item.mineBlock(pos, face, player); if (item.m_count == 0) { item.snap(player); diff --git a/source/world/item/HatchetItem.cpp b/source/world/item/HatchetItem.cpp new file mode 100644 index 000000000..80322f1a3 --- /dev/null +++ b/source/world/item/HatchetItem.cpp @@ -0,0 +1,16 @@ +#include "HatchetItem.hpp" +#include "world/tile/Tile.hpp" + +HatchetItem::HatchetItem(int id, Tier& tier) : ToolItem(id, 1, tier) +{ + static const Tile* hatchetTiles[] = + { + Tile::wood, + Tile::bookshelf, + Tile::treeTrunk, + // Tile::chest + }; + + static const int HATCHET_TILE_COUNT = sizeof(hatchetTiles) / sizeof(Tile*); + initializeTiles(hatchetTiles, HATCHET_TILE_COUNT); +} \ No newline at end of file diff --git a/source/world/item/HatchetItem.hpp b/source/world/item/HatchetItem.hpp new file mode 100644 index 000000000..48cc64928 --- /dev/null +++ b/source/world/item/HatchetItem.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "ToolItem.hpp" + +class HatchetItem : public ToolItem +{ +public: + HatchetItem(int id, Tier& tier); +}; diff --git a/source/world/item/Item.cpp b/source/world/item/Item.cpp index c5aa77dc1..3456e2269 100644 --- a/source/world/item/Item.cpp +++ b/source/world/item/Item.cpp @@ -15,6 +15,9 @@ #include "TileItem.hpp" #include "TilePlanterItem.hpp" #include "RocketItem.hpp" +#include "PickaxeItem.hpp" +#include "ShovelItem.hpp" +#include "HatchetItem.hpp" #define ITEM(x) ((x) - 256) @@ -119,17 +122,16 @@ void Item::initItems() ->setDescriptionId("swordWood") ->handEquipped(); - Item::pickAxe_wood = NEW_ITEM(ITEM_PICKAXE_WOOD) + Item::pickAxe_wood = NEW_X_ITEM(PickaxeItem, ITEM_PICKAXE_WOOD, Tier::WOOD) ->setIcon(0, 6) - ->setDescriptionId("pickaxeWood") - ->handEquipped(); + ->setDescriptionId("pickaxeWood"); - Item::hatchet_wood = NEW_ITEM(ITEM_HATCHET_WOOD) + Item::hatchet_wood = NEW_X_ITEM(HatchetItem, ITEM_HATCHET_WOOD, Tier::WOOD) ->setIcon(0, 7) ->setDescriptionId("hatchetWood") ->handEquipped(); - Item::shovel_wood = NEW_ITEM(ITEM_SHOVEL_WOOD) + Item::shovel_wood = NEW_X_ITEM(ShovelItem, ITEM_SHOVEL_WOOD, Tier::WOOD) ->setIcon(0, 5) ->setDescriptionId("shovelWood") ->handEquipped(); @@ -144,17 +146,17 @@ void Item::initItems() ->setDescriptionId("swordStone") ->handEquipped(); - Item::pickAxe_stone = NEW_ITEM(ITEM_PICKAXE_STONE) + Item::pickAxe_stone = NEW_X_ITEM(PickaxeItem, ITEM_PICKAXE_STONE, Tier::STONE) ->setIcon(1, 6) ->setDescriptionId("pickaxeStone") ->handEquipped(); - Item::hatchet_stone = NEW_ITEM(ITEM_HATCHET_STONE) + Item::hatchet_stone = NEW_X_ITEM(HatchetItem, ITEM_HATCHET_STONE, Tier::STONE) ->setIcon(1, 7) ->setDescriptionId("hatchetStone") ->handEquipped(); - Item::shovel_stone = NEW_ITEM(ITEM_SHOVEL_STONE) + Item::shovel_stone = NEW_X_ITEM(ShovelItem, ITEM_SHOVEL_STONE, Tier::STONE) ->setIcon(1, 5) ->setDescriptionId("shovelStone") ->handEquipped(); @@ -169,17 +171,17 @@ void Item::initItems() ->setDescriptionId("swordIron") ->handEquipped(); - Item::pickAxe_iron = NEW_ITEM(ITEM_PICKAXE_IRON) + Item::pickAxe_iron = NEW_X_ITEM(PickaxeItem, ITEM_PICKAXE_IRON, Tier::IRON) ->setIcon(2, 6) ->setDescriptionId("pickaxeIron") ->handEquipped(); - Item::hatchet_iron = NEW_ITEM(ITEM_HATCHET_IRON) + Item::hatchet_iron = NEW_X_ITEM(HatchetItem, ITEM_HATCHET_IRON, Tier::IRON) ->setIcon(2, 7) ->setDescriptionId("hatchetIron") ->handEquipped(); - Item::shovel_iron= NEW_ITEM(ITEM_SHOVEL_IRON) + Item::shovel_iron= NEW_X_ITEM(ShovelItem, ITEM_SHOVEL_IRON, Tier::IRON) ->setIcon(2, 5) ->setDescriptionId("shovelIron") ->handEquipped(); @@ -194,17 +196,17 @@ void Item::initItems() ->setDescriptionId("swordGold") ->handEquipped(); - Item::pickAxe_gold = NEW_ITEM(ITEM_PICKAXE_GOLD) + Item::pickAxe_gold = NEW_X_ITEM(PickaxeItem, ITEM_PICKAXE_GOLD, Tier::GOLD) ->setIcon(4, 6) ->setDescriptionId("pickaxeGold") ->handEquipped(); - Item::hatchet_gold = NEW_ITEM(ITEM_HATCHET_GOLD) + Item::hatchet_gold = NEW_X_ITEM(HatchetItem, ITEM_HATCHET_GOLD, Tier::GOLD) ->setIcon(4, 7) ->setDescriptionId("hatchetGold") ->handEquipped(); - Item::shovel_gold = NEW_ITEM(ITEM_SHOVEL_GOLD) + Item::shovel_gold = NEW_X_ITEM(ShovelItem, ITEM_SHOVEL_GOLD, Tier::GOLD) ->setIcon(4, 5) ->setDescriptionId("shovelGold") ->handEquipped(); @@ -219,17 +221,17 @@ void Item::initItems() ->setDescriptionId("swordDiamond") ->handEquipped(); - Item::pickAxe_emerald = NEW_ITEM(ITEM_PICKAXE_EMERALD) + Item::pickAxe_emerald = NEW_X_ITEM(PickaxeItem, ITEM_PICKAXE_EMERALD, Tier::EMERALD) ->setIcon(3, 6) ->setDescriptionId("pickaxeDiamond") ->handEquipped(); - Item::hatchet_emerald = NEW_ITEM(ITEM_HATCHET_EMERALD) + Item::hatchet_emerald = NEW_X_ITEM(HatchetItem, ITEM_HATCHET_EMERALD, Tier::EMERALD) ->setIcon(3, 7) ->setDescriptionId("hatchetDiamond") ->handEquipped(); - Item::shovel_emerald = NEW_ITEM(ITEM_SHOVEL_EMERALD) + Item::shovel_emerald = NEW_X_ITEM(ShovelItem, ITEM_SHOVEL_EMERALD, Tier::EMERALD) ->setIcon(3, 5) ->setDescriptionId("shovelDiamond") ->handEquipped(); @@ -571,7 +573,7 @@ bool Item::useOn(ItemStack* instance, Player* player, Level* level, const TilePo return false; } -float Item::getDestroySpeed(ItemStack* instance, Tile* tile) const +float Item::getDestroySpeed(ItemStack* instance, const Tile* tile) const { return 1.0f; } @@ -606,7 +608,7 @@ void Item::hurtEnemy(ItemStack* instance, Mob* mob) const } -void Item::mineBlock(ItemStack* instance, const TilePos& pos, Facing::Name face) const +void Item::mineBlock(ItemStack* instance, const TilePos& pos, Facing::Name face, Player* player) const { } @@ -616,7 +618,7 @@ int Item::getAttackDamage(Entity* ent) const return 1; } -bool Item::canDestroySpecial(Tile* tile) const +bool Item::canDestroySpecial(const Tile* tile) const { return false; } @@ -800,10 +802,10 @@ Item *Item::quiver; Item::Tier - Item::Tier::WOOD (0, 59, 2.0f, 0), - Item::Tier::STONE (1, 131, 4.0f, 1), - Item::Tier::IRON (2, 250, 6.0f, 2), - Item::Tier::EMERALD(3, 1561, 8.0f, 3), - Item::Tier::GOLD (0, 32, 12.0f, 0); + Item::Tier::WOOD (0, 0/* 59 */, 2.0f, 0), + Item::Tier::STONE (1, 0/* 131 */, 4.0f, 1), + Item::Tier::IRON (2, 0/* 250 */, 6.0f, 2), + Item::Tier::EMERALD(3, 0/* 1561 */, 8.0f, 3), + Item::Tier::GOLD (0, 0/* 32 */, 12.0f, 0); std::string Item::ICON_DESCRIPTION_PREFIX = "item."; diff --git a/source/world/item/Item.hpp b/source/world/item/Item.hpp index 6b713cf76..0d16a9d8f 100644 --- a/source/world/item/Item.hpp +++ b/source/world/item/Item.hpp @@ -45,17 +45,18 @@ class Item struct Tier { - int field_0; - int m_Durability; - float m_HitStrength; - int field_C; - - Tier(int a, int b, float c, int d) : - field_0(a), - m_Durability(b), - m_HitStrength(c), - field_C(d) - {} + int m_level; + int m_uses; + float m_speed; + int m_damage; + + Tier(int level, int uses, float speed, int damage) : + m_level(level), + m_uses(uses), + m_speed(speed), + m_damage(damage) + { + } // Item tiers static Tier WOOD, STONE, IRON, EMERALD, GOLD; @@ -72,16 +73,16 @@ class Item virtual int getIcon(const ItemStack*) const; virtual bool useOn(ItemStack*, Level*, const TilePos& pos, Facing::Name face) const; virtual bool useOn(ItemStack*, Player*, Level*, const TilePos& pos, Facing::Name face) const; - virtual float getDestroySpeed(ItemStack*, Tile*) const; + virtual float getDestroySpeed(ItemStack*, const Tile*) const; virtual ItemStack* use(ItemStack*, Level*, Player*) const; virtual int getMaxStackSize() const; virtual TileData getLevelDataForAuxValue(int x) const; virtual bool isStackedByData() const; virtual int getMaxDamage() const; virtual void hurtEnemy(ItemStack*, Mob*) const; - virtual void mineBlock(ItemStack*, const TilePos& pos, Facing::Name face) const; + virtual void mineBlock(ItemStack*, const TilePos& pos, Facing::Name face, Player* player) const; virtual int getAttackDamage(Entity*) const; - virtual bool canDestroySpecial(Tile*) const; + virtual bool canDestroySpecial(const Tile*) const; virtual void interactEnemy(ItemStack*, Mob*) const; virtual Item* handEquipped(); virtual bool isHandEquipped() const; diff --git a/source/world/item/ItemStack.cpp b/source/world/item/ItemStack.cpp index 0a50bd64f..e51915755 100644 --- a/source/world/item/ItemStack.cpp +++ b/source/world/item/ItemStack.cpp @@ -226,7 +226,7 @@ void ItemStack::set(int inCount) #endif } -bool ItemStack::canDestroySpecial(Tile* tile) +bool ItemStack::canDestroySpecial(const Tile* tile) { return getItem()->canDestroySpecial(tile); } @@ -244,7 +244,7 @@ std::string ItemStack::getHovertextName() const return getItem()->getHovertextName(); } -float ItemStack::getDestroySpeed(Tile* tile) +float ItemStack::getDestroySpeed(const Tile* tile) { return getItem()->getDestroySpeed(this, tile); } @@ -281,25 +281,28 @@ void ItemStack::hurt(int by) void ItemStack::hurtAndBreak(int amount, Entity* ent) { - if (isDamageableItem()) + if (!isDamageableItem()) { - m_auxValue += amount; - if (m_auxValue > getMaxDamage()) - { - if (ent->isPlayer()) - { - //((Player*)ent)->awardStat(Stats::statItemBreak[m_itemID]); - } + return; + } - --m_count; - if (m_count < 0) - m_count = 0; - m_auxValue = 0; + m_auxValue += amount; + if (m_auxValue > getMaxDamage()) + { + if (ent->isPlayer()) + { + //((Player*)ent)->awardStat(Stats::statItemBreak[m_itemID]); } + + --m_count; + if (m_count < 0) + m_count = 0; + + m_auxValue = 0; } } -void ItemStack::hurtEnemy(Mob* mob) +void ItemStack::hurtEnemy(Mob* mob, Player* player) { getItem()->hurtEnemy(this, mob); } @@ -335,9 +338,9 @@ bool ItemStack::isStackedByData() const return getItem()->isStackedByData(); } -void ItemStack::mineBlock(const TilePos& pos, Facing::Name face) +void ItemStack::mineBlock(const TilePos& pos, Facing::Name face, Player* player) { - return getItem()->mineBlock(this, pos, face); + return getItem()->mineBlock(this, pos, face, player); } void ItemStack::shrink(int count) diff --git a/source/world/item/ItemStack.hpp b/source/world/item/ItemStack.hpp index f34dc0c23..e2fe93bbc 100644 --- a/source/world/item/ItemStack.hpp +++ b/source/world/item/ItemStack.hpp @@ -64,22 +64,22 @@ class ItemStack bool hasSameUserData(const ItemStack& other) const; void set(int inCount); - bool canDestroySpecial(Tile*); + bool canDestroySpecial(const Tile*); std::string getDescriptionId(); std::string getHovertextName() const; - float getDestroySpeed(Tile*); + float getDestroySpeed(const Tile*); int getIcon() const; int getMaxDamage() const; int getMaxStackSize() const; void hurt(int by); void hurtAndBreak(int, Entity*); - void hurtEnemy(Mob*); + void hurtEnemy(Mob*, Player*); void interactEnemy(Mob*); bool isDamageableItem() const; bool isDamaged() const; bool isStackable() const; bool isStackedByData() const; - void mineBlock(const TilePos& pos, Facing::Name face); + void mineBlock(const TilePos& pos, Facing::Name face, Player* player); void shrink(int count = 1); ItemStack remove(int count); void setDescriptionId(const std::string&); diff --git a/source/world/item/PickaxeItem.cpp b/source/world/item/PickaxeItem.cpp new file mode 100644 index 000000000..8851e6e4c --- /dev/null +++ b/source/world/item/PickaxeItem.cpp @@ -0,0 +1,59 @@ +#include "PickaxeItem.hpp" +#include "world/tile/Tile.hpp" + +PickaxeItem::PickaxeItem(int id, Tier& tier) : ToolItem(id, 1, tier) +{ + static const Tile* pickaxeTiles[] = + { + Tile::stoneBrick, + Tile::stoneSlab, + Tile::stoneSlabHalf, + Tile::stairs_stone, + Tile::rock, + Tile::mossStone, + Tile::ironOre, + Tile::ironBlock, + Tile::coalOre, + Tile::goldBlock, + Tile::goldOre, + Tile::emeraldOre, + Tile::emeraldBlock, + Tile::ice, + Tile::netherrack, + Tile::lapisOre, + Tile::lapisBlock + }; + + static const int PICKAXE_TILE_COUNT = sizeof(pickaxeTiles) / sizeof(Tile*); + + initializeTiles(pickaxeTiles, PICKAXE_TILE_COUNT); +} + +bool PickaxeItem::canDestroySpecial(const Tile* tile) const +{ + // Try to keep the level checks in order. + int level = m_tier.m_level; + + if (tile == Tile::obsidian) + return level >= 3; + + if (tile == Tile::emeraldBlock || tile == Tile::emeraldOre) + return level >= 2; + + if (tile == Tile::goldBlock || tile == Tile::goldOre) + return level >= 2; + + if (tile == Tile::redStoneOre || tile == Tile::redStoneOre_lit) + return level >= 2; + + if (tile == Tile::ironBlock || tile == Tile::ironOre) + return level >= 1; + + if (tile == Tile::lapisBlock || tile == Tile::lapisOre) + return level >= 1; + + if (tile->m_pMaterial == Material::stone) + return true; + + return tile->m_pMaterial == Material::metal; +} \ No newline at end of file diff --git a/source/world/item/PickaxeItem.hpp b/source/world/item/PickaxeItem.hpp new file mode 100644 index 000000000..63efdde50 --- /dev/null +++ b/source/world/item/PickaxeItem.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "ToolItem.hpp" + +class PickaxeItem : public ToolItem +{ +public: + PickaxeItem(int id, Tier& tier); + bool canDestroySpecial(const Tile* tile) const override; +}; \ No newline at end of file diff --git a/source/world/item/ShovelItem.cpp b/source/world/item/ShovelItem.cpp new file mode 100644 index 000000000..c508a12f0 --- /dev/null +++ b/source/world/item/ShovelItem.cpp @@ -0,0 +1,27 @@ +#include "ShovelItem.hpp" +#include "world/tile/Tile.hpp" + +ShovelItem::ShovelItem(int id, Tier& tier) : ToolItem(id, 1, tier) +{ + static const Tile* shovelTiles[] = + { + Tile::grass, + Tile::dirt, + Tile::sand, + Tile::gravel, + Tile::topSnow, + Tile::snow, + Tile::clay + }; + static const int SHOVEL_TILE_COUNT = sizeof(shovelTiles) / sizeof(Tile*); + + initializeTiles(shovelTiles, SHOVEL_TILE_COUNT); +} + +bool ShovelItem::canDestroySpecial(const Tile* tile) const +{ + if (tile == Tile::topSnow) + return true; + + return tile == Tile::snow; +} diff --git a/source/world/item/ShovelItem.hpp b/source/world/item/ShovelItem.hpp new file mode 100644 index 000000000..95aad2e6c --- /dev/null +++ b/source/world/item/ShovelItem.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "ToolItem.hpp" + +class ShovelItem : public ToolItem +{ +public: + ShovelItem(int id, Tier& tier); + + bool canDestroySpecial(const Tile* tile) const override; +}; diff --git a/source/world/item/ToolItem.cpp b/source/world/item/ToolItem.cpp new file mode 100644 index 000000000..31fd85150 --- /dev/null +++ b/source/world/item/ToolItem.cpp @@ -0,0 +1,49 @@ +#include "ToolItem.hpp" +#include "world/entity/Mob.hpp" +#include "world/entity/Player.hpp" + +ToolItem::ToolItem(int id, int attackDamageBase, Tier& tier) : Item(id), m_speed(tier.m_speed), m_attackDamage(attackDamageBase + tier.m_damage), m_tier(tier) +{ + m_maxStackSize = 1; + m_maxDamage = tier.m_uses; +} + +void ToolItem::initializeTiles(const Tile** tiles, int tileCount) +{ + // Up for alternate solutions. The variables are initialized from an array during runtime to remove nondeterministic static array initialization time. + // Since the tiles do not have an opportunity to initialize with a statically allocated array and initializer lists are out of the question, this was my compromise. + // See PickaxeItem for use of impl + m_tiles.reserve(tileCount); + for (int i = 0; i < tileCount; ++i) + m_tiles.push_back(tiles[i]); +} + + +float ToolItem::getDestroySpeed(ItemStack* instance, const Tile* tile) const +{ + if (std::find(m_tiles.begin(), m_tiles.end(), tile) != m_tiles.end()) + { + return m_speed; + } + return 1.0f; +} + +void ToolItem::hurtEnemy(ItemStack* instance, Mob* mob) const +{ + instance->hurtAndBreak(2, mob); +} + +void ToolItem::mineBlock(ItemStack* instance, const TilePos& pos, Facing::Name face, Player* player) const +{ + instance->hurtAndBreak(1, player); +} + +int ToolItem::getAttackDamage(Entity* entity) const +{ + return m_attackDamage; +} + +bool ToolItem::isHandEquipped() const +{ + return true; +} \ No newline at end of file diff --git a/source/world/item/ToolItem.hpp b/source/world/item/ToolItem.hpp new file mode 100644 index 000000000..21b343fe7 --- /dev/null +++ b/source/world/item/ToolItem.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include "Item.hpp" + +class ToolItem : public Item +{ +public: + ToolItem(int id, int attackDamageBase, Tier& tier); + + float getDestroySpeed(ItemStack* instance, const Tile* tile) const; + void hurtEnemy(ItemStack* instance, Mob* mob) const override; + void mineBlock(ItemStack*, const TilePos& pos, Facing::Name face, Player* player) const override; + int getAttackDamage(Entity* entity) const override; + bool isHandEquipped() const override; + +protected: + void initializeTiles(const Tile** tiles, int tileCount); + +private: + float m_speed; + int m_attackDamage; + +protected: + Tier& m_tier; + + std::vector m_tiles; +}; diff --git a/source/world/level/Material.cpp b/source/world/level/Material.cpp index 4b57d7af4..c43583be0 100644 --- a/source/world/level/Material.cpp +++ b/source/world/level/Material.cpp @@ -55,8 +55,8 @@ void Material::initMaterials() air = new GasMaterial(); dirt = new Material(); wood = new Material(true); - stone = new Material(); - metal = new Material(); + stone = (new Material())->setNonMineable(); + metal = (new Material())->setNonMineable(); water = new LiquidMaterial(); lava = new LiquidMaterial(); leaves = new Material(true); @@ -70,14 +70,14 @@ void Material::initMaterials() explosive = new Material(true); coral = new Material(); ice = new Material(); - topSnow = new DecorationMaterial(); - snow = new Material(); + topSnow = (new DecorationMaterial())->setNonMineable(); + snow = (new Material())->setNonMineable(); cactus = new Material(); clay = new Material(); vegetable = new Material(); portal = new Material(); cake = new Material(); - web = new Material(); + web = (new Material())->setNonMineable(); } void Material::teardownMaterials() @@ -115,6 +115,11 @@ bool Material::isLiquid() const return false; } +bool Material::isMineable() const +{ + return m_bMineable; +} + bool Material::letsWaterThrough() const { if (isLiquid()) @@ -133,6 +138,12 @@ bool Material::isFlammable() const return m_bFlammable; } +Material* Material::setNonMineable() +{ + m_bMineable = false; + return this; +} + bool Material::blocksLight() const { return true; diff --git a/source/world/level/Material.hpp b/source/world/level/Material.hpp index 50960a2e6..c5adcf73b 100644 --- a/source/world/level/Material.hpp +++ b/source/world/level/Material.hpp @@ -21,11 +21,13 @@ class Material static void teardownMaterials(); virtual bool isLiquid() const; + virtual bool isMineable() const; virtual bool letsWaterThrough() const; virtual bool isSolid() const; virtual bool blocksLight() const; virtual bool blocksMotion() const; virtual bool isFlammable() const; + virtual Material* setNonMineable(); public: static Material @@ -58,6 +60,7 @@ class Material public: bool m_bFlammable; + bool m_bMineable; }; class GasMaterial : public Material diff --git a/source/world/tile/Tile.cpp b/source/world/tile/Tile.cpp index eafaad6ad..2e890e3ca 100644 --- a/source/world/tile/Tile.cpp +++ b/source/world/tile/Tile.cpp @@ -1043,13 +1043,16 @@ void Tile::handleEntityInside(Level* pLevel, const TilePos& pos, const Entity* p float Tile::getDestroyProgress(Player* player) const { + if (player->isCreative()) + return 1.0f; + if (m_hardness < 0.0f) return 0.0f; if (!player->canDestroy(this)) return 1.0f / m_hardness / 100.0f; - return player->getDestroySpeed() / m_hardness / 30.0f; + return player->getDestroySpeed(this) / m_hardness / 30.0f; } void Tile::spawnResources(Level* pLevel, const TilePos& pos, TileData data)