Merge branch 'master' into blocks
Conflicts: MCServer/furnace.txt MCServer/items.ini
This commit is contained in:
commit
cc9acd13b9
@ -2846,7 +2846,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 bd23915df763b182610c6163c5ff2d64a0756560
|
||||
Subproject commit 7cc99285ae5117418f657c3b295ca71f2b75b4f4
|
@ -88,13 +88,13 @@ 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
|
||||
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, PrismarineCrystal, 1:2, 2:1, 2:2, 2:3, 3: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
|
||||
@ -329,8 +329,8 @@ MelonSeeds = MelonSlice, *
|
||||
PumpkinSeeds, 4 = Pumpkin, *
|
||||
PumpkinPie = Pumpkin, * | Sugar, * | egg, *
|
||||
Wheat, 9 = Haybale, *
|
||||
MushroomStew = Cooked Rabbit, 2:1 | Carrot, 1:2 | BakedPotato 2:2 | BrownMushroom 3:2 | Bowl, 2:3
|
||||
MushroomStew = Cooked Rabbit, 2:1 | Carrot, 1:2 | BakedPotato 2:2 | RedMushroom 3:2 | Bowl, 2:3
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
@ -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,66 +42,67 @@
|
||||
#--------------------------
|
||||
# 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
|
||||
349:1:1 @ 200 = 350:1:1 # 1 Raw Salmon -> 1 Cooked Salmon
|
||||
17:1 @ 200 = 263:1:1 # 1 Log -> 1 Charcoal
|
||||
81:1 @ 200 = 351:1:2 # 1 Cactus -> 1 Green Dye
|
||||
19:1:1 @ 200 = 19:1 # 1 Wet Sponge -> 1 Sponge
|
||||
98:1 @ 200 = 98:1:2 # 1 Stonebrick -> 1 Cracked Stonebrick
|
||||
411:1 @ 200 = 412:1 # 1 Raw Rabbit -> 1 Cooked Rabbit
|
||||
423:1 @ 200 = 424:1 # 1 Raw Mutton -> 1 Cooked Mutton
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#--------------------------
|
||||
# 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
|
||||
! 188:1 = 300 # 1 Spruce Fence -> 15 sec
|
||||
! 189:1 = 300 # 1 Birch Fence -> 15 sec
|
||||
! 190:1 = 300 # 1 Jungle Fence -> 15 sec
|
||||
! 191:1 = 300 # 1 Dark Oak Fence -> 15 sec
|
||||
! 192:1 = 300 # 1 Acacia 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
|
||||
! 183:1 = 300 # 1 Spruce Fence Gate -> 15 sec
|
||||
! 184:1 = 300 # 1 Birch Fence Gate -> 15 sec
|
||||
! 185:1 = 300 # 1 Jungle Fence Gate -> 15 sec
|
||||
! 186:1 = 300 # 1 Dark Oak Fence Gate -> 15 sec
|
||||
! 187:1 = 300 # 1 Acacia 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
|
||||
|
||||
|
||||
|
@ -411,6 +411,7 @@ darkprismarine=168:2
|
||||
sealantern=169
|
||||
haybale=170
|
||||
carpet=171
|
||||
hardenedclay=172
|
||||
redsandstone=179
|
||||
chiseledredsandstone=179:1
|
||||
smoothredsandstone=179:2
|
||||
|
@ -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
|
||||
|
@ -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 :
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -30,18 +30,18 @@ public:
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
|
||||
if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow))
|
||||
|
||||
cEnchantments Enchantments = a_Player->GetInventory().GetEquippedItem().m_Enchantments;
|
||||
if (Enchantments.GetLevel(cEnchantments::enchSilkTouch) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
|
||||
if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
|
||||
// This is called later than the real destroying of this ice block
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
|
||||
// This is called later than the real destroying of this ice block
|
||||
}
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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));
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
// tolua_begin
|
||||
|
||||
class cArrowEntity :
|
||||
@ -46,7 +47,7 @@ public:
|
||||
|
||||
/// Returns the damage modifier coeff.
|
||||
double GetDamageCoeff(void) const { return m_DamageCoeff; }
|
||||
|
||||
|
||||
/// Sets the damage modifier coeff
|
||||
void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; }
|
||||
|
||||
@ -89,7 +90,7 @@ protected:
|
||||
|
||||
/// If true, the arrow is in the process of being collected - don't go to anyone else
|
||||
bool m_bIsCollected;
|
||||
|
||||
|
||||
/// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air
|
||||
Vector3i m_HitBlockPos;
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "../Tracer.h"
|
||||
#include "Player.h"
|
||||
#include "Items/ItemHandler.h"
|
||||
#include "../FastRandom.h"
|
||||
|
||||
|
||||
|
||||
@ -316,8 +317,107 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
|
||||
// IsOnGround() only is false if the player is moving downwards
|
||||
// TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain)
|
||||
if (!Player->IsOnGround())
|
||||
const cEnchantments & Enchantments = Player->GetEquippedItem().m_Enchantments;
|
||||
|
||||
int SharpnessLevel = Enchantments.GetLevel(cEnchantments::enchSharpness);
|
||||
int SmiteLevel = Enchantments.GetLevel(cEnchantments::enchSmite);
|
||||
int BaneOfArthropodsLevel = Enchantments.GetLevel(cEnchantments::enchBaneOfArthropods);
|
||||
|
||||
if (SharpnessLevel > 0)
|
||||
{
|
||||
a_TDI.FinalDamage += (int)ceil(1.25 * SharpnessLevel);
|
||||
}
|
||||
else if (SmiteLevel > 0)
|
||||
{
|
||||
if (IsMob())
|
||||
{
|
||||
cMonster * Monster = (cMonster *)this;
|
||||
switch (Monster->GetMobType())
|
||||
{
|
||||
case cMonster::mtSkeleton:
|
||||
case cMonster::mtZombie:
|
||||
case cMonster::mtWither:
|
||||
case cMonster::mtZombiePigman:
|
||||
{
|
||||
a_TDI.FinalDamage += (int)ceil(2.5 * SmiteLevel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (BaneOfArthropodsLevel > 0)
|
||||
{
|
||||
if (IsMob())
|
||||
{
|
||||
cMonster * Monster = (cMonster *)this;
|
||||
switch (Monster->GetMobType())
|
||||
{
|
||||
case cMonster::mtSpider:
|
||||
case cMonster::mtCaveSpider:
|
||||
case cMonster::mtSilverfish:
|
||||
{
|
||||
a_TDI.RawDamage += (int)ceil(2.5 * BaneOfArthropodsLevel);
|
||||
// TODO: Add slowness effect
|
||||
|
||||
break;
|
||||
};
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int FireAspectLevel = Enchantments.GetLevel(cEnchantments::enchFireAspect);
|
||||
if (FireAspectLevel > 0)
|
||||
{
|
||||
int BurnTicks = 3;
|
||||
|
||||
if (FireAspectLevel > 1)
|
||||
{
|
||||
BurnTicks += 4 * (FireAspectLevel - 1);
|
||||
}
|
||||
if (!IsMob() && !IsSubmerged() && !IsSwimming())
|
||||
{
|
||||
StartBurning(BurnTicks * 20);
|
||||
}
|
||||
else if (IsMob() && !IsSubmerged() && !IsSwimming())
|
||||
{
|
||||
cMonster * Monster = (cMonster *)this;
|
||||
switch (Monster->GetMobType())
|
||||
{
|
||||
case cMonster::mtGhast:
|
||||
case cMonster::mtZombiePigman:
|
||||
case cMonster::mtMagmaCube:
|
||||
{
|
||||
break;
|
||||
};
|
||||
default: StartBurning(BurnTicks * 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ThornsLevel = 0;
|
||||
const cItem ArmorItems[] = { GetEquippedHelmet(), GetEquippedChestplate(), GetEquippedLeggings(), GetEquippedBoots() };
|
||||
for (size_t i = 0; i < ARRAYCOUNT(ArmorItems); i++)
|
||||
{
|
||||
const cItem & Item = ArmorItems[i];
|
||||
ThornsLevel = std::max(ThornsLevel, Item.m_Enchantments.GetLevel(cEnchantments::enchThorns));
|
||||
}
|
||||
|
||||
if (ThornsLevel > 0)
|
||||
{
|
||||
int Chance = ThornsLevel * 15;
|
||||
|
||||
cFastRandom Random;
|
||||
int RandomValue = Random.GenerateRandomInteger(0, 100);
|
||||
|
||||
if (RandomValue <= Chance)
|
||||
{
|
||||
a_TDI.Attacker->TakeDamage(dtAttack, this, 0, Random.GenerateRandomInteger(1, 4), 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Player->IsOnGround())
|
||||
{
|
||||
if ((a_TDI.DamageType == dtAttack) || (a_TDI.DamageType == dtArrowAttack))
|
||||
{
|
||||
a_TDI.FinalDamage += 2;
|
||||
@ -328,13 +428,123 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
Player->GetStatManager().AddValue(statDamageDealt, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5));
|
||||
}
|
||||
|
||||
if (IsPlayer())
|
||||
{
|
||||
double TotalEPF = 0.0;
|
||||
double EPFProtection = 0.00;
|
||||
double EPFFireProtection = 0.00;
|
||||
double EPFBlastProtection = 0.00;
|
||||
double EPFProjectileProtection = 0.00;
|
||||
double EPFFeatherFalling = 0.00;
|
||||
|
||||
const cItem ArmorItems[] = { GetEquippedHelmet(), GetEquippedChestplate(), GetEquippedLeggings(), GetEquippedBoots() };
|
||||
for (size_t i = 0; i < ARRAYCOUNT(ArmorItems); i++)
|
||||
{
|
||||
const cItem & Item = ArmorItems[i];
|
||||
int Level = Item.m_Enchantments.GetLevel(cEnchantments::enchProtection);
|
||||
if (Level > 0)
|
||||
{
|
||||
EPFProtection += (6 + Level * Level) * 0.75 / 3;
|
||||
}
|
||||
|
||||
Level = Item.m_Enchantments.GetLevel(cEnchantments::enchFireProtection);
|
||||
if (Level > 0)
|
||||
{
|
||||
EPFFireProtection += (6 + Level * Level) * 1.25 / 3;
|
||||
}
|
||||
|
||||
Level = Item.m_Enchantments.GetLevel(cEnchantments::enchFeatherFalling);
|
||||
if (Level > 0)
|
||||
{
|
||||
EPFFeatherFalling += (6 + Level * Level) * 2.5 / 3;
|
||||
}
|
||||
|
||||
Level = Item.m_Enchantments.GetLevel(cEnchantments::enchBlastProtection);
|
||||
if (Level > 0)
|
||||
{
|
||||
EPFBlastProtection += (6 + Level * Level) * 1.5 / 3;
|
||||
}
|
||||
|
||||
Level = Item.m_Enchantments.GetLevel(cEnchantments::enchProjectileProtection);
|
||||
if (Level > 0)
|
||||
{
|
||||
EPFProjectileProtection += (6 + Level * Level) * 1.5 / 3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TotalEPF = EPFProtection + EPFFireProtection + EPFFeatherFalling + EPFBlastProtection + EPFProjectileProtection;
|
||||
|
||||
EPFProtection = EPFProtection / TotalEPF;
|
||||
EPFFireProtection = EPFFireProtection / TotalEPF;
|
||||
EPFFeatherFalling = EPFFeatherFalling / TotalEPF;
|
||||
EPFBlastProtection = EPFBlastProtection / TotalEPF;
|
||||
EPFProjectileProtection = EPFProjectileProtection / TotalEPF;
|
||||
|
||||
if (TotalEPF > 25)
|
||||
{
|
||||
TotalEPF = 25;
|
||||
}
|
||||
|
||||
cFastRandom Random;
|
||||
float RandomValue = Random.GenerateRandomInteger(50, 100) * 0.01f;
|
||||
|
||||
TotalEPF = ceil(TotalEPF * RandomValue);
|
||||
|
||||
if (TotalEPF > 20)
|
||||
{
|
||||
TotalEPF = 20;
|
||||
}
|
||||
|
||||
EPFProtection = TotalEPF * EPFProtection;
|
||||
EPFFireProtection = TotalEPF * EPFFireProtection;
|
||||
EPFFeatherFalling = TotalEPF * EPFFeatherFalling;
|
||||
EPFBlastProtection = TotalEPF * EPFBlastProtection;
|
||||
EPFProjectileProtection = TotalEPF * EPFProjectileProtection;
|
||||
|
||||
int RemovedDamage = 0;
|
||||
|
||||
if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtAdmin))
|
||||
{
|
||||
RemovedDamage += (int)ceil(EPFProtection * 0.04 * a_TDI.FinalDamage);
|
||||
}
|
||||
|
||||
if ((a_TDI.DamageType == dtFalling) || (a_TDI.DamageType == dtFall) || (a_TDI.DamageType == dtEnderPearl))
|
||||
{
|
||||
RemovedDamage += (int)ceil(EPFFeatherFalling * 0.04 * a_TDI.FinalDamage);
|
||||
}
|
||||
|
||||
if (a_TDI.DamageType == dtBurning)
|
||||
{
|
||||
RemovedDamage += (int)ceil(EPFFireProtection * 0.04 * a_TDI.FinalDamage);
|
||||
}
|
||||
|
||||
if (a_TDI.DamageType == dtExplosion)
|
||||
{
|
||||
RemovedDamage += (int)ceil(EPFBlastProtection * 0.04 * a_TDI.FinalDamage);
|
||||
}
|
||||
|
||||
if (a_TDI.DamageType == dtProjectile)
|
||||
{
|
||||
RemovedDamage += (int)ceil(EPFBlastProtection * 0.04 * a_TDI.FinalDamage);
|
||||
}
|
||||
|
||||
if (a_TDI.FinalDamage < RemovedDamage)
|
||||
{
|
||||
RemovedDamage = 0;
|
||||
}
|
||||
|
||||
a_TDI.FinalDamage -= RemovedDamage;
|
||||
}
|
||||
|
||||
m_Health -= (short)a_TDI.FinalDamage;
|
||||
|
||||
// TODO: Apply damage to armor
|
||||
|
||||
m_Health = std::max(m_Health, 0);
|
||||
|
||||
if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL)) // Knockback for only players and mobs
|
||||
// Add knockback:
|
||||
if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL))
|
||||
{
|
||||
int KnockbackLevel = a_TDI.Attacker->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchKnockback); // More common enchantment
|
||||
if (KnockbackLevel < 1)
|
||||
@ -1252,6 +1462,8 @@ void cEntity::HandleAir(void)
|
||||
// See if the entity is /submerged/ water (block above is water)
|
||||
// Get the type of block the entity is standing in:
|
||||
|
||||
int RespirationLevel = GetEquippedHelmet().m_Enchantments.GetLevel(cEnchantments::enchRespiration);
|
||||
|
||||
if (IsSubmerged())
|
||||
{
|
||||
if (!IsPlayer()) // Players control themselves
|
||||
@ -1259,6 +1471,11 @@ void cEntity::HandleAir(void)
|
||||
SetSpeedY(1); // Float in the water
|
||||
}
|
||||
|
||||
if (RespirationLevel > 0)
|
||||
{
|
||||
((cPawn *)this)->AddEntityEffect(cEntityEffect::effNightVision, 200, 5, 0);
|
||||
}
|
||||
|
||||
if (m_AirLevel <= 0)
|
||||
{
|
||||
// Runs the air tick timer to check whether the player should be damaged
|
||||
@ -1285,6 +1502,12 @@ void cEntity::HandleAir(void)
|
||||
// Set the air back to maximum
|
||||
m_AirLevel = MAX_AIR_LEVEL;
|
||||
m_AirTickTimer = DROWNING_TICKS;
|
||||
|
||||
if (RespirationLevel > 0)
|
||||
{
|
||||
m_AirTickTimer = DROWNING_TICKS + (RespirationLevel * 15 * 20);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
||||
@ -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,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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFurnaceRecipe::PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing)
|
||||
{
|
||||
LOGWARN("Error parsing furnace recipes at line %i pos " SIZE_T_FMT ": missing '%s'", a_Line, a_Position, a_CharactersMissing.c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFurnaceRecipe::ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue)
|
||||
{
|
||||
// TODO: replace atoi with std::stoi
|
||||
AString::size_type End;
|
||||
if (a_IsLastValue)
|
||||
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))
|
||||
{
|
||||
LOGWARNING("furnace.txt: line %d: Cannot parse cook time \"%s\".", a_LineNum, InputSplit[1].c_str());
|
||||
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ParseItem(Sides[1], *OutputItem))
|
||||
{
|
||||
LOGWARNING("furnace.txt: line %d: Cannot parse output item \"%s\".", a_LineNum, Sides[1].c_str());
|
||||
LOGINFO("Offending line: \"%s\"", a_Line.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
cRecipe Recipe;
|
||||
Recipe.In = InputItem.release();
|
||||
Recipe.Out = OutputItem.release();
|
||||
Recipe.CookTime = CookTime;
|
||||
m_pState->Recipes.push_back(Recipe);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFurnaceRecipe::ParseItem(const AString & a_String, cItem & a_Item)
|
||||
{
|
||||
AString ItemString = a_String;
|
||||
|
||||
const AStringVector & SplitAmount = StringSplit(ItemString, ",");
|
||||
ItemString = SplitAmount[0];
|
||||
|
||||
const AStringVector & SplitMeta = StringSplit(ItemString, ":");
|
||||
ItemString = SplitMeta[0];
|
||||
|
||||
if (!StringToItem(ItemString, a_Item))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SplitAmount.size() > 1)
|
||||
{
|
||||
if (!StringToInteger<char>(SplitAmount[1].c_str(), a_Item.m_ItemCount))
|
||||
{
|
||||
PrintParseError(a_Line, a_Begin, a_Delimiter);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// stoi won't throw an exception if the string is alphanumeric, we should check for this
|
||||
if (!DoesStringContainOnlyNumbers(a_Text.substr(a_Begin, End - a_Begin)))
|
||||
{
|
||||
PrintParseError(a_Line, a_Begin, "number");
|
||||
return false;
|
||||
}
|
||||
a_Value = atoi(a_Text.substr(a_Begin, End - a_Begin).c_str());
|
||||
|
||||
a_Begin = End + 1; // Jump over delimiter
|
||||
if (SplitMeta.size() > 1)
|
||||
{
|
||||
if (!StringToInteger<short>(SplitMeta[1].c_str(), a_Item.m_ItemDamage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
} ;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user