1
0

Merge pull request #731 from mc-server/ballisticmissiles

Ballistic firework missiles
This commit is contained in:
Tiger Wang 2014-03-09 21:43:15 +00:00
commit 1985a9c33e
21 changed files with 703 additions and 178 deletions

View File

@ -39,6 +39,7 @@
# #
# Need to list each of the four log types, otherwise all logs would get converted into apple planks (^0) # Need to list each of the four log types, otherwise all logs would get converted into apple planks (^0)
ApplePlanks, 4 = AppleLog, * ApplePlanks, 4 = AppleLog, *
ConiferPlanks, 4 = ConiferLog, * ConiferPlanks, 4 = ConiferLog, *
BirchPlanks, 4 = BirchLog, * BirchPlanks, 4 = BirchLog, *
@ -434,6 +435,55 @@ GoldNugget, 9 = GoldIngot, *
EnchantmentTable = Obsidian, 1:3, 2:3, 3:3, 2:2 | Diamond, 1:2, 3:2 | Book, 2:1 EnchantmentTable = Obsidian, 1:3, 2:3, 3:3, 2:2 | Diamond, 1:2, 3:2 | Book, 2:1
#******************************************************#
# Fireworks & Co.
# (Best not to add non-vanilla items to this as it will cause internal firework data handling code to log warnings)
# Ballistic firework rockets - plain and with firework star, all with 1 - 3 gunpowder
FireworkRocket = Paper, * | Gunpowder, *
FireworkRocket = Paper, * | Gunpowder, * | Gunpowder, *
FireworkRocket = Paper, * | Gunpowder, * | Gunpowder, * | Gunpowder, *
FireworkRocket = FireworkStar, * | Paper, * | Gunpowder, *
FireworkRocket = FireworkStar, * | Paper, * | Gunpowder, * | Gunpowder, *
FireworkRocket = FireworkStar, * | Paper, * | Gunpowder, * | Gunpowder, * | Gunpowder, *
# Radioactive firework stars
# Plain powder and dye
FireworkStar = Gunpowder, * | Dye ^-1, *
# Powder and effect, with effect combining
FireworkStar = Gunpowder, * | Dye ^-1, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Glowdust, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Glowdust, * | Diamond, *
# Powder and shape (no shape combining possible)
FireworkStar = Gunpowder, * | Dye ^-1, * | Firecharge, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Goldnugget, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Feather, *
FireworkStar = Gunpowder, * | Dye ^-1, * | SkeletonHead ^-1, *
# Power and shape (no shape combining possible), combined with effect
FireworkStar = Gunpowder, * | Dye ^-1, * | Firecharge, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Firecharge, * | Glowdust, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Goldnugget, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Goldnugget, * | Glowdust, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Feather, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Feather, * | Glowdust, *
FireworkStar = Gunpowder, * | Dye ^-1, * | SkeletonHead ^-1, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | SkeletonHead ^-1, * | Glowdust, *
# Power and shape (no shape combining possible), combined with effect (with effect combining)
FireworkStar = Gunpowder, * | Dye ^-1, * | Firecharge, * | Glowdust, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Goldnugget, * | Glowdust, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | Feather, * | Glowdust, * | Diamond, *
FireworkStar = Gunpowder, * | Dye ^-1, * | SkeletonHead ^-1, * | Glowdust, * | Diamond, *
# Star fade colour-change
FireworkStar = FireworkStar, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, *
FireworkStar = FireworkStar, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, * | Dye ^-1, *

View File

@ -52,6 +52,9 @@
/** Maximum number of explosions to send this tick, server will start dropping if exceeded */ /** Maximum number of explosions to send this tick, server will start dropping if exceeded */
#define MAX_EXPLOSIONS_PER_TICK 20 #define MAX_EXPLOSIONS_PER_TICK 20
/** Maximum number of block change interactions a player can perform per tick - exceeding this causes a kick */
#define MAX_BLOCK_CHANGE_INTERACTIONS 20
/** How many ticks before the socket is closed after the client is destroyed (#31) */ /** How many ticks before the socket is closed after the client is destroyed (#31) */
static const int TICKS_BEFORE_CLOSE = 20; static const int TICKS_BEFORE_CLOSE = 20;
@ -700,6 +703,14 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status
); );
m_NumBlockChangeInteractionsThisTick++;
if (!CheckBlockInteractionsRate())
{
Kick("Too many blocks were destroyed per unit time - hacked client?");
return;
}
cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
if (PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status)) if (PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status))
{ {
@ -708,12 +719,6 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
return; return;
} }
if (!CheckBlockInteractionsRate())
{
// Too many interactions per second, simply ignore. Probably a hacked client, so don't even send bak the block
return;
}
switch (a_Status) switch (a_Status)
{ {
case DIG_STATUS_DROP_HELD: // Drop held item case DIG_STATUS_DROP_HELD: // Drop held item
@ -944,7 +949,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType); cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType);
BlockHandler->OnCancelRightClick(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); BlockHandler->OnCancelRightClick(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
if (a_BlockFace > -1) if (a_BlockFace != BLOCK_FACE_NONE)
{ {
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
@ -955,7 +960,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
if (!CheckBlockInteractionsRate()) if (!CheckBlockInteractionsRate())
{ {
LOGD("Too many block interactions, aborting placement"); Kick("Too many blocks were placed/interacted with per unit time - hacked client?");
return; return;
} }
@ -1634,28 +1639,12 @@ bool cClientHandle::CheckBlockInteractionsRate(void)
{ {
ASSERT(m_Player != NULL); ASSERT(m_Player != NULL);
ASSERT(m_Player->GetWorld() != NULL); ASSERT(m_Player->GetWorld() != NULL);
/*
// TODO: _X 2012_11_01: This needs a total re-thinking and rewriting if (m_NumBlockChangeInteractionsThisTick > MAX_BLOCK_CHANGE_INTERACTIONS)
int LastActionCnt = m_Player->GetLastBlockActionCnt();
if ((m_Player->GetWorld()->GetTime() - m_Player->GetLastBlockActionTime()) < 0.1)
{ {
// Limit the number of block interactions per tick
m_Player->SetLastBlockActionTime(); //Player tried to interact with a block. Reset last block interation time.
m_Player->SetLastBlockActionCnt(LastActionCnt + 1);
if (m_Player->GetLastBlockActionCnt() > MAXBLOCKCHANGEINTERACTIONS)
{
// Kick if more than MAXBLOCKCHANGEINTERACTIONS per tick
LOGWARN("Player %s tried to interact with a block too quickly! (could indicate bot) Was Kicked.", m_Username.c_str());
Kick("You're a baaaaaad boy!");
return false; return false;
} }
}
else
{
m_Player->SetLastBlockActionCnt(0); // Reset count
m_Player->SetLastBlockActionTime(); // Player tried to interact with a block. Reset last block interation time.
}
*/
return true; return true;
} }
@ -1728,8 +1717,9 @@ void cClientHandle::Tick(float a_Dt)
} }
} }
// Reset explosion counter: // Reset explosion & block change counters:
m_NumExplosionsThisTick = 0; m_NumExplosionsThisTick = 0;
m_NumBlockChangeInteractionsThisTick = 0;
} }

View File

@ -46,7 +46,6 @@ class cClientHandle : // tolua_export
public cSocketThreads::cCallback public cSocketThreads::cCallback
{ // tolua_export { // tolua_export
public: public:
static const int MAXBLOCKCHANGEINTERACTIONS = 20; // 5 didn't help, 10 still doesn't work in Creative, 20 seems to have done the trick
#if defined(ANDROID_NDK) #if defined(ANDROID_NDK)
static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini) static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini)
@ -322,6 +321,9 @@ private:
/** Number of explosions sent this tick */ /** Number of explosions sent this tick */
int m_NumExplosionsThisTick; int m_NumExplosionsThisTick;
/** Number of place or break interactions this tick */
int m_NumBlockChangeInteractionsThisTick;
static int s_ClientCount; static int s_ClientCount;
int m_UniqueID; int m_UniqueID;

View File

@ -1,4 +1,4 @@

// CraftingRecipes.cpp // CraftingRecipes.cpp
// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes // Interfaces to the cCraftingRecipes class representing the storage of crafting recipes
@ -762,9 +762,94 @@ cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_Crafti
Recipe->m_Ingredients.push_back(*itrS); Recipe->m_Ingredients.push_back(*itrS);
} }
Recipe->m_Ingredients.insert(Recipe->m_Ingredients.end(), MatchedSlots.begin(), MatchedSlots.end()); Recipe->m_Ingredients.insert(Recipe->m_Ingredients.end(), MatchedSlots.begin(), MatchedSlots.end());
// We use Recipe instead of a_Recipe because we want the wildcard ingredients' slot numbers as well, which was just added previously
HandleFireworks(a_CraftingGrid, Recipe.get(), a_GridStride, a_OffsetX, a_OffsetY);
return Recipe.release(); return Recipe.release();
} }
void cCraftingRecipes::HandleFireworks(const cItem * a_CraftingGrid, cCraftingRecipes::cRecipe * a_Recipe, int a_GridStride, int a_OffsetX, int a_OffsetY)
{
// TODO: add support for more than one dye in the recipe
// A manual and temporary solution (listing everything) is in crafting.txt for fade colours, but a programmatic solutions needs to be done for everything else
if (a_Recipe->m_Result.m_ItemType == E_ITEM_FIREWORK_ROCKET)
{
for (cRecipeSlots::const_iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr)
{
switch (itr->m_Item.m_ItemType)
{
case E_ITEM_FIREWORK_STAR:
{
// Result was a rocket, found a star - copy star data to rocket data
int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
a_Recipe->m_Result.m_FireworkItem.CopyFrom(a_CraftingGrid[GridID].m_FireworkItem);
break;
}
case E_ITEM_GUNPOWDER:
{
// Gunpowder - increase flight time
a_Recipe->m_Result.m_FireworkItem.m_FlightTimeInTicks += 20;
break;
}
case E_ITEM_PAPER: break;
default: LOG("Unexpected item in firework rocket a_Recipe, was the crafting file fireworks section changed?"); break;
}
}
}
else if (a_Recipe->m_Result.m_ItemType == E_ITEM_FIREWORK_STAR)
{
std::vector<int> DyeColours;
bool FoundStar = false;
for (cRecipeSlots::const_iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr)
{
switch (itr->m_Item.m_ItemType)
{
case E_ITEM_FIREWORK_STAR:
{
// Result was star, found another star - probably adding fade colours, but copy data over anyhow
FoundStar = true;
int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
a_Recipe->m_Result.m_FireworkItem.CopyFrom(a_CraftingGrid[GridID].m_FireworkItem);
break;
}
case E_ITEM_DYE:
{
int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
DyeColours.push_back(cFireworkItem::GetVanillaColourCodeFromDye(a_CraftingGrid[GridID].m_ItemDamage));
break;
}
case E_ITEM_GUNPOWDER: break;
case E_ITEM_DIAMOND: a_Recipe->m_Result.m_FireworkItem.m_HasTrail = true; break;
case E_ITEM_GLOWSTONE_DUST: a_Recipe->m_Result.m_FireworkItem.m_HasFlicker = true; break;
case E_ITEM_FIRE_CHARGE: a_Recipe->m_Result.m_FireworkItem.m_Type = 1; break;
case E_ITEM_GOLD_NUGGET: a_Recipe->m_Result.m_FireworkItem.m_Type = 2; break;
case E_ITEM_FEATHER: a_Recipe->m_Result.m_FireworkItem.m_Type = 4; break;
case E_ITEM_HEAD: a_Recipe->m_Result.m_FireworkItem.m_Type = 3; break;
default: LOG("Unexpected item in firework star a_Recipe, was the crafting file fireworks section changed?"); break; // ermahgerd BARD ardmins
}
}
if (FoundStar && (!DyeColours.empty()))
{
// Found a star and a dye? Fade colours.
a_Recipe->m_Result.m_FireworkItem.m_FadeColours = DyeColours;
}
else if (!DyeColours.empty())
{
// Only dye? Normal colours.
a_Recipe->m_Result.m_FireworkItem.m_Colours = DyeColours;
}
}
}

View File

@ -165,6 +165,9 @@ protected:
/// Checks if the grid matches the specified recipe, offset by the specified offsets. Returns a matched cRecipe * if so, or NULL if not matching. Caller must delete the return value! /// Checks if the grid matches the specified recipe, offset by the specified offsets. Returns a matched cRecipe * if so, or NULL if not matching. Caller must delete the return value!
cRecipe * MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY); cRecipe * MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY);
/** Searches for anything firework related, and does the data setting if appropriate */
void HandleFireworks(const cItem * a_CraftingGrid, cCraftingRecipes::cRecipe * a_Recipe, int a_GridStride, int a_OffsetX, int a_OffsetY);
} ; } ;

View File

@ -10,18 +10,11 @@
#include "../BlockEntities/BlockEntity.h" #include "../BlockEntities/BlockEntity.h"
#include "../GroupManager.h" #include "../GroupManager.h"
#include "../Group.h" #include "../Group.h"
#include "../ChatColor.h"
#include "../Item.h"
#include "../Tracer.h"
#include "../Root.h" #include "../Root.h"
#include "../OSSupport/Timer.h" #include "../OSSupport/Timer.h"
#include "../MersenneTwister.h"
#include "../Chunk.h" #include "../Chunk.h"
#include "../Items/ItemHandler.h" #include "../Items/ItemHandler.h"
#include "../Vector3d.h"
#include "../Vector3f.h"
#include "inifile/iniFile.h" #include "inifile/iniFile.h"
#include "json/json.h" #include "json/json.h"
@ -45,10 +38,7 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
, m_Inventory(*this) , m_Inventory(*this)
, m_CurrentWindow(NULL) , m_CurrentWindow(NULL)
, m_InventoryWindow(NULL) , m_InventoryWindow(NULL)
, m_TimeLastPickupCheck(0.f)
, m_Color('-') , m_Color('-')
, m_LastBlockActionTime(0)
, m_LastBlockActionCnt(0)
, m_GameMode(eGameMode_NotSet) , m_GameMode(eGameMode_NotSet)
, m_IP("") , m_IP("")
, m_ClientHandle(a_Client) , m_ClientHandle(a_Client)
@ -86,7 +76,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
m_LastPlayerListTime = t1.GetNowTime(); m_LastPlayerListTime = t1.GetNowTime();
m_TimeLastTeleportPacket = 0; m_TimeLastTeleportPacket = 0;
m_TimeLastPickupCheck = 0;
m_PlayerName = a_PlayerName; m_PlayerName = a_PlayerName;
m_bDirtyPosition = true; // So chunks are streamed to player at spawn m_bDirtyPosition = true; // So chunks are streamed to player at spawn
@ -1047,27 +1036,6 @@ void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse)
void cPlayer::SetLastBlockActionTime()
{
if (m_World != NULL)
{
m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f;
}
}
void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt )
{
m_LastBlockActionCnt = a_LastBlockActionCnt;
}
void cPlayer::SetGameMode(eGameMode a_GameMode) void cPlayer::SetGameMode(eGameMode a_GameMode)
{ {
if ((a_GameMode < gmMin) || (a_GameMode >= gmMax)) if ((a_GameMode < gmMin) || (a_GameMode >= gmMax))

View File

@ -50,7 +50,7 @@ public:
/// Returns the curently equipped weapon; empty item if none /// Returns the curently equipped weapon; empty item if none
virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); } virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); }
/// Returns the currently equipped helmet; empty item if nonte /// Returns the currently equipped helmet; empty item if none
virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); } virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); }
/// Returns the currently equipped chestplate; empty item if none /// Returns the currently equipped chestplate; empty item if none
@ -166,11 +166,6 @@ public:
void SetIP(const AString & a_IP); void SetIP(const AString & a_IP);
float GetLastBlockActionTime() { return m_LastBlockActionTime; }
int GetLastBlockActionCnt() { return m_LastBlockActionCnt; }
void SetLastBlockActionCnt( int );
void SetLastBlockActionTime();
// Sets the current gamemode, doesn't check validity, doesn't send update packets to client // Sets the current gamemode, doesn't check validity, doesn't send update packets to client
void LoginSetGameMode(eGameMode a_GameMode); void LoginSetGameMode(eGameMode a_GameMode);
@ -416,12 +411,8 @@ protected:
cWindow * m_CurrentWindow; cWindow * m_CurrentWindow;
cWindow * m_InventoryWindow; cWindow * m_InventoryWindow;
float m_TimeLastPickupCheck;
char m_Color; char m_Color;
float m_LastBlockActionTime;
int m_LastBlockActionCnt;
eGameMode m_GameMode; eGameMode m_GameMode;
AString m_IP; AString m_IP;

View File

@ -214,7 +214,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 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; Vector3d Speed;
if (a_Speed != NULL) if (a_Speed != NULL)
@ -231,8 +231,15 @@ cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator,
case pkGhastFireball: return new cGhastFireballEntity (a_Creator, a_X, a_Y, a_Z, Speed); 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 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 pkExpBottle: return new cExpBottleEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkFirework: return new cFireworkEntity (a_Creator, a_X, a_Y, a_Z ); case pkFirework:
// TODO: the rest {
if (a_Item.m_FireworkItem.m_Colours.empty())
{
return NULL;
}
return new cFireworkEntity(a_Creator, a_X, a_Y, a_Z, a_Item);
}
} }
LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind); LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind);
@ -276,6 +283,7 @@ AString cProjectileEntity::GetMCAClassName(void) const
case pkExpBottle: return "ThrownExpBottle"; case pkExpBottle: return "ThrownExpBottle";
case pkSplashPotion: return "ThrownPotion"; case pkSplashPotion: return "ThrownPotion";
case pkWitherSkull: return "WitherSkull"; case pkWitherSkull: return "WitherSkull";
case pkFirework: return "Firework";
case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this? case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this?
} }
ASSERT(!"Unhandled projectile entity kind!"); ASSERT(!"Unhandled projectile entity kind!");
@ -693,8 +701,10 @@ void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_H
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFireworkEntity : // cFireworkEntity :
cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z) : cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item) :
super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25),
m_ExplodeTimer(0),
m_FireworkItem(a_Item)
{ {
} }
@ -702,30 +712,20 @@ super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
void cFireworkEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
if ((a_HitFace != BLOCK_FACE_BOTTOM) && (a_HitFace != BLOCK_FACE_NONE))
{
return;
}
SetSpeed(0, 0, 0);
SetPosition(GetPosX(), GetPosY() - 0.5, GetPosZ());
m_IsInGround = true;
BroadcastMovementUpdate();
}
void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{ {
int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
int PosY = POSY_TOINT;
if ((PosY < 0) || (PosY >= cChunkDef::Height))
{
goto setspeed;
}
if (m_IsInGround) if (m_IsInGround)
{ {
if (a_Chunk.GetBlock((int)GetPosX(), (int)GetPosY() + 1, (int)GetPosZ()) == E_BLOCK_AIR) if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) == E_BLOCK_AIR)
{ {
m_IsInGround = false; m_IsInGround = false;
} }
@ -734,28 +734,35 @@ void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
return; return;
} }
} }
else
Vector3d PerTickSpeed = GetSpeed() / 20;
Vector3d Pos = GetPosition();
// Trace the tick's worth of movement as a line:
Vector3d NextPos = Pos + PerTickSpeed;
cProjectileTracerCallback TracerCallback(this);
if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos))
{ {
// Something has been hit, abort all other processing if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) != E_BLOCK_AIR)
{
OnHitSolidBlock(GetPosition(), BLOCK_FACE_YM);
return; return;
} }
// The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff }
// Update the position: setspeed:
SetPosition(NextPos); AddSpeedY(1);
AddPosition(GetSpeed() * (a_Dt / 1000));
}
// Add slowdown and gravity effect to the speed:
Vector3d NewSpeed(GetSpeed());
NewSpeed.y += 2;
NewSpeed *= TracerCallback.GetSlowdownCoeff();
SetSpeed(NewSpeed); void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
if (m_ExplodeTimer == m_FireworkItem.m_FireworkItem.m_FlightTimeInTicks)
{
m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_FIREWORK_EXPLODE);
Destroy();
}
m_ExplodeTimer++;
} }

View File

@ -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, 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); 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 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 /// 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); virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace);
@ -305,13 +305,19 @@ public:
CLASS_PROTODEF(cFireworkEntity); CLASS_PROTODEF(cFireworkEntity);
cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z); cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item);
const cItem & GetItem(void) const { return m_FireworkItem; }
protected: protected:
// cProjectileEntity overrides: // cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
private:
int m_ExplodeTimer;
cItem m_FireworkItem;
// tolua_begin // tolua_begin

View File

@ -1,4 +1,4 @@

#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Item.h" #include "Item.h"
@ -139,6 +139,16 @@ void cItem::GetJson(Json::Value & a_OutValue) const
{ {
a_OutValue["Lore"] = m_Lore; a_OutValue["Lore"] = m_Lore;
} }
if ((m_ItemType == E_ITEM_FIREWORK_ROCKET) || (m_ItemType == E_ITEM_FIREWORK_STAR))
{
a_OutValue["Flicker"] = m_FireworkItem.m_HasFlicker;
a_OutValue["Trail"] = m_FireworkItem.m_HasTrail;
a_OutValue["Type"] = m_FireworkItem.m_Type;
a_OutValue["FlightTimeInTicks"] = m_FireworkItem.m_FlightTimeInTicks;
a_OutValue["Colours"] = m_FireworkItem.ColoursToString(m_FireworkItem);
a_OutValue["FadeColours"] = m_FireworkItem.FadeColoursToString(m_FireworkItem);
}
} }
} }
@ -157,6 +167,16 @@ void cItem::FromJson(const Json::Value & a_Value)
m_Enchantments.AddFromString(a_Value.get("ench", "").asString()); m_Enchantments.AddFromString(a_Value.get("ench", "").asString());
m_CustomName = a_Value.get("Name", "").asString(); m_CustomName = a_Value.get("Name", "").asString();
m_Lore = a_Value.get("Lore", "").asString(); m_Lore = a_Value.get("Lore", "").asString();
if ((m_ItemType == E_ITEM_FIREWORK_ROCKET) || (m_ItemType == E_ITEM_FIREWORK_STAR))
{
m_FireworkItem.m_HasFlicker = a_Value.get("Flicker", false).asBool();
m_FireworkItem.m_HasTrail = a_Value.get("Trail", false).asBool();
m_FireworkItem.m_Type = (NIBBLETYPE)a_Value.get("Type", 0).asInt();
m_FireworkItem.m_FlightTimeInTicks = (short)a_Value.get("FlightTimeInTicks", 0).asInt();
m_FireworkItem.ColoursFromString(a_Value.get("Colours", "").asString(), m_FireworkItem);
m_FireworkItem.FadeColoursFromString(a_Value.get("FadeColours", "").asString(), m_FireworkItem);
}
} }
} }

View File

@ -11,6 +11,7 @@
#include "Defines.h" #include "Defines.h"
#include "Enchantments.h" #include "Enchantments.h"
#include "WorldStorage/FireworksSerializer.h"
@ -38,7 +39,8 @@ public:
m_ItemCount(0), m_ItemCount(0),
m_ItemDamage(0), m_ItemDamage(0),
m_CustomName(""), m_CustomName(""),
m_Lore("") m_Lore(""),
m_FireworkItem()
{ {
} }
@ -57,7 +59,8 @@ public:
m_ItemDamage (a_ItemDamage), m_ItemDamage (a_ItemDamage),
m_Enchantments(a_Enchantments), m_Enchantments(a_Enchantments),
m_CustomName (a_CustomName), m_CustomName (a_CustomName),
m_Lore (a_Lore) m_Lore (a_Lore),
m_FireworkItem()
{ {
if (!IsValidItem(m_ItemType)) if (!IsValidItem(m_ItemType))
{ {
@ -77,7 +80,8 @@ public:
m_ItemDamage (a_CopyFrom.m_ItemDamage), m_ItemDamage (a_CopyFrom.m_ItemDamage),
m_Enchantments(a_CopyFrom.m_Enchantments), m_Enchantments(a_CopyFrom.m_Enchantments),
m_CustomName (a_CopyFrom.m_CustomName), m_CustomName (a_CopyFrom.m_CustomName),
m_Lore (a_CopyFrom.m_Lore) m_Lore (a_CopyFrom.m_Lore),
m_FireworkItem(a_CopyFrom.m_FireworkItem)
{ {
} }
@ -90,6 +94,7 @@ public:
m_Enchantments.Clear(); m_Enchantments.Clear();
m_CustomName = ""; m_CustomName = "";
m_Lore = ""; m_Lore = "";
m_FireworkItem.EmptyData();
} }
@ -115,7 +120,8 @@ public:
(m_ItemDamage == a_Item.m_ItemDamage) && (m_ItemDamage == a_Item.m_ItemDamage) &&
(m_Enchantments == a_Item.m_Enchantments) && (m_Enchantments == a_Item.m_Enchantments) &&
(m_CustomName == a_Item.m_CustomName) && (m_CustomName == a_Item.m_CustomName) &&
(m_Lore == a_Item.m_Lore) (m_Lore == a_Item.m_Lore) &&
m_FireworkItem.IsEqualTo(a_Item.m_FireworkItem)
); );
} }
@ -177,6 +183,8 @@ public:
cEnchantments m_Enchantments; cEnchantments m_Enchantments;
AString m_CustomName; AString m_CustomName;
AString m_Lore; AString m_Lore;
cFireworkItem m_FireworkItem;
}; };
// tolua_end // tolua_end

View File

@ -35,7 +35,7 @@ public:
Vector3d Pos = a_Player->GetThrowStartPos(); Vector3d Pos = a_Player->GetThrowStartPos();
Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff; Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff;
a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &Speed); a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, a_Player->GetEquippedItem(), &Speed);
return true; return true;
} }
@ -127,13 +127,13 @@ public:
return false; return false;
} }
a_World->CreateProjectile(a_BlockX + 0.5, a_BlockY + 1, a_BlockZ + 0.5, m_ProjectileKind, a_Player, a_Player->GetEquippedItem());
if (!a_Player->IsGameModeCreative()) if (!a_Player->IsGameModeCreative())
{ {
a_Player->GetInventory().RemoveOneEquippedItem(); a_Player->GetInventory().RemoveOneEquippedItem();
} }
a_World->CreateProjectile(a_BlockX + 0.5, a_BlockY + 1, a_BlockZ + 0.5, m_ProjectileKind, a_Player, 0);
return true; return true;
} }

View File

@ -68,17 +68,28 @@ void cSheep::OnRightClicked(cPlayer & a_Player)
void cSheep::Tick(float a_Dt, cChunk & a_Chunk) void cSheep::Tick(float a_Dt, cChunk & a_Chunk)
{ {
// The sheep should not move when he's eating so only handle the physics. super::Tick(a_Dt, a_Chunk);
int PosX = POSX_TOINT;
int PosY = POSY_TOINT - 1;
int PosZ = POSZ_TOINT;
if ((PosY <= 0) || (PosY > cChunkDef::Height))
{
return;
}
if (m_TimeToStopEating > 0) if (m_TimeToStopEating > 0)
{ {
HandlePhysics(a_Dt, a_Chunk); m_bMovingToDestination = false; // The sheep should not move when he's eating
m_TimeToStopEating--; m_TimeToStopEating--;
if (m_TimeToStopEating == 0) if (m_TimeToStopEating == 0)
{ {
if (m_World->GetBlock((int) GetPosX(), (int) GetPosY() - 1, (int) GetPosZ()) == E_BLOCK_GRASS) if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) // Make sure grass hasn't been destroyed in the meantime
{ {
// The sheep ate the grass so we change it to dirt. // The sheep ate the grass so we change it to dirt
m_World->SetBlock((int) GetPosX(), (int) GetPosY() - 1, (int) GetPosZ(), E_BLOCK_DIRT, 0); m_World->SetBlock(PosX, PosY, PosZ, E_BLOCK_DIRT, 0);
GetWorld()->BroadcastSoundParticleEffect(2001, PosX, PosY, PosX, E_BLOCK_GRASS);
m_IsSheared = false; m_IsSheared = false;
m_World->BroadcastEntityMetadata(*this); m_World->BroadcastEntityMetadata(*this);
} }
@ -86,12 +97,11 @@ void cSheep::Tick(float a_Dt, cChunk & a_Chunk)
} }
else else
{ {
super::Tick(a_Dt, a_Chunk);
if (m_World->GetTickRandomNumber(600) == 1) if (m_World->GetTickRandomNumber(600) == 1)
{ {
if (m_World->GetBlock((int) GetPosX(), (int) GetPosY() - 1, (int) GetPosZ()) == E_BLOCK_GRASS) if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS)
{ {
m_World->BroadcastEntityStatus(*this, 10); m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_SHEEP_EATING);
m_TimeToStopEating = 40; m_TimeToStopEating = 40;
} }
} }

View File

@ -2079,17 +2079,20 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
// Load enchantments and custom display names from the NBT data: // Load enchantments and custom display names from the NBT data:
for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag))
{ {
if ( AString TagName = NBT.GetName(tag);
(NBT.GetType(tag) == TAG_List) && switch (NBT.GetType(tag))
( {
(NBT.GetName(tag) == "ench") || case TAG_List:
(NBT.GetName(tag) == "StoredEnchantments") {
) if ((TagName == "ench") || (TagName == "StoredEnchantments")) // Enchantments tags
)
{ {
EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, NBT, tag); EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, NBT, tag);
} }
else if ((NBT.GetType(tag) == TAG_Compound) && (NBT.GetName(tag) == "display")) // Custom name and lore tag break;
}
case TAG_Compound:
{
if (TagName == "display") // Custom name and lore tag
{ {
for (int displaytag = NBT.GetFirstChild(tag); displaytag >= 0; displaytag = NBT.GetNextSibling(displaytag)) for (int displaytag = NBT.GetFirstChild(tag); displaytag >= 0; displaytag = NBT.GetNextSibling(displaytag))
{ {
@ -2110,6 +2113,14 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
} }
} }
} }
else if ((TagName == "Fireworks") || (TagName == "Explosion"))
{
cFireworkItem::ParseFromNBT(a_Item.m_FireworkItem, NBT, tag, (ENUM_ITEM_ID)a_Item.m_ItemType);
}
break;
}
default: LOGD("Unimplemented NBT data when parsing!"); break;
}
} }
} }
@ -2272,7 +2283,7 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item)
WriteByte (a_Item.m_ItemCount); WriteByte (a_Item.m_ItemCount);
WriteShort(a_Item.m_ItemDamage); WriteShort(a_Item.m_ItemDamage);
if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty()) if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (a_Item.m_ItemType != E_ITEM_FIREWORK_ROCKET) && (a_Item.m_ItemType != E_ITEM_FIREWORK_STAR))
{ {
WriteShort(-1); WriteShort(-1);
return; return;
@ -2311,6 +2322,10 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item)
} }
Writer.EndCompound(); Writer.EndCompound();
} }
if ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR))
{
cFireworkItem::WriteToNBTCompound(a_Item.m_FireworkItem, Writer, (ENUM_ITEM_ID)a_Item.m_ItemType);
}
Writer.Finish(); Writer.Finish();
AString Compressed; AString Compressed;
CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
@ -2487,10 +2502,22 @@ void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity)
} }
case cEntity::etProjectile: case cEntity::etProjectile:
{ {
if (((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow) cProjectileEntity & Projectile = (cProjectileEntity &)a_Entity;
switch (Projectile.GetProjectileKind())
{
case cProjectileEntity::pkArrow:
{ {
WriteByte(0x10); WriteByte(0x10);
WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0);
break;
}
case cProjectileEntity::pkFirework:
{
WriteByte(0xA8);
WriteItem(((const cFireworkEntity &)a_Entity).GetItem());
break;
}
default: break;
} }
break; break;
} }

View File

@ -2980,9 +2980,9 @@ 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 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_Speed); cProjectileEntity * Projectile = cProjectileEntity::Create(a_Kind, a_Creator, a_PosX, a_PosY, a_PosZ, a_Item, a_Speed);
if (Projectile == NULL) if (Projectile == NULL)
{ {
return -1; return -1;
@ -3007,16 +3007,16 @@ void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Resul
{ {
size_t LastSpace = a_Text.find_last_of(" "); // Find the position of the last space size_t LastSpace = a_Text.find_last_of(" "); // Find the position of the last space
std::string LastWord = a_Text.substr(LastSpace + 1, a_Text.length()); //Find the last word AString LastWord = a_Text.substr(LastSpace + 1, a_Text.length()); // Find the last word
std::string PlayerName ((*itr)->GetName()); AString PlayerName ((*itr)->GetName());
std::size_t Found = PlayerName.find(LastWord); //Try to find last word in playername size_t Found = PlayerName.find(LastWord); // Try to find last word in playername
if (Found!=0) if (Found == AString::npos)
{ {
continue; // No match continue; // No match
} }
a_Results.push_back((*itr)->GetName()); //Match! a_Results.push_back(PlayerName); // Match!
} }
} }

View File

@ -708,7 +708,7 @@ public:
int SpawnMobFinalize(cMonster* a_Monster); int SpawnMobFinalize(cMonster* a_Monster);
/** Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise */ /** Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise */
int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, 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! */ /** 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)); } int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); }

View File

@ -309,7 +309,7 @@ protected:
eTagType m_ItemType; // for TAG_List, the element type eTagType m_ItemType; // for TAG_List, the element type
} ; } ;
static const int MAX_STACK = 50; // Highliy doubtful that an NBT would be constructed this many levels deep static const int MAX_STACK = 50; // Highly doubtful that an NBT would be constructed this many levels deep
// These two fields emulate a stack. A raw array is used due to speed issues - no reallocations are allowed. // These two fields emulate a stack. A raw array is used due to speed issues - no reallocations are allowed.
sParent m_Stack[MAX_STACK]; sParent m_Stack[MAX_STACK];

View File

@ -0,0 +1,252 @@

#include "Globals.h"
#include "FireworksSerializer.h"
#include "WorldStorage/FastNBT.h"
void cFireworkItem::WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFastNBTWriter & a_Writer, const ENUM_ITEM_ID a_Type)
{
switch (a_Type)
{
case E_ITEM_FIREWORK_ROCKET:
{
a_Writer.BeginCompound("Fireworks");
a_Writer.AddByte("Flight", a_FireworkItem.m_FlightTimeInTicks / 20);
a_Writer.BeginList("Explosions", TAG_Compound);
a_Writer.BeginCompound("");
a_Writer.AddByte("Flicker", a_FireworkItem.m_HasFlicker);
a_Writer.AddByte("Trail", a_FireworkItem.m_HasTrail);
a_Writer.AddByte("Type", a_FireworkItem.m_Type);
a_Writer.AddIntArray("Colors", a_FireworkItem.m_Colours.data(), a_FireworkItem.m_Colours.size());
a_Writer.AddIntArray("FadeColors", a_FireworkItem.m_FadeColours.data(), a_FireworkItem.m_FadeColours.size());
a_Writer.EndCompound();
a_Writer.EndList();
a_Writer.EndCompound();
break;
}
case E_ITEM_FIREWORK_STAR:
{
a_Writer.BeginCompound("Explosion");
a_Writer.AddByte("Flicker", a_FireworkItem.m_HasFlicker);
a_Writer.AddByte("Trail", a_FireworkItem.m_HasTrail);
a_Writer.AddByte("Type", a_FireworkItem.m_Type);
if (!a_FireworkItem.m_Colours.empty())
{
a_Writer.AddIntArray("Colors", a_FireworkItem.m_Colours.data(), a_FireworkItem.m_Colours.size());
}
if (!a_FireworkItem.m_FadeColours.empty())
{
a_Writer.AddIntArray("FadeColors", a_FireworkItem.m_FadeColours.data(), a_FireworkItem.m_FadeColours.size());
}
a_Writer.EndCompound();
break;
}
default: ASSERT(!"Unhandled firework item!"); break;
}
}
void cFireworkItem::ParseFromNBT(cFireworkItem & a_FireworkItem, const cParsedNBT & a_NBT, int a_TagIdx, const ENUM_ITEM_ID a_Type)
{
if (a_TagIdx < 0)
{
return;
}
switch (a_Type)
{
case E_ITEM_FIREWORK_STAR:
{
for (int explosiontag = a_NBT.GetFirstChild(a_TagIdx); explosiontag >= 0; explosiontag = a_NBT.GetNextSibling(explosiontag))
{
eTagType TagType = a_NBT.GetType(explosiontag);
if (TagType == TAG_Byte) // Custon name tag
{
AString ExplosionName = a_NBT.GetName(explosiontag);
if (ExplosionName == "Flicker")
{
a_FireworkItem.m_HasFlicker = (a_NBT.GetByte(explosiontag) == 1);
}
else if (ExplosionName == "Trail")
{
a_FireworkItem.m_HasTrail = (a_NBT.GetByte(explosiontag) == 1);
}
else if (ExplosionName == "Type")
{
a_FireworkItem.m_Type = a_NBT.GetByte(explosiontag);
}
}
else if (TagType == TAG_IntArray)
{
AString ExplosionName = a_NBT.GetName(explosiontag);
if (ExplosionName == "Colors")
{
// Divide by four as data length returned in bytes
int DataLength = a_NBT.GetDataLength(explosiontag) / 4;
if (DataLength == 0)
{
continue;
}
const int * ColourData = (const int *)(a_NBT.GetData(explosiontag));
for (int i = 0; i < DataLength; i++)
{
a_FireworkItem.m_Colours.push_back(ntohl(ColourData[i]));
}
}
else if (ExplosionName == "FadeColors")
{
int DataLength = a_NBT.GetDataLength(explosiontag) / 4;
if (DataLength == 0)
{
continue;
}
const int * FadeColourData = (const int *)(a_NBT.GetData(explosiontag));
for (int i = 0; i < DataLength; i++)
{
a_FireworkItem.m_FadeColours.push_back(ntohl(FadeColourData[i]));
}
}
}
}
break;
}
case E_ITEM_FIREWORK_ROCKET:
{
for (int fireworkstag = a_NBT.GetFirstChild(a_TagIdx); fireworkstag >= 0; fireworkstag = a_NBT.GetNextSibling(fireworkstag))
{
eTagType TagType = a_NBT.GetType(fireworkstag);
if (TagType == TAG_Byte) // Custon name tag
{
if (a_NBT.GetName(fireworkstag) == "Flight")
{
a_FireworkItem.m_FlightTimeInTicks = a_NBT.GetByte(fireworkstag) * 20;
}
}
else if ((TagType == TAG_List) && (a_NBT.GetName(fireworkstag) == "Explosions"))
{
int ExplosionsChild = a_NBT.GetFirstChild(fireworkstag);
if ((a_NBT.GetType(ExplosionsChild) == TAG_Compound) && (a_NBT.GetName(ExplosionsChild).empty()))
{
ParseFromNBT(a_FireworkItem, a_NBT, ExplosionsChild, E_ITEM_FIREWORK_STAR);
}
}
}
break;
}
default: ASSERT(!"Unhandled firework item!"); break;
}
}
AString cFireworkItem::ColoursToString(const cFireworkItem & a_FireworkItem)
{
AString Result;
for (std::vector<int>::const_iterator itr = a_FireworkItem.m_Colours.begin(); itr != a_FireworkItem.m_Colours.end(); ++itr)
{
AppendPrintf(Result, "%i;", *itr);
}
return Result;
}
void cFireworkItem::ColoursFromString(const AString & a_String, cFireworkItem & a_FireworkItem)
{
AStringVector Split = StringSplit(a_String, ";");
for (size_t itr = 0; itr < Split.size(); ++itr)
{
if (Split[itr].empty())
{
continue;
}
a_FireworkItem.m_Colours.push_back(atoi(Split[itr].c_str()));
}
}
AString cFireworkItem::FadeColoursToString(const cFireworkItem & a_FireworkItem)
{
AString Result;
for (std::vector<int>::const_iterator itr = a_FireworkItem.m_FadeColours.begin(); itr != a_FireworkItem.m_FadeColours.end(); ++itr)
{
AppendPrintf(Result, "%i;", *itr);
}
return Result;
}
void cFireworkItem::FadeColoursFromString(const AString & a_String, cFireworkItem & a_FireworkItem)
{
AStringVector Split = StringSplit(a_String, ";");
for (size_t itr = 0; itr < Split.size(); ++itr)
{
if (Split[itr].empty())
{
continue;
}
a_FireworkItem.m_FadeColours.push_back(atoi(Split[itr].c_str()));
}
}
int cFireworkItem::GetVanillaColourCodeFromDye(short a_DyeMeta)
{
/*
Colours are supposed to be calculated via: R << 16 + G << 8 + B
However, the RGB values fireworks use aren't the same as the ones for dyes (the ones listed in the MC Wiki)
Therefore, here is a list of numbers gotten via the Protocol Proxy
*/
switch (a_DyeMeta)
{
case E_META_DYE_BLACK: return 0x1E1B1B;
case E_META_DYE_RED: return 0xB3312C;
case E_META_DYE_GREEN: return 0x3B511A;
case E_META_DYE_BROWN: return 0x51301A;
case E_META_DYE_BLUE: return 0x253192;
case E_META_DYE_PURPLE: return 0x7B2FBE;
case E_META_DYE_CYAN: return 0x287697;
case E_META_DYE_LIGHTGRAY: return 0xABABAB;
case E_META_DYE_GRAY: return 0x434343;
case E_META_DYE_PINK: return 0xD88198;
case E_META_DYE_LIGHTGREEN: return 0x41CD34;
case E_META_DYE_YELLOW: return 0xDECF2A;
case E_META_DYE_LIGHTBLUE: return 0x6689D3;
case E_META_DYE_MAGENTA: return 0xC354CD;
case E_META_DYE_ORANGE: return 0xEB8844;
case E_META_DYE_WHITE: return 0xF0F0F0;
default: ASSERT(!"Unhandled dye meta whilst trying to get colour code for fireworks!"); return 0;
}
}

View File

@ -0,0 +1,92 @@
// FireworksSerializer.h
// Declares the cFireworkItem class representing a firework or firework star
#pragma once
#include "Defines.h"
class cFastNBTWriter;
class cParsedNBT;
class cFireworkItem
{
public:
cFireworkItem(void) :
m_HasFlicker(false),
m_HasTrail(false),
m_Type(0),
m_FlightTimeInTicks(0)
{
}
inline void CopyFrom(const cFireworkItem & a_Item)
{
m_FlightTimeInTicks = a_Item.m_FlightTimeInTicks;
m_HasFlicker = a_Item.m_HasFlicker;
m_HasTrail = a_Item.m_HasTrail;
m_Type = a_Item.m_Type;
m_Colours = a_Item.m_Colours;
m_FadeColours = a_Item.m_FadeColours;
}
inline void EmptyData(void)
{
m_FlightTimeInTicks = 0;
m_HasFlicker = false;
m_Type = 0;
m_HasTrail = false;
m_Colours.clear();
m_FadeColours.clear();
}
inline bool IsEqualTo(const cFireworkItem & a_Item) const
{
return
(
(m_FlightTimeInTicks == a_Item.m_FlightTimeInTicks) &&
(m_HasFlicker == a_Item.m_HasFlicker) &&
(m_HasTrail == a_Item.m_HasTrail) &&
(m_Type == a_Item.m_Type) &&
(m_Colours == a_Item.m_Colours) &&
(m_FadeColours == a_Item.m_FadeColours)
);
}
/** Writes firework NBT data to a Writer object */
static void WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFastNBTWriter & a_Writer, const ENUM_ITEM_ID a_Type);
/** Reads NBT data from a NBT object and populates a FireworkItem with it */
static void ParseFromNBT(cFireworkItem & a_FireworkItem, const cParsedNBT & a_NBT, int a_TagIdx, const ENUM_ITEM_ID a_Type);
/** Converts the firework's vector of colours into a string of values separated by a semicolon */
static AString ColoursToString(const cFireworkItem & a_FireworkItem);
/** Parses a string containing encoded firework colours and populates a FireworkItem with it */
static void ColoursFromString(const AString & a_String, cFireworkItem & a_FireworkItem);
/** Converts the firework's vector of fade colours into a string of values separated by a semicolon */
static AString FadeColoursToString(const cFireworkItem & a_FireworkItem);
/** Parses a string containing encoded firework fade colours and populates a FireworkItem with it */
static void FadeColoursFromString(const AString & a_String, cFireworkItem & a_FireworkItem);
/** Returns a colour code for fireworks used by the network code */
static int GetVanillaColourCodeFromDye(short a_DyeMeta);
bool m_HasFlicker;
bool m_HasTrail;
NIBBLETYPE m_Type;
short m_FlightTimeInTicks;
std::vector<int> m_Colours;
std::vector<int> m_FadeColours;
};

View File

@ -91,11 +91,19 @@ void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AStrin
} }
// Write the enchantments: // Write the enchantments:
if (!a_Item.m_Enchantments.IsEmpty() || ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR)))
{
m_Writer.BeginCompound("tag");
if ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR))
{
cFireworkItem::WriteToNBTCompound(a_Item.m_FireworkItem, m_Writer, (ENUM_ITEM_ID)a_Item.m_ItemType);
}
if (!a_Item.m_Enchantments.IsEmpty()) if (!a_Item.m_Enchantments.IsEmpty())
{ {
const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
m_Writer.BeginCompound("tag");
EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, m_Writer, TagName); EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, m_Writer, TagName);
}
m_Writer.EndCompound(); m_Writer.EndCompound();
} }

View File

@ -664,6 +664,12 @@ bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_
EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, a_NBT, EnchTag); EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, a_NBT, EnchTag);
} }
int FireworksTag = a_NBT.FindChildByName(TagTag, ((a_Item.m_ItemType == E_ITEM_FIREWORK_STAR) ? "Fireworks" : "Explosion"));
if (EnchTag > 0)
{
cFireworkItem::ParseFromNBT(a_Item.m_FireworkItem, a_NBT, FireworksTag, (ENUM_ITEM_ID)a_Item.m_ItemType);
}
return true; return true;
} }