diff options
Diffstat (limited to 'src')
83 files changed, 2230 insertions, 437 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 4fe86e1c5..1e5dfd2fe 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -40,7 +40,7 @@ $cfile "../Entities/Painting.h" $cfile "../Entities/Pickup.h" $cfile "../Entities/ProjectileEntity.h" $cfile "../Entities/TNTEntity.h" -$cfile "../Entities/Effects.h" +$cfile "../Entities/EntityEffect.h" $cfile "../Server.h" $cfile "../World.h" $cfile "../Inventory.h" diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 8ba20c026..0a0534724 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -57,13 +57,14 @@ public: virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) = 0; virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0; + virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) = 0; virtual bool OnHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) = 0; virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) = 0; - virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer) = 0; + virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) = 0; virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) = 0; virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) = 0; virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 104380ea4..c36763c38 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -420,6 +420,26 @@ bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason +bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_ADD_EFFECT]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Entity, a_EffectType, a_EffectDurationTicks, a_EffectIntensity, a_DistanceModifier, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) { cCSLock Lock(m_CriticalSection); @@ -575,14 +595,14 @@ bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, -bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer) +bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) { cCSLock Lock(m_CriticalSection); bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - m_LuaState.Call((int)(**itr), &a_Victim, a_Killer, cLuaState::Return, res); + m_LuaState.Call((int)(**itr), &a_Victim, a_Killer, &a_TDI, cLuaState::Return, res); if (res) { return true; @@ -1524,6 +1544,7 @@ const char * cPluginLua::GetHookFnName(int a_HookType) case cPluginManager::HOOK_CRAFTING_NO_RECIPE: return "OnCraftingNoRecipe"; case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect"; case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation"; + case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect"; case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand"; case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake"; case cPluginManager::HOOK_KILLING: return "OnKilling"; diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index 9c9de95c6..4a9634c09 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -80,13 +80,14 @@ public: virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) override; virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override; + virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) override; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) override; virtual bool OnHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) override; virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) override; - virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer) override; + virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) override; virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override; virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) override; virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 7e6502515..6b5e60dcc 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -474,6 +474,27 @@ bool cPluginManager::CallHookDisconnect(cClientHandle & a_Client, const AString +bool cPluginManager::CallHookEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_ENTITY_ADD_EFFECT); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnEntityAddEffect(a_Entity, a_EffectType, a_EffectDurationTicks, a_EffectIntensity, a_DistanceModifier)) + { + return true; + } + } + return false; +} + + + + + bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) { FIND_HOOK(HOOK_EXECUTE_COMMAND); @@ -588,14 +609,14 @@ bool cPluginManager::CallHookHopperPushingItem(cWorld & a_World, cHopperEntity & -bool cPluginManager::CallHookKilling(cEntity & a_Victim, cEntity * a_Killer) +bool cPluginManager::CallHookKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) { FIND_HOOK(HOOK_KILLING); VERIFY_HOOK; for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) { - if ((*itr)->OnKilling(a_Victim, a_Killer)) + if ((*itr)->OnKilling(a_Victim, a_Killer, a_TDI)) { return true; } diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index d435024bb..94a366d33 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -82,6 +82,7 @@ public: // tolua_export HOOK_CRAFTING_NO_RECIPE, HOOK_DISCONNECT, HOOK_PLAYER_ANIMATION, + HOOK_ENTITY_ADD_EFFECT, HOOK_EXECUTE_COMMAND, HOOK_EXPLODED, HOOK_EXPLODING, @@ -183,13 +184,14 @@ public: // tolua_export bool CallHookCollectingPickup (cPlayer * a_Player, cPickup & a_Pickup); bool CallHookCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason); + bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier); bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == NULL, it is a console cmd bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); bool CallHookHandshake (cClientHandle * a_ClientHandle, const AString & a_Username); bool CallHookHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum); bool CallHookHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum); - bool CallHookKilling (cEntity & a_Victim, cEntity * a_Killer); + bool CallHookKilling (cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI); bool CallHookLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username); bool CallHookPlayerAnimation (cPlayer & a_Player, int a_Animation); bool CallHookPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp index 0012742fb..108c2ce1b 100644 --- a/src/BlockEntities/DropSpenserEntity.cpp +++ b/src/BlockEntities/DropSpenserEntity.cpp @@ -42,7 +42,7 @@ cDropSpenserEntity::~cDropSpenserEntity() void cDropSpenserEntity::AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction) { - switch (a_Direction) + switch (a_Direction & 0x07) // Vanilla uses the 8th bit to determine power state - we don't { case E_META_DROPSPENSER_FACING_YM: a_BlockY--; return; case E_META_DROPSPENSER_FACING_YP: a_BlockY++; return; diff --git a/src/BlockEntities/FlowerPotEntity.h b/src/BlockEntities/FlowerPotEntity.h index da3fe9b7e..e3570eb9a 100644 --- a/src/BlockEntities/FlowerPotEntity.h +++ b/src/BlockEntities/FlowerPotEntity.h @@ -53,7 +53,7 @@ public: cItem GetItem(void) const { return m_Item; } /** Set the item in the flower pot */ - void SetItem(const cItem a_Item) { m_Item = a_Item; } + void SetItem(const cItem & a_Item) { m_Item = a_Item; } // tolua_end diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index aaf82d150..7af4b9d5d 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -368,13 +368,13 @@ bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) { - cChestEntity * Chest = (cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ); - if (Chest == NULL) + cChestEntity * MainChest = (cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ); + if (MainChest == NULL) { LOGWARNING("%s: A chest entity was not found where expected, at {%d, %d, %d}", __FUNCTION__, m_PosX, m_PosY + 1, m_PosZ); return false; } - if (MoveItemsFromGrid(*Chest)) + if (MoveItemsFromGrid(*MainChest)) { // Moved the item from the chest directly above the hopper return true; @@ -403,20 +403,20 @@ bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) } BLOCKTYPE Block = Neighbor->GetBlock(x, m_PosY + 1, z); - if (Block != Chest->GetBlockType()) + if (Block != MainChest->GetBlockType()) { // Not the same kind of chest continue; } - Chest = (cChestEntity *)Neighbor->GetBlockEntity(m_PosX + Coords[i].x, m_PosY + 1, m_PosZ + Coords[i].z); - if (Chest == NULL) + cChestEntity * SideChest = (cChestEntity *)Neighbor->GetBlockEntity(m_PosX + Coords[i].x, m_PosY + 1, m_PosZ + Coords[i].z); + if (SideChest == NULL) { LOGWARNING("%s: A chest entity was not found where expected, at {%d, %d, %d}", __FUNCTION__, m_PosX + Coords[i].x, m_PosY + 1, m_PosZ + Coords[i].z); } else { - if (MoveItemsFromGrid(*Chest)) + if (MoveItemsFromGrid(*SideChest)) { return true; } diff --git a/src/BlockID.cpp b/src/BlockID.cpp index bfe826f40..8edc51664 100644 --- a/src/BlockID.cpp +++ b/src/BlockID.cpp @@ -363,6 +363,7 @@ AString DamageTypeToString(eDamageType a_DamageType) case dtLightning: return "dtLightning"; case dtOnFire: return "dtOnFire"; case dtPoisoning: return "dtPoisoning"; + case dtWithering: return "dtWithering"; case dtPotionOfHarming: return "dtPotionOfHarming"; case dtRangedAttack: return "dtRangedAttack"; case dtStarving: return "dtStarving"; @@ -408,6 +409,7 @@ eDamageType StringToDamageType(const AString & a_DamageTypeString) { dtCactusContact, "dtCactusContact"}, { dtLavaContact, "dtLavaContact"}, { dtPoisoning, "dtPoisoning"}, + { dtWithering, "dtWithering"}, { dtOnFire, "dtOnFire"}, { dtFireContact, "dtFireContact"}, { dtInVoid, "dtInVoid"}, @@ -433,6 +435,7 @@ eDamageType StringToDamageType(const AString & a_DamageTypeString) { dtCactusContact, "dtCacti"}, { dtLavaContact, "dtLava"}, { dtPoisoning, "dtPoison"}, + { dtWithering, "dtWither"}, { dtOnFire, "dtBurning"}, { dtFireContact, "dtInFire"}, { dtAdmin, "dtPlugin"}, diff --git a/src/BlockID.h b/src/BlockID.h index 272fd319d..ba05e9e1a 100644 --- a/src/BlockID.h +++ b/src/BlockID.h @@ -319,7 +319,8 @@ enum ENUM_ITEM_ID E_ITEM_GHAST_TEAR = 370, E_ITEM_GOLD_NUGGET = 371, E_ITEM_NETHER_WART = 372, - E_ITEM_POTIONS = 373, + E_ITEM_POTION = 373, + E_ITEM_POTIONS = 373, // OBSOLETE, use E_ITEM_POTION instead E_ITEM_GLASS_BOTTLE = 374, E_ITEM_SPIDER_EYE = 375, E_ITEM_FERMENTED_SPIDER_EYE = 376, @@ -811,6 +812,7 @@ enum eDamageType dtCactusContact, // Contact with a cactus block dtLavaContact, // Contact with a lava block dtPoisoning, // Having the poison effect + dtWithering, // Having the wither effect dtOnFire, // Being on fire dtFireContact, // Standing inside a fire block dtInVoid, // Falling into the Void (Y < 0) @@ -837,6 +839,7 @@ enum eDamageType dtCacti = dtCactusContact, dtLava = dtLavaContact, dtPoison = dtPoisoning, + dtWither = dtWithering, dtBurning = dtOnFire, dtInFire = dtFireContact, dtPlugin = dtAdmin, diff --git a/src/BlockInfo.cpp b/src/BlockInfo.cpp index 4fc1d602c..b4b98a2f0 100644 --- a/src/BlockInfo.cpp +++ b/src/BlockInfo.cpp @@ -14,7 +14,7 @@ cBlockInfo::cBlockInfo() , m_Transparent(false) , m_OneHitDig(false) , m_PistonBreakable(false) - , m_IsSnowable(true) + , m_IsSnowable(false) , m_RequiresSpecialTool(false) , m_IsSolid(true) , m_FullyOccupiesVoxel(false) @@ -65,48 +65,117 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info) } // Emissive blocks + a_Info[E_BLOCK_ACTIVE_COMPARATOR ].m_LightValue = 9; + a_Info[E_BLOCK_BEACON ].m_LightValue = 15; + a_Info[E_BLOCK_BREWING_STAND ].m_LightValue = 1; + a_Info[E_BLOCK_BROWN_MUSHROOM ].m_LightValue = 1; + a_Info[E_BLOCK_BURNING_FURNACE ].m_LightValue = 13; + a_Info[E_BLOCK_DRAGON_EGG ].m_LightValue = 1; + a_Info[E_BLOCK_END_PORTAL ].m_LightValue = 15; + a_Info[E_BLOCK_END_PORTAL_FRAME ].m_LightValue = 1; + a_Info[E_BLOCK_ENDER_CHEST ].m_LightValue = 7; a_Info[E_BLOCK_FIRE ].m_LightValue = 15; a_Info[E_BLOCK_GLOWSTONE ].m_LightValue = 15; a_Info[E_BLOCK_JACK_O_LANTERN ].m_LightValue = 15; a_Info[E_BLOCK_LAVA ].m_LightValue = 15; - a_Info[E_BLOCK_STATIONARY_LAVA ].m_LightValue = 15; - a_Info[E_BLOCK_END_PORTAL ].m_LightValue = 15; - a_Info[E_BLOCK_REDSTONE_LAMP_ON ].m_LightValue = 15; - a_Info[E_BLOCK_TORCH ].m_LightValue = 14; - a_Info[E_BLOCK_BURNING_FURNACE ].m_LightValue = 13; a_Info[E_BLOCK_NETHER_PORTAL ].m_LightValue = 11; + a_Info[E_BLOCK_REDSTONE_LAMP_ON ].m_LightValue = 15; a_Info[E_BLOCK_REDSTONE_ORE_GLOWING].m_LightValue = 9; a_Info[E_BLOCK_REDSTONE_REPEATER_ON].m_LightValue = 9; a_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_LightValue = 7; - a_Info[E_BLOCK_BREWING_STAND ].m_LightValue = 1; - a_Info[E_BLOCK_BROWN_MUSHROOM ].m_LightValue = 1; - a_Info[E_BLOCK_DRAGON_EGG ].m_LightValue = 1; + a_Info[E_BLOCK_STATIONARY_LAVA ].m_LightValue = 15; + a_Info[E_BLOCK_TORCH ].m_LightValue = 14; // Spread blocks + a_Info[E_BLOCK_ACTIVATOR_RAIL ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_ACTIVE_COMPARATOR ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_AIR ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_ANVIL ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_BEACON ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_BED ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_BIG_FLOWER ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_BROWN_MUSHROOM ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_BREWING_STAND ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_CACTUS ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_CAKE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_CARPET ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_CARROTS ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_CAULDRON ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_CHEST ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_COBBLESTONE_WALL ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_COCOA_POD ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_COBWEB ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_CROPS ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_DANDELION ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_DAYLIGHT_SENSOR ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_DEAD_BUSH ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_DETECTOR_RAIL ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_DRAGON_EGG ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_ENCHANTMENT_TABLE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_ENDER_CHEST ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_END_PORTAL ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_END_PORTAL_FRAME ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_FARMLAND ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_FENCE ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_FENCE_GATE ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_FIRE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_FLOWER ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_FLOWER_POT ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_GLASS ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_GLASS_PANE ].m_SpreadLightFalloff = 1; - a_Info[E_BLOCK_GLOWSTONE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_HEAD ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_HOPPER ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_ICE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_IRON_BARS ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_IRON_DOOR ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_LADDER ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_LEAVES ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_LEVER ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_LILY_PAD ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_MELON_STEM ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_MOB_SPAWNER ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_NETHER_PORTAL ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_NETHER_WART ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_NEW_LEAVES ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_PISTON ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_PISTON_EXTENSION ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_PISTON_MOVED_BLOCK ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_POTATOES ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_POWERED_RAIL ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_PUMPKIN_STEM ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_RAIL ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_RED_MUSHROOM ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_REDSTONE_REPEATER_OFF].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_REDSTONE_REPEATER_ON].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_REDSTONE_TORCH_OFF ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_REDSTONE_WIRE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_SAPLING ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_SIGN_POST ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_SNOW ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_STAINED_GLASS ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_STAINED_GLASS_PANE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_STICKY_PISTON ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_STONE_BUTTON ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_STONE_SLAB ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_SUGARCANE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_TALL_GRASS ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_TORCH ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_TRAPDOOR ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_TRAPPED_CHEST ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_TRIPWIRE ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_TRIPWIRE_HOOK ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_VINES ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_WALLSIGN ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_WOODEN_BUTTON ].m_SpreadLightFalloff = 1; a_Info[E_BLOCK_WOODEN_DOOR ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_WOODEN_PRESSURE_PLATE].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_WOODEN_SLAB ].m_SpreadLightFalloff = 1; // Light in water and lava dissapears faster: a_Info[E_BLOCK_LAVA ].m_SpreadLightFalloff = 3; @@ -117,19 +186,34 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info) // Transparent blocks a_Info[E_BLOCK_ACTIVATOR_RAIL ].m_Transparent = true; + a_Info[E_BLOCK_ACTIVE_COMPARATOR ].m_Transparent = true; a_Info[E_BLOCK_AIR ].m_Transparent = true; a_Info[E_BLOCK_ANVIL ].m_Transparent = true; + a_Info[E_BLOCK_BEACON ].m_Transparent = true; + a_Info[E_BLOCK_BED ].m_Transparent = true; a_Info[E_BLOCK_BIG_FLOWER ].m_Transparent = true; a_Info[E_BLOCK_BROWN_MUSHROOM ].m_Transparent = true; + a_Info[E_BLOCK_BREWING_STAND ].m_Transparent = true; + a_Info[E_BLOCK_CACTUS ].m_Transparent = true; a_Info[E_BLOCK_CAKE ].m_Transparent = true; + a_Info[E_BLOCK_CARPET ].m_Transparent = true; a_Info[E_BLOCK_CARROTS ].m_Transparent = true; + a_Info[E_BLOCK_CAULDRON ].m_Transparent = true; a_Info[E_BLOCK_CHEST ].m_Transparent = true; a_Info[E_BLOCK_COBBLESTONE_WALL ].m_Transparent = true; + a_Info[E_BLOCK_COCOA_POD ].m_Transparent = true; a_Info[E_BLOCK_COBWEB ].m_Transparent = true; a_Info[E_BLOCK_CROPS ].m_Transparent = true; a_Info[E_BLOCK_DANDELION ].m_Transparent = true; + a_Info[E_BLOCK_DAYLIGHT_SENSOR ].m_Transparent = true; + a_Info[E_BLOCK_DEAD_BUSH ].m_Transparent = true; a_Info[E_BLOCK_DETECTOR_RAIL ].m_Transparent = true; + a_Info[E_BLOCK_DRAGON_EGG ].m_Transparent = true; + a_Info[E_BLOCK_ENCHANTMENT_TABLE ].m_Transparent = true; a_Info[E_BLOCK_ENDER_CHEST ].m_Transparent = true; + a_Info[E_BLOCK_END_PORTAL ].m_Transparent = true; + a_Info[E_BLOCK_END_PORTAL_FRAME ].m_Transparent = true; + a_Info[E_BLOCK_FARMLAND ].m_Transparent = true; a_Info[E_BLOCK_FENCE ].m_Transparent = true; a_Info[E_BLOCK_FENCE_GATE ].m_Transparent = true; a_Info[E_BLOCK_FIRE ].m_Transparent = true; @@ -139,43 +223,61 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info) a_Info[E_BLOCK_GLASS_PANE ].m_Transparent = true; a_Info[E_BLOCK_HEAD ].m_Transparent = true; a_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_Transparent = true; + a_Info[E_BLOCK_HOPPER ].m_Transparent = true; a_Info[E_BLOCK_ICE ].m_Transparent = true; + a_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_Transparent = true; + a_Info[E_BLOCK_IRON_BARS ].m_Transparent = true; a_Info[E_BLOCK_IRON_DOOR ].m_Transparent = true; a_Info[E_BLOCK_LADDER ].m_Transparent = true; a_Info[E_BLOCK_LAVA ].m_Transparent = true; a_Info[E_BLOCK_LEAVES ].m_Transparent = true; a_Info[E_BLOCK_LEVER ].m_Transparent = true; + a_Info[E_BLOCK_LILY_PAD ].m_Transparent = true; a_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_Transparent = true; a_Info[E_BLOCK_MELON_STEM ].m_Transparent = true; + a_Info[E_BLOCK_MOB_SPAWNER ].m_Transparent = true; a_Info[E_BLOCK_NETHER_BRICK_FENCE ].m_Transparent = true; + a_Info[E_BLOCK_NETHER_PORTAL ].m_Transparent = true; + a_Info[E_BLOCK_NETHER_WART ].m_Transparent = true; a_Info[E_BLOCK_NEW_LEAVES ].m_Transparent = true; + a_Info[E_BLOCK_PISTON ].m_Transparent = true; + a_Info[E_BLOCK_PISTON_EXTENSION ].m_Transparent = true; + a_Info[E_BLOCK_PISTON_MOVED_BLOCK ].m_Transparent = true; a_Info[E_BLOCK_POTATOES ].m_Transparent = true; a_Info[E_BLOCK_POWERED_RAIL ].m_Transparent = true; - a_Info[E_BLOCK_PISTON_EXTENSION ].m_Transparent = true; a_Info[E_BLOCK_PUMPKIN_STEM ].m_Transparent = true; a_Info[E_BLOCK_RAIL ].m_Transparent = true; a_Info[E_BLOCK_RED_MUSHROOM ].m_Transparent = true; + a_Info[E_BLOCK_REDSTONE_REPEATER_OFF].m_Transparent = true; + a_Info[E_BLOCK_REDSTONE_REPEATER_ON].m_Transparent = true; + a_Info[E_BLOCK_REDSTONE_TORCH_OFF ].m_Transparent = true; + a_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_Transparent = true; + a_Info[E_BLOCK_REDSTONE_WIRE ].m_Transparent = true; + a_Info[E_BLOCK_SAPLING ].m_Transparent = true; a_Info[E_BLOCK_SIGN_POST ].m_Transparent = true; a_Info[E_BLOCK_SNOW ].m_Transparent = true; a_Info[E_BLOCK_STAINED_GLASS ].m_Transparent = true; a_Info[E_BLOCK_STAINED_GLASS_PANE ].m_Transparent = true; a_Info[E_BLOCK_STATIONARY_LAVA ].m_Transparent = true; a_Info[E_BLOCK_STATIONARY_WATER ].m_Transparent = true; + a_Info[E_BLOCK_STICKY_PISTON ].m_Transparent = true; a_Info[E_BLOCK_STONE_BUTTON ].m_Transparent = true; a_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_Transparent = true; + a_Info[E_BLOCK_STONE_SLAB ].m_Transparent = true; + a_Info[E_BLOCK_SUGARCANE ].m_Transparent = true; + a_Info[E_BLOCK_TALL_GRASS ].m_Transparent = true; + a_Info[E_BLOCK_TORCH ].m_Transparent = true; + a_Info[E_BLOCK_TRAPDOOR ].m_Transparent = true; a_Info[E_BLOCK_TRAPPED_CHEST ].m_Transparent = true; a_Info[E_BLOCK_TRIPWIRE ].m_Transparent = true; a_Info[E_BLOCK_TRIPWIRE_HOOK ].m_Transparent = true; - a_Info[E_BLOCK_TALL_GRASS ].m_Transparent = true; - a_Info[E_BLOCK_TORCH ].m_Transparent = true; a_Info[E_BLOCK_VINES ].m_Transparent = true; a_Info[E_BLOCK_WALLSIGN ].m_Transparent = true; a_Info[E_BLOCK_WATER ].m_Transparent = true; a_Info[E_BLOCK_WOODEN_BUTTON ].m_Transparent = true; a_Info[E_BLOCK_WOODEN_DOOR ].m_Transparent = true; a_Info[E_BLOCK_WOODEN_PRESSURE_PLATE].m_Transparent = true; - - // TODO: Any other transparent blocks? + a_Info[E_BLOCK_WOODEN_SLAB ].m_Transparent = true; // One hit break blocks: @@ -185,10 +287,12 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info) a_Info[E_BLOCK_CARROTS ].m_OneHitDig = true; a_Info[E_BLOCK_CROPS ].m_OneHitDig = true; a_Info[E_BLOCK_DANDELION ].m_OneHitDig = true; + a_Info[E_BLOCK_DEAD_BUSH ].m_OneHitDig = true; a_Info[E_BLOCK_FIRE ].m_OneHitDig = true; a_Info[E_BLOCK_FLOWER ].m_OneHitDig = true; a_Info[E_BLOCK_FLOWER_POT ].m_OneHitDig = true; a_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_OneHitDig = true; + a_Info[E_BLOCK_LILY_PAD ].m_OneHitDig = true; a_Info[E_BLOCK_MELON_STEM ].m_OneHitDig = true; a_Info[E_BLOCK_POTATOES ].m_OneHitDig = true; a_Info[E_BLOCK_PUMPKIN_STEM ].m_OneHitDig = true; @@ -212,24 +316,32 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info) a_Info[E_BLOCK_BED ].m_PistonBreakable = true; a_Info[E_BLOCK_BIG_FLOWER ].m_PistonBreakable = true; a_Info[E_BLOCK_BROWN_MUSHROOM ].m_PistonBreakable = true; + a_Info[E_BLOCK_CACTUS ].m_PistonBreakable = true; a_Info[E_BLOCK_CAKE ].m_PistonBreakable = true; + a_Info[E_BLOCK_CARROTS ].m_PistonBreakable = true; + a_Info[E_BLOCK_COCOA_POD ].m_PistonBreakable = true; a_Info[E_BLOCK_COBWEB ].m_PistonBreakable = true; a_Info[E_BLOCK_CROPS ].m_PistonBreakable = true; a_Info[E_BLOCK_DANDELION ].m_PistonBreakable = true; a_Info[E_BLOCK_DEAD_BUSH ].m_PistonBreakable = true; + a_Info[E_BLOCK_DRAGON_EGG ].m_PistonBreakable = true; a_Info[E_BLOCK_FIRE ].m_PistonBreakable = true; a_Info[E_BLOCK_FLOWER ].m_PistonBreakable = true; + a_Info[E_BLOCK_FLOWER_POT ].m_PistonBreakable = true; a_Info[E_BLOCK_HEAD ].m_PistonBreakable = true; a_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_PistonBreakable = true; a_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_PistonBreakable = true; a_Info[E_BLOCK_IRON_DOOR ].m_PistonBreakable = true; a_Info[E_BLOCK_JACK_O_LANTERN ].m_PistonBreakable = true; a_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_PistonBreakable = true; + a_Info[E_BLOCK_LILY_PAD ].m_PistonBreakable = true; a_Info[E_BLOCK_LADDER ].m_PistonBreakable = true; a_Info[E_BLOCK_LAVA ].m_PistonBreakable = true; a_Info[E_BLOCK_LEVER ].m_PistonBreakable = true; a_Info[E_BLOCK_MELON ].m_PistonBreakable = true; a_Info[E_BLOCK_MELON_STEM ].m_PistonBreakable = true; + a_Info[E_BLOCK_NETHER_WART ].m_PistonBreakable = true; + a_Info[E_BLOCK_POTATOES ].m_PistonBreakable = true; a_Info[E_BLOCK_PUMPKIN ].m_PistonBreakable = true; a_Info[E_BLOCK_PUMPKIN_STEM ].m_PistonBreakable = true; a_Info[E_BLOCK_REDSTONE_REPEATER_OFF].m_PistonBreakable = true; @@ -239,6 +351,8 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info) a_Info[E_BLOCK_REDSTONE_WIRE ].m_PistonBreakable = true; a_Info[E_BLOCK_RED_MUSHROOM ].m_PistonBreakable = true; a_Info[E_BLOCK_REEDS ].m_PistonBreakable = true; + a_Info[E_BLOCK_SAPLING ].m_PistonBreakable = true; + a_Info[E_BLOCK_SIGN_POST ].m_PistonBreakable = true; a_Info[E_BLOCK_SNOW ].m_PistonBreakable = true; a_Info[E_BLOCK_STATIONARY_LAVA ].m_PistonBreakable = true; a_Info[E_BLOCK_STATIONARY_WATER ].m_PistonBreakable = true; @@ -246,61 +360,85 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info) a_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_PistonBreakable = true; a_Info[E_BLOCK_TALL_GRASS ].m_PistonBreakable = true; a_Info[E_BLOCK_TORCH ].m_PistonBreakable = true; + a_Info[E_BLOCK_TRAPDOOR ].m_PistonBreakable = true; a_Info[E_BLOCK_TRIPWIRE ].m_PistonBreakable = true; a_Info[E_BLOCK_TRIPWIRE_HOOK ].m_PistonBreakable = true; a_Info[E_BLOCK_VINES ].m_PistonBreakable = true; + a_Info[E_BLOCK_WALLSIGN ].m_PistonBreakable = true; a_Info[E_BLOCK_WATER ].m_PistonBreakable = true; a_Info[E_BLOCK_WOODEN_BUTTON ].m_PistonBreakable = true; a_Info[E_BLOCK_WOODEN_DOOR ].m_PistonBreakable = true; a_Info[E_BLOCK_WOODEN_PRESSURE_PLATE].m_PistonBreakable = true; - // Blocks that cannot be snowed over: - a_Info[E_BLOCK_ACTIVE_COMPARATOR ].m_IsSnowable = false; - a_Info[E_BLOCK_AIR ].m_IsSnowable = false; - a_Info[E_BLOCK_BIG_FLOWER ].m_IsSnowable = false; - a_Info[E_BLOCK_BROWN_MUSHROOM ].m_IsSnowable = false; - a_Info[E_BLOCK_CACTUS ].m_IsSnowable = false; - a_Info[E_BLOCK_CHEST ].m_IsSnowable = false; - a_Info[E_BLOCK_CROPS ].m_IsSnowable = false; - a_Info[E_BLOCK_COBBLESTONE_WALL ].m_IsSnowable = false; - a_Info[E_BLOCK_DANDELION ].m_IsSnowable = false; - a_Info[E_BLOCK_FIRE ].m_IsSnowable = false; - a_Info[E_BLOCK_FLOWER ].m_IsSnowable = false; - a_Info[E_BLOCK_GLASS ].m_IsSnowable = false; - a_Info[E_BLOCK_ICE ].m_IsSnowable = false; - a_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_IsSnowable = false; - a_Info[E_BLOCK_LAVA ].m_IsSnowable = false; - a_Info[E_BLOCK_LILY_PAD ].m_IsSnowable = false; - a_Info[E_BLOCK_REDSTONE_REPEATER_OFF].m_IsSnowable = false; - a_Info[E_BLOCK_REDSTONE_REPEATER_ON].m_IsSnowable = false; - a_Info[E_BLOCK_REDSTONE_TORCH_OFF ].m_IsSnowable = false; - a_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_IsSnowable = false; - a_Info[E_BLOCK_REDSTONE_WIRE ].m_IsSnowable = false; - a_Info[E_BLOCK_RED_MUSHROOM ].m_IsSnowable = false; - a_Info[E_BLOCK_REEDS ].m_IsSnowable = false; - a_Info[E_BLOCK_SAPLING ].m_IsSnowable = false; - a_Info[E_BLOCK_SIGN_POST ].m_IsSnowable = false; - a_Info[E_BLOCK_SNOW ].m_IsSnowable = false; - a_Info[E_BLOCK_STAINED_GLASS ].m_IsSnowable = false; - a_Info[E_BLOCK_STAINED_GLASS_PANE ].m_IsSnowable = false; - a_Info[E_BLOCK_STATIONARY_LAVA ].m_IsSnowable = false; - a_Info[E_BLOCK_STATIONARY_WATER ].m_IsSnowable = false; - a_Info[E_BLOCK_TALL_GRASS ].m_IsSnowable = false; - a_Info[E_BLOCK_TNT ].m_IsSnowable = false; - a_Info[E_BLOCK_TORCH ].m_IsSnowable = false; - a_Info[E_BLOCK_TRAPPED_CHEST ].m_IsSnowable = false; - a_Info[E_BLOCK_TRIPWIRE ].m_IsSnowable = false; - a_Info[E_BLOCK_TRIPWIRE_HOOK ].m_IsSnowable = false; - a_Info[E_BLOCK_VINES ].m_IsSnowable = false; - a_Info[E_BLOCK_WALLSIGN ].m_IsSnowable = false; - a_Info[E_BLOCK_WATER ].m_IsSnowable = false; - a_Info[E_BLOCK_RAIL ].m_IsSnowable = false; - a_Info[E_BLOCK_ACTIVATOR_RAIL ].m_IsSnowable = false; - a_Info[E_BLOCK_POWERED_RAIL ].m_IsSnowable = false; - a_Info[E_BLOCK_DETECTOR_RAIL ].m_IsSnowable = false; - a_Info[E_BLOCK_COBWEB ].m_IsSnowable = false; - a_Info[E_BLOCK_HEAD ].m_IsSnowable = false; + // Blocks that can be snowed over: + a_Info[E_BLOCK_BEDROCK ].m_IsSnowable = true; + a_Info[E_BLOCK_BLOCK_OF_COAL ].m_IsSnowable = true; + a_Info[E_BLOCK_BLOCK_OF_REDSTONE ].m_IsSnowable = true; + a_Info[E_BLOCK_BOOKCASE ].m_IsSnowable = true; + a_Info[E_BLOCK_BRICK ].m_IsSnowable = true; + a_Info[E_BLOCK_CLAY ].m_IsSnowable = true; + a_Info[E_BLOCK_CRAFTING_TABLE ].m_IsSnowable = true; + a_Info[E_BLOCK_COAL_ORE ].m_IsSnowable = true; + a_Info[E_BLOCK_COMMAND_BLOCK ].m_IsSnowable = true; + a_Info[E_BLOCK_COBBLESTONE ].m_IsSnowable = true; + a_Info[E_BLOCK_DIAMOND_BLOCK ].m_IsSnowable = true; + a_Info[E_BLOCK_DIAMOND_ORE ].m_IsSnowable = true; + a_Info[E_BLOCK_DIRT ].m_IsSnowable = true; + a_Info[E_BLOCK_DISPENSER ].m_IsSnowable = true; + a_Info[E_BLOCK_DOUBLE_STONE_SLAB ].m_IsSnowable = true; + a_Info[E_BLOCK_DOUBLE_WOODEN_SLAB ].m_IsSnowable = true; + a_Info[E_BLOCK_DROPPER ].m_IsSnowable = true; + a_Info[E_BLOCK_EMERALD_BLOCK ].m_IsSnowable = true; + a_Info[E_BLOCK_EMERALD_ORE ].m_IsSnowable = true; + a_Info[E_BLOCK_END_STONE ].m_IsSnowable = true; + a_Info[E_BLOCK_FURNACE ].m_IsSnowable = true; + a_Info[E_BLOCK_GLOWSTONE ].m_IsSnowable = true; + a_Info[E_BLOCK_GOLD_BLOCK ].m_IsSnowable = true; + a_Info[E_BLOCK_GOLD_ORE ].m_IsSnowable = true; + a_Info[E_BLOCK_GRASS ].m_IsSnowable = true; + a_Info[E_BLOCK_GRAVEL ].m_IsSnowable = true; + a_Info[E_BLOCK_HARDENED_CLAY ].m_IsSnowable = true; + a_Info[E_BLOCK_HAY_BALE ].m_IsSnowable = true; + a_Info[E_BLOCK_HUGE_BROWN_MUSHROOM ].m_IsSnowable = true; + a_Info[E_BLOCK_HUGE_RED_MUSHROOM ].m_IsSnowable = true; + a_Info[E_BLOCK_IRON_BLOCK ].m_IsSnowable = true; + a_Info[E_BLOCK_IRON_ORE ].m_IsSnowable = true; + a_Info[E_BLOCK_JACK_O_LANTERN ].m_IsSnowable = true; + a_Info[E_BLOCK_JUKEBOX ].m_IsSnowable = true; + a_Info[E_BLOCK_LAPIS_BLOCK ].m_IsSnowable = true; + a_Info[E_BLOCK_LAPIS_ORE ].m_IsSnowable = true; + a_Info[E_BLOCK_LEAVES ].m_IsSnowable = true; + a_Info[E_BLOCK_LIT_FURNACE ].m_IsSnowable = true; + a_Info[E_BLOCK_LOG ].m_IsSnowable = true; + a_Info[E_BLOCK_MELON ].m_IsSnowable = true; + a_Info[E_BLOCK_MOSSY_COBBLESTONE ].m_IsSnowable = true; + a_Info[E_BLOCK_MYCELIUM ].m_IsSnowable = true; + a_Info[E_BLOCK_NETHER_BRICK ].m_IsSnowable = true; + a_Info[E_BLOCK_NETHER_QUARTZ_ORE ].m_IsSnowable = true; + a_Info[E_BLOCK_NETHERRACK ].m_IsSnowable = true; + a_Info[E_BLOCK_NEW_LEAVES ].m_IsSnowable = true; + a_Info[E_BLOCK_NEW_LOG ].m_IsSnowable = true; + a_Info[E_BLOCK_NOTE_BLOCK ].m_IsSnowable = true; + a_Info[E_BLOCK_OBSIDIAN ].m_IsSnowable = true; + a_Info[E_BLOCK_PLANKS ].m_IsSnowable = true; + a_Info[E_BLOCK_PUMPKIN ].m_IsSnowable = true; + a_Info[E_BLOCK_QUARTZ_BLOCK ].m_IsSnowable = true; + a_Info[E_BLOCK_REDSTONE_LAMP_OFF ].m_IsSnowable = true; + a_Info[E_BLOCK_REDSTONE_LAMP_ON ].m_IsSnowable = true; + a_Info[E_BLOCK_REDSTONE_ORE ].m_IsSnowable = true; + a_Info[E_BLOCK_REDSTONE_ORE_GLOWING].m_IsSnowable = true; + a_Info[E_BLOCK_SAND ].m_IsSnowable = true; + a_Info[E_BLOCK_SANDSTONE ].m_IsSnowable = true; + a_Info[E_BLOCK_SILVERFISH_EGG ].m_IsSnowable = true; + a_Info[E_BLOCK_SNOW_BLOCK ].m_IsSnowable = true; + a_Info[E_BLOCK_SOULSAND ].m_IsSnowable = true; + a_Info[E_BLOCK_SPONGE ].m_IsSnowable = true; + a_Info[E_BLOCK_STAINED_CLAY ].m_IsSnowable = true; + a_Info[E_BLOCK_STONE ].m_IsSnowable = true; + a_Info[E_BLOCK_STONE_BRICKS ].m_IsSnowable = true; + a_Info[E_BLOCK_TNT ].m_IsSnowable = true; + a_Info[E_BLOCK_WOOL ].m_IsSnowable = true; // Blocks that don't drop without a special tool: @@ -323,6 +461,7 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info) a_Info[E_BLOCK_IRON_ORE ].m_RequiresSpecialTool = true; a_Info[E_BLOCK_LAPIS_BLOCK ].m_RequiresSpecialTool = true; a_Info[E_BLOCK_LAPIS_ORE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_LEAVES ].m_RequiresSpecialTool = true; a_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_RequiresSpecialTool = true; a_Info[E_BLOCK_MOSSY_COBBLESTONE ].m_RequiresSpecialTool = true; a_Info[E_BLOCK_NETHERRACK ].m_RequiresSpecialTool = true; @@ -455,9 +594,9 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info) a_Info[E_BLOCK_SILVERFISH_EGG ].m_FullyOccupiesVoxel = true; a_Info[E_BLOCK_SPONGE ].m_FullyOccupiesVoxel = true; a_Info[E_BLOCK_STAINED_CLAY ].m_FullyOccupiesVoxel = true; - a_Info[E_BLOCK_WOOL ].m_FullyOccupiesVoxel = true; a_Info[E_BLOCK_STONE ].m_FullyOccupiesVoxel = true; a_Info[E_BLOCK_STONE_BRICKS ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_WOOL ].m_FullyOccupiesVoxel = true; } diff --git a/src/Blocks/BlockCloth.h b/src/Blocks/BlockCloth.h index a136d3b9d..3c1ae7c25 100644 --- a/src/Blocks/BlockCloth.h +++ b/src/Blocks/BlockCloth.h @@ -16,13 +16,6 @@ public: { } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_BLOCK_WOOL, 1, a_BlockMeta)); - } - - virtual const char * GetStepSound(void) override { return "step.cloth"; diff --git a/src/Blocks/BlockFarmland.h b/src/Blocks/BlockFarmland.h index 3dd5bcd1d..390b6e5ee 100644 --- a/src/Blocks/BlockFarmland.h +++ b/src/Blocks/BlockFarmland.h @@ -19,15 +19,13 @@ class cBlockFarmlandHandler : public cBlockHandler { - typedef cBlockHandler super; public: - cBlockFarmlandHandler(void) : - super(E_BLOCK_FARMLAND) + cBlockFarmlandHandler(BLOCKTYPE a_BlockType) : + cBlockHandler(a_BlockType) { } - virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override { bool Found = false; @@ -105,6 +103,11 @@ public: } } } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.Add(E_BLOCK_DIRT, 1, 0); // Reset meta + } } ; diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index 730774ab1..cef1f5f09 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -211,7 +211,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_EMERALD_ORE: return new cBlockOreHandler (a_BlockType); case E_BLOCK_ENCHANTMENT_TABLE: return new cBlockEnchantmentTableHandler(a_BlockType); case E_BLOCK_ENDER_CHEST: return new cBlockEnderchestHandler (a_BlockType); - case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler ( ); + case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler (a_BlockType); case E_BLOCK_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType); case E_BLOCK_FIRE: return new cBlockFireHandler (a_BlockType); case E_BLOCK_FLOWER_POT: return new cBlockFlowerPotHandler (a_BlockType); diff --git a/src/Blocks/BlockHayBale.h b/src/Blocks/BlockHayBale.h index 5b646e264..3c6472adb 100644 --- a/src/Blocks/BlockHayBale.h +++ b/src/Blocks/BlockHayBale.h @@ -1,7 +1,6 @@ #pragma once -#include "BlockHandler.h" #include "BlockSideways.h" diff --git a/src/Blocks/BlockLadder.h b/src/Blocks/BlockLadder.h index a605edf3f..120396c7d 100644 --- a/src/Blocks/BlockLadder.h +++ b/src/Blocks/BlockLadder.h @@ -3,17 +3,19 @@ #include "BlockHandler.h" #include "../World.h" +#include "ClearMetaOnDrop.h" class cBlockLadderHandler : - public cMetaRotator<cBlockHandler, 0x07, 0x02, 0x05, 0x03, 0x04> + public cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x07, 0x02, 0x05, 0x03, 0x04> > { + typedef cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x07, 0x02, 0x05, 0x03, 0x04> > super; public: cBlockLadderHandler(BLOCKTYPE a_BlockType) - : cMetaRotator<cBlockHandler, 0x07, 0x02, 0x05, 0x03, 0x04>(a_BlockType) + : super(a_BlockType) { } @@ -41,21 +43,27 @@ public: } - static NIBBLETYPE DirectionToMetaData(eBlockFace a_Direction) // tolua_export - { // tolua_export + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.Add(m_BlockType, 1, 0); // Reset meta + } + + + static NIBBLETYPE DirectionToMetaData(eBlockFace a_Direction) + { switch (a_Direction) { case BLOCK_FACE_ZM: return 0x2; case BLOCK_FACE_ZP: return 0x3; case BLOCK_FACE_XM: return 0x4; case BLOCK_FACE_XP: return 0x5; - default: return 0x2; + default: return 0x2; } - } // tolua_export + } - static eBlockFace MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export - { // tolua_export + static eBlockFace MetaDataToDirection(NIBBLETYPE a_MetaData) + { switch (a_MetaData) { case 0x2: return BLOCK_FACE_ZM; @@ -64,10 +72,10 @@ public: case 0x5: return BLOCK_FACE_XP; default: return BLOCK_FACE_ZM; } - } // tolua_export + } - /// Finds a suitable Direction for the Ladder. Returns BLOCK_FACE_BOTTOM on failure + /** Finds a suitable Direction for the Ladder. Returns BLOCK_FACE_BOTTOM on failure */ static eBlockFace FindSuitableBlockFace(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) { for (int FaceInt = BLOCK_FACE_ZM; FaceInt <= BLOCK_FACE_XP; FaceInt++) diff --git a/src/Blocks/BlockLilypad.h b/src/Blocks/BlockLilypad.h index 2dd4ec768..53277caa5 100644 --- a/src/Blocks/BlockLilypad.h +++ b/src/Blocks/BlockLilypad.h @@ -8,18 +8,14 @@ class cBlockLilypadHandler : - public cBlockHandler + public cClearMetaOnDrop<cBlockHandler> { + typedef cClearMetaOnDrop<cBlockHandler> super; public: - cBlockLilypadHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + + cBlockLilypadHandler(BLOCKTYPE a_BlockType) : + super(a_BlockType) { - // Reset meta to zero - a_Pickups.push_back(cItem(E_BLOCK_LILY_PAD, 1, 0)); } }; diff --git a/src/Blocks/BlockPiston.h b/src/Blocks/BlockPiston.h index 27a44d829..1c8ac6a35 100644 --- a/src/Blocks/BlockPiston.h +++ b/src/Blocks/BlockPiston.h @@ -94,7 +94,6 @@ private: switch (a_BlockType) { case E_BLOCK_ANVIL: - case E_BLOCK_BED: case E_BLOCK_BEDROCK: case E_BLOCK_BREWING_STAND: case E_BLOCK_CHEST: diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index ac2b9817a..4692a47df 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -6,13 +6,16 @@ class cBlockPumpkinHandler : - public cMetaRotator<cBlockHandler, 0x07, 0x02, 0x03, 0x00, 0x01, false> + public cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x07, 0x02, 0x03, 0x00, 0x01, false> > { + typedef cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x07, 0x02, 0x03, 0x00, 0x01, false> > super; public: - cBlockPumpkinHandler(BLOCKTYPE a_BlockType) - : cMetaRotator<cBlockHandler, 0x07, 0x02, 0x03, 0x00, 0x01, false>(a_BlockType) + + cBlockPumpkinHandler(BLOCKTYPE a_BlockType) : + super(a_BlockType) { } + virtual void OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override { diff --git a/src/Blocks/BlockSideways.h b/src/Blocks/BlockSideways.h index d67c3aa24..5b37efd75 100644 --- a/src/Blocks/BlockSideways.h +++ b/src/Blocks/BlockSideways.h @@ -32,36 +32,36 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { - a_Pickups.Add(m_BlockType, 1, a_BlockMeta & 0x3); + a_Pickups.Add(m_BlockType, 1, a_BlockMeta & 0x3); // Reset meta } - inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace, NIBBLETYPE a_WoodMeta) + inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace, NIBBLETYPE a_Meta) { switch (a_BlockFace) { case BLOCK_FACE_YM: case BLOCK_FACE_YP: { - return a_WoodMeta; // Top or bottom, just return original + return a_Meta; // Top or bottom, just return original } case BLOCK_FACE_ZP: case BLOCK_FACE_ZM: { - return a_WoodMeta | 0x8; // North or south + return a_Meta | 0x8; // North or south } case BLOCK_FACE_XP: case BLOCK_FACE_XM: { - return a_WoodMeta | 0x4; // East or west + return a_Meta | 0x4; // East or west } default: { ASSERT(!"Unhandled block face!"); - return a_WoodMeta | 0xC; // No idea, give a special meta (all sides bark) + return a_Meta | 0xC; // No idea, give a special meta } } } diff --git a/src/Blocks/ClearMetaOnDrop.h b/src/Blocks/ClearMetaOnDrop.h new file mode 100644 index 000000000..3f8c33819 --- /dev/null +++ b/src/Blocks/ClearMetaOnDrop.h @@ -0,0 +1,24 @@ + +#pragma once + +// mixin for use to clear meta values when the block is converted to a pickup + +// Usage: inherit from this class, passing the parent class as the parameter Base +// For example to use in class Foo which should inherit Bar use +// class Foo : public cClearMetaOnDrop<Bar>; + +template<class Base> +class cClearMetaOnDrop : public Base +{ +public: + + cClearMetaOnDrop(BLOCKTYPE a_BlockType) : + Base(a_BlockType) + {} + + virtual ~cClearMetaOnDrop() {} + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(this->m_BlockType)); + } +}; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5ff1c4e3c..fdc33cd82 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,7 +43,7 @@ set(BINDING_DEPENDECIES Cuboid.h Defines.h Enchantments.h - Entities/Effects.h + Entities/EntityEffect.h Entities/Entity.h Entities/Floater.h Entities/Pawn.h diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 9bc3e5c49..b9bb39aa8 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -652,7 +652,7 @@ void cChunkMap::BroadcastSoundEffect(const AString & a_SoundName, double a_X, do cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(std::floor(a_X), std::floor(a_Z), ChunkX, ChunkZ); + cChunkDef::BlockToChunk((int)std::floor(a_X), (int)std::floor(a_Z), ChunkX, ChunkZ); cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); if (Chunk == NULL) { @@ -1549,7 +1549,7 @@ void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player) cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk->IsValid()) + if ((Chunk != NULL) && (Chunk->IsValid())) { Chunk->SendBlockTo(a_X, a_Y, a_Z, a_Player->GetClientHandle()); } diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 340db6394..bd6fc1287 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -290,17 +290,18 @@ void cClientHandle::Kick(const AString & a_Reason) -void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID) +void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties) { if (m_State != csAuthenticating) { return; } - ASSERT( m_Player == NULL ); + ASSERT(m_Player == NULL); m_Username = a_Name; m_UUID = a_UUID; + m_Properties = a_Properties; // Send login success (if the protocol supports it): m_Protocol->SendLoginSuccess(); @@ -324,7 +325,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID) if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player)) { cRoot::Get()->BroadcastChatJoin(Printf("%s has joined the game", GetUsername().c_str())); - LOGINFO("Player %s has joined the game.", m_Username.c_str()); + LOGINFO("Player %s has joined the game", m_Username.c_str()); } m_ConfirmPosition = m_Player->GetPosition(); @@ -876,7 +877,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB case DIG_STATUS_SHOOT_EAT: { cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); - if (ItemHandler->IsFood()) + if (ItemHandler->IsFood() || ItemHandler->IsDrinkable(m_Player->GetEquippedItem().m_ItemDamage)) { m_Player->AbortEating(); return; @@ -1201,15 +1202,16 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e return; } + short EquippedDamage = Equipped.m_ItemDamage; cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemType); if (ItemHandler->IsPlaceable() && (a_BlockFace != BLOCK_FACE_NONE)) { HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); } - else if (ItemHandler->IsFood() && !m_Player->IsGameModeCreative()) + else if ((ItemHandler->IsFood() || ItemHandler->IsDrinkable(EquippedDamage)) && !m_Player->IsGameModeCreative()) { - if (m_Player->IsSatiated()) + if (m_Player->IsSatiated() && !ItemHandler->IsDrinkable(EquippedDamage)) { // The player is satiated, they cannot eat return; diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 922740b11..ca39a01cd 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -20,6 +20,7 @@ #include "Map.h" #include "Enchantments.h" #include "UI/SlotArea.h" +#include "json/json.h" @@ -67,6 +68,8 @@ public: const AString & GetUUID(void) const { return m_UUID; } // tolua_export void SetUUID(const AString & a_UUID) { m_UUID = a_UUID; } + + const Json::Value & GetProperties(void) const { return m_Properties; } /** Generates an UUID based on the username stored for this client, and stores it in the m_UUID member. This is used for the offline (non-auth) mode, when there's no UUID source. @@ -91,8 +94,10 @@ public: static AString FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2); - void Kick(const AString & a_Reason); // tolua_export - void Authenticate(const AString & a_Name, const AString & a_UUID); // Called by cAuthenticator when the user passes authentication + void Kick(const AString & a_Reason); // tolua_export + + /** Authenticates the specified user, called by cAuthenticator */ + void Authenticate(const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties); void StreamChunks(void); @@ -280,6 +285,7 @@ private: AString m_Username; AString m_Password; + Json::Value m_Properties; cCriticalSection m_CSChunkLists; cChunkCoordsList m_LoadedChunks; // Chunks that the player belongs to diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp index d5e41bd46..a3a1667e4 100644 --- a/src/Entities/ArrowEntity.cpp +++ b/src/Entities/ArrowEntity.cpp @@ -79,8 +79,8 @@ void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFa } Vector3d Hit = a_HitPos; - Vector3d SinkMovement = (GetSpeed() / 800); - Hit += (SinkMovement * 0.01) / SinkMovement.Length(); // Make arrow sink into block a centimetre so it lodges (but not to far so it goes black clientside) + Vector3d SinkMovement = (GetSpeed() / 1000); + Hit += SinkMovement * (0.0005 / SinkMovement.Length()); // Make arrow sink into block a centimetre so it lodges (but not to far so it goes black clientside) super::OnHitSolidBlock(Hit, a_HitFace); Vector3i BlockHit = Hit.Floor(); diff --git a/src/Entities/Effects.h b/src/Entities/Effects.h deleted file mode 100644 index baf3302fb..000000000 --- a/src/Entities/Effects.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -// tolua_begin -enum ENUM_ENTITY_EFFECT -{ - E_EFFECT_SPEED = 1, - E_EFFECT_SLOWNESS = 2, - E_EFFECT_HASTE = 3, - E_EFFECT_MINING_FATIGUE = 4, - E_EFFECT_STENGTH = 5, - E_EFFECT_INSTANT_HEALTH = 6, - E_EFFECT_INSTANT_DAMAGE = 7, - E_EFFECT_JUMP_BOOST = 8, - E_EFFECT_NAUSEA = 9, - E_EFFECT_REGENERATION = 10, - E_EFFECT_RESISTANCE = 11, - E_EFFECT_FIRE_RESISTANCE = 12, - E_EFFECT_WATER_BREATHING = 13, - E_EFFECT_INVISIBILITY = 14, - E_EFFECT_BLINDNESS = 15, - E_EFFECT_NIGHT_VISION = 16, - E_EFFECT_HUNGER = 17, - E_EFFECT_WEAKNESS = 18, - E_EFFECT_POISON = 19, - E_EFFECT_WITHER = 20, - E_EFFECT_HEALTH_BOOST = 21, - E_EFFECT_ABSORPTION = 22, - E_EFFECT_SATURATION = 23, -} ; -// tolua_end diff --git a/src/Entities/EnderCrystal.cpp b/src/Entities/EnderCrystal.cpp index a640b236c..bf86a6c42 100644 --- a/src/Entities/EnderCrystal.cpp +++ b/src/Entities/EnderCrystal.cpp @@ -42,9 +42,9 @@ void cEnderCrystal::Tick(float a_Dt, cChunk & a_Chunk) -void cEnderCrystal::KilledBy(cEntity * a_Killer) +void cEnderCrystal::KilledBy(TakeDamageInfo & a_TDI) { - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); m_World->DoExplosionAt(6.0, GetPosX(), GetPosY(), GetPosZ(), true, esEnderCrystal, this); diff --git a/src/Entities/EnderCrystal.h b/src/Entities/EnderCrystal.h index 5b86df987..30211de13 100644 --- a/src/Entities/EnderCrystal.h +++ b/src/Entities/EnderCrystal.h @@ -24,7 +24,7 @@ private: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void KilledBy(cEntity * a_Killer) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; }; // tolua_export diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 26823924f..c6dc1fca3 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -310,10 +310,14 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) cPlayer * Player = (cPlayer *)a_TDI.Attacker; // IsOnGround() only is false if the player is moving downwards - if (!Player->IsOnGround()) // TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain) + // TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain) + if (!Player->IsOnGround()) { - a_TDI.FinalDamage += 2; - m_World->BroadcastEntityAnimation(*this, 4); // Critical hit + if ((a_TDI.DamageType == dtAttack) || (a_TDI.DamageType == dtArrowAttack)) + { + a_TDI.FinalDamage += 2; + m_World->BroadcastEntityAnimation(*this, 4); // Critical hit + } } Player->GetStatManager().AddValue(statDamageDealt, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5)); @@ -368,7 +372,7 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) if (m_Health <= 0) { - KilledBy(a_TDI.Attacker); + KilledBy(a_TDI); if (a_TDI.Attacker != NULL) { @@ -431,6 +435,7 @@ bool cEntity::ArmorCoversAgainst(eDamageType a_DamageType) case dtStarving: case dtInVoid: case dtPoisoning: + case dtWithering: case dtPotionOfHarming: case dtFalling: case dtLightning: @@ -524,11 +529,11 @@ double cEntity::GetKnockbackAmountAgainst(const cEntity & a_Receiver) -void cEntity::KilledBy(cEntity * a_Killer) +void cEntity::KilledBy(TakeDamageInfo & a_TDI) { m_Health = 0; - cRoot::Get()->GetPluginManager()->CallHookKilling(*this, a_Killer); + cRoot::Get()->GetPluginManager()->CallHookKilling(*this, a_TDI.Attacker, a_TDI); if (m_Health > 0) { @@ -538,7 +543,7 @@ void cEntity::KilledBy(cEntity * a_Killer) // Drop loot: cItems Drops; - GetDrops(Drops, a_Killer); + GetDrops(Drops, a_TDI.Attacker); m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); m_World->BroadcastEntityStatus(*this, esGenericDead); diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index f4080f8aa..ea44c2722 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -158,6 +158,7 @@ public: bool IsPlayer (void) const { return (m_EntityType == etPlayer); } bool IsPickup (void) const { return (m_EntityType == etPickup); } bool IsMob (void) const { return (m_EntityType == etMonster); } + bool IsPawn (void) const { return (IsMob() || IsPlayer()); } bool IsFallingBlock(void) const { return (m_EntityType == etFallingBlock); } bool IsMinecart (void) const { return (m_EntityType == etMinecart); } bool IsBoat (void) const { return (m_EntityType == etBoat); } @@ -309,13 +310,13 @@ public: virtual cItem GetEquippedBoots(void) const { return cItem(); } /// Called when the health drops below zero. a_Killer may be NULL (environmental damage) - virtual void KilledBy(cEntity * a_Killer); + virtual void KilledBy(TakeDamageInfo & a_TDI); /// Called when the entity kills another entity virtual void Killed(cEntity * a_Victim) {} /// Heals the specified amount of HPs - void Heal(int a_HitPoints); + virtual void Heal(int a_HitPoints); /// Returns the health of this entity int GetHealth(void) const { return m_Health; } diff --git a/src/Entities/EntityEffect.cpp b/src/Entities/EntityEffect.cpp new file mode 100644 index 000000000..4da7cac85 --- /dev/null +++ b/src/Entities/EntityEffect.cpp @@ -0,0 +1,287 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "EntityEffect.h" +#include "../Mobs/Monster.h" +#include "Player.h" + + + + +cEntityEffect::cEntityEffect(): + m_Ticks(0), + m_Duration(0), + m_Intensity(0), + m_DistanceModifier(1) +{ + +} + + + + + +cEntityEffect::cEntityEffect(int a_Duration, short a_Intensity, double a_DistanceModifier): + m_Ticks(0), + m_Duration(a_Duration), + m_Intensity(a_Intensity), + m_DistanceModifier(a_DistanceModifier) +{ + +} + + + + + +cEntityEffect::cEntityEffect(const cEntityEffect & a_OtherEffect): + m_Ticks(a_OtherEffect.m_Ticks), + m_Duration(a_OtherEffect.m_Duration), + m_Intensity(a_OtherEffect.m_Intensity), + m_DistanceModifier(a_OtherEffect.m_DistanceModifier) +{ + +} + + + + + +cEntityEffect & cEntityEffect::operator=(cEntityEffect a_OtherEffect) +{ + std::swap(m_Ticks, a_OtherEffect.m_Ticks); + std::swap(m_Duration, a_OtherEffect.m_Duration); + std::swap(m_Intensity, a_OtherEffect.m_Intensity); + std::swap(m_DistanceModifier, a_OtherEffect.m_DistanceModifier); + return *this; +} + + + + + +cEntityEffect * cEntityEffect::CreateEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, short a_Intensity, double a_DistanceModifier) +{ + switch (a_EffectType) + { + case cEntityEffect::effNoEffect: return new cEntityEffect (a_Duration, a_Intensity, a_DistanceModifier); + + case cEntityEffect::effAbsorption: return new cEntityEffectAbsorption (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effBlindness: return new cEntityEffectBlindness (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effFireResistance: return new cEntityEffectFireResistance(a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effHaste: return new cEntityEffectHaste (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effHealthBoost: return new cEntityEffectHealthBoost (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effHunger: return new cEntityEffectHunger (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effInstantDamage: return new cEntityEffectInstantDamage (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effInstantHealth: return new cEntityEffectInstantHealth (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effInvisibility: return new cEntityEffectInvisibility (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effJumpBoost: return new cEntityEffectJumpBoost (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effMiningFatigue: return new cEntityEffectMiningFatigue (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effNausea: return new cEntityEffectNausea (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effNightVision: return new cEntityEffectNightVision (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effPoison: return new cEntityEffectPoison (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effRegeneration: return new cEntityEffectRegeneration (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effResistance: return new cEntityEffectResistance (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effSaturation: return new cEntityEffectSaturation (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effSlowness: return new cEntityEffectSlowness (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effSpeed: return new cEntityEffectSpeed (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effStrength: return new cEntityEffectStrength (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effWaterBreathing: return new cEntityEffectWaterBreathing(a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effWeakness: return new cEntityEffectWeakness (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effWither: return new cEntityEffectWither (a_Duration, a_Intensity, a_DistanceModifier); + } + + ASSERT(!"Unhandled entity effect type!"); + return NULL; +} + + + + + +void cEntityEffect::OnTick(cPawn & a_Target) +{ + // Reduce the effect's duration + ++m_Ticks; +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectInstantHealth: + +void cEntityEffectInstantHealth::OnActivate(cPawn & a_Target) +{ + // Base amount = 6, doubles for every increase in intensity + int amount = (int)(6 * (1 << m_Intensity) * m_DistanceModifier); + + if (a_Target.IsMob() && ((cMonster &) a_Target).IsUndead()) + { + a_Target.TakeDamage(dtPotionOfHarming, NULL, amount, 0); // TODO: Store attacker in a pointer-safe way, pass to TakeDamage + return; + } + a_Target.Heal(amount); +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectInstantDamage: + +void cEntityEffectInstantDamage::OnActivate(cPawn & a_Target) +{ + // Base amount = 6, doubles for every increase in intensity + int amount = (int)(6 * (1 << m_Intensity) * m_DistanceModifier); + + if (a_Target.IsMob() && ((cMonster &) a_Target).IsUndead()) + { + a_Target.Heal(amount); + return; + } + a_Target.TakeDamage(dtPotionOfHarming, NULL, amount, 0); // TODO: Store attacker in a pointer-safe way, pass to TakeDamage +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectRegeneration: + +void cEntityEffectRegeneration::OnTick(cPawn & a_Target) +{ + super::OnTick(a_Target); + + if (a_Target.IsMob() && ((cMonster &) a_Target).IsUndead()) + { + return; + } + + // Regen frequency = 50 ticks, divided by potion level (Regen II = 25 ticks) + int frequency = (int) std::floor(50.0 / (double)(m_Intensity + 1)); + + if ((m_Ticks % frequency) != 0) + { + return; + } + + a_Target.Heal(1); +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectHunger: + +void cEntityEffectHunger::OnTick(cPawn & a_Target) +{ + super::OnTick(a_Target); + + if (a_Target.IsPlayer()) + { + cPlayer & Target = (cPlayer &) a_Target; + Target.SetFoodExhaustionLevel(Target.GetFoodExhaustionLevel() + 0.025); // 0.5 per second = 0.025 per tick + } +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectWeakness: + +void cEntityEffectWeakness::OnTick(cPawn & a_Target) +{ + super::OnTick(a_Target); + + // Damage reduction = 0.5 damage, multiplied by potion level (Weakness II = 1 damage) + // double dmg_reduc = 0.5 * (a_Effect.GetIntensity() + 1); + + // TODO: Implement me! + // TODO: Weakened villager zombies can be turned back to villagers with the god apple +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectPoison: + +void cEntityEffectPoison::OnTick(cPawn & a_Target) +{ + super::OnTick(a_Target); + + if (a_Target.IsMob()) + { + cMonster & Target = (cMonster &) a_Target; + + // Doesn't effect undead mobs, spiders + if ( + Target.IsUndead() || + (Target.GetMobType() == cMonster::mtSpider) || + (Target.GetMobType() == cMonster::mtCaveSpider) + ) + { + return; + } + } + + // Poison frequency = 25 ticks, divided by potion level (Poison II = 12 ticks) + int frequency = (int) std::floor(25.0 / (double)(m_Intensity + 1)); + + if ((m_Ticks % frequency) == 0) + { + // Cannot take poison damage when health is at 1 + if (a_Target.GetHealth() > 1) + { + a_Target.TakeDamage(dtPoisoning, NULL, 1, 0); + } + } +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectWither: + +void cEntityEffectWither::OnTick(cPawn & a_Target) +{ + super::OnTick(a_Target); + + // Damage frequency = 40 ticks, divided by effect level (Wither II = 20 ticks) + int frequency = (int) std::floor(25.0 / (double)(m_Intensity + 1)); + + if ((m_Ticks % frequency) == 0) + { + a_Target.TakeDamage(dtWither, NULL, 1, 0); + } +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectSaturation: + +void cEntityEffectSaturation::OnTick(cPawn & a_Target) +{ + if (a_Target.IsPlayer()) + { + cPlayer & Target = (cPlayer &) a_Target; + Target.SetFoodSaturationLevel(Target.GetFoodSaturationLevel() + (1 + m_Intensity)); // Increase saturation 1 per tick, adds 1 for every increase in level + } +} + + + + diff --git a/src/Entities/EntityEffect.h b/src/Entities/EntityEffect.h new file mode 100644 index 000000000..ebd611ff0 --- /dev/null +++ b/src/Entities/EntityEffect.h @@ -0,0 +1,476 @@ +#pragma once + +class cPawn; + +// tolua_begin +class cEntityEffect +{ +public: + + /** All types of entity effects (numbers correspond to protocol / storage types) */ + enum eType + { + effNoEffect = 0, + effSpeed = 1, + effSlowness = 2, + effHaste = 3, + effMiningFatigue = 4, + effStrength = 5, + effInstantHealth = 6, + effInstantDamage = 7, + effJumpBoost = 8, + effNausea = 9, + effRegeneration = 10, + effResistance = 11, + effFireResistance = 12, + effWaterBreathing = 13, + effInvisibility = 14, + effBlindness = 15, + effNightVision = 16, + effHunger = 17, + effWeakness = 18, + effPoison = 19, + effWither = 20, + effHealthBoost = 21, + effAbsorption = 22, + effSaturation = 23, + } ; + + // tolua_end + + /** Creates an empty entity effect */ + cEntityEffect(void); + + /** Creates an entity effect of the specified type + @param a_Duration How long this effect will last, in ticks + @param a_Intensity How strong the effect will be applied + @param a_DistanceModifier The distance modifier for affecting potency, defaults to 1 */ + cEntityEffect(int a_Duration, short a_Intensity, double a_DistanceModifier = 1); + + /** Creates an entity effect by copying another + @param a_OtherEffect The other effect to copy */ + cEntityEffect(const cEntityEffect & a_OtherEffect); + + /** Creates an entity effect by copying another + @param a_OtherEffect The other effect to copy */ + cEntityEffect & operator=(cEntityEffect a_OtherEffect); + + virtual ~cEntityEffect(void) {}; + + /** Creates a pointer to the proper entity effect from the effect type + @warning This function creates raw pointers that must be manually managed. + @param a_EffectType The effect type to create the effect from + @param a_Duration How long this effect will last, in ticks + @param a_Intensity How strong the effect will be applied + @param a_DistanceModifier The distance modifier for affecting potency, defaults to 1 */ + static cEntityEffect * CreateEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, short a_Intensity, double a_DistanceModifier); + + /** Returns how many ticks this effect has been active for */ + int GetTicks(void) const { return m_Ticks; } + + /** Returns the duration of the effect */ + int GetDuration(void) const { return m_Duration; } + + /** Returns how strong the effect will be applied */ + short GetIntensity(void) const { return m_Intensity; } + + /** Returns the distance modifier for affecting potency */ + double GetDistanceModifier(void) const { return m_DistanceModifier; } + + void SetTicks(int a_Ticks) { m_Ticks = a_Ticks; } + void SetDuration(int a_Duration) { m_Duration = a_Duration; } + void SetIntensity(short a_Intensity) { m_Intensity = a_Intensity; } + void SetDistanceModifier(double a_DistanceModifier) { m_DistanceModifier = a_DistanceModifier; } + + /** Called on each tick. + By default increases the m_Ticks, descendants may override to provide additional processing. */ + virtual void OnTick(cPawn & a_Target); + + /** Called when the effect is first added to an entity */ + virtual void OnActivate(cPawn & a_Target) { } + + /** Called when the effect is removed from an entity */ + virtual void OnDeactivate(cPawn & a_Target) { } + +protected: + /** How many ticks this effect has been active for */ + int m_Ticks; + + /** How long this effect will last, in ticks */ + int m_Duration; + + /** How strong the effect will be applied */ + short m_Intensity; + + /** The distance modifier for affecting potency */ + double m_DistanceModifier; +}; // tolua_export + + + + + +class cEntityEffectSpeed: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectSpeed(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectSlowness: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectSlowness(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectHaste: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectHaste(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectMiningFatigue: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectMiningFatigue(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectStrength: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectStrength(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectInstantHealth: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectInstantHealth(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + virtual void OnActivate(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectInstantDamage: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectInstantDamage(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + virtual void OnActivate(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectJumpBoost: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectJumpBoost(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectNausea: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectNausea(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectRegeneration: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectRegeneration(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + virtual void OnTick(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectResistance: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectResistance(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectFireResistance: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectFireResistance(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectWaterBreathing: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectWaterBreathing(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectInvisibility: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectInvisibility(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectBlindness: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectBlindness(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectNightVision: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectNightVision(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectHunger: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectHunger(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + // cEntityEffect overrides: + virtual void OnTick(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectWeakness: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectWeakness(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + // cEntityEffect overrides: + virtual void OnTick(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectPoison: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectPoison(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + // cEntityEffect overrides: + virtual void OnTick(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectWither: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectWither(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + // cEntityEffect overrides: + virtual void OnTick(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectHealthBoost: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectHealthBoost(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectAbsorption: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectAbsorption(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectSaturation: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectSaturation(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + virtual void OnTick(cPawn & a_Target) override; +}; + + + + diff --git a/src/Entities/ItemFrame.cpp b/src/Entities/ItemFrame.cpp index 7bc7bda8d..4cd707faa 100644 --- a/src/Entities/ItemFrame.cpp +++ b/src/Entities/ItemFrame.cpp @@ -51,17 +51,17 @@ void cItemFrame::OnRightClicked(cPlayer & a_Player) -void cItemFrame::KilledBy(cEntity * a_Killer) +void cItemFrame::KilledBy(TakeDamageInfo & a_TDI) { if (m_Item.IsEmpty()) { SetHealth(0); - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); Destroy(); return; } - if ((a_Killer != NULL) && a_Killer->IsPlayer() && !((cPlayer *)a_Killer)->IsGameModeCreative()) + if ((a_TDI.Attacker != NULL) && a_TDI.Attacker->IsPlayer() && !((cPlayer *)a_TDI.Attacker)->IsGameModeCreative()) { cItems Item; Item.push_back(m_Item); diff --git a/src/Entities/ItemFrame.h b/src/Entities/ItemFrame.h index 6577e7d94..9261e52cc 100644 --- a/src/Entities/ItemFrame.h +++ b/src/Entities/ItemFrame.h @@ -35,7 +35,7 @@ public: private: virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void KilledBy(cEntity * a_Killer) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override; cItem m_Item; diff --git a/src/Entities/Painting.h b/src/Entities/Painting.h index c1024bd1b..30af6d412 100644 --- a/src/Entities/Painting.h +++ b/src/Entities/Painting.h @@ -26,9 +26,9 @@ private: virtual void SpawnOn(cClientHandle & a_Client) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override; - virtual void KilledBy(cEntity * a_Killer) override + virtual void KilledBy(TakeDamageInfo & a_TDI) override { - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); Destroy(); } diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp index fffefd538..fe6c24a7a 100644 --- a/src/Entities/Pawn.cpp +++ b/src/Entities/Pawn.cpp @@ -2,14 +2,16 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Pawn.h" +#include "../World.h" +#include "../Bindings/PluginManager.h" -cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) - : cEntity(a_EntityType, 0, 0, 0, a_Width, a_Height) - , m_bBurnable(true) +cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height): + super(a_EntityType, 0, 0, 0, a_Width, a_Height), + m_EntityEffects(tEffectMap()) { } @@ -17,3 +19,95 @@ cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) +void cPawn::Tick(float a_Dt, cChunk & a_Chunk) +{ + // Iterate through this entity's applied effects + for (tEffectMap::iterator iter = m_EntityEffects.begin(); iter != m_EntityEffects.end();) + { + // Copies values to prevent pesky wrong accesses and erasures + cEntityEffect::eType EffectType = iter->first; + cEntityEffect * Effect = iter->second; + + Effect->OnTick(*this); + + // Iterates (must be called before any possible erasure) + ++iter; + + // Remove effect if duration has elapsed + if (Effect->GetDuration() - Effect->GetTicks() <= 0) + { + RemoveEntityEffect(EffectType); + } + + // TODO: Check for discrepancies between client and server effect values + } + + super::Tick(a_Dt, a_Chunk); +} + + + + + +void cPawn::KilledBy(TakeDamageInfo & a_TDI) +{ + ClearEntityEffects(); + super::KilledBy(a_TDI); +} + + + + + +void cPawn::AddEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, short a_Intensity, double a_DistanceModifier) +{ + // Check if the plugins allow the addition: + if (cPluginManager::Get()->CallHookEntityAddEffect(*this, a_EffectType, a_Duration, a_Intensity, a_DistanceModifier)) + { + // A plugin disallows the addition, bail out. + return; + } + + // No need to add empty effects: + if (a_EffectType == cEntityEffect::effNoEffect) + { + return; + } + a_Duration = (int)(a_Duration * a_DistanceModifier); + + m_EntityEffects[a_EffectType] = cEntityEffect::CreateEntityEffect(a_EffectType, a_Duration, a_Intensity, a_DistanceModifier); + m_World->BroadcastEntityEffect(*this, a_EffectType, a_Intensity, a_Duration); + m_EntityEffects[a_EffectType]->OnActivate(*this); +} + + + + + +void cPawn::RemoveEntityEffect(cEntityEffect::eType a_EffectType) +{ + m_World->BroadcastRemoveEntityEffect(*this, a_EffectType); + m_EntityEffects[a_EffectType]->OnDeactivate(*this); + delete m_EntityEffects[a_EffectType]; + m_EntityEffects.erase(a_EffectType); +} + + + + + +void cPawn::ClearEntityEffects() +{ + // Iterate through this entity's applied effects + for (tEffectMap::iterator iter = m_EntityEffects.begin(); iter != m_EntityEffects.end();) + { + // Copy values to prevent pesky wrong erasures + cEntityEffect::eType EffectType = iter->first; + + // Iterates (must be called before any possible erasure) + ++iter; + + // Remove effect + RemoveEntityEffect(EffectType); + } +} diff --git a/src/Entities/Pawn.h b/src/Entities/Pawn.h index e76337d86..63c7cfbb6 100644 --- a/src/Entities/Pawn.h +++ b/src/Entities/Pawn.h @@ -2,6 +2,7 @@ #pragma once #include "Entity.h" +#include "EntityEffect.h" @@ -18,9 +19,34 @@ public: CLASS_PROTODEF(cPawn); cPawn(eEntityType a_EntityType, double a_Width, double a_Height); + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; + + // tolua_begin + + /** Applies an entity effect + Checks with plugins if they allow the addition. + @param a_EffectType The entity effect to apply + @param a_EffectDurationTicks The duration of the effect + @param a_EffectIntensity The level of the effect (0 = Potion I, 1 = Potion II, etc) + @param a_DistanceModifier The scalar multiplied to the potion duration, only applies to splash potions) + */ + void AddEntityEffect(cEntityEffect::eType a_EffectType, int a_EffectDurationTicks, short a_EffectIntensity, double a_DistanceModifier = 1); + + /** Removes a currently applied entity effect + @param a_EffectType The entity effect to remove + */ + void RemoveEntityEffect(cEntityEffect::eType a_EffectType); + + /** Removes all currently applied entity effects (used when drinking milk) */ + void ClearEntityEffects(void); + + // tolua_end protected: - bool m_bBurnable; + typedef std::map<cEntityEffect::eType, cEntityEffect *> tEffectMap; + tEffectMap m_EntityEffects; } ; // tolua_export diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index b1b7fc74e..da50843b6 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -41,7 +41,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) : m_FoodSaturationLevel(5.0), m_FoodTickTimer(0), m_FoodExhaustionLevel(0.0), - m_FoodPoisonedTicksRemaining(0), m_LastJumpHeight(0), m_LastGroundHeight(0), m_bTouchGround(false), @@ -130,7 +129,7 @@ cPlayer::~cPlayer(void) if (!cRoot::Get()->GetPluginManager()->CallHookPlayerDestroyed(*this)) { cRoot::Get()->BroadcastChatLeave(Printf("%s has left the game", GetName().c_str())); - LOGINFO("Player %s has left the game.", GetName().c_str()); + LOGINFO("Player %s has left the game", GetName().c_str()); } LOGD("Deleting cPlayer \"%s\" at %p, ID %d", GetName().c_str(), this, GetUniqueID()); @@ -563,18 +562,9 @@ void cPlayer::SetFoodExhaustionLevel(double a_FoodExhaustionLevel) -void cPlayer::SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining) -{ - m_FoodPoisonedTicksRemaining = a_FoodPoisonedTicksRemaining; -} - - - - - bool cPlayer::Feed(int a_Food, double a_Saturation) { - if (m_FoodLevel >= MAX_FOOD_LEVEL) + if (IsSatiated()) { return false; } @@ -590,17 +580,7 @@ bool cPlayer::Feed(int a_Food, double a_Saturation) void cPlayer::FoodPoison(int a_NumTicks) { - bool HasBeenFoodPoisoned = (m_FoodPoisonedTicksRemaining > 0); - m_FoodPoisonedTicksRemaining = std::max(m_FoodPoisonedTicksRemaining, a_NumTicks); - if (!HasBeenFoodPoisoned) - { - m_World->BroadcastRemoveEntityEffect(*this, E_EFFECT_HUNGER); - SendHealth(); - } - else - { - m_World->BroadcastEntityEffect(*this, E_EFFECT_HUNGER, 0, 400); // Give the player the "Hunger" effect for 20 seconds. - } + AddEntityEffect(cEntityEffect::effHunger, a_NumTicks, 0, NULL); } @@ -885,9 +865,9 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) -void cPlayer::KilledBy(cEntity * a_Killer) +void cPlayer::KilledBy(TakeDamageInfo & a_TDI) { - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); if (m_Health > 0) { @@ -911,19 +891,40 @@ void cPlayer::KilledBy(cEntity * a_Killer) m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); SaveToDisk(); // Save it, yeah the world is a tough place ! - if (a_Killer == NULL) + if (a_TDI.Attacker == NULL) { - GetWorld()->BroadcastChatDeath(Printf("%s was killed by environmental damage", GetName().c_str())); + AString DamageText; + switch (a_TDI.DamageType) + { + case dtRangedAttack: DamageText = "was shot"; break; + case dtLightning: DamageText = "was plasmified by lightining"; break; + case dtFalling: DamageText = (GetWorld()->GetTickRandomNumber(10) % 2 == 0) ? "fell to death" : "hit the ground too hard"; break; + case dtDrowning: DamageText = "drowned"; break; + case dtSuffocating: DamageText = (GetWorld()->GetTickRandomNumber(10) % 2 == 0) ? "git merge'd into a block" : "fused with a block"; break; + case dtStarving: DamageText = "forgot the importance of food"; break; + case dtCactusContact: DamageText = "was impaled on a cactus"; break; + case dtLavaContact: DamageText = "was melted by lava"; break; + case dtPoisoning: DamageText = "died from septicaemia"; break; + case dtOnFire: DamageText = "forgot to stop, drop, and roll"; break; + case dtFireContact: DamageText = "burnt themselves to death"; break; + case dtInVoid: DamageText = "somehow fell out of the world"; break; + case dtPotionOfHarming: DamageText = "was magicked to death"; break; + case dtEnderPearl: DamageText = "misused an ender pearl"; break; + case dtAdmin: DamageText = "was administrator'd"; break; + case dtExplosion: DamageText = "blew up"; break; + default: DamageText = "died, somehow; we've no idea how though"; break; + } + GetWorld()->BroadcastChatDeath(Printf("%s %s", GetName().c_str(), DamageText.c_str())); } - else if (a_Killer->IsPlayer()) + else if (a_TDI.Attacker->IsPlayer()) { - cPlayer * Killer = (cPlayer *)a_Killer; + cPlayer * Killer = (cPlayer *)a_TDI.Attacker; GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), Killer->GetName().c_str())); } else { - AString KillerClass = a_Killer->GetClass(); + AString KillerClass = a_TDI.Attacker->GetClass(); KillerClass.erase(KillerClass.begin()); // Erase the 'c' of the class (e.g. "cWitch" -> "Witch") GetWorld()->BroadcastChatDeath(Printf("%s was killed by a %s", GetName().c_str(), KillerClass.c_str())); @@ -1804,7 +1805,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName) cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); StatSerializer.Load(); - LOGD("Player \"%s\" was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"", + LOGD("Player %s was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"", GetName().c_str(), a_FileName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() ); @@ -1994,17 +1995,6 @@ void cPlayer::HandleFood(void) } } - // Apply food poisoning food exhaustion: - if (m_FoodPoisonedTicksRemaining > 0) - { - m_FoodPoisonedTicksRemaining--; - m_FoodExhaustionLevel += 0.025; // 0.5 per second = 0.025 per tick - } - else - { - m_World->BroadcastRemoveEntityEffect(*this, E_EFFECT_HUNGER); // Remove the "Hunger" effect. - } - // Apply food exhaustion that has accumulated: if (m_FoodExhaustionLevel >= 4.0) { @@ -2141,6 +2131,8 @@ void cPlayer::ApplyFoodExhaustionFromMovement() { return; } + + // If we have just teleported, apply no exhaustion if (m_bIsTeleporting) { m_bIsTeleporting = false; @@ -2152,6 +2144,13 @@ void cPlayer::ApplyFoodExhaustionFromMovement() { return; } + + // Process exhaustion every two ticks as that is how frequently m_LastPos is updated + // Otherwise, we apply exhaustion for a 'movement' every tick, one of which is an already processed value + if (GetWorld()->GetWorldAge() % 2 != 0) + { + return; + } // Calculate the distance travelled, update the last pos: Vector3d Movement(GetPosition() - m_LastPos); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 8f9b46e0f..793f11e57 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -265,13 +265,12 @@ public: void TossPickup(const cItem & a_Item); /** Heals the player by the specified amount of HPs (positive only); sends health update */ - void Heal(int a_Health); + virtual void Heal(int a_Health) override; int GetFoodLevel (void) const { return m_FoodLevel; } double GetFoodSaturationLevel (void) const { return m_FoodSaturationLevel; } int GetFoodTickTimer (void) const { return m_FoodTickTimer; } double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; } - int GetFoodPoisonedTicksRemaining(void) const { return m_FoodPoisonedTicksRemaining; } /** Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore */ bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); } @@ -280,7 +279,6 @@ public: void SetFoodSaturationLevel (double a_FoodSaturationLevel); void SetFoodTickTimer (int a_FoodTickTimer); void SetFoodExhaustionLevel (double a_FoodExhaustionLevel); - void SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining); /** Adds to FoodLevel and FoodSaturationLevel, returns true if any food has been consumed, false if player "full" */ bool Feed(int a_Food, double a_Saturation); @@ -291,7 +289,7 @@ public: m_FoodExhaustionLevel += a_Exhaustion; } - /** Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two */ + /** Starts the food poisoning for the specified amount of ticks */ void FoodPoison(int a_NumTicks); /** Returns true if the player is currently in the process of eating the currently equipped item */ @@ -324,7 +322,7 @@ public: /** Aborts the current eating operation */ void AbortEating(void); - virtual void KilledBy(cEntity * a_Killer) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; virtual void Killed(cEntity * a_Victim) override; @@ -449,14 +447,11 @@ protected: double m_FoodSaturationLevel; /** Count-up to the healing or damaging action, based on m_FoodLevel */ - int m_FoodTickTimer; + int m_FoodTickTimer; /** A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little */ double m_FoodExhaustionLevel; - /** Number of ticks remaining for the foodpoisoning effect; zero if not foodpoisoned */ - int m_FoodPoisonedTicksRemaining; - float m_LastJumpHeight; float m_LastGroundHeight; bool m_bTouchGround; diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index 0bb34019e..019ebeaa5 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -21,6 +21,7 @@ #include "FireChargeEntity.h" #include "FireworkEntity.h" #include "GhastFireballEntity.h" +#include "WitherSkullEntity.h" #include "Player.h" @@ -243,7 +244,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve -cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item, const Vector3d * a_Speed) +cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem * a_Item, const Vector3d * a_Speed) { Vector3d Speed; if (a_Speed != NULL) @@ -260,14 +261,16 @@ cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, case pkGhastFireball: return new cGhastFireballEntity (a_Creator, a_X, a_Y, a_Z, Speed); case pkFireCharge: return new cFireChargeEntity (a_Creator, a_X, a_Y, a_Z, Speed); case pkExpBottle: return new cExpBottleEntity (a_Creator, a_X, a_Y, a_Z, Speed); + case pkWitherSkull: return new cWitherSkullEntity (a_Creator, a_X, a_Y, a_Z, Speed); case pkFirework: { - if (a_Item.m_FireworkItem.m_Colours.empty()) + ASSERT(a_Item != NULL); + if (a_Item->m_FireworkItem.m_Colours.empty()) { return NULL; } - return new cFireworkEntity(a_Creator, a_X, a_Y, a_Z, a_Item); + return new cFireworkEntity(a_Creator, a_X, a_Y, a_Z, *a_Item); } } @@ -310,7 +313,7 @@ AString cProjectileEntity::GetMCAClassName(void) const case pkFireCharge: return "SmallFireball"; case pkEnderPearl: return "ThrownEnderpearl"; case pkExpBottle: return "ThrownExpBottle"; - case pkSplashPotion: return "ThrownPotion"; + case pkSplashPotion: return "SplashPotion"; case pkWitherSkull: return "WitherSkull"; case pkFirework: return "Firework"; case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this? diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h index 7b38169e2..14cee1272 100644 --- a/src/Entities/ProjectileEntity.h +++ b/src/Entities/ProjectileEntity.h @@ -46,7 +46,7 @@ public: cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height); - static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item, const Vector3d * a_Speed = NULL); + static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem * a_Item, const Vector3d * a_Speed = NULL); /// Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace); diff --git a/src/Entities/SplashPotionEntity.cpp b/src/Entities/SplashPotionEntity.cpp new file mode 100644 index 000000000..ed5afaf11 --- /dev/null +++ b/src/Entities/SplashPotionEntity.cpp @@ -0,0 +1,120 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "SplashPotionEntity.h" +#include "Pawn.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSplashPotionEntityCallback: + +/** Used to distribute the splashed potion effect among nearby entities */ +class cSplashPotionCallback : + public cEntityCallback +{ +public: + /** Creates the callback. + @param a_HitPos The position where the splash potion has splashed + @param a_EntityEffectType The effect type of the potion + @param a_EntityEffect The effect description */ + cSplashPotionCallback(const Vector3d & a_HitPos, cEntityEffect::eType a_EntityEffectType, const cEntityEffect & a_EntityEffect) : + m_HitPos(a_HitPos), + m_EntityEffectType(a_EntityEffectType), + m_EntityEffect(a_EntityEffect) + { + } + + /** Called by cWorld::ForEachEntity(), adds the stored entity effect to the entity, if it is close enough. */ + virtual bool Item(cEntity * a_Entity) override + { + double SplashDistance = (a_Entity->GetPosition() - m_HitPos).Length(); + if (SplashDistance >= 20) + { + // Too far away + return false; + } + if (!a_Entity->IsPawn()) + { + // Not an entity that can take effects + return false; + } + + // y = -0.25x + 1, where x is the distance from the player. Approximation for potion splash. + // TODO: better equation + double Reduction = -0.25 * SplashDistance + 1.0; + if (Reduction < 0) + { + Reduction = 0; + } + + ((cPawn *) a_Entity)->AddEntityEffect(m_EntityEffectType, m_EntityEffect.GetDuration(), m_EntityEffect.GetIntensity(), Reduction); + return false; + } + +private: + const Vector3d & m_HitPos; + cEntityEffect::eType m_EntityEffectType; + const cEntityEffect & m_EntityEffect; +}; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSplashPotionEntity: + +cSplashPotionEntity::cSplashPotionEntity( + cEntity * a_Creator, + double a_X, double a_Y, double a_Z, + const Vector3d & a_Speed, + cEntityEffect::eType a_EntityEffectType, + cEntityEffect a_EntityEffect, + int a_PotionParticleType +) : + super(pkSplashPotion, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25), + m_EntityEffectType(a_EntityEffectType), + m_EntityEffect(a_EntityEffect), + m_PotionParticleType(a_PotionParticleType) +{ + SetSpeed(a_Speed); +} + + + + + +void cSplashPotionEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) +{ + Splash(a_HitPos); + Destroy(); +} + + + + + +void cSplashPotionEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + a_EntityHit.TakeDamage(dtRangedAttack, this, 0, 1); + Splash(a_HitPos); + Destroy(true); +} + + + + + +void cSplashPotionEntity::Splash(const Vector3d & a_HitPos) +{ + cSplashPotionCallback Callback(a_HitPos, m_EntityEffectType, m_EntityEffect); + m_World->ForEachEntity(Callback); + + m_World->BroadcastSoundParticleEffect(2002, (int)a_HitPos.x, (int)a_HitPos.y, (int)a_HitPos.z, m_PotionParticleType); +} + + + + diff --git a/src/Entities/SplashPotionEntity.h b/src/Entities/SplashPotionEntity.h new file mode 100644 index 000000000..076e477da --- /dev/null +++ b/src/Entities/SplashPotionEntity.h @@ -0,0 +1,59 @@ +// +// SplashPotionEntity.h +// + +#pragma once + +#include "ProjectileEntity.h" +#include "EntityEffect.h" +#include "../World.h" +#include "Entity.h" + + + + +// tolua_begin + +class cSplashPotionEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cSplashPotionEntity); + + cSplashPotionEntity( + cEntity * a_Creator, + double a_X, double a_Y, double a_Z, + const Vector3d & a_Speed, + cEntityEffect::eType a_EntityEffectType, + cEntityEffect a_EntityEffect, + int a_PotionParticleType + ); + + cEntityEffect::eType GetEntityEffectType (void) const { return m_EntityEffectType; } + cEntityEffect GetEntityEffect (void) const { return m_EntityEffect; } + int GetPotionParticleType(void) const { return m_PotionParticleType; } + + void SetEntityEffectType(cEntityEffect::eType a_EntityEffectType) { m_EntityEffectType = a_EntityEffectType; } + void SetEntityEffect(cEntityEffect a_EntityEffect) { m_EntityEffect = a_EntityEffect; } + void SetPotionParticleType(int a_PotionParticleType) { m_PotionParticleType = a_PotionParticleType; } + +protected: + + cEntityEffect::eType m_EntityEffectType; + cEntityEffect m_EntityEffect; + int m_PotionParticleType; + + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; + virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + + /** Splashes the potion, fires its particle effects and sounds + @param a_HitPos The position where the potion will splash */ + void Splash(const Vector3d & a_HitPos); +} ; // tolua_export diff --git a/src/Entities/WitherSkullEntity.cpp b/src/Entities/WitherSkullEntity.cpp new file mode 100644 index 000000000..a7e774bba --- /dev/null +++ b/src/Entities/WitherSkullEntity.cpp @@ -0,0 +1,49 @@ + +// WitherSkullEntity.cpp + +// Implements the cWitherSkullEntity class representing the entity used by both blue and black wither skulls + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "WitherSkullEntity.h" +#include "../World.h" + + + + + +cWitherSkullEntity::cWitherSkullEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkWitherSkull, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cWitherSkullEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) +{ + // TODO: Explode + // TODO: Apply wither effect to entities nearby + Destroy(); +} + + + + + +void cWitherSkullEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + // TODO: If entity is Ender Crystal, destroy it + a_EntityHit.TakeDamage(dtRangedAttack, this, 0, 1); + + // TODO: Explode + // TODO: Apply wither effect to entity and others nearby + + Destroy(true); +} + + + + diff --git a/src/Entities/WitherSkullEntity.h b/src/Entities/WitherSkullEntity.h new file mode 100644 index 000000000..8b3639802 --- /dev/null +++ b/src/Entities/WitherSkullEntity.h @@ -0,0 +1,35 @@ + +// WitherSkullEntity.h + +// Declares the cWitherSkullEntity class representing the entity used by both blue and black wither skulls + +#pragma once + +#include "ProjectileEntity.h" + + + + + +// tolua_begin + +class cWitherSkullEntity : +public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cWitherSkullEntity); + + cWitherSkullEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; + virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + +} ; // tolua_export diff --git a/src/Generating/ChunkDesc.cpp b/src/Generating/ChunkDesc.cpp index 7711723fc..e4b305022 100644 --- a/src/Generating/ChunkDesc.cpp +++ b/src/Generating/ChunkDesc.cpp @@ -290,7 +290,7 @@ void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX LOGWARNING("%s: MaxRelX less than zero, adjusting to zero", __FUNCTION__); a_MaxRelX = 0; } - else if (a_MinRelX >= cChunkDef::Width) + else if (a_MaxRelX >= cChunkDef::Width) { LOGWARNING("%s: MaxRelX more than chunk width, adjusting to chunk width", __FUNCTION__); a_MaxRelX = cChunkDef::Width - 1; @@ -311,7 +311,7 @@ void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX LOGWARNING("%s: MaxRelY less than zero, adjusting to zero", __FUNCTION__); a_MaxRelY = 0; } - else if (a_MinRelY >= cChunkDef::Height) + else if (a_MaxRelY >= cChunkDef::Height) { LOGWARNING("%s: MaxRelY more than chunk height, adjusting to chunk height", __FUNCTION__); a_MaxRelY = cChunkDef::Height - 1; @@ -332,7 +332,7 @@ void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX LOGWARNING("%s: MaxRelZ less than zero, adjusting to zero", __FUNCTION__); a_MaxRelZ = 0; } - else if (a_MinRelZ >= cChunkDef::Width) + else if (a_MaxRelZ >= cChunkDef::Width) { LOGWARNING("%s: MaxRelZ more than chunk width, adjusting to chunk width", __FUNCTION__); a_MaxRelZ = cChunkDef::Width - 1; diff --git a/src/Generating/GridStructGen.cpp b/src/Generating/GridStructGen.cpp index 95f8c38bc..fd1d5e49f 100644 --- a/src/Generating/GridStructGen.cpp +++ b/src/Generating/GridStructGen.cpp @@ -44,6 +44,7 @@ cGridStructGen::cGridStructGen( int a_MaxStructureSizeX, int a_MaxStructureSizeZ, size_t a_MaxCacheSize ) : + m_Seed(a_Seed), m_Noise(a_Seed), m_GridSizeX(a_GridSizeX), m_GridSizeZ(a_GridSizeZ), @@ -53,6 +54,16 @@ cGridStructGen::cGridStructGen( m_MaxStructureSizeZ(a_MaxStructureSizeZ), m_MaxCacheSize(a_MaxCacheSize) { + if (m_GridSizeX == 0) + { + LOG("Grid Size cannot be zero, setting to 1"); + m_GridSizeX = 1; + } + if (m_GridSizeZ == 0) + { + LOG("Grid Size cannot be zero, setting to 1"); + m_GridSizeZ = 1; + } size_t NumStructuresPerQuery = (size_t)(((m_MaxStructureSizeX + m_MaxOffsetX) / m_GridSizeX + 1) * ((m_MaxStructureSizeZ + m_MaxOffsetZ) / m_GridSizeZ + 1)); if (NumStructuresPerQuery > m_MaxCacheSize) { diff --git a/src/Globals.h b/src/Globals.h index 0c11429bd..998676fb1 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -383,6 +383,5 @@ T Clamp(T a_Value, T a_Min, T a_Max) #include "BiomeDef.h" #include "BlockID.h" #include "BlockInfo.h" -#include "Entities/Effects.h" diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h index 5529b4e36..a733bda19 100644 --- a/src/Items/ItemBucket.h +++ b/src/Items/ItemBucket.h @@ -203,7 +203,7 @@ public: if (a_BlockType != E_BLOCK_AIR) { m_ReplacedBlock = a_BlockType; - if (!cFluidSimulator::CanWashAway(a_BlockType)) + if (!cFluidSimulator::CanWashAway(a_BlockType) && !IsBlockLiquid(a_BlockType)) { AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, (eBlockFace)a_EntryFace); // Was an unwashawayable block, can't overwrite it! } @@ -215,7 +215,7 @@ public: } Callbacks; cLineBlockTracer Tracer(*a_World, Callbacks); - Vector3d Start(a_Player->GetEyePosition() + a_Player->GetLookVector()); + Vector3d Start(a_Player->GetEyePosition()); Vector3d End(a_Player->GetEyePosition() + a_Player->GetLookVector() * 5); // cTracer::Trace returns true when whole line was traversed. By returning true when we hit something, we ensure that this never happens if liquid could be placed diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp index 423039cf4..85406c826 100644 --- a/src/Items/ItemHandler.cpp +++ b/src/Items/ItemHandler.cpp @@ -19,6 +19,7 @@ #include "ItemCloth.h" #include "ItemComparator.h" #include "ItemDoor.h" +#include "ItemMilk.h" #include "ItemDye.h" #include "ItemEmptyMap.h" #include "ItemFishingRod.h" @@ -34,6 +35,7 @@ #include "ItemNetherWart.h" #include "ItemPainting.h" #include "ItemPickaxe.h" +#include "ItemPotion.h" #include "ItemThrowable.h" #include "ItemRedstoneDust.h" #include "ItemRedstoneRepeater.h" @@ -120,9 +122,11 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType); case E_ITEM_MAP: return new cItemMapHandler(); + case E_ITEM_MILK: return new cItemMilkHandler(); case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType); case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType); case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType); + case E_ITEM_POTIONS: return new cItemPotionHandler(); case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); @@ -470,33 +474,17 @@ bool cItemHandler::IsTool() bool cItemHandler::IsFood(void) { - switch (m_ItemType) - { - case E_ITEM_RED_APPLE: - case E_ITEM_GOLDEN_APPLE: - case E_ITEM_MUSHROOM_SOUP: - case E_ITEM_BREAD: - case E_ITEM_RAW_PORKCHOP: - case E_ITEM_COOKED_PORKCHOP: - case E_ITEM_MILK: - case E_ITEM_RAW_FISH: - case E_ITEM_COOKED_FISH: - case E_ITEM_COOKIE: - case E_ITEM_MELON_SLICE: - case E_ITEM_RAW_BEEF: - case E_ITEM_STEAK: - case E_ITEM_RAW_CHICKEN: - case E_ITEM_COOKED_CHICKEN: - case E_ITEM_ROTTEN_FLESH: - case E_ITEM_SPIDER_EYE: - case E_ITEM_CARROT: - case E_ITEM_POTATO: - case E_ITEM_BAKED_POTATO: - case E_ITEM_POISONOUS_POTATO: - { - return true; - } - } // switch (m_ItemType) + return false; +} + + + + + +bool cItemHandler::IsDrinkable(short a_ItemDamage) +{ + UNUSED(a_ItemDamage); + return false; } @@ -580,7 +568,7 @@ bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item) cFastRandom r1; if ((r1.NextInt(100, a_Player->GetUniqueID()) - Info.PoisonChance) <= 0) { - a_Player->FoodPoison(300); + a_Player->FoodPoison(600); // Give the player food poisoning for 30 seconds. } } diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h index e13198cd7..cffca11ab 100644 --- a/src/Items/ItemHandler.h +++ b/src/Items/ItemHandler.h @@ -82,6 +82,9 @@ public: /** Indicates if this item is food */ virtual bool IsFood(void); + /** Indicates if this item is drinkable */ + virtual bool IsDrinkable(short a_ItemDamage); + /** Blocks simply get placed */ virtual bool IsPlaceable(void); @@ -99,7 +102,7 @@ public: BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta ); - /** Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can�t) DEFAULT: False */ + /** Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can't) DEFAULT: False */ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType); static cItemHandler * GetItemHandler(int a_ItemType); diff --git a/src/Items/ItemMilk.h b/src/Items/ItemMilk.h new file mode 100644 index 000000000..db7bc13be --- /dev/null +++ b/src/Items/ItemMilk.h @@ -0,0 +1,28 @@ + +#pragma once + +class cItemMilkHandler: + public cItemHandler +{ + typedef cItemHandler super; +public: + cItemMilkHandler(): + super(E_ITEM_MILK) + { + } + + virtual bool IsDrinkable(short a_ItemDamage) override + { + UNUSED(a_ItemDamage); + return true; + } + + virtual bool EatItem(cPlayer * a_Player, cItem * a_Item) override + { + UNUSED(a_Item); + a_Player->ClearEntityEffects(); + a_Player->GetInventory().RemoveOneEquippedItem(); + a_Player->GetInventory().AddItem(E_ITEM_BUCKET); + return true; + } +}; diff --git a/src/Items/ItemPotion.h b/src/Items/ItemPotion.h new file mode 100644 index 000000000..f3afbf99b --- /dev/null +++ b/src/Items/ItemPotion.h @@ -0,0 +1,199 @@ + +#pragma once + +#include "../Entities/EntityEffect.h" +#include "../Entities/SplashPotionEntity.h" + +class cItemPotionHandler: + public cItemHandler +{ + typedef cItemHandler super; + +public: + + cItemPotionHandler(): + super(E_ITEM_POTION) + { + } + + + /** Returns the potion particle type (used by the client for visuals), based on the potion's damage value */ + static int GetPotionParticleType(short a_ItemDamage) + { + // Lowest six bits + return (a_ItemDamage & 0x3f); + } + + + /** Translates the potion's damage value into the entity effect that the potion gives */ + static cEntityEffect::eType GetEntityEffectType(short a_ItemDamage) + { + // Lowest four bits + // Potion effect bits are different from entity effect values + // For reference: http://minecraft.gamepedia.com/Data_values#.22Potion_effect.22_bits + switch (a_ItemDamage & 0x0f) + { + case 0x01: return cEntityEffect::effRegeneration; + case 0x02: return cEntityEffect::effSpeed; + case 0x03: return cEntityEffect::effFireResistance; + case 0x04: return cEntityEffect::effPoison; + case 0x05: return cEntityEffect::effInstantHealth; + case 0x06: return cEntityEffect::effNightVision; + case 0x08: return cEntityEffect::effWeakness; + case 0x09: return cEntityEffect::effStrength; + case 0x0a: return cEntityEffect::effSlowness; + 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; + } + } + return cEntityEffect::effNoEffect; + } + + + /** Retrieves the intensity level from the potion's damage value. + Returns 0 for level I potions, 1 for level II potions. */ + static short GetEntityEffectIntensity(short a_ItemDamage) + { + // Level II potion if the fifth lowest bit is set + return ((a_ItemDamage & 0x20) != 0) ? 1 : 0; + } + + + /** Returns the effect duration, in ticks, based on the potion's damage value */ + static int GetEntityEffectDuration(short a_ItemDamage) + { + // Base duration in ticks + int base = 0; + double TierCoeff = 1, ExtCoeff = 1, SplashCoeff = 1; + + switch (GetEntityEffectType(a_ItemDamage)) + { + case cEntityEffect::effRegeneration: + case cEntityEffect::effPoison: + { + base = 900; + break; + } + + case cEntityEffect::effSpeed: + case cEntityEffect::effFireResistance: + case cEntityEffect::effNightVision: + case cEntityEffect::effStrength: + case cEntityEffect::effWaterBreathing: + case cEntityEffect::effInvisibility: + { + base = 3600; + break; + } + + case cEntityEffect::effWeakness: + case cEntityEffect::effSlowness: + { + base = 1800; + break; + } + } + + // If potion is level II, half the duration. If not, stays the same + TierCoeff = (GetEntityEffectIntensity(a_ItemDamage) > 0) ? 0.5 : 1; + + // If potion is extended, multiply duration by 8/3. If not, stays the same + // Extended potion if sixth lowest bit is set + ExtCoeff = (a_ItemDamage & 0x40) ? (8.0 / 3.0) : 1; + + // If potion is splash potion, multiply duration by 3/4. If not, stays the same + SplashCoeff = IsPotionDrinkable(a_ItemDamage) ? 1 : 0.75; + + // Ref.: + // http://minecraft.gamepedia.com/Data_values#.22Tier.22_bit + // http://minecraft.gamepedia.com/Data_values#.22Extended_duration.22_bit + // http://minecraft.gamepedia.com/Data_values#.22Splash_potion.22_bit + + return (int)(base * TierCoeff * ExtCoeff * SplashCoeff); + } + + + /** Returns true if the potion with the given damage is drinkable */ + static bool IsPotionDrinkable(short a_ItemDamage) + { + // Drinkable potion if 13th lowest bit is set + // Ref.: http://minecraft.gamepedia.com/Potions#Data_value_table + return ((a_ItemDamage & 0x2000) != 0); + } + + + // cItemHandler overrides: + virtual bool IsDrinkable(short a_ItemDamage) override + { + // Drinkable potion if 13th lowest bit is set + // Ref.: http://minecraft.gamepedia.com/Potions#Data_value_table + return IsPotionDrinkable(a_ItemDamage); + } + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + { + short PotionDamage = a_Item.m_ItemDamage; + + // Do not throw non-splash potions: + if (IsPotionDrinkable(PotionDamage)) + { + return false; + } + + Vector3d Pos = a_Player->GetThrowStartPos(); + Vector3d Speed = a_Player->GetLookVector() * 7; + + cSplashPotionEntity * Projectile = new cSplashPotionEntity( + a_Player, Pos.x, Pos.y, Pos.z, Speed, + GetEntityEffectType(PotionDamage), cEntityEffect(GetEntityEffectDuration(PotionDamage), + GetEntityEffectIntensity(PotionDamage)), GetPotionParticleType(PotionDamage) + ); + if (Projectile == NULL) + { + return false; + } + if (!Projectile->Initialize(*a_World)) + { + delete Projectile; + return false; + } + + if (!a_Player->IsGameModeCreative()) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + } + + return true; + } + + + virtual bool EatItem(cPlayer * a_Player, cItem * a_Item) override + { + short PotionDamage = a_Item->m_ItemDamage; + + // Do not drink undrinkable potions: + if (!IsDrinkable(a_Item->m_ItemDamage)) + { + return false; + } + + a_Player->AddEntityEffect(GetEntityEffectType(PotionDamage), GetEntityEffectDuration(PotionDamage), GetEntityEffectIntensity(PotionDamage)); + a_Player->GetInventory().RemoveOneEquippedItem(); + a_Player->GetInventory().AddItem(E_ITEM_GLASS_BOTTLE); + return true; + } +}; + + + + diff --git a/src/Items/ItemThrowable.h b/src/Items/ItemThrowable.h index fde7b8e67..c151c5d3a 100644 --- a/src/Items/ItemThrowable.h +++ b/src/Items/ItemThrowable.h @@ -35,7 +35,7 @@ public: cFastRandom Random; a_World->BroadcastSoundEffect("random.bow", a_Player->GetPosX(), a_Player->GetPosY() - a_Player->GetHeight(), a_Player->GetPosZ(), 0.5f, 0.4f / (Random.NextFloat(1.0f) * 0.4f + 0.8f)); - if (a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, a_Player->GetEquippedItem(), &Speed) < 0) + if (a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &a_Player->GetEquippedItem(), &Speed) < 0) { return false; } @@ -135,7 +135,7 @@ public: return false; } - if (a_World->CreateProjectile(a_BlockX + 0.5, a_BlockY + 1, a_BlockZ + 0.5, m_ProjectileKind, a_Player, a_Player->GetEquippedItem()) < 0) + if (a_World->CreateProjectile(a_BlockX + 0.5, a_BlockY + 1, a_BlockZ + 0.5, m_ProjectileKind, a_Player, &a_Player->GetEquippedItem()) < 0) { return false; } diff --git a/src/LineBlockTracer.cpp b/src/LineBlockTracer.cpp index b03652bab..2395aa43e 100644 --- a/src/LineBlockTracer.cpp +++ b/src/LineBlockTracer.cpp @@ -203,6 +203,15 @@ bool cLineBlockTracer::Item(cChunk * a_Chunk) m_Callbacks->OnNoChunk(); return false; } + + // Move to next block + if (!MoveToNextBlock()) + { + // We've reached the end + m_Callbacks->OnNoMoreHits(); + return true; + } + if (a_Chunk->IsValid()) { BLOCKTYPE BlockType; @@ -225,14 +234,6 @@ bool cLineBlockTracer::Item(cChunk * a_Chunk) } } - // Move to next block - if (!MoveToNextBlock()) - { - // We've reached the end - m_Callbacks->OnNoMoreHits(); - return true; - } - // Update the current chunk if (a_Chunk != NULL) { diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index 85b122034..de881f4eb 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -95,12 +95,14 @@ void cAggressiveMonster::Attack(float a_Dt) { m_AttackInterval += a_Dt * m_AttackRate; - if ((m_Target != NULL) && (m_AttackInterval > 3.0)) + if ((m_Target == NULL) || (m_AttackInterval < 3.0)) { - // Setting this higher gives us more wiggle room for attackrate - m_AttackInterval = 0.0; - m_Target->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); + return; } + + // Setting this higher gives us more wiggle room for attackrate + m_AttackInterval = 0.0; + m_Target->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); } diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp index 1157b81f9..118a6e93b 100644 --- a/src/Mobs/CaveSpider.cpp +++ b/src/Mobs/CaveSpider.cpp @@ -27,6 +27,21 @@ void cCaveSpider::Tick(float a_Dt, cChunk & a_Chunk) +void cCaveSpider::Attack(float a_Dt) +{ + super::Attack(a_Dt); + + if (m_Target->IsPawn()) + { + // TODO: Easy = no poison, Medium = 7 seconds, Hard = 15 seconds + ((cPawn *) m_Target)->AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0); + } +} + + + + + void cCaveSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer) { int LootingLevel = 0; diff --git a/src/Mobs/CaveSpider.h b/src/Mobs/CaveSpider.h index be9f174f9..3f8b2cece 100644 --- a/src/Mobs/CaveSpider.h +++ b/src/Mobs/CaveSpider.h @@ -17,6 +17,7 @@ public: CLASS_PROTODEF(cCaveSpider); virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Attack(float a_Dt) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; } ; diff --git a/src/Mobs/IronGolem.h b/src/Mobs/IronGolem.h index 41c60438c..30f9bedff 100644 --- a/src/Mobs/IronGolem.h +++ b/src/Mobs/IronGolem.h @@ -19,7 +19,7 @@ public: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - // Iron golems do not drown + // Iron golems do not drown nor float virtual void HandleAir(void) override {} virtual void SetSwimState(cChunk & a_Chunk) override {} } ; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 16f75db67..e36634c73 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -435,6 +435,7 @@ void cMonster::HandleFalling() + int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ) { int PosY = POSY_TOINT; @@ -492,9 +493,9 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) -void cMonster::KilledBy(cEntity * a_Killer) +void cMonster::KilledBy(TakeDamageInfo & a_TDI) { - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); if (m_SoundHurt != "") { m_World->BroadcastSoundEffect(m_SoundDeath, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); @@ -558,7 +559,7 @@ void cMonster::KilledBy(cEntity * a_Killer) break; } } - if ((a_Killer != NULL) && (!IsBaby())) + if ((a_TDI.Attacker != NULL) && (!IsBaby())) { m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward); } @@ -706,6 +707,25 @@ void cMonster::GetMonsterConfig(const AString & a_Name) +bool cMonster::IsUndead(void) +{ + switch (GetMobType()) + { + case mtZombie: + case mtZombiePigman: + case mtSkeleton: + case mtWither: + { + return true; + } + } + return false; +} + + + + + AString cMonster::MobTypeToString(cMonster::eType a_MobType) { // Mob types aren't sorted, so we need to search linearly: diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 7d7e90eb2..59bcdaa37 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -90,7 +90,7 @@ public: virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void KilledBy(cEntity * a_Killer) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; virtual void MoveToPosition(const Vector3f & a_Position); virtual void MoveToPosition(const Vector3d & a_Position); // tolua_export @@ -107,6 +107,9 @@ public: /// Reads the monster configuration for the specified monster name and assigns it to this object. void GetMonsterConfig(const AString & a_Name); + /** Returns whether this mob is undead (skeleton, zombie, etc.) */ + bool IsUndead(void); + virtual void EventLosePlayer(void); virtual void CheckEventLostPlayer(void); @@ -178,6 +181,7 @@ protected: /** Stores if mobile is currently moving towards the ultimate, final destination */ bool m_bMovingToDestination; + /** Finds the first non-air block position (not the highest, as cWorld::GetHeight does) If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1 If current Y is solid, goes up to find first nonsolid block, and returns that */ diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp index da4cc7765..578b47995 100644 --- a/src/Mobs/Wither.cpp +++ b/src/Mobs/Wither.cpp @@ -103,9 +103,9 @@ void cWither::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cWither::KilledBy(cEntity * a_Killer) +void cWither::KilledBy(TakeDamageInfo & a_TDI) { - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); class cPlayerCallback : public cPlayerListCallback { diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h index 03a320788..7d76f70f5 100644 --- a/src/Mobs/Wither.h +++ b/src/Mobs/Wither.h @@ -29,7 +29,7 @@ public: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void KilledBy(cEntity * a_Killer) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; private: diff --git a/src/Mobs/ZombiePigman.cpp b/src/Mobs/ZombiePigman.cpp index c9d94face..05350f877 100644 --- a/src/Mobs/ZombiePigman.cpp +++ b/src/Mobs/ZombiePigman.cpp @@ -37,11 +37,11 @@ void cZombiePigman::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cZombiePigman::KilledBy(cEntity * a_Killer) +void cZombiePigman::KilledBy(TakeDamageInfo & a_TDI) { - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); - if ((a_Killer != NULL) && (a_Killer->IsPlayer())) + if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer())) { // TODO: Anger all nearby zombie pigmen // TODO: In vanilla, if one player angers ZPs, do they attack any nearby player, or only that one attacker? diff --git a/src/Mobs/ZombiePigman.h b/src/Mobs/ZombiePigman.h index ab3cebf6d..a2ebc87cb 100644 --- a/src/Mobs/ZombiePigman.h +++ b/src/Mobs/ZombiePigman.h @@ -17,7 +17,7 @@ public: CLASS_PROTODEF(cZombiePigman); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void KilledBy(cEntity * a_Killer) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; } ; diff --git a/src/Protocol/Authenticator.cpp b/src/Protocol/Authenticator.cpp index 54b823e0f..001bc29cf 100644 --- a/src/Protocol/Authenticator.cpp +++ b/src/Protocol/Authenticator.cpp @@ -11,9 +11,6 @@ #include "PolarSSL++/BlockingSslClientSocket.h" -#include <sstream> -#include <iomanip> - @@ -21,6 +18,60 @@ #define DEFAULT_AUTH_SERVER "sessionserver.mojang.com" #define DEFAULT_AUTH_ADDRESS "/session/minecraft/hasJoined?username=%USERNAME%&serverId=%SERVERID%" +/** This is the data of the root certs for Starfield Technologies, the CA that signed sessionserver.mojang.com's cert: +Downloaded from http://certs.starfieldtech.com/repository/ */ +static const AString gStarfieldCACert( + // G2 cert + "-----BEGIN CERTIFICATE-----\n" + "MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx\n" + "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT\n" + "HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs\n" + "ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw\n" + "MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6\n" + "b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj\n" + "aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp\n" + "Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg\n" + "nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1\n" + "HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N\n" + "Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN\n" + "dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0\n" + "HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO\n" + "BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G\n" + "CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU\n" + "sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3\n" + "4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg\n" + "8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K\n" + "pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1\n" + "mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0\n" + "-----END CERTIFICATE-----\n\n" + // Original (G1) cert: + "-----BEGIN CERTIFICATE-----\n" + "MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl\n" + "MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp\n" + "U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw\n" + "NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE\n" + "ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp\n" + "ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3\n" + "DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf\n" + "8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN\n" + "+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0\n" + "X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa\n" + "K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA\n" + "1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G\n" + "A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR\n" + "zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0\n" + "YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD\n" + "bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w\n" + "DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3\n" + "L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D\n" + "eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl\n" + "xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp\n" + "VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY\n" + "WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=\n" + "-----END CERTIFICATE-----\n" +); + @@ -61,7 +112,8 @@ void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, co { if (!m_ShouldAuthenticate) { - cRoot::Get()->AuthenticateUser(a_ClientID, a_UserName, cClientHandle::GenerateOfflineUUID(a_UserName)); + Json::Value Value; + cRoot::Get()->AuthenticateUser(a_ClientID, a_UserName, cClientHandle::GenerateOfflineUUID(a_UserName), Value); return; } @@ -121,10 +173,11 @@ void cAuthenticator::Execute(void) AString NewUserName = UserName; AString UUID; - if (AuthWithYggdrasil(NewUserName, ServerID, UUID)) + Json::Value Properties; + if (AuthWithYggdrasil(NewUserName, ServerID, UUID, Properties)) { - LOGINFO("User %s authenticated with UUID '%s'", NewUserName.c_str(), UUID.c_str()); - cRoot::Get()->AuthenticateUser(ClientID, NewUserName, UUID); + LOGINFO("User %s authenticated with UUID %s", NewUserName.c_str(), UUID.c_str()); + cRoot::Get()->AuthenticateUser(ClientID, NewUserName, UUID, Properties); } else { @@ -137,97 +190,27 @@ void cAuthenticator::Execute(void) -bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, AString & a_UUID) +bool cAuthenticator::SecureGetFromAddress(const AString & a_CACerts, const AString & a_ExpectedPeerName, const AString & a_Data, AString & a_Response) { - LOGD("Trying to auth user %s", a_UserName.c_str()); - - int ret; - unsigned char buf[1024]; - - /* Initialize certificates */ - // This is the data of the root certs for Starfield Technologies, the CA that signed sessionserver.mojang.com's cert: - // Downloaded from http://certs.starfieldtech.com/repository/ - static const AString StarfieldCACert( - // G2 cert - "-----BEGIN CERTIFICATE-----\n" - "MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx\n" - "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT\n" - "HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs\n" - "ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw\n" - "MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6\n" - "b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj\n" - "aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp\n" - "Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" - "ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg\n" - "nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1\n" - "HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N\n" - "Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN\n" - "dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0\n" - "HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO\n" - "BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G\n" - "CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU\n" - "sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3\n" - "4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg\n" - "8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K\n" - "pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1\n" - "mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0\n" - "-----END CERTIFICATE-----\n\n" - // Original (G1) cert: - "-----BEGIN CERTIFICATE-----\n" - "MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl\n" - "MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp\n" - "U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw\n" - "NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE\n" - "ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp\n" - "ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3\n" - "DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf\n" - "8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN\n" - "+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0\n" - "X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa\n" - "K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA\n" - "1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G\n" - "A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR\n" - "zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0\n" - "YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD\n" - "bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w\n" - "DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3\n" - "L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D\n" - "eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl\n" - "xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp\n" - "VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY\n" - "WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=\n" - "-----END CERTIFICATE-----\n" - ); - // Connect the socket: cBlockingSslClientSocket Socket; - Socket.SetTrustedRootCertsFromString(StarfieldCACert, m_Server); - if (!Socket.Connect(m_Server, 443)) + Socket.SetTrustedRootCertsFromString(a_CACerts, a_ExpectedPeerName); + if (!Socket.Connect(a_ExpectedPeerName, 443)) { - LOGWARNING("cAuthenticator: Can't connect to %s: %s", m_Server.c_str(), Socket.GetLastErrorText().c_str()); + LOGWARNING("cAuthenticator: Can't connect to %s: %s", a_ExpectedPeerName.c_str(), Socket.GetLastErrorText().c_str()); return false; } - // Create the GET request: - AString ActualAddress = m_Address; - ReplaceString(ActualAddress, "%USERNAME%", a_UserName); - ReplaceString(ActualAddress, "%SERVERID%", a_ServerId); - - AString Request; - Request += "GET " + ActualAddress + " HTTP/1.0\r\n"; - Request += "Host: " + m_Server + "\r\n"; - Request += "User-Agent: MCServer\r\n"; - Request += "Connection: close\r\n"; - Request += "\r\n"; - - if (!Socket.Send(Request.c_str(), Request.size())) + if (!Socket.Send(a_Data.c_str(), a_Data.size())) { LOGWARNING("cAuthenticator: Writing SSL data failed: %s", Socket.GetLastErrorText().c_str()); return false; } // Read the HTTP response: - std::string Response; + int ret; + unsigned char buf[1024]; + for (;;) { ret = Socket.Receive(buf, sizeof(buf)); @@ -235,7 +218,7 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S if ((ret == POLARSSL_ERR_NET_WANT_READ) || (ret == POLARSSL_ERR_NET_WANT_WRITE)) { // This value should never be returned, it is handled internally by cBlockingSslClientSocket - LOGWARNING("cAuthenticator: SSL reading failed internally."); + LOGWARNING("cAuthenticator: SSL reading failed internally"); return false; } if (ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) @@ -252,18 +235,46 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S break; } - Response.append((const char *)buf, (size_t)ret); + a_Response.append((const char *)buf, (size_t)ret); } Socket.Disconnect(); + return true; +} + + + + + +bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, AString & a_UUID, Json::Value & a_Properties) +{ + LOGD("Trying to authenticate user %s", a_UserName.c_str()); + + // Create the GET request: + AString ActualAddress = m_Address; + ReplaceString(ActualAddress, "%USERNAME%", a_UserName); + ReplaceString(ActualAddress, "%SERVERID%", a_ServerId); + + AString Request; + Request += "GET " + ActualAddress + " HTTP/1.0\r\n"; + Request += "Host: " + m_Server + "\r\n"; + Request += "User-Agent: MCServer\r\n"; + Request += "Connection: close\r\n"; + Request += "\r\n"; + + AString Response; + if (!SecureGetFromAddress(gStarfieldCACert, m_Server, Request, Response)) + { + return false; + } // Check the HTTP status line: - AString prefix("HTTP/1.1 200 OK"); + const AString Prefix("HTTP/1.1 200 OK"); AString HexDump; - if (Response.compare(0, prefix.size(), prefix)) + if (Response.compare(0, Prefix.size(), Prefix)) { - LOGINFO("User %s failed to auth, bad http status line received", a_UserName.c_str()); - LOG("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); + LOGINFO("User %s failed to auth, bad HTTP status line received", a_UserName.c_str()); + LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); return false; } @@ -271,8 +282,8 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S size_t idxHeadersEnd = Response.find("\r\n\r\n"); if (idxHeadersEnd == AString::npos) { - LOGINFO("User %s failed to authenticate, bad http response header received", a_UserName.c_str()); - LOG("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); + LOGINFO("User %s failed to authenticate, bad HTTP response header received", a_UserName.c_str()); + LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); return false; } Response.erase(0, idxHeadersEnd + 4); @@ -286,12 +297,13 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S Json::Reader reader; if (!reader.parse(Response, root, false)) { - LOGWARNING("cAuthenticator: Cannot parse Received Data to json!"); + LOGWARNING("cAuthenticator: Cannot parse received data (authentication) to JSON!"); return false; } a_UserName = root.get("name", "Unknown").asString(); a_UUID = root.get("id", "").asString(); - + a_Properties = root["properties"]; + // If the UUID doesn't contain the hashes, insert them at the proper places: if (a_UUID.size() == 32) { @@ -300,6 +312,7 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S a_UUID.insert(18, "-"); a_UUID.insert(23, "-"); } + return true; } @@ -307,3 +320,69 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S +/* In case we want to export this function to the plugin API later - don't forget to add the relevant INI configuration lines for DEFAULT_PROPERTIES_ADDRESS + +#define DEFAULT_PROPERTIES_ADDRESS "/session/minecraft/profile/%UUID%" + +// Gets the properties, such as skin, of a player based on their UUID via Mojang's API +bool GetPlayerProperties(const AString & a_UUID, Json::Value & a_Properties); + +bool cAuthenticator::GetPlayerProperties(const AString & a_UUID, Json::Value & a_Properties) +{ + LOGD("Trying to get properties for user %s", a_UUID.c_str()); + + // Create the GET request: + AString ActualAddress = m_PropertiesAddress; + ReplaceString(ActualAddress, "%UUID%", a_UUID); + + AString Request; + Request += "GET " + ActualAddress + " HTTP/1.0\r\n"; + Request += "Host: " + m_Server + "\r\n"; + Request += "User-Agent: MCServer\r\n"; + Request += "Connection: close\r\n"; + Request += "\r\n"; + + AString Response; + if (!ConnectSecurelyToAddress(gStarfieldCACert, m_Server, Request, Response)) + { + return false; + } + + // Check the HTTP status line: + const AString Prefix("HTTP/1.1 200 OK"); + AString HexDump; + if (Response.compare(0, Prefix.size(), Prefix)) + { + LOGINFO("Failed to get properties for user %s, bad HTTP status line received", a_UUID.c_str()); + LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); + return false; + } + + // Erase the HTTP headers from the response: + size_t idxHeadersEnd = Response.find("\r\n\r\n"); + if (idxHeadersEnd == AString::npos) + { + LOGINFO("Failed to get properties for user %s, bad HTTP response header received", a_UUID.c_str()); + LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); + return false; + } + Response.erase(0, idxHeadersEnd + 4); + + // Parse the Json response: + if (Response.empty()) + { + return false; + } + + Json::Value root; + Json::Reader reader; + if (!reader.parse(Response, root, false)) + { + LOGWARNING("cAuthenticator: Cannot parse received properties data to JSON!"); + return false; + } + + a_Properties = root["properties"]; + return true; +} +*/ diff --git a/src/Protocol/Authenticator.h b/src/Protocol/Authenticator.h index 211f51394..244d94c0b 100644 --- a/src/Protocol/Authenticator.h +++ b/src/Protocol/Authenticator.h @@ -23,6 +23,11 @@ // fwd: "cRoot.h" class cRoot; +namespace Json +{ + class Value; +} + @@ -73,13 +78,19 @@ private: AString m_Server; AString m_Address; + AString m_PropertiesAddress; bool m_ShouldAuthenticate; /** cIsThread override: */ virtual void Execute(void) override; - /** Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep) */ - bool AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, AString & a_UUID); + /** Connects to a hostname using SSL, sends given data, and sets the response, returning whether all was successful or not */ + bool SecureGetFromAddress(const AString & a_CACerts, const AString & a_ExpectedPeerName, const AString & a_Request, AString & a_Response); + + /** Returns true if the user authenticated okay, false on error + Sets the username, UUID, and properties (i.e. skin) fields + */ + bool AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, AString & a_UUID, Json::Value & a_Properties); }; diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp index b2b84953c..7a8c2221e 100644 --- a/src/Protocol/Protocol132.cpp +++ b/src/Protocol/Protocol132.cpp @@ -280,16 +280,12 @@ void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player) void cProtocol132::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) { - int SrcX = std::floor(a_X * 8.0); - int SrcY = std::floor(a_Y * 8.0); - int SrcZ = std::floor(a_Z * 8.0); - cCSLock Lock(m_CSPacket); WriteByte (PACKET_SOUND_EFFECT); WriteString (a_SoundName); - WriteInt (SrcX); - WriteInt (SrcY); - WriteInt (SrcZ); + WriteInt ((int)(a_X * 8.0)); + WriteInt ((int)(a_Y * 8.0)); + WriteInt ((int)(a_Z * 8.0)); WriteFloat (a_Volume); WriteChar ((char)(a_Pitch * 63.0f)); Flush(); diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index ae077d69e..50c2014c6 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -1088,15 +1088,11 @@ void cProtocol172::SendSoundEffect(const AString & a_SoundName, double a_X, doub { ASSERT(m_State == 3); // In game mode? - int SrcX = std::floor(a_X * 8.0); - int SrcY = std::floor(a_Y * 8.0); - int SrcZ = std::floor(a_Z * 8.0); - cPacketizer Pkt(*this, 0x29); // Sound Effect packet Pkt.WriteString(a_SoundName); - Pkt.WriteInt(SrcX); - Pkt.WriteInt(SrcY); - Pkt.WriteInt(SrcZ); + Pkt.WriteInt((int)(a_X * 8.0)); + Pkt.WriteInt((int)(a_Y * 8.0)); + Pkt.WriteInt((int)(a_Z * 8.0)); Pkt.WriteFloat(a_Volume); Pkt.WriteByte((Byte)(a_Pitch * 63)); } @@ -3019,7 +3015,18 @@ void cProtocol176::SendPlayerSpawn(const cPlayer & a_Player) Pkt.WriteVarInt(a_Player.GetUniqueID()); Pkt.WriteString(a_Player.GetClientHandle()->GetUUID()); Pkt.WriteString(a_Player.GetName()); - Pkt.WriteVarInt(0); // We have no data to send here + + const Json::Value & Properties = m_Client->GetProperties(); + const Json::Value::const_iterator End = Properties.end(); + Pkt.WriteVarInt(Properties.size()); + + for (Json::Value::iterator itr = Properties.begin(); itr != End; ++itr) + { + Pkt.WriteString(((Json::Value)*itr).get("name", "").toStyledString()); + Pkt.WriteString(((Json::Value)*itr).get("value", "").toStyledString()); + Pkt.WriteString(((Json::Value)*itr).get("signature", "").toStyledString()); + } + Pkt.WriteFPInt(a_Player.GetPosX()); Pkt.WriteFPInt(a_Player.GetPosY()); Pkt.WriteFPInt(a_Player.GetPosZ()); diff --git a/src/Root.cpp b/src/Root.cpp index c82b05a66..ec1351ef1 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -499,9 +499,9 @@ void cRoot::KickUser(int a_ClientID, const AString & a_Reason) -void cRoot::AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID) +void cRoot::AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties) { - m_Server->AuthenticateUser(a_ClientID, a_Name, a_UUID); + m_Server->AuthenticateUser(a_ClientID, a_Name, a_UUID, a_Properties); } diff --git a/src/Root.h b/src/Root.h index d2a4d1eed..4a3c205d3 100644 --- a/src/Root.h +++ b/src/Root.h @@ -26,6 +26,11 @@ class cCompositeChat; typedef cItemCallback<cPlayer> cPlayerListCallback; typedef cItemCallback<cWorld> cWorldListCallback; +namespace Json +{ + class Value; +} + @@ -89,7 +94,7 @@ public: void KickUser(int a_ClientID, const AString & a_Reason); /// Called by cAuthenticator to auth the specified user - void AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID); + void AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties); /// Executes commands queued in the command queue void TickCommands(void); diff --git a/src/Server.cpp b/src/Server.cpp index 9220731eb..17551eeb1 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -656,14 +656,14 @@ void cServer::KickUser(int a_ClientID, const AString & a_Reason) -void cServer::AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID) +void cServer::AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties) { cCSLock Lock(m_CSClients); for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) { if ((*itr)->GetUniqueID() == a_ClientID) { - (*itr)->Authenticate(a_Name, a_UUID); + (*itr)->Authenticate(a_Name, a_UUID, a_Properties); return; } } // for itr - m_Clients[] diff --git a/src/Server.h b/src/Server.h index 5227799e8..cf5fa524f 100644 --- a/src/Server.h +++ b/src/Server.h @@ -37,10 +37,15 @@ class cPlayer; class cClientHandle; class cIniFile; -class cCommandOutputCallback ; +class cCommandOutputCallback; typedef std::list<cClientHandle *> cClientHandleList; +namespace Json +{ + class Value; +} + @@ -83,7 +88,9 @@ public: // tolua_export void Shutdown(void); void KickUser(int a_ClientID, const AString & a_Reason); - void AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID); // Called by cAuthenticator to auth the specified user + + /** Authenticates the specified user, called by cAuthenticator */ + void AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties); const AString & GetServerID(void) const { return m_ServerID; } // tolua_export diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index 8b9d830cd..55c5cbd09 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -396,7 +396,13 @@ void cIncrementalRedstoneSimulator::HandleRedstoneTorch(int a_RelBlockX, int a_R int X = a_RelBlockX; int Y = a_RelBlockY; int Z = a_RelBlockZ; AddFaceDirection(X, Y, Z, cBlockTorchHandler::MetaDataToDirection(m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)), true); // Inverse true to get the block torch is on - if (AreCoordsDirectlyPowered(X, Y, Z)) + cChunk * Neighbour = m_Chunk->GetRelNeighborChunk(X, Z); + if ((Neighbour == NULL) || !Neighbour->IsValid()) + { + return; + } + + if (AreCoordsDirectlyPowered(X, Y, Z, Neighbour)) { // There was a match, torch goes off m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_TORCH_OFF, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)); @@ -445,9 +451,15 @@ void cIncrementalRedstoneSimulator::HandleRedstoneTorch(int a_RelBlockX, int a_R // Check if the block the torch is on is powered int X = a_RelBlockX; int Y = a_RelBlockY; int Z = a_RelBlockZ; AddFaceDirection(X, Y, Z, cBlockTorchHandler::MetaDataToDirection(m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)), true); // Inverse true to get the block torch is on - + + cChunk * Neighbour = m_Chunk->GetRelNeighborChunk(X, Z); + if ((Neighbour == NULL) || !Neighbour->IsValid()) + { + return; + } + // See if off state torch can be turned on again - if (AreCoordsDirectlyPowered(X, Y, Z)) + if (AreCoordsDirectlyPowered(X, Y, Z, Neighbour)) { return; // Something matches, torch still powered } @@ -1492,13 +1504,14 @@ void cIncrementalRedstoneSimulator::HandleTripwire(int a_RelBlockX, int a_RelBlo -bool cIncrementalRedstoneSimulator::AreCoordsDirectlyPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +bool cIncrementalRedstoneSimulator::AreCoordsDirectlyPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, cChunk * a_Chunk) { + // Torches want to access neighbour's data when on a wall, hence the extra chunk parameter + int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - PoweredBlocksList * Powered = m_Chunk->GetNeighborChunk(BlockX, BlockZ)->GetRedstoneSimulatorPoweredBlocksList(); // Torches want to access neighbour's data when on a wall - for (PoweredBlocksList::const_iterator itr = Powered->begin(); itr != Powered->end(); ++itr) // Check powered list + for (PoweredBlocksList::const_iterator itr = a_Chunk->GetRedstoneSimulatorPoweredBlocksList()->begin(); itr != a_Chunk->GetRedstoneSimulatorPoweredBlocksList()->end(); ++itr) // Check powered list { if (itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) { @@ -1900,6 +1913,11 @@ void cIncrementalRedstoneSimulator::SetBlockPowered(int a_RelBlockX, int a_RelBl int SourceZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelSourceZ; cChunk * Neighbour = m_Chunk->GetRelNeighborChunkAdjustCoords(a_RelBlockX, a_RelBlockZ); // Adjust coordinates for the later call using these values + if ((Neighbour == NULL) || !Neighbour->IsValid()) + { + return; + } + PoweredBlocksList * Powered = Neighbour->GetRedstoneSimulatorPoweredBlocksList(); // We need to insert the value into the chunk who owns the block position for (PoweredBlocksList::iterator itr = Powered->begin(); itr != Powered->end(); ++itr) { @@ -1977,6 +1995,11 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered( } cChunk * Neighbour = m_Chunk->GetNeighborChunk(BlockX, BlockZ); + if ((Neighbour == NULL) || !Neighbour->IsValid()) + { + return; + } + LinkedBlocksList * Linked = Neighbour->GetRedstoneSimulatorLinkedBlocksList(); for (LinkedBlocksList::iterator itr = Linked->begin(); itr != Linked->end(); ++itr) // Check linked powered list { @@ -2078,6 +2101,13 @@ bool cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_RelBlockX, in void cIncrementalRedstoneSimulator::SetSourceUnpowered(int a_SourceX, int a_SourceY, int a_SourceZ, cChunk * a_Chunk, bool a_IsFirstCall) { + if (!a_IsFirstCall) // The neighbouring chunks passed when this parameter is false may be invalid + { + if ((a_Chunk == NULL) || !a_Chunk->IsValid()) + { + return; + } + } // TODO: on C++11 support, change both of these to llama functions pased to a std::remove_if for (PoweredBlocksList::iterator itr = a_Chunk->GetRedstoneSimulatorPoweredBlocksList()->begin(); itr != a_Chunk->GetRedstoneSimulatorPoweredBlocksList()->end();) diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h index ce987a60f..1faf4187b 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.h +++ b/src/Simulator/IncrementalRedstoneSimulator.h @@ -162,9 +162,9 @@ private: void SetSourceUnpowered(int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, cChunk * a_Chunk, bool a_IsFirstCall = true); /** Returns if a coordinate is powered or linked powered */ - bool AreCoordsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) { return AreCoordsDirectlyPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ) || AreCoordsLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); } + bool AreCoordsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) { return AreCoordsDirectlyPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk) || AreCoordsLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); } /** Returns if a coordinate is in the directly powered blocks list */ - bool AreCoordsDirectlyPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + bool AreCoordsDirectlyPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, cChunk * a_Chunk); /** Returns if a coordinate is in the indirectly powered blocks list */ bool AreCoordsLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); /** Returns if a coordinate was marked as simulated (for blocks toggleable by players) */ diff --git a/src/World.cpp b/src/World.cpp index 46a193d4f..f7279e9bc 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -2363,7 +2363,7 @@ void cWorld::RemovePlayer(cPlayer * a_Player) } { cCSLock Lock(m_CSPlayers); - LOGD("Removing player \"%s\" from world \"%s\".", a_Player->GetName().c_str(), m_WorldName.c_str()); + LOGD("Removing player %s from world \"%s\"", a_Player->GetName().c_str(), m_WorldName.c_str()); m_Players.remove(a_Player); } @@ -2468,10 +2468,13 @@ cPlayer * cWorld::FindClosestPlayer(const Vector3d & a_Pos, float a_SightLimit, if (Distance < ClosestDistance) { - if (a_CheckLineOfSight && !LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length())) + if (a_CheckLineOfSight) { - ClosestDistance = Distance; - ClosestPlayer = *itr; + if(!LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length())) + { + ClosestDistance = Distance; + ClosestPlayer = *itr; + } } else { @@ -2998,7 +3001,7 @@ int cWorld::SpawnMobFinalize(cMonster * a_Monster) -int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem & a_Item, const Vector3d * a_Speed) +int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem * a_Item, const Vector3d * a_Speed) { cProjectileEntity * Projectile = cProjectileEntity::Create(a_Kind, a_Creator, a_PosX, a_PosY, a_PosZ, a_Item, a_Speed); if (Projectile == NULL) diff --git a/src/World.h b/src/World.h index be055f004..5bb0e640f 100644 --- a/src/World.h +++ b/src/World.h @@ -751,7 +751,7 @@ public: /** Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise Item parameter used currently for Fireworks to correctly set entity metadata based on item metadata */ - int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem & a_Item, const Vector3d * a_Speed = NULL); // tolua_export + int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem * a_Item, const Vector3d * a_Speed = NULL); // tolua_export /** Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread! */ int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); } diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 3ccbceb7a..6d0b60371 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -29,6 +29,7 @@ #include "../Entities/Minecart.h" #include "../Entities/Pickup.h" #include "../Entities/ArrowEntity.h" +#include "../Entities/SplashPotionEntity.h" #include "../Entities/TNTEntity.h" #include "../Entities/ExpOrb.h" #include "../Entities/HangingEntity.h" @@ -604,6 +605,16 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile) m_Writer.AddDouble("damage", Arrow->GetDamageCoeff()); break; } + case cProjectileEntity::pkSplashPotion: + { + cSplashPotionEntity * Potion = (cSplashPotionEntity *)a_Projectile; + + m_Writer.AddInt("EffectType", (Int16)Potion->GetEntityEffectType()); + m_Writer.AddInt("EffectDuration", (Int16)Potion->GetEntityEffect().GetDuration()); + m_Writer.AddShort("EffectIntensity", Potion->GetEntityEffect().GetIntensity()); + m_Writer.AddDouble("EffectDistanceModifier", Potion->GetEntityEffect().GetDistanceModifier()); + m_Writer.AddInt("PotionName", Potion->GetPotionParticleType()); + } case cProjectileEntity::pkGhastFireball: { m_Writer.AddInt("ExplosionPower", 1); diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h index f73f4ad7d..710a06a70 100644 --- a/src/WorldStorage/NBTChunkSerializer.h +++ b/src/WorldStorage/NBTChunkSerializer.h @@ -46,6 +46,7 @@ class cTNTEntity; class cExpOrb; class cHangingEntity; class cItemFrame; +class cEntityEffect; diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 0b2a4c053..1a43cf4ba 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -36,6 +36,7 @@ #include "../Entities/Minecart.h" #include "../Entities/Pickup.h" #include "../Entities/ArrowEntity.h" +#include "../Entities/SplashPotionEntity.h" #include "../Entities/ThrownEggEntity.h" #include "../Entities/ThrownEnderPearlEntity.h" #include "../Entities/ThrownSnowballEntity.h" @@ -1156,6 +1157,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a { LoadArrowFromNBT(a_Entities, a_NBT, a_EntityTagIdx); } + else if (strncmp(a_IDTag, "SplashPotion", a_IDTagLength) == 0) + { + LoadSplashPotionFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } else if (strncmp(a_IDTag, "Snowball", a_IDTagLength) == 0) { LoadSnowballFromNBT(a_Entities, a_NBT, a_EntityTagIdx); @@ -1662,6 +1667,29 @@ void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ +void cWSSAnvil::LoadSplashPotionFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cSplashPotionEntity> SplashPotion(new cSplashPotionEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0), cEntityEffect::effNoEffect, cEntityEffect(), 0)); + if (!LoadProjectileBaseFromNBT(*SplashPotion.get(), a_NBT, a_TagIdx)) + { + return; + } + + int EffectDuration = a_NBT.FindChildByName(a_TagIdx, "EffectDuration"); + int EffectIntensity = a_NBT.FindChildByName(a_TagIdx, "EffectIntensity"); + int EffectDistanceModifier = a_NBT.FindChildByName(a_TagIdx, "EffectDistanceModifier"); + + SplashPotion->SetEntityEffectType((cEntityEffect::eType) a_NBT.FindChildByName(a_TagIdx, "EffectType")); + SplashPotion->SetEntityEffect(cEntityEffect(EffectDuration, EffectIntensity, EffectDistanceModifier)); + SplashPotion->SetPotionParticleType(a_NBT.FindChildByName(a_TagIdx, "PotionName")); + + // Store the new splash potion in the entities list: + a_Entities.push_back(SplashPotion.release()); +} + + + + void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 6f619fdb8..5b629f017 100644 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -163,6 +163,7 @@ protected: void LoadMinecartHFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadArrowFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSplashPotionFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadSnowballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadEggFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadFireballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); |