diff --git a/.gitignore b/.gitignore index 409421c85..d666f5b29 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,8 @@ GPUCache *.cbp ## KDevelop *.kdev* +## Vim +.cache/ # world inside source ChunkWorx.ini diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index 89e9955a9..551688c7b 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -17849,6 +17849,10 @@ end { Notes = "A mask that indicates the bits of the metadata that specify the facing of redstone repeaters.", }, + E_META_SPAWN_EGG_ENDERMITE = + { + Notes = "", + }, E_META_SPAWN_EGG_WITHER_SKELETON = { Notes = "" diff --git a/Server/monsters.ini b/Server/monsters.ini index fb8f3babc..3d6028ed8 100644 --- a/Server/monsters.ini +++ b/Server/monsters.ini @@ -48,6 +48,13 @@ AttackRate=1.0 MaxHealth=200 SightDistance=25.0 +[Endermite] +AttackDamage=2.0 +AttackRange=0.75 +AttackRate=1.0 +MaxHealth=8 +SightDistance=16.0 + [Enderman] AttackDamage=4.0 AttackRange=1.0 diff --git a/src/BlockType.h b/src/BlockType.h index a8d382907..f570f02e1 100644 --- a/src/BlockType.h +++ b/src/BlockType.h @@ -1146,6 +1146,7 @@ enum ENUM_ITEM_META : short E_META_SPAWN_EGG_WITHER = 64, E_META_SPAWN_EGG_BAT = 65, E_META_SPAWN_EGG_WITCH = 66, + E_META_SPAWN_EGG_ENDERMITE = 67, E_META_SPAWN_EGG_GUARDIAN = 68, E_META_SPAWN_EGG_PIG = 90, E_META_SPAWN_EGG_SHEEP = 91, diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 2fbb62d03..7d7676553 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -510,6 +510,7 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) case mtSpider: case mtCaveSpider: case mtSilverfish: + case mtEndermite: { MagicalCriticalHit = true; a_TDI.FinalDamage += 2.5f * BaneOfArthropodsLevel; diff --git a/src/Entities/ThrownEnderPearlEntity.cpp b/src/Entities/ThrownEnderPearlEntity.cpp index d01245b83..d1da8acfd 100644 --- a/src/Entities/ThrownEnderPearlEntity.cpp +++ b/src/Entities/ThrownEnderPearlEntity.cpp @@ -17,6 +17,8 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "FastRandom.h" +#include "Mobs/MonsterTypes.h" #include "ThrownEnderPearlEntity.h" #include "Player.h" @@ -72,11 +74,25 @@ void cThrownEnderPearlEntity::TeleportCreator(Vector3d a_HitPos) return; } + + GetWorld()->FindAndDoWithPlayer(m_CreatorData.m_Name, [=](cPlayer & a_Entity) { + + auto & Random = GetRandomProvider(); + + // 5% chance to spawn an endermite + if (Random.RandBool(0.05)) + { + Vector3d PlayerPosition = a_Entity.GetPosition(); + m_World->SpawnMob(PlayerPosition.x, PlayerPosition.y, PlayerPosition.z, mtEndermite); + } + + // Teleport the creator here, make them take 5 damage: a_Entity.TeleportToCoords(a_HitPos.x, a_HitPos.y + 0.2, a_HitPos.z); a_Entity.TakeDamage(dtEnderPearl, this, 5, 0); + return false; }); } diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h index b54e38343..80e9ccb97 100644 --- a/src/Items/ItemSpawnEgg.h +++ b/src/Items/ItemSpawnEgg.h @@ -88,6 +88,7 @@ public: 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_ENDERMITE: return mtEndermite; case E_META_SPAWN_EGG_GHAST: return mtGhast; case E_META_SPAWN_EGG_GUARDIAN: return mtGuardian; case E_META_SPAWN_EGG_HORSE: return mtHorse; diff --git a/src/Items/ItemThrowable.h b/src/Items/ItemThrowable.h index 4915a0ef3..2b18b2d14 100644 --- a/src/Items/ItemThrowable.h +++ b/src/Items/ItemThrowable.h @@ -19,6 +19,9 @@ #pragma once +#include "ItemHandler.h" +#include "Entities/ProjectileEntity.h" + diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index 95a14d2e2..746c78135 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -19,9 +19,9 @@ #include "AggressiveMonster.h" -#include "../World.h" -#include "../Entities/Player.h" -#include "../LineBlockTracer.h" +#include "LineBlockTracer.h" +#include "World.h" +#include "Entities/Player.h" @@ -62,6 +62,61 @@ void cAggressiveMonster::EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk) +cMonster * cAggressiveMonster::GetMonsterOfTypeInSight(eMonsterType a_MobType, unsigned int a_SightDistance) +{ + + cMonster * FoundTarget = nullptr; + auto MinimumDistance = static_cast(a_SightDistance * a_SightDistance); + + class cCallback : public cBlockTracer::cCallbacks + { + public: + bool OnNextBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_EntryFace) override + { + return a_BlockType != E_BLOCK_AIR; + } + }; + + auto Callbacks = cCallback(); + auto Tracer = cLineBlockTracer(*GetWorld(), Callbacks); + + cEntityCallback Callback = [&](cEntity & a_Entity) + { + if (!a_Entity.IsMob()) + { + return false; + } + + auto & Other = dynamic_cast(a_Entity); + if (Other.GetMobType() != a_MobType) + { + return false; + } + + Vector3d MyHeadPosition = GetPosition().addedY(GetHeight()); + Vector3d TargetPosition = Other.GetPosition().addedY(Other.GetHeight()); + double TargetDistance = (MyHeadPosition - TargetPosition).SqrLength(); + + if ( + (MinimumDistance > TargetDistance) && + (TargetDistance < (a_SightDistance * a_SightDistance)) + ) + { + FoundTarget = & Other; + return true; + } + return false; + }; + + cBoundingBox CheckZone(GetPosition().addedXZ(-a_SightDistance, -a_SightDistance), GetPosition().addedXZ(a_SightDistance, a_SightDistance)); + m_World->ForEachEntityInBox(CheckZone, Callback); + return FoundTarget; +} + + + + + void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { Super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h index eb147c47e..8d507f9fe 100644 --- a/src/Mobs/AggressiveMonster.h +++ b/src/Mobs/AggressiveMonster.h @@ -46,12 +46,18 @@ public: virtual void EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk) override; + /** + * Check if a monster of certain type is in sight + * + * @param a_mobtype the mob type to find + * @param SightDistance max distance to check + * + * @return pointer to the mob found + */ + cMonster * GetMonsterOfTypeInSight(eMonsterType a_mobtype, unsigned int SightDistance=16); + /** Try to perform attack returns true if attack was deemed successful (hit player, fired projectile, creeper exploded, etc.) even if it didn't actually do damage return false if e.g. the mob is still in cooldown from a previous attack */ virtual bool Attack(std::chrono::milliseconds a_Dt); } ; - - - - diff --git a/src/Mobs/CMakeLists.txt b/src/Mobs/CMakeLists.txt index 932b90b29..010b39afa 100644 --- a/src/Mobs/CMakeLists.txt +++ b/src/Mobs/CMakeLists.txt @@ -9,6 +9,7 @@ target_sources( Cow.cpp Creeper.cpp EnderDragon.cpp + Endermite.cpp Enderman.cpp Ghast.cpp Giant.cpp @@ -49,6 +50,7 @@ target_sources( Cow.h Creeper.h EnderDragon.h + Endermite.h Enderman.h Ghast.h Giant.h diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp index 3bfcdb387..2564977a9 100644 --- a/src/Mobs/Enderman.cpp +++ b/src/Mobs/Enderman.cpp @@ -165,6 +165,29 @@ void cEnderman::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) return; } + if (m_EMState != CHASING) + { + cMonster * EndermiteFound = GetMonsterOfTypeInSight(mtEndermite, 64); + if (EndermiteFound != nullptr) + { + SetTarget(EndermiteFound); + m_EMState = CHASING; + m_bIsScreaming = true; + } + } + else + { + const auto Target = GetTarget(); + if (Target != nullptr) + { + if (!Target->IsTicking()) + { + m_EMState = IDLE; + m_bIsScreaming = false; + } + } + } + PREPARE_REL_AND_CHUNK(GetPosition().Floor(), a_Chunk); if (!RelSuccess) { diff --git a/src/Mobs/Endermite.cpp b/src/Mobs/Endermite.cpp new file mode 100644 index 000000000..3a89de98a --- /dev/null +++ b/src/Mobs/Endermite.cpp @@ -0,0 +1,41 @@ + +#include "Globals.h" + +#include "Endermite.h" + +#include "../World.h" +#include "../Chunk.h" +#include "../Blocks/BlockHandler.h" +#include "../Blocks/BlockInfested.h" + + + + + +cEndermite::cEndermite() : + Super("Endermite", mtEndermite, "entity.endermite.hurt", "entity.endermite.death", "entity.endermite.ambient", 0.4f, 0.3f), + m_Timer(0), + m_Lifetime(2 * 1000 * 60) // 2 minutes (2 * 1000 (mili to sec) * 60 (sec to min) * 2 because tick = 0.5 sec) +{ +} + + + + + +void cEndermite::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + Super::Tick(a_Dt, a_Chunk); + + // Not destroying the endermite if a name is set + if (m_CustomName.empty()) + { + m_Timer += a_Dt; + // Destroy the endermite after 2 minutes + if (m_Timer > m_Lifetime) + { + Destroy(); + } + + } +} diff --git a/src/Mobs/Endermite.h b/src/Mobs/Endermite.h new file mode 100644 index 000000000..9e0102a07 --- /dev/null +++ b/src/Mobs/Endermite.h @@ -0,0 +1,24 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + +class cEndermite: + public cAggressiveMonster +{ + using Super = cAggressiveMonster; + + // Endermite should despawn in two minutes + std::chrono::milliseconds m_Timer; + std::chrono::milliseconds m_Lifetime; + +public: + + cEndermite(); + + CLASS_PROTODEF(cEndermite) + + void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; +} ; diff --git a/src/Mobs/IncludeAllMonsters.h b/src/Mobs/IncludeAllMonsters.h index c0aa2bd55..b7f629e59 100644 --- a/src/Mobs/IncludeAllMonsters.h +++ b/src/Mobs/IncludeAllMonsters.h @@ -21,6 +21,7 @@ #include "Cow.h" #include "Creeper.h" #include "Enderman.h" +#include "Endermite.h" #include "EnderDragon.h" #include "Ghast.h" #include "Giant.h" diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 54fcaf681..b905a733d 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -61,6 +61,7 @@ static const struct {mtCow, "cow", "Cow", "cow"}, {mtCreeper, "creeper", "Creeper", "creeper"}, {mtEnderman, "enderman", "Enderman", "enderman"}, + {mtEndermite, "endermite", "Endermite", "endermite"}, {mtEnderDragon, "enderdragon", "EnderDragon", "ender_dragon"}, {mtGhast, "ghast", "Ghast", "ghast"}, {mtGiant, "giant", "Giant", "giant"}, @@ -669,6 +670,11 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) Reward = GetRandomProvider().RandInt(6, 8); break; } + case mtEndermite: + { + Reward = 3; + break; + } case mtBlaze: { Reward = 10; @@ -1302,6 +1308,7 @@ std::unique_ptr cMonster::NewMonsterFromType(eMonsterType a_MobType) case mtCow: return std::make_unique(); case mtCreeper: return std::make_unique(); case mtEnderDragon: return std::make_unique(); + case mtEndermite: return std::make_unique(); case mtEnderman: return std::make_unique(); case mtGhast: return std::make_unique(); case mtGiant: return std::make_unique(); diff --git a/src/Protocol/Protocol_1_10.cpp b/src/Protocol/Protocol_1_10.cpp index e60f18a97..3980b33a8 100644 --- a/src/Protocol/Protocol_1_10.cpp +++ b/src/Protocol/Protocol_1_10.cpp @@ -953,8 +953,6 @@ void cProtocol_1_10_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_ } case mtCat: - case mtEndermite: - case mtPolarBear: case mtShulker: @@ -971,6 +969,7 @@ void cProtocol_1_10_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_ case mtCaveSpider: case mtEnderDragon: + case mtEndermite: case mtGiant: case mtIronGolem: case mtMooshroom: diff --git a/src/Protocol/Protocol_1_11.cpp b/src/Protocol/Protocol_1_11.cpp index 11937c86b..39ea8b20c 100644 --- a/src/Protocol/Protocol_1_11.cpp +++ b/src/Protocol/Protocol_1_11.cpp @@ -1246,8 +1246,6 @@ void cProtocol_1_11_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_ case mtDonkey: case mtMule: - case mtEndermite: - case mtEvoker: case mtLlama: @@ -1270,6 +1268,7 @@ void cProtocol_1_11_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_ case mtCaveSpider: case mtEnderDragon: + case mtEndermite: case mtGiant: case mtIronGolem: case mtMooshroom: diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp index cfd300572..c84d413d8 100644 --- a/src/Protocol/Protocol_1_12.cpp +++ b/src/Protocol/Protocol_1_12.cpp @@ -951,8 +951,6 @@ void cProtocol_1_12::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mo case mtDonkey: case mtMule: - case mtEndermite: - case mtEvoker: case mtHusk: @@ -981,6 +979,7 @@ void cProtocol_1_12::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mo break; } + case mtEndermite: case mtGiant: case mtSilverfish: case mtSquid: diff --git a/src/Protocol/Protocol_1_13.cpp b/src/Protocol/Protocol_1_13.cpp index d561fcd51..fff328696 100644 --- a/src/Protocol/Protocol_1_13.cpp +++ b/src/Protocol/Protocol_1_13.cpp @@ -1315,8 +1315,6 @@ void cProtocol_1_13::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mo case mtDrowned: - case mtEndermite: - case mtEvoker: case mtIllusioner: @@ -1364,6 +1362,7 @@ void cProtocol_1_13::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mo break; } + case mtEndermite: case mtGiant: case mtSilverfish: case mtSquid: diff --git a/src/Protocol/Protocol_1_14.cpp b/src/Protocol/Protocol_1_14.cpp index 83510ee65..223c585ac 100644 --- a/src/Protocol/Protocol_1_14.cpp +++ b/src/Protocol/Protocol_1_14.cpp @@ -1618,8 +1618,6 @@ void cProtocol_1_14::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mo case mtDrowned: - case mtEndermite: - case mtEvoker: case mtIllusioner: @@ -1667,6 +1665,7 @@ void cProtocol_1_14::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mo break; } + case mtEndermite: case mtGiant: case mtSilverfish: case mtSquid: diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index d9f2ca5e4..8ce47e509 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -3777,7 +3777,6 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M case mtCat: - case mtEndermite: case mtDonkey: case mtMule: @@ -3795,6 +3794,7 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M case mtIronGolem: case mtMooshroom: case mtSilverfish: + case mtEndermite: case mtSnowGolem: case mtSpider: case mtSquid: diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index 8692dd60f..8dea08846 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -2334,8 +2334,6 @@ void cProtocol_1_9_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M case mtDonkey: - case mtEndermite: - case mtMule: case mtStray: @@ -2352,6 +2350,7 @@ void cProtocol_1_9_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M case mtCaveSpider: case mtEnderDragon: + case mtEndermite: case mtGiant: case mtIronGolem: case mtMooshroom: