1
0

Merge branch 'master' into Inventory

This commit is contained in:
Howaner 2014-09-02 20:11:38 +02:00
commit e431bb4e63
56 changed files with 1483 additions and 680 deletions

View File

@ -2851,7 +2851,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)." }, 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"}, 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." }, 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." }, 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." }, 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."}, 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."},

@ -1 +1 @@
Subproject commit 7cc99285ae5117418f657c3b295ca71f2b75b4f4 Subproject commit bd23915df763b182610c6163c5ff2d64a0756560

View File

@ -44,6 +44,8 @@ ApplePlanks, 4 = AppleLog, *
ConiferPlanks, 4 = ConiferLog, * ConiferPlanks, 4 = ConiferLog, *
BirchPlanks, 4 = BirchLog, * BirchPlanks, 4 = BirchLog, *
JunglePlanks, 4 = JungleLog, * JunglePlanks, 4 = JungleLog, *
AcaciaPlanks, 4 = AcaciaLog, *
DarkOakPlanks, 4 = DarkOakLog, *
Stick, 4 = Planks, 2:2, 2:3 Stick, 4 = Planks, 2:2, 2:3
Torch, 4 = Stick, 1:2 | Coal, 1:1 Torch, 4 = Stick, 1:2 | Coal, 1:1
Workbench = Planks, 1:1, 1:2, 2:1, 2:2 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 # Blocks
# #
IronBlock = IronIngot, 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 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 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 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 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 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 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 NetherBrick = netherbrickitem, 1:1, 1:2, 2:1, 2:2
Glowstone = GlowstoneDust, 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 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 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 PillarQuartzBlock = QuartzSlab, 1:1, 1:2
ChiseledQuartzBlock, 2 = QuartzBlock, 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 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 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: # Slabs:
StoneSlab, 6 = Stone, 1:1, 2:1, 3:1 StoneSlab, 6 = Stone, 1:1, 2:1, 3:1
SandstoneSlab, 6 = Sandstone, 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 CobblestoneSlab, 6 = Cobblestone, 1:1, 2:1, 3:1
BrickSlab, 6 = BrickBlock, 1:1, 2:1, 3:1 BrickSlab, 6 = BrickBlock, 1:1, 2:1, 3:1
StonebrickSlab, 6 = StoneBrick, 1:1, 2:1, 3:1 StonebrickSlab, 6 = StoneBrick, 1:1, 2:1, 3:1
NetherbrickSlab, 6 = NetherBrick, 1:1, 2:1, 3:1 NetherbrickSlab, 6 = NetherBrick, 1:1, 2:1, 3:1
Quartzslab, 6 = QuartzBlock, 1:1, 2:1, 3:1 Quartzslab, 6 = QuartzBlock, 1:1, 2:1, 3:1
snow, 6 = SnowBlock, 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: # Stairs:
WoodStairs, 4 = Planks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 SpruceWoodStairs, 4 = SprucePlanks, 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, 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, 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 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 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 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, 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 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 RedSandstoneStairs, 4 = RedSandstone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
ClayBlock = Clay, 1:1, 1:2, 2:1, 2:2 RedSandstoneStairs, 4 = RedSandstone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
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
# Other # Other
Carpet = Wool, 1:3, 2:3 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 # Mechanisms
# #
WoodenDoor = Planks, 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
IronDoor = IronIngot, 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 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 WoodPlate = Planks, 1:1, 2:1
StonePlate = Stone, 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 StoneButton = Stone, 1:1
WoodenButton = Planks, 1:1 WoodenButton = Planks, 1:1
RedstoneTorchOn = Stick, 1:2 | RedstoneDust, 1:1 RedstoneTorchOn = Stick, 1:2 | RedstoneDust, 1:1
@ -286,6 +330,8 @@ MelonSeeds = MelonSlice, *
PumpkinSeeds, 4 = Pumpkin, * PumpkinSeeds, 4 = Pumpkin, *
PumpkinPie = Pumpkin, * | Sugar, * | egg, * PumpkinPie = Pumpkin, * | Sugar, * | egg, *
Wheat, 9 = Haybale, * 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 Paper, 3 = Sugarcane, 1:1, 2:1, 3:1
Book = Paper, *, *, * | leather, * Book = Paper, *, *, * | leather, *
Bookandquill = Book, * | feather, * | inksac, * 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 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 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 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 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 GoldIngot = GoldNugget, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
EyeOfEnder = EnderPearl, * | BlazePowder, * 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 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 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 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

View File

@ -10,25 +10,28 @@
# An Item is defined by an Item Type, an amount (and damage) # 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 # The damage is optional, and if not specified it's assumed to be 0
# #
# -Cactus Green: # Cactus Green example:
# 351 : 1 ( : 2 ) # 351 : 2 ( , 1 )
# ItemType : Amount ( : Damage ) # ItemType : Damage ( , Amount )
# or simple use the item name (marked in items.ini):
# CactusGreen ( , 1 )
# #
# #
# **** Recipe and result **** # **** 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 # Write in full:
# ItemType : Amount @ ticks = ItemID : Amount # Cobble : 0 , 1 @ 200 = 1 : 1 , 1
# ItemType : Damage , Amount @ ticks = ItemType : Damage , Amount
# #
# #
# **** Fuel **** # **** Fuel ****
# #
# !17:1 = 300 -> 1 Wood burns for 300 ticks (15 s) # !17:1 = 300 -> 1 Wood burns for 300 ticks (15 s)
# #
# ! 17 : 1 = 300 # ! Wood , 1 = 300
# Fuel ItemType : Amount = ticks # Fuel ItemType , Amount = ticks
# #
#******************************************************# #******************************************************#
@ -39,20 +42,24 @@
#-------------------------- #--------------------------
# Smelting recipes # Smelting recipes
4:1 @ 200 = 1:1 # 1 Cobblestone -> 1 Rock Cobble = Stone
15:1 @ 200 = 265:1 # 1 Iron Ore -> 1 Iron Ingot IronOre = IronIngot
14:1 @ 200 = 266:1 # 1 Gold Ore -> 1 Gold Ingot GoldOre = GoldIngot
153:1 @ 200 = 406:1 # 1 Quartz Ore -> 1 Quartz NetherQuartzOre = NetherQuartz
12:1 @ 200 = 20:1 # 1 Sand -> 1 Glass Sand = Glass
319:1 @ 200 = 320:1 # 1 Raw Pork -> 1 Cooked Pork Pork = CookedPork
363:1 @ 200 = 364:1 # 1 Raw Beef -> 1 Cooked Beef (steak) RawBeef = Steak
365:1 @ 200 = 366:1 # 1 Raw Chicken -> 1 Cooked Chicken RawChicken = CookedChicken
337:1 @ 200 = 336:1 # 1 Clay -> 1 Clay Brick Clay = Brick
82:1 @ 200 = 172:1 # 1 Clay Block -> 1 Hardened Clay ClayBlock = HardenedClay
87:1 @ 200 = 405:1 # 1 NetherRack -> 1 NetherBrick TallGrass = NetherBrickItem
349:1 @ 200 = 350:1 # 1 Raw Fish -> 1 Cooked Fish RawFish = CookedFish
17:1 @ 200 = 263:1:1 # 1 Log -> 1 Charcoal Log = CharCoal
81:1 @ 200 = 351:1:2 # 1 Cactus -> 1 Green Dye Cactus = GreenDye
WetSponge = Sponge
Stonebrick = CrackedStonebrick
RawRabbit = CookedRabbit
RawMutton = CookedMutton
@ -61,31 +68,41 @@
#-------------------------- #--------------------------
# Fuels # Fuels
! 263:1 = 1600 # 1 Coal -> 80 sec ! CharCoal = 1600 # -> 80 sec
! 263:1:1 = 1600 # 1 Charcoal -> 80 sec ! Coal = 1600 # -> 80 sec
! 126:1 = 15 # 1 Halfslab -> 7.5 sec ! WoodenSlab = 15 # -> 7.5 sec
! 5:1 = 300 # 1 Planks -> 15 sec ! Planks = 300 # -> 15 sec
! 280:1 = 100 # 1 Stick -> 5 sec ! Stick = 100 # -> 5 sec
! 85:1 = 300 # 1 Fence -> 15 sec ! Fence = 300 # -> 15 sec
! 53:1 = 300 # 1 Wooden Stairs -> 15 sec ! SpruceFence = 300 # -> 15 sec
! 58:1 = 300 # 1 Crafting Table -> 15 sec ! BirchFence = 300 # -> 15 sec
! 47:1 = 300 # 1 Bookshelf -> 15 sec ! JungleFence = 300 # -> 15 sec
! 54:1 = 300 # 1 Chest -> 15 sec ! DarkOakFence = 300 # -> 15 sec
! 84:1 = 300 # 1 Jukebox -> 15 sec ! AcaciaFence = 300 # -> 15 sec
! 327:1 = 20000 # 1 Lava Bucket -> 1000 sec ! WoodStairs = 300 # -> 15 sec
! 17:1 = 300 # 1 Wood -> 15 sec ! Workbench = 300 # -> 15 sec
! 6:1 = 100 # 1 Sapling -> 5 sec ! Bookshelf = 300 # -> 15 sec
! 173:1 = 16000 # 1 Coal Block -> 800 sec ! Chest = 300 # -> 15 sec
! 369:1 = 2400 # 1 Blaze Rod -> 120 sec ! Jukebox = 300 # -> 15 sec
! 25:1 = 300 # 1 Note Block -> 15 sec ! Lavabucket = 20000 # -> 1000 sec
! 151:1 = 300 # 1 Daylight Sensor -> 15 sec ! Log = 300 # -> 15 sec
! 107:1 = 300 # 1 Fence Gate -> 15 sec ! Sapling = 100 # -> 5 sec
! 167:1 = 300 # 1 Trapdoor -> 15 sec ! CoalBlock = 16000 # -> 800 sec
! 146:1 = 300 # 1 Trapped Chest -> 15 sec ! BlazeRod = 2400 # -> 120 sec
! 72:1 = 300 # 1 Pressure Plate -> 15 sec ! NoteBlock = 300 # -> 15 sec
! 270:1 = 200 # 1 Wooden Pickaxe -> 10 sec ! DaylightSensor = 300 # -> 15 sec
! 271:1 = 200 # 1 Wooden Axe -> 10 sec ! FenceGate = 300 # -> 15 sec
! 269:1 = 200 # 1 Wooden Shovel -> 10 sec ! SpruceFenceGate = 300 # -> 15 sec
! 290:1 = 200 # 1 Wooden Hoe -> 10 sec ! BirchFenceGate = 300 # -> 15 sec
! 268:1 = 200 # 1 Wooden Sword -> 10 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

View File

@ -1,9 +1,17 @@
[Items] [Items]
air=0 air=0
rock=1 rock=1
granite=1:1
polishedgranite=1:2
diorite=1:3
polisheddiorite=1:4
andesite=1:5
polishedandesite=1:6
stone=1 stone=1
grass=2 grass=2
dirt=3 dirt=3
coarseddirt=3:1
podzol=3:2
cobblestone=4 cobblestone=4
cobble=4 cobble=4
planks=5 planks=5
@ -17,6 +25,11 @@ birchplanks=5:2
lightplanks=5:2 lightplanks=5:2
jungleplanks=5:3 jungleplanks=5:3
redplanks=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. ; Obsolete: do not use "wood", as its meaning is not clear - wiki uses log as wood, we use planks as wood.
wood=5 wood=5
@ -40,6 +53,7 @@ stilllava=11
slava=11 slava=11
stationarylava=11 stationarylava=11
sand=12 sand=12
redsand=12:1
gravel=13 gravel=13
goldore=14 goldore=14
ironore=15 ironore=15
@ -64,6 +78,7 @@ spruceleaves=18:1
birchleaves=18:2 birchleaves=18:2
jungleleaves=18:3 jungleleaves=18:3
sponge=19 sponge=19
wetsponge=19:1
glass=20 glass=20
lapisore=21 lapisore=21
lapisblock=22 lapisblock=22
@ -169,6 +184,7 @@ torch=50
fire=51 fire=51
mobspawner=52 mobspawner=52
woodstairs=53 woodstairs=53
oakwoodstairs=53
chest=54 chest=54
redstonedust=55 redstonedust=55
redstonewire=55 redstonewire=55
@ -274,13 +290,47 @@ redstonelamp=123
redstonelampoff=123 redstonelampoff=123
redstonelampon=124 redstonelampon=124
woodendoubleslab=125 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 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 sandstonestairs=128
emeraldore=129 emeraldore=129
enderchest=130 enderchest=130
tripwirehook=131 tripwirehook=131
tripwire=132 tripwire=132
emeraldblock=133 emeraldblock=133
coniferwoodstairs=134
pinewoodstairs=134
sprucewoodstairs=134
darkwoodstairs=134
birchwoodstairs=135
whitewoodstairs=135
junglewoodstairs=136
commandblock=137 commandblock=137
beacon=138 beacon=138
cobblestonewall=139 cobblestonewall=139
@ -343,12 +393,54 @@ brownstainedglasspane=160:12
greenstainedglasspane=160:13 greenstainedglasspane=160:13
redstainedglasspane=160:14 redstainedglasspane=160:14
blackstainedglasspane=160:15 blackstainedglasspane=160:15
acaciawood=162 acacialeaves=161
darkoakwood=162:1 bigoakleaves=161:1
acaciawoodenstairs=163 darkoakleaves=161:1
darkoakwoodenstairs=164 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 haybale=170
carpet=171 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 ironshovel=256
ironspade=256 ironspade=256
ironpickaxe=257 ironpickaxe=257
@ -596,13 +688,52 @@ netherbrickitem=405
netherquartz=406 netherquartz=406
tntminecart=407 tntminecart=407
hopperminecart=408 hopperminecart=408
prismarineshard=409
prismarinecrystals=410
rawrabbit=411
cookedrabbit=412
rabbitstew=413
rabbitsoup=413
rabbitsfood=414
rabbithide=415
armorstand=416
ironhorsearmor=417 ironhorsearmor=417
goldhorsearmor=418 goldhorsearmor=418
diamondhorsearmor=419 diamondhorsearmor=419
lead=420 lead=420
nametag=421 nametag=421
commandblockminecart=422 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 goldrecord=2256
greenrecord=2257 greenrecord=2257
blocksrecord=2258 blocksrecord=2258

View File

@ -3,7 +3,7 @@
#include <memory> #include <memory>
template<class T> template <class T>
class cAllocationPool class cAllocationPool
{ {
public: public:
@ -34,7 +34,7 @@ public:
/** Allocates memory storing unused elements in a linked list. Keeps at least NumElementsInReserve /** Allocates memory storing unused elements in a linked list. Keeps at least NumElementsInReserve
elements in the list unless malloc fails so that the program has a reserve to handle OOM.**/ elements in the list unless malloc fails so that the program has a reserve to handle OOM.**/
template<class T, size_t NumElementsInReserve> template <class T, size_t NumElementsInReserve>
class cListAllocationPool : public cAllocationPool<T> class cListAllocationPool : public cAllocationPool<T>
{ {
public: public:

View File

@ -301,11 +301,11 @@ static int tolua_cFile_GetFolderContents(lua_State * tolua_S)
template< template <
class Ty1, class Ty1,
class Ty2, class Ty2,
bool (Ty1::*Func1)(const AString &, cItemCallback<Ty2> &) bool (Ty1::*Func1)(const AString &, cItemCallback<Ty2> &)
> >
static int tolua_DoWith(lua_State* tolua_S) static int tolua_DoWith(lua_State* tolua_S)
{ {
int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
@ -395,7 +395,7 @@ static int tolua_DoWith(lua_State* tolua_S)
template< template <
class Ty1, class Ty1,
class Ty2, class Ty2,
bool (Ty1::*Func1)(int, cItemCallback<Ty2> &) bool (Ty1::*Func1)(int, cItemCallback<Ty2> &)
@ -485,7 +485,7 @@ static int tolua_DoWithID(lua_State* tolua_S)
template< template <
class Ty1, class Ty1,
class Ty2, class Ty2,
bool (Ty1::*Func1)(int, int, int, cItemCallback<Ty2> &) bool (Ty1::*Func1)(int, int, int, cItemCallback<Ty2> &)
@ -580,7 +580,7 @@ static int tolua_DoWithXYZ(lua_State* tolua_S)
template< template <
class Ty1, class Ty1,
class Ty2, class Ty2,
bool (Ty1::*Func1)(int, int, cItemCallback<Ty2> &) bool (Ty1::*Func1)(int, int, cItemCallback<Ty2> &)
@ -676,7 +676,7 @@ static int tolua_ForEachInChunk(lua_State * tolua_S)
template< template <
class Ty1, class Ty1,
class Ty2, class Ty2,
bool (Ty1::*Func1)(cItemCallback<Ty2> &) bool (Ty1::*Func1)(cItemCallback<Ty2> &)
@ -2663,7 +2663,7 @@ static int tolua_cRoot_GetFurnaceRecipe(lua_State * tolua_S)
// Get the recipe for the input // Get the recipe for the input
cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe();
const cFurnaceRecipe::Recipe * Recipe = FR->GetRecipeFrom(*Input); const cFurnaceRecipe::cRecipe * Recipe = FR->GetRecipeFrom(*Input);
if (Recipe == NULL) if (Recipe == NULL)
{ {
// There is no such furnace recipe for this input, return no value // There is no such furnace recipe for this input, return no value

View File

@ -28,7 +28,7 @@ typedef void (CombinatorFunc)(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLE
// This wild construct allows us to pass a function argument and still have it inlined by the compiler :) // This wild construct allows us to pass a function argument and still have it inlined by the compiler :)
/// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function /// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function
template<bool MetasValid, CombinatorFunc Combinator> template <bool MetasValid, CombinatorFunc Combinator>
void InternalMergeBlocks( void InternalMergeBlocks(
BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes, BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes,
NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas, NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas,
@ -74,7 +74,7 @@ void InternalMergeBlocks(
/// Combinator used for cBlockArea::msOverwrite merging /// Combinator used for cBlockArea::msOverwrite merging
template<bool MetaValid> template <bool MetaValid>
void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{ {
a_DstType = a_SrcType; a_DstType = a_SrcType;
@ -89,7 +89,7 @@ void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLE
/// Combinator used for cBlockArea::msFillAir merging /// Combinator used for cBlockArea::msFillAir merging
template<bool MetaValid> template <bool MetaValid>
void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{ {
if (a_DstType == E_BLOCK_AIR) if (a_DstType == E_BLOCK_AIR)
@ -108,7 +108,7 @@ void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETY
/// Combinator used for cBlockArea::msImprint merging /// Combinator used for cBlockArea::msImprint merging
template<bool MetaValid> template <bool MetaValid>
void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{ {
if (a_SrcType != E_BLOCK_AIR) if (a_SrcType != E_BLOCK_AIR)
@ -127,7 +127,7 @@ void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETY
/// Combinator used for cBlockArea::msLake merging /// Combinator used for cBlockArea::msLake merging
template<bool MetaValid> template <bool MetaValid>
void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{ {
// Sponge is the NOP block // Sponge is the NOP block
@ -201,7 +201,7 @@ void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE
/** Combinator used for cBlockArea::msSpongePrint merging */ /** Combinator used for cBlockArea::msSpongePrint merging */
template<bool MetaValid> template <bool MetaValid>
void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{ {
// Sponge overwrites nothing, everything else overwrites anything // Sponge overwrites nothing, everything else overwrites anything
@ -220,7 +220,7 @@ void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBB
/** Combinator used for cBlockArea::msDifference merging */ /** Combinator used for cBlockArea::msDifference merging */
template<bool MetaValid> template <bool MetaValid>
void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{ {
if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta))) if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta)))
@ -246,7 +246,7 @@ void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBL
/** Combinator used for cBlockArea::msMask merging */ /** Combinator used for cBlockArea::msMask merging */
template<bool MetaValid> template <bool MetaValid>
void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{ {
// If the blocks are the same, keep the dest; otherwise replace with air // If the blocks are the same, keep the dest; otherwise replace with air
@ -2121,7 +2121,7 @@ void cBlockArea::RelSetData(
template<bool MetasValid> template <bool MetasValid>
void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas) void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas)
{ {
// Block types are compulsory, block metas are voluntary // Block types are compulsory, block metas are voluntary

View File

@ -362,7 +362,7 @@ protected:
NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
); );
template<bool MetasValid> template <bool MetasValid>
void MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas); void MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas);
// tolua_begin // tolua_begin
} ; } ;

View File

@ -13,6 +13,7 @@
#include "../Root.h" #include "../Root.h"
#include "../Server.h" // ExecuteConsoleCommand() #include "../Server.h" // ExecuteConsoleCommand()
#include "../Chunk.h" #include "../Chunk.h"
#include "../ChatColor.h"
@ -187,12 +188,11 @@ void cCommandBlockEntity::SaveToJson(Json::Value & a_Value)
void cCommandBlockEntity::Execute() 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 : class CommandBlockOutCb :
@ -206,15 +206,28 @@ void cCommandBlockEntity::Execute()
virtual void Out(const AString & a_Text) virtual void Out(const AString & a_Text)
{ {
// Overwrite field // 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); } CmdBlockOutCb(this);
LOGD("cCommandBlockEntity: Executing command %s", m_Command.c_str()); // Administrator commands are not executable by command blocks:
if (
cServer * Server = cRoot::Get()->GetServer(); (m_Command != "stop") &&
(m_Command != "restart") &&
Server->ExecuteConsoleCommand(m_Command, CmdBlockOutCb); (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. // TODO 2014-01-18 xdot: Update the signal strength.
m_Result = 0; m_Result = 0;

View File

@ -105,7 +105,7 @@ protected:
NIBBLETYPE m_BlockMeta; NIBBLETYPE m_BlockMeta;
/// The recipe for the current input slot /// The recipe for the current input slot
const cFurnaceRecipe::Recipe * m_CurrentRecipe; const cFurnaceRecipe::cRecipe * m_CurrentRecipe;
/// The item that is being smelted /// The item that is being smelted
cItem m_LastInput; cItem m_LastInput;

View File

@ -40,14 +40,15 @@ public:
) override ) override
{ {
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
NIBBLETYPE HighBits = a_BlockMeta & 0x0c; // Only highest two bits are preserved NIBBLETYPE Meta = (NIBBLETYPE)a_Player->GetEquippedItem().m_ItemDamage;
int Direction = (int)floor(a_Player->GetYaw() * 4.0 / 360.0 + 1.5) & 0x3; int Direction = (int)floor(a_Player->GetYaw() * 4.0 / 360.0 + 1.5) & 0x3;
switch (Direction) switch (Direction)
{ {
case 0: a_BlockMeta = 0x2 | HighBits; break; case 0: a_BlockMeta = 0x2 | Meta << 2; break;
case 1: a_BlockMeta = 0x3 | HighBits; break; case 1: a_BlockMeta = 0x3 | Meta << 2; break;
case 2: a_BlockMeta = 0x0 | HighBits; break; case 2: a_BlockMeta = 0x0 | Meta << 2; break;
case 3: a_BlockMeta = 0x1 | HighBits; break; case 3: a_BlockMeta = 0x1 | Meta << 2; break;
default: default:
{ {
return false; return false;

View File

@ -431,10 +431,45 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac
else else
{ {
// TODO: Add a proper overridable function for this // 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: // Allow plugins to modify the pickups:
a_BlockPluginInterface.CallHookBlockToPickups(a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups); a_BlockPluginInterface.CallHookBlockToPickups(a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups);

View File

@ -30,18 +30,18 @@ public:
{ {
return; return;
} }
BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); cEnchantments Enchantments = a_Player->GetInventory().GetEquippedItem().m_Enchantments;
if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow)) 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); 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 // This is called later than the real destroying of this ice block
}
} }
} ; } ;

View File

@ -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); a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE);
for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++) 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++) for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++)
{ {

View File

@ -7,7 +7,7 @@
// For example to use in class Foo which should inherit Bar use // For example to use in class Foo which should inherit Bar use
// class Foo : public cClearMetaOnDrop<Bar>; // class Foo : public cClearMetaOnDrop<Bar>;
template<class Base> template <class Base>
class cClearMetaOnDrop : public Base class cClearMetaOnDrop : public Base
{ {
public: public:

View File

@ -20,7 +20,7 @@ Usage:
Inherit from this class providing your base class as Base, the BitMask for the direction bits in bitmask and the masked value for the directions in North, East, South, West. There is also an aptional parameter AssertIfNotMatched. Set this if it is invalid for a block to exist in any other state. Inherit from this class providing your base class as Base, the BitMask for the direction bits in bitmask and the masked value for the directions in North, East, South, West. There is also an aptional parameter AssertIfNotMatched. Set this if it is invalid for a block to exist in any other state.
*/ */
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched = false> template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched = false>
class cMetaRotator : public Base class cMetaRotator : public Base
{ {
public: public:
@ -41,7 +41,7 @@ public:
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched> template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCW(NIBBLETYPE a_Meta) NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCW(NIBBLETYPE a_Meta)
{ {
NIBBLETYPE OtherMeta = a_Meta & (~BitMask); NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
@ -63,7 +63,7 @@ NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatc
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched> template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCCW(NIBBLETYPE a_Meta) NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCCW(NIBBLETYPE a_Meta)
{ {
NIBBLETYPE OtherMeta = a_Meta & (~BitMask); NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
@ -85,7 +85,7 @@ NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatc
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched> template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorXY(NIBBLETYPE a_Meta) NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorXY(NIBBLETYPE a_Meta)
{ {
NIBBLETYPE OtherMeta = a_Meta & (~BitMask); NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
@ -102,7 +102,7 @@ NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatc
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched> template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorYZ(NIBBLETYPE a_Meta) NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorYZ(NIBBLETYPE a_Meta)
{ {
NIBBLETYPE OtherMeta = a_Meta & (~BitMask); NIBBLETYPE OtherMeta = a_Meta & (~BitMask);

View File

@ -256,6 +256,11 @@ set(EXECUTABLE MCServer)
if (MSVC) if (MSVC)
get_directory_property(BINDING_OUTPUTS DIRECTORY "Bindings" DEFINITION BINDING_OUTPUTS) get_directory_property(BINDING_OUTPUTS DIRECTORY "Bindings" DEFINITION BINDING_OUTPUTS)
get_directory_property(BINDING_DEPENDENCIES DIRECTORY "Bindings" DEFINITION BINDING_DEPENDENCIES) 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( ADD_CUSTOM_COMMAND(
OUTPUT ${BINDING_OUTPUTS} OUTPUT ${BINDING_OUTPUTS}
@ -268,7 +273,7 @@ if (MSVC)
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/
# add any new generation dependencies here # add any new generation dependencies here
DEPENDS ${BINDING_DEPENDENCIES} DEPENDS ${BINDINGS_DEPENDENCIES}
) )
endif() endif()

View File

@ -119,6 +119,7 @@ local g_ViolationPatterns =
{"while%(", "Needs a space after \"while\""}, {"while%(", "Needs a space after \"while\""},
{"switch%(", "Needs a space after \"switch\""}, {"switch%(", "Needs a space after \"switch\""},
{"catch%(", "Needs a space after \"catch\""}, {"catch%(", "Needs a space after \"catch\""},
{"template<", "Needs a space after \"template\""},
-- No space after keyword's parenthesis: -- No space after keyword's parenthesis:
{"[^%a#]if %( ", "Remove the space after \"(\""}, {"[^%a#]if %( ", "Remove the space after \"(\""},

View File

@ -296,6 +296,16 @@ void cChunk::SetAllData(cSetChunkData & a_SetChunkData)
} }
m_BlockEntities.clear(); m_BlockEntities.clear();
std::swap(a_SetChunkData.GetBlockEntities(), m_BlockEntities); std::swap(a_SetChunkData.GetBlockEntities(), m_BlockEntities);
// Check that all block entities have a valid blocktype at their respective coords (DEBUG-mode only):
#ifdef _DEBUG
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
{
BLOCKTYPE EntityBlockType = (*itr)->GetBlockType();
BLOCKTYPE WorldBlockType = GetBlock((*itr)->GetRelX(), (*itr)->GetPosY(), (*itr)->GetRelZ());
ASSERT(EntityBlockType == WorldBlockType);
} // for itr - m_BlockEntities
#endif // _DEBUG
// Set all block entities' World variable: // Set all block entities' World variable:
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)

View File

@ -155,7 +155,7 @@ public:
void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients = true); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc. void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients = true); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc.
BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const; BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const;
BLOCKTYPE GetBlock(Vector3i a_cords) const { return GetBlock(a_cords.x, a_cords.y, a_cords.z);} BLOCKTYPE GetBlock(const Vector3i & a_RelCoords) const { return GetBlock(a_RelCoords.x, a_RelCoords.y, a_RelCoords.z); }
void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
void GetBlockInfo (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); void GetBlockInfo (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight);

View File

@ -197,32 +197,32 @@ public:
inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z) inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z)
{ {
ASSERT((a_X >= 0) && (a_X <= Width)); ASSERT((a_X >= 0) && (a_X < Width));
ASSERT((a_Z >= 0) && (a_Z <= Width)); ASSERT((a_Z >= 0) && (a_Z < Width));
return a_HeightMap[a_X + Width * a_Z]; 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) 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_X >= 0) && (a_X < Width));
ASSERT((a_Z >= 0) && (a_Z <= Width)); ASSERT((a_Z >= 0) && (a_Z < Width));
a_HeightMap[a_X + Width * a_Z] = a_Height; a_HeightMap[a_X + Width * a_Z] = a_Height;
} }
inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z) inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z)
{ {
ASSERT((a_X >= 0) && (a_X <= Width)); ASSERT((a_X >= 0) && (a_X < Width));
ASSERT((a_Z >= 0) && (a_Z <= Width)); ASSERT((a_Z >= 0) && (a_Z < Width));
return a_BiomeMap[a_X + Width * a_Z]; return a_BiomeMap[a_X + Width * a_Z];
} }
inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome) 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_X >= 0) && (a_X < Width));
ASSERT((a_Z >= 0) && (a_Z <= Width)); ASSERT((a_Z >= 0) && (a_Z < Width));
a_BiomeMap[a_X + Width * a_Z] = a_Biome; a_BiomeMap[a_X + Width * a_Z] = a_Biome;
} }

View File

@ -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 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); area.SetBlockTypeMeta(bx + x, by + y, bz + z, E_BLOCK_AIR, 0);
a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z)); a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
break; break;
} }
} // switch (BlockType) } // switch (BlockType)
} // for z } // for z
@ -1916,51 +1914,31 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
virtual bool Item(cEntity * a_Entity) override virtual bool Item(cEntity * a_Entity) override
{ {
if (a_Entity->IsPickup()) if (a_Entity->IsPickup() && (a_Entity->GetTicksAlive() < 20))
{ {
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) // If pickup age is smaller than one second, it is invincible (so we don't kill pickups that were just spawned)
return false;
}
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
{
cBoundingBox bbEntity(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight());
if (!m_bbTNT.IsInside(bbEntity)) // If bbEntity is inside bbTNT, not vice versa!
{ {
return false; return false;
} }
}
Vector3d EntityPos = a_Entity->GetPosition(); // 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
cBoundingBox bbEntity(EntityPos, a_Entity->GetWidth() / 2, a_Entity->GetHeight()); a_Entity->TakeDamage(dtExplosion, NULL, (int)((1 / DistanceFromExplosion.Length()) * 6 * m_ExplosionSize), 0);
if (!m_bbTNT.IsInside(bbEntity)) // IsInside actually acts like DoesSurround
{
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());
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);
} }
// Apply force to entities around the explosion - code modified from World.cpp DoExplosionAt() // Apply force to entities around the explosion - code modified from World.cpp DoExplosionAt()
Vector3d distance_explosion = a_Entity->GetPosition() - m_ExplosionPos; DistanceFromExplosion.Normalize();
if (distance_explosion.SqrLength() < 4096.0) DistanceFromExplosion *= m_ExplosionSize * m_ExplosionSize;
{ a_Entity->AddSpeed(DistanceFromExplosion);
distance_explosion.Normalize();
distance_explosion *= m_ExplosionSize * m_ExplosionSize;
a_Entity->AddSpeed(distance_explosion);
}
return false; return false;
} }

View File

@ -1059,7 +1059,8 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
if ( if (
m_Player->IsGameModeCreative() && m_Player->IsGameModeCreative() &&
ItemCategory::IsSword(m_Player->GetInventory().GetEquippedItem().m_ItemType) ItemCategory::IsSword(m_Player->GetInventory().GetEquippedItem().m_ItemType) &&
(m_Player->GetWorld()->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_FIRE)
) )
{ {
// Players can't destroy blocks with a Sword in the hand. // Players can't destroy blocks with a Sword in the hand.

View File

@ -1,4 +1,4 @@

// CraftingRecipes.cpp // CraftingRecipes.cpp
// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes // Interfaces to the cCraftingRecipes class representing the storage of crafting recipes
@ -83,7 +83,7 @@ cItem & cCraftingGrid::GetItem(int x, int y) const
void cCraftingGrid::SetItem(int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth) void cCraftingGrid::SetItem(int x, int y, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth)
{ {
// Accessible through scripting, must verify parameters: // Accessible through scripting, must verify parameters:
if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height)) if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height))
@ -228,7 +228,7 @@ void cCraftingRecipe::Clear(void)
void cCraftingRecipe::SetResult(ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth) void cCraftingRecipe::SetResult(ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth)
{ {
m_Result = cItem(a_ItemType, a_ItemCount, a_ItemHealth); m_Result = cItem(a_ItemType, a_ItemCount, a_ItemHealth);
} }
@ -324,7 +324,11 @@ void cCraftingRecipes::LoadRecipes(void)
return; return;
} }
AString Everything; AString Everything;
f.ReadRestOfFile(Everything); if (!f.ReadRestOfFile(Everything))
{
LOGWARNING("Cannot read file \"crafting.txt\", no crafting recipes will be available!");
return;
}
f.Close(); f.Close();
// Split it into lines, then process each line as a single recipe: // Split it into lines, then process each line as a single recipe:
@ -362,7 +366,10 @@ void cCraftingRecipes::ClearRecipes(void)
void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine) void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine)
{ {
AStringVector Sides = StringSplit(a_RecipeLine, "="); AString RecipeLine(a_RecipeLine);
RecipeLine.erase(std::remove_if(RecipeLine.begin(), RecipeLine.end(), isspace), RecipeLine.end());
AStringVector Sides = StringSplit(RecipeLine, "=");
if (Sides.size() != 2) if (Sides.size() != 2)
{ {
LOGWARNING("crafting.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1); LOGWARNING("crafting.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
@ -388,8 +395,7 @@ void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine
} }
if (ResultSplit.size() > 1) if (ResultSplit.size() > 1)
{ {
Recipe->m_Result.m_ItemCount = atoi(ResultSplit[1].c_str()); if (!StringToInteger<char>(ResultSplit[1].c_str(), Recipe->m_Result.m_ItemCount))
if (Recipe->m_Result.m_ItemCount == 0)
{ {
LOGWARNING("crafting.txt: line %d: Cannot parse result count, ignoring the recipe.", a_LineNum); LOGWARNING("crafting.txt: line %d: Cannot parse result count, ignoring the recipe.", a_LineNum);
LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str());
@ -441,8 +447,7 @@ bool cCraftingRecipes::ParseItem(const AString & a_String, cItem & a_Item)
if (Split.size() > 1) if (Split.size() > 1)
{ {
AString Damage = TrimString(Split[1]); AString Damage = TrimString(Split[1]);
a_Item.m_ItemDamage = atoi(Damage.c_str()); if (!StringToInteger<short>(Damage.c_str(), a_Item.m_ItemDamage))
if ((a_Item.m_ItemDamage == 0) && (Damage.compare("0") != 0))
{ {
// Parsing the number failed // Parsing the number failed
return false; return false;

View File

@ -33,7 +33,7 @@ public:
int GetWidth (void) const {return m_Width; } int GetWidth (void) const {return m_Width; }
int GetHeight(void) const {return m_Height; } int GetHeight(void) const {return m_Height; }
cItem & GetItem (int x, int y) const; cItem & GetItem (int x, int y) const;
void SetItem (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth); void SetItem (int x, int y, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth);
void SetItem (int x, int y, const cItem & a_Item); void SetItem (int x, int y, const cItem & a_Item);
void Clear (void); void Clear (void);
@ -72,13 +72,13 @@ public:
int GetIngredientsHeight(void) const {return m_Ingredients.GetHeight(); } int GetIngredientsHeight(void) const {return m_Ingredients.GetHeight(); }
cItem & GetIngredient (int x, int y) const {return m_Ingredients.GetItem(x, y); } cItem & GetIngredient (int x, int y) const {return m_Ingredients.GetItem(x, y); }
const cItem & GetResult (void) const {return m_Result; } const cItem & GetResult (void) const {return m_Result; }
void SetResult (ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth); void SetResult (ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth);
void SetResult (const cItem & a_Item) void SetResult (const cItem & a_Item)
{ {
m_Result = a_Item; m_Result = a_Item;
} }
void SetIngredient (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth) void SetIngredient (int x, int y, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth)
{ {
m_Ingredients.SetItem(x, y, a_ItemType, a_ItemCount, a_ItemHealth); m_Ingredients.SetItem(x, y, a_ItemType, a_ItemCount, a_ItemHealth);
} }

View File

@ -528,7 +528,7 @@ inline float GetSpecialSignf( float a_Val)
template<class T> inline T Diff(T a_Val1, T a_Val2) template <class T> inline T Diff(T a_Val1, T a_Val2)
{ {
return std::abs(a_Val1 - a_Val2); return std::abs(a_Val1 - a_Val2);
} }

View File

@ -90,6 +90,13 @@ void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFa
// Broadcast arrow hit sound // 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)); 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); 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 // Broadcast successful hit sound
GetWorld()->BroadcastSoundEffect("random.successful_hit", GetPosX(), GetPosY(), GetPosZ(), 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); GetWorld()->BroadcastSoundEffect("random.successful_hit", GetPosX(), GetPosY(), GetPosZ(), 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));

View File

@ -10,6 +10,7 @@
// tolua_begin // tolua_begin
class cArrowEntity : class cArrowEntity :
@ -46,7 +47,7 @@ public:
/// Returns the damage modifier coeff. /// Returns the damage modifier coeff.
double GetDamageCoeff(void) const { return m_DamageCoeff; } double GetDamageCoeff(void) const { return m_DamageCoeff; }
/// Sets the damage modifier coeff /// Sets the damage modifier coeff
void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; } 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 /// If true, the arrow is in the process of being collected - don't go to anyone else
bool m_bIsCollected; bool m_bIsCollected;
/// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air /// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air
Vector3i m_HitBlockPos; Vector3i m_HitBlockPos;

View File

@ -13,6 +13,7 @@
#include "../Tracer.h" #include "../Tracer.h"
#include "Player.h" #include "Player.h"
#include "Items/ItemHandler.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 // 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) // 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)) if ((a_TDI.DamageType == dtAttack) || (a_TDI.DamageType == dtArrowAttack))
{ {
a_TDI.FinalDamage += 2; 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)); 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; m_Health -= (short)a_TDI.FinalDamage;
// TODO: Apply damage to armor // TODO: Apply damage to armor
m_Health = std::max(m_Health, 0); 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 int KnockbackLevel = a_TDI.Attacker->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchKnockback); // More common enchantment
if (KnockbackLevel < 1) if (KnockbackLevel < 1)
@ -1252,6 +1462,8 @@ void cEntity::HandleAir(void)
// See if the entity is /submerged/ water (block above is water) // See if the entity is /submerged/ water (block above is water)
// Get the type of block the entity is standing in: // Get the type of block the entity is standing in:
int RespirationLevel = GetEquippedHelmet().m_Enchantments.GetLevel(cEnchantments::enchRespiration);
if (IsSubmerged()) if (IsSubmerged())
{ {
if (!IsPlayer()) // Players control themselves if (!IsPlayer()) // Players control themselves
@ -1259,6 +1471,11 @@ void cEntity::HandleAir(void)
SetSpeedY(1); // Float in the water SetSpeedY(1); // Float in the water
} }
if (RespirationLevel > 0)
{
((cPawn *)this)->AddEntityEffect(cEntityEffect::effNightVision, 200, 5, 0);
}
if (m_AirLevel <= 0) if (m_AirLevel <= 0)
{ {
// Runs the air tick timer to check whether the player should be damaged // 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 // Set the air back to maximum
m_AirLevel = MAX_AIR_LEVEL; m_AirLevel = MAX_AIR_LEVEL;
m_AirTickTimer = DROWNING_TICKS; m_AirTickTimer = DROWNING_TICKS;
if (RespirationLevel > 0)
{
m_AirTickTimer = DROWNING_TICKS + (RespirationLevel * 15 * 20);
}
} }
} }

View File

@ -55,7 +55,6 @@ void cItemFrame::KilledBy(TakeDamageInfo & a_TDI)
{ {
if (m_Item.IsEmpty()) if (m_Item.IsEmpty())
{ {
SetHealth(0);
super::KilledBy(a_TDI); super::KilledBy(a_TDI);
Destroy(); Destroy();
return; return;

View File

@ -871,11 +871,101 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
return true; return true;
} }
case E_META_RAIL_CURVED_ZM_XM: case E_META_RAIL_CURVED_ZM_XM:
case E_META_RAIL_CURVED_ZM_XP:
case E_META_RAIL_CURVED_ZP_XM:
case E_META_RAIL_CURVED_ZP_XP: case E_META_RAIL_CURVED_ZP_XP:
{ {
// TODO - simply can't be bothered right now Vector3d Distance = MinecartCollisionCallback.GetCollidedEntityPosition() - Vector3d(GetPosX(), 0, GetPosZ());
// Prevent division by small numbers
if (abs(Distance.z) < 0.001)
{
Distance.z = 0.001;
}
/* Check to which side the minecart is to be pushed.
Let's consider a z-x-coordinate system where the minecart is the center (0/0).
The minecart moves along the line x = -z, the perpendicular line to this is x = z.
In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
if (
((Distance.z > 0) && ((Distance.x / Distance.z) >= 1)) ||
((Distance.z < 0) && ((Distance.x / Distance.z) <= 1))
)
{
// Moving -X +Z
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.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.0));
SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
}
}
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.0));
AddSpeedZ(-4 / sqrt(2.0));
}
else
{
// ~ SpeedX > 0 Moving in the "right" direction
SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
}
break;
}
case E_META_RAIL_CURVED_ZM_XP:
case E_META_RAIL_CURVED_ZP_XM:
{
Vector3d Distance = MinecartCollisionCallback.GetCollidedEntityPosition() - Vector3d(GetPosX(), 0, GetPosZ());
// Prevent division by small numbers
if (abs(Distance.z) < 0.001)
{
Distance.z = 0.001;
}
/* Check to which side the minecart is to be pushed.
Let's consider a z-x-coordinate system where the minecart is the center (0/0).
The minecart moves along the line x = z, the perpendicular line to this is x = -z.
In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
if (
((Distance.z > 0) && ((Distance.x / Distance.z) <= -1)) ||
((Distance.z < 0) && ((Distance.x / Distance.z) >= -1))
)
{
// Moving +X +Z
if ((GetSpeedX() * 0.4) < 0.01)
{
// ~ SpeedX <= 0 Immobile or not moving in the "right" direction
AddSpeedX(4 / sqrt(2.0));
AddSpeedZ(4 / sqrt(2.0));
}
else
{
// ~ SpeedX > 0 Moving in the "right" direction
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.0));
AddSpeedZ(-4 / sqrt(2.0));
}
else
{
// ~ SpeedX < 0 Moving in the "right" direction
SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
}
break; break;
} }
default: break; default: break;

View File

@ -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); 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()) if (PickupCombiningCallback.FoundMatchingPickup())
{ {
m_World->BroadcastEntityMetadata(*this); m_World->BroadcastEntityMetadata(*this);

View File

@ -15,6 +15,7 @@
#include "../Chunk.h" #include "../Chunk.h"
#include "../Items/ItemHandler.h" #include "../Items/ItemHandler.h"
#include "../Vector3.h" #include "../Vector3.h"
#include "../FastRandom.h"
#include "../WorldStorage/StatSerializer.h" #include "../WorldStorage/StatSerializer.h"
#include "../CompositeChat.h" #include "../CompositeChat.h"
@ -1795,6 +1796,28 @@ void cPlayer::UseEquippedItem(int a_Amount)
return; 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)) if (GetInventory().DamageEquippedItem(a_Amount))
{ {
m_World->BroadcastSoundEffect("random.break", GetPosX(), GetPosY(), GetPosZ(), 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); m_World->BroadcastSoundEffect("random.break", GetPosX(), GetPosY(), GetPosZ(), 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));

View File

@ -222,7 +222,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a
m_ProjectileKind(a_Kind), m_ProjectileKind(a_Kind),
m_CreatorData( m_CreatorData(
((a_Creator != NULL) ? a_Creator->GetUniqueID() : -1), ((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) 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) : 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), super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height),
m_ProjectileKind(a_Kind), 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) m_IsInGround(false)
{ {
SetSpeed(a_Speed); SetSpeed(a_Speed);

View File

@ -94,14 +94,16 @@ protected:
*/ */
struct CreatorData 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_UniqueID(a_UniqueID),
m_Name(a_Name) m_Name(a_Name),
m_Enchantments(a_Enchantments)
{ {
} }
const int m_UniqueID; const int m_UniqueID;
AString m_Name; AString m_Name;
cEnchantments m_Enchantments;
}; };
/** The type of projectile I am */ /** The type of projectile I am */

View File

@ -12,8 +12,8 @@
typedef std::list< cFurnaceRecipe::Recipe > RecipeList; typedef std::list<cFurnaceRecipe::cRecipe> RecipeList;
typedef std::list< cFurnaceRecipe::Fuel > FuelList; typedef std::list<cFurnaceRecipe::cFuel> FuelList;
@ -30,7 +30,7 @@ struct cFurnaceRecipe::sFurnaceRecipeState
cFurnaceRecipe::cFurnaceRecipe() cFurnaceRecipe::cFurnaceRecipe()
: m_pState( new sFurnaceRecipeState) : m_pState(new sFurnaceRecipeState)
{ {
ReloadRecipes(); ReloadRecipes();
} }
@ -68,12 +68,18 @@ void cFurnaceRecipe::ReloadRecipes(void)
while (std::getline(f, ParsingLine)) while (std::getline(f, ParsingLine))
{ {
LineNum++; LineNum++;
ParsingLine.erase(std::remove_if(ParsingLine.begin(), ParsingLine.end(), isspace), ParsingLine.end()); // Remove ALL whitespace from the line
if (ParsingLine.empty()) if (ParsingLine.empty())
{ {
continue; 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]) switch (ParsingLine[0])
{ {
case '#': case '#':
@ -103,97 +109,131 @@ 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 AString Line(a_Line);
int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0; Line.erase(Line.begin()); // Remove the beginning "!"
AString::size_type BeginPos = 1; // Begin at one after exclamation mark (bang) Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
if ( std::auto_ptr<cItem> Item(new cItem);
!ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID int BurnTime;
!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 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;
}
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;
}
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; return;
} }
// Add to fuel list: // Add to fuel list:
Fuel F; cFuel Fuel;
F.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth); Fuel.In = Item.release();
F.BurnTime = IBurnTime; Fuel.BurnTime = BurnTime;
m_pState->Fuel.push_back(F); m_pState->Fuel.push_back(Fuel);
} }
void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, int a_LineNum) void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum)
{ {
int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0; AString Line(a_Line);
int OItemID = 0, OItemCount = 0, OItemHealth = 0; Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
AString::size_type BeginPos = 0; // Begin at start of line
if ( int CookTime = 200;
!ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID std::auto_ptr<cItem> InputItem(new cItem());
!ReadOptionalNumbers(BeginPos, ":", "@", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health) std::auto_ptr<cItem> OutputItem(new cItem());
!ReadMandatoryNumber(BeginPos, "=", a_Line, a_LineNum, IBurnTime) || // Read item burn time
!ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, OItemID) || // Read result ID const AStringVector & Sides = StringSplit(Line, "=");
!ReadOptionalNumbers(BeginPos, ":", "012456789", a_Line, a_LineNum, OItemCount, OItemHealth, true) // Read result count (and optionally health) - last value 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; return;
} }
// Add to recipe list const AStringVector & InputSplit = StringSplit(Sides[0], "@");
Recipe R; if (!ParseItem(InputSplit[0], *InputItem))
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);
}
void cFurnaceRecipe::PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing)
{
LOGWARN("Error parsing furnace recipes at line %i pos " SIZE_T_FMT ": missing '%s'", a_Line, a_Position, a_CharactersMissing.c_str());
}
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)
{ {
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 (!StringToInteger<int>(InputSplit[1], CookTime))
if (End == AString::npos) {
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;
}
}
if (!ParseItem(Sides[1], *OutputItem))
{
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;
}
cRecipe Recipe;
Recipe.In = InputItem.release();
Recipe.Out = OutputItem.release();
Recipe.CookTime = CookTime;
m_pState->Recipes.push_back(Recipe);
}
bool cFurnaceRecipe::ParseItem(const AString & a_String, cItem & a_Item)
{
AString ItemString = a_String;
const AStringVector & SplitAmount = StringSplit(ItemString, ",");
ItemString = SplitAmount[0];
const AStringVector & SplitMeta = StringSplit(ItemString, ":");
ItemString = SplitMeta[0];
if (!StringToItem(ItemString, a_Item))
{
return false;
}
if (SplitAmount.size() > 1)
{
if (!StringToInteger<char>(SplitAmount[1].c_str(), a_Item.m_ItemCount))
{ {
PrintParseError(a_Line, a_Begin, a_Delimiter);
return false; return false;
} }
} }
// 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)))
{
PrintParseError(a_Line, a_Begin, "number");
return false;
}
a_Value = atoi(a_Text.substr(a_Begin, End - a_Begin).c_str());
a_Begin = End + 1; // Jump over delimiter if (SplitMeta.size() > 1)
{
if (!StringToInteger<short>(SplitMeta[1].c_str(), a_Item.m_ItemDamage))
{
return false;
}
}
return true; return true;
} }
@ -201,84 +241,23 @@ bool cFurnaceRecipe::ReadMandatoryNumber(AString::size_type & a_Begin, const ASt
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)
{
// TODO: replace atoi with std::stoi
AString::size_type End, Begin = a_Begin;
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;
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());
a_Begin = End + 1; // Jump over delimiter
return true;
}
else
{
return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue);
}
}
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);
}
void cFurnaceRecipe::ClearRecipes(void) void cFurnaceRecipe::ClearRecipes(void)
{ {
for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr) for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
{ {
Recipe R = *itr; cRecipe Recipe = *itr;
delete R.In; delete Recipe.In;
R.In = NULL; Recipe.In = NULL;
delete R.Out; delete Recipe.Out;
R.Out = NULL; Recipe.Out = NULL;
} }
m_pState->Recipes.clear(); m_pState->Recipes.clear();
for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr) for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
{ {
Fuel F = *itr; cFuel Fuel = *itr;
delete F.In; delete Fuel.In;
F.In = NULL; Fuel.In = NULL;
} }
m_pState->Fuel.clear(); 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) for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
{ {
const Recipe & R = *itr; const cRecipe & Recipe = *itr;
if ((R.In->m_ItemType == a_Ingredient.m_ItemType) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount)) 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; continue;
} }
else else
{ {
BestRecipe = &R; BestRecipe = &Recipe;
} }
} }
} }
@ -317,16 +296,16 @@ int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const
int BestFuel = 0; int BestFuel = 0;
for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr) for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
{ {
const Fuel & F = *itr; const cFuel & Fuel = *itr;
if ((F.In->m_ItemType == a_Fuel.m_ItemType) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount)) 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; continue;
} }
else else
{ {
BestFuel = F.BurnTime; BestFuel = Fuel.BurnTime;
} }
} }
} }

View File

@ -19,23 +19,23 @@ public:
void ReloadRecipes(void); void ReloadRecipes(void);
struct Fuel struct cFuel
{ {
cItem * In; cItem * In;
int BurnTime; ///< How long this fuel burns, in ticks int BurnTime; ///< How long this fuel burns, in ticks
}; };
struct Recipe struct cRecipe
{ {
cItem * In; cItem * In;
cItem * Out; cItem * Out;
int CookTime; ///< How long this recipe takes to smelt, in ticks int CookTime; ///< How long this recipe takes to smelt, in ticks
}; };
/// Returns a recipe for the specified input, NULL if no recipe found /** Returns a recipe for the specified input, NULL if no recipe found */
const Recipe * GetRecipeFrom(const cItem & a_Ingredient) const; 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; int GetBurnTime(const cItem & a_Fuel) const;
private: private:
@ -43,33 +43,14 @@ private:
/** Parses the fuel contained in the line, adds it to m_pState's fuels. /** Parses the fuel contained in the line, adds it to m_pState's fuels.
Logs a warning to the console on input error. */ 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. /** Parses the recipe contained in the line, adds it to m_pState's recipes.
Logs a warning to the console on input error. */ Logs a warning to the console on input error. */
void AddRecipeFromLine(const AString & a_Line, int a_LineNum); void AddRecipeFromLine(const AString & a_Line, unsigned 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);
/** 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; struct sFurnaceRecipeState;
sFurnaceRecipeState * m_pState; sFurnaceRecipeState * m_pState;

View File

@ -75,7 +75,6 @@ public:
Arrow = NULL; Arrow = NULL;
return; return;
} }
a_Player->GetWorld()->BroadcastSoundEffect("random.bow", a_Player->GetPosX(), a_Player->GetPosY(), a_Player->GetPosZ(), 0.5, (float)Force); a_Player->GetWorld()->BroadcastSoundEffect("random.bow", a_Player->GetPosX(), a_Player->GetPosY(), a_Player->GetPosZ(), 0.5, (float)Force);
if (!a_Player->IsGameModeCreative()) if (!a_Player->IsGameModeCreative())
{ {
@ -83,8 +82,19 @@ public:
{ {
a_Player->GetInventory().RemoveItem(cItem(E_ITEM_ARROW)); a_Player->GetInventory().RemoveItem(cItem(E_ITEM_ARROW));
} }
else
{
Arrow->SetPickupState(cArrowEntity::psNoPickup);
}
a_Player->UseEquippedItem(); a_Player->UseEquippedItem();
} }
if (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchFlame) > 0)
{
Arrow->StartBurning(100);
}
} }
} ; } ;

View File

@ -75,7 +75,7 @@ public:
int FoodLevel; int FoodLevel;
double Saturation; double Saturation;
FoodInfo(int a_FoodLevel, double a_Saturation, int a_PoisonChance = 0) : FoodInfo(int a_FoodLevel, double a_Saturation) :
FoodLevel(a_FoodLevel), FoodLevel(a_FoodLevel),
Saturation(a_Saturation) Saturation(a_Saturation)
{ {

View File

@ -31,7 +31,7 @@ Linearly interpolates values in the array between the equidistant anchor points
Works in-place (input is already present at the correct output coords) Works in-place (input is already present at the correct output coords)
Uses templates to make it possible for the compiler to further optimizer the loops Uses templates to make it possible for the compiler to further optimizer the loops
*/ */
template< template <
int SizeX, int SizeY, // Dimensions of the array int SizeX, int SizeY, // Dimensions of the array
int AnchorStepX, int AnchorStepY, int AnchorStepX, int AnchorStepY,
typename TYPE typename TYPE
@ -83,7 +83,7 @@ void LinearUpscale2DArrayInPlace(TYPE * a_Array)
Linearly interpolates values in the array between the equidistant anchor points (upscales). Linearly interpolates values in the array between the equidistant anchor points (upscales).
Works on two arrays, input is packed and output is to be completely constructed. Works on two arrays, input is packed and output is to be completely constructed.
*/ */
template<typename TYPE> void LinearUpscale2DArray( template <typename TYPE> void LinearUpscale2DArray(
TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY
int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array
TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1)
@ -153,7 +153,7 @@ template<typename TYPE> void LinearUpscale2DArray(
Linearly interpolates values in the array between the equidistant anchor points (upscales). Linearly interpolates values in the array between the equidistant anchor points (upscales).
Works on two arrays, input is packed and output is to be completely constructed. Works on two arrays, input is packed and output is to be completely constructed.
*/ */
template<typename TYPE> void LinearUpscale3DArray( template <typename TYPE> void LinearUpscale3DArray(
TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ
int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array
TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1) TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1)

View File

@ -20,7 +20,7 @@ cQueueFuncs and is used as the default behavior.
*/ */
/// This empty struct allows for the callback functions to be inlined /// This empty struct allows for the callback functions to be inlined
template<class T> template <class T>
struct cQueueFuncs struct cQueueFuncs
{ {
public: public:

View File

@ -158,7 +158,8 @@ cMojangAPI::cMojangAPI(void) :
m_NameToUUIDServer(DEFAULT_NAME_TO_UUID_SERVER), m_NameToUUIDServer(DEFAULT_NAME_TO_UUID_SERVER),
m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS), m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS),
m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER), m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER),
m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS) m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS),
m_RankMgr(NULL)
{ {
} }

View File

@ -979,9 +979,18 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
AString ServerAddress; AString ServerAddress;
short ServerPort; short ServerPort;
UInt32 NextState; UInt32 NextState;
m_Buffer.ReadVarUTF8String(ServerAddress); if (!m_Buffer.ReadVarUTF8String(ServerAddress))
m_Buffer.ReadBEShort(ServerPort); {
m_Buffer.ReadVarInt(NextState); break;
}
if (!m_Buffer.ReadBEShort(ServerPort))
{
break;
}
if (!m_Buffer.ReadVarInt(NextState))
{
break;
}
m_Buffer.CommitRead(); m_Buffer.CommitRead();
m_Protocol = new cProtocol172(m_Client, ServerAddress, (UInt16)ServerPort, NextState); m_Protocol = new cProtocol172(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
return true; return true;
@ -991,9 +1000,18 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
AString ServerAddress; AString ServerAddress;
short ServerPort; short ServerPort;
UInt32 NextState; UInt32 NextState;
m_Buffer.ReadVarUTF8String(ServerAddress); if (!m_Buffer.ReadVarUTF8String(ServerAddress))
m_Buffer.ReadBEShort(ServerPort); {
m_Buffer.ReadVarInt(NextState); break;
}
if (!m_Buffer.ReadBEShort(ServerPort))
{
break;
}
if (!m_Buffer.ReadVarInt(NextState))
{
break;
}
m_Buffer.CommitRead(); m_Buffer.CommitRead();
m_Protocol = new cProtocol176(m_Client, ServerAddress, (UInt16)ServerPort, NextState); m_Protocol = new cProtocol176(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
return true; return true;

View File

@ -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 = ?"); 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.bind(1, a_PlayerUUID);
stmt.executeStep(); // executeStep returns false on no data
if (stmt.isDone()) if (!stmt.executeStep())
{ {
// No data returned from the DB // No data returned from the DB
return AString(); return AString();

View File

@ -468,16 +468,6 @@ void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCall
void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd) 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): // Put the command into a queue (Alleviates FS #363):
cCSLock Lock(m_CSPendingCommands); cCSLock Lock(m_CSPendingCommands);
m_PendingCommands.push_back(cCommand(a_Cmd, new cLogCommandDeleteSelfOutputCallback)); 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) 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") if (a_Cmd == "stop")
{ {
m_bStop = true; m_bStop = true;
return;
} }
else if (a_Cmd == "restart") else if (a_Cmd == "restart")
{ {
m_bRestart = true; m_bRestart = true;
return;
} }
LOG("Executing console command: \"%s\"", a_Cmd.c_str()); LOG("Executing console command: \"%s\"", a_Cmd.c_str());

View File

@ -458,56 +458,80 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
return; return;
} }
// Special handling: "stop" and "restart" are built in // "stop" and "restart" are handled in cRoot::ExecuteConsoleCommand, our caller, due to its access to controlling variables
if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0))
{
return;
}
// "help" and "reload" are to be handled by MCS, so that they work no matter what // "help" and "reload" are to be handled by MCS, so that they work no matter what
if (split[0] == "help") if (split[0] == "help")
{ {
PrintHelp(split, a_Output); PrintHelp(split, a_Output);
a_Output.Finished();
return; return;
} }
else if (split[0] == "reload") else if (split[0] == "reload")
{ {
cPluginManager::Get()->ReloadPlugins(); cPluginManager::Get()->ReloadPlugins();
a_Output.Finished();
return; return;
} }
else if (split[0] == "reloadplugins") else if (split[0] == "reloadplugins")
{ {
cPluginManager::Get()->ReloadPlugins(); cPluginManager::Get()->ReloadPlugins();
a_Output.Out("Plugins reloaded");
a_Output.Finished();
return; return;
} }
else if (split[0] == "load") else if (split[0] == "load")
{ {
if (split.size() > 1) if (split.size() > 1)
{ {
cPluginManager::Get()->LoadPlugin(split[1]); a_Output.Out(cPluginManager::Get()->LoadPlugin(split[1]) ? "Plugin loaded" : "Error occurred loading plugin");
return;
} }
else else
{ {
a_Output.Out("No plugin given! Command: load <pluginname>"); a_Output.Out("Usage: load <pluginname>");
a_Output.Finished();
return;
} }
a_Output.Finished();
return;
} }
else if (split[0] == "unload") else if (split[0] == "unload")
{ {
if (split.size() > 1) if (split.size() > 1)
{ {
cPluginManager::Get()->RemovePlugin(cPluginManager::Get()->GetPlugin(split[1])); cPluginManager::Get()->RemovePlugin(cPluginManager::Get()->GetPlugin(split[1]));
return; a_Output.Out("Plugin unloaded");
} }
else else
{ {
a_Output.Out("No plugin given! Command: unload <pluginname>"); a_Output.Out("Usage: unload <pluginname>");
a_Output.Finished();
return;
} }
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): // 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("chunkstats", NULL, " - Displays detailed chunk memory statistics");
PlgMgr->BindConsoleCommand("load <pluginname>", NULL, " - Adds and enables the specified plugin"); PlgMgr->BindConsoleCommand("load <pluginname>", NULL, " - Adds and enables the specified plugin");
PlgMgr->BindConsoleCommand("unload <pluginname>", NULL, " - Disables 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) #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"); PlgMgr->BindConsoleCommand("dumpmem", NULL, " - Dumps all used memory blocks together with their callstacks into memdump.xml");

View File

@ -5,6 +5,7 @@
#include "Globals.h" #include "Globals.h"
#include "SetChunkData.h" #include "SetChunkData.h"
#include "BlockEntities/BlockEntity.h"
@ -116,3 +117,35 @@ void cSetChunkData::CalculateHeightMap(void)
void cSetChunkData::RemoveInvalidBlockEntities(void)
{
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end();)
{
BLOCKTYPE EntityBlockType = (*itr)->GetBlockType();
BLOCKTYPE WorldBlockType = cChunkDef::GetBlock(m_BlockTypes, (*itr)->GetRelX(), (*itr)->GetPosY(), (*itr)->GetRelZ());
if (EntityBlockType != WorldBlockType)
{
// Bad blocktype, remove the block entity:
LOGD("Block entity blocktype mismatch at {%d, %d, %d}: entity for blocktype %s(%d) in block %s(%d). Deleting the block entity.",
(*itr)->GetPosX(), (*itr)->GetPosY(), (*itr)->GetPosZ(),
ItemTypeToString(EntityBlockType).c_str(), EntityBlockType,
ItemTypeToString(WorldBlockType).c_str(), WorldBlockType
);
cBlockEntityList::iterator itr2 = itr;
itr2++;
delete *itr;
m_BlockEntities.erase(itr);
itr = itr2;
}
else
{
// Good blocktype, keep the block entity:
++itr;
}
} // for itr - m_BlockEntities[]
}

View File

@ -92,6 +92,9 @@ public:
/** Calculates the heightmap based on the contained blocktypes and marks it valid. */ /** Calculates the heightmap based on the contained blocktypes and marks it valid. */
void CalculateHeightMap(void); void CalculateHeightMap(void);
/** Removes the block entities that don't have a proper blocktype at their corresponding coords. */
void RemoveInvalidBlockEntities(void);
protected: protected:
int m_ChunkX; int m_ChunkX;
int m_ChunkZ; int m_ChunkZ;

View File

@ -98,7 +98,7 @@ int cVanillaFluidSimulator::CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int
} }
// Check if block below is passable // Check if block below is passable
if (!a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY - 1, a_RelZ, BlockType, BlockMeta)) if ((a_RelY > 0) && !a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY - 1, a_RelZ, BlockType, BlockMeta))
{ {
return Cost; return Cost;
} }

View File

@ -9,6 +9,7 @@
#pragma once #pragma once
#include <string> #include <string>
#include <limits>
@ -98,6 +99,68 @@ extern int GetBEInt(const char * a_Mem);
/// Writes four bytes to the specified memory location so that they interpret as BigEndian int /// Writes four bytes to the specified memory location so that they interpret as BigEndian int
extern void SetBEInt(char * a_Mem, Int32 a_Value); extern void SetBEInt(char * a_Mem, Int32 a_Value);
/// Parses any integer type. Checks bounds and returns errors out of band.
template <class T>
bool StringToInteger(const AString & a_str, T & a_Num)
{
size_t i = 0;
bool positive = true;
T result = 0;
if (a_str[0] == '+')
{
i++;
}
else if (a_str[0] == '-')
{
i++;
positive = false;
}
if (positive)
{
for (size_t size = a_str.size(); i < size; i++)
{
if ((a_str[i] < '0') || (a_str[i] > '9'))
{
return false;
}
if (std::numeric_limits<T>::max() / 10 < result)
{
return false;
}
result *= 10;
T digit = a_str[i] - '0';
if (std::numeric_limits<T>::max() - digit < result)
{
return false;
}
result += digit;
}
}
else
{
for (size_t size = a_str.size(); i < size; i++)
{
if ((a_str[i] < '0') || (a_str[i] > '9'))
{
return false;
}
if (std::numeric_limits<T>::min() / 10 > result)
{
return false;
}
result *= 10;
T digit = a_str[i] - '0';
if (std::numeric_limits<T>::min() + digit > result)
{
return false;
}
result -= digit;
}
}
a_Num = result;
return true;
}
// If you have any other string helper functions, declare them here // If you have any other string helper functions, declare them here

View File

@ -1226,24 +1226,26 @@ void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_Blo
return; return;
} }
// TODO: Add damage to entities and implement block hardiness // TODO: Implement block hardiness
Vector3d explosion_pos = Vector3d(a_BlockX, a_BlockY, a_BlockZ); Vector3d explosion_pos = Vector3d(a_BlockX, a_BlockY, a_BlockZ);
cVector3iArray BlocksAffected; cVector3iArray BlocksAffected;
m_ChunkMap->DoExplosionAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, 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); BroadcastSoundEffect("random.explode", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 1.0f, 0.6f);
{ {
cCSLock Lock(m_CSPlayers); cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{ {
cClientHandle * ch = (*itr)->GetClientHandle(); cClientHandle * ch = (*itr)->GetClientHandle();
if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) if (ch == NULL)
{ {
continue; continue;
} }
Vector3d distance_explosion = (*itr)->GetPosition() - explosion_pos; Vector3d distance_explosion = (*itr)->GetPosition() - explosion_pos;
if (distance_explosion.SqrLength() < 4096.0) 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; double power = a_ExplosionSize / real_distance;
if (power <= 1) 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); cPluginManager::Get()->CallHookExploded(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData);
} }
@ -3484,14 +3487,16 @@ void cWorld::cChunkGeneratorCallbacks::OnChunkGenerated(cChunkDesc & a_ChunkDesc
cChunkDef::BlockNibbles BlockMetas; cChunkDef::BlockNibbles BlockMetas;
a_ChunkDesc.CompressBlockMetas(BlockMetas); a_ChunkDesc.CompressBlockMetas(BlockMetas);
m_World->QueueSetChunkData(cSetChunkDataPtr(new cSetChunkData( cSetChunkDataPtr SetChunkData(new cSetChunkData(
a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(),
a_ChunkDesc.GetBlockTypes(), BlockMetas, a_ChunkDesc.GetBlockTypes(), BlockMetas,
NULL, NULL, // We don't have lighting, chunk will be lighted when needed NULL, NULL, // We don't have lighting, chunk will be lighted when needed
&a_ChunkDesc.GetHeightMap(), &a_ChunkDesc.GetBiomeMap(), &a_ChunkDesc.GetHeightMap(), &a_ChunkDesc.GetBiomeMap(),
a_ChunkDesc.GetEntities(), a_ChunkDesc.GetBlockEntities(), a_ChunkDesc.GetEntities(), a_ChunkDesc.GetBlockEntities(),
true true
))); ));
SetChunkData->RemoveInvalidBlockEntities();
m_World->QueueSetChunkData(SetChunkData);
} }

View File

@ -615,7 +615,6 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile)
{ {
m_Writer.BeginCompound(""); m_Writer.BeginCompound("");
AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName()); AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName());
Vector3d Pos = a_Projectile->GetPosition();
m_Writer.AddByte("inGround", a_Projectile->IsInGround() ? 1 : 0); m_Writer.AddByte("inGround", a_Projectile->IsInGround() ? 1 : 0);
switch (a_Projectile->GetProjectileKind()) switch (a_Projectile->GetProjectileKind())

View File

@ -396,7 +396,7 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
} // for y } // for y
//*/ //*/
m_World->QueueSetChunkData(cSetChunkDataPtr(new cSetChunkData( cSetChunkDataPtr SetChunkData(new cSetChunkData(
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
BlockTypes, MetaData, BlockTypes, MetaData,
IsLightValid ? BlockLight : NULL, IsLightValid ? BlockLight : NULL,
@ -404,7 +404,8 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
NULL, Biomes, NULL, Biomes,
Entities, BlockEntities, Entities, BlockEntities,
false false
))); ));
m_World->QueueSetChunkData(SetChunkData);
return true; return true;
} }
@ -581,64 +582,28 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
{ {
continue; continue;
} }
int sID = a_NBT.FindChildByName(Child, "id");
if (sID < 0) // Get the BlockEntity's position
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, Child, x, y, z))
{
LOGWARNING("Bad block entity, missing the coords. Will be ignored.");
continue;
}
int RelX = x, RelY = y, RelZ = z, ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ);
// Load the proper BlockEntity type based on the block type:
BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, RelY, RelZ);
NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, RelY, RelZ);
std::auto_ptr<cBlockEntity> be(LoadBlockEntityFromNBT(a_NBT, Child, x, y, z, BlockType, BlockMeta));
if (be.get() == NULL)
{ {
continue; continue;
} }
if (strncmp(a_NBT.GetData(sID), "Beacon", a_NBT.GetDataLength(sID)) == 0)
{ // Add the BlockEntity to the loaded data:
LoadBeaconFromNBT(a_BlockEntities, a_NBT, Child); a_BlockEntities.push_back(be.release());
}
else if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0)
{
LoadChestFromNBT(a_BlockEntities, a_NBT, Child, E_BLOCK_CHEST);
}
else if (strncmp(a_NBT.GetData(sID), "Control", a_NBT.GetDataLength(sID)) == 0)
{
LoadCommandBlockFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Dropper", a_NBT.GetDataLength(sID)) == 0)
{
LoadDropperFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "FlowerPot", a_NBT.GetDataLength(sID)) == 0)
{
LoadFlowerPotFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Furnace", a_NBT.GetDataLength(sID)) == 0)
{
LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child, a_BlockTypes, a_BlockMetas);
}
else if (strncmp(a_NBT.GetData(sID), "Hopper", a_NBT.GetDataLength(sID)) == 0)
{
LoadHopperFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Music", a_NBT.GetDataLength(sID)) == 0)
{
LoadNoteFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "RecordPlayer", a_NBT.GetDataLength(sID)) == 0)
{
LoadJukeboxFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Sign", a_NBT.GetDataLength(sID)) == 0)
{
LoadSignFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Skull", a_NBT.GetDataLength(sID)) == 0)
{
LoadMobHeadFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Trap", a_NBT.GetDataLength(sID)) == 0)
{
LoadDispenserFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "TrappedChest", a_NBT.GetDataLength(sID)) == 0)
{
LoadChestFromNBT(a_BlockEntities, a_NBT, Child, E_BLOCK_TRAPPED_CHEST);
}
// TODO: Other block entities
} // for Child - tag children } // for Child - tag children
} }
@ -646,6 +611,52 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
cBlockEntity * cWSSAnvil::LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a_Tag, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
// Load the specific BlockEntity type:
switch (a_BlockType)
{
// Specific entity loaders:
case E_BLOCK_BEACON: return LoadBeaconFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_CHEST: return LoadChestFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CHEST);
case E_BLOCK_COMMAND_BLOCK: return LoadCommandBlockFromNBT(a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_DISPENSER: return LoadDispenserFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_DROPPER: return LoadDropperFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_FLOWER_POT: return LoadFlowerPotFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_FURNACE: return LoadFurnaceFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FURNACE, a_BlockMeta);
case E_BLOCK_HEAD: return LoadMobHeadFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_HOPPER: return LoadHopperFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_JUKEBOX: return LoadJukeboxFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_LIT_FURNACE: return LoadFurnaceFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LIT_FURNACE, a_BlockMeta);
case E_BLOCK_NOTE_BLOCK: return LoadNoteBlockFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
case E_BLOCK_SIGN_POST: return LoadSignFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SIGN_POST);
case E_BLOCK_TRAPPED_CHEST: return LoadChestFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_TRAPPED_CHEST);
case E_BLOCK_WALLSIGN: return LoadSignFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WALLSIGN);
// Blocktypes that have block entities but don't load their contents from disk:
case E_BLOCK_ENDER_CHEST: return NULL;
}
// All the other blocktypes should have no entities assigned to them. Report an error:
// Get the "id" tag:
int TagID = a_NBT.FindChildByName(a_Tag, "id");
AString TypeName("<unknown>");
if (TagID >= 0)
{
TypeName.assign(a_NBT.GetData(TagID), (size_t)a_NBT.GetDataLength(TagID));
}
LOGINFO("WorldLoader(%s): Block entity mismatch: block type %s (%d), type \"%s\", at {%d, %d, %d}; the entity will be lost.",
m_World->GetName().c_str(),
ItemTypeToString(a_BlockType).c_str(), a_BlockType, TypeName.c_str(),
a_BlockX, a_BlockY, a_BlockZ
);
return NULL;
}
bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx) bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx)
{ {
int Type = a_NBT.FindChildByName(a_TagIdx, "id"); int Type = a_NBT.FindChildByName(a_TagIdx, "id");
@ -656,7 +667,6 @@ bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_
a_Item.m_ItemType = a_NBT.GetShort(Type); a_Item.m_ItemType = a_NBT.GetShort(Type);
if (a_Item.m_ItemType < 0) if (a_Item.m_ItemType < 0)
{ {
LOGD("Encountered an item with negative type (%d). Replacing with an empty item.", a_NBT.GetShort(Type));
a_Item.Empty(); a_Item.Empty();
return true; return true;
} }
@ -754,16 +764,46 @@ void cWSSAnvil::LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a
void cWSSAnvil::LoadBeaconFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) bool cWSSAnvil::CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, const char * a_ExpectedType)
{ {
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); // Check if the given tag is a compound:
int x, y, z; if (a_NBT.GetType(a_TagIdx) != TAG_Compound)
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{ {
return; return false;
} }
std::auto_ptr<cBeaconEntity> Beacon(new cBeaconEntity(x, y, z, m_World)); // Get the "id" tag:
int TagID = a_NBT.FindChildByName(a_TagIdx, "id");
if (TagID < 0)
{
return false;
}
// Compare the value:
if (strncmp(a_NBT.GetData(TagID), a_ExpectedType, (size_t)a_NBT.GetDataLength(TagID)) == 0)
{
return true;
}
LOGWARNING("Block entity type mismatch: exp \"%s\", got \"%s\".",
a_ExpectedType,
AString(a_NBT.GetData(TagID), (size_t)a_NBT.GetDataLength(TagID)).c_str()
);
return false;
}
cBlockEntity * cWSSAnvil::LoadBeaconFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Beacon"))
{
return NULL;
}
std::auto_ptr<cBeaconEntity> Beacon(new cBeaconEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int CurrentLine = a_NBT.FindChildByName(a_TagIdx, "Levels"); int CurrentLine = a_NBT.FindChildByName(a_TagIdx, "Levels");
if (CurrentLine >= 0) if (CurrentLine >= 0)
@ -790,88 +830,128 @@ void cWSSAnvil::LoadBeaconFromNBT(cBlockEntityList & a_BlockEntities, const cPar
LoadItemGridFromNBT(Beacon->GetContents(), a_NBT, Items); LoadItemGridFromNBT(Beacon->GetContents(), a_NBT, Items);
} }
a_BlockEntities.push_back(Beacon.release()); return Beacon.release();
} }
void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType) cBlockEntity * cWSSAnvil::LoadChestFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_ChestBlockType)
{ {
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); // Check if the data has a proper type:
int x, y, z; // TODO: Does vanilla use "TrappedChest" or not? MCWiki says no, but previous code says yes
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) // Ref.: http://minecraft.gamepedia.com/Trapped_Chest
// https://github.com/mc-server/MCServer/blob/d0551e2e0a98a28f31a88d489d17b408e4a7d38d/src/WorldStorage/WSSAnvil.cpp#L637
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Chest") && !CheckBlockEntityType(a_NBT, a_TagIdx, "TrappedChest"))
{ {
return; return NULL;
} }
int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{ {
return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this return NULL; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
} }
std::auto_ptr<cChestEntity> Chest(new cChestEntity(x, y, z, m_World, a_ChestType)); std::auto_ptr<cChestEntity> Chest(new cChestEntity(a_BlockX, a_BlockY, a_BlockZ, m_World, a_ChestBlockType));
LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items); LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items);
a_BlockEntities.push_back(Chest.release()); return Chest.release();
} }
void cWSSAnvil::LoadDispenserFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) cBlockEntity * cWSSAnvil::LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{ {
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); // Check if the data has a proper type:
int x, y, z; if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Control"))
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{ {
return; return NULL;
} }
std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Command");
if (currentLine >= 0)
{
CmdBlock->SetCommand(a_NBT.GetString(currentLine));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "SuccessCount");
if (currentLine >= 0)
{
CmdBlock->SetResult(a_NBT.GetInt(currentLine));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "LastOutput");
if (currentLine >= 0)
{
CmdBlock->SetLastOutput(a_NBT.GetString(currentLine));
}
// TODO 2014-01-18 xdot: Figure out what TrackOutput is and parse it.
return CmdBlock.release();
}
cBlockEntity * cWSSAnvil::LoadDispenserFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Trap"))
{
return NULL;
}
int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{ {
return; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this return NULL; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this
} }
std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(x, y, z, m_World)); std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Dispenser->GetContents(), a_NBT, Items); LoadItemGridFromNBT(Dispenser->GetContents(), a_NBT, Items);
a_BlockEntities.push_back(Dispenser.release()); return Dispenser.release();
} }
void cWSSAnvil::LoadDropperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) cBlockEntity * cWSSAnvil::LoadDropperFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{ {
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); // Check if the data has a proper type:
int x, y, z; if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Dropper"))
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{ {
return; return NULL;
} }
int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{ {
return; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this return NULL; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this
} }
std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(x, y, z, m_World)); std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items); LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items);
a_BlockEntities.push_back(Dropper.release()); return Dropper.release();
} }
void cWSSAnvil::LoadFlowerPotFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) cBlockEntity * cWSSAnvil::LoadFlowerPotFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{ {
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); // Check if the data has a proper type:
int x, y, z; if (!CheckBlockEntityType(a_NBT, a_TagIdx, "FlowerPot"))
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{ {
return; return NULL;
} }
std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(x, y, z, m_World));
std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
short ItemType = 0, ItemData = 0; short ItemType = 0, ItemData = 0;
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Item"); int currentLine = a_NBT.FindChildByName(a_TagIdx, "Item");
@ -887,37 +967,28 @@ void cWSSAnvil::LoadFlowerPotFromNBT(cBlockEntityList & a_BlockEntities, const c
} }
FlowerPot->SetItem(cItem(ItemType, 1, ItemData)); FlowerPot->SetItem(cItem(ItemType, 1, ItemData));
a_BlockEntities.push_back(FlowerPot.release()); return FlowerPot.release();
} }
void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas) cBlockEntity * cWSSAnvil::LoadFurnaceFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{ {
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); // Check if the data has a proper type:
int x, y, z; if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Furnace"))
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{ {
return; return NULL;
} }
int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{ {
return; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this return NULL; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this
} }
// Convert coords to relative: std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World));
int RelX = x;
int RelZ = z;
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(RelX, y, RelZ, ChunkX, ChunkZ);
// Create the furnace entity, with proper BlockType and BlockMeta info:
BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, y, RelZ);
NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, y, RelZ);
std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(x, y, z, BlockType, BlockMeta, m_World));
// Load slots: // Load slots:
for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child)) for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child))
@ -954,86 +1025,121 @@ void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cPa
// Restart cooking: // Restart cooking:
Furnace->ContinueCooking(); Furnace->ContinueCooking();
a_BlockEntities.push_back(Furnace.release()); return Furnace.release();
} }
void cWSSAnvil::LoadHopperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) cBlockEntity * cWSSAnvil::LoadHopperFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{ {
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); // Check if the data has a proper type:
int x, y, z; if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Hopper"))
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{ {
return; return NULL;
} }
int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{ {
return; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this return NULL; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this
} }
std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(x, y, z, m_World)); std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items); LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items);
a_BlockEntities.push_back(Hopper.release()); return Hopper.release();
} }
void cWSSAnvil::LoadJukeboxFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) cBlockEntity * cWSSAnvil::LoadJukeboxFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{ {
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); // Check if the data has a proper type:
int x, y, z; if (!CheckBlockEntityType(a_NBT, a_TagIdx, "RecordPlayer"))
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{ {
return; return NULL;
} }
std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(x, y, z, m_World));
std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int Record = a_NBT.FindChildByName(a_TagIdx, "Record"); int Record = a_NBT.FindChildByName(a_TagIdx, "Record");
if (Record >= 0) if (Record >= 0)
{ {
Jukebox->SetRecord(a_NBT.GetInt(Record)); Jukebox->SetRecord(a_NBT.GetInt(Record));
} }
a_BlockEntities.push_back(Jukebox.release()); return Jukebox.release();
} }
void cWSSAnvil::LoadNoteFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) cBlockEntity * cWSSAnvil::LoadMobHeadFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{ {
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); // Check if the data has a proper type:
int x, y, z; if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Skull"))
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{ {
return; return NULL;
} }
std::auto_ptr<cNoteEntity> Note(new cNoteEntity(x, y, z, m_World));
std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType");
if (currentLine >= 0)
{
MobHead->SetType(static_cast<eMobHeadType>(a_NBT.GetByte(currentLine)));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "Rot");
if (currentLine >= 0)
{
MobHead->SetRotation(static_cast<eMobHeadRotation>(a_NBT.GetByte(currentLine)));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "ExtraType");
if (currentLine >= 0)
{
MobHead->SetOwner(a_NBT.GetString(currentLine));
}
return MobHead.release();
}
cBlockEntity * cWSSAnvil::LoadNoteBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
// Check if the data has a proper type:
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Music"))
{
return NULL;
}
std::auto_ptr<cNoteEntity> NoteBlock(new cNoteEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int note = a_NBT.FindChildByName(a_TagIdx, "note"); int note = a_NBT.FindChildByName(a_TagIdx, "note");
if (note >= 0) if (note >= 0)
{ {
Note->SetPitch(a_NBT.GetByte(note)); NoteBlock->SetPitch(a_NBT.GetByte(note));
} }
a_BlockEntities.push_back(Note.release()); return NoteBlock.release();
} }
void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) cBlockEntity * cWSSAnvil::LoadSignFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
{ {
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); // Check if the data has a proper type:
int x, y, z; if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Sign"))
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{ {
return; return NULL;
} }
std::auto_ptr<cSignEntity> Sign(new cSignEntity(E_BLOCK_SIGN_POST, x, y, z, m_World));
std::auto_ptr<cSignEntity> Sign(new cSignEntity(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1"); int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1");
if (currentLine >= 0) if (currentLine >= 0)
@ -1059,79 +1165,7 @@ void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParse
Sign->SetLine(3, a_NBT.GetString(currentLine)); Sign->SetLine(3, a_NBT.GetString(currentLine));
} }
a_BlockEntities.push_back(Sign.release()); return Sign.release();
}
void cWSSAnvil::LoadMobHeadFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{
return;
}
std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(x, y, z, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType");
if (currentLine >= 0)
{
MobHead->SetType(static_cast<eMobHeadType>(a_NBT.GetByte(currentLine)));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "Rot");
if (currentLine >= 0)
{
MobHead->SetRotation(static_cast<eMobHeadRotation>(a_NBT.GetByte(currentLine)));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "ExtraType");
if (currentLine >= 0)
{
MobHead->SetOwner(a_NBT.GetString(currentLine));
}
a_BlockEntities.push_back(MobHead.release());
}
void cWSSAnvil::LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{
return;
}
std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(x, y, z, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Command");
if (currentLine >= 0)
{
CmdBlock->SetCommand(a_NBT.GetString(currentLine));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "SuccessCount");
if (currentLine >= 0)
{
CmdBlock->SetResult(a_NBT.GetInt(currentLine));
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "LastOutput");
if (currentLine >= 0)
{
CmdBlock->SetLastOutput(a_NBT.GetString(currentLine));
}
// TODO 2014-01-18 xdot: Figure out what TrackOutput is and parse it.
a_BlockEntities.push_back(CmdBlock.release());
} }

View File

@ -125,6 +125,10 @@ protected:
/// Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1) /// Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1)
void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas); void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
/** Loads the data for a block entity from the specified NBT tag.
Returns the loaded block entity, or NULL upon failure. */
cBlockEntity * LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a_Tag, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
/// Loads a cItem contents from the specified NBT tag; returns true if successful. Doesn't load the Slot tag /// Loads a cItem contents from the specified NBT tag; returns true if successful. Doesn't load the Slot tag
bool LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx); bool LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx);
@ -134,18 +138,21 @@ protected:
*/ */
void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0); void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0);
void LoadBeaconFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); /** Returns true iff the "id" child tag inside the specified tag equals the specified expected type. */
void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType); bool CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, const char * a_ExpectedType);
void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); cBlockEntity * LoadBeaconFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
void LoadFlowerPotFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); cBlockEntity * LoadChestFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_ChestBlockType);
void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas); cBlockEntity * LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); cBlockEntity * LoadDispenserFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); cBlockEntity * LoadDropperFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); cBlockEntity * LoadFlowerPotFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); cBlockEntity * LoadFurnaceFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
void LoadMobHeadFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); cBlockEntity * LoadHopperFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
void LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); cBlockEntity * LoadJukeboxFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadMobHeadFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadNoteBlockFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * LoadSignFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_SignBlockType);
void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, size_t a_IDTagLength); void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, size_t a_IDTagLength);

View File

@ -45,7 +45,7 @@ int main(int argc, char ** argv)
BLOCKTYPE * WritePosition = &TestBuffer[WritePosIdx]; BLOCKTYPE * WritePosition = &TestBuffer[WritePosIdx];
memset(TestBuffer, 0x03, sizeof(TestBuffer)); memset(TestBuffer, 0x03, sizeof(TestBuffer));
size_t LastReportedStep = 1; 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) if (idx / 500 != LastReportedStep)
{ {
@ -53,7 +53,7 @@ int main(int argc, char ** argv)
LastReportedStep = idx / 500; 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); Data.CopyBlockTypes(WritePosition, idx, len);