summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHowaner <franzi.moos@googlemail.com>2014-09-02 20:03:42 +0200
committerHowaner <franzi.moos@googlemail.com>2014-09-02 20:03:42 +0200
commitd3b8100a7b84a465ca52238d459bb10d32940c46 (patch)
tree2c272ec985b9228ef2e4892aeeabefa54786d215
parentRemoved SexyMotd link (diff)
parentRe-added alternate spellings of darkgraywool. (diff)
downloadcuberite-d3b8100a7b84a465ca52238d459bb10d32940c46.tar
cuberite-d3b8100a7b84a465ca52238d459bb10d32940c46.tar.gz
cuberite-d3b8100a7b84a465ca52238d459bb10d32940c46.tar.bz2
cuberite-d3b8100a7b84a465ca52238d459bb10d32940c46.tar.lz
cuberite-d3b8100a7b84a465ca52238d459bb10d32940c46.tar.xz
cuberite-d3b8100a7b84a465ca52238d459bb10d32940c46.tar.zst
cuberite-d3b8100a7b84a465ca52238d459bb10d32940c46.zip
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua2
m---------MCServer/Plugins/Core0
-rw-r--r--MCServer/crafting.txt140
-rw-r--r--MCServer/furnace.txt115
-rw-r--r--MCServer/items.ini141
-rw-r--r--src/Bindings/ManualBindings.cpp2
-rw-r--r--src/BlockEntities/CommandBlockEntity.cpp35
-rw-r--r--src/BlockEntities/FurnaceEntity.h2
-rw-r--r--src/Blocks/BlockHandler.cpp39
-rw-r--r--src/Blocks/BlockIce.h22
-rw-r--r--src/Blocks/BlockLeaves.h2
-rw-r--r--src/CMakeLists.txt7
-rw-r--r--src/ChunkDef.h16
-rw-r--r--src/ChunkMap.cpp66
-rw-r--r--src/CraftingRecipes.cpp2
-rw-r--r--src/Entities/ArrowEntity.cpp37
-rw-r--r--src/Entities/ArrowEntity.h5
-rw-r--r--src/Entities/Entity.cpp227
-rw-r--r--src/Entities/ItemFrame.cpp1
-rw-r--r--src/Entities/Minecart.cpp36
-rw-r--r--src/Entities/Pickup.cpp8
-rw-r--r--src/Entities/Player.cpp23
-rw-r--r--src/Entities/ProjectileEntity.cpp5
-rw-r--r--src/Entities/ProjectileEntity.h6
-rw-r--r--src/FurnaceRecipe.cpp243
-rw-r--r--src/FurnaceRecipe.h37
-rw-r--r--src/Items/ItemBow.h12
-rw-r--r--src/Items/ItemHandler.h2
-rw-r--r--src/Protocol/MojangAPI.cpp3
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp30
-rw-r--r--src/RankManager.cpp4
-rw-r--r--src/Root.cpp14
-rw-r--r--src/Server.cpp55
-rw-r--r--src/SetChunkData.cpp2
-rw-r--r--src/World.cpp9
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp1
-rw-r--r--src/WorldStorage/WSSAnvil.cpp4
-rw-r--r--tests/ChunkData/CopyBlocks.cpp4
38 files changed, 954 insertions, 405 deletions
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index 434dd8a90..337ce47c3 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -2848,7 +2848,7 @@ end
MirrorBlockFaceY = { Params = "{{Globals#BlockFaces|eBlockFace}}", Return = "{{Globals#BlockFaces|eBlockFace}}", Notes = "Returns the {{Globals#BlockFaces|eBlockFace}} that corresponds to the given {{Globals#BlockFaces|eBlockFace}} after mirroring it around the Y axis (or rotating 180 degrees around it)." },
NoCaseCompare = {Params = "string, string", Return = "number", Notes = "Case-insensitive string comparison; returns 0 if the strings are the same"},
NormalizeAngleDegrees = { Params = "AngleDegrees", Return = "AngleDegrees", Notes = "Returns the angle, wrapped into the [-180, +180) range." },
- ReplaceString = {Params = "full-string, to-be-replaced-string, to-replace-string", Notes = "Replaces *each* occurence of to-be-replaced-string in full-string with to-replace-string"},
+ ReplaceString = {Params = "full-string, to-be-replaced-string, to-replace-string", Return = "string", Notes = "Replaces *each* occurence of to-be-replaced-string in full-string with to-replace-string"},
RotateBlockFaceCCW = { Params = "{{Globals#BlockFaces|eBlockFace}}", Return = "{{Globals#BlockFaces|eBlockFace}}", Notes = "Returns the {{Globals#BlockFaces|eBlockFace}} that corresponds to the given {{Globals#BlockFaces|eBlockFace}} after rotating it around the Y axis 90 degrees counter-clockwise." },
RotateBlockFaceCW = { Params = "{{Globals#BlockFaces|eBlockFace}}", Return = "{{Globals#BlockFaces|eBlockFace}}", Notes = "Returns the {{Globals#BlockFaces|eBlockFace}} that corresponds to the given {{Globals#BlockFaces|eBlockFace}} after rotating it around the Y axis 90 degrees clockwise." },
StringSplit = {Params = "string, SeperatorsString", Return = "array table of strings", Notes = "Seperates string into multiple by splitting every time any of the characters in SeperatorsString is encountered."},
diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core
-Subproject 7cc99285ae5117418f657c3b295ca71f2b75b4f
+Subproject bd23915df763b182610c6163c5ff2d64a075656
diff --git a/MCServer/crafting.txt b/MCServer/crafting.txt
index 684294bb3..d29344b64 100644
--- a/MCServer/crafting.txt
+++ b/MCServer/crafting.txt
@@ -44,6 +44,8 @@ ApplePlanks, 4 = AppleLog, *
ConiferPlanks, 4 = ConiferLog, *
BirchPlanks, 4 = BirchLog, *
JunglePlanks, 4 = JungleLog, *
+AcaciaPlanks, 4 = AcaciaLog, *
+DarkOakPlanks, 4 = DarkOakLog, *
Stick, 4 = Planks, 2:2, 2:3
Torch, 4 = Stick, 1:2 | Coal, 1:1
Workbench = Planks, 1:1, 1:2, 2:1, 2:2
@@ -59,36 +61,77 @@ Furnace = Cobblestone, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3
#******************************************************#
# Blocks
#
-IronBlock = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
-GoldBlock = GoldIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
-DiamondBlock = Diamond, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
-LapisBlock = LapisLazuli, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
-EmeraldBlock = Emerald, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
-RedstoneBlock = RedstoneDust, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
-QuartzBlock = NetherQuartz, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
-NetherBrick = netherbrickitem, 1:1, 1:2, 2:1, 2:2
-Glowstone = GlowstoneDust, 1:1, 1:2, 2:1, 2:2
-Wool = String, 1:1, 1:2, 2:1, 2:2
-TNT = Gunpowder, 1:1, 3:1, 2:2, 1:3, 3:3 | Sand, 2:1, 1:2, 3:2, 2:3
-PillarQuartzBlock = QuartzSlab, 1:1, 1:2
-ChiseledQuartzBlock, 2 = QuartzBlock, 1:1, 1:2
-CoalBlock = Coal, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
-HayBale = Wheat, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
+IronBlock = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
+GoldBlock = GoldIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
+DiamondBlock = Diamond, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
+LapisBlock = LapisLazuli, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
+EmeraldBlock = Emerald, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
+RedstoneBlock = RedstoneDust, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
+QuartzBlock = NetherQuartz, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
+NetherBrick = netherbrickitem, 1:1, 1:2, 2:1, 2:2
+Glowstone = GlowstoneDust, 1:1, 1:2, 2:1, 2:2
+Wool = String, 1:1, 1:2, 2:1, 2:2
+TNT = Gunpowder, 1:1, 3:1, 2:2, 1:3, 3:3 | Sand, 2:1, 1:2, 3:2, 2:3
+PillarQuartzBlock = QuartzSlab, 1:1, 1:2
+ChiseledQuartzBlock, 2 = QuartzBlock, 1:1, 1:2
+CoalBlock = Coal, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
+HayBale = Wheat, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
+SnowBlock = SnowBall, 1:1, 1:2, 2:1, 2:2
+ClayBlock = Clay, 1:1, 1:2, 2:1, 2:2
+BrickBlock = Brick, 1:1, 1:2, 2:1, 2:2
+StoneBrick, 4 = Stone, 1:1, 1:2, 2:1, 2:2
+BookShelf = Planks, 1:1, 2:1, 3:1, 1:3, 2:3, 3:3 | Book, 1:2, 2:2, 3:2
+Sandstone, 4 = Sand, 1:1, 1:2, 2:1, 2:2
+SmoothSandstone, 4 = Sandstone, 1:1, 1:2, 2:1, 2:2
+OrnamentSandstone = SandstoneSlab, 1:1, 1:2
+JackOLantern = Pumpkin, 1:1 | Torch, 1:2
+PolishedGranite, 4 = Granite, 1:1, 1:2, 2:1, 2:2
+PolishedDiorite, 4 = Diorite, 1:1, 1:2, 2:1, 2:2
+PolishedAndesite, 4 = Andesite, 1:1, 1:2, 2:1, 2:2
+CoarsedDirt, 4 = Dirt, 1:1, 2:2 | Gravel, 1:2, 2:1
+CoarsedDirt, 4 = Gravel, 1:1, 2:2 | Dirt, 1:2, 2:1
+SlimeBlock = Slimeball, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
+Prismarine = PrismarineShard, 1:1, 1:2, 2:1, 2:2
+PrismarineBricks = PrismarineShard, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
+DarkPrismarine = PrismarineShard, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Inksac, 2:2
+SeaLantern = PrismarineShard, 1:1, 1:3, 3:1, 3:3 | PrismarineCrystals, 1:2, 2:1, 2:2, 2:3, 3:2
+RedSandstone, 4 = RedSand, 1:1, 1:2, 2:1, 2:2
+ChiseledRedSandstone, 4 = RedSandstoneSlab, 1:1, 1:2
+SmoothRedSandstone, 4 = RedSand, 1:1, 1:2, 2:1, 2:2
+MossyStoneBrick = Stonebrick, * | Vines, *
+Leather = RabbitHide, 1:1, 1:2, 2:1, 2:2
# Slabs:
StoneSlab, 6 = Stone, 1:1, 2:1, 3:1
SandstoneSlab, 6 = Sandstone, 1:1, 2:1, 3:1
-WoodSlab, 6 = Planks, 1:1, 2:1, 3:1
+SpruceWoodSlab, 6 = SprucePlanks, 1:1, 2:1, 3:1
+BirchWoodSlab, 6 = BirchPlanks, 1:1, 2:1, 3:1
+JungleWoodSlab, 6 = JunglePlanks, 1:1, 2:1, 3:1
+AcaciaWoodSlab, 6 = AcaciaPlanks, 1:1, 2:1, 3:1
+DarkOakWoodSlab, 6 = DarkOakPlanks, 1:1, 2:1, 3:1
+OakWoodSlab, 6 = OakPlanks, 1:1, 2:1, 3:1
CobblestoneSlab, 6 = Cobblestone, 1:1, 2:1, 3:1
BrickSlab, 6 = BrickBlock, 1:1, 2:1, 3:1
StonebrickSlab, 6 = StoneBrick, 1:1, 2:1, 3:1
NetherbrickSlab, 6 = NetherBrick, 1:1, 2:1, 3:1
Quartzslab, 6 = QuartzBlock, 1:1, 2:1, 3:1
snow, 6 = SnowBlock, 1:1, 2:1, 3:1
+RedSandstoneSlab, 6 = RedSandstone, 1:1, 2:1, 3:1
+
# Stairs:
-WoodStairs, 4 = Planks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
-WoodStairs, 4 = Planks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
+SpruceWoodStairs, 4 = SprucePlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
+SpruceWoodStairs, 4 = SprucePlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
+BirchWoodStairs, 4 = BirchPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
+BirchWoodStairs, 4 = BirchPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
+JungleWoodStairs, 4 = JunglePlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
+JungleWoodStairs, 4 = JunglePlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
+AcaciaWoodStairs, 4 = AcaciaPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
+AcaciaWoodStairs, 4 = AcaciaPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
+DarkOakWoodStairs, 4 = DarkOakPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
+DarkOakWoodStairs, 4 = DarkOakPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
+WoodStairs, 4 = OakPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
+WoodStairs, 4 = OakPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
cobblestoneStairs, 4 = Cobblestone, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
cobblestoneStairs, 4 = Cobblestone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
BrickStairs, 4 = BrickBlock, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
@@ -101,15 +144,8 @@ quartzstairs, 4 = QuartzBlock, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
quartzstairs, 4 = QuartzBlock, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
StoneBrickStairs, 4 = StoneBrick, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
StoneBrickStairs, 4 = StoneBrick, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
-SnowBlock = SnowBall, 1:1, 1:2, 2:1, 2:2
-ClayBlock = Clay, 1:1, 1:2, 2:1, 2:2
-BrickBlock = Brick, 1:1, 1:2, 2:1, 2:2
-StoneBrick, 4 = Stone, 1:1, 1:2, 2:1, 2:2
-BookShelf = Planks, 1:1, 2:1, 3:1, 1:3, 2:3, 3:3 | Book, 1:2, 2:2, 3:2
-Sandstone, 4 = Sand, 1:1, 1:2, 2:1, 2:2
-SmoothSandstone, 4 = Sandstone, 1:1, 1:2, 2:1, 2:2
-OrnamentSandstone = SandstoneSlab, 1:1, 1:2
-JackOLantern = Pumpkin, 1:1 | Torch, 1:2
+RedSandstoneStairs, 4 = RedSandstone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
+RedSandstoneStairs, 4 = RedSandstone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
# Other
Carpet = Wool, 1:3, 2:3
@@ -244,11 +280,19 @@ ActivatorRail, 6 = IronIngot, 1:1, 1:2, 1:3, 3:1, 3:2, 3:3 | Stick, 2:1, 2:3 | R
#******************************************************#
# Mechanisms
#
-WoodenDoor = Planks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
-IronDoor = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
+WoodenDoor, 3 = OakPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
+SpruceDoor, 3 = SprucePlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
+BirchDoor, 3 = BirchPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
+JungleDoor, 3 = JunglePlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
+AcaciaDoor, 3 = AcaciaPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
+DarkOakDoor, 3 = DarkOakPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
+IronDoor, 3 = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
TrapDoor, 2 = Planks, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2
+IronTrapDoor = IronIngot, 1:1, 1:2, 2:1, 2:2
WoodPlate = Planks, 1:1, 2:1
StonePlate = Stone, 1:1, 2:1
+lightweightedpressureplate = IronIngot, 1:1, 2:1
+heavyweightedpressureplate = GoldIngot, 1:1, 2:1
StoneButton = Stone, 1:1
WoodenButton = Planks, 1:1
RedstoneTorchOn = Stick, 1:2 | RedstoneDust, 1:1
@@ -286,6 +330,8 @@ MelonSeeds = MelonSlice, *
PumpkinSeeds, 4 = Pumpkin, *
PumpkinPie = Pumpkin, * | Sugar, * | egg, *
Wheat, 9 = Haybale, *
+RabbitStew = Cooked Rabbit, 2:1 | Carrot, 1:2 | BakedPotato, 2:2 | BrownMushroom, 3:2 | Bowl, 2:3
+RabbitStew = Cooked Rabbit, 2:1 | Carrot, 1:2 | BakedPotato, 2:2 | RedMushroom, 3:2 | Bowl, 2:3
@@ -314,17 +360,49 @@ IronBars, 16 = IronIngot, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2
Paper, 3 = Sugarcane, 1:1, 2:1, 3:1
Book = Paper, *, *, * | leather, *
Bookandquill = Book, * | feather, * | inksac, *
-Fence, 2 = Stick, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2
+Fence, 3 = OakPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2
+SpruceFence, 3 = SprucePlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2
+BirchFence, 3 = BirchPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2
+JungleFence, 3 = JunglePlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2
+DarkOakFence, 3 = DarkOakPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2
+AcaciaFence, 3 = AcaciaPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2
Cobblestonewall, 6 = cobblestone, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
mossycobblestonewall, 6 = mossycobblestone, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
NetherBrickFence, 6 = NetherBrick, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2
-FenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | Planks, 2:1, 2:2
+FenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | OakPlanks, 2:1, 2:2
+SpruceFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | SprucePlanks, 2:1, 2:2
+BirchFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | BirchPlanks, 2:1, 2:2
+JungleFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | JunglePlanks, 2:1, 2:2
+DarkOakFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | DarkOakPlanks, 2:1, 2:2
+AcaciaFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | AcaciaPlanks, 2:1, 2:2
Bed = Planks, 1:2, 2:2, 3:2 | Wool, 1:1, 2:1, 3:1
GoldIngot = GoldNugget, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
EyeOfEnder = EnderPearl, * | BlazePowder, *
Beacon = Glass, 1:1, 1:2, 2:1, 3:1, 3:2 | Obsidian, 1:3, 2:3, 3:3 | NetherStar, 2:2
Anvil = IronBlock, 1:1, 2:1, 3:1 | IronIngot, 2:2, 1:3, 2:3, 3:3
FlowerPot = Brick, 1:2, 2:3, 3:2
+ArmorStand = Stick, 1:1, 1:3, 2:1, 2:2, 3:1, 3:3 | StoneSlab, 2:3
+
+# These are just the basic ones, you can add various shapes and stuff to each of them
+# ToDo: Add the various shapes (saved in NBT-Tags, not in meta)
+# Banners:
+
+WhiteBanner = Stick, 2:3 | WhiteWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+OrangeBanner = Stick, 2:3 | OrangeWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+MagentaBanner = Stick, 2:3 | MagentaWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+LightBlueBanner = Stick, 2:3 | LightBlueWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+YellowBanner = Stick, 2:3 | YellowWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+LimeBanner = Stick, 2:3 | LimeWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+PinkBanner = Stick, 2:3 | PinkWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+GrayBanner = Stick, 2:3 | GrayWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+LightGrayBanner = Stick, 2:3 | LightGrayWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+CyanBanner = Stick, 2:3 | CyanWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+PurpleBanner = Stick, 2:3 | PurpleWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+BlueBanner = Stick, 2:3 | BlueWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+BrownBanner = Stick, 2:3 | BrownWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+GreenBanner = Stick, 2:3 | GreenWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+RedBanner = Stick, 2:3 | RedWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
+BlackBanner = Stick, 2:3 | BlackWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
diff --git a/MCServer/furnace.txt b/MCServer/furnace.txt
index d6177184b..0c12a798d 100644
--- a/MCServer/furnace.txt
+++ b/MCServer/furnace.txt
@@ -10,25 +10,28 @@
# An Item is defined by an Item Type, an amount (and damage)
# The damage is optional, and if not specified it's assumed to be 0
#
-# -Cactus Green:
-# 351 : 1 ( : 2 )
-# ItemType : Amount ( : Damage )
+# Cactus Green example:
+# 351 : 2 ( , 1 )
+# ItemType : Damage ( , Amount )
+# or simple use the item name (marked in items.ini):
+# CactusGreen ( , 1 )
#
#
# **** Recipe and result ****
#
-# 4:1@200=1:1 -> Produces 1 smooth stone from 1 cobblestone in 200 ticks (10 seconds)
+# Cobble @ 200 = Stone -> Produces 1 smooth stone from 1 cobblestone in 200 ticks (10 seconds)
#
-# 4 : 1 @ 200 = 1 : 1
-# ItemType : Amount @ ticks = ItemID : Amount
+# Write in full:
+# Cobble : 0 , 1 @ 200 = 1 : 1 , 1
+# ItemType : Damage , Amount @ ticks = ItemType : Damage , Amount
#
#
# **** Fuel ****
#
# !17:1 = 300 -> 1 Wood burns for 300 ticks (15 s)
#
-# ! 17 : 1 = 300
-# Fuel ItemType : Amount = ticks
+# ! Wood , 1 = 300
+# Fuel ItemType , Amount = ticks
#
#******************************************************#
@@ -39,20 +42,24 @@
#--------------------------
# Smelting recipes
-4:1 @ 200 = 1:1 # 1 Cobblestone -> 1 Rock
-15:1 @ 200 = 265:1 # 1 Iron Ore -> 1 Iron Ingot
-14:1 @ 200 = 266:1 # 1 Gold Ore -> 1 Gold Ingot
-153:1 @ 200 = 406:1 # 1 Quartz Ore -> 1 Quartz
-12:1 @ 200 = 20:1 # 1 Sand -> 1 Glass
-319:1 @ 200 = 320:1 # 1 Raw Pork -> 1 Cooked Pork
-363:1 @ 200 = 364:1 # 1 Raw Beef -> 1 Cooked Beef (steak)
-365:1 @ 200 = 366:1 # 1 Raw Chicken -> 1 Cooked Chicken
-337:1 @ 200 = 336:1 # 1 Clay -> 1 Clay Brick
-82:1 @ 200 = 172:1 # 1 Clay Block -> 1 Hardened Clay
-87:1 @ 200 = 405:1 # 1 NetherRack -> 1 NetherBrick
-349:1 @ 200 = 350:1 # 1 Raw Fish -> 1 Cooked Fish
-17:1 @ 200 = 263:1:1 # 1 Log -> 1 Charcoal
-81:1 @ 200 = 351:1:2 # 1 Cactus -> 1 Green Dye
+Cobble = Stone
+IronOre = IronIngot
+GoldOre = GoldIngot
+NetherQuartzOre = NetherQuartz
+Sand = Glass
+Pork = CookedPork
+RawBeef = Steak
+RawChicken = CookedChicken
+Clay = Brick
+ClayBlock = HardenedClay
+TallGrass = NetherBrickItem
+RawFish = CookedFish
+Log = CharCoal
+Cactus = GreenDye
+WetSponge = Sponge
+Stonebrick = CrackedStonebrick
+RawRabbit = CookedRabbit
+RawMutton = CookedMutton
@@ -61,31 +68,41 @@
#--------------------------
# Fuels
-! 263:1 = 1600 # 1 Coal -> 80 sec
-! 263:1:1 = 1600 # 1 Charcoal -> 80 sec
-! 126:1 = 15 # 1 Halfslab -> 7.5 sec
-! 5:1 = 300 # 1 Planks -> 15 sec
-! 280:1 = 100 # 1 Stick -> 5 sec
-! 85:1 = 300 # 1 Fence -> 15 sec
-! 53:1 = 300 # 1 Wooden Stairs -> 15 sec
-! 58:1 = 300 # 1 Crafting Table -> 15 sec
-! 47:1 = 300 # 1 Bookshelf -> 15 sec
-! 54:1 = 300 # 1 Chest -> 15 sec
-! 84:1 = 300 # 1 Jukebox -> 15 sec
-! 327:1 = 20000 # 1 Lava Bucket -> 1000 sec
-! 17:1 = 300 # 1 Wood -> 15 sec
-! 6:1 = 100 # 1 Sapling -> 5 sec
-! 173:1 = 16000 # 1 Coal Block -> 800 sec
-! 369:1 = 2400 # 1 Blaze Rod -> 120 sec
-! 25:1 = 300 # 1 Note Block -> 15 sec
-! 151:1 = 300 # 1 Daylight Sensor -> 15 sec
-! 107:1 = 300 # 1 Fence Gate -> 15 sec
-! 167:1 = 300 # 1 Trapdoor -> 15 sec
-! 146:1 = 300 # 1 Trapped Chest -> 15 sec
-! 72:1 = 300 # 1 Pressure Plate -> 15 sec
-! 270:1 = 200 # 1 Wooden Pickaxe -> 10 sec
-! 271:1 = 200 # 1 Wooden Axe -> 10 sec
-! 269:1 = 200 # 1 Wooden Shovel -> 10 sec
-! 290:1 = 200 # 1 Wooden Hoe -> 10 sec
-! 268:1 = 200 # 1 Wooden Sword -> 10 sec
+! CharCoal = 1600 # -> 80 sec
+! Coal = 1600 # -> 80 sec
+! WoodenSlab = 15 # -> 7.5 sec
+! Planks = 300 # -> 15 sec
+! Stick = 100 # -> 5 sec
+! Fence = 300 # -> 15 sec
+! SpruceFence = 300 # -> 15 sec
+! BirchFence = 300 # -> 15 sec
+! JungleFence = 300 # -> 15 sec
+! DarkOakFence = 300 # -> 15 sec
+! AcaciaFence = 300 # -> 15 sec
+! WoodStairs = 300 # -> 15 sec
+! Workbench = 300 # -> 15 sec
+! Bookshelf = 300 # -> 15 sec
+! Chest = 300 # -> 15 sec
+! Jukebox = 300 # -> 15 sec
+! Lavabucket = 20000 # -> 1000 sec
+! Log = 300 # -> 15 sec
+! Sapling = 100 # -> 5 sec
+! CoalBlock = 16000 # -> 800 sec
+! BlazeRod = 2400 # -> 120 sec
+! NoteBlock = 300 # -> 15 sec
+! DaylightSensor = 300 # -> 15 sec
+! FenceGate = 300 # -> 15 sec
+! SpruceFenceGate = 300 # -> 15 sec
+! BirchFenceGate = 300 # -> 15 sec
+! JungleFenceGate = 300 # -> 15 sec
+! DarkOakFenceGate = 300 # -> 15 sec
+! Trapdoor = 300 # -> 15 sec
+! TrappedChest = 300 # -> 15 sec
+! WoodPlate = 300 # -> 15 sec
+! WoodPickaxe = 200 # -> 10 sec
+! WoodAxe = 200 # -> 10 sec
+! WoodShovel = 200 # -> 10 sec
+! WoodHoe = 200 # -> 10 sec
+! WoodSword = 200 # -> 10 sec
+
diff --git a/MCServer/items.ini b/MCServer/items.ini
index 7b8fcf4ee..6eb9eee6e 100644
--- a/MCServer/items.ini
+++ b/MCServer/items.ini
@@ -1,9 +1,17 @@
[Items]
air=0
rock=1
+granite=1:1
+polishedgranite=1:2
+diorite=1:3
+polisheddiorite=1:4
+andesite=1:5
+polishedandesite=1:6
stone=1
grass=2
dirt=3
+coarseddirt=3:1
+podzol=3:2
cobblestone=4
cobble=4
planks=5
@@ -17,6 +25,11 @@ birchplanks=5:2
lightplanks=5:2
jungleplanks=5:3
redplanks=5:3
+acaciaplanks=5:4
+darkoakplanks=5:5
+bigoakplanks=5:5
+roofedoakplanks=5:5
+
; Obsolete: do not use "wood", as its meaning is not clear - wiki uses log as wood, we use planks as wood.
wood=5
@@ -40,6 +53,7 @@ stilllava=11
slava=11
stationarylava=11
sand=12
+redsand=12:1
gravel=13
goldore=14
ironore=15
@@ -64,6 +78,7 @@ spruceleaves=18:1
birchleaves=18:2
jungleleaves=18:3
sponge=19
+wetsponge=19:1
glass=20
lapisore=21
lapisblock=22
@@ -169,6 +184,7 @@ torch=50
fire=51
mobspawner=52
woodstairs=53
+oakwoodstairs=53
chest=54
redstonedust=55
redstonewire=55
@@ -274,13 +290,47 @@ redstonelamp=123
redstonelampoff=123
redstonelampon=124
woodendoubleslab=125
+appledoublewoodslab=125:0
+oakwooddoubleslab=125:0
+coniferwooddoubleslab=125:1
+pinewooddoubleslab=125:1
+sprucewooddoubleslab=125:1
+darkwooddoubleslab=125:1
+birchwooddoubleslab=125:2
+whitewooddoubleslab=125:2
+junglewooddoubleslab=125:3
+acaciawooddoubleslab=125:4
+bigoakwooddoubleslab=125:5
+darkoakwooddoubleslab=125:5
+roofedwooddoubleslab=125:5
woodenslab=126
+applewoodslab=126:0
+oakwoodslab=126:0
+coniferwoodslab=126:1
+pinewoodslab=126:1
+sprucewoodslab=126:1
+darkwoodslab=126:1
+birchwoodslab=126:2
+whitewoodslab=126:2
+junglewoodslab=126:3
+acaciawoodslab=126:4
+bigoakwoodslab=126:5
+darkoakwoodslab=126:5
+roofedwoodslab=126:5
+cocoabeans=127
sandstonestairs=128
emeraldore=129
enderchest=130
tripwirehook=131
tripwire=132
emeraldblock=133
+coniferwoodstairs=134
+pinewoodstairs=134
+sprucewoodstairs=134
+darkwoodstairs=134
+birchwoodstairs=135
+whitewoodstairs=135
+junglewoodstairs=136
commandblock=137
beacon=138
cobblestonewall=139
@@ -343,12 +393,54 @@ brownstainedglasspane=160:12
greenstainedglasspane=160:13
redstainedglasspane=160:14
blackstainedglasspane=160:15
-acaciawood=162
-darkoakwood=162:1
-acaciawoodenstairs=163
-darkoakwoodenstairs=164
+acacialeaves=161
+bigoakleaves=161:1
+darkoakleaves=161:1
+roofedoakleaves=161:1
+acacialog=162
+bigoaklog=162:1
+darkoaklog=162:1
+roofedoaklog=162:1
+acaciawoodstairs=163
+bigoakwoodstiars=164
+darkoakwoodstairs=164
+roofedoakwoodstairs=164
+slimeblock=165
+irontrapdoor=167
+prismarine=168
+prismarinebricks=168:1
+darkprismarine=168:2
+sealantern=169
haybale=170
carpet=171
+hardenedclay=172
+redsandstone=179
+chiseledredsandstone=179:1
+smoothredsandstone=179:2
+redsandstonestairs=180
+redsandstoneslab=182
+coniferfencegate=183
+pinefencegate=183
+sprucefencegate=183
+darkfencegate=183
+birchfencegate=184
+whitefencegate=184
+junglefencegate=185
+darkoakfencegate=186
+bigoakfencegate=186
+roofedoakfencegate=186
+acaciafencegate=187
+coniferfence=188
+pinefence=188
+sprucefence=188
+darkfence=188
+birchfence=189
+whitefence=189
+junglefence=190
+darkoakfence=191
+bigoakfence=191
+roofedoakfence=191
+acaciafence=192
ironshovel=256
ironspade=256
ironpickaxe=257
@@ -596,13 +688,52 @@ netherbrickitem=405
netherquartz=406
tntminecart=407
hopperminecart=408
+prismarineshard=409
+prismarinecrystals=410
+rawrabbit=411
+cookedrabbit=412
+rabbitstew=413
+rabbitsoup=413
+rabbitsfood=414
+rabbithide=415
+armorstand=416
ironhorsearmor=417
goldhorsearmor=418
diamondhorsearmor=419
lead=420
nametag=421
commandblockminecart=422
-
+rawmutton=423
+cookedmutton=424
+banner=425
+blackbanner=415:0
+redbanner=415:1
+greenbanner=415:2
+brownbanner=415:3
+bluebanner=415:4
+purplebanner=415:5
+cyanbanner=415:6
+silverbanner=415:7
+lightgraybanner=415:7
+graybanner=415:8
+pinkbanner=415:9
+limebanner=415:10
+yellowbanner=415:11
+lightbluebanner=415:12
+magentabanner=415:13
+orangebanner=415:14
+whitebanner=415:15
+coniferdoor=427
+pinedoor=427
+sprucedoor=427
+darkdoor=427
+birchdoor=428
+whitedoor=428
+jungledoor=429
+acaciadoor=430
+bigoakdoor=431
+darkoakdoor=431
+roofedoakdoor=431
goldrecord=2256
greenrecord=2257
blocksrecord=2258
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index b2c57e52d..adf10a72f 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -2663,7 +2663,7 @@ static int tolua_cRoot_GetFurnaceRecipe(lua_State * tolua_S)
// Get the recipe for the input
cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe();
- const cFurnaceRecipe::Recipe * Recipe = FR->GetRecipeFrom(*Input);
+ const cFurnaceRecipe::cRecipe * Recipe = FR->GetRecipeFrom(*Input);
if (Recipe == NULL)
{
// There is no such furnace recipe for this input, return no value
diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp
index 45f8a3e4d..20702a9ac 100644
--- a/src/BlockEntities/CommandBlockEntity.cpp
+++ b/src/BlockEntities/CommandBlockEntity.cpp
@@ -13,6 +13,7 @@
#include "../Root.h"
#include "../Server.h" // ExecuteConsoleCommand()
#include "../Chunk.h"
+#include "../ChatColor.h"
@@ -187,12 +188,11 @@ void cCommandBlockEntity::SaveToJson(Json::Value & a_Value)
void cCommandBlockEntity::Execute()
{
- if (m_World != NULL)
+ ASSERT(m_World != NULL); // Execute should not be called before the command block is attached to a world
+
+ if (!m_World->AreCommandBlocksEnabled())
{
- if (!m_World->AreCommandBlocksEnabled())
- {
- return;
- }
+ return;
}
class CommandBlockOutCb :
@@ -206,15 +206,28 @@ void cCommandBlockEntity::Execute()
virtual void Out(const AString & a_Text)
{
// Overwrite field
- m_CmdBlock->SetLastOutput(a_Text);
+ m_CmdBlock->SetLastOutput(cClientHandle::FormatChatPrefix(m_CmdBlock->GetWorld()->ShouldUseChatPrefixes(), "SUCCESS", cChatColor::Green, cChatColor::White) + a_Text);
}
} CmdBlockOutCb(this);
- LOGD("cCommandBlockEntity: Executing command %s", m_Command.c_str());
-
- cServer * Server = cRoot::Get()->GetServer();
-
- Server->ExecuteConsoleCommand(m_Command, CmdBlockOutCb);
+ // Administrator commands are not executable by command blocks:
+ if (
+ (m_Command != "stop") &&
+ (m_Command != "restart") &&
+ (m_Command != "kick") &&
+ (m_Command != "ban") &&
+ (m_Command != "ipban")
+ )
+ {
+ cServer * Server = cRoot::Get()->GetServer();
+ LOGD("cCommandBlockEntity: Executing command %s", m_Command.c_str());
+ Server->ExecuteConsoleCommand(m_Command, CmdBlockOutCb);
+ }
+ else
+ {
+ SetLastOutput(cClientHandle::FormatChatPrefix(GetWorld()->ShouldUseChatPrefixes(), "FAILURE", cChatColor::Rose, cChatColor::White) + "Adminstration commands can not be executed");
+ LOGD("cCommandBlockEntity: Prevented execution of administration command %s", m_Command.c_str());
+ }
// TODO 2014-01-18 xdot: Update the signal strength.
m_Result = 0;
diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h
index 4f935a74b..cf1a755e0 100644
--- a/src/BlockEntities/FurnaceEntity.h
+++ b/src/BlockEntities/FurnaceEntity.h
@@ -105,7 +105,7 @@ protected:
NIBBLETYPE m_BlockMeta;
/// The recipe for the current input slot
- const cFurnaceRecipe::Recipe * m_CurrentRecipe;
+ const cFurnaceRecipe::cRecipe * m_CurrentRecipe;
/// The item that is being smelted
cItem m_LastInput;
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
index 028277e4c..6767d4de4 100644
--- a/src/Blocks/BlockHandler.cpp
+++ b/src/Blocks/BlockHandler.cpp
@@ -431,10 +431,45 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac
else
{
// TODO: Add a proper overridable function for this
- Pickups.Add(m_BlockType, 1, Meta);
+ if (a_Digger != NULL)
+ {
+ cEnchantments Enchantments = a_Digger->GetEquippedWeapon().m_Enchantments;
+ if ((Enchantments.GetLevel(cEnchantments::enchSilkTouch) > 0) && a_Digger->IsPlayer())
+ {
+ switch (m_BlockType)
+ {
+ case E_BLOCK_CAKE:
+ case E_BLOCK_CARROTS:
+ case E_BLOCK_COCOA_POD:
+ case E_BLOCK_DOUBLE_STONE_SLAB:
+ case E_BLOCK_DOUBLE_WOODEN_SLAB:
+ case E_BLOCK_FIRE:
+ case E_BLOCK_FARMLAND:
+ case E_BLOCK_MELON_STEM:
+ case E_BLOCK_MOB_SPAWNER:
+ case E_BLOCK_NETHER_WART:
+ case E_BLOCK_POTATOES:
+ case E_BLOCK_PUMPKIN_STEM:
+ case E_BLOCK_SNOW:
+ case E_BLOCK_SUGARCANE:
+ case E_BLOCK_TALL_GRASS:
+ case E_BLOCK_CROPS:
+ {
+ // Silktouch can't be used for this blocks
+ ConvertToPickups(Pickups, Meta);
+ break;
+ };
+ default: Pickups.Add(m_BlockType, 1, Meta);
+ }
+ }
+ else
+ {
+ Pickups.Add(m_BlockType, 1, Meta);
+ }
+ }
}
}
-
+
// Allow plugins to modify the pickups:
a_BlockPluginInterface.CallHookBlockToPickups(a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups);
diff --git a/src/Blocks/BlockIce.h b/src/Blocks/BlockIce.h
index c38630fe3..47a84e5a7 100644
--- a/src/Blocks/BlockIce.h
+++ b/src/Blocks/BlockIce.h
@@ -30,18 +30,18 @@ public:
{
return;
}
-
- BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
- if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow))
+
+ cEnchantments Enchantments = a_Player->GetInventory().GetEquippedItem().m_Enchantments;
+ if (Enchantments.GetLevel(cEnchantments::enchSilkTouch) == 0)
{
- return;
+ BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
+ if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow))
+ {
+ return;
+ }
+
+ a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
+ // This is called later than the real destroying of this ice block
}
-
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
- // This is called later than the real destroying of this ice block
}
} ;
-
-
-
-
diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h
index 972dd6232..a8aa28a0f 100644
--- a/src/Blocks/BlockLeaves.h
+++ b/src/Blocks/BlockLeaves.h
@@ -152,7 +152,7 @@ bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ)
a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE);
for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++)
{
- for (int y = a_BlockY - i; y <= a_BlockY + i; y++)
+ for (int y = std::max(a_BlockY - i, 0); y <= std::min(a_BlockY + i, 255); y++)
{
for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++)
{
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 37657ba91..2d96662a9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -256,6 +256,11 @@ set(EXECUTABLE MCServer)
if (MSVC)
get_directory_property(BINDING_OUTPUTS DIRECTORY "Bindings" DEFINITION BINDING_OUTPUTS)
get_directory_property(BINDING_DEPENDENCIES DIRECTORY "Bindings" DEFINITION BINDING_DEPENDENCIES)
+
+ # The paths in BINDING_DEPENDENCIES are relative to the Bindings folder, convert them relative to this folder:
+ foreach (dep ${BINDING_DEPENDENCIES})
+ list (APPEND BINDINGS_DEPENDENCIES "Bindings/${dep}")
+ endforeach(dep)
ADD_CUSTOM_COMMAND(
OUTPUT ${BINDING_OUTPUTS}
@@ -268,7 +273,7 @@ if (MSVC)
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/
# add any new generation dependencies here
- DEPENDS ${BINDING_DEPENDENCIES}
+ DEPENDS ${BINDINGS_DEPENDENCIES}
)
endif()
diff --git a/src/ChunkDef.h b/src/ChunkDef.h
index 51075ab4a..b8b4291c7 100644
--- a/src/ChunkDef.h
+++ b/src/ChunkDef.h
@@ -197,32 +197,32 @@ public:
inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z)
{
- ASSERT((a_X >= 0) && (a_X <= Width));
- ASSERT((a_Z >= 0) && (a_Z <= Width));
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
return a_HeightMap[a_X + Width * a_Z];
}
inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height)
{
- ASSERT((a_X >= 0) && (a_X <= Width));
- ASSERT((a_Z >= 0) && (a_Z <= Width));
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
a_HeightMap[a_X + Width * a_Z] = a_Height;
}
inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z)
{
- ASSERT((a_X >= 0) && (a_X <= Width));
- ASSERT((a_Z >= 0) && (a_Z <= Width));
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
return a_BiomeMap[a_X + Width * a_Z];
}
inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome)
{
- ASSERT((a_X >= 0) && (a_X <= Width));
- ASSERT((a_Z >= 0) && (a_Z <= Width));
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
a_BiomeMap[a_X + Width * a_Z] = a_Biome;
}
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index dd8be0631..a3692ef11 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -1880,21 +1880,19 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
}
else if ((m_World->GetTNTShrapnelLevel() > slNone) && (m_World->GetTickRandomNumber(100) < 20)) // 20% chance of flinging stuff around
{
- if (!cBlockInfo::FullyOccupiesVoxel(Block))
+ // If the block is shrapnel-able, make a falling block entity out of it:
+ if (
+ ((m_World->GetTNTShrapnelLevel() == slAll) && cBlockInfo::FullyOccupiesVoxel(Block)) ||
+ ((m_World->GetTNTShrapnelLevel() == slGravityAffectedOnly) && ((Block == E_BLOCK_SAND) || (Block == E_BLOCK_GRAVEL)))
+ )
{
- break;
+ m_World->SpawnFallingBlock(bx + x, by + y + 5, bz + z, Block, area.GetBlockMeta(bx + x, by + y, bz + z));
}
- else if ((m_World->GetTNTShrapnelLevel() == slGravityAffectedOnly) && ((Block != E_BLOCK_SAND) && (Block != E_BLOCK_GRAVEL)))
- {
- break;
- }
- m_World->SpawnFallingBlock(bx + x, by + y + 5, bz + z, Block, area.GetBlockMeta(bx + x, by + y, bz + z));
}
area.SetBlockTypeMeta(bx + x, by + y, bz + z, E_BLOCK_AIR, 0);
a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
break;
-
}
} // switch (BlockType)
} // for z
@@ -1916,51 +1914,31 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
virtual bool Item(cEntity * a_Entity) override
{
- if (a_Entity->IsPickup())
- {
- if (((cPickup *)a_Entity)->GetAge() < 20) // If pickup age is smaller than one second, it is invincible (so we don't kill pickups that were just spawned)
- {
- return false;
- }
- }
-
- Vector3d EntityPos = a_Entity->GetPosition();
- cBoundingBox bbEntity(EntityPos, a_Entity->GetWidth() / 2, a_Entity->GetHeight());
-
- if (!m_bbTNT.IsInside(bbEntity)) // IsInside actually acts like DoesSurround
+ if (a_Entity->IsPickup() && (a_Entity->GetTicksAlive() < 20))
{
+ // If pickup age is smaller than one second, it is invincible (so we don't kill pickups that were just spawned)
return false;
}
-
- Vector3d AbsoluteEntityPos(abs(EntityPos.x), abs(EntityPos.y), abs(EntityPos.z));
-
- // Work out how far we are from the edge of the TNT's explosive effect
- AbsoluteEntityPos -= m_ExplosionPos;
-
- // All to positive
- AbsoluteEntityPos.x = abs(AbsoluteEntityPos.x);
- AbsoluteEntityPos.y = abs(AbsoluteEntityPos.y);
- AbsoluteEntityPos.z = abs(AbsoluteEntityPos.z);
-
- double FinalDamage = (((1 / AbsoluteEntityPos.x) + (1 / AbsoluteEntityPos.y) + (1 / AbsoluteEntityPos.z)) * 2) * m_ExplosionSize;
-
- // Clip damage values
- FinalDamage = Clamp(FinalDamage, 0.0, (double)a_Entity->GetMaxHealth());
+ Vector3d DistanceFromExplosion = a_Entity->GetPosition() - m_ExplosionPos;
+
if (!a_Entity->IsTNT() && !a_Entity->IsFallingBlock()) // Don't apply damage to other TNT entities and falling blocks, they should be invincible
{
- a_Entity->TakeDamage(dtExplosion, NULL, (int)FinalDamage, 0);
- }
+ cBoundingBox bbEntity(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight());
- // Apply force to entities around the explosion - code modified from World.cpp DoExplosionAt()
- Vector3d distance_explosion = a_Entity->GetPosition() - m_ExplosionPos;
- if (distance_explosion.SqrLength() < 4096.0)
- {
- distance_explosion.Normalize();
- distance_explosion *= m_ExplosionSize * m_ExplosionSize;
+ if (!m_bbTNT.IsInside(bbEntity)) // If bbEntity is inside bbTNT, not vice versa!
+ {
+ return false;
+ }
- a_Entity->AddSpeed(distance_explosion);
+ // Ensure that the damage dealt is inversely proportional to the distance to the TNT centre - the closer a player is, the harder they are hit
+ a_Entity->TakeDamage(dtExplosion, NULL, (int)((1 / DistanceFromExplosion.Length()) * 6 * m_ExplosionSize), 0);
}
+
+ // Apply force to entities around the explosion - code modified from World.cpp DoExplosionAt()
+ DistanceFromExplosion.Normalize();
+ DistanceFromExplosion *= m_ExplosionSize * m_ExplosionSize;
+ a_Entity->AddSpeed(DistanceFromExplosion);
return false;
}
diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp
index 223184c64..ed3409207 100644
--- a/src/CraftingRecipes.cpp
+++ b/src/CraftingRecipes.cpp
@@ -367,7 +367,7 @@ void cCraftingRecipes::ClearRecipes(void)
void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine)
{
AString RecipeLine(a_RecipeLine);
- RecipeLine.erase(std::remove(RecipeLine.begin(), RecipeLine.end(), ' '), RecipeLine.end());
+ RecipeLine.erase(std::remove_if(RecipeLine.begin(), RecipeLine.end(), isspace), RecipeLine.end());
AStringVector Sides = StringSplit(RecipeLine, "=");
if (Sides.size() != 2)
diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp
index 913519c4c..954e0a267 100644
--- a/src/Entities/ArrowEntity.cpp
+++ b/src/Entities/ArrowEntity.cpp
@@ -90,6 +90,13 @@ void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFa
// Broadcast arrow hit sound
m_World->BroadcastSoundEffect("random.bowhit", (double)X, (double)Y, (double)Z, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+
+ if ((m_World->GetBlock(Hit) == E_BLOCK_TNT) && IsOnFire())
+ {
+ m_World->SetBlock(X, Y, Z, E_BLOCK_AIR, 0);
+ m_World->SpawnPrimedTNT(X, Y, Z);
+ }
+
}
@@ -103,8 +110,36 @@ void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
Damage += m_World->GetTickRandomNumber(Damage / 2 + 2);
}
- a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1);
+
+ int PowerLevel = m_CreatorData.m_Enchantments.GetLevel(cEnchantments::enchPower);
+ if (PowerLevel > 0)
+ {
+ int ExtraDamage = (int)ceil(0.25 * (PowerLevel + 1));
+ Damage += ExtraDamage;
+ }
+
+ int KnockbackAmount = 1;
+ int PunchLevel = m_CreatorData.m_Enchantments.GetLevel(cEnchantments::enchPunch);
+ if (PunchLevel > 0)
+ {
+ Vector3d LookVector = GetLookVector();
+ Vector3f FinalSpeed = Vector3f(0, 0, 0);
+ switch (PunchLevel)
+ {
+ case 1: FinalSpeed = LookVector * Vector3d(5, 0.3, 5); break;
+ case 2: FinalSpeed = LookVector * Vector3d(8, 0.3, 8); break;
+ default: break;
+ }
+ a_EntityHit.SetSpeed(FinalSpeed);
+ }
+
+ a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, KnockbackAmount);
+ if (IsOnFire() && !a_EntityHit.IsSubmerged() && !a_EntityHit.IsSwimming())
+ {
+ a_EntityHit.StartBurning(100);
+ }
+
// Broadcast successful hit sound
GetWorld()->BroadcastSoundEffect("random.successful_hit", GetPosX(), GetPosY(), GetPosZ(), 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
diff --git a/src/Entities/ArrowEntity.h b/src/Entities/ArrowEntity.h
index 4bfcb1f6d..a1e7a17e7 100644
--- a/src/Entities/ArrowEntity.h
+++ b/src/Entities/ArrowEntity.h
@@ -10,6 +10,7 @@
+
// tolua_begin
class cArrowEntity :
@@ -46,7 +47,7 @@ public:
/// Returns the damage modifier coeff.
double GetDamageCoeff(void) const { return m_DamageCoeff; }
-
+
/// Sets the damage modifier coeff
void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; }
@@ -89,7 +90,7 @@ protected:
/// If true, the arrow is in the process of being collected - don't go to anyone else
bool m_bIsCollected;
-
+
/// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air
Vector3i m_HitBlockPos;
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 32f220897..89d1cffa1 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -13,6 +13,7 @@
#include "../Tracer.h"
#include "Player.h"
#include "Items/ItemHandler.h"
+#include "../FastRandom.h"
@@ -316,8 +317,107 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
// IsOnGround() only is false if the player is moving downwards
// TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain)
- if (!Player->IsOnGround())
+ const cEnchantments & Enchantments = Player->GetEquippedItem().m_Enchantments;
+
+ int SharpnessLevel = Enchantments.GetLevel(cEnchantments::enchSharpness);
+ int SmiteLevel = Enchantments.GetLevel(cEnchantments::enchSmite);
+ int BaneOfArthropodsLevel = Enchantments.GetLevel(cEnchantments::enchBaneOfArthropods);
+
+ if (SharpnessLevel > 0)
+ {
+ a_TDI.FinalDamage += (int)ceil(1.25 * SharpnessLevel);
+ }
+ else if (SmiteLevel > 0)
+ {
+ if (IsMob())
+ {
+ cMonster * Monster = (cMonster *)this;
+ switch (Monster->GetMobType())
+ {
+ case cMonster::mtSkeleton:
+ case cMonster::mtZombie:
+ case cMonster::mtWither:
+ case cMonster::mtZombiePigman:
+ {
+ a_TDI.FinalDamage += (int)ceil(2.5 * SmiteLevel);
+ break;
+ }
+ }
+ }
+ }
+ else if (BaneOfArthropodsLevel > 0)
{
+ if (IsMob())
+ {
+ cMonster * Monster = (cMonster *)this;
+ switch (Monster->GetMobType())
+ {
+ case cMonster::mtSpider:
+ case cMonster::mtCaveSpider:
+ case cMonster::mtSilverfish:
+ {
+ a_TDI.RawDamage += (int)ceil(2.5 * BaneOfArthropodsLevel);
+ // TODO: Add slowness effect
+
+ break;
+ };
+ default: break;
+ }
+ }
+ }
+
+ int FireAspectLevel = Enchantments.GetLevel(cEnchantments::enchFireAspect);
+ if (FireAspectLevel > 0)
+ {
+ int BurnTicks = 3;
+
+ if (FireAspectLevel > 1)
+ {
+ BurnTicks += 4 * (FireAspectLevel - 1);
+ }
+ if (!IsMob() && !IsSubmerged() && !IsSwimming())
+ {
+ StartBurning(BurnTicks * 20);
+ }
+ else if (IsMob() && !IsSubmerged() && !IsSwimming())
+ {
+ cMonster * Monster = (cMonster *)this;
+ switch (Monster->GetMobType())
+ {
+ case cMonster::mtGhast:
+ case cMonster::mtZombiePigman:
+ case cMonster::mtMagmaCube:
+ {
+ break;
+ };
+ default: StartBurning(BurnTicks * 20);
+ }
+ }
+ }
+
+ int ThornsLevel = 0;
+ const cItem ArmorItems[] = { GetEquippedHelmet(), GetEquippedChestplate(), GetEquippedLeggings(), GetEquippedBoots() };
+ for (size_t i = 0; i < ARRAYCOUNT(ArmorItems); i++)
+ {
+ const cItem & Item = ArmorItems[i];
+ ThornsLevel = std::max(ThornsLevel, Item.m_Enchantments.GetLevel(cEnchantments::enchThorns));
+ }
+
+ if (ThornsLevel > 0)
+ {
+ int Chance = ThornsLevel * 15;
+
+ cFastRandom Random;
+ int RandomValue = Random.GenerateRandomInteger(0, 100);
+
+ if (RandomValue <= Chance)
+ {
+ a_TDI.Attacker->TakeDamage(dtAttack, this, 0, Random.GenerateRandomInteger(1, 4), 0);
+ }
+ }
+
+ if (!Player->IsOnGround())
+ {
if ((a_TDI.DamageType == dtAttack) || (a_TDI.DamageType == dtArrowAttack))
{
a_TDI.FinalDamage += 2;
@@ -328,13 +428,123 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
Player->GetStatManager().AddValue(statDamageDealt, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5));
}
+ if (IsPlayer())
+ {
+ double TotalEPF = 0.0;
+ double EPFProtection = 0.00;
+ double EPFFireProtection = 0.00;
+ double EPFBlastProtection = 0.00;
+ double EPFProjectileProtection = 0.00;
+ double EPFFeatherFalling = 0.00;
+
+ const cItem ArmorItems[] = { GetEquippedHelmet(), GetEquippedChestplate(), GetEquippedLeggings(), GetEquippedBoots() };
+ for (size_t i = 0; i < ARRAYCOUNT(ArmorItems); i++)
+ {
+ const cItem & Item = ArmorItems[i];
+ int Level = Item.m_Enchantments.GetLevel(cEnchantments::enchProtection);
+ if (Level > 0)
+ {
+ EPFProtection += (6 + Level * Level) * 0.75 / 3;
+ }
+
+ Level = Item.m_Enchantments.GetLevel(cEnchantments::enchFireProtection);
+ if (Level > 0)
+ {
+ EPFFireProtection += (6 + Level * Level) * 1.25 / 3;
+ }
+
+ Level = Item.m_Enchantments.GetLevel(cEnchantments::enchFeatherFalling);
+ if (Level > 0)
+ {
+ EPFFeatherFalling += (6 + Level * Level) * 2.5 / 3;
+ }
+
+ Level = Item.m_Enchantments.GetLevel(cEnchantments::enchBlastProtection);
+ if (Level > 0)
+ {
+ EPFBlastProtection += (6 + Level * Level) * 1.5 / 3;
+ }
+
+ Level = Item.m_Enchantments.GetLevel(cEnchantments::enchProjectileProtection);
+ if (Level > 0)
+ {
+ EPFProjectileProtection += (6 + Level * Level) * 1.5 / 3;
+ }
+
+ }
+
+ TotalEPF = EPFProtection + EPFFireProtection + EPFFeatherFalling + EPFBlastProtection + EPFProjectileProtection;
+
+ EPFProtection = EPFProtection / TotalEPF;
+ EPFFireProtection = EPFFireProtection / TotalEPF;
+ EPFFeatherFalling = EPFFeatherFalling / TotalEPF;
+ EPFBlastProtection = EPFBlastProtection / TotalEPF;
+ EPFProjectileProtection = EPFProjectileProtection / TotalEPF;
+
+ if (TotalEPF > 25)
+ {
+ TotalEPF = 25;
+ }
+
+ cFastRandom Random;
+ float RandomValue = Random.GenerateRandomInteger(50, 100) * 0.01f;
+
+ TotalEPF = ceil(TotalEPF * RandomValue);
+
+ if (TotalEPF > 20)
+ {
+ TotalEPF = 20;
+ }
+
+ EPFProtection = TotalEPF * EPFProtection;
+ EPFFireProtection = TotalEPF * EPFFireProtection;
+ EPFFeatherFalling = TotalEPF * EPFFeatherFalling;
+ EPFBlastProtection = TotalEPF * EPFBlastProtection;
+ EPFProjectileProtection = TotalEPF * EPFProjectileProtection;
+
+ int RemovedDamage = 0;
+
+ if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtAdmin))
+ {
+ RemovedDamage += (int)ceil(EPFProtection * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if ((a_TDI.DamageType == dtFalling) || (a_TDI.DamageType == dtFall) || (a_TDI.DamageType == dtEnderPearl))
+ {
+ RemovedDamage += (int)ceil(EPFFeatherFalling * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if (a_TDI.DamageType == dtBurning)
+ {
+ RemovedDamage += (int)ceil(EPFFireProtection * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if (a_TDI.DamageType == dtExplosion)
+ {
+ RemovedDamage += (int)ceil(EPFBlastProtection * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if (a_TDI.DamageType == dtProjectile)
+ {
+ RemovedDamage += (int)ceil(EPFBlastProtection * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if (a_TDI.FinalDamage < RemovedDamage)
+ {
+ RemovedDamage = 0;
+ }
+
+ a_TDI.FinalDamage -= RemovedDamage;
+ }
+
m_Health -= (short)a_TDI.FinalDamage;
// TODO: Apply damage to armor
m_Health = std::max(m_Health, 0);
- if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL)) // Knockback for only players and mobs
+ // Add knockback:
+ if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL))
{
int KnockbackLevel = a_TDI.Attacker->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchKnockback); // More common enchantment
if (KnockbackLevel < 1)
@@ -1252,6 +1462,8 @@ void cEntity::HandleAir(void)
// See if the entity is /submerged/ water (block above is water)
// Get the type of block the entity is standing in:
+ int RespirationLevel = GetEquippedHelmet().m_Enchantments.GetLevel(cEnchantments::enchRespiration);
+
if (IsSubmerged())
{
if (!IsPlayer()) // Players control themselves
@@ -1259,6 +1471,11 @@ void cEntity::HandleAir(void)
SetSpeedY(1); // Float in the water
}
+ if (RespirationLevel > 0)
+ {
+ ((cPawn *)this)->AddEntityEffect(cEntityEffect::effNightVision, 200, 5, 0);
+ }
+
if (m_AirLevel <= 0)
{
// Runs the air tick timer to check whether the player should be damaged
@@ -1285,6 +1502,12 @@ void cEntity::HandleAir(void)
// Set the air back to maximum
m_AirLevel = MAX_AIR_LEVEL;
m_AirTickTimer = DROWNING_TICKS;
+
+ if (RespirationLevel > 0)
+ {
+ m_AirTickTimer = DROWNING_TICKS + (RespirationLevel * 15 * 20);
+ }
+
}
}
diff --git a/src/Entities/ItemFrame.cpp b/src/Entities/ItemFrame.cpp
index f0b0c8c65..0bc10ec60 100644
--- a/src/Entities/ItemFrame.cpp
+++ b/src/Entities/ItemFrame.cpp
@@ -55,7 +55,6 @@ void cItemFrame::KilledBy(TakeDamageInfo & a_TDI)
{
if (m_Item.IsEmpty())
{
- SetHealth(0);
super::KilledBy(a_TDI);
Destroy();
return;
diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp
index 21c58fdba..1501eea84 100644
--- a/src/Entities/Minecart.cpp
+++ b/src/Entities/Minecart.cpp
@@ -891,31 +891,31 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
)
{
// Moving -X +Z
- if ((-GetSpeedX() * 0.4 / sqrt(2)) < 0.01)
+ if ((-GetSpeedX() * 0.4 / sqrt(2.0)) < 0.01)
{
// ~ SpeedX >= 0 Immobile or not moving in the "right" direction. Give it a bump!
- AddSpeedX(-4 / sqrt(2));
- AddSpeedZ(4 / sqrt(2));
+ AddSpeedX(-4 / sqrt(2.0));
+ AddSpeedZ(4 / sqrt(2.0));
}
else
{
// ~ SpeedX < 0 Moving in the "right" direction. Only accelerate it a bit.
- SetSpeedX(GetSpeedX() * 0.4 / sqrt(2));
- SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2));
+ SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
+ SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
}
}
- else if ((GetSpeedX() * 0.4 / sqrt(2)) < 0.01)
+ else if ((GetSpeedX() * 0.4 / sqrt(2.0)) < 0.01)
{
// Moving +X -Z
// ~ SpeedX <= 0 Immobile or not moving in the "right" direction
- AddSpeedX(4 / sqrt(2));
- AddSpeedZ(-4 / sqrt(2));
+ AddSpeedX(4 / sqrt(2.0));
+ AddSpeedZ(-4 / sqrt(2.0));
}
else
{
// ~ SpeedX > 0 Moving in the "right" direction
- SetSpeedX(GetSpeedX() * 0.4 / sqrt(2));
- SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2));
+ SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
+ SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
}
break;
}
@@ -943,28 +943,28 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
if ((GetSpeedX() * 0.4) < 0.01)
{
// ~ SpeedX <= 0 Immobile or not moving in the "right" direction
- AddSpeedX(4 / sqrt(2));
- AddSpeedZ(4 / sqrt(2));
+ AddSpeedX(4 / sqrt(2.0));
+ AddSpeedZ(4 / sqrt(2.0));
}
else
{
// ~ SpeedX > 0 Moving in the "right" direction
- SetSpeedX(GetSpeedX() * 0.4 / sqrt(2));
- SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2));
+ SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
+ SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
}
}
else if ((-GetSpeedX() * 0.4) < 0.01)
{
// Moving -X -Z
// ~ SpeedX >= 0 Immobile or not moving in the "right" direction
- AddSpeedX(-4 / sqrt(2));
- AddSpeedZ(-4 / sqrt(2));
+ AddSpeedX(-4 / sqrt(2.0));
+ AddSpeedZ(-4 / sqrt(2.0));
}
else
{
// ~ SpeedX < 0 Moving in the "right" direction
- SetSpeedX(GetSpeedX() * 0.4 / sqrt(2));
- SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2));
+ SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
+ SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
}
break;
}
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
index aab534f41..87b5bed07 100644
--- a/src/Entities/Pickup.cpp
+++ b/src/Entities/Pickup.cpp
@@ -150,10 +150,14 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
}
}
- if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize())) // Don't combine into an already full pickup
+ // Try to combine the pickup with adjacent same-item pickups:
+ if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize())) // Don't combine if already full
{
+ // By using a_Chunk's ForEachEntity() instead of cWorld's, pickups don't combine across chunk boundaries.
+ // That is a small price to pay for not having to traverse the entire world for each entity.
+ // The speedup in the tick thread is quite considerable.
cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this);
- m_World->ForEachEntity(PickupCombiningCallback); // Not ForEachEntityInChunk, otherwise pickups don't combine across chunk boundaries
+ a_Chunk.ForEachEntity(PickupCombiningCallback);
if (PickupCombiningCallback.FoundMatchingPickup())
{
m_World->BroadcastEntityMetadata(*this);
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 5e0da3298..b3bb1072e 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -15,6 +15,7 @@
#include "../Chunk.h"
#include "../Items/ItemHandler.h"
#include "../Vector3.h"
+#include "../FastRandom.h"
#include "../WorldStorage/StatSerializer.h"
#include "../CompositeChat.h"
@@ -1793,6 +1794,28 @@ void cPlayer::UseEquippedItem(int a_Amount)
return;
}
+ // If the item has an unbreaking enchantment, give it a random chance of not breaking:
+ cItem Item = GetEquippedItem();
+ int UnbreakingLevel = Item.m_Enchantments.GetLevel(cEnchantments::enchUnbreaking);
+ if (UnbreakingLevel > 0)
+ {
+ int chance;
+ if (ItemCategory::IsArmor(Item.m_ItemType))
+ {
+ chance = 60 + (40 / (UnbreakingLevel + 1));
+ }
+ else
+ {
+ chance = 100 / (UnbreakingLevel + 1);
+ }
+
+ cFastRandom Random;
+ if (Random.NextInt(101) <= chance)
+ {
+ return;
+ }
+ }
+
if (GetInventory().DamageEquippedItem(a_Amount))
{
m_World->BroadcastSoundEffect("random.break", GetPosX(), GetPosY(), GetPosZ(), 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index 43023ec28..acc9bd674 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -222,7 +222,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a
m_ProjectileKind(a_Kind),
m_CreatorData(
((a_Creator != NULL) ? a_Creator->GetUniqueID() : -1),
- ((a_Creator != NULL) ? (a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "") : "")
+ ((a_Creator != NULL) ? (a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "") : ""),
+ ((a_Creator != NULL) ? a_Creator->GetEquippedWeapon().m_Enchantments : cEnchantments())
),
m_IsInGround(false)
{
@@ -235,7 +236,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a
cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height) :
super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height),
m_ProjectileKind(a_Kind),
- m_CreatorData(a_Creator->GetUniqueID(), a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : ""),
+ m_CreatorData(a_Creator->GetUniqueID(), a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "", a_Creator->GetEquippedWeapon().m_Enchantments),
m_IsInGround(false)
{
SetSpeed(a_Speed);
diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h
index 0ebc32f36..990136a32 100644
--- a/src/Entities/ProjectileEntity.h
+++ b/src/Entities/ProjectileEntity.h
@@ -94,14 +94,16 @@ protected:
*/
struct CreatorData
{
- CreatorData(int a_UniqueID, const AString & a_Name) :
+ CreatorData(int a_UniqueID, const AString & a_Name, const cEnchantments & a_Enchantments) :
m_UniqueID(a_UniqueID),
- m_Name(a_Name)
+ m_Name(a_Name),
+ m_Enchantments(a_Enchantments)
{
}
const int m_UniqueID;
AString m_Name;
+ cEnchantments m_Enchantments;
};
/** The type of projectile I am */
diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp
index ab772e97b..d200ef3d7 100644
--- a/src/FurnaceRecipe.cpp
+++ b/src/FurnaceRecipe.cpp
@@ -12,8 +12,8 @@
-typedef std::list< cFurnaceRecipe::Recipe > RecipeList;
-typedef std::list< cFurnaceRecipe::Fuel > FuelList;
+typedef std::list<cFurnaceRecipe::cRecipe> RecipeList;
+typedef std::list<cFurnaceRecipe::cFuel> FuelList;
@@ -30,7 +30,7 @@ struct cFurnaceRecipe::sFurnaceRecipeState
cFurnaceRecipe::cFurnaceRecipe()
- : m_pState( new sFurnaceRecipeState)
+ : m_pState(new sFurnaceRecipeState)
{
ReloadRecipes();
}
@@ -68,12 +68,18 @@ void cFurnaceRecipe::ReloadRecipes(void)
while (std::getline(f, ParsingLine))
{
LineNum++;
- ParsingLine.erase(std::remove_if(ParsingLine.begin(), ParsingLine.end(), isspace), ParsingLine.end()); // Remove ALL whitespace from the line
if (ParsingLine.empty())
{
continue;
}
+ // Remove comments from the line:
+ size_t FirstCommentSymbol = ParsingLine.find('#');
+ if ((FirstCommentSymbol != AString::npos) && (FirstCommentSymbol != 0))
+ {
+ ParsingLine.erase(ParsingLine.begin() + (const long)FirstCommentSymbol, ParsingLine.end());
+ }
+
switch (ParsingLine[0])
{
case '#':
@@ -103,159 +109,132 @@ void cFurnaceRecipe::ReloadRecipes(void)
-void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, int a_LineNum)
+void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, unsigned int a_LineNum)
{
- // Fuel
- int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0;
- AString::size_type BeginPos = 1; // Begin at one after exclamation mark (bang)
-
- if (
- !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID
- !ReadOptionalNumbers(BeginPos, ":", "=", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health)
- !ReadMandatoryNumber(BeginPos, "0123456789", a_Line, a_LineNum, IBurnTime, true) // Read item burn time - last value
- )
+ AString Line(a_Line);
+ Line.erase(Line.begin()); // Remove the beginning "!"
+ Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
+
+ std::auto_ptr<cItem> Item(new cItem);
+ int BurnTime;
+
+ const AStringVector & Sides = StringSplit(Line, "=");
+ if (Sides.size() != 2)
{
+ LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
- // Add to fuel list:
- Fuel F;
- F.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth);
- F.BurnTime = IBurnTime;
- m_pState->Fuel.push_back(F);
-}
-
-
-
-
+ if (!ParseItem(Sides[0], *Item))
+ {
+ LOGWARNING("furnace.txt: line %d: Cannot parse item \"%s\".", a_LineNum, Sides[0].c_str());
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
+ }
-void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, int a_LineNum)
-{
- int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0;
- int OItemID = 0, OItemCount = 0, OItemHealth = 0;
- AString::size_type BeginPos = 0; // Begin at start of line
-
- if (
- !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID
- !ReadOptionalNumbers(BeginPos, ":", "@", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health)
- !ReadMandatoryNumber(BeginPos, "=", a_Line, a_LineNum, IBurnTime) || // Read item burn time
- !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, OItemID) || // Read result ID
- !ReadOptionalNumbers(BeginPos, ":", "012456789", a_Line, a_LineNum, OItemCount, OItemHealth, true) // Read result count (and optionally health) - last value
- )
+ if (!StringToInteger<int>(Sides[1], BurnTime))
{
+ LOGWARNING("furnace.txt: line %d: Cannot parse burn time.", a_LineNum);
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
- // Add to recipe list
- Recipe R;
- R.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth);
- R.Out = new cItem((ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth);
- R.CookTime = IBurnTime;
- m_pState->Recipes.push_back(R);
+ // Add to fuel list:
+ cFuel Fuel;
+ Fuel.In = Item.release();
+ Fuel.BurnTime = BurnTime;
+ m_pState->Fuel.push_back(Fuel);
}
-void cFurnaceRecipe::PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing)
+void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum)
{
- LOGWARN("Error parsing furnace recipes at line %i pos " SIZE_T_FMT ": missing '%s'", a_Line, a_Position, a_CharactersMissing.c_str());
-}
-
-
+ AString Line(a_Line);
+ Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
+ int CookTime = 200;
+ std::auto_ptr<cItem> InputItem(new cItem());
+ std::auto_ptr<cItem> OutputItem(new cItem());
+ const AStringVector & Sides = StringSplit(Line, "=");
+ if (Sides.size() != 2)
+ {
+ LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
+ }
-bool cFurnaceRecipe::ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue)
-{
- // TODO: replace atoi with std::stoi
- AString::size_type End;
- if (a_IsLastValue)
+ const AStringVector & InputSplit = StringSplit(Sides[0], "@");
+ if (!ParseItem(InputSplit[0], *InputItem))
{
- End = a_Text.find_first_not_of(a_Delimiter, a_Begin);
+ LOGWARNING("furnace.txt: line %d: Cannot parse input item \"%s\".", a_LineNum, InputSplit[0].c_str());
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
}
- else
+
+ if (InputSplit.size() > 1)
{
- End = a_Text.find_first_of(a_Delimiter, a_Begin);
- if (End == AString::npos)
+ if (!StringToInteger<int>(InputSplit[1], CookTime))
{
- PrintParseError(a_Line, a_Begin, a_Delimiter);
- return false;
+ LOGWARNING("furnace.txt: line %d: Cannot parse cook time \"%s\".", a_LineNum, InputSplit[1].c_str());
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
}
}
-
- // stoi won't throw an exception if the string is alphanumeric, we should check for this
- if (!DoesStringContainOnlyNumbers(a_Text.substr(a_Begin, End - a_Begin)))
+
+ if (!ParseItem(Sides[1], *OutputItem))
{
- PrintParseError(a_Line, a_Begin, "number");
- return false;
+ LOGWARNING("furnace.txt: line %d: Cannot parse output item \"%s\".", a_LineNum, Sides[1].c_str());
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
}
- a_Value = atoi(a_Text.substr(a_Begin, End - a_Begin).c_str());
- a_Begin = End + 1; // Jump over delimiter
- return true;
+ cRecipe Recipe;
+ Recipe.In = InputItem.release();
+ Recipe.Out = OutputItem.release();
+ Recipe.CookTime = CookTime;
+ m_pState->Recipes.push_back(Recipe);
}
-bool cFurnaceRecipe::ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue)
+bool cFurnaceRecipe::ParseItem(const AString & a_String, cItem & a_Item)
{
- // TODO: replace atoi with std::stoi
- AString::size_type End, Begin = a_Begin;
+ AString ItemString = a_String;
+
+ const AStringVector & SplitAmount = StringSplit(ItemString, ",");
+ ItemString = SplitAmount[0];
- End = a_Text.find_first_of(a_DelimiterOne, Begin);
- if (End != AString::npos)
- {
- if (DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin)))
- {
- a_ValueOne = std::atoi(a_Text.substr(Begin, End - Begin).c_str());
- Begin = End + 1;
+ const AStringVector & SplitMeta = StringSplit(ItemString, ":");
+ ItemString = SplitMeta[0];
- if (a_IsLastValue)
- {
- End = a_Text.find_first_not_of(a_DelimiterTwo, Begin);
- }
- else
- {
- End = a_Text.find_first_of(a_DelimiterTwo, Begin);
- if (End == AString::npos)
- {
- PrintParseError(a_Line, Begin, a_DelimiterTwo);
- return false;
- }
- }
-
- // stoi won't throw an exception if the string is alphanumeric, we should check for this
- if (!DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin)))
- {
- PrintParseError(a_Line, Begin, "number");
- return false;
- }
- a_ValueTwo = atoi(a_Text.substr(Begin, End - Begin).c_str());
+ if (!StringToItem(ItemString, a_Item))
+ {
+ return false;
+ }
- a_Begin = End + 1; // Jump over delimiter
- return true;
- }
- else
+ if (SplitAmount.size() > 1)
+ {
+ if (!StringToInteger<char>(SplitAmount[1].c_str(), a_Item.m_ItemCount))
{
- return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue);
+ return false;
}
}
-
- return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue);
-}
-
-
-
-
-bool cFurnaceRecipe::DoesStringContainOnlyNumbers(const AString & a_String)
-{
- // TODO: replace this with std::all_of(a_String.begin(), a_String.end(), isdigit)
- return (a_String.find_first_not_of("0123456789") == AString::npos);
+ if (SplitMeta.size() > 1)
+ {
+ if (!StringToInteger<short>(SplitMeta[1].c_str(), a_Item.m_ItemDamage))
+ {
+ return false;
+ }
+ }
+ return true;
}
@@ -266,19 +245,19 @@ void cFurnaceRecipe::ClearRecipes(void)
{
for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
{
- Recipe R = *itr;
- delete R.In;
- R.In = NULL;
- delete R.Out;
- R.Out = NULL;
+ cRecipe Recipe = *itr;
+ delete Recipe.In;
+ Recipe.In = NULL;
+ delete Recipe.Out;
+ Recipe.Out = NULL;
}
m_pState->Recipes.clear();
for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
{
- Fuel F = *itr;
- delete F.In;
- F.In = NULL;
+ cFuel Fuel = *itr;
+ delete Fuel.In;
+ Fuel.In = NULL;
}
m_pState->Fuel.clear();
}
@@ -287,21 +266,21 @@ void cFurnaceRecipe::ClearRecipes(void)
-const cFurnaceRecipe::Recipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
+const cFurnaceRecipe::cRecipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
{
- const Recipe * BestRecipe = 0;
+ const cRecipe * BestRecipe = 0;
for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
{
- const Recipe & R = *itr;
- if ((R.In->m_ItemType == a_Ingredient.m_ItemType) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount))
+ const cRecipe & Recipe = *itr;
+ if ((Recipe.In->m_ItemType == a_Ingredient.m_ItemType) && (Recipe.In->m_ItemCount <= a_Ingredient.m_ItemCount))
{
- if (BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount))
+ if (BestRecipe && (BestRecipe->In->m_ItemCount > Recipe.In->m_ItemCount))
{
continue;
}
else
{
- BestRecipe = &R;
+ BestRecipe = &Recipe;
}
}
}
@@ -317,16 +296,16 @@ int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const
int BestFuel = 0;
for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
{
- const Fuel & F = *itr;
- if ((F.In->m_ItemType == a_Fuel.m_ItemType) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount))
+ const cFuel & Fuel = *itr;
+ if ((Fuel.In->m_ItemType == a_Fuel.m_ItemType) && (Fuel.In->m_ItemCount <= a_Fuel.m_ItemCount))
{
- if (BestFuel > 0 && (BestFuel > F.BurnTime))
+ if (BestFuel > 0 && (BestFuel > Fuel.BurnTime))
{
continue;
}
else
{
- BestFuel = F.BurnTime;
+ BestFuel = Fuel.BurnTime;
}
}
}
diff --git a/src/FurnaceRecipe.h b/src/FurnaceRecipe.h
index 77ed35a57..6a1650695 100644
--- a/src/FurnaceRecipe.h
+++ b/src/FurnaceRecipe.h
@@ -19,23 +19,23 @@ public:
void ReloadRecipes(void);
- struct Fuel
+ struct cFuel
{
cItem * In;
int BurnTime; ///< How long this fuel burns, in ticks
};
- struct Recipe
+ struct cRecipe
{
cItem * In;
cItem * Out;
int CookTime; ///< How long this recipe takes to smelt, in ticks
};
- /// Returns a recipe for the specified input, NULL if no recipe found
- const Recipe * GetRecipeFrom(const cItem & a_Ingredient) const;
+ /** Returns a recipe for the specified input, NULL if no recipe found */
+ const cRecipe * GetRecipeFrom(const cItem & a_Ingredient) const;
- /// Returns the amount of time that the specified fuel burns, in ticks
+ /** Returns the amount of time that the specified fuel burns, in ticks */
int GetBurnTime(const cItem & a_Fuel) const;
private:
@@ -43,33 +43,14 @@ private:
/** Parses the fuel contained in the line, adds it to m_pState's fuels.
Logs a warning to the console on input error. */
- void AddFuelFromLine(const AString & a_Line, int a_LineNum);
+ void AddFuelFromLine(const AString & a_Line, unsigned int a_LineNum);
/** Parses the recipe contained in the line, adds it to m_pState's recipes.
Logs a warning to the console on input error. */
- void AddRecipeFromLine(const AString & a_Line, int a_LineNum);
-
- /** Calls LOGWARN with the line, position, and error */
- static void PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing);
-
- /** Reads a number from a string given, starting at a given position and ending at a delimiter given
- Updates beginning position to the delimiter found + 1, and updates the value to the one read
- If it encounters a substring that is not fully numeric, it will call SetParseError() and return false; the caller should abort processing
- Otherwise, the function will return true
- */
- static bool ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue = false);
-
- /** Reads two numbers from a string given, starting at a given position and ending at the first delimiter given, then again (with an updated position) until the second delimiter given
- Updates beginning position to the second delimiter found + 1, and updates the values to the ones read
- If it encounters a substring that is not fully numeric whilst reading the second value, it will call SetParseError() and return false; the caller should abort processing
- If this happens whilst reading the first value, it will call ReadMandatoryNumber() with the appropriate position, as this may legitimately occur with the optional value and AString::find_first_of finding the incorrect delimiter. It will return the result of ReadMandatoryNumber()
- True will be returned definitively for an optional value that is valid
- */
- static bool ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue = false);
-
- /** Uses std::all_of to determine if a string contains only digits */
- static bool DoesStringContainOnlyNumbers(const AString & a_String);
+ void AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum);
+ /** Parses an item string in the format "<ItemType>[: <Damage>][, <Amount>]", returns true if successful. */
+ bool ParseItem(const AString & a_String, cItem & a_Item);
struct sFurnaceRecipeState;
sFurnaceRecipeState * m_pState;
diff --git a/src/Items/ItemBow.h b/src/Items/ItemBow.h
index fdc24689c..f29cc5d59 100644
--- a/src/Items/ItemBow.h
+++ b/src/Items/ItemBow.h
@@ -75,7 +75,6 @@ public:
Arrow = NULL;
return;
}
-
a_Player->GetWorld()->BroadcastSoundEffect("random.bow", a_Player->GetPosX(), a_Player->GetPosY(), a_Player->GetPosZ(), 0.5, (float)Force);
if (!a_Player->IsGameModeCreative())
{
@@ -83,8 +82,19 @@ public:
{
a_Player->GetInventory().RemoveItem(cItem(E_ITEM_ARROW));
}
+ else
+ {
+ Arrow->SetPickupState(cArrowEntity::psNoPickup);
+ }
+
+
a_Player->UseEquippedItem();
}
+
+ if (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchFlame) > 0)
+ {
+ Arrow->StartBurning(100);
+ }
}
} ;
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
index 8b554ee34..67c250a97 100644
--- a/src/Items/ItemHandler.h
+++ b/src/Items/ItemHandler.h
@@ -75,7 +75,7 @@ public:
int FoodLevel;
double Saturation;
- FoodInfo(int a_FoodLevel, double a_Saturation, int a_PoisonChance = 0) :
+ FoodInfo(int a_FoodLevel, double a_Saturation) :
FoodLevel(a_FoodLevel),
Saturation(a_Saturation)
{
diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp
index 4e5c41a8a..28da83c31 100644
--- a/src/Protocol/MojangAPI.cpp
+++ b/src/Protocol/MojangAPI.cpp
@@ -158,7 +158,8 @@ cMojangAPI::cMojangAPI(void) :
m_NameToUUIDServer(DEFAULT_NAME_TO_UUID_SERVER),
m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS),
m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER),
- m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS)
+ m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS),
+ m_RankMgr(NULL)
{
}
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index c831da251..8b395230a 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -979,9 +979,18 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
AString ServerAddress;
short ServerPort;
UInt32 NextState;
- m_Buffer.ReadVarUTF8String(ServerAddress);
- m_Buffer.ReadBEShort(ServerPort);
- m_Buffer.ReadVarInt(NextState);
+ if (!m_Buffer.ReadVarUTF8String(ServerAddress))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadBEShort(ServerPort))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadVarInt(NextState))
+ {
+ break;
+ }
m_Buffer.CommitRead();
m_Protocol = new cProtocol172(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
return true;
@@ -991,9 +1000,18 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
AString ServerAddress;
short ServerPort;
UInt32 NextState;
- m_Buffer.ReadVarUTF8String(ServerAddress);
- m_Buffer.ReadBEShort(ServerPort);
- m_Buffer.ReadVarInt(NextState);
+ if (!m_Buffer.ReadVarUTF8String(ServerAddress))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadBEShort(ServerPort))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadVarInt(NextState))
+ {
+ break;
+ }
m_Buffer.CommitRead();
m_Protocol = new cProtocol176(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
return true;
diff --git a/src/RankManager.cpp b/src/RankManager.cpp
index dc6eec9e4..e5896f8f3 100644
--- a/src/RankManager.cpp
+++ b/src/RankManager.cpp
@@ -477,8 +477,8 @@ AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID)
{
SQLite::Statement stmt(m_DB, "SELECT Rank.Name FROM Rank LEFT JOIN PlayerRank ON Rank.RankID = PlayerRank.RankID WHERE PlayerRank.PlayerUUID = ?");
stmt.bind(1, a_PlayerUUID);
- stmt.executeStep();
- if (stmt.isDone())
+ // executeStep returns false on no data
+ if (!stmt.executeStep())
{
// No data returned from the DB
return AString();
diff --git a/src/Root.cpp b/src/Root.cpp
index ef66f9870..f04cbf08b 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -468,16 +468,6 @@ void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCall
void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
{
- // Some commands are built-in:
- if (a_Cmd == "stop")
- {
- m_bStop = true;
- }
- else if (a_Cmd == "restart")
- {
- m_bRestart = true;
- }
-
// Put the command into a queue (Alleviates FS #363):
cCSLock Lock(m_CSPendingCommands);
m_PendingCommands.push_back(cCommand(a_Cmd, new cLogCommandDeleteSelfOutputCallback));
@@ -489,14 +479,16 @@ void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
{
- // Some commands are built-in:
+ // cRoot handles stopping and restarting due to our access to controlling variables
if (a_Cmd == "stop")
{
m_bStop = true;
+ return;
}
else if (a_Cmd == "restart")
{
m_bRestart = true;
+ return;
}
LOG("Executing console command: \"%s\"", a_Cmd.c_str());
diff --git a/src/Server.cpp b/src/Server.cpp
index 958fe83c8..069e2a169 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -458,56 +458,80 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
return;
}
- // Special handling: "stop" and "restart" are built in
- if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0))
- {
- return;
- }
+ // "stop" and "restart" are handled in cRoot::ExecuteConsoleCommand, our caller, due to its access to controlling variables
// "help" and "reload" are to be handled by MCS, so that they work no matter what
if (split[0] == "help")
{
PrintHelp(split, a_Output);
+ a_Output.Finished();
return;
}
else if (split[0] == "reload")
{
cPluginManager::Get()->ReloadPlugins();
+ a_Output.Finished();
return;
}
else if (split[0] == "reloadplugins")
{
cPluginManager::Get()->ReloadPlugins();
+ a_Output.Out("Plugins reloaded");
+ a_Output.Finished();
return;
}
else if (split[0] == "load")
{
if (split.size() > 1)
{
- cPluginManager::Get()->LoadPlugin(split[1]);
-
- return;
+ a_Output.Out(cPluginManager::Get()->LoadPlugin(split[1]) ? "Plugin loaded" : "Error occurred loading plugin");
}
else
{
- a_Output.Out("No plugin given! Command: load <pluginname>");
- a_Output.Finished();
- return;
+ a_Output.Out("Usage: load <pluginname>");
}
+ a_Output.Finished();
+ return;
}
else if (split[0] == "unload")
{
if (split.size() > 1)
{
cPluginManager::Get()->RemovePlugin(cPluginManager::Get()->GetPlugin(split[1]));
- return;
+ a_Output.Out("Plugin unloaded");
}
else
{
- a_Output.Out("No plugin given! Command: unload <pluginname>");
- a_Output.Finished();
- return;
+ a_Output.Out("Usage: unload <pluginname>");
}
+ a_Output.Finished();
+ return;
+ }
+ if (split[0] == "destroyentities")
+ {
+ class WorldCallback : public cWorldListCallback
+ {
+ virtual bool Item(cWorld * a_World) override
+ {
+ class EntityCallback : public cEntityCallback
+ {
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ if (!a_Entity->IsPlayer())
+ {
+ a_Entity->Destroy();
+ }
+ return false;
+ }
+ } EC;
+ a_World->ForEachEntity(EC);
+ return false;
+ }
+ } WC;
+ cRoot::Get()->ForEachWorld(WC);
+ a_Output.Out("Destroyed all entities");
+ a_Output.Finished();
+ return;
}
// There is currently no way a plugin can do these (and probably won't ever be):
@@ -602,6 +626,7 @@ void cServer::BindBuiltInConsoleCommands(void)
PlgMgr->BindConsoleCommand("chunkstats", NULL, " - Displays detailed chunk memory statistics");
PlgMgr->BindConsoleCommand("load <pluginname>", NULL, " - Adds and enables the specified plugin");
PlgMgr->BindConsoleCommand("unload <pluginname>", NULL, " - Disables the specified plugin");
+ PlgMgr->BindConsoleCommand("destroyentities", NULL, " - Destroys all entities in all worlds");
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
PlgMgr->BindConsoleCommand("dumpmem", NULL, " - Dumps all used memory blocks together with their callstacks into memdump.xml");
diff --git a/src/SetChunkData.cpp b/src/SetChunkData.cpp
index 97903074a..bfe59fbcb 100644
--- a/src/SetChunkData.cpp
+++ b/src/SetChunkData.cpp
@@ -134,8 +134,8 @@ void cSetChunkData::RemoveInvalidBlockEntities(void)
);
cBlockEntityList::iterator itr2 = itr;
itr2++;
- m_BlockEntities.erase(itr);
delete *itr;
+ m_BlockEntities.erase(itr);
itr = itr2;
}
else
diff --git a/src/World.cpp b/src/World.cpp
index b01e53638..99e09c658 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -1226,24 +1226,26 @@ void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_Blo
return;
}
- // TODO: Add damage to entities and implement block hardiness
+ // TODO: Implement block hardiness
Vector3d explosion_pos = Vector3d(a_BlockX, a_BlockY, a_BlockZ);
cVector3iArray BlocksAffected;
m_ChunkMap->DoExplosionAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, BlocksAffected);
BroadcastSoundEffect("random.explode", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 1.0f, 0.6f);
+
{
cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
cClientHandle * ch = (*itr)->GetClientHandle();
- if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
+ if (ch == NULL)
{
continue;
}
+
Vector3d distance_explosion = (*itr)->GetPosition() - explosion_pos;
if (distance_explosion.SqrLength() < 4096.0)
{
- double real_distance = std::max(0.004, sqrt(distance_explosion.SqrLength()));
+ double real_distance = std::max(0.004, distance_explosion.Length());
double power = a_ExplosionSize / real_distance;
if (power <= 1)
{
@@ -1255,6 +1257,7 @@ void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_Blo
}
}
}
+
cPluginManager::Get()->CallHookExploded(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData);
}
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index e435a1b1f..68e541eba 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -615,7 +615,6 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile)
{
m_Writer.BeginCompound("");
AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName());
- Vector3d Pos = a_Projectile->GetPosition();
m_Writer.AddByte("inGround", a_Projectile->IsInGround() ? 1 : 0);
switch (a_Projectile->GetProjectileKind())
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index 5978e5ef8..e79cc291d 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -592,10 +592,6 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
}
int RelX = x, RelY = y, RelZ = z, ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ);
- if (RelY == 2)
- {
- LOGD("HERE");
- }
// Load the proper BlockEntity type based on the block type:
BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, RelY, RelZ);
diff --git a/tests/ChunkData/CopyBlocks.cpp b/tests/ChunkData/CopyBlocks.cpp
index ec9451099..99f416e94 100644
--- a/tests/ChunkData/CopyBlocks.cpp
+++ b/tests/ChunkData/CopyBlocks.cpp
@@ -45,7 +45,7 @@ int main(int argc, char ** argv)
BLOCKTYPE * WritePosition = &TestBuffer[WritePosIdx];
memset(TestBuffer, 0x03, sizeof(TestBuffer));
size_t LastReportedStep = 1;
- for (size_t idx = 0; idx < 5000; idx += 7)
+ for (size_t idx = 0; idx < 5000; idx += 73)
{
if (idx / 500 != LastReportedStep)
{
@@ -53,7 +53,7 @@ int main(int argc, char ** argv)
LastReportedStep = idx / 500;
}
- for (size_t len = 3; len < 1000; len += 13)
+ for (size_t len = 3; len < 700; len += 13)
{
Data.CopyBlockTypes(WritePosition, idx, len);