View
@@ -61,30 +61,45 @@ Furnace = Cobblestone, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3
#******************************************************#
# Blocks
#
IronBlock = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
GoldBlock = GoldIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
DiamondBlock = Diamond, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
LapisBlock = LapisLazuli, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
EmeraldBlock = Emerald, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
RedstoneBlock = RedstoneDust, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
QuartzBlock = NetherQuartz, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
NetherBrick = netherbrickitem, 1:1, 1:2, 2:1, 2:2
Glowstone = GlowstoneDust, 1:1, 1:2, 2:1, 2:2
Wool = String, 1:1, 1:2, 2:1, 2:2
TNT = Gunpowder, 1:1, 3:1, 2:2, 1:3, 3:3 | Sand, 2:1, 1:2, 3:2, 2:3
PillarQuartzBlock = QuartzSlab, 1:1, 1:2
ChiseledQuartzBlock, 2 = QuartzBlock, 1:1, 1:2
CoalBlock = Coal, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
HayBale = Wheat, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
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
IronBlock = IronIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
GoldBlock = GoldIngot, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
DiamondBlock = Diamond, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
LapisBlock = LapisLazuli, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
EmeraldBlock = Emerald, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
RedstoneBlock = RedstoneDust, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
QuartzBlock = NetherQuartz, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
NetherBrick = netherbrickitem, 1:1, 1:2, 2:1, 2:2
Glowstone = GlowstoneDust, 1:1, 1:2, 2:1, 2:2
Wool = String, 1:1, 1:2, 2:1, 2:2
TNT = Gunpowder, 1:1, 3:1, 2:2, 1:3, 3:3 | Sand, 2:1, 1:2, 3:2, 2:3
PillarQuartzBlock = QuartzSlab, 1:1, 1:2
ChiseledQuartzBlock, 2 = QuartzBlock, 1:1, 1:2
CoalBlock = Coal, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
HayBale = Wheat, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
SnowBlock = SnowBall, 1:1, 1:2, 2:1, 2:2
ClayBlock = Clay, 1:1, 1:2, 2:1, 2:2
BrickBlock = Brick, 1:1, 1:2, 2:1, 2:2
StoneBrick, 4 = Stone, 1:1, 1:2, 2:1, 2:2
BookShelf = Planks, 1:1, 2:1, 3:1, 1:3, 2:3, 3:3 | Book, 1:2, 2:2, 3:2
Sandstone, 4 = Sand, 1:1, 1:2, 2:1, 2:2
SmoothSandstone, 4 = Sandstone, 1:1, 1:2, 2:1, 2:2
OrnamentSandstone = SandstoneSlab, 1:1, 1:2
JackOLantern = Pumpkin, 1:1 | Torch, 1:2
PolishedGranite, 4 = Granite, 1:1, 1:2, 2:1, 2:2
PolishedDiorite, 4 = Diorite, 1:1, 1:2, 2:1, 2:2
PolishedAndesite, 4 = Andesite, 1:1, 1:2, 2:1, 2:2
CoarsedDirt, 4 = Dirt, 1:1, 2:2 | Gravel, 1:2, 2:1
CoarsedDirt, 4 = Gravel, 1:1, 2:2 | Dirt, 1:2, 2:1
SlimeBlock = Slimeball, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
Prismarine = PrismarineShard, 1:1, 1:2, 2:1, 2:2
PrismarineBricks = PrismarineShard, 1:1, 1:2, 1:3, 2:1, 2:2, 2:3, 3:1, 3:2, 3:3
DarkPrismarine = PrismarineShard, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Inksac, 2:2
SeaLantern = PrismarineShard, 1:1, 1:3, 3:1, 3:3 | PrismarineCrystals, 1:2, 2:1, 2:2, 2:3, 3:2
RedSandstone, 4 = RedSand, 1:1, 1:2, 2:1, 2:2
ChiseledRedSandstone, 4 = RedSandstoneSlab, 1:1, 1:2
SmoothRedSandstone, 4 = RedSand, 1:1, 1:2, 2:1, 2:2
MossyStoneBrick = Stonebrick, * | Vines, *
Leather = RabbitHide, 1:1, 1:2, 2:1, 2:2
# Slabs:
StoneSlab, 6 = Stone, 1:1, 2:1, 3:1
@@ -101,6 +116,7 @@ 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:
@@ -128,6 +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
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
@@ -262,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
@@ -304,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
@@ -332,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
View
@@ -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
View
@@ -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
@@ -45,6 +53,7 @@ stilllava=11
slava=11
stationarylava=11
sand=12
redsand=12:1
gravel=13
goldore=14
ironore=15
@@ -69,6 +78,7 @@ spruceleaves=18:1
birchleaves=18:2
jungleleaves=18:3
sponge=19
wetsponge=19:1
glass=20
lapisore=21
lapisblock=22
@@ -395,8 +405,42 @@ 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
@@ -644,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
View
@@ -668,6 +668,24 @@ void cIniFile::Clear(void)
bool cIniFile::HasValue(const AString & a_KeyName, const AString & a_ValueName)
{
// Find the key:
int keyID = FindKey(a_KeyName);
if (keyID == noID)
{
return false;
}
// Find the value:
int valueID = FindValue(keyID, a_ValueName);
return (valueID != noID);
}
void cIniFile::AddHeaderComment(const AString & comment)
{
comments.push_back(comment);
View
@@ -53,7 +53,9 @@ class cIniFile
/// Removes the UTF-8 BOMs (Byte order makers), if present.
void RemoveBom(AString & a_line) const;
public:
enum errors
{
noID = -1,
@@ -79,6 +81,9 @@ class cIniFile
/// Deletes all stored ini data (but doesn't touch the file)
void Clear(void);
/** Returns true iff the specified value exists. */
bool HasValue(const AString & a_KeyName, const AString & a_ValueName);
/// Returns index of specified key, or noID if not found
int FindKey(const AString & keyname) const;
View
@@ -36,7 +36,9 @@ extern "C" {
#define TEMPLATE_BIND(p)
#endif
#define TOLUA_TEMPLATE_BIND(p)
#ifndef TOLUA_TEMPLATE_BIND
#define TOLUA_TEMPLATE_BIND(p)
#endif
#define TOLUA_PROTECTED_DESTRUCTOR
#define TOLUA_PROPERTY_TYPE(p)
View
@@ -107,7 +107,7 @@ void cLuaChunkStay::AddChunkCoord(cLuaState & L, int a_Index)
}
} // for itr - m_Chunks[]
m_Chunks.push_back(cChunkCoords(ChunkX, ZERO_CHUNK_Y, ChunkZ));
m_Chunks.push_back(cChunkCoords(ChunkX, ChunkZ));
}
View
@@ -859,6 +859,32 @@ void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal)
void cLuaState::GetStackValue(int a_StackPos, pBoundingBox & a_ReturnedVal)
{
tolua_Error err;
if (tolua_isusertype(m_LuaState, a_StackPos, "cBoundingBox", false, &err))
{
a_ReturnedVal = *((cBoundingBox **)lua_touserdata(m_LuaState, a_StackPos));
}
}
void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal)
{
tolua_Error err;
if (tolua_isusertype(m_LuaState, a_StackPos, "cWorld", false, &err))
{
a_ReturnedVal = *((cWorld **)lua_touserdata(m_LuaState, a_StackPos));
}
}
bool cLuaState::CallFunction(int a_NumResults)
{
ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
View
@@ -59,6 +59,10 @@ class cTNTEntity;
class cCreeper;
class cHopperEntity;
class cBlockEntity;
class cBoundingBox;
typedef cBoundingBox * pBoundingBox;
typedef cWorld * pWorld;
@@ -230,6 +234,12 @@ class cLuaState
/** Retrieve value at a_StackPos, if it is a valid number, converting and clamping it to eWeather.
If not, a_Value is unchanged. */
void GetStackValue(int a_StackPos, eWeather & a_Value);
/** Retrieve value at a_StackPos, if it is a valid cBoundingBox class. If not, a_Value is unchanged */
void GetStackValue(int a_StackPos, pBoundingBox & a_Value);
/** Retrieve value at a_StackPos, if it is a valid cWorld class. If not, a_Value is unchanged */
void GetStackValue(int a_StackPos, pWorld & a_Value);
// Include the cLuaState::Call() overload implementation that is generated by the gen_LuaState_Call.lua script:
@@ -328,6 +338,14 @@ class cLuaState
*/
bool PushFunction(int a_FnRef);
/** Pushes a function that has been saved as a reference.
Returns true if successful. Logs a warning on failure
*/
bool PushFunction(const cRef & a_FnRef)
{
return PushFunction((int)a_FnRef);
}
/** Pushes a function that is stored in a referenced table by name
Returns true if successful. Logs a warning on failure
*/
View
@@ -676,6 +676,75 @@ static int tolua_ForEachInChunk(lua_State * tolua_S)
template <
class Ty1,
class Ty2,
bool (Ty1::*Func1)(const cBoundingBox &, cItemCallback<Ty2> &)
>
static int tolua_ForEachInBox(lua_State * tolua_S)
{
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cWorld") ||
!L.CheckParamUserType(2, "cBoundingBox") ||
!L.CheckParamFunction(3) ||
!L.CheckParamEnd(4)
)
{
return 0;
}
// Get the params:
Ty1 * Self = NULL;
cBoundingBox * Box = NULL;
L.GetStackValues(1, Self, Box);
ASSERT(Self != NULL); // We have verified the type at the top, so we should get valid objects here
ASSERT(Box != NULL);
// Create a reference for the function:
cLuaState::cRef FnRef(L, 3);
// Callback wrapper for the Lua function:
class cLuaCallback : public cItemCallback<Ty2>
{
public:
cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FuncRef) :
m_LuaState(a_LuaState),
m_FnRef(a_FuncRef)
{}
private:
// cItemCallback<Ty2> overrides:
virtual bool Item(Ty2 * a_Item) override
{
bool res = false;
if (!m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res))
{
LOGWARNING("Failed to call Lua callback");
m_LuaState.LogStackTrace();
return true; // Abort enumeration
}
return res;
}
cLuaState & m_LuaState;
cLuaState::cRef & m_FnRef;
} Callback(L, FnRef);
bool bRetVal = (Self->*Func1)(*Box, Callback);
FnRef.UnRef();
/* Push return value on stack */
tolua_pushboolean(tolua_S, bRetVal);
return 1;
}
template <
class Ty1,
class Ty2,
@@ -2663,7 +2732,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
@@ -3327,6 +3396,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>);
tolua_function(tolua_S, "ForEachEntityInBox", tolua_ForEachInBox< cWorld, cEntity, &cWorld::ForEachEntityInBox>);
tolua_function(tolua_S, "ForEachEntityInChunk", tolua_ForEachInChunk<cWorld, cEntity, &cWorld::ForEachEntityInChunk>);
tolua_function(tolua_S, "ForEachFurnaceInChunk", tolua_ForEachInChunk<cWorld, cFurnaceEntity, &cWorld::ForEachFurnaceInChunk>);
tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>);
View
@@ -109,7 +109,7 @@ local function WriteOverload(f, a_NumParams, a_NumReturns)
-- Write the function signature:
f:write("bool Call(")
f:write("FnT a_Function")
f:write("const FnT & a_Function")
for i = 1, a_NumParams do
f:write(", ParamT", i, " a_Param", i)
end
View
@@ -188,12 +188,11 @@ void cCommandBlockEntity::SaveToJson(Json::Value & a_Value)
void cCommandBlockEntity::Execute()
{
if (m_World != NULL)
ASSERT(m_World != NULL); // Execute should not be called before the command block is attached to a world
if (!m_World->AreCommandBlocksEnabled())
{
if (!m_World->AreCommandBlocksEnabled())
{
return;
}
return;
}
class CommandBlockOutCb :
View
@@ -105,7 +105,7 @@ class cFurnaceEntity :
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;
View
@@ -362,6 +362,8 @@ enum ENUM_ITEM_ID
E_ITEM_LEAD = 420,
E_ITEM_NAME_TAG = 421,
E_ITEM_MINECART_WITH_COMMAND_BLOCK = 422,
E_ITEM_RAW_MUTTON = 423,
E_ITEM_MUTTON = 424,
// Keep these two as the last values of the consecutive list, without a number - they will get their correct number assigned automagically by C++
// IsValidItem() depends on this!
View
@@ -431,10 +431,45 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac
else
{
// TODO: Add a proper overridable function for this
Pickups.Add(m_BlockType, 1, Meta);
if (a_Digger != NULL)
{
cEnchantments Enchantments = a_Digger->GetEquippedWeapon().m_Enchantments;
if ((Enchantments.GetLevel(cEnchantments::enchSilkTouch) > 0) && a_Digger->IsPlayer())
{
switch (m_BlockType)
{
case E_BLOCK_CAKE:
case E_BLOCK_CARROTS:
case E_BLOCK_COCOA_POD:
case E_BLOCK_DOUBLE_STONE_SLAB:
case E_BLOCK_DOUBLE_WOODEN_SLAB:
case E_BLOCK_FIRE:
case E_BLOCK_FARMLAND:
case E_BLOCK_MELON_STEM:
case E_BLOCK_MOB_SPAWNER:
case E_BLOCK_NETHER_WART:
case E_BLOCK_POTATOES:
case E_BLOCK_PUMPKIN_STEM:
case E_BLOCK_SNOW:
case E_BLOCK_SUGARCANE:
case E_BLOCK_TALL_GRASS:
case E_BLOCK_CROPS:
{
// Silktouch can't be used for this blocks
ConvertToPickups(Pickups, Meta);
break;
};
default: Pickups.Add(m_BlockType, 1, Meta);
}
}
else
{
Pickups.Add(m_BlockType, 1, Meta);
}
}
}
}
// Allow plugins to modify the pickups:
a_BlockPluginInterface.CallHookBlockToPickups(a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups);
View
@@ -30,18 +30,18 @@ class cBlockIceHandler :
{
return;
}
BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow))
cEnchantments Enchantments = a_Player->GetInventory().GetEquippedItem().m_Enchantments;
if (Enchantments.GetLevel(cEnchantments::enchSilkTouch) == 0)
{
return;
BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow))
{
return;
}
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
// This is called later than the real destroying of this ice block
}
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
// This is called later than the real destroying of this ice block
}
} ;
View
@@ -80,6 +80,17 @@ class cBoundingBox
/// Calculates the intersection of the two bounding boxes; returns true if nonempty
bool Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection);
double GetMinX(void) const { return m_Min.x; }
double GetMinY(void) const { return m_Min.y; }
double GetMinZ(void) const { return m_Min.z; }
double GetMaxX(void) const { return m_Max.x; }
double GetMaxY(void) const { return m_Max.y; }
double GetMaxZ(void) const { return m_Max.z; }
const Vector3d & GetMin(void) const { return m_Min; }
const Vector3d & GetMax(void) const { return m_Max; }
protected:
Vector3d m_Min;
Vector3d m_Max;
View
@@ -256,6 +256,11 @@ set(EXECUTABLE MCServer)
if (MSVC)
get_directory_property(BINDING_OUTPUTS DIRECTORY "Bindings" DEFINITION BINDING_OUTPUTS)
get_directory_property(BINDING_DEPENDENCIES DIRECTORY "Bindings" DEFINITION BINDING_DEPENDENCIES)
# The paths in BINDING_DEPENDENCIES are relative to the Bindings folder, convert them relative to this folder:
foreach (dep ${BINDING_DEPENDENCIES})
list (APPEND BINDINGS_DEPENDENCIES "Bindings/${dep}")
endforeach(dep)
ADD_CUSTOM_COMMAND(
OUTPUT ${BINDING_OUTPUTS}
@@ -268,7 +273,7 @@ if (MSVC)
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/
# add any new generation dependencies here
DEPENDS ${BINDING_DEPENDENCIES}
DEPENDS ${BINDINGS_DEPENDENCIES}
)
endif()
View
@@ -37,6 +37,7 @@
#include "MobSpawner.h"
#include "BlockInServerPluginInterface.h"
#include "SetChunkData.h"
#include "BoundingBox.h"
#include "json/json.h"
@@ -65,7 +66,7 @@ sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_Bloc
// cChunk:
cChunk::cChunk(
int a_ChunkX, int a_ChunkY, int a_ChunkZ,
int a_ChunkX, int a_ChunkZ,
cChunkMap * a_ChunkMap, cWorld * a_World,
cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP,
cAllocationPool<cChunkData::sChunkSection> & a_Pool
@@ -77,7 +78,6 @@ cChunk::cChunk(
m_HasLoadFailed(false),
m_StayCount(0),
m_PosX(a_ChunkX),
m_PosY(a_ChunkY),
m_PosZ(a_ChunkZ),
m_World(a_World),
m_ChunkMap(a_ChunkMap),
@@ -653,7 +653,7 @@ void cChunk::MoveEntityToNewChunk(cEntity * a_Entity)
cChunk * Neighbor = GetNeighborChunk(a_Entity->GetChunkX() * cChunkDef::Width, a_Entity->GetChunkZ() * cChunkDef::Width);
if (Neighbor == NULL)
{
Neighbor = m_ChunkMap->GetChunkNoLoad(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ());
Neighbor = m_ChunkMap->GetChunkNoLoad(a_Entity->GetChunkX(), a_Entity->GetChunkZ());
if (Neighbor == NULL)
{
// TODO: What to do with this?
@@ -1960,6 +1960,30 @@ bool cChunk::ForEachEntity(cEntityCallback & a_Callback)
bool cChunk::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback)
{
// The entity list is locked by the parent chunkmap's CS
for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2)
{
++itr2;
cBoundingBox EntBox((*itr)->GetPosition(), (*itr)->GetWidth() / 2, (*itr)->GetHeight());
if (!EntBox.DoesIntersect(a_Box))
{
// The entity is not in the specified box
continue;
}
if (a_Callback.Item(*itr))
{
return false;
}
} // for itr - m_Entitites[]
return true;
}
bool cChunk::DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult)
{
// The entity list is locked by the parent chunkmap's CS
@@ -2603,7 +2627,7 @@ cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ)
int BlockZ = m_PosZ * cChunkDef::Width + a_RelZ;
int ChunkX, ChunkZ;
BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ);
return m_ChunkMap->GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
return m_ChunkMap->GetChunkNoLoad(ChunkX, ChunkZ);
}
// Walk the neighbors:
View
@@ -67,7 +67,7 @@ class cChunk :
{
public:
cChunk(
int a_ChunkX, int a_ChunkY, int a_ChunkZ, // Chunk coords
int a_ChunkX, int a_ChunkZ, // Chunk coords
cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects
cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP, // Neighbor chunks
cAllocationPool<cChunkData::sChunkSection> & a_Pool
@@ -216,6 +216,10 @@ class cChunk :
/** Calls the callback for each entity; returns true if all entities processed, false if the callback aborted by returning true */
bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible
/** Calls the callback for each entity that has a nonempty intersection with the specified boundingbox.
Returns true if all entities processed, false if the callback aborted by returning true. */
bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback); // Lua-accessible
/** Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. */
bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult); // Lua-accessible
View
@@ -16,11 +16,6 @@
/** This is really only a placeholder to be used in places where we need to "make up" a chunk's Y coord.
It will help us when the new chunk format comes out and we need to patch everything up for compatibility.
*/
#define ZERO_CHUNK_Y 0
// Used to smoothly convert to new axis ordering. One will be removed when deemed stable.
#define AXIS_ORDER_YZX 1 // Original (1.1-)
#define AXIS_ORDER_XZY 2 // New (1.2+)
@@ -197,32 +192,32 @@ class cChunkDef
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;
}
@@ -377,14 +372,13 @@ class cChunkCoords
{
public:
int m_ChunkX;
int m_ChunkY;
int m_ChunkZ;
cChunkCoords(int a_ChunkX, int a_ChunkY, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ) {}
cChunkCoords(int a_ChunkX, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ) {}
bool operator == (const cChunkCoords & a_Other) const
{
return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkY == a_Other.m_ChunkY) && (m_ChunkZ == a_Other.m_ChunkZ));
return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkZ == a_Other.m_ChunkZ));
}
} ;
@@ -395,6 +389,27 @@ typedef std::vector<cChunkCoords> cChunkCoordsVector;
class cChunkCoordsWithBool
{
public:
int m_ChunkX;
int m_ChunkZ;
bool m_ForceGenerate;
cChunkCoordsWithBool(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate) : m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ), m_ForceGenerate(a_ForceGenerate){}
bool operator == (const cChunkCoordsWithBool & a_Other) const
{
return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkZ == a_Other.m_ChunkZ) && (m_ForceGenerate == a_Other.m_ForceGenerate));
}
};
typedef std::list<cChunkCoordsWithBool> cChunkCoordsWithBoolList;
/// Interface class used as a callback for operations that involve chunk coords
class cChunkCoordCallback
{
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -36,6 +36,7 @@ class cBlockArea;
class cMobCensus;
class cMobSpawner;
class cSetChunkData;
class cBoundingBox;
typedef std::list<cClientHandle *> cClientHandleList;
typedef cChunk * cChunkPtr;
@@ -209,6 +210,11 @@ class cChunkMap
/** Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true */
bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Lua-accessible
/** Calls the callback for each entity that has a nonempty intersection with the specified boundingbox.
Returns true if all entities processed, false if the callback aborted by returning true.
If any chunk in the box is missing, ignores the entities in that chunk silently. */
bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback); // Lua-accessible
/** Destroys and returns a list of blocks destroyed in the explosion at the specified coordinates */
void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlockAffected);
@@ -270,16 +276,16 @@ class cChunkMap
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible
/** Touches the chunk, causing it to be loaded or generated */
void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void TouchChunk(int a_ChunkX, int a_ChunkZ);
/** Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) */
bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
bool LoadChunk(int a_ChunkX, int a_ChunkZ);
/** Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() */
void LoadChunks(const cChunkCoordsList & a_Chunks);
/** Marks the chunk as failed-to-load */
void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ);
/** Sets the sign text. Returns true if sign text changed. */
bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
@@ -363,7 +369,7 @@ class cChunkMap
~cChunkLayer();
/** Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check */
cChunkPtr GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ);
cChunkPtr GetChunk( int a_ChunkX, int a_ChunkZ);
/** Returns the specified chunk, or NULL if not created yet */
cChunk * FindChunk(int a_ChunkX, int a_ChunkZ);
@@ -456,9 +462,9 @@ class cChunkMap
std::auto_ptr<cAllocationPool<cChunkData::sChunkSection> > m_Pool;
cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid
cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate
cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate
cChunkPtr GetChunk (int a_ChunkX, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid
cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate
cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkZ); // Doesn't load, doesn't generate
/** Gets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) */
bool LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
View
@@ -81,7 +81,7 @@ void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkZ)
// This is probably never gonna be called twice for the same chunk, and if it is, we don't mind, so we don't check
{
cCSLock Lock(m_CS);
m_ChunksReady.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
m_ChunksReady.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
}
m_evtQueue.Set();
}
@@ -95,12 +95,12 @@ void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle *
ASSERT(a_Client != NULL);
{
cCSLock Lock(m_CS);
if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)) != m_SendChunks.end())
if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, a_ChunkZ, a_Client)) != m_SendChunks.end())
{
// Already queued, bail out
return;
}
m_SendChunks.push_back(sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client));
m_SendChunks.push_back(sSendChunk(a_ChunkX, a_ChunkZ, a_Client));
}
m_evtQueue.Set();
}
@@ -160,7 +160,7 @@ void cChunkSender::Execute(void)
m_ChunksReady.pop_front();
Lock.Unlock();
SendChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, NULL);
SendChunk(Coords.m_ChunkX, Coords.m_ChunkZ, NULL);
}
else
{
@@ -169,7 +169,7 @@ void cChunkSender::Execute(void)
m_SendChunks.pop_front();
Lock.Unlock();
SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkY, Chunk.m_ChunkZ, Chunk.m_Client);
SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client);
}
Lock.Lock();
int RemoveCount = m_RemoveCount;
@@ -186,14 +186,14 @@ void cChunkSender::Execute(void)
void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client)
void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
{
ASSERT(m_World != NULL);
// Ask the client if it still wants the chunk:
if (a_Client != NULL)
{
if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ))
if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkZ))
{
return;
}
View
@@ -95,13 +95,11 @@ class cChunkSender:
struct sSendChunk
{
int m_ChunkX;
int m_ChunkY;
int m_ChunkZ;
cClientHandle * m_Client;
sSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) :
sSendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) :
m_ChunkX(a_ChunkX),
m_ChunkY(a_ChunkY),
m_ChunkZ(a_ChunkZ),
m_Client(a_Client)
{
@@ -111,7 +109,6 @@ class cChunkSender:
{
return (
(a_Other.m_ChunkX == m_ChunkX) &&
(a_Other.m_ChunkY == m_ChunkY) &&
(a_Other.m_ChunkZ == m_ChunkZ) &&
(a_Other.m_Client == m_Client)
);
@@ -162,7 +159,7 @@ class cChunkSender:
virtual void BlockEntity (cBlockEntity * a_Entity) override;
/// Sends the specified chunk to a_Client, or to all chunk clients if a_Client == NULL
void SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client);
void SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
} ;
View
@@ -51,7 +51,7 @@ void cChunkStay::Add(int a_ChunkX, int a_ChunkZ)
return;
}
} // for itr - Chunks[]
m_Chunks.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
m_Chunks.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
}
View
@@ -472,13 +472,13 @@ void cClientHandle::StreamChunks(void)
// For each distance touch chunks in a hollow square centered around current position:
for (int i = -d; i <= d; ++i)
{
World->TouchChunk(ChunkPosX + d, ZERO_CHUNK_Y, ChunkPosZ + i);
World->TouchChunk(ChunkPosX - d, ZERO_CHUNK_Y, ChunkPosZ + i);
World->TouchChunk(ChunkPosX + d, ChunkPosZ + i);
World->TouchChunk(ChunkPosX - d, ChunkPosZ + i);
} // for i
for (int i = -d + 1; i < d; ++i)
{
World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ + d);
World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ - d);
World->TouchChunk(ChunkPosX + i, ChunkPosZ + d);
World->TouchChunk(ChunkPosX + i, ChunkPosZ - d);
} // for i
} // for d
}
@@ -501,8 +501,8 @@ void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ)
{
{
cCSLock Lock(m_CSChunkLists);
m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
}
World->SendChunkTo(a_ChunkX, a_ChunkZ, this);
}
@@ -2734,15 +2734,15 @@ bool cClientHandle::HasPluginChannel(const AString & a_PluginChannel)
bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkZ)
{
if (m_State >= csDestroying)
{
return false;
}
cCSLock Lock(m_CSChunkLists);
return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)) != m_ChunksToSend.end());
return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkZ)) != m_ChunksToSend.end());
}
@@ -2758,9 +2758,9 @@ void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ)
LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, this);
cCSLock Lock(m_CSChunkLists);
if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)) == m_ChunksToSend.end())
if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkZ)) == m_ChunksToSend.end())
{
m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
}
}
@@ -2858,11 +2858,27 @@ void cClientHandle::SocketClosed(void)
void cClientHandle::HandleEnchantItem(Byte & WindowID, Byte & Enchantment)
void cClientHandle::HandleEnchantItem(Byte & a_WindowID, Byte & a_Enchantment)
{
cEnchantingWindow * Window = (cEnchantingWindow*)m_Player->GetWindow();
if (a_Enchantment > 2)
{
LOGWARNING("%s attempt to crash the server with invalid enchanting selection!", GetUsername().c_str());
Kick("Invalid enchanting!");
return;
}
if (
(m_Player->GetWindow() == NULL) ||
(m_Player->GetWindow()->GetWindowID() != a_WindowID) ||
(m_Player->GetWindow()->GetWindowType() != cWindow::wtEnchantment)
)
{
return;
}
cEnchantingWindow * Window = (cEnchantingWindow*) m_Player->GetWindow();
cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player);
int BaseEnchantmentLevel = Window->GetPropertyValue(Enchantment);
int BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment);
if (Item.EnchantByXPLevels(BaseEnchantmentLevel))
{
View
@@ -209,7 +209,7 @@ class cClientHandle : // tolua_export
// tolua_end
/** Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) */
bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
bool WantsSendChunk(int a_ChunkX, int a_ChunkZ);
/** Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) */
void AddWantedChunk(int a_ChunkX, int a_ChunkZ);
@@ -269,7 +269,7 @@ class cClientHandle : // tolua_export
void RemoveFromWorld(void);
/** Called when the player will enchant a Item */
void HandleEnchantItem(Byte & WindowID, Byte & Enchantment);
void HandleEnchantItem(Byte & a_WindowID, Byte & a_Enchantment);
private:
View
@@ -90,6 +90,13 @@ void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFa
// Broadcast arrow hit sound
m_World->BroadcastSoundEffect("random.bowhit", (double)X, (double)Y, (double)Z, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
if ((m_World->GetBlock(Hit) == E_BLOCK_TNT) && IsOnFire())
{
m_World->SetBlock(X, Y, Z, E_BLOCK_AIR, 0);
m_World->SpawnPrimedTNT(X, Y, Z);
}
}
@@ -103,8 +110,36 @@ void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
Damage += m_World->GetTickRandomNumber(Damage / 2 + 2);
}
a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1);
int PowerLevel = m_CreatorData.m_Enchantments.GetLevel(cEnchantments::enchPower);
if (PowerLevel > 0)
{
int ExtraDamage = (int)ceil(0.25 * (PowerLevel + 1));
Damage += ExtraDamage;
}
int KnockbackAmount = 1;
int PunchLevel = m_CreatorData.m_Enchantments.GetLevel(cEnchantments::enchPunch);
if (PunchLevel > 0)
{
Vector3d LookVector = GetLookVector();
Vector3f FinalSpeed = Vector3f(0, 0, 0);
switch (PunchLevel)
{
case 1: FinalSpeed = LookVector * Vector3d(5, 0.3, 5); break;
case 2: FinalSpeed = LookVector * Vector3d(8, 0.3, 8); break;
default: break;
}
a_EntityHit.SetSpeed(FinalSpeed);
}
a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, KnockbackAmount);
if (IsOnFire() && !a_EntityHit.IsSubmerged() && !a_EntityHit.IsSwimming())
{
a_EntityHit.StartBurning(100);
}
// Broadcast successful hit sound
GetWorld()->BroadcastSoundEffect("random.successful_hit", GetPosX(), GetPosY(), GetPosZ(), 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
View
@@ -10,6 +10,7 @@
// tolua_begin
class cArrowEntity :
@@ -46,7 +47,7 @@ class cArrowEntity :
/// Returns the damage modifier coeff.
double GetDamageCoeff(void) const { return m_DamageCoeff; }
/// Sets the damage modifier coeff
void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; }
@@ -89,7 +90,7 @@ class cArrowEntity :
/// If true, the arrow is in the process of being collected - don't go to anyone else
bool m_bIsCollected;
/// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air
Vector3i m_HitBlockPos;
View
@@ -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,13 +1462,20 @@ 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
{
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);
}
}
}
View
@@ -34,14 +34,14 @@ cEntityEffect::eType cEntityEffect::GetPotionEffectType(short a_ItemDamage)
case 0x08: return cEntityEffect::effWeakness;
case 0x09: return cEntityEffect::effStrength;
case 0x0a: return cEntityEffect::effSlowness;
case 0x0b: return cEntityEffect::effJumpBoost;
case 0x0c: return cEntityEffect::effInstantDamage;
case 0x0d: return cEntityEffect::effWaterBreathing;
case 0x0e: return cEntityEffect::effInvisibility;
// No effect potions
case 0x00:
case 0x07:
case 0x0b: // Will be potion of leaping in 1.8
case 0x0f:
{
break;
View
@@ -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);
View
@@ -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));
View
@@ -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);
View
@@ -94,14 +94,16 @@ class cProjectileEntity :
*/
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 */
View
@@ -12,8 +12,8 @@
typedef std::list< cFurnaceRecipe::Recipe > RecipeList;
typedef std::list< cFurnaceRecipe::Fuel > FuelList;
typedef std::list<cFurnaceRecipe::cRecipe> RecipeList;
typedef std::list<cFurnaceRecipe::cFuel> FuelList;
@@ -30,7 +30,7 @@ struct cFurnaceRecipe::sFurnaceRecipeState
cFurnaceRecipe::cFurnaceRecipe()
: m_pState( new sFurnaceRecipeState)
: m_pState(new sFurnaceRecipeState)
{
ReloadRecipes();
}
@@ -68,12 +68,18 @@ void cFurnaceRecipe::ReloadRecipes(void)
while (std::getline(f, ParsingLine))
{
LineNum++;
ParsingLine.erase(std::remove_if(ParsingLine.begin(), ParsingLine.end(), isspace), ParsingLine.end()); // Remove ALL whitespace from the line
if (ParsingLine.empty())
{
continue;
}
// Remove comments from the line:
size_t FirstCommentSymbol = ParsingLine.find('#');
if ((FirstCommentSymbol != AString::npos) && (FirstCommentSymbol != 0))
{
ParsingLine.erase(ParsingLine.begin() + (const long)FirstCommentSymbol, ParsingLine.end());
}
switch (ParsingLine[0])
{
case '#':
@@ -103,159 +109,132 @@ void cFurnaceRecipe::ReloadRecipes(void)
void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, int a_LineNum)
void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, unsigned int a_LineNum)
{
// Fuel
int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0;
AString::size_type BeginPos = 1; // Begin at one after exclamation mark (bang)
if (
!ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID
!ReadOptionalNumbers(BeginPos, ":", "=", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health)
!ReadMandatoryNumber(BeginPos, "0123456789", a_Line, a_LineNum, IBurnTime, true) // Read item burn time - last value
)
AString Line(a_Line);
Line.erase(Line.begin()); // Remove the beginning "!"
Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
std::auto_ptr<cItem> Item(new cItem);
int BurnTime;
const AStringVector & Sides = StringSplit(Line, "=");
if (Sides.size() != 2)
{
LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
// Add to fuel list:
Fuel F;
F.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth);
F.BurnTime = IBurnTime;
m_pState->Fuel.push_back(F);
}
if (!ParseItem(Sides[0], *Item))
{
LOGWARNING("furnace.txt: line %d: Cannot parse item \"%s\".", a_LineNum, Sides[0].c_str());
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, int a_LineNum)
{
int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0;
int OItemID = 0, OItemCount = 0, OItemHealth = 0;
AString::size_type BeginPos = 0; // Begin at start of line
if (
!ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID
!ReadOptionalNumbers(BeginPos, ":", "@", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health)
!ReadMandatoryNumber(BeginPos, "=", a_Line, a_LineNum, IBurnTime) || // Read item burn time
!ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, OItemID) || // Read result ID
!ReadOptionalNumbers(BeginPos, ":", "012456789", a_Line, a_LineNum, OItemCount, OItemHealth, true) // Read result count (and optionally health) - last value
)
if (!StringToInteger<int>(Sides[1], BurnTime))
{
LOGWARNING("furnace.txt: line %d: Cannot parse burn time.", a_LineNum);
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
// Add to recipe list
Recipe R;
R.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth);
R.Out = new cItem((ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth);
R.CookTime = IBurnTime;
m_pState->Recipes.push_back(R);
// Add to fuel list:
cFuel Fuel;
Fuel.In = Item.release();
Fuel.BurnTime = BurnTime;
m_pState->Fuel.push_back(Fuel);
}
void cFurnaceRecipe::PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing)
void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum)
{
LOGWARN("Error parsing furnace recipes at line %i pos " SIZE_T_FMT ": missing '%s'", a_Line, a_Position, a_CharactersMissing.c_str());
}
AString Line(a_Line);
Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
int CookTime = 200;
std::auto_ptr<cItem> InputItem(new cItem());
std::auto_ptr<cItem> OutputItem(new cItem());
const AStringVector & Sides = StringSplit(Line, "=");
if (Sides.size() != 2)
{
LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
bool cFurnaceRecipe::ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue)
{
// TODO: replace atoi with std::stoi
AString::size_type End;
if (a_IsLastValue)
const AStringVector & InputSplit = StringSplit(Sides[0], "@");
if (!ParseItem(InputSplit[0], *InputItem))
{
End = a_Text.find_first_not_of(a_Delimiter, a_Begin);
LOGWARNING("furnace.txt: line %d: Cannot parse input item \"%s\".", a_LineNum, InputSplit[0].c_str());
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
else
if (InputSplit.size() > 1)
{
End = a_Text.find_first_of(a_Delimiter, a_Begin);
if (End == AString::npos)
if (!StringToInteger<int>(InputSplit[1], CookTime))
{
PrintParseError(a_Line, a_Begin, a_Delimiter);
return false;
LOGWARNING("furnace.txt: line %d: Cannot parse cook time \"%s\".", a_LineNum, InputSplit[1].c_str());
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
}
// stoi won't throw an exception if the string is alphanumeric, we should check for this
if (!DoesStringContainOnlyNumbers(a_Text.substr(a_Begin, End - a_Begin)))
if (!ParseItem(Sides[1], *OutputItem))
{
PrintParseError(a_Line, a_Begin, "number");
return false;
LOGWARNING("furnace.txt: line %d: Cannot parse output item \"%s\".", a_LineNum, Sides[1].c_str());
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
a_Value = atoi(a_Text.substr(a_Begin, End - a_Begin).c_str());
a_Begin = End + 1; // Jump over delimiter
return true;
cRecipe Recipe;
Recipe.In = InputItem.release();
Recipe.Out = OutputItem.release();
Recipe.CookTime = CookTime;
m_pState->Recipes.push_back(Recipe);
}
bool cFurnaceRecipe::ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue)
bool cFurnaceRecipe::ParseItem(const AString & a_String, cItem & a_Item)
{
// TODO: replace atoi with std::stoi
AString::size_type End, Begin = a_Begin;
AString ItemString = a_String;
const AStringVector & SplitAmount = StringSplit(ItemString, ",");
ItemString = SplitAmount[0];
End = a_Text.find_first_of(a_DelimiterOne, Begin);
if (End != AString::npos)
{
if (DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin)))
{
a_ValueOne = std::atoi(a_Text.substr(Begin, End - Begin).c_str());
Begin = End + 1;
const AStringVector & SplitMeta = StringSplit(ItemString, ":");
ItemString = SplitMeta[0];
if (a_IsLastValue)
{
End = a_Text.find_first_not_of(a_DelimiterTwo, Begin);
}
else
{
End = a_Text.find_first_of(a_DelimiterTwo, Begin);
if (End == AString::npos)
{
PrintParseError(a_Line, Begin, a_DelimiterTwo);
return false;
}
}
// stoi won't throw an exception if the string is alphanumeric, we should check for this
if (!DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin)))
{
PrintParseError(a_Line, Begin, "number");
return false;
}
a_ValueTwo = atoi(a_Text.substr(Begin, End - Begin).c_str());
if (!StringToItem(ItemString, a_Item))
{
return false;
}
a_Begin = End + 1; // Jump over delimiter
return true;
}
else
if (SplitAmount.size() > 1)
{
if (!StringToInteger<char>(SplitAmount[1].c_str(), a_Item.m_ItemCount))
{
return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue);
return false;
}
}
return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue);
}
bool cFurnaceRecipe::DoesStringContainOnlyNumbers(const AString & a_String)
{
// TODO: replace this with std::all_of(a_String.begin(), a_String.end(), isdigit)
return (a_String.find_first_not_of("0123456789") == AString::npos);
if (SplitMeta.size() > 1)
{
if (!StringToInteger<short>(SplitMeta[1].c_str(), a_Item.m_ItemDamage))
{
return false;
}
}
return true;
}
@@ -266,19 +245,19 @@ void cFurnaceRecipe::ClearRecipes(void)
{
for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
{
Recipe R = *itr;
delete R.In;
R.In = NULL;
delete R.Out;
R.Out = NULL;
cRecipe Recipe = *itr;
delete Recipe.In;
Recipe.In = NULL;
delete Recipe.Out;
Recipe.Out = NULL;
}
m_pState->Recipes.clear();
for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
{
Fuel F = *itr;
delete F.In;
F.In = NULL;
cFuel Fuel = *itr;
delete Fuel.In;
Fuel.In = NULL;
}
m_pState->Fuel.clear();
}
@@ -287,21 +266,21 @@ void cFurnaceRecipe::ClearRecipes(void)
const cFurnaceRecipe::Recipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
const cFurnaceRecipe::cRecipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
{
const Recipe * BestRecipe = 0;
const cRecipe * BestRecipe = 0;
for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
{
const Recipe & R = *itr;
if ((R.In->m_ItemType == a_Ingredient.m_ItemType) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount))
const cRecipe & Recipe = *itr;
if ((Recipe.In->m_ItemType == a_Ingredient.m_ItemType) && (Recipe.In->m_ItemCount <= a_Ingredient.m_ItemCount))
{
if (BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount))
if (BestRecipe && (BestRecipe->In->m_ItemCount > Recipe.In->m_ItemCount))
{
continue;
}
else
{
BestRecipe = &R;
BestRecipe = &Recipe;
}
}
}
@@ -317,16 +296,16 @@ int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const
int BestFuel = 0;
for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
{
const Fuel & F = *itr;
if ((F.In->m_ItemType == a_Fuel.m_ItemType) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount))
const cFuel & Fuel = *itr;
if ((Fuel.In->m_ItemType == a_Fuel.m_ItemType) && (Fuel.In->m_ItemCount <= a_Fuel.m_ItemCount))
{
if (BestFuel > 0 && (BestFuel > F.BurnTime))
if (BestFuel > 0 && (BestFuel > Fuel.BurnTime))
{
continue;
}
else
{
BestFuel = F.BurnTime;
BestFuel = Fuel.BurnTime;
}
}
}
View
@@ -19,57 +19,38 @@ class cFurnaceRecipe
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:
void ClearRecipes(void);
/** 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;
View
@@ -151,7 +151,7 @@ void cBioGenCache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a
LOGD("BioGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses));
LOGD("BioGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits);
}
for (int i = 0; i < m_CacheSize; i++)
{
if (
@@ -208,6 +208,59 @@ void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile)
////////////////////////////////////////////////////////////////////////////////
// cBioGenMulticache:
cBioGenMulticache::cBioGenMulticache(cBiomeGen * a_BioGenToCache, size_t a_CacheSize, size_t a_CachesLength) :
m_CachesLength(a_CachesLength)
{
m_Caches.reserve(a_CachesLength);
for (size_t i = 0; i < a_CachesLength; i++)
{
m_Caches.push_back(new cBioGenCache(a_BioGenToCache, a_CacheSize));
}
}
cBioGenMulticache::~cBioGenMulticache()
{
for (std::vector<cBiomeGen*>::iterator it = m_Caches.begin(); it != m_Caches.end(); it++)
{
delete *it;
}
}
void cBioGenMulticache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
const size_t coefficient = 3;
const size_t cacheIdx = ((size_t)a_ChunkX + coefficient * (size_t)a_ChunkZ) % m_CachesLength;
m_Caches[cacheIdx]->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
}
void cBioGenMulticache::InitializeBiomeGen(cIniFile & a_IniFile)
{
for (std::vector<cBiomeGen*>::iterator it = m_Caches.begin(); it != m_Caches.end(); it++)
{
cBiomeGen * tmp = *it;
tmp->InitializeBiomeGen(a_IniFile);
}
}
////////////////////////////////////////////////////////////////////////////////
// cBiomeGenList:
View
@@ -80,6 +80,32 @@ class cBioGenCache :
class cBioGenMulticache :
public cBiomeGen
{
typedef cBiomeGen super;
public:
/*
a_CacheSize defines the size of each singular cache
a_CachesLength defines how many caches are used for the multicache
*/
cBioGenMulticache(cBiomeGen * a_BioGenToCache, size_t a_CacheSize, size_t a_CachesLength); // Doesn't take ownership of a_BioGenToCache
~cBioGenMulticache();
protected:
size_t m_CachesLength;
std::vector<cBiomeGen*> m_Caches;
virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
};
/// Base class for generators that use a list of available biomes. This class takes care of the list.
class cBiomeGenList :
public cBiomeGen
View
@@ -52,10 +52,21 @@ bool cChunkGenerator::Start(cPluginInterface & a_PluginInterface, cChunkSink & a
m_PluginInterface = &a_PluginInterface;
m_ChunkSink = &a_ChunkSink;
MTRand rnd;
m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", (int)rnd.randInt());
// Get the seed; create a new one and log it if not found in the INI file:
if (a_IniFile.HasValue("Seed", "Seed"))
{
m_Seed = a_IniFile.GetValueI("Seed", "Seed");
}
else
{
MTRand rnd;
m_Seed = rnd.randInt();
LOGINFO("Chosen a new random seed for world: %d", m_Seed);
a_IniFile.SetValueI("Seed", "Seed", m_Seed);
}
// Get the generator engine based on the INI file settings:
AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable");
if (NoCaseCompare(GeneratorName, "Noise3D") == 0)
{
m_Generator = new cNoise3DGenerator(*this);
@@ -99,15 +110,15 @@ void cChunkGenerator::Stop(void)
void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate)
{
{
cCSLock Lock(m_CS);
// Check if it is already in the queue:
for (cChunkCoordsList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
for (cChunkCoordsWithBoolList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
{
if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ))
{
// Already in the queue, bail out
return;
@@ -119,7 +130,7 @@ void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_Chunk
{
LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (" SIZE_T_FMT ")", a_ChunkX, a_ChunkZ, m_Queue.size());
}
m_Queue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
m_Queue.push_back(cChunkCoordsWithBool(a_ChunkX, a_ChunkZ, a_ForceGenerate));
}
m_Event.Set();
@@ -229,9 +240,9 @@ void cChunkGenerator::Execute(void)
continue;
}
cChunkCoords coords = m_Queue.front(); // Get next coord from queue
m_Queue.erase( m_Queue.begin()); // Remove coordinate from queue
cChunkCoordsWithBool coords = m_Queue.front(); // Get next coord from queue
bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT);
m_Queue.erase(m_Queue.begin()); // Remove coordinate from queue
Lock.Unlock(); // Unlock ASAP
m_evtRemoved.Set();
@@ -245,8 +256,7 @@ void cChunkGenerator::Execute(void)
LastReportTick = clock();
}
// Hack for regenerating chunks: if Y != 0, the chunk is considered invalid, even if it has its data set
if ((coords.m_ChunkY == 0) && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
if (!coords.m_ForceGenerate && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
{
LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ);
// Already generated, ignore request
@@ -259,8 +269,8 @@ void cChunkGenerator::Execute(void)
continue;
}
LOGD("Generating chunk [%d, %d, %d]", coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
DoGenerate(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
LOGD("Generating chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
DoGenerate(coords.m_ChunkX, coords.m_ChunkZ);
NumChunksGenerated++;
} // while (!bStop)
@@ -269,7 +279,7 @@ void cChunkGenerator::Execute(void)
void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ)
{
ASSERT(m_PluginInterface != NULL);
ASSERT(m_ChunkSink != NULL);
View
@@ -116,7 +116,7 @@ class cChunkGenerator :
void Stop(void);
/// Queues the chunk for generation; removes duplicate requests
void QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate);
/// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading.
void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
@@ -137,10 +137,10 @@ class cChunkGenerator :
int m_Seed;
cCriticalSection m_CS;
cChunkCoordsList m_Queue;
cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate
cEvent m_evtRemoved; ///< Set when an item is removed from the queue
cCriticalSection m_CS;
cChunkCoordsWithBoolList m_Queue;
cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate
cEvent m_evtRemoved; ///< Set when an item is removed from the queue
cGenerator * m_Generator; ///< The actual generator engine used to generate chunks
@@ -154,7 +154,7 @@ class cChunkGenerator :
// cIsThread override:
virtual void Execute(void) override;
void DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void DoGenerate(int a_ChunkX, int a_ChunkZ);
};
View
@@ -230,6 +230,8 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
// Add a cache, if requested:
int CacheSize = a_IniFile.GetValueSetI("Generator", "BiomeGenCacheSize", CacheOffByDefault ? 0 : 64);
int MultiCacheLength = a_IniFile.GetValueSetI("Generator", "BiomeGenMultiCacheLength", 4);
if (CacheSize > 0)
{
if (CacheSize < 4)
@@ -241,7 +243,16 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
}
LOGD("Using a cache for biomegen of size %d.", CacheSize);
m_UnderlyingBiomeGen = m_BiomeGen;
m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize);
if (MultiCacheLength > 0)
{
LOGD("Enabling multicache for biomegen of length %d.", MultiCacheLength);
m_BiomeGen = new cBioGenMulticache(m_UnderlyingBiomeGen, CacheSize, MultiCacheLength);
}
else
{
m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize);
}
}
}
View
@@ -190,31 +190,35 @@ void cItem::FromJson(const Json::Value & a_Value)
bool cItem::IsEnchantable(short item)
bool cItem::IsEnchantable(short a_ItemType, bool a_WithBook)
{
if ((item >= 256) && (item <= 259))
if (
ItemCategory::IsAxe(a_ItemType) ||
ItemCategory::IsSword(a_ItemType) ||
ItemCategory::IsShovel(a_ItemType) ||
ItemCategory::IsPickaxe(a_ItemType) ||
(a_WithBook && ItemCategory::IsHoe(a_ItemType)) ||
ItemCategory::IsArmor(a_ItemType)
)
{
return true;
}
if ((item >= 267) && (item <= 279))
{
return true;
}
if ((item >= 283) && (item <= 286))
{
return true;
}
if ((item >= 290) && (item <= 294))
{
return true;
}
if ((item >= 298) && (item <= 317))
{
return true;
}
if ((item == 346) || (item == 359) || (item == 261))
switch (a_ItemType)
{
return true;
case E_ITEM_BOOK:
case E_ITEM_BOW:
case E_ITEM_FISHING_ROD:
{
return true;
}
case E_ITEM_CARROT_ON_STICK:
case E_ITEM_SHEARS:
case E_ITEM_FLINT_AND_STEEL:
{
return a_WithBook;
}
}
return false;
@@ -299,73 +303,77 @@ int cItem::GetEnchantability()
bool cItem::EnchantByXPLevels(int a_NumXPLevels)
{
if (!cItem::IsEnchantable(m_ItemType) && (m_ItemType != E_ITEM_BOOK))
if (!cItem::IsEnchantable(m_ItemType))
{
return false;
}
int Enchantability = GetEnchantability();
if (Enchantability == 0)
{
return false;
}
cFastRandom Random;
int ModifiedEnchantmentLevel = a_NumXPLevels + (int)Random.NextFloat((float)Enchantability / 4) + (int)Random.NextFloat((float)Enchantability / 4) + 1;
float RandomBonus = 1.0F + (Random.NextFloat(1) + Random.NextFloat(1) - 1.0F) * 0.15F;
int FinalEnchantmentLevel = (int)(ModifiedEnchantmentLevel * RandomBonus + 0.5F);
cWeightedEnchantments enchantments;
cEnchantments::AddItemEnchantmentWeights(enchantments, m_ItemType, FinalEnchantmentLevel);
cWeightedEnchantments Enchantments;
cEnchantments::AddItemEnchantmentWeights(Enchantments, m_ItemType, FinalEnchantmentLevel);
if (m_ItemType == E_ITEM_BOOK)
{
m_ItemType = E_ITEM_ENCHANTED_BOOK;
}
cEnchantments Enchantment1 = cEnchantments::GetRandomEnchantmentFromVector(enchantments);
cEnchantments Enchantment1 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments);
m_Enchantments.AddFromString(Enchantment1.ToString());
cEnchantments::RemoveEnchantmentWeightFromVector(enchantments, Enchantment1);
cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment1);
// Checking for conflicting enchantments
cEnchantments::CheckEnchantmentConflictsFromVector(enchantments, Enchantment1);
cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment1);
float NewEnchantmentLevel = (float)a_NumXPLevels;
// Next Enchantment (Second)
NewEnchantmentLevel = NewEnchantmentLevel / 2;
float SecondEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100;
if (enchantments.empty() || (Random.NextFloat(100) > SecondEnchantmentChance))
if (Enchantments.empty() || (Random.NextFloat(100) > SecondEnchantmentChance))
{
return true;
}
cEnchantments Enchantment2 = cEnchantments::GetRandomEnchantmentFromVector(enchantments);
cEnchantments Enchantment2 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments);
m_Enchantments.AddFromString(Enchantment2.ToString());
cEnchantments::RemoveEnchantmentWeightFromVector(enchantments, Enchantment2);
cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment2);
// Checking for conflicting enchantments
cEnchantments::CheckEnchantmentConflictsFromVector(enchantments, Enchantment2);
cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment2);
// Next Enchantment (Third)
NewEnchantmentLevel = NewEnchantmentLevel / 2;
float ThirdEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100;
if (enchantments.empty() || (Random.NextFloat(100) > ThirdEnchantmentChance))
if (Enchantments.empty() || (Random.NextFloat(100) > ThirdEnchantmentChance))
{
return true;
}
cEnchantments Enchantment3 = cEnchantments::GetRandomEnchantmentFromVector(enchantments);
cEnchantments Enchantment3 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments);
m_Enchantments.AddFromString(Enchantment3.ToString());
cEnchantments::RemoveEnchantmentWeightFromVector(enchantments, Enchantment3);
cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment3);
// Checking for conflicting enchantments
cEnchantments::CheckEnchantmentConflictsFromVector(enchantments, Enchantment3);
cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment3);
// Next Enchantment (Fourth)
NewEnchantmentLevel = NewEnchantmentLevel / 2;
float FourthEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100;
if (enchantments.empty() || (Random.NextFloat(100) > FourthEnchantmentChance))
if (Enchantments.empty() || (Random.NextFloat(100) > FourthEnchantmentChance))
{
return true;
}
cEnchantments Enchantment4 = cEnchantments::GetRandomEnchantmentFromVector(enchantments);
cEnchantments Enchantment4 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments);
m_Enchantments.AddFromString(Enchantment4.ToString());
return true;
View
@@ -183,8 +183,10 @@ class cItem
/** Loads the item data from JSON representation */
void FromJson(const Json::Value & a_Value);
/** Returns true if the specified item type is enchantable (as per 1.2.5 protocol requirements) */
static bool IsEnchantable(short a_ItemType); // tolua_export
/** Returns true if the specified item type is enchantable.
If WithBook is true, the function is used in the anvil inventory with book enchantments.
So it checks the "only book enchantments" too. Example: You can only enchant a hoe with a book. */
static bool IsEnchantable(short a_ItemType, bool a_WithBook = false); // tolua_export
/** Returns the enchantability of the item. When the item hasn't a enchantability, it will returns 0 */
int GetEnchantability(); // tolua_export
View
@@ -75,16 +75,26 @@ class cItemBowHandler :
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())
{
if (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchInfinity) == 0)
{
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);
}
}
} ;
View
@@ -29,7 +29,7 @@ class cItemFoodHandler :
switch (m_ItemType)
{
// Please keep alpha-sorted.
case E_ITEM_BAKED_POTATO: return FoodInfo(6, 7.2);
case E_ITEM_BAKED_POTATO: return FoodInfo(5, 7.2);
case E_ITEM_BREAD: return FoodInfo(5, 6);
// Carrots handled in ItemSeeds
case E_ITEM_COOKED_CHICKEN: return FoodInfo(6, 7.2);
@@ -43,14 +43,16 @@ class cItemFoodHandler :
case E_ITEM_POISONOUS_POTATO: return FoodInfo(2, 1.2);
// Potatoes handled in ItemSeeds
case E_ITEM_PUMPKIN_PIE: return FoodInfo(8, 4.8);
case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4);
case E_ITEM_RAW_BEEF: return FoodInfo(3, 1.8);
case E_ITEM_RAW_CHICKEN: return FoodInfo(2, 1.2);
case E_ITEM_RAW_FISH: return FoodInfo(2, 1.2);
case E_ITEM_RAW_MUTTON: return FoodInfo(2, 1.2);
case E_ITEM_RAW_PORKCHOP: return FoodInfo(3, 1.8);
case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4);
case E_ITEM_ROTTEN_FLESH: return FoodInfo(4, 0.8);
case E_ITEM_SPIDER_EYE: return FoodInfo(2, 3.2);
case E_ITEM_STEAK: return FoodInfo(8, 12.8);
case E_ITEM_MUTTON: return FoodInfo(6, 9.6);
}
LOGWARNING("%s: Unknown food item (%d), returning zero nutrition", __FUNCTION__, m_ItemType);
return FoodInfo(0, 0.f);
View
@@ -207,7 +207,7 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
}
// Food (please keep alpha-sorted):
// (carrots and potatoes handled in SeedHandler as both seed and food
// (carrots and potatoes handled separately in SeedHandler as they're both seed and food)
case E_ITEM_BAKED_POTATO:
case E_ITEM_BREAD:
case E_ITEM_COOKED_CHICKEN:
@@ -217,13 +217,15 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
case E_ITEM_GOLDEN_CARROT:
case E_ITEM_MELON_SLICE:
case E_ITEM_MUSHROOM_SOUP:
case E_ITEM_MUTTON:
case E_ITEM_POISONOUS_POTATO:
case E_ITEM_PUMPKIN_PIE:
case E_ITEM_RED_APPLE:
case E_ITEM_RAW_BEEF:
case E_ITEM_RAW_CHICKEN:
case E_ITEM_RAW_FISH:
case E_ITEM_RAW_MUTTON:
case E_ITEM_RAW_PORKCHOP:
case E_ITEM_RED_APPLE:
case E_ITEM_ROTTEN_FLESH:
case E_ITEM_SPIDER_EYE:
case E_ITEM_STEAK:
View
@@ -37,7 +37,7 @@ class cItemSeedsHandler :
{
switch (m_ItemType)
{
case E_ITEM_CARROT: return FoodInfo(4, 4.8);
case E_ITEM_CARROT: return FoodInfo(3, 4.8);
case E_ITEM_POTATO: return FoodInfo(1, 0.6);
default: return FoodInfo(0, 0);
}
View
@@ -39,6 +39,13 @@ void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
a_Drops.push_back(cItem(E_BLOCK_WOOL, 1, m_WoolColor));
}
int LootingLevel = 0;
if (a_Killer != NULL)
{
LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
}
AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_MUTTON : E_ITEM_RAW_MUTTON);
}
View
@@ -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)
{
}
View
@@ -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;
View
@@ -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();
View
@@ -134,8 +134,8 @@ void cSetChunkData::RemoveInvalidBlockEntities(void)
);
cBlockEntityList::iterator itr2 = itr;
itr2++;
m_BlockEntities.erase(itr);
delete *itr;
m_BlockEntities.erase(itr);
itr = itr2;
}
else
View
@@ -180,3 +180,65 @@ extern int UncompressStringGZIP(const char * a_Data, size_t a_Length, AString &
extern int InflateString(const char * a_Data, size_t a_Length, AString & a_Uncompressed)
{
a_Uncompressed.reserve(a_Length);
char Buffer[64 KiB];
z_stream strm;
memset(&strm, 0, sizeof(strm));
strm.next_in = (Bytef *)a_Data;
strm.avail_in = (uInt)a_Length;
strm.next_out = (Bytef *)Buffer;
strm.avail_out = sizeof(Buffer);
int res = inflateInit(&strm); // Force GZIP decoding
if (res != Z_OK)
{
LOG("%s: inflation initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg);
return res;
}
for (;;)
{
res = inflate(&strm, Z_NO_FLUSH);
switch (res)
{
case Z_OK:
{
// Some data has been uncompressed. Consume the buffer and continue uncompressing
a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out);
strm.next_out = (Bytef *)Buffer;
strm.avail_out = sizeof(Buffer);
if (strm.avail_in == 0)
{
// All data has been uncompressed
inflateEnd(&strm);
return Z_OK;
}
break;
}
case Z_STREAM_END:
{
// Finished uncompressing. Consume the rest of the buffer and return
a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out);
inflateEnd(&strm);
return Z_OK;
}
default:
{
// An error has occurred, log it and return the error value
LOG("%s: inflation failed: %d (\"%s\").", __FUNCTION__, res, strm.msg);
inflateEnd(&strm);
return res;
}
} // switch (res)
} // while (true)
}
View
@@ -21,5 +21,7 @@ extern int CompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_
/// Uncompresses a_Data into a_Uncompressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib
extern int UncompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Uncompressed);
/** Uncompresses a_Data into a_Uncompressed using Inflate; returns Z_OK for success or Z_XXX error constants same as zlib */
extern int InflateString(const char * a_Data, size_t a_Length, AString & a_Uncompressed);
Oops, something went wrong.