diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index d71a0ccbe..a1dba7448 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -9306,6 +9306,10 @@ a_Player:OpenWindow(Window); { Notes = "", }, + mtZombieVillager = + { + Notes = "", + }, }, ConstantGroups = { @@ -17065,6 +17069,7 @@ end "mtWolf", "mtZombie", "mtZombiePigman", + "mtZombieVillager", "mtMax", }, TextBefore = [[ diff --git a/Server/monsters.ini b/Server/monsters.ini index 31e5b1479..2cd5e8892 100644 --- a/Server/monsters.ini +++ b/Server/monsters.ini @@ -220,3 +220,10 @@ AttackRate=1.0 IsFireproof=1 MaxHealth=20 SightDistance=25.0 + +[ZombieVillager] +AttackDamage=4.0 +AttackRange=2.0 +AttackRate=1.0 +MaxHealth=20 +SightDistance=25.0 diff --git a/Tools/AnvilStats/Utils.cpp b/Tools/AnvilStats/Utils.cpp index 741c01667..a3fbedf28 100644 --- a/Tools/AnvilStats/Utils.cpp +++ b/Tools/AnvilStats/Utils.cpp @@ -16,37 +16,38 @@ struct const char * String; } g_EntityTypes[] = { - {entBat, "Bat"}, - {entBlaze, "Blaze"}, - {entCaveSpider, "CaveSpider"}, - {entChicken, "Chicken"}, - {entCow, "Cow"}, - {entCreeper, "Creeper"}, - {entEnderDragon, "EnderDragon"}, - {entEnderman, "Enderman"}, - {entGhast, "Ghast"}, - {entGiant, "Giant"}, - {entGuardian, "Guardian"}, - {entLavaSlime, "LavaSlime"}, - {entMushroomCow, "MushroomCow"}, - {entOzelot, "Ozelot"}, - {entPig, "Pig"}, - {entPigZombie, "PigZombie"}, - {entRabbit, "Rabbit"}, - {entSheep, "Sheep"}, - {entSilverfish, "Slverfish"}, - {entSkeleton, "Skeleton"}, - {entSlime, "Slime"}, - {entSnowMan, "SnowMan"}, - {entSpider, "Spider"}, - {entSquid, "Squid"}, - {entVillager, "Villager"}, - {entVillagerGolem, "VillagerGolem"}, - {entWitch, "Witch"}, - {entWitherBoss, "WitherBoss"}, - {entWolf, "Wolf"}, - {entZombie, "Zombie"}, - {entUnknown, "Unknown"}, + {entBat, "Bat"}, + {entBlaze, "Blaze"}, + {entCaveSpider, "CaveSpider"}, + {entChicken, "Chicken"}, + {entCow, "Cow"}, + {entCreeper, "Creeper"}, + {entEnderDragon, "EnderDragon"}, + {entEnderman, "Enderman"}, + {entGhast, "Ghast"}, + {entGiant, "Giant"}, + {entGuardian, "Guardian"}, + {entLavaSlime, "LavaSlime"}, + {entMushroomCow, "MushroomCow"}, + {entOzelot, "Ozelot"}, + {entPig, "Pig"}, + {entPigZombie, "PigZombie"}, + {entRabbit, "Rabbit"}, + {entSheep, "Sheep"}, + {entSilverfish, "Slverfish"}, + {entSkeleton, "Skeleton"}, + {entSlime, "Slime"}, + {entSnowMan, "SnowMan"}, + {entSpider, "Spider"}, + {entSquid, "Squid"}, + {entVillager, "Villager"}, + {entVillagerGolem, "VillagerGolem"}, + {entWitch, "Witch"}, + {entWitherBoss, "WitherBoss"}, + {entWolf, "Wolf"}, + {entZombie, "Zombie"}, + {entZombieVillager, "ZombieVillager"}, + {entUnknown, "Unknown"}, } ; diff --git a/Tools/AnvilStats/Utils.h b/Tools/AnvilStats/Utils.h index e3172649c..f2d23f287 100644 --- a/Tools/AnvilStats/Utils.h +++ b/Tools/AnvilStats/Utils.h @@ -45,6 +45,7 @@ enum eEntityType entWitherBoss, entWolf, entZombie, + entZombieVillager, entUnknown, entMax = entUnknown, } ; diff --git a/src/BlockType.h b/src/BlockType.h index 8a70226db..9f8c8fb55 100644 --- a/src/BlockType.h +++ b/src/BlockType.h @@ -1079,6 +1079,7 @@ enum ENUM_ITEM_META : short E_META_SPAWN_EGG_PRIMED_TNT = 20, E_META_SPAWN_EGG_FALLING_BLOCK = 21, E_META_SPAWN_EGG_FIREWORK = 22, + E_META_SPAWN_EGG_ZOMBIE_VILLAGER = 27, E_META_SPAWN_EGG_BOAT = 41, E_META_SPAWN_EGG_MINECART = 42, E_META_SPAWN_EGG_MINECART_CHEST = 43, diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 453bd03d2..e7682e6da 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -456,9 +456,10 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) switch (Monster->GetMobType()) { case mtSkeleton: - case mtZombie: case mtWither: + case mtZombie: case mtZombiePigman: + case mtZombieVillager: { a_TDI.FinalDamage += 2.5f * SmiteLevel; break; diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h index 6d4219236..d57b93b2c 100644 --- a/src/Items/ItemSpawnEgg.h +++ b/src/Items/ItemSpawnEgg.h @@ -87,11 +87,8 @@ public: case E_META_SPAWN_EGG_WOLF: return mtWolf; case E_META_SPAWN_EGG_ZOMBIE: return mtZombie; case E_META_SPAWN_EGG_ZOMBIE_PIGMAN: return mtZombiePigman; + case E_META_SPAWN_EGG_ZOMBIE_VILLAGER: return mtZombieVillager; + default: return mtInvalidType; } - return mtInvalidType; } } ; - - - - diff --git a/src/Mobs/CMakeLists.txt b/src/Mobs/CMakeLists.txt index 15456595c..579ba8938 100644 --- a/src/Mobs/CMakeLists.txt +++ b/src/Mobs/CMakeLists.txt @@ -36,7 +36,8 @@ SET (SRCS WitherSkeleton.cpp Wolf.cpp Zombie.cpp - ZombiePigman.cpp) + ZombiePigman.cpp + ZombieVillager.cpp) SET (HDRS AggressiveMonster.h @@ -78,7 +79,8 @@ SET (HDRS WitherSkeleton.h Wolf.h Zombie.h - ZombiePigman.h) + ZombiePigman.h + ZombieVillager.h) if(NOT MSVC) add_library(Mobs ${SRCS} ${HDRS}) diff --git a/src/Mobs/IncludeAllMonsters.h b/src/Mobs/IncludeAllMonsters.h index 17a9dfacd..afb79c97c 100644 --- a/src/Mobs/IncludeAllMonsters.h +++ b/src/Mobs/IncludeAllMonsters.h @@ -30,3 +30,4 @@ #include "Wolf.h" #include "Zombie.h" #include "ZombiePigman.h" +#include "ZombieVillager.h" diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 5e3026a57..d20afa589 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -69,6 +69,7 @@ static const struct {mtWolf, "wolf", "Wolf", "wolf"}, {mtZombie, "zombie", "Zombie", "zombie"}, {mtZombiePigman, "zombiepigman", "PigZombie", "zombie_pigman"}, + {mtZombieVillager, "zombievillager", "ZombieVillager", "zombie_villager"}, } ; @@ -662,6 +663,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) case mtWitherSkeleton: case mtZombie: case mtZombiePigman: + case mtZombieVillager: case mtSlime: case mtMagmaCube: { @@ -1078,6 +1080,7 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) case mtWolf: return mfPassive; case mtZombie: return mfHostile; case mtZombiePigman: return mfHostile; + case mtZombieVillager: return mfHostile; default: { @@ -1176,17 +1179,7 @@ std::unique_ptr cMonster::NewMonsterFromType(eMonsterType a_MobType) { return cpp14::make_unique(1 << Random.RandInt(2)); // Size 1, 2 or 4 } - case mtVillager: - { - int VillagerType = Random.RandInt(6); - if (VillagerType == 6) - { - // Give farmers a better chance of spawning - VillagerType = 0; - } - - return cpp14::make_unique(static_cast(VillagerType)); - } + case mtVillager: return cpp14::make_unique(cVillager::GetRandomProfession()); case mtHorse: { // Horses take a type (species), a colour, and a style (dots, stripes, etc.) @@ -1203,7 +1196,10 @@ std::unique_ptr cMonster::NewMonsterFromType(eMonsterType a_MobType) return cpp14::make_unique(HorseType, HorseColor, HorseStyle, HorseTameTimes); } - + case mtZombieVillager: + { + return cpp14::make_unique(cVillager::GetRandomProfession()); + } case mtBat: return cpp14::make_unique(); case mtBlaze: return cpp14::make_unique(); case mtCaveSpider: return cpp14::make_unique(); diff --git a/src/Mobs/MonsterTypes.h b/src/Mobs/MonsterTypes.h index 7a864fda4..6d3c57852 100644 --- a/src/Mobs/MonsterTypes.h +++ b/src/Mobs/MonsterTypes.h @@ -11,6 +11,7 @@ enum eMonsterType { mtInvalidType = -1, +<<<<<<< HEAD mtBat, mtBlaze, mtCaveSpider, @@ -43,6 +44,41 @@ enum eMonsterType mtWolf, mtZombie, mtZombiePigman, +======= + mtBat = E_META_SPAWN_EGG_BAT, + mtBlaze = E_META_SPAWN_EGG_BLAZE, + mtCaveSpider = E_META_SPAWN_EGG_CAVE_SPIDER, + mtChicken = E_META_SPAWN_EGG_CHICKEN, + mtCow = E_META_SPAWN_EGG_COW, + mtCreeper = E_META_SPAWN_EGG_CREEPER, + mtEnderDragon = E_META_SPAWN_EGG_ENDER_DRAGON, + mtEnderman = E_META_SPAWN_EGG_ENDERMAN, + mtGhast = E_META_SPAWN_EGG_GHAST, + mtGiant = E_META_SPAWN_EGG_GIANT, + mtGuardian = E_META_SPAWN_EGG_GUARDIAN, + mtHorse = E_META_SPAWN_EGG_HORSE, + mtIronGolem = E_META_SPAWN_EGG_IRON_GOLEM, + mtMagmaCube = E_META_SPAWN_EGG_MAGMA_CUBE, + mtMooshroom = E_META_SPAWN_EGG_MOOSHROOM, + mtOcelot = E_META_SPAWN_EGG_OCELOT, + mtPig = E_META_SPAWN_EGG_PIG, + mtRabbit = E_META_SPAWN_EGG_RABBIT, + mtSheep = E_META_SPAWN_EGG_SHEEP, + mtSilverfish = E_META_SPAWN_EGG_SILVERFISH, + mtSkeleton = E_META_SPAWN_EGG_SKELETON, + mtSlime = E_META_SPAWN_EGG_SLIME, + mtSnowGolem = E_META_SPAWN_EGG_SNOW_GOLEM, + mtSpider = E_META_SPAWN_EGG_SPIDER, + mtSquid = E_META_SPAWN_EGG_SQUID, + mtVillager = E_META_SPAWN_EGG_VILLAGER, + mtWitch = E_META_SPAWN_EGG_WITCH, + mtWither = E_META_SPAWN_EGG_WITHER, + mtWolf = E_META_SPAWN_EGG_WOLF, + mtZombie = E_META_SPAWN_EGG_ZOMBIE, + mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN, + mtZombieVillager = E_META_SPAWN_EGG_ZOMBIE_VILLAGER, + mtMax = 120, // This is just a hotfix for https://forum.cuberite.org/thread-1616.html. Tolua is too bad to find the highest value, so this is needed. +>>>>>>> Add Zombie Villagers } ; // tolua_end diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp index 9558ba536..eca8ed2b6 100644 --- a/src/Mobs/Villager.cpp +++ b/src/Mobs/Villager.cpp @@ -107,6 +107,26 @@ void cVillager::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) + +void cVillager::KilledBy(TakeDamageInfo & a_TDI) +{ + super::KilledBy(a_TDI); + + // TODO: 0% chance on Easy, 50% chance on Normal and 100% chance on Hard + if (GetRandomProvider().RandBool(0.5) && (a_TDI.Attacker != nullptr) && (a_TDI.Attacker->IsMob())) + { + eMonsterType MonsterType = (static_cast(a_TDI.Attacker)->GetMobType()); + if ((MonsterType == mtZombie) || (MonsterType == mtZombieVillager)) + { + m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), mtZombieVillager, false); + } + } +} + + + + + //////////////////////////////////////////////////////////////////////////////// // Farmer functions: @@ -203,7 +223,17 @@ bool cVillager::IsBlockFarmable(BLOCKTYPE a_BlockType) { return true; } + default: return false; } - return false; } + + + + +cVillager::eVillagerType cVillager::GetRandomProfession() +{ + int Profession = GetRandomProvider().RandInt(cVillager::eVillagerType::vtMax - 1); + + return static_cast(Profession); +} diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h index b9a0b7675..2d6644193 100644 --- a/src/Mobs/Villager.h +++ b/src/Mobs/Villager.h @@ -29,9 +29,13 @@ public: CLASS_PROTODEF(cVillager) + /** Returns a random Profession. */ + static eVillagerType GetRandomProfession(); + // cEntity overrides virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void KilledBy (TakeDamageInfo & a_TDI) override; // cVillager functions /** return true if the given blocktype are: crops, potatoes or carrots. */ diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp index 451255ed2..02c1efa86 100644 --- a/src/Mobs/Zombie.cpp +++ b/src/Mobs/Zombie.cpp @@ -9,10 +9,8 @@ -cZombie::cZombie(bool a_IsVillagerZombie) : - super("Zombie", mtZombie, "entity.zombie.hurt", "entity.zombie.death", "entity.zombie.ambient", 0.6, 1.8), - m_IsVillagerZombie(a_IsVillagerZombie), - m_IsConverting(false) +cZombie::cZombie() : + super("Zombie", mtZombie, "entity.zombie.hurt", "entity.zombie.death", "entity.zombie.ambient", 0.6, 1.8) { } diff --git a/src/Mobs/Zombie.h b/src/Mobs/Zombie.h index 47a9f1904..20f59a627 100644 --- a/src/Mobs/Zombie.h +++ b/src/Mobs/Zombie.h @@ -12,21 +12,13 @@ class cZombie : typedef cAggressiveMonster super; public: - cZombie(bool a_IsVillagerZombie); + cZombie(); CLASS_PROTODEF(cZombie) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual bool IsUndead(void) override { return true; } - bool IsVillagerZombie(void) const { return m_IsVillagerZombie; } - bool IsConverting (void) const { return m_IsConverting; } - -private: - - bool m_IsVillagerZombie; - bool m_IsConverting; - } ; diff --git a/src/Mobs/ZombieVillager.cpp b/src/Mobs/ZombieVillager.cpp new file mode 100644 index 000000000..9d17e6038 --- /dev/null +++ b/src/Mobs/ZombieVillager.cpp @@ -0,0 +1,87 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ZombieVillager.h" +#include "../World.h" +#include "../LineBlockTracer.h" +#include "../Entities/Player.h" + + + + + +cZombieVillager::cZombieVillager(cVillager::eVillagerType a_Profession) : + super("ZombieVillager", mtZombieVillager, "entity.zombie_villager.hurt", "entity.zombie_villager.death", "entity.ambient", 0.6, 1.8), + m_ConversionTime(-1), + m_Profession(a_Profession) +{ + SetBurnsInDaylight(true); +} + + + + + +void cZombieVillager::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + unsigned int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ROTTEN_FLESH); + cItems RareDrops; + RareDrops.Add(cItem(E_ITEM_IRON)); + RareDrops.Add(cItem(E_ITEM_CARROT)); + RareDrops.Add(cItem(E_ITEM_POTATO)); + AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); + AddRandomArmorDropItem(a_Drops, LootingLevel); + AddRandomWeaponDropItem(a_Drops, LootingLevel); +} + + + + + +void cZombieVillager::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + if (!IsTicking()) + { + // The base class tick destroyed us + return; + } + + LOGD("Conversion time: %d", m_ConversionTime); + + if (m_ConversionTime == 0) + { + m_World->BroadcastSoundEffect("entity.zombie_villager.cure", GetPosition(), 1.0f, 1.0f); + Destroy(); + m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), mtVillager, false); + } + else if (m_ConversionTime > 0) + { + m_ConversionTime--; + } +} + + + + + +void cZombieVillager::OnRightClicked(cPlayer & a_Player) +{ + super::OnRightClicked(a_Player); + + const cItem & EquippedItem = a_Player.GetEquippedItem(); + if ((EquippedItem.m_ItemType == E_ITEM_GOLDEN_APPLE) && GetEntityEffect(cEntityEffect::effWeakness) != nullptr) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + + m_ConversionTime = 6000; + } +} diff --git a/src/Mobs/ZombieVillager.h b/src/Mobs/ZombieVillager.h new file mode 100644 index 000000000..c4c4ae3d1 --- /dev/null +++ b/src/Mobs/ZombieVillager.h @@ -0,0 +1,33 @@ +#pragma once + +#include "AggressiveMonster.h" +#include "Villager.h" + + + + + +class cZombieVillager : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cZombieVillager(cVillager::eVillagerType a_Profession); + + CLASS_PROTODEF(cZombieVillager) + + virtual void GetDrops (cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void OnRightClicked (cPlayer & a_Player) override; + virtual bool IsUndead (void) override { return true; } + + int ConversionTime (void) const { return m_ConversionTime; } + cVillager::eVillagerType GetProfession (void) const { return m_Profession; } + +private: + + int m_ConversionTime; + cVillager::eVillagerType m_Profession; + +} ; diff --git a/src/Protocol/Protocol_1_10.cpp b/src/Protocol/Protocol_1_10.cpp index 8a23ec09d..83df39a6c 100644 --- a/src/Protocol/Protocol_1_10.cpp +++ b/src/Protocol/Protocol_1_10.cpp @@ -1008,11 +1008,11 @@ void cProtocol_1_10_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_ a_Pkt.WriteBEUInt8(ZOMBIE_TYPE); a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); - a_Pkt.WriteVarInt32(Zombie.IsVillagerZombie() ? 1 : 0); // TODO: This actually encodes the zombie villager profession, but that isn't implemented yet. + a_Pkt.WriteVarInt32(0); a_Pkt.WriteBEUInt8(ZOMBIE_CONVERTING); a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); - a_Pkt.WriteBool(Zombie.IsConverting()); + a_Pkt.WriteBool(false); break; } // case mtZombie @@ -1025,6 +1025,23 @@ void cProtocol_1_10_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_ break; } // case mtZombiePigman + case mtZombieVillager: + { + auto & ZombieVillager = reinterpret_cast(a_Mob); + a_Pkt.WriteBEUInt8(ZOMBIE_IS_BABY); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); + a_Pkt.WriteBool(ZombieVillager.IsBaby()); + + a_Pkt.WriteBEUInt8(ZOMBIE_TYPE); + a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); + a_Pkt.WriteVarInt32(ZombieVillager.GetProfession()); + + a_Pkt.WriteBEUInt8(ZOMBIE_CONVERTING); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); + a_Pkt.WriteBool(ZombieVillager.ConversionTime() != -1); + break; + } // case mtZombieVillager + default: break; } // switch (a_Mob.GetType()) } diff --git a/src/Protocol/Protocol_1_11.cpp b/src/Protocol/Protocol_1_11.cpp index 8a846b540..7e46fa332 100644 --- a/src/Protocol/Protocol_1_11.cpp +++ b/src/Protocol/Protocol_1_11.cpp @@ -1182,15 +1182,6 @@ void cProtocol_1_11_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_ a_Pkt.WriteBEUInt8(ZOMBIE_IS_BABY); a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); a_Pkt.WriteBool(Zombie.IsBaby()); - - // These don't exist - // a_Pkt.WriteBEUInt8(ZOMBIE_TYPE); - // a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); - // a_Pkt.WriteVarInt32(Zombie.IsVillagerZombie() ? 1 : 0); - - // a_Pkt.WriteBEUInt8(ZOMBIE_CONVERTING); - // a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); - // a_Pkt.WriteBool(Zombie.IsConverting()); break; } // case mtZombie @@ -1203,6 +1194,23 @@ void cProtocol_1_11_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_ break; } // case mtZombiePigman + case mtZombieVillager: + { + auto & ZombieVillager = reinterpret_cast(a_Mob); + a_Pkt.WriteBEUInt8(ZOMBIE_IS_BABY); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); + a_Pkt.WriteBool(ZombieVillager.IsBaby()); + + a_Pkt.WriteBEUInt8(ZOMBIE_VILLAGER_CONVERTING); + a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); + a_Pkt.WriteVarInt32(static_cast(ZombieVillager.ConversionTime())); + + a_Pkt.WriteBEUInt8(ZOMBIE_VILLAGER_PROFESSION); + a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); + a_Pkt.WriteVarInt32(static_cast(ZombieVillager.GetProfession())); + break; + } // case mtZombieVillager + default: break; } // switch (a_Mob.GetType()) } diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp index 01bbf8104..8cf8df6ca 100644 --- a/src/Protocol/Protocol_1_12.cpp +++ b/src/Protocol/Protocol_1_12.cpp @@ -908,9 +908,6 @@ void cProtocol_1_12::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mo // a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); // a_Pkt.WriteVarInt32(Zombie.IsVillagerZombie() ? 1 : 0); - // a_Pkt.WriteBEUInt8(ZOMBIE_CONVERTING); - // a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); - // a_Pkt.WriteBool(Zombie.IsConverting()); break; } // case mtZombie @@ -923,6 +920,23 @@ void cProtocol_1_12::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mo break; } // case mtZombiePigman + case mtZombieVillager: + { + auto & ZombieVillager = reinterpret_cast(a_Mob); + a_Pkt.WriteBEUInt8(ZOMBIE_IS_BABY); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); + a_Pkt.WriteBool(ZombieVillager.IsBaby()); + + a_Pkt.WriteBEUInt8(ZOMBIE_VILLAGER_CONVERTING); + a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); + a_Pkt.WriteVarInt32(static_cast(ZombieVillager.ConversionTime())); + + a_Pkt.WriteBEUInt8(ZOMBIE_VILLAGER_PROFESSION); + a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); + a_Pkt.WriteVarInt32(static_cast(ZombieVillager.GetProfession())); + break; + } // case mtZombieVillager + case mtBlaze: case mtEnderDragon: case mtGuardian: diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index 1b3c261f5..a516ec891 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -3825,9 +3825,9 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M a_Pkt.WriteBEUInt8(0x0c); a_Pkt.WriteBEInt8(Zombie.IsBaby() ? 1 : -1); a_Pkt.WriteBEUInt8(0x0d); - a_Pkt.WriteBEUInt8(Zombie.IsVillagerZombie() ? 1 : 0); + a_Pkt.WriteBEUInt8(0); a_Pkt.WriteBEUInt8(0x0e); - a_Pkt.WriteBEUInt8(Zombie.IsConverting() ? 1 : 0); + a_Pkt.WriteBEUInt8(0); break; } // case mtZombie @@ -3839,6 +3839,18 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M break; } // case mtZombiePigman + case mtZombieVillager: + { + auto & ZombieVillager = reinterpret_cast(a_Mob); + a_Pkt.WriteBEUInt8(0x0c); + a_Pkt.WriteBEInt8(ZombieVillager.IsBaby() ? 1 : -1); + a_Pkt.WriteBEUInt8(0x0d); + a_Pkt.WriteBEUInt8(1); + a_Pkt.WriteBEUInt8(0x0e); + a_Pkt.WriteBEUInt8((ZombieVillager.ConversionTime() == -1) ? 0 : 1); + break; + } // case mtZombieVillager + default: break; } // switch (a_Mob.GetType()) } diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index 9f786b02d..e33b65b69 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -4329,11 +4329,11 @@ void cProtocol_1_9_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M a_Pkt.WriteBEUInt8(12); // Index 12: Is a villager a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); - a_Pkt.WriteVarInt32(Zombie.IsVillagerZombie() ? 1 : 0); // TODO: This actually encodes the zombie villager profession, but that isn't implemented yet. + a_Pkt.WriteVarInt32(0); a_Pkt.WriteBEUInt8(13); // Index 13: Is converting a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); - a_Pkt.WriteBool(Zombie.IsConverting()); + a_Pkt.WriteBool(false); break; } // case mtZombie @@ -4346,6 +4346,23 @@ void cProtocol_1_9_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M break; } // case mtZombiePigman + case mtZombieVillager: + { + auto & ZombieVillager = reinterpret_cast(a_Mob); + a_Pkt.WriteBEUInt8(11); // Index 11: Is baby + a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); + a_Pkt.WriteBool(ZombieVillager.IsBaby()); + + a_Pkt.WriteBEUInt8(12); // Index 12: Is a villager + a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); + a_Pkt.WriteVarInt32(ZombieVillager.GetProfession()); + + a_Pkt.WriteBEUInt8(13); // Index 13: Is converting + a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); + a_Pkt.WriteBool(ZombieVillager.ConversionTime() != -1); + break; + } // case mtZombieVillager + default: break; } // switch (a_Mob.GetType()) } diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 3e8994786..138921264 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -738,10 +738,17 @@ public: case mtVillager: EntityClass = "Villager"; break; case mtWitch: EntityClass = "Witch"; break; case mtWither: EntityClass = "WitherBoss"; break; +<<<<<<< HEAD case mtWitherSkeleton: EntityClass = "WitherSkeleton"; break; case mtWolf: EntityClass = "Wolf"; break; case mtZombie: EntityClass = "Zombie"; break; case mtZombiePigman: EntityClass = "PigZombie"; break; +======= + case mtWolf: EntityClass = "Wolf"; break; + case mtZombie: EntityClass = "Zombie"; break; + case mtZombiePigman: EntityClass = "PigZombie"; break; + case mtZombieVillager: EntityClass = "ZombieVillager"; break; +>>>>>>> Add Zombie Villagers default: { ASSERT(!"Unhandled monster type"); @@ -901,10 +908,7 @@ public: } case mtZombie: { - const cZombie *Zombie = static_cast(a_Monster); - mWriter.AddByte("IsVillager", Zombie->IsVillagerZombie() ? 1 : 0); - mWriter.AddByte("IsConverting", Zombie->IsConverting() ? 1 : 0); - mWriter.AddInt ("Age", Zombie->GetAge()); + mWriter.AddInt("Age", static_cast(a_Monster)->GetAge()); break; } case mtZombiePigman: @@ -912,6 +916,13 @@ public: mWriter.AddInt("Age", static_cast(a_Monster)->GetAge()); break; } + case mtZombieVillager: + { + const cZombieVillager *ZombieVillager = reinterpret_cast(a_Monster); + mWriter.AddInt("Profession", ZombieVillager->GetProfession()); + mWriter.AddInt("ConversionTime", ZombieVillager->ConversionTime()); + mWriter.AddInt("Age", ZombieVillager->GetAge()); + } case mtBlaze: case mtCaveSpider: case mtChicken: diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 31934e181..9915e9eb7 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -1568,6 +1568,8 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a { "minecraft:zombie", &cWSSAnvil::LoadZombieFromNBT }, { "PigZombie", &cWSSAnvil::LoadPigZombieFromNBT }, { "minecraft:zombie_pigman", &cWSSAnvil::LoadPigZombieFromNBT }, + { "ZombieVillager", &cWSSAnvil::LoadZombieVillagerFromNBT }, + { "minecraft:zombie_villager", &cWSSAnvil::LoadZombieVillagerFromNBT }, }; auto it = EntityTypeToFunction.find(AString(a_IDTag, a_IDTagLength)); @@ -2968,15 +2970,7 @@ void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N void cWSSAnvil::LoadZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - int IsVillagerIdx = a_NBT.FindChildByName(a_TagIdx, "IsVillager"); - if (IsVillagerIdx < 0) - { - return; - } - - bool IsVillagerZombie = ((a_NBT.GetByte(IsVillagerIdx) == 1) ? true : false); - - std::unique_ptr Monster = cpp14::make_unique(IsVillagerZombie); + std::unique_ptr Monster = cpp14::make_unique(); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2994,7 +2988,7 @@ void cWSSAnvil::LoadZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a switch (a_NBT.GetType(AgeableIdx)) { case TAG_Byte: Age = static_cast(a_NBT.GetByte(AgeableIdx)); break; - case TAG_Int: Age = a_NBT.GetInt (AgeableIdx); break; + case TAG_Int: Age = a_NBT.GetInt(AgeableIdx); break; default: Age = 0; break; } Monster->SetAge(Age); @@ -3040,6 +3034,49 @@ void cWSSAnvil::LoadPigZombieFromNBT(cEntityList & a_Entities, const cParsedNBT +void cWSSAnvil::LoadZombieVillagerFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int ProfessionIdx = a_NBT.FindChildByName(a_TagIdx, "Profession"); + if (ProfessionIdx < 0) + { + return; + } + + cVillager::eVillagerType Profession = static_cast(a_NBT.GetInt(ProfessionIdx)); + + std::unique_ptr Monster = cpp14::make_unique(Profession); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + // TODO: Conversion time + + int AgeableIdx = a_NBT.FindChildByName(a_TagIdx, "Age"); + if (AgeableIdx > 0) + { + int Age; + switch (a_NBT.GetType(AgeableIdx)) + { + case TAG_Byte: Age = static_cast(a_NBT.GetByte(AgeableIdx)); break; + case TAG_Int: Age = a_NBT.GetInt (AgeableIdx); break; + default: Age = 0; break; + } + Monster->SetAge(Age); + } + + a_Entities.emplace_back(std::move(Monster)); +} + + + + + std::pair cWSSAnvil::LoadEntityOwner(const cParsedNBT & a_NBT, int a_TagIdx) { // Load the owner information. OwnerUUID or Owner may be specified, possibly both: diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index e6b3e5ee9..caee4ade2 100755 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -232,6 +232,7 @@ protected: void LoadWolfFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadZombieFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadPigZombieFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadZombieVillagerFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); /** Loads the owner name and UUID from the entity at the specified NBT tag. Returns a pair of {name, uuid}. If the entity is not owned, name is an empty string and uuid is nil. */