Merge branch 'master' into Inventory
This commit is contained in:
commit
e431bb4e63
@ -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)." },
|
||||
NoCaseCompare = {Params = "string, string", Return = "number", Notes = "Case-insensitive string comparison; returns 0 if the strings are the same"},
|
||||
NormalizeAngleDegrees = { Params = "AngleDegrees", Return = "AngleDegrees", Notes = "Returns the angle, wrapped into the [-180, +180) range." },
|
||||
ReplaceString = {Params = "full-string, to-be-replaced-string, to-replace-string", Notes = "Replaces *each* occurence of to-be-replaced-string in full-string with to-replace-string"},
|
||||
ReplaceString = {Params = "full-string, to-be-replaced-string, to-replace-string", Return = "string", Notes = "Replaces *each* occurence of to-be-replaced-string in full-string with to-replace-string"},
|
||||
RotateBlockFaceCCW = { Params = "{{Globals#BlockFaces|eBlockFace}}", Return = "{{Globals#BlockFaces|eBlockFace}}", Notes = "Returns the {{Globals#BlockFaces|eBlockFace}} that corresponds to the given {{Globals#BlockFaces|eBlockFace}} after rotating it around the Y axis 90 degrees counter-clockwise." },
|
||||
RotateBlockFaceCW = { Params = "{{Globals#BlockFaces|eBlockFace}}", Return = "{{Globals#BlockFaces|eBlockFace}}", Notes = "Returns the {{Globals#BlockFaces|eBlockFace}} that corresponds to the given {{Globals#BlockFaces|eBlockFace}} after rotating it around the Y axis 90 degrees clockwise." },
|
||||
StringSplit = {Params = "string, SeperatorsString", Return = "array table of strings", Notes = "Seperates string into multiple by splitting every time any of the characters in SeperatorsString is encountered."},
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 7cc99285ae5117418f657c3b295ca71f2b75b4f4
|
||||
Subproject commit bd23915df763b182610c6163c5ff2d64a0756560
|
@ -44,6 +44,8 @@ ApplePlanks, 4 = AppleLog, *
|
||||
ConiferPlanks, 4 = ConiferLog, *
|
||||
BirchPlanks, 4 = BirchLog, *
|
||||
JunglePlanks, 4 = JungleLog, *
|
||||
AcaciaPlanks, 4 = AcaciaLog, *
|
||||
DarkOakPlanks, 4 = DarkOakLog, *
|
||||
Stick, 4 = Planks, 2:2, 2:3
|
||||
Torch, 4 = Stick, 1:2 | Coal, 1:1
|
||||
Workbench = Planks, 1:1, 1:2, 2:1, 2:2
|
||||
@ -74,21 +76,62 @@ PillarQuartzBlock = QuartzSlab, 1:1, 1:2
|
||||
ChiseledQuartzBlock, 2 = QuartzBlock, 1:1, 1:2
|
||||
CoalBlock = Coal, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
|
||||
HayBale = Wheat, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
|
||||
SnowBlock = SnowBall, 1:1, 1:2, 2:1, 2:2
|
||||
ClayBlock = Clay, 1:1, 1:2, 2:1, 2:2
|
||||
BrickBlock = Brick, 1:1, 1:2, 2:1, 2:2
|
||||
StoneBrick, 4 = Stone, 1:1, 1:2, 2:1, 2:2
|
||||
BookShelf = Planks, 1:1, 2:1, 3:1, 1:3, 2:3, 3:3 | Book, 1:2, 2:2, 3:2
|
||||
Sandstone, 4 = Sand, 1:1, 1:2, 2:1, 2:2
|
||||
SmoothSandstone, 4 = Sandstone, 1:1, 1:2, 2:1, 2:2
|
||||
OrnamentSandstone = SandstoneSlab, 1:1, 1:2
|
||||
JackOLantern = Pumpkin, 1:1 | Torch, 1:2
|
||||
PolishedGranite, 4 = Granite, 1:1, 1:2, 2:1, 2:2
|
||||
PolishedDiorite, 4 = Diorite, 1:1, 1:2, 2:1, 2:2
|
||||
PolishedAndesite, 4 = Andesite, 1:1, 1:2, 2:1, 2:2
|
||||
CoarsedDirt, 4 = Dirt, 1:1, 2:2 | Gravel, 1:2, 2:1
|
||||
CoarsedDirt, 4 = Gravel, 1:1, 2:2 | Dirt, 1:2, 2:1
|
||||
SlimeBlock = Slimeball, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
|
||||
Prismarine = PrismarineShard, 1:1, 1:2, 2:1, 2:2
|
||||
PrismarineBricks = PrismarineShard, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
|
||||
DarkPrismarine = PrismarineShard, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Inksac, 2:2
|
||||
SeaLantern = PrismarineShard, 1:1, 1:3, 3:1, 3:3 | PrismarineCrystals, 1:2, 2:1, 2:2, 2:3, 3:2
|
||||
RedSandstone, 4 = RedSand, 1:1, 1:2, 2:1, 2:2
|
||||
ChiseledRedSandstone, 4 = RedSandstoneSlab, 1:1, 1:2
|
||||
SmoothRedSandstone, 4 = RedSand, 1:1, 1:2, 2:1, 2:2
|
||||
MossyStoneBrick = Stonebrick, * | Vines, *
|
||||
Leather = RabbitHide, 1:1, 1:2, 2:1, 2:2
|
||||
|
||||
# Slabs:
|
||||
StoneSlab, 6 = Stone, 1:1, 2:1, 3:1
|
||||
SandstoneSlab, 6 = Sandstone, 1:1, 2:1, 3:1
|
||||
WoodSlab, 6 = Planks, 1:1, 2:1, 3:1
|
||||
SpruceWoodSlab, 6 = SprucePlanks, 1:1, 2:1, 3:1
|
||||
BirchWoodSlab, 6 = BirchPlanks, 1:1, 2:1, 3:1
|
||||
JungleWoodSlab, 6 = JunglePlanks, 1:1, 2:1, 3:1
|
||||
AcaciaWoodSlab, 6 = AcaciaPlanks, 1:1, 2:1, 3:1
|
||||
DarkOakWoodSlab, 6 = DarkOakPlanks, 1:1, 2:1, 3:1
|
||||
OakWoodSlab, 6 = OakPlanks, 1:1, 2:1, 3:1
|
||||
CobblestoneSlab, 6 = Cobblestone, 1:1, 2:1, 3:1
|
||||
BrickSlab, 6 = BrickBlock, 1:1, 2:1, 3:1
|
||||
StonebrickSlab, 6 = StoneBrick, 1:1, 2:1, 3:1
|
||||
NetherbrickSlab, 6 = NetherBrick, 1:1, 2:1, 3:1
|
||||
Quartzslab, 6 = QuartzBlock, 1:1, 2:1, 3:1
|
||||
snow, 6 = SnowBlock, 1:1, 2:1, 3:1
|
||||
RedSandstoneSlab, 6 = RedSandstone, 1:1, 2:1, 3:1
|
||||
|
||||
|
||||
# Stairs:
|
||||
WoodStairs, 4 = Planks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
|
||||
WoodStairs, 4 = Planks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
|
||||
SpruceWoodStairs, 4 = SprucePlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
|
||||
SpruceWoodStairs, 4 = SprucePlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
|
||||
BirchWoodStairs, 4 = BirchPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
|
||||
BirchWoodStairs, 4 = BirchPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
|
||||
JungleWoodStairs, 4 = JunglePlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
|
||||
JungleWoodStairs, 4 = JunglePlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
|
||||
AcaciaWoodStairs, 4 = AcaciaPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
|
||||
AcaciaWoodStairs, 4 = AcaciaPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
|
||||
DarkOakWoodStairs, 4 = DarkOakPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
|
||||
DarkOakWoodStairs, 4 = DarkOakPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
|
||||
WoodStairs, 4 = OakPlanks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
|
||||
WoodStairs, 4 = OakPlanks, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
|
||||
cobblestoneStairs, 4 = Cobblestone, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
|
||||
cobblestoneStairs, 4 = Cobblestone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
|
||||
BrickStairs, 4 = BrickBlock, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
|
||||
@ -101,15 +144,8 @@ quartzstairs, 4 = QuartzBlock, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
|
||||
quartzstairs, 4 = QuartzBlock, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
|
||||
StoneBrickStairs, 4 = StoneBrick, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3
|
||||
StoneBrickStairs, 4 = StoneBrick, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
|
||||
SnowBlock = SnowBall, 1:1, 1:2, 2:1, 2:2
|
||||
ClayBlock = Clay, 1:1, 1:2, 2:1, 2:2
|
||||
BrickBlock = Brick, 1:1, 1:2, 2:1, 2:2
|
||||
StoneBrick, 4 = Stone, 1:1, 1:2, 2:1, 2:2
|
||||
BookShelf = Planks, 1:1, 2:1, 3:1, 1:3, 2:3, 3:3 | Book, 1:2, 2:2, 3:2
|
||||
Sandstone, 4 = Sand, 1:1, 1:2, 2:1, 2:2
|
||||
SmoothSandstone, 4 = Sandstone, 1:1, 1:2, 2:1, 2:2
|
||||
OrnamentSandstone = SandstoneSlab, 1:1, 1:2
|
||||
JackOLantern = Pumpkin, 1:1 | Torch, 1:2
|
||||
RedSandstoneStairs, 4 = RedSandstone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
|
||||
RedSandstoneStairs, 4 = RedSandstone, 3:1, 2:2, 3:2, 1:3, 2:3, 3:3
|
||||
|
||||
# Other
|
||||
Carpet = Wool, 1:3, 2:3
|
||||
@ -244,11 +280,19 @@ ActivatorRail, 6 = IronIngot, 1:1, 1:2, 1:3, 3:1, 3:2, 3:3 | Stick, 2:1, 2:3 | R
|
||||
#******************************************************#
|
||||
# Mechanisms
|
||||
#
|
||||
WoodenDoor = Planks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
|
||||
IronDoor = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
|
||||
WoodenDoor, 3 = OakPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
|
||||
SpruceDoor, 3 = SprucePlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
|
||||
BirchDoor, 3 = BirchPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
|
||||
JungleDoor, 3 = JunglePlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
|
||||
AcaciaDoor, 3 = AcaciaPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
|
||||
DarkOakDoor, 3 = DarkOakPlanks, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
|
||||
IronDoor, 3 = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3
|
||||
TrapDoor, 2 = Planks, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2
|
||||
IronTrapDoor = IronIngot, 1:1, 1:2, 2:1, 2:2
|
||||
WoodPlate = Planks, 1:1, 2:1
|
||||
StonePlate = Stone, 1:1, 2:1
|
||||
lightweightedpressureplate = IronIngot, 1:1, 2:1
|
||||
heavyweightedpressureplate = GoldIngot, 1:1, 2:1
|
||||
StoneButton = Stone, 1:1
|
||||
WoodenButton = Planks, 1:1
|
||||
RedstoneTorchOn = Stick, 1:2 | RedstoneDust, 1:1
|
||||
@ -286,6 +330,8 @@ MelonSeeds = MelonSlice, *
|
||||
PumpkinSeeds, 4 = Pumpkin, *
|
||||
PumpkinPie = Pumpkin, * | Sugar, * | egg, *
|
||||
Wheat, 9 = Haybale, *
|
||||
RabbitStew = Cooked Rabbit, 2:1 | Carrot, 1:2 | BakedPotato, 2:2 | BrownMushroom, 3:2 | Bowl, 2:3
|
||||
RabbitStew = Cooked Rabbit, 2:1 | Carrot, 1:2 | BakedPotato, 2:2 | RedMushroom, 3:2 | Bowl, 2:3
|
||||
|
||||
|
||||
|
||||
@ -314,17 +360,49 @@ IronBars, 16 = IronIngot, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2
|
||||
Paper, 3 = Sugarcane, 1:1, 2:1, 3:1
|
||||
Book = Paper, *, *, * | leather, *
|
||||
Bookandquill = Book, * | feather, * | inksac, *
|
||||
Fence, 2 = Stick, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2
|
||||
Fence, 3 = OakPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2
|
||||
SpruceFence, 3 = SprucePlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2
|
||||
BirchFence, 3 = BirchPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2
|
||||
JungleFence, 3 = JunglePlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2
|
||||
DarkOakFence, 3 = DarkOakPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2
|
||||
AcaciaFence, 3 = AcaciaPlanks, 1:1, 1:2, 3:1, 3:2 | Stick, 2:1, 2:2
|
||||
Cobblestonewall, 6 = cobblestone, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
|
||||
mossycobblestonewall, 6 = mossycobblestone, 1:2, 1:3, 2:2, 2:3, 3:2, 3:3
|
||||
NetherBrickFence, 6 = NetherBrick, 1:1, 2:1, 3:1, 1:2, 2:2, 3:2
|
||||
FenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | Planks, 2:1, 2:2
|
||||
FenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | OakPlanks, 2:1, 2:2
|
||||
SpruceFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | SprucePlanks, 2:1, 2:2
|
||||
BirchFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | BirchPlanks, 2:1, 2:2
|
||||
JungleFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | JunglePlanks, 2:1, 2:2
|
||||
DarkOakFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | DarkOakPlanks, 2:1, 2:2
|
||||
AcaciaFenceGate = Stick, 1:1, 1:2, 3:1, 3:2 | AcaciaPlanks, 2:1, 2:2
|
||||
Bed = Planks, 1:2, 2:2, 3:2 | Wool, 1:1, 2:1, 3:1
|
||||
GoldIngot = GoldNugget, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
|
||||
EyeOfEnder = EnderPearl, * | BlazePowder, *
|
||||
Beacon = Glass, 1:1, 1:2, 2:1, 3:1, 3:2 | Obsidian, 1:3, 2:3, 3:3 | NetherStar, 2:2
|
||||
Anvil = IronBlock, 1:1, 2:1, 3:1 | IronIngot, 2:2, 1:3, 2:3, 3:3
|
||||
FlowerPot = Brick, 1:2, 2:3, 3:2
|
||||
ArmorStand = Stick, 1:1, 1:3, 2:1, 2:2, 3:1, 3:3 | StoneSlab, 2:3
|
||||
|
||||
# These are just the basic ones, you can add various shapes and stuff to each of them
|
||||
# ToDo: Add the various shapes (saved in NBT-Tags, not in meta)
|
||||
# Banners:
|
||||
|
||||
WhiteBanner = Stick, 2:3 | WhiteWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
OrangeBanner = Stick, 2:3 | OrangeWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
MagentaBanner = Stick, 2:3 | MagentaWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
LightBlueBanner = Stick, 2:3 | LightBlueWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
YellowBanner = Stick, 2:3 | YellowWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
LimeBanner = Stick, 2:3 | LimeWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
PinkBanner = Stick, 2:3 | PinkWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
GrayBanner = Stick, 2:3 | GrayWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
LightGrayBanner = Stick, 2:3 | LightGrayWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
CyanBanner = Stick, 2:3 | CyanWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
PurpleBanner = Stick, 2:3 | PurpleWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
BlueBanner = Stick, 2:3 | BlueWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
BrownBanner = Stick, 2:3 | BrownWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
GreenBanner = Stick, 2:3 | GreenWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
RedBanner = Stick, 2:3 | RedWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
BlackBanner = Stick, 2:3 | BlackWool, 1:1, 1:2, 2:1, 2:2, 2:1, 2:2
|
||||
|
||||
|
||||
|
||||
|
@ -10,25 +10,28 @@
|
||||
# An Item is defined by an Item Type, an amount (and damage)
|
||||
# The damage is optional, and if not specified it's assumed to be 0
|
||||
#
|
||||
# -Cactus Green:
|
||||
# 351 : 1 ( : 2 )
|
||||
# ItemType : Amount ( : Damage )
|
||||
# Cactus Green example:
|
||||
# 351 : 2 ( , 1 )
|
||||
# ItemType : Damage ( , Amount )
|
||||
# or simple use the item name (marked in items.ini):
|
||||
# CactusGreen ( , 1 )
|
||||
#
|
||||
#
|
||||
# **** Recipe and result ****
|
||||
#
|
||||
# 4:1@200=1:1 -> Produces 1 smooth stone from 1 cobblestone in 200 ticks (10 seconds)
|
||||
# Cobble @ 200 = Stone -> Produces 1 smooth stone from 1 cobblestone in 200 ticks (10 seconds)
|
||||
#
|
||||
# 4 : 1 @ 200 = 1 : 1
|
||||
# ItemType : Amount @ ticks = ItemID : Amount
|
||||
# Write in full:
|
||||
# Cobble : 0 , 1 @ 200 = 1 : 1 , 1
|
||||
# ItemType : Damage , Amount @ ticks = ItemType : Damage , Amount
|
||||
#
|
||||
#
|
||||
# **** Fuel ****
|
||||
#
|
||||
# !17:1 = 300 -> 1 Wood burns for 300 ticks (15 s)
|
||||
#
|
||||
# ! 17 : 1 = 300
|
||||
# Fuel ItemType : Amount = ticks
|
||||
# ! Wood , 1 = 300
|
||||
# Fuel ItemType , Amount = ticks
|
||||
#
|
||||
#******************************************************#
|
||||
|
||||
@ -39,20 +42,24 @@
|
||||
#--------------------------
|
||||
# Smelting recipes
|
||||
|
||||
4:1 @ 200 = 1:1 # 1 Cobblestone -> 1 Rock
|
||||
15:1 @ 200 = 265:1 # 1 Iron Ore -> 1 Iron Ingot
|
||||
14:1 @ 200 = 266:1 # 1 Gold Ore -> 1 Gold Ingot
|
||||
153:1 @ 200 = 406:1 # 1 Quartz Ore -> 1 Quartz
|
||||
12:1 @ 200 = 20:1 # 1 Sand -> 1 Glass
|
||||
319:1 @ 200 = 320:1 # 1 Raw Pork -> 1 Cooked Pork
|
||||
363:1 @ 200 = 364:1 # 1 Raw Beef -> 1 Cooked Beef (steak)
|
||||
365:1 @ 200 = 366:1 # 1 Raw Chicken -> 1 Cooked Chicken
|
||||
337:1 @ 200 = 336:1 # 1 Clay -> 1 Clay Brick
|
||||
82:1 @ 200 = 172:1 # 1 Clay Block -> 1 Hardened Clay
|
||||
87:1 @ 200 = 405:1 # 1 NetherRack -> 1 NetherBrick
|
||||
349:1 @ 200 = 350:1 # 1 Raw Fish -> 1 Cooked Fish
|
||||
17:1 @ 200 = 263:1:1 # 1 Log -> 1 Charcoal
|
||||
81:1 @ 200 = 351:1:2 # 1 Cactus -> 1 Green Dye
|
||||
Cobble = Stone
|
||||
IronOre = IronIngot
|
||||
GoldOre = GoldIngot
|
||||
NetherQuartzOre = NetherQuartz
|
||||
Sand = Glass
|
||||
Pork = CookedPork
|
||||
RawBeef = Steak
|
||||
RawChicken = CookedChicken
|
||||
Clay = Brick
|
||||
ClayBlock = HardenedClay
|
||||
TallGrass = NetherBrickItem
|
||||
RawFish = CookedFish
|
||||
Log = CharCoal
|
||||
Cactus = GreenDye
|
||||
WetSponge = Sponge
|
||||
Stonebrick = CrackedStonebrick
|
||||
RawRabbit = CookedRabbit
|
||||
RawMutton = CookedMutton
|
||||
|
||||
|
||||
|
||||
@ -61,31 +68,41 @@
|
||||
#--------------------------
|
||||
# Fuels
|
||||
|
||||
! 263:1 = 1600 # 1 Coal -> 80 sec
|
||||
! 263:1:1 = 1600 # 1 Charcoal -> 80 sec
|
||||
! 126:1 = 15 # 1 Halfslab -> 7.5 sec
|
||||
! 5:1 = 300 # 1 Planks -> 15 sec
|
||||
! 280:1 = 100 # 1 Stick -> 5 sec
|
||||
! 85:1 = 300 # 1 Fence -> 15 sec
|
||||
! 53:1 = 300 # 1 Wooden Stairs -> 15 sec
|
||||
! 58:1 = 300 # 1 Crafting Table -> 15 sec
|
||||
! 47:1 = 300 # 1 Bookshelf -> 15 sec
|
||||
! 54:1 = 300 # 1 Chest -> 15 sec
|
||||
! 84:1 = 300 # 1 Jukebox -> 15 sec
|
||||
! 327:1 = 20000 # 1 Lava Bucket -> 1000 sec
|
||||
! 17:1 = 300 # 1 Wood -> 15 sec
|
||||
! 6:1 = 100 # 1 Sapling -> 5 sec
|
||||
! 173:1 = 16000 # 1 Coal Block -> 800 sec
|
||||
! 369:1 = 2400 # 1 Blaze Rod -> 120 sec
|
||||
! 25:1 = 300 # 1 Note Block -> 15 sec
|
||||
! 151:1 = 300 # 1 Daylight Sensor -> 15 sec
|
||||
! 107:1 = 300 # 1 Fence Gate -> 15 sec
|
||||
! 167:1 = 300 # 1 Trapdoor -> 15 sec
|
||||
! 146:1 = 300 # 1 Trapped Chest -> 15 sec
|
||||
! 72:1 = 300 # 1 Pressure Plate -> 15 sec
|
||||
! 270:1 = 200 # 1 Wooden Pickaxe -> 10 sec
|
||||
! 271:1 = 200 # 1 Wooden Axe -> 10 sec
|
||||
! 269:1 = 200 # 1 Wooden Shovel -> 10 sec
|
||||
! 290:1 = 200 # 1 Wooden Hoe -> 10 sec
|
||||
! 268:1 = 200 # 1 Wooden Sword -> 10 sec
|
||||
! CharCoal = 1600 # -> 80 sec
|
||||
! Coal = 1600 # -> 80 sec
|
||||
! WoodenSlab = 15 # -> 7.5 sec
|
||||
! Planks = 300 # -> 15 sec
|
||||
! Stick = 100 # -> 5 sec
|
||||
! Fence = 300 # -> 15 sec
|
||||
! SpruceFence = 300 # -> 15 sec
|
||||
! BirchFence = 300 # -> 15 sec
|
||||
! JungleFence = 300 # -> 15 sec
|
||||
! DarkOakFence = 300 # -> 15 sec
|
||||
! AcaciaFence = 300 # -> 15 sec
|
||||
! WoodStairs = 300 # -> 15 sec
|
||||
! Workbench = 300 # -> 15 sec
|
||||
! Bookshelf = 300 # -> 15 sec
|
||||
! Chest = 300 # -> 15 sec
|
||||
! Jukebox = 300 # -> 15 sec
|
||||
! Lavabucket = 20000 # -> 1000 sec
|
||||
! Log = 300 # -> 15 sec
|
||||
! Sapling = 100 # -> 5 sec
|
||||
! CoalBlock = 16000 # -> 800 sec
|
||||
! BlazeRod = 2400 # -> 120 sec
|
||||
! NoteBlock = 300 # -> 15 sec
|
||||
! DaylightSensor = 300 # -> 15 sec
|
||||
! FenceGate = 300 # -> 15 sec
|
||||
! SpruceFenceGate = 300 # -> 15 sec
|
||||
! BirchFenceGate = 300 # -> 15 sec
|
||||
! JungleFenceGate = 300 # -> 15 sec
|
||||
! DarkOakFenceGate = 300 # -> 15 sec
|
||||
! Trapdoor = 300 # -> 15 sec
|
||||
! TrappedChest = 300 # -> 15 sec
|
||||
! WoodPlate = 300 # -> 15 sec
|
||||
! WoodPickaxe = 200 # -> 10 sec
|
||||
! WoodAxe = 200 # -> 10 sec
|
||||
! WoodShovel = 200 # -> 10 sec
|
||||
! WoodHoe = 200 # -> 10 sec
|
||||
! WoodSword = 200 # -> 10 sec
|
||||
|
||||
|
||||
|
@ -1,9 +1,17 @@
|
||||
[Items]
|
||||
air=0
|
||||
rock=1
|
||||
granite=1:1
|
||||
polishedgranite=1:2
|
||||
diorite=1:3
|
||||
polisheddiorite=1:4
|
||||
andesite=1:5
|
||||
polishedandesite=1:6
|
||||
stone=1
|
||||
grass=2
|
||||
dirt=3
|
||||
coarseddirt=3:1
|
||||
podzol=3:2
|
||||
cobblestone=4
|
||||
cobble=4
|
||||
planks=5
|
||||
@ -17,6 +25,11 @@ birchplanks=5:2
|
||||
lightplanks=5:2
|
||||
jungleplanks=5:3
|
||||
redplanks=5:3
|
||||
acaciaplanks=5:4
|
||||
darkoakplanks=5:5
|
||||
bigoakplanks=5:5
|
||||
roofedoakplanks=5:5
|
||||
|
||||
|
||||
; Obsolete: do not use "wood", as its meaning is not clear - wiki uses log as wood, we use planks as wood.
|
||||
wood=5
|
||||
@ -40,6 +53,7 @@ stilllava=11
|
||||
slava=11
|
||||
stationarylava=11
|
||||
sand=12
|
||||
redsand=12:1
|
||||
gravel=13
|
||||
goldore=14
|
||||
ironore=15
|
||||
@ -64,6 +78,7 @@ spruceleaves=18:1
|
||||
birchleaves=18:2
|
||||
jungleleaves=18:3
|
||||
sponge=19
|
||||
wetsponge=19:1
|
||||
glass=20
|
||||
lapisore=21
|
||||
lapisblock=22
|
||||
@ -169,6 +184,7 @@ torch=50
|
||||
fire=51
|
||||
mobspawner=52
|
||||
woodstairs=53
|
||||
oakwoodstairs=53
|
||||
chest=54
|
||||
redstonedust=55
|
||||
redstonewire=55
|
||||
@ -274,13 +290,47 @@ redstonelamp=123
|
||||
redstonelampoff=123
|
||||
redstonelampon=124
|
||||
woodendoubleslab=125
|
||||
appledoublewoodslab=125:0
|
||||
oakwooddoubleslab=125:0
|
||||
coniferwooddoubleslab=125:1
|
||||
pinewooddoubleslab=125:1
|
||||
sprucewooddoubleslab=125:1
|
||||
darkwooddoubleslab=125:1
|
||||
birchwooddoubleslab=125:2
|
||||
whitewooddoubleslab=125:2
|
||||
junglewooddoubleslab=125:3
|
||||
acaciawooddoubleslab=125:4
|
||||
bigoakwooddoubleslab=125:5
|
||||
darkoakwooddoubleslab=125:5
|
||||
roofedwooddoubleslab=125:5
|
||||
woodenslab=126
|
||||
applewoodslab=126:0
|
||||
oakwoodslab=126:0
|
||||
coniferwoodslab=126:1
|
||||
pinewoodslab=126:1
|
||||
sprucewoodslab=126:1
|
||||
darkwoodslab=126:1
|
||||
birchwoodslab=126:2
|
||||
whitewoodslab=126:2
|
||||
junglewoodslab=126:3
|
||||
acaciawoodslab=126:4
|
||||
bigoakwoodslab=126:5
|
||||
darkoakwoodslab=126:5
|
||||
roofedwoodslab=126:5
|
||||
cocoabeans=127
|
||||
sandstonestairs=128
|
||||
emeraldore=129
|
||||
enderchest=130
|
||||
tripwirehook=131
|
||||
tripwire=132
|
||||
emeraldblock=133
|
||||
coniferwoodstairs=134
|
||||
pinewoodstairs=134
|
||||
sprucewoodstairs=134
|
||||
darkwoodstairs=134
|
||||
birchwoodstairs=135
|
||||
whitewoodstairs=135
|
||||
junglewoodstairs=136
|
||||
commandblock=137
|
||||
beacon=138
|
||||
cobblestonewall=139
|
||||
@ -343,12 +393,54 @@ brownstainedglasspane=160:12
|
||||
greenstainedglasspane=160:13
|
||||
redstainedglasspane=160:14
|
||||
blackstainedglasspane=160:15
|
||||
acaciawood=162
|
||||
darkoakwood=162:1
|
||||
acaciawoodenstairs=163
|
||||
darkoakwoodenstairs=164
|
||||
acacialeaves=161
|
||||
bigoakleaves=161:1
|
||||
darkoakleaves=161:1
|
||||
roofedoakleaves=161:1
|
||||
acacialog=162
|
||||
bigoaklog=162:1
|
||||
darkoaklog=162:1
|
||||
roofedoaklog=162:1
|
||||
acaciawoodstairs=163
|
||||
bigoakwoodstiars=164
|
||||
darkoakwoodstairs=164
|
||||
roofedoakwoodstairs=164
|
||||
slimeblock=165
|
||||
irontrapdoor=167
|
||||
prismarine=168
|
||||
prismarinebricks=168:1
|
||||
darkprismarine=168:2
|
||||
sealantern=169
|
||||
haybale=170
|
||||
carpet=171
|
||||
hardenedclay=172
|
||||
redsandstone=179
|
||||
chiseledredsandstone=179:1
|
||||
smoothredsandstone=179:2
|
||||
redsandstonestairs=180
|
||||
redsandstoneslab=182
|
||||
coniferfencegate=183
|
||||
pinefencegate=183
|
||||
sprucefencegate=183
|
||||
darkfencegate=183
|
||||
birchfencegate=184
|
||||
whitefencegate=184
|
||||
junglefencegate=185
|
||||
darkoakfencegate=186
|
||||
bigoakfencegate=186
|
||||
roofedoakfencegate=186
|
||||
acaciafencegate=187
|
||||
coniferfence=188
|
||||
pinefence=188
|
||||
sprucefence=188
|
||||
darkfence=188
|
||||
birchfence=189
|
||||
whitefence=189
|
||||
junglefence=190
|
||||
darkoakfence=191
|
||||
bigoakfence=191
|
||||
roofedoakfence=191
|
||||
acaciafence=192
|
||||
ironshovel=256
|
||||
ironspade=256
|
||||
ironpickaxe=257
|
||||
@ -596,13 +688,52 @@ netherbrickitem=405
|
||||
netherquartz=406
|
||||
tntminecart=407
|
||||
hopperminecart=408
|
||||
prismarineshard=409
|
||||
prismarinecrystals=410
|
||||
rawrabbit=411
|
||||
cookedrabbit=412
|
||||
rabbitstew=413
|
||||
rabbitsoup=413
|
||||
rabbitsfood=414
|
||||
rabbithide=415
|
||||
armorstand=416
|
||||
ironhorsearmor=417
|
||||
goldhorsearmor=418
|
||||
diamondhorsearmor=419
|
||||
lead=420
|
||||
nametag=421
|
||||
commandblockminecart=422
|
||||
|
||||
rawmutton=423
|
||||
cookedmutton=424
|
||||
banner=425
|
||||
blackbanner=415:0
|
||||
redbanner=415:1
|
||||
greenbanner=415:2
|
||||
brownbanner=415:3
|
||||
bluebanner=415:4
|
||||
purplebanner=415:5
|
||||
cyanbanner=415:6
|
||||
silverbanner=415:7
|
||||
lightgraybanner=415:7
|
||||
graybanner=415:8
|
||||
pinkbanner=415:9
|
||||
limebanner=415:10
|
||||
yellowbanner=415:11
|
||||
lightbluebanner=415:12
|
||||
magentabanner=415:13
|
||||
orangebanner=415:14
|
||||
whitebanner=415:15
|
||||
coniferdoor=427
|
||||
pinedoor=427
|
||||
sprucedoor=427
|
||||
darkdoor=427
|
||||
birchdoor=428
|
||||
whitedoor=428
|
||||
jungledoor=429
|
||||
acaciadoor=430
|
||||
bigoakdoor=431
|
||||
darkoakdoor=431
|
||||
roofedoakdoor=431
|
||||
goldrecord=2256
|
||||
greenrecord=2257
|
||||
blocksrecord=2258
|
||||
|
@ -2663,7 +2663,7 @@ static int tolua_cRoot_GetFurnaceRecipe(lua_State * tolua_S)
|
||||
|
||||
// Get the recipe for the input
|
||||
cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe();
|
||||
const cFurnaceRecipe::Recipe * Recipe = FR->GetRecipeFrom(*Input);
|
||||
const cFurnaceRecipe::cRecipe * Recipe = FR->GetRecipeFrom(*Input);
|
||||
if (Recipe == NULL)
|
||||
{
|
||||
// There is no such furnace recipe for this input, return no value
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "../Root.h"
|
||||
#include "../Server.h" // ExecuteConsoleCommand()
|
||||
#include "../Chunk.h"
|
||||
#include "../ChatColor.h"
|
||||
|
||||
|
||||
|
||||
@ -187,13 +188,12 @@ void cCommandBlockEntity::SaveToJson(Json::Value & a_Value)
|
||||
|
||||
void cCommandBlockEntity::Execute()
|
||||
{
|
||||
if (m_World != NULL)
|
||||
{
|
||||
ASSERT(m_World != NULL); // Execute should not be called before the command block is attached to a world
|
||||
|
||||
if (!m_World->AreCommandBlocksEnabled())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
class CommandBlockOutCb :
|
||||
public cCommandOutputCallback
|
||||
@ -206,15 +206,28 @@ void cCommandBlockEntity::Execute()
|
||||
virtual void Out(const AString & a_Text)
|
||||
{
|
||||
// Overwrite field
|
||||
m_CmdBlock->SetLastOutput(a_Text);
|
||||
m_CmdBlock->SetLastOutput(cClientHandle::FormatChatPrefix(m_CmdBlock->GetWorld()->ShouldUseChatPrefixes(), "SUCCESS", cChatColor::Green, cChatColor::White) + a_Text);
|
||||
}
|
||||
} CmdBlockOutCb(this);
|
||||
|
||||
LOGD("cCommandBlockEntity: Executing command %s", m_Command.c_str());
|
||||
|
||||
// Administrator commands are not executable by command blocks:
|
||||
if (
|
||||
(m_Command != "stop") &&
|
||||
(m_Command != "restart") &&
|
||||
(m_Command != "kick") &&
|
||||
(m_Command != "ban") &&
|
||||
(m_Command != "ipban")
|
||||
)
|
||||
{
|
||||
cServer * Server = cRoot::Get()->GetServer();
|
||||
|
||||
LOGD("cCommandBlockEntity: Executing command %s", m_Command.c_str());
|
||||
Server->ExecuteConsoleCommand(m_Command, CmdBlockOutCb);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLastOutput(cClientHandle::FormatChatPrefix(GetWorld()->ShouldUseChatPrefixes(), "FAILURE", cChatColor::Rose, cChatColor::White) + "Adminstration commands can not be executed");
|
||||
LOGD("cCommandBlockEntity: Prevented execution of administration command %s", m_Command.c_str());
|
||||
}
|
||||
|
||||
// TODO 2014-01-18 xdot: Update the signal strength.
|
||||
m_Result = 0;
|
||||
|
@ -105,7 +105,7 @@ protected:
|
||||
NIBBLETYPE m_BlockMeta;
|
||||
|
||||
/// The recipe for the current input slot
|
||||
const cFurnaceRecipe::Recipe * m_CurrentRecipe;
|
||||
const cFurnaceRecipe::cRecipe * m_CurrentRecipe;
|
||||
|
||||
/// The item that is being smelted
|
||||
cItem m_LastInput;
|
||||
|
@ -40,14 +40,15 @@ public:
|
||||
) override
|
||||
{
|
||||
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;
|
||||
|
||||
switch (Direction)
|
||||
{
|
||||
case 0: a_BlockMeta = 0x2 | HighBits; break;
|
||||
case 1: a_BlockMeta = 0x3 | HighBits; break;
|
||||
case 2: a_BlockMeta = 0x0 | HighBits; break;
|
||||
case 3: a_BlockMeta = 0x1 | HighBits; break;
|
||||
case 0: a_BlockMeta = 0x2 | Meta << 2; break;
|
||||
case 1: a_BlockMeta = 0x3 | Meta << 2; break;
|
||||
case 2: a_BlockMeta = 0x0 | Meta << 2; break;
|
||||
case 3: a_BlockMeta = 0x1 | Meta << 2; break;
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
|
@ -431,9 +431,44 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac
|
||||
else
|
||||
{
|
||||
// TODO: Add a proper overridable function for this
|
||||
if (a_Digger != NULL)
|
||||
{
|
||||
cEnchantments Enchantments = a_Digger->GetEquippedWeapon().m_Enchantments;
|
||||
if ((Enchantments.GetLevel(cEnchantments::enchSilkTouch) > 0) && a_Digger->IsPlayer())
|
||||
{
|
||||
switch (m_BlockType)
|
||||
{
|
||||
case E_BLOCK_CAKE:
|
||||
case E_BLOCK_CARROTS:
|
||||
case E_BLOCK_COCOA_POD:
|
||||
case E_BLOCK_DOUBLE_STONE_SLAB:
|
||||
case E_BLOCK_DOUBLE_WOODEN_SLAB:
|
||||
case E_BLOCK_FIRE:
|
||||
case E_BLOCK_FARMLAND:
|
||||
case E_BLOCK_MELON_STEM:
|
||||
case E_BLOCK_MOB_SPAWNER:
|
||||
case E_BLOCK_NETHER_WART:
|
||||
case E_BLOCK_POTATOES:
|
||||
case E_BLOCK_PUMPKIN_STEM:
|
||||
case E_BLOCK_SNOW:
|
||||
case E_BLOCK_SUGARCANE:
|
||||
case E_BLOCK_TALL_GRASS:
|
||||
case E_BLOCK_CROPS:
|
||||
{
|
||||
// Silktouch can't be used for this blocks
|
||||
ConvertToPickups(Pickups, Meta);
|
||||
break;
|
||||
};
|
||||
default: Pickups.Add(m_BlockType, 1, Meta);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Pickups.Add(m_BlockType, 1, Meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow plugins to modify the pickups:
|
||||
a_BlockPluginInterface.CallHookBlockToPickups(a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups);
|
||||
|
@ -31,6 +31,9 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
cEnchantments Enchantments = a_Player->GetInventory().GetEquippedItem().m_Enchantments;
|
||||
if (Enchantments.GetLevel(cEnchantments::enchSilkTouch) == 0)
|
||||
{
|
||||
BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
|
||||
if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow))
|
||||
{
|
||||
@ -40,8 +43,5 @@ public:
|
||||
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
|
||||
}
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -152,7 +152,7 @@ bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE);
|
||||
for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++)
|
||||
{
|
||||
for (int y = a_BlockY - i; y <= a_BlockY + i; y++)
|
||||
for (int y = std::max(a_BlockY - i, 0); y <= std::min(a_BlockY + i, 255); y++)
|
||||
{
|
||||
for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++)
|
||||
{
|
||||
|
@ -257,6 +257,11 @@ if (MSVC)
|
||||
get_directory_property(BINDING_OUTPUTS DIRECTORY "Bindings" DEFINITION BINDING_OUTPUTS)
|
||||
get_directory_property(BINDING_DEPENDENCIES DIRECTORY "Bindings" DEFINITION BINDING_DEPENDENCIES)
|
||||
|
||||
# The paths in BINDING_DEPENDENCIES are relative to the Bindings folder, convert them relative to this folder:
|
||||
foreach (dep ${BINDING_DEPENDENCIES})
|
||||
list (APPEND BINDINGS_DEPENDENCIES "Bindings/${dep}")
|
||||
endforeach(dep)
|
||||
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT ${BINDING_OUTPUTS}
|
||||
|
||||
@ -268,7 +273,7 @@ if (MSVC)
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/
|
||||
|
||||
# add any new generation dependencies here
|
||||
DEPENDS ${BINDING_DEPENDENCIES}
|
||||
DEPENDS ${BINDINGS_DEPENDENCIES}
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -119,6 +119,7 @@ local g_ViolationPatterns =
|
||||
{"while%(", "Needs a space after \"while\""},
|
||||
{"switch%(", "Needs a space after \"switch\""},
|
||||
{"catch%(", "Needs a space after \"catch\""},
|
||||
{"template<", "Needs a space after \"template\""},
|
||||
|
||||
-- No space after keyword's parenthesis:
|
||||
{"[^%a#]if %( ", "Remove the space after \"(\""},
|
||||
|
@ -297,6 +297,16 @@ void cChunk::SetAllData(cSetChunkData & a_SetChunkData)
|
||||
m_BlockEntities.clear();
|
||||
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:
|
||||
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
|
||||
{
|
||||
|
@ -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.
|
||||
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 GetBlockInfo (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight);
|
||||
|
||||
|
@ -197,32 +197,32 @@ public:
|
||||
|
||||
inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z)
|
||||
{
|
||||
ASSERT((a_X >= 0) && (a_X <= Width));
|
||||
ASSERT((a_Z >= 0) && (a_Z <= Width));
|
||||
ASSERT((a_X >= 0) && (a_X < Width));
|
||||
ASSERT((a_Z >= 0) && (a_Z < Width));
|
||||
return a_HeightMap[a_X + Width * a_Z];
|
||||
}
|
||||
|
||||
|
||||
inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height)
|
||||
{
|
||||
ASSERT((a_X >= 0) && (a_X <= Width));
|
||||
ASSERT((a_Z >= 0) && (a_Z <= Width));
|
||||
ASSERT((a_X >= 0) && (a_X < Width));
|
||||
ASSERT((a_Z >= 0) && (a_Z < Width));
|
||||
a_HeightMap[a_X + Width * a_Z] = a_Height;
|
||||
}
|
||||
|
||||
|
||||
inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z)
|
||||
{
|
||||
ASSERT((a_X >= 0) && (a_X <= Width));
|
||||
ASSERT((a_Z >= 0) && (a_Z <= Width));
|
||||
ASSERT((a_X >= 0) && (a_X < Width));
|
||||
ASSERT((a_Z >= 0) && (a_Z < Width));
|
||||
return a_BiomeMap[a_X + Width * a_Z];
|
||||
}
|
||||
|
||||
|
||||
inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome)
|
||||
{
|
||||
ASSERT((a_X >= 0) && (a_X <= Width));
|
||||
ASSERT((a_Z >= 0) && (a_Z <= Width));
|
||||
ASSERT((a_X >= 0) && (a_X < Width));
|
||||
ASSERT((a_Z >= 0) && (a_Z < Width));
|
||||
a_BiomeMap[a_X + Width * a_Z] = a_Biome;
|
||||
}
|
||||
|
||||
|
@ -1880,21 +1880,19 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
|
||||
}
|
||||
else if ((m_World->GetTNTShrapnelLevel() > slNone) && (m_World->GetTickRandomNumber(100) < 20)) // 20% chance of flinging stuff around
|
||||
{
|
||||
if (!cBlockInfo::FullyOccupiesVoxel(Block))
|
||||
// If the block is shrapnel-able, make a falling block entity out of it:
|
||||
if (
|
||||
((m_World->GetTNTShrapnelLevel() == slAll) && cBlockInfo::FullyOccupiesVoxel(Block)) ||
|
||||
((m_World->GetTNTShrapnelLevel() == slGravityAffectedOnly) && ((Block == E_BLOCK_SAND) || (Block == E_BLOCK_GRAVEL)))
|
||||
)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if ((m_World->GetTNTShrapnelLevel() == slGravityAffectedOnly) && ((Block != E_BLOCK_SAND) && (Block != E_BLOCK_GRAVEL)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_World->SpawnFallingBlock(bx + x, by + y + 5, bz + z, Block, area.GetBlockMeta(bx + x, by + y, bz + z));
|
||||
}
|
||||
}
|
||||
|
||||
area.SetBlockTypeMeta(bx + x, by + y, bz + z, E_BLOCK_AIR, 0);
|
||||
a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
|
||||
break;
|
||||
|
||||
}
|
||||
} // switch (BlockType)
|
||||
} // for z
|
||||
@ -1916,51 +1914,31 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
|
||||
|
||||
virtual bool Item(cEntity * a_Entity) override
|
||||
{
|
||||
if (a_Entity->IsPickup())
|
||||
{
|
||||
if (((cPickup *)a_Entity)->GetAge() < 20) // If pickup age is smaller than one second, it is invincible (so we don't kill pickups that were just spawned)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3d EntityPos = a_Entity->GetPosition();
|
||||
cBoundingBox bbEntity(EntityPos, a_Entity->GetWidth() / 2, a_Entity->GetHeight());
|
||||
|
||||
if (!m_bbTNT.IsInside(bbEntity)) // IsInside actually acts like DoesSurround
|
||||
if (a_Entity->IsPickup() && (a_Entity->GetTicksAlive() < 20))
|
||||
{
|
||||
// If pickup age is smaller than one second, it is invincible (so we don't kill pickups that were just spawned)
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3d AbsoluteEntityPos(abs(EntityPos.x), abs(EntityPos.y), abs(EntityPos.z));
|
||||
|
||||
// Work out how far we are from the edge of the TNT's explosive effect
|
||||
AbsoluteEntityPos -= m_ExplosionPos;
|
||||
|
||||
// All to positive
|
||||
AbsoluteEntityPos.x = abs(AbsoluteEntityPos.x);
|
||||
AbsoluteEntityPos.y = abs(AbsoluteEntityPos.y);
|
||||
AbsoluteEntityPos.z = abs(AbsoluteEntityPos.z);
|
||||
|
||||
double FinalDamage = (((1 / AbsoluteEntityPos.x) + (1 / AbsoluteEntityPos.y) + (1 / AbsoluteEntityPos.z)) * 2) * m_ExplosionSize;
|
||||
|
||||
// Clip damage values
|
||||
FinalDamage = Clamp(FinalDamage, 0.0, (double)a_Entity->GetMaxHealth());
|
||||
Vector3d DistanceFromExplosion = a_Entity->GetPosition() - m_ExplosionPos;
|
||||
|
||||
if (!a_Entity->IsTNT() && !a_Entity->IsFallingBlock()) // Don't apply damage to other TNT entities and falling blocks, they should be invincible
|
||||
{
|
||||
a_Entity->TakeDamage(dtExplosion, NULL, (int)FinalDamage, 0);
|
||||
cBoundingBox bbEntity(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight());
|
||||
|
||||
if (!m_bbTNT.IsInside(bbEntity)) // If bbEntity is inside bbTNT, not vice versa!
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that the damage dealt is inversely proportional to the distance to the TNT centre - the closer a player is, the harder they are hit
|
||||
a_Entity->TakeDamage(dtExplosion, NULL, (int)((1 / DistanceFromExplosion.Length()) * 6 * m_ExplosionSize), 0);
|
||||
}
|
||||
|
||||
// Apply force to entities around the explosion - code modified from World.cpp DoExplosionAt()
|
||||
Vector3d distance_explosion = a_Entity->GetPosition() - m_ExplosionPos;
|
||||
if (distance_explosion.SqrLength() < 4096.0)
|
||||
{
|
||||
distance_explosion.Normalize();
|
||||
distance_explosion *= m_ExplosionSize * m_ExplosionSize;
|
||||
|
||||
a_Entity->AddSpeed(distance_explosion);
|
||||
}
|
||||
DistanceFromExplosion.Normalize();
|
||||
DistanceFromExplosion *= m_ExplosionSize * m_ExplosionSize;
|
||||
a_Entity->AddSpeed(DistanceFromExplosion);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1059,7 +1059,8 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
|
||||
|
||||
if (
|
||||
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.
|
||||
|
@ -1,4 +1,4 @@
|
||||
|
||||
|
||||
// CraftingRecipes.cpp
|
||||
|
||||
// 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:
|
||||
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);
|
||||
}
|
||||
@ -324,7 +324,11 @@ void cCraftingRecipes::LoadRecipes(void)
|
||||
return;
|
||||
}
|
||||
AString Everything;
|
||||
f.ReadRestOfFile(Everything);
|
||||
if (!f.ReadRestOfFile(Everything))
|
||||
{
|
||||
LOGWARNING("Cannot read file \"crafting.txt\", no crafting recipes will be available!");
|
||||
return;
|
||||
}
|
||||
f.Close();
|
||||
|
||||
// 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
Recipe->m_Result.m_ItemCount = atoi(ResultSplit[1].c_str());
|
||||
if (Recipe->m_Result.m_ItemCount == 0)
|
||||
if (!StringToInteger<char>(ResultSplit[1].c_str(), Recipe->m_Result.m_ItemCount))
|
||||
{
|
||||
LOGWARNING("crafting.txt: line %d: Cannot parse result count, ignoring the recipe.", a_LineNum);
|
||||
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)
|
||||
{
|
||||
AString Damage = TrimString(Split[1]);
|
||||
a_Item.m_ItemDamage = atoi(Damage.c_str());
|
||||
if ((a_Item.m_ItemDamage == 0) && (Damage.compare("0") != 0))
|
||||
if (!StringToInteger<short>(Damage.c_str(), a_Item.m_ItemDamage))
|
||||
{
|
||||
// Parsing the number failed
|
||||
return false;
|
||||
|
@ -33,7 +33,7 @@ public:
|
||||
int GetWidth (void) const {return m_Width; }
|
||||
int GetHeight(void) const {return m_Height; }
|
||||
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 Clear (void);
|
||||
|
||||
@ -72,13 +72,13 @@ public:
|
||||
int GetIngredientsHeight(void) const {return m_Ingredients.GetHeight(); }
|
||||
cItem & GetIngredient (int x, int y) const {return m_Ingredients.GetItem(x, y); }
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -90,6 +90,13 @@ void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFa
|
||||
|
||||
// Broadcast arrow hit sound
|
||||
m_World->BroadcastSoundEffect("random.bowhit", (double)X, (double)Y, (double)Z, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
|
||||
|
||||
if ((m_World->GetBlock(Hit) == E_BLOCK_TNT) && IsOnFire())
|
||||
{
|
||||
m_World->SetBlock(X, Y, Z, E_BLOCK_AIR, 0);
|
||||
m_World->SpawnPrimedTNT(X, Y, Z);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -103,7 +110,35 @@ void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
|
||||
{
|
||||
Damage += m_World->GetTickRandomNumber(Damage / 2 + 2);
|
||||
}
|
||||
a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1);
|
||||
|
||||
int PowerLevel = m_CreatorData.m_Enchantments.GetLevel(cEnchantments::enchPower);
|
||||
if (PowerLevel > 0)
|
||||
{
|
||||
int ExtraDamage = (int)ceil(0.25 * (PowerLevel + 1));
|
||||
Damage += ExtraDamage;
|
||||
}
|
||||
|
||||
int KnockbackAmount = 1;
|
||||
int PunchLevel = m_CreatorData.m_Enchantments.GetLevel(cEnchantments::enchPunch);
|
||||
if (PunchLevel > 0)
|
||||
{
|
||||
Vector3d LookVector = GetLookVector();
|
||||
Vector3f FinalSpeed = Vector3f(0, 0, 0);
|
||||
switch (PunchLevel)
|
||||
{
|
||||
case 1: FinalSpeed = LookVector * Vector3d(5, 0.3, 5); break;
|
||||
case 2: FinalSpeed = LookVector * Vector3d(8, 0.3, 8); break;
|
||||
default: break;
|
||||
}
|
||||
a_EntityHit.SetSpeed(FinalSpeed);
|
||||
}
|
||||
|
||||
a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, KnockbackAmount);
|
||||
|
||||
if (IsOnFire() && !a_EntityHit.IsSubmerged() && !a_EntityHit.IsSwimming())
|
||||
{
|
||||
a_EntityHit.StartBurning(100);
|
||||
}
|
||||
|
||||
// Broadcast successful hit sound
|
||||
GetWorld()->BroadcastSoundEffect("random.successful_hit", GetPosX(), GetPosY(), GetPosZ(), 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
// tolua_begin
|
||||
|
||||
class cArrowEntity :
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "../Tracer.h"
|
||||
#include "Player.h"
|
||||
#include "Items/ItemHandler.h"
|
||||
#include "../FastRandom.h"
|
||||
|
||||
|
||||
|
||||
@ -316,6 +317,105 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
|
||||
// IsOnGround() only is false if the player is moving downwards
|
||||
// TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain)
|
||||
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))
|
||||
@ -328,13 +428,123 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
Player->GetStatManager().AddValue(statDamageDealt, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5));
|
||||
}
|
||||
|
||||
if (IsPlayer())
|
||||
{
|
||||
double TotalEPF = 0.0;
|
||||
double EPFProtection = 0.00;
|
||||
double EPFFireProtection = 0.00;
|
||||
double EPFBlastProtection = 0.00;
|
||||
double EPFProjectileProtection = 0.00;
|
||||
double EPFFeatherFalling = 0.00;
|
||||
|
||||
const cItem ArmorItems[] = { GetEquippedHelmet(), GetEquippedChestplate(), GetEquippedLeggings(), GetEquippedBoots() };
|
||||
for (size_t i = 0; i < ARRAYCOUNT(ArmorItems); i++)
|
||||
{
|
||||
const cItem & Item = ArmorItems[i];
|
||||
int Level = Item.m_Enchantments.GetLevel(cEnchantments::enchProtection);
|
||||
if (Level > 0)
|
||||
{
|
||||
EPFProtection += (6 + Level * Level) * 0.75 / 3;
|
||||
}
|
||||
|
||||
Level = Item.m_Enchantments.GetLevel(cEnchantments::enchFireProtection);
|
||||
if (Level > 0)
|
||||
{
|
||||
EPFFireProtection += (6 + Level * Level) * 1.25 / 3;
|
||||
}
|
||||
|
||||
Level = Item.m_Enchantments.GetLevel(cEnchantments::enchFeatherFalling);
|
||||
if (Level > 0)
|
||||
{
|
||||
EPFFeatherFalling += (6 + Level * Level) * 2.5 / 3;
|
||||
}
|
||||
|
||||
Level = Item.m_Enchantments.GetLevel(cEnchantments::enchBlastProtection);
|
||||
if (Level > 0)
|
||||
{
|
||||
EPFBlastProtection += (6 + Level * Level) * 1.5 / 3;
|
||||
}
|
||||
|
||||
Level = Item.m_Enchantments.GetLevel(cEnchantments::enchProjectileProtection);
|
||||
if (Level > 0)
|
||||
{
|
||||
EPFProjectileProtection += (6 + Level * Level) * 1.5 / 3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TotalEPF = EPFProtection + EPFFireProtection + EPFFeatherFalling + EPFBlastProtection + EPFProjectileProtection;
|
||||
|
||||
EPFProtection = EPFProtection / TotalEPF;
|
||||
EPFFireProtection = EPFFireProtection / TotalEPF;
|
||||
EPFFeatherFalling = EPFFeatherFalling / TotalEPF;
|
||||
EPFBlastProtection = EPFBlastProtection / TotalEPF;
|
||||
EPFProjectileProtection = EPFProjectileProtection / TotalEPF;
|
||||
|
||||
if (TotalEPF > 25)
|
||||
{
|
||||
TotalEPF = 25;
|
||||
}
|
||||
|
||||
cFastRandom Random;
|
||||
float RandomValue = Random.GenerateRandomInteger(50, 100) * 0.01f;
|
||||
|
||||
TotalEPF = ceil(TotalEPF * RandomValue);
|
||||
|
||||
if (TotalEPF > 20)
|
||||
{
|
||||
TotalEPF = 20;
|
||||
}
|
||||
|
||||
EPFProtection = TotalEPF * EPFProtection;
|
||||
EPFFireProtection = TotalEPF * EPFFireProtection;
|
||||
EPFFeatherFalling = TotalEPF * EPFFeatherFalling;
|
||||
EPFBlastProtection = TotalEPF * EPFBlastProtection;
|
||||
EPFProjectileProtection = TotalEPF * EPFProjectileProtection;
|
||||
|
||||
int RemovedDamage = 0;
|
||||
|
||||
if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtAdmin))
|
||||
{
|
||||
RemovedDamage += (int)ceil(EPFProtection * 0.04 * a_TDI.FinalDamage);
|
||||
}
|
||||
|
||||
if ((a_TDI.DamageType == dtFalling) || (a_TDI.DamageType == dtFall) || (a_TDI.DamageType == dtEnderPearl))
|
||||
{
|
||||
RemovedDamage += (int)ceil(EPFFeatherFalling * 0.04 * a_TDI.FinalDamage);
|
||||
}
|
||||
|
||||
if (a_TDI.DamageType == dtBurning)
|
||||
{
|
||||
RemovedDamage += (int)ceil(EPFFireProtection * 0.04 * a_TDI.FinalDamage);
|
||||
}
|
||||
|
||||
if (a_TDI.DamageType == dtExplosion)
|
||||
{
|
||||
RemovedDamage += (int)ceil(EPFBlastProtection * 0.04 * a_TDI.FinalDamage);
|
||||
}
|
||||
|
||||
if (a_TDI.DamageType == dtProjectile)
|
||||
{
|
||||
RemovedDamage += (int)ceil(EPFBlastProtection * 0.04 * a_TDI.FinalDamage);
|
||||
}
|
||||
|
||||
if (a_TDI.FinalDamage < RemovedDamage)
|
||||
{
|
||||
RemovedDamage = 0;
|
||||
}
|
||||
|
||||
a_TDI.FinalDamage -= RemovedDamage;
|
||||
}
|
||||
|
||||
m_Health -= (short)a_TDI.FinalDamage;
|
||||
|
||||
// TODO: Apply damage to armor
|
||||
|
||||
m_Health = std::max(m_Health, 0);
|
||||
|
||||
if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL)) // Knockback for only players and mobs
|
||||
// Add knockback:
|
||||
if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL))
|
||||
{
|
||||
int KnockbackLevel = a_TDI.Attacker->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchKnockback); // More common enchantment
|
||||
if (KnockbackLevel < 1)
|
||||
@ -1252,6 +1462,8 @@ void cEntity::HandleAir(void)
|
||||
// See if the entity is /submerged/ water (block above is water)
|
||||
// Get the type of block the entity is standing in:
|
||||
|
||||
int RespirationLevel = GetEquippedHelmet().m_Enchantments.GetLevel(cEnchantments::enchRespiration);
|
||||
|
||||
if (IsSubmerged())
|
||||
{
|
||||
if (!IsPlayer()) // Players control themselves
|
||||
@ -1259,6 +1471,11 @@ void cEntity::HandleAir(void)
|
||||
SetSpeedY(1); // Float in the water
|
||||
}
|
||||
|
||||
if (RespirationLevel > 0)
|
||||
{
|
||||
((cPawn *)this)->AddEntityEffect(cEntityEffect::effNightVision, 200, 5, 0);
|
||||
}
|
||||
|
||||
if (m_AirLevel <= 0)
|
||||
{
|
||||
// Runs the air tick timer to check whether the player should be damaged
|
||||
@ -1285,6 +1502,12 @@ void cEntity::HandleAir(void)
|
||||
// Set the air back to maximum
|
||||
m_AirLevel = MAX_AIR_LEVEL;
|
||||
m_AirTickTimer = DROWNING_TICKS;
|
||||
|
||||
if (RespirationLevel > 0)
|
||||
{
|
||||
m_AirTickTimer = DROWNING_TICKS + (RespirationLevel * 15 * 20);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,6 @@ void cItemFrame::KilledBy(TakeDamageInfo & a_TDI)
|
||||
{
|
||||
if (m_Item.IsEmpty())
|
||||
{
|
||||
SetHealth(0);
|
||||
super::KilledBy(a_TDI);
|
||||
Destroy();
|
||||
return;
|
||||
|
@ -871,11 +871,101 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
|
||||
return true;
|
||||
}
|
||||
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:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
default: break;
|
||||
|
@ -150,10 +150,14 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize())) // Don't combine into an already full pickup
|
||||
// Try to combine the pickup with adjacent same-item pickups:
|
||||
if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize())) // Don't combine if already full
|
||||
{
|
||||
// By using a_Chunk's ForEachEntity() instead of cWorld's, pickups don't combine across chunk boundaries.
|
||||
// That is a small price to pay for not having to traverse the entire world for each entity.
|
||||
// The speedup in the tick thread is quite considerable.
|
||||
cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this);
|
||||
m_World->ForEachEntity(PickupCombiningCallback); // Not ForEachEntityInChunk, otherwise pickups don't combine across chunk boundaries
|
||||
a_Chunk.ForEachEntity(PickupCombiningCallback);
|
||||
if (PickupCombiningCallback.FoundMatchingPickup())
|
||||
{
|
||||
m_World->BroadcastEntityMetadata(*this);
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "../Chunk.h"
|
||||
#include "../Items/ItemHandler.h"
|
||||
#include "../Vector3.h"
|
||||
#include "../FastRandom.h"
|
||||
|
||||
#include "../WorldStorage/StatSerializer.h"
|
||||
#include "../CompositeChat.h"
|
||||
@ -1795,6 +1796,28 @@ void cPlayer::UseEquippedItem(int a_Amount)
|
||||
return;
|
||||
}
|
||||
|
||||
// If the item has an unbreaking enchantment, give it a random chance of not breaking:
|
||||
cItem Item = GetEquippedItem();
|
||||
int UnbreakingLevel = Item.m_Enchantments.GetLevel(cEnchantments::enchUnbreaking);
|
||||
if (UnbreakingLevel > 0)
|
||||
{
|
||||
int chance;
|
||||
if (ItemCategory::IsArmor(Item.m_ItemType))
|
||||
{
|
||||
chance = 60 + (40 / (UnbreakingLevel + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
chance = 100 / (UnbreakingLevel + 1);
|
||||
}
|
||||
|
||||
cFastRandom Random;
|
||||
if (Random.NextInt(101) <= chance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetInventory().DamageEquippedItem(a_Amount))
|
||||
{
|
||||
m_World->BroadcastSoundEffect("random.break", GetPosX(), GetPosY(), GetPosZ(), 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
|
||||
|
@ -222,7 +222,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a
|
||||
m_ProjectileKind(a_Kind),
|
||||
m_CreatorData(
|
||||
((a_Creator != NULL) ? a_Creator->GetUniqueID() : -1),
|
||||
((a_Creator != NULL) ? (a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "") : "")
|
||||
((a_Creator != NULL) ? (a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "") : ""),
|
||||
((a_Creator != NULL) ? a_Creator->GetEquippedWeapon().m_Enchantments : cEnchantments())
|
||||
),
|
||||
m_IsInGround(false)
|
||||
{
|
||||
@ -235,7 +236,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a
|
||||
cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height) :
|
||||
super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height),
|
||||
m_ProjectileKind(a_Kind),
|
||||
m_CreatorData(a_Creator->GetUniqueID(), a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : ""),
|
||||
m_CreatorData(a_Creator->GetUniqueID(), a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "", a_Creator->GetEquippedWeapon().m_Enchantments),
|
||||
m_IsInGround(false)
|
||||
{
|
||||
SetSpeed(a_Speed);
|
||||
|
@ -94,14 +94,16 @@ protected:
|
||||
*/
|
||||
struct CreatorData
|
||||
{
|
||||
CreatorData(int a_UniqueID, const AString & a_Name) :
|
||||
CreatorData(int a_UniqueID, const AString & a_Name, const cEnchantments & a_Enchantments) :
|
||||
m_UniqueID(a_UniqueID),
|
||||
m_Name(a_Name)
|
||||
m_Name(a_Name),
|
||||
m_Enchantments(a_Enchantments)
|
||||
{
|
||||
}
|
||||
|
||||
const int m_UniqueID;
|
||||
AString m_Name;
|
||||
cEnchantments m_Enchantments;
|
||||
};
|
||||
|
||||
/** The type of projectile I am */
|
||||
|
@ -12,8 +12,8 @@
|
||||
|
||||
|
||||
|
||||
typedef std::list< cFurnaceRecipe::Recipe > RecipeList;
|
||||
typedef std::list< cFurnaceRecipe::Fuel > FuelList;
|
||||
typedef std::list<cFurnaceRecipe::cRecipe> RecipeList;
|
||||
typedef std::list<cFurnaceRecipe::cFuel> FuelList;
|
||||
|
||||
|
||||
|
||||
@ -68,12 +68,18 @@ void cFurnaceRecipe::ReloadRecipes(void)
|
||||
while (std::getline(f, ParsingLine))
|
||||
{
|
||||
LineNum++;
|
||||
ParsingLine.erase(std::remove_if(ParsingLine.begin(), ParsingLine.end(), isspace), ParsingLine.end()); // Remove ALL whitespace from the line
|
||||
if (ParsingLine.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove comments from the line:
|
||||
size_t FirstCommentSymbol = ParsingLine.find('#');
|
||||
if ((FirstCommentSymbol != AString::npos) && (FirstCommentSymbol != 0))
|
||||
{
|
||||
ParsingLine.erase(ParsingLine.begin() + (const long)FirstCommentSymbol, ParsingLine.end());
|
||||
}
|
||||
|
||||
switch (ParsingLine[0])
|
||||
{
|
||||
case '#':
|
||||
@ -103,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
|
||||
int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0;
|
||||
AString::size_type BeginPos = 1; // Begin at one after exclamation mark (bang)
|
||||
AString Line(a_Line);
|
||||
Line.erase(Line.begin()); // Remove the beginning "!"
|
||||
Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
|
||||
|
||||
if (
|
||||
!ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID
|
||||
!ReadOptionalNumbers(BeginPos, ":", "=", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health)
|
||||
!ReadMandatoryNumber(BeginPos, "0123456789", a_Line, a_LineNum, IBurnTime, true) // Read item burn time - last value
|
||||
)
|
||||
std::auto_ptr<cItem> Item(new cItem);
|
||||
int BurnTime;
|
||||
|
||||
const AStringVector & Sides = StringSplit(Line, "=");
|
||||
if (Sides.size() != 2)
|
||||
{
|
||||
LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
|
||||
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Add to fuel list:
|
||||
Fuel F;
|
||||
F.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth);
|
||||
F.BurnTime = IBurnTime;
|
||||
m_pState->Fuel.push_back(F);
|
||||
cFuel Fuel;
|
||||
Fuel.In = Item.release();
|
||||
Fuel.BurnTime = BurnTime;
|
||||
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;
|
||||
int OItemID = 0, OItemCount = 0, OItemHealth = 0;
|
||||
AString::size_type BeginPos = 0; // Begin at start of line
|
||||
AString Line(a_Line);
|
||||
Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
|
||||
|
||||
if (
|
||||
!ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID
|
||||
!ReadOptionalNumbers(BeginPos, ":", "@", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health)
|
||||
!ReadMandatoryNumber(BeginPos, "=", a_Line, a_LineNum, IBurnTime) || // Read item burn time
|
||||
!ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, OItemID) || // Read result ID
|
||||
!ReadOptionalNumbers(BeginPos, ":", "012456789", a_Line, a_LineNum, OItemCount, OItemHealth, true) // Read result count (and optionally health) - last value
|
||||
)
|
||||
int CookTime = 200;
|
||||
std::auto_ptr<cItem> InputItem(new cItem());
|
||||
std::auto_ptr<cItem> OutputItem(new cItem());
|
||||
|
||||
const AStringVector & Sides = StringSplit(Line, "=");
|
||||
if (Sides.size() != 2)
|
||||
{
|
||||
LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
|
||||
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Add to recipe list
|
||||
Recipe R;
|
||||
R.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth);
|
||||
R.Out = new cItem((ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth);
|
||||
R.CookTime = IBurnTime;
|
||||
m_pState->Recipes.push_back(R);
|
||||
const AStringVector & InputSplit = StringSplit(Sides[0], "@");
|
||||
if (!ParseItem(InputSplit[0], *InputItem))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (InputSplit.size() > 1)
|
||||
{
|
||||
if (!StringToInteger<int>(InputSplit[1], CookTime))
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFurnaceRecipe::PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing)
|
||||
bool cFurnaceRecipe::ParseItem(const AString & a_String, cItem & a_Item)
|
||||
{
|
||||
LOGWARN("Error parsing furnace recipes at line %i pos " SIZE_T_FMT ": missing '%s'", a_Line, a_Position, a_CharactersMissing.c_str());
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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)
|
||||
if (SplitAmount.size() > 1)
|
||||
{
|
||||
// TODO: replace atoi with std::stoi
|
||||
AString::size_type End;
|
||||
if (a_IsLastValue)
|
||||
if (!StringToInteger<char>(SplitAmount[1].c_str(), a_Item.m_ItemCount))
|
||||
{
|
||||
End = a_Text.find_first_not_of(a_Delimiter, a_Begin);
|
||||
}
|
||||
else
|
||||
{
|
||||
End = a_Text.find_first_of(a_Delimiter, a_Begin);
|
||||
if (End == AString::npos)
|
||||
{
|
||||
PrintParseError(a_Line, a_Begin, a_Delimiter);
|
||||
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)))
|
||||
if (SplitMeta.size() > 1)
|
||||
{
|
||||
if (!StringToInteger<short>(SplitMeta[1].c_str(), a_Item.m_ItemDamage))
|
||||
{
|
||||
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
|
||||
}
|
||||
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)
|
||||
{
|
||||
for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
|
||||
{
|
||||
Recipe R = *itr;
|
||||
delete R.In;
|
||||
R.In = NULL;
|
||||
delete R.Out;
|
||||
R.Out = NULL;
|
||||
cRecipe Recipe = *itr;
|
||||
delete Recipe.In;
|
||||
Recipe.In = NULL;
|
||||
delete Recipe.Out;
|
||||
Recipe.Out = NULL;
|
||||
}
|
||||
m_pState->Recipes.clear();
|
||||
|
||||
for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
|
||||
{
|
||||
Fuel F = *itr;
|
||||
delete F.In;
|
||||
F.In = NULL;
|
||||
cFuel Fuel = *itr;
|
||||
delete Fuel.In;
|
||||
Fuel.In = NULL;
|
||||
}
|
||||
m_pState->Fuel.clear();
|
||||
}
|
||||
@ -287,21 +266,21 @@ void cFurnaceRecipe::ClearRecipes(void)
|
||||
|
||||
|
||||
|
||||
const cFurnaceRecipe::Recipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
|
||||
const cFurnaceRecipe::cRecipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
|
||||
{
|
||||
const Recipe * BestRecipe = 0;
|
||||
const cRecipe * BestRecipe = 0;
|
||||
for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
|
||||
{
|
||||
const Recipe & R = *itr;
|
||||
if ((R.In->m_ItemType == a_Ingredient.m_ItemType) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount))
|
||||
const cRecipe & Recipe = *itr;
|
||||
if ((Recipe.In->m_ItemType == a_Ingredient.m_ItemType) && (Recipe.In->m_ItemCount <= a_Ingredient.m_ItemCount))
|
||||
{
|
||||
if (BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount))
|
||||
if (BestRecipe && (BestRecipe->In->m_ItemCount > Recipe.In->m_ItemCount))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
BestRecipe = &R;
|
||||
BestRecipe = &Recipe;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -317,16 +296,16 @@ int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const
|
||||
int BestFuel = 0;
|
||||
for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
|
||||
{
|
||||
const Fuel & F = *itr;
|
||||
if ((F.In->m_ItemType == a_Fuel.m_ItemType) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount))
|
||||
const cFuel & Fuel = *itr;
|
||||
if ((Fuel.In->m_ItemType == a_Fuel.m_ItemType) && (Fuel.In->m_ItemCount <= a_Fuel.m_ItemCount))
|
||||
{
|
||||
if (BestFuel > 0 && (BestFuel > F.BurnTime))
|
||||
if (BestFuel > 0 && (BestFuel > Fuel.BurnTime))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
BestFuel = F.BurnTime;
|
||||
BestFuel = Fuel.BurnTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,23 +19,23 @@ public:
|
||||
|
||||
void ReloadRecipes(void);
|
||||
|
||||
struct Fuel
|
||||
struct cFuel
|
||||
{
|
||||
cItem * In;
|
||||
int BurnTime; ///< How long this fuel burns, in ticks
|
||||
};
|
||||
|
||||
struct Recipe
|
||||
struct cRecipe
|
||||
{
|
||||
cItem * In;
|
||||
cItem * Out;
|
||||
int CookTime; ///< How long this recipe takes to smelt, in ticks
|
||||
};
|
||||
|
||||
/// Returns a recipe for the specified input, NULL if no recipe found
|
||||
const Recipe * GetRecipeFrom(const cItem & a_Ingredient) const;
|
||||
/** Returns a recipe for the specified input, NULL if no recipe found */
|
||||
const cRecipe * GetRecipeFrom(const cItem & a_Ingredient) const;
|
||||
|
||||
/// Returns the amount of time that the specified fuel burns, in ticks
|
||||
/** Returns the amount of time that the specified fuel burns, in ticks */
|
||||
int GetBurnTime(const cItem & a_Fuel) const;
|
||||
|
||||
private:
|
||||
@ -43,33 +43,14 @@ private:
|
||||
|
||||
/** Parses the fuel contained in the line, adds it to m_pState's fuels.
|
||||
Logs a warning to the console on input error. */
|
||||
void AddFuelFromLine(const AString & a_Line, int a_LineNum);
|
||||
void AddFuelFromLine(const AString & a_Line, unsigned int a_LineNum);
|
||||
|
||||
/** Parses the recipe contained in the line, adds it to m_pState's recipes.
|
||||
Logs a warning to the console on input error. */
|
||||
void AddRecipeFromLine(const AString & a_Line, int a_LineNum);
|
||||
|
||||
/** Calls LOGWARN with the line, position, and error */
|
||||
static void PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing);
|
||||
|
||||
/** Reads a number from a string given, starting at a given position and ending at a delimiter given
|
||||
Updates beginning position to the delimiter found + 1, and updates the value to the one read
|
||||
If it encounters a substring that is not fully numeric, it will call SetParseError() and return false; the caller should abort processing
|
||||
Otherwise, the function will return true
|
||||
*/
|
||||
static bool ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue = false);
|
||||
|
||||
/** Reads two numbers from a string given, starting at a given position and ending at the first delimiter given, then again (with an updated position) until the second delimiter given
|
||||
Updates beginning position to the second delimiter found + 1, and updates the values to the ones read
|
||||
If it encounters a substring that is not fully numeric whilst reading the second value, it will call SetParseError() and return false; the caller should abort processing
|
||||
If this happens whilst reading the first value, it will call ReadMandatoryNumber() with the appropriate position, as this may legitimately occur with the optional value and AString::find_first_of finding the incorrect delimiter. It will return the result of ReadMandatoryNumber()
|
||||
True will be returned definitively for an optional value that is valid
|
||||
*/
|
||||
static bool ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue = false);
|
||||
|
||||
/** Uses std::all_of to determine if a string contains only digits */
|
||||
static bool DoesStringContainOnlyNumbers(const AString & a_String);
|
||||
void AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum);
|
||||
|
||||
/** Parses an item string in the format "<ItemType>[: <Damage>][, <Amount>]", returns true if successful. */
|
||||
bool ParseItem(const AString & a_String, cItem & a_Item);
|
||||
|
||||
struct sFurnaceRecipeState;
|
||||
sFurnaceRecipeState * m_pState;
|
||||
|
@ -75,7 +75,6 @@ public:
|
||||
Arrow = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
a_Player->GetWorld()->BroadcastSoundEffect("random.bow", a_Player->GetPosX(), a_Player->GetPosY(), a_Player->GetPosZ(), 0.5, (float)Force);
|
||||
if (!a_Player->IsGameModeCreative())
|
||||
{
|
||||
@ -83,8 +82,19 @@ public:
|
||||
{
|
||||
a_Player->GetInventory().RemoveItem(cItem(E_ITEM_ARROW));
|
||||
}
|
||||
else
|
||||
{
|
||||
Arrow->SetPickupState(cArrowEntity::psNoPickup);
|
||||
}
|
||||
|
||||
|
||||
a_Player->UseEquippedItem();
|
||||
}
|
||||
|
||||
if (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchFlame) > 0)
|
||||
{
|
||||
Arrow->StartBurning(100);
|
||||
}
|
||||
}
|
||||
} ;
|
||||
|
||||
|
@ -75,7 +75,7 @@ public:
|
||||
int FoodLevel;
|
||||
double Saturation;
|
||||
|
||||
FoodInfo(int a_FoodLevel, double a_Saturation, int a_PoisonChance = 0) :
|
||||
FoodInfo(int a_FoodLevel, double a_Saturation) :
|
||||
FoodLevel(a_FoodLevel),
|
||||
Saturation(a_Saturation)
|
||||
{
|
||||
|
@ -158,7 +158,8 @@ cMojangAPI::cMojangAPI(void) :
|
||||
m_NameToUUIDServer(DEFAULT_NAME_TO_UUID_SERVER),
|
||||
m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS),
|
||||
m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER),
|
||||
m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS)
|
||||
m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS),
|
||||
m_RankMgr(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -979,9 +979,18 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
|
||||
AString ServerAddress;
|
||||
short ServerPort;
|
||||
UInt32 NextState;
|
||||
m_Buffer.ReadVarUTF8String(ServerAddress);
|
||||
m_Buffer.ReadBEShort(ServerPort);
|
||||
m_Buffer.ReadVarInt(NextState);
|
||||
if (!m_Buffer.ReadVarUTF8String(ServerAddress))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!m_Buffer.ReadBEShort(ServerPort))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!m_Buffer.ReadVarInt(NextState))
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_Buffer.CommitRead();
|
||||
m_Protocol = new cProtocol172(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
|
||||
return true;
|
||||
@ -991,9 +1000,18 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
|
||||
AString ServerAddress;
|
||||
short ServerPort;
|
||||
UInt32 NextState;
|
||||
m_Buffer.ReadVarUTF8String(ServerAddress);
|
||||
m_Buffer.ReadBEShort(ServerPort);
|
||||
m_Buffer.ReadVarInt(NextState);
|
||||
if (!m_Buffer.ReadVarUTF8String(ServerAddress))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!m_Buffer.ReadBEShort(ServerPort))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!m_Buffer.ReadVarInt(NextState))
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_Buffer.CommitRead();
|
||||
m_Protocol = new cProtocol176(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
|
||||
return true;
|
||||
|
@ -477,8 +477,8 @@ AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID)
|
||||
{
|
||||
SQLite::Statement stmt(m_DB, "SELECT Rank.Name FROM Rank LEFT JOIN PlayerRank ON Rank.RankID = PlayerRank.RankID WHERE PlayerRank.PlayerUUID = ?");
|
||||
stmt.bind(1, a_PlayerUUID);
|
||||
stmt.executeStep();
|
||||
if (stmt.isDone())
|
||||
// executeStep returns false on no data
|
||||
if (!stmt.executeStep())
|
||||
{
|
||||
// No data returned from the DB
|
||||
return AString();
|
||||
|
14
src/Root.cpp
14
src/Root.cpp
@ -468,16 +468,6 @@ void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCall
|
||||
|
||||
void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
|
||||
{
|
||||
// Some commands are built-in:
|
||||
if (a_Cmd == "stop")
|
||||
{
|
||||
m_bStop = true;
|
||||
}
|
||||
else if (a_Cmd == "restart")
|
||||
{
|
||||
m_bRestart = true;
|
||||
}
|
||||
|
||||
// Put the command into a queue (Alleviates FS #363):
|
||||
cCSLock Lock(m_CSPendingCommands);
|
||||
m_PendingCommands.push_back(cCommand(a_Cmd, new cLogCommandDeleteSelfOutputCallback));
|
||||
@ -489,14 +479,16 @@ void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
|
||||
|
||||
void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
|
||||
{
|
||||
// Some commands are built-in:
|
||||
// cRoot handles stopping and restarting due to our access to controlling variables
|
||||
if (a_Cmd == "stop")
|
||||
{
|
||||
m_bStop = true;
|
||||
return;
|
||||
}
|
||||
else if (a_Cmd == "restart")
|
||||
{
|
||||
m_bRestart = true;
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("Executing console command: \"%s\"", a_Cmd.c_str());
|
||||
|
@ -458,56 +458,80 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
|
||||
return;
|
||||
}
|
||||
|
||||
// Special handling: "stop" and "restart" are built in
|
||||
if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// "stop" and "restart" are handled in cRoot::ExecuteConsoleCommand, our caller, due to its access to controlling variables
|
||||
|
||||
// "help" and "reload" are to be handled by MCS, so that they work no matter what
|
||||
if (split[0] == "help")
|
||||
{
|
||||
PrintHelp(split, a_Output);
|
||||
a_Output.Finished();
|
||||
return;
|
||||
}
|
||||
else if (split[0] == "reload")
|
||||
{
|
||||
cPluginManager::Get()->ReloadPlugins();
|
||||
a_Output.Finished();
|
||||
return;
|
||||
}
|
||||
else if (split[0] == "reloadplugins")
|
||||
{
|
||||
cPluginManager::Get()->ReloadPlugins();
|
||||
a_Output.Out("Plugins reloaded");
|
||||
a_Output.Finished();
|
||||
return;
|
||||
}
|
||||
else if (split[0] == "load")
|
||||
{
|
||||
if (split.size() > 1)
|
||||
{
|
||||
cPluginManager::Get()->LoadPlugin(split[1]);
|
||||
|
||||
return;
|
||||
a_Output.Out(cPluginManager::Get()->LoadPlugin(split[1]) ? "Plugin loaded" : "Error occurred loading plugin");
|
||||
}
|
||||
else
|
||||
{
|
||||
a_Output.Out("No plugin given! Command: load <pluginname>");
|
||||
a_Output.Out("Usage: load <pluginname>");
|
||||
}
|
||||
a_Output.Finished();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (split[0] == "unload")
|
||||
{
|
||||
if (split.size() > 1)
|
||||
{
|
||||
cPluginManager::Get()->RemovePlugin(cPluginManager::Get()->GetPlugin(split[1]));
|
||||
return;
|
||||
a_Output.Out("Plugin unloaded");
|
||||
}
|
||||
else
|
||||
{
|
||||
a_Output.Out("No plugin given! Command: unload <pluginname>");
|
||||
a_Output.Out("Usage: unload <pluginname>");
|
||||
}
|
||||
a_Output.Finished();
|
||||
return;
|
||||
}
|
||||
if (split[0] == "destroyentities")
|
||||
{
|
||||
class WorldCallback : public cWorldListCallback
|
||||
{
|
||||
virtual bool Item(cWorld * a_World) override
|
||||
{
|
||||
class EntityCallback : public cEntityCallback
|
||||
{
|
||||
virtual bool Item(cEntity * a_Entity) override
|
||||
{
|
||||
if (!a_Entity->IsPlayer())
|
||||
{
|
||||
a_Entity->Destroy();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} EC;
|
||||
a_World->ForEachEntity(EC);
|
||||
return false;
|
||||
}
|
||||
} WC;
|
||||
cRoot::Get()->ForEachWorld(WC);
|
||||
a_Output.Out("Destroyed all entities");
|
||||
a_Output.Finished();
|
||||
return;
|
||||
}
|
||||
|
||||
// There is currently no way a plugin can do these (and probably won't ever be):
|
||||
@ -602,6 +626,7 @@ void cServer::BindBuiltInConsoleCommands(void)
|
||||
PlgMgr->BindConsoleCommand("chunkstats", NULL, " - Displays detailed chunk memory statistics");
|
||||
PlgMgr->BindConsoleCommand("load <pluginname>", NULL, " - Adds and enables the specified plugin");
|
||||
PlgMgr->BindConsoleCommand("unload <pluginname>", NULL, " - Disables the specified plugin");
|
||||
PlgMgr->BindConsoleCommand("destroyentities", NULL, " - Destroys all entities in all worlds");
|
||||
|
||||
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
|
||||
PlgMgr->BindConsoleCommand("dumpmem", NULL, " - Dumps all used memory blocks together with their callstacks into memdump.xml");
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "Globals.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[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -92,6 +92,9 @@ public:
|
||||
/** Calculates the heightmap based on the contained blocktypes and marks it valid. */
|
||||
void CalculateHeightMap(void);
|
||||
|
||||
/** Removes the block entities that don't have a proper blocktype at their corresponding coords. */
|
||||
void RemoveInvalidBlockEntities(void);
|
||||
|
||||
protected:
|
||||
int m_ChunkX;
|
||||
int m_ChunkZ;
|
||||
|
@ -98,7 +98,7 @@ int cVanillaFluidSimulator::CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#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
|
||||
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
|
||||
|
||||
|
||||
|
@ -1226,24 +1226,26 @@ void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_Blo
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Add damage to entities and implement block hardiness
|
||||
// TODO: Implement block hardiness
|
||||
Vector3d explosion_pos = Vector3d(a_BlockX, a_BlockY, a_BlockZ);
|
||||
cVector3iArray BlocksAffected;
|
||||
m_ChunkMap->DoExplosionAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, BlocksAffected);
|
||||
BroadcastSoundEffect("random.explode", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 1.0f, 0.6f);
|
||||
|
||||
{
|
||||
cCSLock Lock(m_CSPlayers);
|
||||
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
|
||||
{
|
||||
cClientHandle * ch = (*itr)->GetClientHandle();
|
||||
if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
|
||||
if (ch == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector3d distance_explosion = (*itr)->GetPosition() - explosion_pos;
|
||||
if (distance_explosion.SqrLength() < 4096.0)
|
||||
{
|
||||
double real_distance = std::max(0.004, sqrt(distance_explosion.SqrLength()));
|
||||
double real_distance = std::max(0.004, distance_explosion.Length());
|
||||
double power = a_ExplosionSize / real_distance;
|
||||
if (power <= 1)
|
||||
{
|
||||
@ -1255,6 +1257,7 @@ void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_Blo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cPluginManager::Get()->CallHookExploded(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData);
|
||||
}
|
||||
|
||||
@ -3484,14 +3487,16 @@ void cWorld::cChunkGeneratorCallbacks::OnChunkGenerated(cChunkDesc & a_ChunkDesc
|
||||
cChunkDef::BlockNibbles BlockMetas;
|
||||
a_ChunkDesc.CompressBlockMetas(BlockMetas);
|
||||
|
||||
m_World->QueueSetChunkData(cSetChunkDataPtr(new cSetChunkData(
|
||||
cSetChunkDataPtr SetChunkData(new cSetChunkData(
|
||||
a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(),
|
||||
a_ChunkDesc.GetBlockTypes(), BlockMetas,
|
||||
NULL, NULL, // We don't have lighting, chunk will be lighted when needed
|
||||
&a_ChunkDesc.GetHeightMap(), &a_ChunkDesc.GetBiomeMap(),
|
||||
a_ChunkDesc.GetEntities(), a_ChunkDesc.GetBlockEntities(),
|
||||
true
|
||||
)));
|
||||
));
|
||||
SetChunkData->RemoveInvalidBlockEntities();
|
||||
m_World->QueueSetChunkData(SetChunkData);
|
||||
}
|
||||
|
||||
|
||||
|
@ -615,7 +615,6 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile)
|
||||
{
|
||||
m_Writer.BeginCompound("");
|
||||
AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName());
|
||||
Vector3d Pos = a_Projectile->GetPosition();
|
||||
m_Writer.AddByte("inGround", a_Projectile->IsInGround() ? 1 : 0);
|
||||
|
||||
switch (a_Projectile->GetProjectileKind())
|
||||
|
@ -396,7 +396,7 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
|
||||
} // for y
|
||||
//*/
|
||||
|
||||
m_World->QueueSetChunkData(cSetChunkDataPtr(new cSetChunkData(
|
||||
cSetChunkDataPtr SetChunkData(new cSetChunkData(
|
||||
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
|
||||
BlockTypes, MetaData,
|
||||
IsLightValid ? BlockLight : NULL,
|
||||
@ -404,7 +404,8 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
|
||||
NULL, Biomes,
|
||||
Entities, BlockEntities,
|
||||
false
|
||||
)));
|
||||
));
|
||||
m_World->QueueSetChunkData(SetChunkData);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -581,64 +582,28 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
|
||||
{
|
||||
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;
|
||||
}
|
||||
if (strncmp(a_NBT.GetData(sID), "Beacon", a_NBT.GetDataLength(sID)) == 0)
|
||||
{
|
||||
LoadBeaconFromNBT(a_BlockEntities, a_NBT, Child);
|
||||
}
|
||||
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
|
||||
|
||||
// Add the BlockEntity to the loaded data:
|
||||
a_BlockEntities.push_back(be.release());
|
||||
} // 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)
|
||||
{
|
||||
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);
|
||||
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();
|
||||
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);
|
||||
int x, y, z;
|
||||
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
|
||||
// Check if the given tag is a compound:
|
||||
if (a_NBT.GetType(a_TagIdx) != TAG_Compound)
|
||||
{
|
||||
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");
|
||||
if (CurrentLine >= 0)
|
||||
@ -790,88 +830,128 @@ void cWSSAnvil::LoadBeaconFromNBT(cBlockEntityList & a_BlockEntities, const cPar
|
||||
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);
|
||||
int x, y, z;
|
||||
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
|
||||
// Check if the data has a proper type:
|
||||
// TODO: Does vanilla use "TrappedChest" or not? MCWiki says no, but previous code says yes
|
||||
// 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");
|
||||
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);
|
||||
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);
|
||||
int x, y, z;
|
||||
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
|
||||
// Check if the data has a proper type:
|
||||
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Control"))
|
||||
{
|
||||
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");
|
||||
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);
|
||||
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);
|
||||
int x, y, z;
|
||||
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
|
||||
// Check if the data has a proper type:
|
||||
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Dropper"))
|
||||
{
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
|
||||
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);
|
||||
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);
|
||||
int x, y, z;
|
||||
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
|
||||
// Check if the data has a proper type:
|
||||
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "FlowerPot"))
|
||||
{
|
||||
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;
|
||||
|
||||
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));
|
||||
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);
|
||||
int x, y, z;
|
||||
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
|
||||
// Check if the data has a proper type:
|
||||
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Furnace"))
|
||||
{
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
|
||||
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:
|
||||
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));
|
||||
std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World));
|
||||
|
||||
// Load slots:
|
||||
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:
|
||||
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);
|
||||
int x, y, z;
|
||||
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
|
||||
// Check if the data has a proper type:
|
||||
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Hopper"))
|
||||
{
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
|
||||
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);
|
||||
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);
|
||||
int x, y, z;
|
||||
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
|
||||
// Check if the data has a proper type:
|
||||
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "RecordPlayer"))
|
||||
{
|
||||
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");
|
||||
if (Record >= 0)
|
||||
{
|
||||
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);
|
||||
int x, y, z;
|
||||
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
|
||||
// Check if the data has a proper type:
|
||||
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Skull"))
|
||||
{
|
||||
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");
|
||||
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);
|
||||
int x, y, z;
|
||||
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
|
||||
// Check if the data has a proper type:
|
||||
if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Sign"))
|
||||
{
|
||||
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");
|
||||
if (currentLine >= 0)
|
||||
@ -1059,79 +1165,7 @@ void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParse
|
||||
Sign->SetLine(3, a_NBT.GetString(currentLine));
|
||||
}
|
||||
|
||||
a_BlockEntities.push_back(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());
|
||||
return Sign.release();
|
||||
}
|
||||
|
||||
|
||||
|
@ -125,6 +125,10 @@ protected:
|
||||
/// 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);
|
||||
|
||||
/** 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
|
||||
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 LoadBeaconFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType);
|
||||
void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadFlowerPotFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
|
||||
void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadMobHeadFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadCommandBlockFromNBT(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. */
|
||||
bool CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, const char * a_ExpectedType);
|
||||
|
||||
cBlockEntity * LoadBeaconFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
cBlockEntity * LoadChestFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_ChestBlockType);
|
||||
cBlockEntity * LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
cBlockEntity * LoadDispenserFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
cBlockEntity * LoadDropperFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
cBlockEntity * LoadFlowerPotFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
cBlockEntity * LoadFurnaceFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
|
||||
cBlockEntity * LoadHopperFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
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);
|
||||
|
||||
|
@ -45,7 +45,7 @@ int main(int argc, char ** argv)
|
||||
BLOCKTYPE * WritePosition = &TestBuffer[WritePosIdx];
|
||||
memset(TestBuffer, 0x03, sizeof(TestBuffer));
|
||||
size_t LastReportedStep = 1;
|
||||
for (size_t idx = 0; idx < 5000; idx += 7)
|
||||
for (size_t idx = 0; idx < 5000; idx += 73)
|
||||
{
|
||||
if (idx / 500 != LastReportedStep)
|
||||
{
|
||||
@ -53,7 +53,7 @@ int main(int argc, char ** argv)
|
||||
LastReportedStep = idx / 500;
|
||||
}
|
||||
|
||||
for (size_t len = 3; len < 1000; len += 13)
|
||||
for (size_t len = 3; len < 700; len += 13)
|
||||
{
|
||||
Data.CopyBlockTypes(WritePosition, idx, len);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user