From 60bcc06f43e0c249204149153976e534b239d138 Mon Sep 17 00:00:00 2001 From: Mat Date: Sat, 4 Apr 2020 14:44:17 +0300 Subject: [PATCH] Implement wither skeletons (#4563) --- Server/Plugins/APIDump/APIDesc.lua | 13 ++ Server/monsters.ini | 8 + src/BlockType.h | 1 + src/Items/ItemSpawnEgg.h | 53 +++---- src/MobSpawner.cpp | 19 ++- src/Mobs/CMakeLists.txt | 2 + src/Mobs/IncludeAllMonsters.h | 1 + src/Mobs/Monster.cpp | 186 ++++++++++++------------ src/Mobs/MonsterTypes.h | 1 + src/Mobs/Skeleton.cpp | 17 +-- src/Mobs/Skeleton.h | 8 +- src/Mobs/WitherSkeleton.cpp | 62 ++++++++ src/Mobs/WitherSkeleton.h | 28 ++++ src/Protocol/Protocol_1_10.cpp | 17 +-- src/Protocol/Protocol_1_11.cpp | 12 +- src/Protocol/Protocol_1_12.cpp | 13 +- src/Protocol/Protocol_1_13.cpp | 1 + src/Protocol/Protocol_1_8.cpp | 16 +- src/Protocol/Protocol_1_9.cpp | 18 +-- src/World.cpp | 2 +- src/WorldStorage/NBTChunkSerializer.cpp | 70 +++++---- src/WorldStorage/WSSAnvil.cpp | 37 ++++- src/WorldStorage/WSSAnvil.h | 1 + 23 files changed, 355 insertions(+), 231 deletions(-) create mode 100644 src/Mobs/WitherSkeleton.cpp create mode 100644 src/Mobs/WitherSkeleton.h diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index 69e7cd75b..d71a0ccbe 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -9290,6 +9290,10 @@ a_Player:OpenWindow(Window); { Notes = "", }, + mtWitherSkeleton = + { + Notes = "", + }, mtWolf = { Notes = "", @@ -16686,6 +16690,10 @@ end { Notes = "A flag in the metadata of heads that indicates that the head is a zombie head.", }, + E_META_SPAWN_EGG_WITHER_SKELETON = + { + Notes = "" + }, E_META_SPONGE_DRY = { Notes = "A flag in the metadata of sponges that indicates that the sponge is dry.", @@ -16790,6 +16798,10 @@ end { Notes = "Something concerning (i.e. reload) is about to happen", }, + mtWitherSkeleton = + { + Notes = "" + }, hMain = { Notes = "The main hand", @@ -17049,6 +17061,7 @@ end "mtVillager", "mtWitch", "mtWither", + "mtWitherSkeleton", "mtWolf", "mtZombie", "mtZombiePigman", diff --git a/Server/monsters.ini b/Server/monsters.ini index bf1fc95a4..31e5b1479 100644 --- a/Server/monsters.ini +++ b/Server/monsters.ini @@ -190,6 +190,14 @@ AttackRate=1.0 MaxHealth=26 SightDistance=25.0 +[WitherSkeleton] +AttackDamage=5.0 +AttackRange=1.0 +AttackRate=1.0 +IsFireproof=1 +MaxHealth=20 +SightDistance=25.0 + [Wolf] AttackDamage=8.0 AttackRange=1.0 diff --git a/src/BlockType.h b/src/BlockType.h index bd4b0c625..8a70226db 100644 --- a/src/BlockType.h +++ b/src/BlockType.h @@ -1063,6 +1063,7 @@ enum ENUM_ITEM_META : short // See also cMonster::eType, since monster type and spawn egg meta are the same E_META_SPAWN_EGG_PICKUP = 1, E_META_SPAWN_EGG_EXPERIENCE_ORB = 2, + E_META_SPAWN_EGG_WITHER_SKELETON = 5, E_META_SPAWN_EGG_LEASH_KNOT = 8, E_META_SPAWN_EGG_PAINTING = 9, E_META_SPAWN_EGG_ARROW = 10, diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h index 52317a6cb..6d4219236 100644 --- a/src/Items/ItemSpawnEgg.h +++ b/src/Items/ItemSpawnEgg.h @@ -60,32 +60,33 @@ public: { switch (a_ItemDamage) { - case E_META_SPAWN_EGG_BAT: return mtBat; - case E_META_SPAWN_EGG_BLAZE: return mtBlaze; - case E_META_SPAWN_EGG_CAVE_SPIDER: return mtCaveSpider; - case E_META_SPAWN_EGG_CHICKEN: return mtChicken; - case E_META_SPAWN_EGG_COW: return mtCow; - case E_META_SPAWN_EGG_CREEPER: return mtCreeper; - case E_META_SPAWN_EGG_ENDERMAN: return mtEnderman; - case E_META_SPAWN_EGG_GHAST: return mtGhast; - case E_META_SPAWN_EGG_GUARDIAN: return mtGuardian; - case E_META_SPAWN_EGG_HORSE: return mtHorse; - case E_META_SPAWN_EGG_MAGMA_CUBE: return mtMagmaCube; - case E_META_SPAWN_EGG_MOOSHROOM: return mtMooshroom; - case E_META_SPAWN_EGG_OCELOT: return mtOcelot; - case E_META_SPAWN_EGG_PIG: return mtPig; - case E_META_SPAWN_EGG_RABBIT: return mtRabbit; - case E_META_SPAWN_EGG_SHEEP: return mtSheep; - case E_META_SPAWN_EGG_SILVERFISH: return mtSilverfish; - case E_META_SPAWN_EGG_SKELETON: return mtSkeleton; - case E_META_SPAWN_EGG_SLIME: return mtSlime; - case E_META_SPAWN_EGG_SPIDER: return mtSpider; - case E_META_SPAWN_EGG_SQUID: return mtSquid; - case E_META_SPAWN_EGG_VILLAGER: return mtVillager; - case E_META_SPAWN_EGG_WITCH: return mtWitch; - 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_BAT: return mtBat; + case E_META_SPAWN_EGG_BLAZE: return mtBlaze; + case E_META_SPAWN_EGG_CAVE_SPIDER: return mtCaveSpider; + case E_META_SPAWN_EGG_CHICKEN: return mtChicken; + case E_META_SPAWN_EGG_COW: return mtCow; + case E_META_SPAWN_EGG_CREEPER: return mtCreeper; + case E_META_SPAWN_EGG_ENDERMAN: return mtEnderman; + case E_META_SPAWN_EGG_GHAST: return mtGhast; + case E_META_SPAWN_EGG_GUARDIAN: return mtGuardian; + case E_META_SPAWN_EGG_HORSE: return mtHorse; + case E_META_SPAWN_EGG_MAGMA_CUBE: return mtMagmaCube; + case E_META_SPAWN_EGG_MOOSHROOM: return mtMooshroom; + case E_META_SPAWN_EGG_OCELOT: return mtOcelot; + case E_META_SPAWN_EGG_PIG: return mtPig; + case E_META_SPAWN_EGG_RABBIT: return mtRabbit; + case E_META_SPAWN_EGG_SHEEP: return mtSheep; + case E_META_SPAWN_EGG_SILVERFISH: return mtSilverfish; + case E_META_SPAWN_EGG_SKELETON: return mtSkeleton; + case E_META_SPAWN_EGG_SLIME: return mtSlime; + case E_META_SPAWN_EGG_SPIDER: return mtSpider; + case E_META_SPAWN_EGG_SQUID: return mtSquid; + case E_META_SPAWN_EGG_VILLAGER: return mtVillager; + case E_META_SPAWN_EGG_WITCH: return mtWitch; + case E_META_SPAWN_EGG_WITHER_SKELETON: return mtWitherSkeleton; + 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; } return mtInvalidType; } diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp index 73531f2fc..4d18b7358 100644 --- a/src/MobSpawner.cpp +++ b/src/MobSpawner.cpp @@ -287,6 +287,18 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, Vector3i a_RelPos, eMonsterType ); } + case mtWitherSkeleton: + { + return ( + (targetBlock == E_BLOCK_AIR) && + (blockAbove == E_BLOCK_AIR) && + (!cBlockInfo::IsTransparent(blockBelow)) && + (skyLight <= 7) && + (blockLight <= 7) && + (random.RandBool(0.6)) + ); + } + case mtWolf: { return ( @@ -443,6 +455,7 @@ std::set cMobSpawner::GetAllowedMobTypes(EMCSBiome a_Biome) ListOfSpawnables.insert(mtBlaze); ListOfSpawnables.insert(mtGhast); ListOfSpawnables.insert(mtMagmaCube); + ListOfSpawnables.insert(mtWitherSkeleton); ListOfSpawnables.insert(mtZombiePigman); return ListOfSpawnables; @@ -461,7 +474,11 @@ cMonster * cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, Vector3i a_RelPos, EMCS { return nullptr; } - if (m_MobType == mtWolf) + if (m_MobType == mtWitherSkeleton) + { + a_MaxPackSize = 5; + } + else if (m_MobType == mtWolf) { a_MaxPackSize = 8; } diff --git a/src/Mobs/CMakeLists.txt b/src/Mobs/CMakeLists.txt index d9969cfdc..15456595c 100644 --- a/src/Mobs/CMakeLists.txt +++ b/src/Mobs/CMakeLists.txt @@ -33,6 +33,7 @@ SET (SRCS Villager.cpp Witch.cpp Wither.cpp + WitherSkeleton.cpp Wolf.cpp Zombie.cpp ZombiePigman.cpp) @@ -74,6 +75,7 @@ SET (HDRS Villager.h Witch.h Wither.h + WitherSkeleton.h Wolf.h Zombie.h ZombiePigman.h) diff --git a/src/Mobs/IncludeAllMonsters.h b/src/Mobs/IncludeAllMonsters.h index 53c709c2b..17a9dfacd 100644 --- a/src/Mobs/IncludeAllMonsters.h +++ b/src/Mobs/IncludeAllMonsters.h @@ -26,6 +26,7 @@ #include "Villager.h" #include "Witch.h" #include "Wither.h" +#include "WitherSkeleton.h" #include "Wolf.h" #include "Zombie.h" #include "ZombiePigman.h" diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 5a01663e5..b56390b6f 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -37,37 +37,38 @@ static const struct const char * m_VanillaNameNBT; } g_MobTypeNames[] = { - {mtBat, "bat", "Bat", "bat"}, - {mtBlaze, "blaze", "Blaze", "blaze"}, - {mtCaveSpider, "cavespider", "CaveSpider", "cave_spider"}, - {mtChicken, "chicken", "Chicken", "chicken"}, - {mtCow, "cow", "Cow", "cow"}, - {mtCreeper, "creeper", "Creeper", "creeper"}, - {mtEnderman, "enderman", "Enderman", "enderman"}, - {mtEnderDragon, "enderdragon", "EnderDragon", "ender_dragon"}, - {mtGhast, "ghast", "Ghast", "ghast"}, - {mtGiant, "giant", "Giant", "giant"}, - {mtGuardian, "guardian", "Guardian", "guardian"}, - {mtHorse, "horse", "EntityHorse", "horse"}, - {mtIronGolem, "irongolem", "VillagerGolem", "iron_golem"}, - {mtMagmaCube, "magmacube", "LavaSlime", "magma_cube"}, - {mtMooshroom, "mooshroom", "MushroomCow", "mooshroom"}, - {mtOcelot, "ocelot", "Ozelot", "ocelot"}, - {mtPig, "pig", "Pig", "pig"}, - {mtRabbit, "rabbit", "Rabbit", "rabbit"}, - {mtSheep, "sheep", "Sheep", "sheep"}, - {mtSilverfish, "silverfish", "Silverfish", "silverfish"}, - {mtSkeleton, "skeleton", "Skeleton", "skeleton"}, - {mtSlime, "slime", "Slime", "slime"}, - {mtSnowGolem, "snowgolem", "SnowMan", "snow_golem"}, - {mtSpider, "spider", "Spider", "spider"}, - {mtSquid, "squid", "Squid", "squid"}, - {mtVillager, "villager", "Villager", "villager"}, - {mtWitch, "witch", "Witch", "witch"}, - {mtWither, "wither", "WitherBoss", "wither"}, - {mtWolf, "wolf", "Wolf", "wolf"}, - {mtZombie, "zombie", "Zombie", "zombie"}, - {mtZombiePigman, "zombiepigman", "PigZombie", "zombie_pigman"}, + {mtBat, "bat", "Bat", "bat"}, + {mtBlaze, "blaze", "Blaze", "blaze"}, + {mtCaveSpider, "cavespider", "CaveSpider", "cave_spider"}, + {mtChicken, "chicken", "Chicken", "chicken"}, + {mtCow, "cow", "Cow", "cow"}, + {mtCreeper, "creeper", "Creeper", "creeper"}, + {mtEnderman, "enderman", "Enderman", "enderman"}, + {mtEnderDragon, "enderdragon", "EnderDragon", "ender_dragon"}, + {mtGhast, "ghast", "Ghast", "ghast"}, + {mtGiant, "giant", "Giant", "giant"}, + {mtGuardian, "guardian", "Guardian", "guardian"}, + {mtHorse, "horse", "EntityHorse", "horse"}, + {mtIronGolem, "irongolem", "VillagerGolem", "iron_golem"}, + {mtMagmaCube, "magmacube", "LavaSlime", "magma_cube"}, + {mtMooshroom, "mooshroom", "MushroomCow", "mooshroom"}, + {mtOcelot, "ocelot", "Ozelot", "ocelot"}, + {mtPig, "pig", "Pig", "pig"}, + {mtRabbit, "rabbit", "Rabbit", "rabbit"}, + {mtSheep, "sheep", "Sheep", "sheep"}, + {mtSilverfish, "silverfish", "Silverfish", "silverfish"}, + {mtSkeleton, "skeleton", "Skeleton", "skeleton"}, + {mtSlime, "slime", "Slime", "slime"}, + {mtSnowGolem, "snowgolem", "SnowMan", "snow_golem"}, + {mtSpider, "spider", "Spider", "spider"}, + {mtSquid, "squid", "Squid", "squid"}, + {mtVillager, "villager", "Villager", "villager"}, + {mtWitch, "witch", "Witch", "witch"}, + {mtWither, "wither", "WitherBoss", "wither"}, + {mtWitherSkeleton, "witherskeleton", "WitherSkeleton", "wither_skeleton"}, + {mtWolf, "wolf", "Wolf", "wolf"}, + {mtZombie, "zombie", "Zombie", "zombie"}, + {mtZombiePigman, "zombiepigman", "PigZombie", "zombie_pigman"}, } ; @@ -658,6 +659,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) case mtSkeleton: case mtSpider: case mtWitch: + case mtWitherSkeleton: case mtZombie: case mtZombiePigman: case mtSlime: @@ -1044,37 +1046,38 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) switch (a_Type) { - case mtBat: return mfAmbient; - case mtBlaze: return mfHostile; - case mtCaveSpider: return mfHostile; - case mtChicken: return mfPassive; - case mtCow: return mfPassive; - case mtCreeper: return mfHostile; - case mtEnderDragon: return mfNoSpawn; - case mtEnderman: return mfHostile; - case mtGhast: return mfHostile; - case mtGiant: return mfNoSpawn; - case mtGuardian: return mfWater; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited! - case mtHorse: return mfPassive; - case mtIronGolem: return mfPassive; - case mtMagmaCube: return mfHostile; - case mtMooshroom: return mfHostile; - case mtOcelot: return mfPassive; - case mtPig: return mfPassive; - case mtRabbit: return mfPassive; - case mtSheep: return mfPassive; - case mtSilverfish: return mfHostile; - case mtSkeleton: return mfHostile; - case mtSlime: return mfHostile; - case mtSnowGolem: return mfNoSpawn; - case mtSpider: return mfHostile; - case mtSquid: return mfWater; - case mtVillager: return mfPassive; - case mtWitch: return mfHostile; - case mtWither: return mfNoSpawn; - case mtWolf: return mfHostile; - case mtZombie: return mfHostile; - case mtZombiePigman: return mfHostile; + case mtBat: return mfAmbient; + case mtBlaze: return mfHostile; + case mtCaveSpider: return mfHostile; + case mtChicken: return mfPassive; + case mtCow: return mfPassive; + case mtCreeper: return mfHostile; + case mtEnderDragon: return mfNoSpawn; + case mtEnderman: return mfHostile; + case mtGhast: return mfHostile; + case mtGiant: return mfNoSpawn; + case mtGuardian: return mfWater; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited! + case mtHorse: return mfPassive; + case mtIronGolem: return mfPassive; + case mtMagmaCube: return mfHostile; + case mtMooshroom: return mfHostile; + case mtOcelot: return mfPassive; + case mtPig: return mfPassive; + case mtRabbit: return mfPassive; + case mtSheep: return mfPassive; + case mtSilverfish: return mfHostile; + case mtSkeleton: return mfHostile; + case mtSlime: return mfHostile; + case mtSnowGolem: return mfNoSpawn; + case mtSpider: return mfHostile; + case mtSquid: return mfWater; + case mtVillager: return mfPassive; + case mtWitch: return mfHostile; + case mtWither: return mfNoSpawn; + case mtWitherSkeleton: return mfHostile; + case mtWolf: return mfHostile; + case mtZombie: return mfHostile; + case mtZombiePigman: return mfHostile; default: { @@ -1173,11 +1176,6 @@ std::unique_ptr cMonster::NewMonsterFromType(eMonsterType a_MobType) { return cpp14::make_unique(1 << Random.RandInt(2)); // Size 1, 2 or 4 } - case mtSkeleton: - { - // TODO: Actual detection of spawning in Nether - return cpp14::make_unique(false); - } case mtVillager: { int VillagerType = Random.RandInt(6); @@ -1206,32 +1204,34 @@ std::unique_ptr cMonster::NewMonsterFromType(eMonsterType a_MobType) return cpp14::make_unique(HorseType, HorseColor, HorseStyle, HorseTameTimes); } - case mtBat: return cpp14::make_unique(); - case mtBlaze: return cpp14::make_unique(); - case mtCaveSpider: return cpp14::make_unique(); - case mtChicken: return cpp14::make_unique(); - case mtCow: return cpp14::make_unique(); - case mtCreeper: return cpp14::make_unique < cCreeper>(); - case mtEnderDragon: return cpp14::make_unique(); - case mtEnderman: return cpp14::make_unique(); - case mtGhast: return cpp14::make_unique(); - case mtGiant: return cpp14::make_unique(); - case mtGuardian: return cpp14::make_unique(); - case mtIronGolem: return cpp14::make_unique(); - case mtMooshroom: return cpp14::make_unique(); - case mtOcelot: return cpp14::make_unique(); - case mtPig: return cpp14::make_unique(); - case mtRabbit: return cpp14::make_unique(); - case mtSheep: return cpp14::make_unique(); - case mtSilverfish: return cpp14::make_unique(); - case mtSnowGolem: return cpp14::make_unique(); - case mtSpider: return cpp14::make_unique(); - case mtSquid: return cpp14::make_unique(); - case mtWitch: return cpp14::make_unique(); - case mtWither: return cpp14::make_unique(); - case mtWolf: return cpp14::make_unique(); - case mtZombie: return cpp14::make_unique(false); // TODO: Infected zombie parameter - case mtZombiePigman: return cpp14::make_unique(); + case mtBat: return cpp14::make_unique(); + case mtBlaze: return cpp14::make_unique(); + case mtCaveSpider: return cpp14::make_unique(); + case mtChicken: return cpp14::make_unique(); + case mtCow: return cpp14::make_unique(); + case mtCreeper: return cpp14::make_unique(); + case mtEnderDragon: return cpp14::make_unique(); + case mtEnderman: return cpp14::make_unique(); + case mtGhast: return cpp14::make_unique(); + case mtGiant: return cpp14::make_unique(); + case mtGuardian: return cpp14::make_unique(); + case mtIronGolem: return cpp14::make_unique(); + case mtMooshroom: return cpp14::make_unique(); + case mtOcelot: return cpp14::make_unique(); + case mtPig: return cpp14::make_unique(); + case mtRabbit: return cpp14::make_unique(); + case mtSheep: return cpp14::make_unique(); + case mtSilverfish: return cpp14::make_unique(); + case mtSkeleton: return cpp14::make_unique(); + case mtSnowGolem: return cpp14::make_unique(); + case mtSpider: return cpp14::make_unique(); + case mtSquid: return cpp14::make_unique(); + case mtWitch: return cpp14::make_unique(); + case mtWither: return cpp14::make_unique(); + case mtWitherSkeleton: return cpp14::make_unique(); + case mtWolf: return cpp14::make_unique(); + case mtZombie: return cpp14::make_unique(false); // TODO: Infected zombie parameter + case mtZombiePigman: return cpp14::make_unique(); default: { ASSERT(!"Unhandled mob type whilst trying to spawn mob!"); diff --git a/src/Mobs/MonsterTypes.h b/src/Mobs/MonsterTypes.h index 6aaa61dc6..7a864fda4 100644 --- a/src/Mobs/MonsterTypes.h +++ b/src/Mobs/MonsterTypes.h @@ -39,6 +39,7 @@ enum eMonsterType mtVillager, mtWitch, mtWither, + mtWitherSkeleton, mtWolf, mtZombie, mtZombiePigman, diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index e8a345d7f..e58bb9384 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -9,9 +9,8 @@ -cSkeleton::cSkeleton(bool IsWither) : - super("Skeleton", mtSkeleton, "entity.skeleton.hurt", "entity.skeleton.death", "entity.skeleton.ambient", 0.6, 1.8), - m_bIsWither(IsWither) +cSkeleton::cSkeleton(void) : + super("Skeleton", mtSkeleton, "entity.skeleton.hurt", "entity.skeleton.death", "entity.skeleton.ambient", 0.6, 1.8) { } @@ -26,18 +25,8 @@ void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer) { LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); } - if (IsWither()) - { - AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_COAL); - cItems RareDrops; - RareDrops.Add(cItem(E_ITEM_HEAD, 1, 1)); - AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); - } - else - { - AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ARROW); + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ARROW); - } AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_BONE); AddRandomArmorDropItem(a_Drops, LootingLevel); AddRandomWeaponDropItem(a_Drops, LootingLevel); diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h index 0316fb9b5..cb7dec5ad 100644 --- a/src/Mobs/Skeleton.h +++ b/src/Mobs/Skeleton.h @@ -13,7 +13,7 @@ class cSkeleton : typedef cAggressiveMonster super; public: - cSkeleton(bool IsWither); + cSkeleton(void); CLASS_PROTODEF(cSkeleton) @@ -23,12 +23,6 @@ public: virtual bool IsUndead(void) override { return true; } - bool IsWither(void) const { return m_bIsWither; } - -private: - - bool m_bIsWither; - } ; diff --git a/src/Mobs/WitherSkeleton.cpp b/src/Mobs/WitherSkeleton.cpp new file mode 100644 index 000000000..7dd8d5017 --- /dev/null +++ b/src/Mobs/WitherSkeleton.cpp @@ -0,0 +1,62 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "WitherSkeleton.h" +#include "../World.h" +#include "../ClientHandle.h" + + + + +cWitherSkeleton::cWitherSkeleton(void) : + super("WitherSkeleton", mtWitherSkeleton, "entity.wither_skeleton.hurt", "entity.wither_skeleton.death", "entity.wither_skeleton.ambient", 0.7, 2.4) +{ +} + + + + + +bool cWitherSkeleton::Attack(std::chrono::milliseconds a_Dt) +{ + if (GetTarget() == nullptr) + { + return false; + } + + GetTarget()->AddEntityEffect(cEntityEffect::effWither, 200, 0); + return super::Attack(a_Dt); +} + + + + + +void cWitherSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + unsigned int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_COAL); + AddRandomUncommonDropItem(a_Drops, 8.5f, E_ITEM_STONE_SWORD, GetRandomProvider().RandInt(50)); + + cItems RareDrops; + RareDrops.Add(cItem(E_ITEM_HEAD, 1, 1)); + AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); + + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_BONE); + AddRandomArmorDropItem(a_Drops, LootingLevel); + AddRandomWeaponDropItem(a_Drops, LootingLevel); +} + + + + + +void cWitherSkeleton::SpawnOn(cClientHandle & a_ClientHandle) +{ + super::SpawnOn(a_ClientHandle); + a_ClientHandle.SendEntityEquipment(*this, 0, cItem(E_ITEM_STONE_SWORD)); +} diff --git a/src/Mobs/WitherSkeleton.h b/src/Mobs/WitherSkeleton.h new file mode 100644 index 000000000..c77d06822 --- /dev/null +++ b/src/Mobs/WitherSkeleton.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cWitherSkeleton : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cWitherSkeleton(void); + + CLASS_PROTODEF(cWitherSkeleton) + + virtual bool Attack(std::chrono::milliseconds a_Dt) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + +} ; + + + + diff --git a/src/Protocol/Protocol_1_10.cpp b/src/Protocol/Protocol_1_10.cpp index 38abdb53b..47d27c461 100644 --- a/src/Protocol/Protocol_1_10.cpp +++ b/src/Protocol/Protocol_1_10.cpp @@ -889,15 +889,6 @@ void cProtocol_1_10_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_ break; } // case mtSheep - case mtSkeleton: - { - auto & Skeleton = static_cast(a_Mob); - a_Pkt.WriteBEUInt8(SKELETON_TYPE); - a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); - a_Pkt.WriteVarInt32(Skeleton.IsWither() ? 1 : 0); - break; - } // case mtSkeleton - case mtSlime: { auto & Slime = static_cast(a_Mob); @@ -940,6 +931,14 @@ void cProtocol_1_10_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_ break; } // case mtWither + case mtWitherSkeleton: + { + a_Pkt.WriteBEUInt8(SKELETON_TYPE); + a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); + a_Pkt.WriteVarInt32(1); // Is wither skeleton + break; + } // case mtWitherSkeleton + case mtWolf: { auto & Wolf = static_cast(a_Mob); diff --git a/src/Protocol/Protocol_1_11.cpp b/src/Protocol/Protocol_1_11.cpp index 1346d64cf..8a846b540 100644 --- a/src/Protocol/Protocol_1_11.cpp +++ b/src/Protocol/Protocol_1_11.cpp @@ -573,6 +573,7 @@ UInt32 cProtocol_1_11_0::GetProtocolMobType(eMonsterType a_MobType) case mtVillager: return 120; case mtWitch: return 66; case mtWither: return 64; + case mtWitherSkeleton: return 5; case mtWolf: return 95; case mtZombie: return 54; case mtZombiePigman: return 57; @@ -1094,17 +1095,6 @@ void cProtocol_1_11_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_ break; } // case mtSheep - case mtSkeleton: - { - // XXX Skeletons are separate entities; all skeletons are currently treated as regular ones - - // auto & Skeleton = static_cast(a_Mob); - // a_Pkt.WriteBEUInt8(SKELETON_TYPE); - // a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); - // a_Pkt.WriteVarInt32(Skeleton.IsWither() ? 1 : 0); - break; - } // case mtSkeleton - case mtSlime: { auto & Slime = static_cast(a_Mob); diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp index b67a244d6..ee2d7064c 100644 --- a/src/Protocol/Protocol_1_12.cpp +++ b/src/Protocol/Protocol_1_12.cpp @@ -815,17 +815,6 @@ void cProtocol_1_12::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mo break; } // case mtSheep - case mtSkeleton: - { - // XXX Skeletons are separate entities; all skeletons are currently treated as regular ones - - // auto & Skeleton = static_cast(a_Mob); - // a_Pkt.WriteBEUInt8(SKELETON_TYPE); - // a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); - // a_Pkt.WriteVarInt32(Skeleton.IsWither() ? 1 : 0); - break; - } // case mtSkeleton - case mtSlime: { auto & Slime = static_cast(a_Mob); @@ -954,7 +943,9 @@ void cProtocol_1_12::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mo case mtGiant: case mtSilverfish: + case mtSkeleton: case mtSquid: + case mtWitherSkeleton: { // Mobs with no extra fields break; diff --git a/src/Protocol/Protocol_1_13.cpp b/src/Protocol/Protocol_1_13.cpp index 5aa23e763..70375795f 100644 --- a/src/Protocol/Protocol_1_13.cpp +++ b/src/Protocol/Protocol_1_13.cpp @@ -430,6 +430,7 @@ UInt32 cProtocol_1_13::GetProtocolMobType(eMonsterType a_MobType) case mtVillager: return 79; case mtWitch: return 82; case mtWither: return 83; + case mtWitherSkeleton: return 84; case mtWolf: return 86; case mtZombie: return 87; case mtZombiePigman: return 53; diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index 9509fe51e..668ca7542 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -1862,6 +1862,7 @@ UInt32 cProtocol_1_8_0::GetProtocolMobType(eMonsterType a_MobType) case mtVillager: return 120; case mtWitch: return 66; case mtWither: return 64; + case mtWitherSkeleton: return 51; case mtWolf: return 95; case mtZombie: return 54; case mtZombiePigman: return 57; @@ -3714,14 +3715,6 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M break; } // case mtRabbit - case mtSkeleton: - { - auto & Skeleton = static_cast(a_Mob); - a_Pkt.WriteBEUInt8(0x0d); - a_Pkt.WriteBEUInt8(Skeleton.IsWither() ? 1 : 0); - break; - } // case mtSkeleton - case mtSlime: { auto & Slime = static_cast(a_Mob); @@ -3758,6 +3751,13 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M break; } // case mtWither + case mtWitherSkeleton: + { + a_Pkt.WriteBEUInt8(0x0d); + a_Pkt.WriteBEUInt8(1); // Is wither skeleton + break; + } // case mtWitherSkeleton + case mtWolf: { auto & Wolf = static_cast(a_Mob); diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index dbcbce18c..b6fa567fd 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -1918,6 +1918,7 @@ UInt32 cProtocol_1_9_0::GetProtocolMobType(eMonsterType a_MobType) case mtVillager: return 120; case mtWitch: return 66; case mtWither: return 64; + case mtWitherSkeleton: return 51; case mtWolf: return 95; case mtZombie: return 54; case mtZombiePigman: return 57; @@ -4201,15 +4202,6 @@ void cProtocol_1_9_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M break; } // case mtSheep - case mtSkeleton: - { - auto & Skeleton = static_cast(a_Mob); - a_Pkt.WriteBEUInt8(11); // Index 11: Type - a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); - a_Pkt.WriteVarInt32(Skeleton.IsWither() ? 1 : 0); - break; - } // case mtSkeleton - case mtSlime: { auto & Slime = static_cast(a_Mob); @@ -4252,6 +4244,14 @@ void cProtocol_1_9_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M break; } // case mtWither + case mtWitherSkeleton: + { + a_Pkt.WriteBEUInt8(11); // Index 11: Type + a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT); + a_Pkt.WriteVarInt32(1); // Is wither skeleton + break; + } // case mtWitherSkeleton + case mtWolf: { auto & Wolf = static_cast(a_Mob); diff --git a/src/World.cpp b/src/World.cpp index 464d84be5..10cfc2047 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -895,7 +895,7 @@ void cWorld::InitializeAndLoadMobSpawningValues(cIniFile & a_IniFile) switch (m_Dimension) { case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, guardian, horse, mooshroom, ocelot, pig, rabbit, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break; // TODO Re-add Enderman when bugs are fixed - case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombiepigman"; break; + case dimNether: DefaultMonsters = "blaze, ghast, magmacube, witherskeleton, zombiepigman"; break; case dimEnd: DefaultMonsters = ""; break; // TODO Re-add Enderman when bugs are fixed case dimNotSet: ASSERT(!"Dimension not set"); break; } diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 014ee2c27..3e8994786 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -710,37 +710,38 @@ public: const char * EntityClass = nullptr; switch (a_Monster->GetMobType()) { - case mtBat: EntityClass = "Bat"; break; - case mtBlaze: EntityClass = "Blaze"; break; - case mtCaveSpider: EntityClass = "CaveSpider"; break; - case mtChicken: EntityClass = "Chicken"; break; - case mtCow: EntityClass = "Cow"; break; - case mtCreeper: EntityClass = "Creeper"; break; - case mtEnderDragon: EntityClass = "EnderDragon"; break; - case mtEnderman: EntityClass = "Enderman"; break; - case mtGhast: EntityClass = "Ghast"; break; - case mtGiant: EntityClass = "Giant"; break; - case mtGuardian: EntityClass = "Guardian"; break; - case mtHorse: EntityClass = "Horse"; break; - case mtIronGolem: EntityClass = "VillagerGolem"; break; - case mtMagmaCube: EntityClass = "LavaSlime"; break; - case mtMooshroom: EntityClass = "MushroomCow"; break; - case mtOcelot: EntityClass = "Ozelot"; break; - case mtPig: EntityClass = "Pig"; break; - case mtRabbit: EntityClass = "Rabbit"; break; - case mtSheep: EntityClass = "Sheep"; break; - case mtSilverfish: EntityClass = "Silverfish"; break; - case mtSkeleton: EntityClass = "Skeleton"; break; - case mtSlime: EntityClass = "Slime"; break; - case mtSnowGolem: EntityClass = "SnowMan"; break; - case mtSpider: EntityClass = "Spider"; break; - case mtSquid: EntityClass = "Squid"; break; - case mtVillager: EntityClass = "Villager"; break; - case mtWitch: EntityClass = "Witch"; break; - case mtWither: EntityClass = "WitherBoss"; break; - case mtWolf: EntityClass = "Wolf"; break; - case mtZombie: EntityClass = "Zombie"; break; - case mtZombiePigman: EntityClass = "PigZombie"; break; + case mtBat: EntityClass = "Bat"; break; + case mtBlaze: EntityClass = "Blaze"; break; + case mtCaveSpider: EntityClass = "CaveSpider"; break; + case mtChicken: EntityClass = "Chicken"; break; + case mtCow: EntityClass = "Cow"; break; + case mtCreeper: EntityClass = "Creeper"; break; + case mtEnderDragon: EntityClass = "EnderDragon"; break; + case mtEnderman: EntityClass = "Enderman"; break; + case mtGhast: EntityClass = "Ghast"; break; + case mtGiant: EntityClass = "Giant"; break; + case mtGuardian: EntityClass = "Guardian"; break; + case mtHorse: EntityClass = "Horse"; break; + case mtIronGolem: EntityClass = "VillagerGolem"; break; + case mtMagmaCube: EntityClass = "LavaSlime"; break; + case mtMooshroom: EntityClass = "MushroomCow"; break; + case mtOcelot: EntityClass = "Ozelot"; break; + case mtPig: EntityClass = "Pig"; break; + case mtRabbit: EntityClass = "Rabbit"; break; + case mtSheep: EntityClass = "Sheep"; break; + case mtSilverfish: EntityClass = "Silverfish"; break; + case mtSkeleton: EntityClass = "Skeleton"; break; + case mtSlime: EntityClass = "Slime"; break; + case mtSnowGolem: EntityClass = "SnowMan"; break; + case mtSpider: EntityClass = "Spider"; break; + case mtSquid: EntityClass = "Squid"; break; + case mtVillager: EntityClass = "Villager"; break; + case mtWitch: EntityClass = "Witch"; break; + case mtWither: EntityClass = "WitherBoss"; break; + case mtWitherSkeleton: EntityClass = "WitherSkeleton"; break; + case mtWolf: EntityClass = "Wolf"; break; + case mtZombie: EntityClass = "Zombie"; break; + case mtZombiePigman: EntityClass = "PigZombie"; break; default: { ASSERT(!"Unhandled monster type"); @@ -869,11 +870,6 @@ public: mWriter.AddInt("Size", static_cast(a_Monster)->GetSize()); break; } - case mtSkeleton: - { - mWriter.AddByte("SkeletonType", (static_cast(a_Monster)->IsWither() ? 1 : 0)); - break; - } case mtVillager: { const cVillager *Villager = static_cast(a_Monster); @@ -927,10 +923,12 @@ public: case mtIronGolem: case mtMooshroom: case mtSilverfish: + case mtSkeleton: case mtSnowGolem: case mtSpider: case mtSquid: case mtWitch: + case mtWitherSkeleton: { // Other mobs have no special tags. break; diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 65facd817..31934e181 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -1560,6 +1560,8 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a { "minecraft:witch", &cWSSAnvil::LoadWitchFromNBT }, { "WitherBoss", &cWSSAnvil::LoadWitherFromNBT }, { "minecraft:wither", &cWSSAnvil::LoadWitherFromNBT }, + { "WitherSkeleton", &cWSSAnvil::LoadWitherSkeletonFromNBT }, + { "minecraft:wither_skeleton", &cWSSAnvil::LoadWitherSkeletonFromNBT }, { "Wolf", &cWSSAnvil::LoadWolfFromNBT }, { "minecraft:wolf", &cWSSAnvil::LoadWolfFromNBT }, { "Zombie", &cWSSAnvil::LoadZombieFromNBT }, @@ -2660,15 +2662,20 @@ void cWSSAnvil::LoadSilverfishFromNBT(cEntityList & a_Entities, const cParsedNBT void cWSSAnvil::LoadSkeletonFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { + // Wither skeleton is a separate mob in Minecraft 1.11+, but we need this to + // load them from older worlds where wither skeletons were only a skeleton with a flag int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "SkeletonType"); - if (TypeIdx < 0) + + std::unique_ptr Monster; + if ((TypeIdx > 0) && (a_NBT.GetByte(TypeIdx) == 1)) { - return; + Monster = cpp14::make_unique(); + } + else + { + Monster = cpp14::make_unique(); } - bool Type = ((a_NBT.GetByte(TypeIdx) == 1) ? true : false); - - std::unique_ptr Monster = cpp14::make_unique(Type); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2863,6 +2870,26 @@ void cWSSAnvil::LoadWitherFromNBT(cEntityList & a_Entities, const cParsedNBT & a +void cWSSAnvil::LoadWitherSkeletonFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + auto Monster = cpp14::make_unique(); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.emplace_back(std::move(Monster)); +} + + + + + void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { std::unique_ptr Monster = cpp14::make_unique(); diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 949ff61ff..e6b3e5ee9 100755 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -228,6 +228,7 @@ protected: void LoadVillagerFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadWitchFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadWitherFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadWitherSkeletonFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); 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);