summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormadmaxoft <github@xoft.cz>2013-08-30 14:24:03 +0200
committermadmaxoft <github@xoft.cz>2013-08-30 14:24:03 +0200
commit17ad4c2610f2c33d5b4a8b42b7d4b8fbda9ade32 (patch)
treefeaaf5bf652bfdc542dee9b9c452391531aee6ba
parentImplemented basic physics for projectiles. (diff)
downloadcuberite-17ad4c2610f2c33d5b4a8b42b7d4b8fbda9ade32.tar
cuberite-17ad4c2610f2c33d5b4a8b42b7d4b8fbda9ade32.tar.gz
cuberite-17ad4c2610f2c33d5b4a8b42b7d4b8fbda9ade32.tar.bz2
cuberite-17ad4c2610f2c33d5b4a8b42b7d4b8fbda9ade32.tar.lz
cuberite-17ad4c2610f2c33d5b4a8b42b7d4b8fbda9ade32.tar.xz
cuberite-17ad4c2610f2c33d5b4a8b42b7d4b8fbda9ade32.tar.zst
cuberite-17ad4c2610f2c33d5b4a8b42b7d4b8fbda9ade32.zip
-rw-r--r--VC2008/MCServer.vcproj4
-rw-r--r--source/BlockID.h4
-rw-r--r--source/ClientHandle.cpp8
-rw-r--r--source/Entities/Player.cpp44
-rw-r--r--source/Entities/Player.h15
-rw-r--r--source/Entities/ProjectileEntity.cpp41
-rw-r--r--source/Entities/ProjectileEntity.h9
-rw-r--r--source/Items/ItemBow.h80
-rw-r--r--source/Items/ItemHandler.cpp22
-rw-r--r--source/Items/ItemHandler.h9
10 files changed, 221 insertions, 15 deletions
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj
index 068992f62..fb82c3520 100644
--- a/VC2008/MCServer.vcproj
+++ b/VC2008/MCServer.vcproj
@@ -2240,6 +2240,10 @@
>
</File>
<File
+ RelativePath="..\source\Items\ItemBow.h"
+ >
+ </File>
+ <File
RelativePath="..\source\Items\ItemBrewingStand.h"
>
</File>
diff --git a/source/BlockID.h b/source/BlockID.h
index 0eb3df96d..de8335e85 100644
--- a/source/BlockID.h
+++ b/source/BlockID.h
@@ -367,7 +367,9 @@ enum ENUM_ITEM_ID
// Keep these two as the last values of the disc list, without a number - they will get their correct number assigned automagically by C++
// IsValidItem() depends on this!
E_ITEM_LAST_DISC_PLUS_ONE, ///< Useless, really, but needs to be present for the following value
- E_ITEM_LAST_DISC = E_ITEM_LAST_DISC_PLUS_ONE - 1 ///< Maximum disc itemtype number used
+ E_ITEM_LAST_DISC = E_ITEM_LAST_DISC_PLUS_ONE - 1, ///< Maximum disc itemtype number used
+
+ E_ITEM_LAST = E_ITEM_LAST_DISC, ///< Maximum valid ItemType
};
diff --git a/source/ClientHandle.cpp b/source/ClientHandle.cpp
index bed9fab92..555ecb952 100644
--- a/source/ClientHandle.cpp
+++ b/source/ClientHandle.cpp
@@ -576,8 +576,8 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, ch
// A plugin doesn't agree with the action. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
return;
}
+ ItemHandler->OnItemShoot(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
}
- LOGINFO("%s: Status SHOOT not implemented", __FUNCTION__);
return;
}
@@ -790,16 +790,16 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, c
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
- cBlockHandler * Handler = cBlockHandler::GetBlockHandler(BlockType);
+ cBlockHandler * BlockHandler = cBlockHandler::GetBlockHandler(BlockType);
- if (Handler->IsUseable() && !m_Player->IsCrouched())
+ if (BlockHandler->IsUseable() && !m_Player->IsCrouched())
{
if (PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
{
// A plugin doesn't agree with using the block, abort
return;
}
- Handler->OnUse(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
+ BlockHandler->OnUse(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
return;
}
diff --git a/source/Entities/Player.cpp b/source/Entities/Player.cpp
index 119afbafc..0cb047933 100644
--- a/source/Entities/Player.cpp
+++ b/source/Entities/Player.cpp
@@ -64,6 +64,8 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
, m_IsSwimming(false)
, m_IsSubmerged(false)
, m_EatingFinishTick(-1)
+ , m_IsChargingBow(false)
+ , m_BowCharge(0)
{
LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d",
a_PlayerName.c_str(), a_Client->GetIPString().c_str(),
@@ -213,6 +215,13 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
// Handle air drowning stuff
HandleAir();
+
+ // Handle charging the bow:
+ if (m_IsChargingBow)
+ {
+ m_BowCharge += 1;
+ LOGD("Player \"%s\" charging bow: %d", m_PlayerName.c_str(), m_BowCharge);
+ }
if (m_bDirtyPosition)
{
@@ -253,6 +262,41 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
+void cPlayer::StartChargingBow(void)
+{
+ LOGD("Player \"%s\" started charging their bow", m_PlayerName.c_str());
+ m_IsChargingBow = true;
+ m_BowCharge = 0;
+}
+
+
+
+
+
+int cPlayer::FinishChargingBow(void)
+{
+ LOGD("Player \"%s\" finished charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge);
+ int res = m_BowCharge;
+ m_IsChargingBow = false;
+ m_BowCharge = 0;
+ return res;
+}
+
+
+
+
+
+void cPlayer::CancelChargingBow(void)
+{
+ LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge);
+ m_IsChargingBow = false;
+ m_BowCharge = 0;
+}
+
+
+
+
+
void cPlayer::SetTouchGround(bool a_bTouchGround)
{
// If just
diff --git a/source/Entities/Player.h b/source/Entities/Player.h
index 5dcce8421..4adf946db 100644
--- a/source/Entities/Player.h
+++ b/source/Entities/Player.h
@@ -62,6 +62,18 @@ public:
/// Returns the currently equipped boots; empty item if none
virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); }
+
+ /// Starts charging the equipped bow
+ void StartChargingBow(void);
+
+ /// Finishes charging the current bow. Returns the number of ticks for which the bow has been charged
+ int FinishChargingBow(void);
+
+ /// Cancels the current bow charging
+ void CancelChargingBow(void);
+
+ /// Returns true if the player is currently charging the bow
+ bool IsChargingBox(void) const { return m_IsChargingBow; }
void SetTouchGround( bool a_bTouchGround );
inline void SetStance( const double a_Stance ) { m_Stance = a_Stance; }
@@ -351,6 +363,9 @@ protected:
/// The world tick in which eating will be finished. -1 if not eating
Int64 m_EatingFinishTick;
+
+ bool m_IsChargingBow;
+ int m_BowCharge;
virtual void Destroyed(void);
diff --git a/source/Entities/ProjectileEntity.cpp b/source/Entities/ProjectileEntity.cpp
index 4983943a9..f405e9aa4 100644
--- a/source/Entities/ProjectileEntity.cpp
+++ b/source/Entities/ProjectileEntity.cpp
@@ -229,6 +229,47 @@ cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a
+cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) :
+ super(pkArrow, &a_Player, PosFromPlayerPos(a_Player), SpeedFromPlayerLook(a_Player, a_Force), 0.5, 0.5),
+ m_PickupState(psInSurvivalOrCreative),
+ m_DamageCoeff(2)
+{
+}
+
+
+
+
+
+Vector3d cArrowEntity::PosFromPlayerPos(const cPlayer & a_Player)
+{
+ Vector3d res = a_Player.GetEyePosition();
+
+ // Adjust the position to be just outside the player's bounding box:
+ res.x += 0.16 * cos(a_Player.GetPitch());
+ res.y += -0.1;
+ res.z += 0.16 * sin(a_Player.GetPitch());
+
+ return res;
+}
+
+
+
+
+
+Vector3d cArrowEntity::SpeedFromPlayerLook(const cPlayer & a_Player, double a_Force)
+{
+ Vector3d res = a_Player.GetLookVector();
+ res.Normalize();
+
+ // TODO: Add a slight random change (+-0.0075 in each direction)
+
+ return res * a_Force * 1.5 * 20;
+}
+
+
+
+
+
bool cArrowEntity::CanPickup(const cPlayer & a_Player) const
{
switch (m_PickupState)
diff --git a/source/Entities/ProjectileEntity.h b/source/Entities/ProjectileEntity.h
index 7a97a2215..de0366014 100644
--- a/source/Entities/ProjectileEntity.h
+++ b/source/Entities/ProjectileEntity.h
@@ -103,8 +103,17 @@ public:
/// Creates a new arrow with psNoPickup state and default damage modifier coeff
cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d a_Speed);
+ /// Creates a new arrow as shot by a player, initializes it from the player object
+ cArrowEntity(cPlayer & a_Player, double a_Force);
+
// tolua_begin
+ /// Returns the initial arrow position, as defined by the player eye position + adjustment.
+ static Vector3d PosFromPlayerPos(const cPlayer & a_Player);
+
+ /// Returns the initial arrow speed, as defined by the player look vector and the force coefficient
+ static Vector3d SpeedFromPlayerLook(const cPlayer & a_Player, double a_Force);
+
/// Returns whether the arrow can be picked up by players
ePickupState GetPickupState(void) const { return m_PickupState; }
diff --git a/source/Items/ItemBow.h b/source/Items/ItemBow.h
new file mode 100644
index 000000000..845192ef7
--- /dev/null
+++ b/source/Items/ItemBow.h
@@ -0,0 +1,80 @@
+
+// ItemBow.h
+
+// Declares the cItemBowHandler class representing the itemhandler for bows
+
+
+
+
+
+#pragma once
+
+#include "../Entities/ProjectileEntity.h"
+
+
+
+
+
+class cItemBowHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemBowHandler(void) :
+ super(E_ITEM_BOW)
+ {
+ }
+
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ ASSERT(a_Player != NULL);
+
+ // Check if the player has an arrow in the inventory, or is in Creative:
+ if (!(a_Player->IsGameModeCreative() || a_Player->GetInventory().HasItems(cItem(E_ITEM_ARROW))))
+ {
+ return false;
+ }
+ a_Player->StartChargingBow();
+ return true;
+ }
+
+
+ virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override
+ {
+ // Actual shot - produce the arrow with speed based on the ticks that the bow was charged
+ ASSERT(a_Player != NULL);
+
+ int BowCharge = a_Player->FinishChargingBow();
+ double Force = (double)BowCharge / 20;
+ Force = (Force * Force + 2 * Force) / 3; // This formula is used by the 1.6.2 client
+ if (Force < 0.1)
+ {
+ // Too little force, ignore the shot
+ return;
+ }
+ if (Force > 1)
+ {
+ Force = 1;
+ }
+
+ // Create the arrow entity:
+ cArrowEntity * Arrow = new cArrowEntity(*a_Player, Force * 2);
+ if (Arrow == NULL)
+ {
+ return;
+ }
+ if (!Arrow->Initialize(a_Player->GetWorld()))
+ {
+ delete Arrow;
+ return;
+ }
+ a_Player->GetWorld()->BroadcastSpawnEntity(*Arrow);
+ }
+} ;
+
+
+
+
+
diff --git a/source/Items/ItemHandler.cpp b/source/Items/ItemHandler.cpp
index 66d36e1a6..c0de4a6ec 100644
--- a/source/Items/ItemHandler.cpp
+++ b/source/Items/ItemHandler.cpp
@@ -8,6 +8,7 @@
// Handlers:
#include "ItemBed.h"
+#include "ItemBow.h"
#include "ItemBrewingStand.h"
#include "ItemBucket.h"
#include "ItemCauldron.h"
@@ -47,18 +48,24 @@ cItemHandler * cItemHandler::m_ItemHandler[2268];
-cItemHandler *cItemHandler::GetItemHandler(int a_ItemType)
+cItemHandler * cItemHandler::GetItemHandler(int a_ItemType)
{
- if(a_ItemType < 0) a_ItemType = 0;
+ if (a_ItemType < 0)
+ {
+ ASSERT(!"Bad item type");
+ a_ItemType = 0;
+ }
- if(!m_HandlerInitialized)
- { //We have to initialize
+ if (!m_HandlerInitialized)
+ {
+ // We need to initialize
memset(m_ItemHandler, 0, sizeof(m_ItemHandler));
m_HandlerInitialized = true;
}
- if(m_ItemHandler[a_ItemType])
- return m_ItemHandler[a_ItemType];
- m_ItemHandler[a_ItemType] = CreateItemHandler(a_ItemType);
+ if (m_ItemHandler[a_ItemType] == NULL)
+ {
+ m_ItemHandler[a_ItemType] = CreateItemHandler(a_ItemType);
+ }
return m_ItemHandler[a_ItemType];
}
@@ -77,6 +84,7 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
+ case E_ITEM_BOW: return new cItemBowHandler;
case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType);
case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType);
case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
diff --git a/source/Items/ItemHandler.h b/source/Items/ItemHandler.h
index 44d43e8f7..e39bb054b 100644
--- a/source/Items/ItemHandler.h
+++ b/source/Items/ItemHandler.h
@@ -21,8 +21,11 @@ class cItemHandler
public:
cItemHandler(int a_ItemType);
- /// Called when the player tries to use the item. Return false to make the item unusable. DEFAULT: False
- virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); //eg for fishing or hoes
+ /// Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir);
+
+ /// Called when the client sends the SHOOT status in the lclk packet
+ virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) {}
/// Called while the player diggs a block using this item
virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace);
@@ -88,7 +91,7 @@ protected:
int m_ItemType;
static cItemHandler *CreateItemHandler(int m_ItemType);
- static cItemHandler *m_ItemHandler[2268];
+ static cItemHandler * m_ItemHandler[E_ITEM_LAST + 1];
static bool m_HandlerInitialized; //used to detect if the itemhandlers are initialized
};