1
0
Fork 0

Implemented ballistic missiles (fireworks)

+ Added fireworks
This commit is contained in:
Tiger Wang 2014-02-26 23:29:14 +00:00
parent cb40d114ab
commit baf2d88921
15 changed files with 641 additions and 91 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)
ApplePlanks, 4 = AppleLog, *
ConiferPlanks, 4 = ConiferLog, *
BirchPlanks, 4 = BirchLog, *
@ -434,6 +435,48 @@ GoldNugget, 9 = GoldIngot, *
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 colour-change
FireworkStar = FireworkStar, * | Dye ^-1, *

View File

@ -1,4 +1,4 @@

// CraftingRecipes.cpp
// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes
@ -762,6 +762,93 @@ cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_Crafti
Recipe->m_Ingredients.push_back(*itrS);
}
Recipe->m_Ingredients.insert(Recipe->m_Ingredients.end(), MatchedSlots.begin(), MatchedSlots.end());
// Henceforth is code to handle fireworks
// We use Recipe instead of a_Recipe because we want the wildcard ingredients' slot numbers as well, which was just added previously
if (Recipe->m_Result.m_ItemType == E_ITEM_FIREWORK_ROCKET)
{
for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != 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);
Recipe->m_Result.m_FireworkItem.CopyFrom(a_CraftingGrid[GridID].m_FireworkItem);
break;
}
case E_ITEM_GUNPOWDER:
{
// Gunpowder - increase flight time
Recipe->m_Result.m_FireworkItem.m_FlightTimeInTicks += 20;
break;
}
case E_ITEM_PAPER: break;
default: LOG("Unexpected item in firework rocket recipe, was the crafting file fireworks section changed?"); break;
}
}
}
else if (Recipe->m_Result.m_ItemType == E_ITEM_FIREWORK_STAR)
{
for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != 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
int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
Recipe->m_Result.m_FireworkItem.CopyFrom(a_CraftingGrid[GridID].m_FireworkItem);
break;
}
case E_ITEM_DYE:
{
// Found a dye in ingredients...
for (cRecipeSlots::const_iterator itrnumerodos = Recipe->m_Ingredients.begin(); itrnumerodos != Recipe->m_Ingredients.end(); ++itrnumerodos)
{
// Loop through ingredients. Can we find a star?
if (itrnumerodos->m_Item.m_ItemType == E_ITEM_FIREWORK_STAR)
{
// Yes, this is definately adding fade colours, unless crafting.txt was changed :/
for (cRecipeSlots::const_iterator itrnumerotres = Recipe->m_Ingredients.begin(); itrnumerotres != Recipe->m_Ingredients.end(); ++itrnumerotres)
{
// Loop again, can we find dye?
if (itrnumerotres->m_Item.m_ItemType == E_ITEM_DYE)
{
// Yep, push back fade colour and exit the loop
// There is a potential for flexibility here - we can move the goto out of the loop, so we can add multiple dyes (∴ multiple fade colours)
// That will require lots of dye-adding to crafting.txt though - TODO, perchance?
int GridID = (itrnumerotres->x + a_OffsetX) + a_GridStride * (itrnumerotres->y + a_OffsetY);
Recipe->m_Result.m_FireworkItem.m_FadeColours.push_back(cFireworkItem::GetVanillaColourCodeFromDye(a_CraftingGrid[GridID].m_ItemDamage));
goto next;
}
}
}
}
// Just normal crafting of star, push back normal colours
int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
Recipe->m_Result.m_FireworkItem.m_Colours.push_back(cFireworkItem::GetVanillaColourCodeFromDye(a_CraftingGrid[GridID].m_ItemDamage));
next:
break;
}
case E_ITEM_GUNPOWDER: break;
case E_ITEM_DIAMOND: Recipe->m_Result.m_FireworkItem.m_HasTrail = true; break;
case E_ITEM_GLOWSTONE_DUST: Recipe->m_Result.m_FireworkItem.m_HasFlicker = true; break;
case E_ITEM_FIRE_CHARGE: Recipe->m_Result.m_FireworkItem.m_Type = 1; break;
case E_ITEM_GOLD_NUGGET: Recipe->m_Result.m_FireworkItem.m_Type = 2; break;
case E_ITEM_FEATHER: Recipe->m_Result.m_FireworkItem.m_Type = 4; break;
case E_ITEM_HEAD: Recipe->m_Result.m_FireworkItem.m_Type = 3; break;
default: LOG("Unexpected item in firework star recipe, was the crafting file fireworks section changed?"); break; // ermahgerd BARD ardmins
}
}
}
return Recipe.release();
}

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;
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 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 pkFirework: return new cFireworkEntity (a_Creator, a_X, a_Y, a_Z );
// TODO: the rest
case pkFirework:
{
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);
@ -276,6 +283,7 @@ AString cProjectileEntity::GetMCAClassName(void) const
case pkExpBottle: return "ThrownExpBottle";
case pkSplashPotion: return "ThrownPotion";
case pkWitherSkull: return "WitherSkull";
case pkFirework: return "Firework";
case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this?
}
ASSERT(!"Unhandled projectile entity kind!");
@ -693,8 +701,10 @@ void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_H
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFireworkEntity :
cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z) :
super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
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),
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)
{
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 (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;
}
@ -734,28 +734,35 @@ void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
return;
}
}
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))
else
{
// Something has been hit, abort all other processing
return;
if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) != E_BLOCK_AIR)
{
OnHitSolidBlock(GetPosition(), BLOCK_FACE_YM);
return;
}
}
// The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff
// Update the position:
SetPosition(NextPos);
setspeed:
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, 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
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace);
@ -305,13 +305,19 @@ public:
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:
// 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 Tick(float a_Dt, cChunk & a_Chunk) override;
private:
int m_ExplodeTimer;
cItem m_FireworkItem;
// tolua_begin

View File

@ -1,4 +1,4 @@

#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Item.h"
@ -139,6 +139,16 @@ void cItem::GetJson(Json::Value & a_OutValue) const
{
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_CustomName = a_Value.get("Name", "").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 "Enchantments.h"
#include "WorldStorage/FireworksSerializer.h"
@ -38,7 +39,8 @@ public:
m_ItemCount(0),
m_ItemDamage(0),
m_CustomName(""),
m_Lore("")
m_Lore(""),
m_FireworkItem()
{
}
@ -57,7 +59,8 @@ public:
m_ItemDamage (a_ItemDamage),
m_Enchantments(a_Enchantments),
m_CustomName (a_CustomName),
m_Lore (a_Lore)
m_Lore (a_Lore),
m_FireworkItem()
{
if (!IsValidItem(m_ItemType))
{
@ -77,7 +80,8 @@ public:
m_ItemDamage (a_CopyFrom.m_ItemDamage),
m_Enchantments(a_CopyFrom.m_Enchantments),
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_CustomName = "";
m_Lore = "";
m_FireworkItem.EmptyData();
}
@ -115,7 +120,8 @@ public:
(m_ItemDamage == a_Item.m_ItemDamage) &&
(m_Enchantments == a_Item.m_Enchantments) &&
(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;
AString m_CustomName;
AString m_Lore;
cFireworkItem m_FireworkItem;
};
// tolua_end

View File

@ -35,7 +35,7 @@ public:
Vector3d Pos = a_Player->GetThrowStartPos();
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;
}
@ -127,13 +127,13 @@ public:
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())
{
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;
}

View File

@ -2069,36 +2069,47 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
// Load enchantments and custom display names from the NBT data:
for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag))
{
if (
(NBT.GetType(tag) == TAG_List) &&
(
(NBT.GetName(tag) == "ench") ||
(NBT.GetName(tag) == "StoredEnchantments")
)
)
AString TagName = NBT.GetName(tag);
switch (NBT.GetType(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
{
for (int displaytag = NBT.GetFirstChild(tag); displaytag >= 0; displaytag = NBT.GetNextSibling(displaytag))
case TAG_List:
{
if ((NBT.GetType(displaytag) == TAG_String) && (NBT.GetName(displaytag) == "Name")) // Custon name tag
if ((TagName == "ench") || (TagName == "StoredEnchantments")) // Enchantments tags
{
a_Item.m_CustomName = NBT.GetString(displaytag);
}
else if ((NBT.GetType(displaytag) == TAG_List) && (NBT.GetName(displaytag) == "Lore")) // Lore tag
{
AString Lore;
for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings
{
AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a newline, used internally by MCS to display a new line in the client; don't forget to c_str ;)
}
a_Item.m_Lore = Lore;
EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, NBT, 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))
{
if ((NBT.GetType(displaytag) == TAG_String) && (NBT.GetName(displaytag) == "Name")) // Custon name tag
{
a_Item.m_CustomName = NBT.GetString(displaytag);
}
else if ((NBT.GetType(displaytag) == TAG_List) && (NBT.GetName(displaytag) == "Lore")) // Lore tag
{
AString Lore;
for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings
{
AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a newline, used internally by MCS to display a new line in the client; don't forget to c_str ;)
}
a_Item.m_Lore = Lore;
}
}
}
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;
}
}
}
@ -2262,7 +2273,7 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item)
WriteByte (a_Item.m_ItemCount);
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);
return;
@ -2301,6 +2312,10 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item)
}
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();
AString Compressed;
CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
@ -2465,10 +2480,22 @@ void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity)
}
case cEntity::etProjectile:
{
if (((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow)
cProjectileEntity & Projectile = (cProjectileEntity &)a_Entity;
switch (Projectile.GetProjectileKind())
{
WriteByte(0x10);
WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0);
case cProjectileEntity::pkArrow:
{
WriteByte(0x10);
WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0);
break;
}
case cProjectileEntity::pkFirework:
{
WriteByte(0xA8);
WriteItem(((const cFireworkEntity &)a_Entity).GetItem());
break;
}
default: break;
}
break;
}

View File

@ -2904,9 +2904,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)
{
return -1;

View File

@ -693,7 +693,7 @@ public:
int SpawnMobFinalize(cMonster* a_Monster);
/** 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! */
int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); }

View File

@ -266,7 +266,7 @@ protected:
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.
sParent m_Stack[MAX_STACK];

View File

@ -0,0 +1,250 @@

#include "Globals.h"
#include "FireworksSerializer.h"
#include "WorldStorage/FastNBT.h"
#include <sstream>
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")
{
if (a_NBT.GetDataLength(explosiontag) == 0)
{
continue;
}
const int * ColourData = (const int *)(a_NBT.GetData(explosiontag));
for (int i = 0; i < ARRAYCOUNT(ColourData); i++)
{
a_FireworkItem.m_Colours.push_back(ntohl(ColourData[i]));
}
}
else if (ExplosionName == "FadeColors")
{
if (a_NBT.GetDataLength(explosiontag) == 0)
{
continue;
}
const int * FadeColourData = (const int *)(a_NBT.GetData(explosiontag));
for (int i = 0; i < ARRAYCOUNT(FadeColourData); 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 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 1973019;
case E_META_DYE_RED: return 11743532;
case E_META_DYE_GREEN: return 3887386;
case E_META_DYE_BROWN: return 5320730;
case E_META_DYE_BLUE: return 2437522;
case E_META_DYE_PURPLE: return 8073150;
case E_META_DYE_CYAN: return 2651799;
case E_META_DYE_LIGHTGRAY: return 11250603;
case E_META_DYE_GRAY: return 4408131;
case E_META_DYE_PINK: return 14188952;
case E_META_DYE_LIGHTGREEN: return 4312372;
case E_META_DYE_YELLOW: return 14602026;
case E_META_DYE_LIGHTBLUE: return 6719955;
case E_META_DYE_MAGENTA: return 12801229;
case E_META_DYE_ORANGE: return 15435844;
case E_META_DYE_WHITE: return 15790320;
default: ASSERT(!"Unhandled dye meta whilst trying to get colour code for fireworks!"); break;
}
}

View File

@ -0,0 +1,88 @@
// 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 const inline 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

@ -90,11 +90,19 @@ void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AStrin
}
// Write the enchantments:
if (!a_Item.m_Enchantments.IsEmpty())
if (!a_Item.m_Enchantments.IsEmpty() || ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR)))
{
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);
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())
{
const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, m_Writer, TagName);
}
m_Writer.EndCompound();
}

View File

@ -658,6 +658,12 @@ bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_
{
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;
}