mirror of
https://github.com/neoStudiosLCE/neoLegacy.git
synced 2026-07-06 07:57:01 +00:00
bug fixes
This commit is contained in:
parent
8cb7c5d507
commit
138c7ab028
|
|
@ -23,7 +23,7 @@ set(MINECRAFT_CLIENT_SOURCES
|
|||
${SOURCES_COMMON}
|
||||
)
|
||||
|
||||
add_executable(Minecraft.Client ${MINECRAFT_CLIENT_SOURCES})
|
||||
add_executable(Minecraft.Client ${MINECRAFT_CLIENT_SOURCES} "PhantomModel.h" "PhantomModel.cpp" "PhantomRenderer.h" "PhantomRenderer.cpp")
|
||||
|
||||
# Only define executable on windows
|
||||
if(PLATFORM_NAME STREQUAL "Windows64")
|
||||
|
|
|
|||
|
|
@ -229,7 +229,13 @@ const WCHAR *ConsoleSoundEngine::wchSoundNames[eSoundType_MAX]=
|
|||
L"mob.rabbit.bunnymurder",
|
||||
L"mob.rabbit.hop",
|
||||
|
||||
|
||||
|
||||
L"mob.phantom.bite", // eSoundType_MOB_PHANTOM_BITE
|
||||
L"mob.phantom.death", // eSoundType_MOB_PHANTOM_DEATH
|
||||
L"mob.phantom.flap", // eSoundType_MOB_PHANTOM_FLAP
|
||||
L"mob.phantom.hurt", // eSoundType_MOB_PHANTOM_HURT
|
||||
L"mob.phantom.idle", // eSoundType_MOB_PHANTOM_IDLE
|
||||
L"mob.phantom.swoop", // eSoundType_MOB_PHANTOM_SWOOP
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
BIN
Minecraft.Client/Common/res/1_2_2/mob/phantom.png
Normal file
BIN
Minecraft.Client/Common/res/1_2_2/mob/phantom.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 639 B |
BIN
Minecraft.Client/Common/res/1_2_2/mob/phantom_eyes.png
Normal file
BIN
Minecraft.Client/Common/res/1_2_2/mob/phantom_eyes.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 B |
|
|
@ -82,6 +82,7 @@
|
|||
#include "BatRenderer.h"
|
||||
#include "CaveSpiderRenderer.h"
|
||||
#include "RabbitRenderer.h"
|
||||
#include "PhantomRenderer.h"
|
||||
#include "ArmorStandRenderer.h"
|
||||
#include "EndermiteRenderer.h"
|
||||
#include "MobRenderer.h"
|
||||
|
|
@ -136,6 +137,7 @@ EntityRenderDispatcher::EntityRenderDispatcher()
|
|||
renderers[eTYPE_WITHERBOSS] = new WitherBossRenderer();
|
||||
|
||||
renderers[eTYPE_RABBIT] = new RabbitRenderer();
|
||||
renderers[eTYPE_PHANTOM] = new PhantomRenderer();
|
||||
|
||||
renderers[eTYPE_ENTITY] = new DefaultRenderer();
|
||||
renderers[eTYPE_PAINTING] = new PaintingRenderer();
|
||||
|
|
|
|||
134
Minecraft.Client/PhantomModel.cpp
Normal file
134
Minecraft.Client/PhantomModel.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#include "stdafx.h"
|
||||
#include "..\Minecraft.World\net.minecraft.world.entity.monster.h"
|
||||
#include "PhantomModel.h"
|
||||
#include "ModelPart.h"
|
||||
#include "../Minecraft.World/Phantom.h"
|
||||
|
||||
PhantomModel::PhantomModel() : Model()
|
||||
{
|
||||
texWidth = 64;
|
||||
texHeight = 64;
|
||||
|
||||
|
||||
body = new ModelPart(this, 0, 8);
|
||||
body->addBox(-3.0f, -2.0f, -8.0f, 5, 3, 9);
|
||||
|
||||
|
||||
head = new ModelPart(this, 0, 0);
|
||||
head->addBox(-4.0f, -2.0f, -5.0f, 7, 3, 5);
|
||||
|
||||
|
||||
leftWing = new ModelPart(this, 23, 12);
|
||||
leftWing->addBox(0.0f, 0.0f, 0.0f, 6, 2, 9);
|
||||
|
||||
|
||||
leftWingTip = new ModelPart(this, 16, 24);
|
||||
leftWingTip->addBox(0.0f, 0.0f, 0.0f, 13, 1, 9);
|
||||
|
||||
|
||||
rightWing = new ModelPart(this, 23, 12);
|
||||
rightWing->bMirror = true;
|
||||
rightWing->addBox(-6.0f, 0.0f, 0.0f, 6, 2, 9);
|
||||
|
||||
|
||||
rightWingTip = new ModelPart(this, 16, 24);
|
||||
rightWingTip->bMirror = true;
|
||||
rightWingTip->addBox(-13.0f, 0.0f, 0.0f, 13, 1, 9);
|
||||
|
||||
|
||||
tailBase = new ModelPart(this, 3, 20);
|
||||
tailBase->addBox(-2.0f, 0.0f, 0.0f, 3, 2, 6);
|
||||
|
||||
|
||||
tailTip = new ModelPart(this, 4, 29);
|
||||
tailTip->addBox(-1.0f, 0.0f, 0.0f, 1, 1, 6);
|
||||
|
||||
|
||||
body->addChild(head);
|
||||
body->addChild(leftWing);
|
||||
body->addChild(rightWing);
|
||||
body->addChild(tailBase);
|
||||
leftWing->addChild(leftWingTip);
|
||||
rightWing->addChild(rightWingTip);
|
||||
tailBase->addChild(tailTip);
|
||||
|
||||
|
||||
const float s = 1.0f / 16.0f;
|
||||
body->compile(s);
|
||||
head->compile(s);
|
||||
leftWing->compile(s);
|
||||
rightWing->compile(s);
|
||||
leftWingTip->compile(s);
|
||||
rightWingTip->compile(s);
|
||||
tailBase->compile(s);
|
||||
tailTip->compile(s);
|
||||
}
|
||||
|
||||
int PhantomModel::modelVersion()
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
void PhantomModel::render(shared_ptr<Entity> entity,
|
||||
float time, float r, float bob,
|
||||
float yRot, float xRot,
|
||||
float scale, bool usecompiled)
|
||||
{
|
||||
shared_ptr<Phantom> phantom = dynamic_pointer_cast<Phantom>(entity);
|
||||
if (!phantom) return;
|
||||
|
||||
|
||||
float flapTime = (float)phantom->tickCount;
|
||||
|
||||
|
||||
const float rad = (float)(PI / 180.0);
|
||||
float f = flapTime * 7.448451f * rad;
|
||||
|
||||
|
||||
body->xRot = -0.1f;
|
||||
body->yRot = 0.0f;
|
||||
body->zRot = 0.0f;
|
||||
body->setPos(0.0f, 0.0f, 0.0f);
|
||||
|
||||
|
||||
head->xRot = 0.2f;
|
||||
head->yRot = 0.0f;
|
||||
head->zRot = 0.0f;
|
||||
head->setPos(0.0f, 1.0f, -7.0f);
|
||||
|
||||
|
||||
leftWing->setPos(2.0f, -2.0f, -8.0f);
|
||||
leftWing->xRot = 0.0f;
|
||||
leftWing->yRot = 0.0f;
|
||||
leftWing->zRot = 0.1f;
|
||||
|
||||
rightWing->setPos(-3.0f, -2.0f, -8.0f);
|
||||
rightWing->xRot = 0.0f;
|
||||
rightWing->yRot = 0.0f;
|
||||
rightWing->zRot = -0.1f;
|
||||
|
||||
leftWingTip->setPos(6.0f, 0.0f, 0.0f);
|
||||
leftWingTip->zRot = 0.1f;
|
||||
|
||||
rightWingTip->setPos(-6.0f, 0.0f, 0.0f);
|
||||
rightWingTip->zRot = -0.1f;
|
||||
|
||||
|
||||
tailBase->setPos(0.0f, -2.0f, 1.0f);
|
||||
tailTip->setPos(0.0f, 0.5f, 6.0f);
|
||||
|
||||
|
||||
float flapAngle = cos(f) * 16.0f * rad;
|
||||
leftWing->zRot += flapAngle;
|
||||
leftWingTip->zRot += flapAngle;
|
||||
rightWing->zRot -= flapAngle;
|
||||
rightWingTip->zRot -= flapAngle;
|
||||
|
||||
|
||||
float tailAngle = -(5.0f + cos(f * 2.0f) * 5.0f) * rad;
|
||||
tailBase->xRot = tailAngle;
|
||||
tailTip->xRot = tailAngle;
|
||||
|
||||
|
||||
body->render(scale, usecompiled);
|
||||
}
|
||||
23
Minecraft.Client/PhantomModel.h
Normal file
23
Minecraft.Client/PhantomModel.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include "Model.h"
|
||||
|
||||
class PhantomModel : public Model
|
||||
{
|
||||
private:
|
||||
ModelPart *body;
|
||||
ModelPart *head;
|
||||
ModelPart *leftWing;
|
||||
ModelPart *leftWingTip;
|
||||
ModelPart *rightWing;
|
||||
ModelPart *rightWingTip;
|
||||
ModelPart *tailBase;
|
||||
ModelPart *tailTip;
|
||||
|
||||
public:
|
||||
PhantomModel();
|
||||
int modelVersion();
|
||||
virtual void render(shared_ptr<Entity> entity,
|
||||
float time, float r, float bob,
|
||||
float yRot, float xRot,
|
||||
float scale, bool usecompiled);
|
||||
};
|
||||
108
Minecraft.Client/PhantomRenderer.cpp
Normal file
108
Minecraft.Client/PhantomRenderer.cpp
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#include "stdafx.h"
|
||||
#include "..\Minecraft.World\net.minecraft.world.entity.monster.h"
|
||||
#include "PhantomRenderer.h"
|
||||
#include "PhantomModel.h"
|
||||
#include "../Minecraft.World/Phantom.h"
|
||||
#include "Textures.h"
|
||||
|
||||
|
||||
|
||||
ResourceLocation PhantomRenderer::PHANTOM_LOCATION = ResourceLocation(TN_MOB_PHANTOM);
|
||||
ResourceLocation PhantomRenderer::PHANTOM_EYES_LOCATION = ResourceLocation(TN_MOB_PHANTOM_EYES);
|
||||
|
||||
PhantomRenderer::PhantomRenderer() : MobRenderer(new PhantomModel(), 0.5f)
|
||||
{
|
||||
modelVersion = static_cast<PhantomModel *>(model)->modelVersion();
|
||||
this->setArmor(model);
|
||||
}
|
||||
|
||||
void PhantomRenderer::render(shared_ptr<Entity> _mob,
|
||||
double x, double y, double z,
|
||||
float rot, float a)
|
||||
{
|
||||
|
||||
int currentVersion = static_cast<PhantomModel *>(model)->modelVersion();
|
||||
if (currentVersion != modelVersion)
|
||||
{
|
||||
modelVersion = currentVersion;
|
||||
model = new PhantomModel();
|
||||
this->setArmor(model);
|
||||
}
|
||||
|
||||
MobRenderer::render(_mob, x, y, z, rot, a);
|
||||
}
|
||||
|
||||
ResourceLocation *PhantomRenderer::getTextureLocation(shared_ptr<Entity> mob)
|
||||
{
|
||||
return &PHANTOM_LOCATION;
|
||||
}
|
||||
|
||||
void PhantomRenderer::scale(shared_ptr<LivingEntity> mob, float a)
|
||||
{
|
||||
|
||||
shared_ptr<Phantom> phantom = dynamic_pointer_cast<Phantom>(mob);
|
||||
if (phantom)
|
||||
{
|
||||
float s = 1.0f + 0.15f * phantom->getPhantomSize();
|
||||
glScalef(s, s, s);
|
||||
}
|
||||
|
||||
//glScalef(0.35f, 0.35f, 0.35f);
|
||||
}
|
||||
|
||||
void PhantomRenderer::setupPosition(shared_ptr<LivingEntity> mob,
|
||||
double x, double y, double z)
|
||||
{
|
||||
MobRenderer::setupPosition(mob, x, y-0.5, z );
|
||||
}
|
||||
|
||||
void PhantomRenderer::setupRotations(shared_ptr<LivingEntity> _mob,
|
||||
float bob, float bodyRot, float a)
|
||||
{
|
||||
glTranslatef(0.0f, -0.5f, 0.0f);
|
||||
glTranslatef(0.0f, cos(bob * 0.3f) * 0.1f, 0.0f);
|
||||
MobRenderer::setupRotations(_mob, bob, bodyRot, a);
|
||||
}
|
||||
int PhantomRenderer::prepareArmor(shared_ptr<LivingEntity> _mob, int layer, float a)
|
||||
{
|
||||
shared_ptr<Phantom> mob = dynamic_pointer_cast<Phantom>(_mob);
|
||||
|
||||
|
||||
if (layer != 0) return -1;
|
||||
|
||||
bindTexture(&PHANTOM_EYES_LOCATION);
|
||||
float br = 1;
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
#ifdef _XBOX
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
#endif
|
||||
|
||||
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
if (mob->isInvisible())
|
||||
{
|
||||
glDepthMask(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDepthMask(true);
|
||||
}
|
||||
|
||||
if (SharedConstants::TEXTURE_LIGHTING)
|
||||
{
|
||||
int col = 0xf0f0;
|
||||
int u = col % 65536;
|
||||
int v = col / 65536;
|
||||
|
||||
glMultiTexCoord2f(GL_TEXTURE1, u / 1.0f, v / 1.0f);
|
||||
glColor4f(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
glColor4f(1, 1, 1, br);
|
||||
|
||||
return 1;
|
||||
}
|
||||
27
Minecraft.Client/PhantomRenderer.h
Normal file
27
Minecraft.Client/PhantomRenderer.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
#include "MobRenderer.h"
|
||||
|
||||
class PhantomModel;
|
||||
|
||||
class PhantomRenderer : public MobRenderer
|
||||
{
|
||||
static ResourceLocation PHANTOM_LOCATION;
|
||||
static ResourceLocation PHANTOM_EYES_LOCATION;
|
||||
int modelVersion;
|
||||
|
||||
public:
|
||||
PhantomRenderer();
|
||||
|
||||
virtual void render(shared_ptr<Entity> mob,
|
||||
double x, double y, double z,
|
||||
float rot, float a);
|
||||
|
||||
protected:
|
||||
virtual ResourceLocation *getTextureLocation(shared_ptr<Entity> mob);
|
||||
virtual void scale(shared_ptr<LivingEntity> mob, float a);
|
||||
virtual void setupPosition(shared_ptr<LivingEntity> mob,
|
||||
double x, double y, double z);
|
||||
virtual void setupRotations(shared_ptr<LivingEntity> mob,
|
||||
float bob, float bodyRot, float a);
|
||||
virtual int prepareArmor(shared_ptr<LivingEntity> mob, int layer, float a);
|
||||
};
|
||||
|
|
@ -175,6 +175,8 @@ const wchar_t *Textures::preLoaded[TN_COUNT] =
|
|||
L"mob/rabbit/white_splotched",
|
||||
L"mob/rabbit/toast",
|
||||
L"mob/rabbit/caerbannog",
|
||||
L"mob/phantom",
|
||||
L"mob/phantom_eyes",
|
||||
|
||||
|
||||
L"item/trapped",
|
||||
|
|
|
|||
|
|
@ -165,6 +165,8 @@ typedef enum _TEXTURE_NAME
|
|||
TN_MOB_RABBIT_WHITE_SPLOTCHED,
|
||||
TN_MOB_RABBIT_TOAST,
|
||||
TN_MOB_RABBIT_CAERBANNOG,
|
||||
TN_MOB_PHANTOM,
|
||||
TN_MOB_PHANTOM_EYES,
|
||||
|
||||
TN_TILE_TRAP_CHEST,
|
||||
TN_TILE_LARGE_TRAP_CHEST,
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -148,9 +148,9 @@ Biome::Biome(int id) : id(id)
|
|||
biomes[id] = this;
|
||||
decorator = createDecorator();
|
||||
|
||||
// -- FIX FIORI ED ERBA GLOBALI --
|
||||
decorator->flowerCount = 2; // Valore base di fiori (esclusi biomi speciali che ne sovrascrivono il numero)
|
||||
decorator->grassCount = 1; // Valore base di erba alta
|
||||
|
||||
decorator->flowerCount = 2;
|
||||
decorator->grassCount = 1;
|
||||
|
||||
friendlies.push_back(new MobSpawnerData(eTYPE_SHEEP, 12, 4, 4));
|
||||
friendlies.push_back(new MobSpawnerData(eTYPE_PIG, 10, 4, 4));
|
||||
|
|
@ -164,6 +164,7 @@ Biome::Biome(int id) : id(id)
|
|||
enemies.push_back(new MobSpawnerData(eTYPE_SLIME, 10, 4, 4));
|
||||
enemies.push_back(new MobSpawnerData(eTYPE_ENDERMAN, 1, 1, 4));
|
||||
enemies.push_back(new MobSpawnerData(eTYPE_WITCH, 1, 1, 1));
|
||||
enemies.push_back(new MobSpawnerData(eTYPE_PHANTOM, 5, 1, 2));
|
||||
|
||||
waterFriendlies.push_back(new MobSpawnerData(eTYPE_SQUID, 10, 4, 4));
|
||||
ambientFriendlies.push_back(new MobSpawnerData(eTYPE_BAT, 10, 8, 8));
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ set(MINECRAFT_WORLD_SOURCES
|
|||
${SOURCES_COMMON}
|
||||
)
|
||||
|
||||
add_library(Minecraft.World STATIC ${MINECRAFT_WORLD_SOURCES} "IceSpikeFeature.cpp" "IceSpikeFeature.h" "BlockBlobFeature.h" "BlockBlobFeature.cpp")
|
||||
add_library(Minecraft.World STATIC ${MINECRAFT_WORLD_SOURCES} "IceSpikeFeature.cpp" "IceSpikeFeature.h" "BlockBlobFeature.h" "BlockBlobFeature.cpp" "Phantom.h" "Phantom.cpp")
|
||||
|
||||
target_include_directories(Minecraft.World
|
||||
PRIVATE
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ enum eINSTANCEOF
|
|||
eTYPE_BLAZE = eTYPE_MONSTER | eTYPE_VALID_IN_SPAWNER_FLAG | 0x6,
|
||||
eTYPE_WITCH = eTYPE_MONSTER | eTYPE_VALID_IN_SPAWNER_FLAG | 0x7,
|
||||
eTYPE_WITHERBOSS = eTYPE_MONSTER | eTYPE_VALID_IN_SPAWNER_FLAG | 0x8,
|
||||
eTYPE_PHANTOM = eTYPE_MONSTER | eTYPE_VALID_IN_SPAWNER_FLAG | 0x9,
|
||||
|
||||
eTYPE_ENDERMITE = eTYPE_MONSTER | eTYPE_VALID_IN_SPAWNER_FLAG | 0x10,
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ void EntityIO::staticCtor()
|
|||
setId(WitherBoss::create, eTYPE_WITHERBOSS, L"WitherBoss", 64);
|
||||
setId(Bat::create, eTYPE_BAT, L"Bat", 65, eMinecraftColour_Mob_Bat_Colour1, eMinecraftColour_Mob_Bat_Colour2, IDS_BAT);
|
||||
setId(Witch::create, eTYPE_WITCH, L"Witch", 66, eMinecraftColour_Mob_Witch_Colour1, eMinecraftColour_Mob_Witch_Colour2, IDS_WITCH);
|
||||
setId(Phantom::create, eTYPE_PHANTOM, L"Phantom", 254, eMinecraftColour_Mob_Ghast_Colour1, eMinecraftColour_Mob_Ghast_Colour2, IDS_ZOMBIE);
|
||||
|
||||
setId(Pig::create, eTYPE_PIG, L"Pig", 90, eMinecraftColour_Mob_Pig_Colour1, eMinecraftColour_Mob_Pig_Colour2, IDS_PIG);
|
||||
setId(Sheep::create, eTYPE_SHEEP, L"Sheep", 91, eMinecraftColour_Mob_Sheep_Colour1, eMinecraftColour_Mob_Sheep_Colour2, IDS_SHEEP);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public:
|
|||
protected:
|
||||
int xpReward;
|
||||
|
||||
private:
|
||||
protected:
|
||||
LookControl *lookControl;
|
||||
MoveControl *moveControl;
|
||||
JumpControl *jumpControl;
|
||||
|
|
|
|||
462
Minecraft.World/Phantom.cpp
Normal file
462
Minecraft.World/Phantom.cpp
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
#include "net.minecraft.world.h"
|
||||
#include "net.minecraft.world.phys.h"
|
||||
#include "net.minecraft.world.level.h"
|
||||
#include "net.minecraft.world.entity.h"
|
||||
#include "net.minecraft.world.entity.ai.attributes.h"
|
||||
#include "net.minecraft.world.entity.ai.control.h"
|
||||
#include "net.minecraft.world.entity.ai.goal.h"
|
||||
#include "net.minecraft.world.entity.ai.goal.target.h"
|
||||
#include "net.minecraft.world.entity.monster.h"
|
||||
#include "net.minecraft.world.entity.player.h"
|
||||
#include "net.minecraft.world.damagesource.h"
|
||||
#include "net.minecraft.world.level.tile.h"
|
||||
#include "LevelEvent.h"
|
||||
#include "SoundTypes.h"
|
||||
#include "Phantom.h"
|
||||
|
||||
|
||||
|
||||
class PhantomMoveControl : public MoveControl {
|
||||
Phantom* phantom;
|
||||
public:
|
||||
PhantomMoveControl(Phantom* p) : MoveControl(p), phantom(p) {}
|
||||
|
||||
void tick() override {
|
||||
if (phantom->horizontalCollision) {
|
||||
phantom->yRot += 180.0f;
|
||||
phantom->flySpeed = 0.1f;
|
||||
}
|
||||
|
||||
double d0 = phantom->moveTargetX - phantom->x;
|
||||
double d1 = phantom->moveTargetY - phantom->y;
|
||||
double d2 = phantom->moveTargetZ - phantom->z;
|
||||
double d3 = sqrt(d0 * d0 + d2 * d2);
|
||||
|
||||
if (abs(d3) > 1.0e-5) {
|
||||
double d4 = 1.0 - abs(d1 * 0.7) / d3;
|
||||
d0 *= d4;
|
||||
d2 *= d4;
|
||||
d3 = sqrt(d0 * d0 + d2 * d2);
|
||||
double d5 = sqrt(d0 * d0 + d2 * d2 + d1 * d1);
|
||||
|
||||
float f1 = (float)(atan2(d2, d0) * 180.0 / PI);
|
||||
float f2 = Mth::wrapDegrees(phantom->yRot + 90.0f);
|
||||
float f3 = Mth::wrapDegrees(f1);
|
||||
|
||||
float diff = Mth::wrapDegrees(f3 - f2);
|
||||
phantom->yRot = f2 + Mth::clamp(diff, -4.0f, 4.0f) - 90.0f;
|
||||
phantom->yBodyRot = phantom->yRot;
|
||||
|
||||
if (abs(diff) < 3.0f) {
|
||||
phantom->flySpeed += Mth::clamp(1.8f - phantom->flySpeed, -0.005f * (1.8f / max(phantom->flySpeed, 0.001f)), 0.005f * (1.8f / max(phantom->flySpeed, 0.001f)));
|
||||
} else {
|
||||
phantom->flySpeed += Mth::clamp(0.2f - phantom->flySpeed, -0.025f, 0.025f);
|
||||
}
|
||||
|
||||
float f4 = (float)(-(atan2(-d1, d3) * 180.0 / PI));
|
||||
phantom->xRot = f4;
|
||||
|
||||
float f5_rad = (phantom->yRot + 90.0f) * (float)(PI / 180.0);
|
||||
float f4_rad = f4 * (float)(PI / 180.0);
|
||||
|
||||
double d6 = phantom->flySpeed * cos(f5_rad) * abs(d0 / d5);
|
||||
double d7 = phantom->flySpeed * sin(f5_rad) * abs(d2 / d5);
|
||||
double d8 = phantom->flySpeed * sin(f4_rad) * abs(d1 / d5);
|
||||
|
||||
phantom->xd += (d6 - phantom->xd) * 0.2;
|
||||
phantom->yd += (d8 - phantom->yd) * 0.2;
|
||||
phantom->zd += (d7 - phantom->zd) * 0.2;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PhantomAttackStrategyGoal : public Goal {
|
||||
Phantom* phantom;
|
||||
int nextSweepTick;
|
||||
public:
|
||||
PhantomAttackStrategyGoal(Phantom* p) : phantom(p), nextSweepTick(0) {}
|
||||
|
||||
bool canUse() override {
|
||||
shared_ptr<LivingEntity> target = phantom->getTarget();
|
||||
return target != nullptr && target->isAlive();
|
||||
}
|
||||
|
||||
void start() override {
|
||||
nextSweepTick = 10 * 20; // About 10 seconds
|
||||
phantom->attackPhase = Phantom::AttackPhase::CIRCLE;
|
||||
setAnchorAboveTarget();
|
||||
}
|
||||
|
||||
void stop() override {
|
||||
if (phantom->hasAnchor) {
|
||||
phantom->anchorY = phantom->level->getTopSolidBlock(phantom->anchorX, phantom->anchorZ) + 10 + phantom->random->nextInt(20);
|
||||
}
|
||||
}
|
||||
|
||||
void tick() override {
|
||||
if (phantom->attackPhase == Phantom::AttackPhase::CIRCLE) {
|
||||
nextSweepTick--;
|
||||
if (nextSweepTick <= 0) {
|
||||
phantom->attackPhase = Phantom::AttackPhase::SWOOP;
|
||||
setAnchorAboveTarget();
|
||||
nextSweepTick = (8 + phantom->random->nextInt(4)) * 20;
|
||||
phantom->level->levelEvent(nullptr, LevelEvent::SOUND_BAT_LIFTOFF, (int)phantom->x, (int)phantom->y, (int)phantom->z, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void setAnchorAboveTarget() {
|
||||
shared_ptr<LivingEntity> target = phantom->getTarget();
|
||||
if (target != nullptr) {
|
||||
phantom->anchorX = Mth::floor(target->x);
|
||||
phantom->anchorY = Mth::floor(target->y) + 20 + phantom->random->nextInt(20);
|
||||
phantom->anchorZ = Mth::floor(target->z);
|
||||
if (phantom->anchorY < phantom->level->seaLevel) {
|
||||
phantom->anchorY = phantom->level->seaLevel + 1;
|
||||
}
|
||||
phantom->hasAnchor = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PhantomSweepAttackGoal : public Goal {
|
||||
Phantom* phantom;
|
||||
public:
|
||||
PhantomSweepAttackGoal(Phantom* p) : phantom(p) {}
|
||||
|
||||
bool canUse() override {
|
||||
return phantom->getTarget() != nullptr && phantom->attackPhase == Phantom::AttackPhase::SWOOP;
|
||||
}
|
||||
|
||||
bool canContinueToUse() override {
|
||||
shared_ptr<LivingEntity> target = phantom->getTarget();
|
||||
if (target == nullptr || !target->isAlive()) return false;
|
||||
return canUse();
|
||||
}
|
||||
|
||||
void stop() override {
|
||||
phantom->setTarget(nullptr);
|
||||
phantom->attackPhase = Phantom::AttackPhase::CIRCLE;
|
||||
}
|
||||
|
||||
void tick() override {
|
||||
shared_ptr<LivingEntity> target = phantom->getTarget();
|
||||
if (target != nullptr) {
|
||||
phantom->moveTargetX = target->x;
|
||||
phantom->moveTargetY = target->y + (target->bbHeight * 0.5);
|
||||
phantom->moveTargetZ = target->z;
|
||||
|
||||
// Hit control
|
||||
double dx = phantom->x - target->x;
|
||||
double dy = phantom->y - target->y;
|
||||
double dz = phantom->z - target->z;
|
||||
if (sqrt(dx*dx + dy*dy + dz*dz) < 2.0) { // Close range
|
||||
phantom->doHurtTarget(target);
|
||||
phantom->playSound(eSoundType_MOB_PHANTOM_BITE, 1.0f, 1.0f);
|
||||
phantom->attackPhase = Phantom::AttackPhase::CIRCLE;
|
||||
phantom->level->levelEvent(nullptr, LevelEvent::SOUND_BAT_LIFTOFF, (int)phantom->x, (int)phantom->y, (int)phantom->z, 0);
|
||||
} else if (phantom->horizontalCollision || phantom->hurtTime > 0) {
|
||||
phantom->attackPhase = Phantom::AttackPhase::CIRCLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PhantomCircleAroundAnchorGoal : public Goal {
|
||||
Phantom* phantom;
|
||||
public:
|
||||
PhantomCircleAroundAnchorGoal(Phantom* p) : phantom(p) {}
|
||||
|
||||
bool canUse() override {
|
||||
return phantom->getTarget() == nullptr || phantom->attackPhase == Phantom::AttackPhase::CIRCLE;
|
||||
}
|
||||
|
||||
void start() override {
|
||||
phantom->circleDistance = 5.0f + phantom->random->nextFloat() * 10.0f;
|
||||
phantom->circleHeight = -4.0f + phantom->random->nextFloat() * 9.0f;
|
||||
phantom->circleClockwise = phantom->random->nextBoolean() ? 1.0f : -1.0f;
|
||||
selectNext();
|
||||
}
|
||||
|
||||
void tick() override {
|
||||
if (phantom->random->nextInt(350) == 0) phantom->circleHeight = -4.0f + phantom->random->nextFloat() * 9.0f;
|
||||
|
||||
if (phantom->random->nextInt(250) == 0) {
|
||||
phantom->circleDistance++;
|
||||
if (phantom->circleDistance > 15.0f) {
|
||||
phantom->circleDistance = 5.0f;
|
||||
phantom->circleClockwise = -phantom->circleClockwise;
|
||||
}
|
||||
}
|
||||
|
||||
if (phantom->random->nextInt(450) == 0) {
|
||||
phantom->circleAngle = phantom->random->nextFloat() * 2.0f * (float)PI;
|
||||
selectNext();
|
||||
}
|
||||
|
||||
if (phantom->isTouchingMoveTarget()) selectNext();
|
||||
|
||||
// Height control to avoid crashing
|
||||
if (phantom->moveTargetY < phantom->y && !phantom->level->isEmptyTile(Mth::floor(phantom->x), Mth::floor(phantom->y) - 1, Mth::floor(phantom->z))) {
|
||||
phantom->circleHeight = max(1.0f, phantom->circleHeight);
|
||||
selectNext();
|
||||
}
|
||||
if (phantom->moveTargetY > phantom->y && !phantom->level->isEmptyTile(Mth::floor(phantom->x), Mth::floor(phantom->y) + 1, Mth::floor(phantom->z))) {
|
||||
phantom->circleHeight = min(-1.0f, phantom->circleHeight);
|
||||
selectNext();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void selectNext() {
|
||||
if (!phantom->hasAnchor) {
|
||||
phantom->anchorX = Mth::floor(phantom->x);
|
||||
phantom->anchorY = Mth::floor(phantom->y);
|
||||
phantom->anchorZ = Mth::floor(phantom->z);
|
||||
phantom->hasAnchor = true;
|
||||
}
|
||||
|
||||
phantom->circleAngle += phantom->circleClockwise * 15.0f * (float)(PI / 180.0);
|
||||
phantom->moveTargetX = phantom->anchorX + 0.5 + phantom->circleDistance * cos(phantom->circleAngle);
|
||||
phantom->moveTargetY = phantom->anchorY - 4.0 + phantom->circleHeight;
|
||||
phantom->moveTargetZ = phantom->anchorZ + 0.5 + phantom->circleDistance * sin(phantom->circleAngle);
|
||||
phantom->moveTargetInitialized = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class PhantomAttackPlayerTargetGoal : public Goal {
|
||||
Phantom* phantom;
|
||||
int nextScanTick;
|
||||
public:
|
||||
PhantomAttackPlayerTargetGoal(Phantom* p) : phantom(p), nextScanTick(20) {}
|
||||
|
||||
bool canUse() override {
|
||||
if (nextScanTick > 0) {
|
||||
nextScanTick--;
|
||||
return false;
|
||||
}
|
||||
nextScanTick = 60;
|
||||
|
||||
|
||||
shared_ptr<Player> player = phantom->level->getNearestAttackablePlayer(phantom->shared_from_this(), 64.0);
|
||||
if (player != nullptr) {
|
||||
phantom->setTarget(player);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool canContinueToUse() override {
|
||||
shared_ptr<LivingEntity> target = phantom->getTarget();
|
||||
return target != nullptr && target->isAlive();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
Phantom::Phantom(Level *level) : FlyingMob(level)
|
||||
{
|
||||
this->defineSynchedData();
|
||||
registerAttributes();
|
||||
setHealth(getMaxHealth());
|
||||
|
||||
xpReward = Enemy::XP_REWARD_SMALL;
|
||||
setSize(0.9f, 0.5f);
|
||||
|
||||
moveTargetX = 0.0;
|
||||
moveTargetY = 0.0;
|
||||
moveTargetZ = 0.0;
|
||||
moveTargetInitialized = false;
|
||||
|
||||
hasAnchor = false;
|
||||
anchorX = anchorY = anchorZ = 0;
|
||||
anchorHeight = 10 + random->nextInt(10);
|
||||
|
||||
attackPhase = AttackPhase::CIRCLE;
|
||||
circleAngle = 0.0f;
|
||||
circleDistance = 5.0f;
|
||||
circleHeight = 0.0f;
|
||||
circleClockwise = 1.0f;
|
||||
|
||||
flySpeed = 0.1f;
|
||||
phantomSize = 0;
|
||||
|
||||
|
||||
delete moveControl;
|
||||
moveControl = new PhantomMoveControl(this);
|
||||
|
||||
|
||||
goalSelector.addGoal(1, new PhantomAttackStrategyGoal(this));
|
||||
goalSelector.addGoal(2, new PhantomSweepAttackGoal(this));
|
||||
goalSelector.addGoal(3, new PhantomCircleAroundAnchorGoal(this));
|
||||
|
||||
|
||||
targetSelector.addGoal(1, new PhantomAttackPlayerTargetGoal(this));
|
||||
}
|
||||
|
||||
void Phantom::setPhantomSize(int size)
|
||||
{
|
||||
phantomSize = Mth::clamp(size, 0, 64);
|
||||
float scale = 1.0f + 0.15f * phantomSize;
|
||||
setSize(0.9f * scale, 0.5f * scale);
|
||||
getAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->setBaseValue(6.0 + phantomSize);
|
||||
}
|
||||
|
||||
void Phantom::defineSynchedData()
|
||||
{
|
||||
FlyingMob::defineSynchedData();
|
||||
}
|
||||
|
||||
void Phantom::registerAttributes()
|
||||
{
|
||||
FlyingMob::registerAttributes();
|
||||
getAttribute(SharedMonsterAttributes::MAX_HEALTH)->setBaseValue(20.0);
|
||||
getAttributes()->registerAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->setBaseValue(6.0);
|
||||
}
|
||||
|
||||
float Phantom::getSoundVolume() { return 1.0f; }
|
||||
float Phantom::getVoicePitch() { return FlyingMob::getVoicePitch(); }
|
||||
int Phantom::getAmbientSound() { return eSoundType_MOB_PHANTOM_IDLE; }
|
||||
int Phantom::getHurtSound() { return eSoundType_MOB_PHANTOM_HURT; }
|
||||
int Phantom::getDeathSound() { return eSoundType_MOB_PHANTOM_DEATH; }
|
||||
|
||||
bool Phantom::isPushable() { return false; }
|
||||
void Phantom::doPush(shared_ptr<Entity> e) { }
|
||||
void Phantom::pushEntities() { }
|
||||
bool Phantom::makeStepSound() { return false; }
|
||||
void Phantom::causeFallDamage(float distance) { }
|
||||
void Phantom::checkFallDamage(double ya, bool onGround) { }
|
||||
bool Phantom::isIgnoringTileTriggers() { return true; }
|
||||
bool Phantom::onLadder() { return false; }
|
||||
|
||||
void Phantom::tick()
|
||||
{
|
||||
|
||||
if (!level->isClientSide && isAlive())
|
||||
{
|
||||
|
||||
if (Calendar::GetMonth() + 1 != 4 || Calendar::GetDayOfMonth() != 1)
|
||||
{
|
||||
|
||||
remove();
|
||||
return;
|
||||
|
||||
|
||||
hurt(DamageSource::magic, getMaxHealth());
|
||||
}
|
||||
}
|
||||
|
||||
FlyingMob::tick();
|
||||
yBodyRot = yRot;
|
||||
|
||||
if (!level->isClientSide && isAlive() && tickCount % 20 == 0)
|
||||
{
|
||||
playSound(eSoundType_MOB_PHANTOM_FLAP, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
|
||||
if (!level->isClientSide && isAlive())
|
||||
{
|
||||
|
||||
if (level->isDay() && !level->isRaining())
|
||||
{
|
||||
float br = getBrightness(1.0f);
|
||||
if (br > 0.5f && level->canSeeSky(
|
||||
Mth::floor(x), static_cast<int>(floor(y + 0.5)), Mth::floor(z)))
|
||||
{
|
||||
setOnFire(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Phantom::initAnchor(int bx, int by, int bz)
|
||||
{
|
||||
anchorX = bx;
|
||||
anchorY = by;
|
||||
anchorZ = bz;
|
||||
hasAnchor = true;
|
||||
}
|
||||
|
||||
bool Phantom::isTouchingMoveTarget() const
|
||||
{
|
||||
double dx = moveTargetX - x;
|
||||
double dy = moveTargetY - y;
|
||||
double dz = moveTargetZ - z;
|
||||
return (dx * dx + dy * dy + dz * dz) < 4.0;
|
||||
}
|
||||
|
||||
bool Phantom::hurt(DamageSource *source, float dmg)
|
||||
{
|
||||
if (isInvulnerable()) return false;
|
||||
return FlyingMob::hurt(source, dmg);
|
||||
}
|
||||
|
||||
bool Phantom::doHurtTarget(shared_ptr<Entity> target)
|
||||
{
|
||||
float damage = (float)getAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->getValue();
|
||||
return target->hurt(DamageSource::mobAttack(dynamic_pointer_cast<LivingEntity>(shared_from_this())), damage);
|
||||
}
|
||||
|
||||
void Phantom::readAdditionalSaveData(CompoundTag *tag)
|
||||
{
|
||||
FlyingMob::readAdditionalSaveData(tag);
|
||||
setPhantomSize(tag->getInt(L"Size"));
|
||||
if (tag->contains(L"AnchorX"))
|
||||
{
|
||||
anchorX = tag->getInt(L"AnchorX");
|
||||
anchorY = tag->getInt(L"AnchorY");
|
||||
anchorZ = tag->getInt(L"AnchorZ");
|
||||
hasAnchor = true;
|
||||
}
|
||||
anchorHeight = 10 + random->nextInt(10);
|
||||
moveTargetInitialized = false;
|
||||
}
|
||||
|
||||
void Phantom::addAdditonalSaveData(CompoundTag *entityTag)
|
||||
{
|
||||
FlyingMob::addAdditonalSaveData(entityTag);
|
||||
entityTag->putInt(L"Size", phantomSize);
|
||||
if (hasAnchor)
|
||||
{
|
||||
entityTag->putInt(L"AnchorX", anchorX);
|
||||
entityTag->putInt(L"AnchorY", anchorY);
|
||||
entityTag->putInt(L"AnchorZ", anchorZ);
|
||||
}
|
||||
}
|
||||
|
||||
bool Phantom::canSpawn()
|
||||
{
|
||||
if (Calendar::GetMonth() + 1 != 4 || Calendar::GetDayOfMonth() != 1) {
|
||||
return false;
|
||||
}
|
||||
int xt = Mth::floor(x);
|
||||
int yt = static_cast<int>(y);
|
||||
int zt = Mth::floor(z);
|
||||
|
||||
//if (yt < level->seaLevel) return false;
|
||||
if (level->isDay()) return false;
|
||||
//if (!level->canSeeSky(xt, yt, zt)) return false;
|
||||
if (level->getRawBrightness(xt, yt, zt) >= 8) return false;
|
||||
|
||||
int topSolidY = level->getTopSolidBlock(xt, zt);
|
||||
if (topSolidY < level->seaLevel) {
|
||||
topSolidY = level->seaLevel;
|
||||
}
|
||||
if (yt <= topSolidY + 5)
|
||||
{
|
||||
int heightBoost = 20 + random->nextInt(15);
|
||||
yt = topSolidY + heightBoost;
|
||||
|
||||
|
||||
setPos(x, (double)yt, z);
|
||||
}
|
||||
if (!level->canSeeSky(xt, yt, zt)) return false;
|
||||
|
||||
return FlyingMob::canSpawn();
|
||||
}
|
||||
|
||||
85
Minecraft.World/Phantom.h
Normal file
85
Minecraft.World/Phantom.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
using namespace std;
|
||||
#include "FlyingMob.h"
|
||||
#include "Enemy.h"
|
||||
|
||||
class Level;
|
||||
class CompoundTag;
|
||||
class DamageSource;
|
||||
class Player;
|
||||
|
||||
class Phantom : public FlyingMob, public Enemy
|
||||
{
|
||||
|
||||
friend class PhantomMoveControl;
|
||||
friend class PhantomAttackStrategyGoal;
|
||||
friend class PhantomCircleAroundAnchorGoal;
|
||||
friend class PhantomSweepAttackGoal;
|
||||
|
||||
public:
|
||||
eINSTANCEOF GetType() { return eTYPE_PHANTOM; }
|
||||
static Entity *create(Level *level) { return new Phantom(level); }
|
||||
enum class AttackPhase { CIRCLE, SWOOP };
|
||||
|
||||
private:
|
||||
int phantomSize;
|
||||
|
||||
|
||||
double moveTargetX, moveTargetY, moveTargetZ;
|
||||
bool moveTargetInitialized;
|
||||
|
||||
// Orbit anchor
|
||||
int anchorX, anchorY, anchorZ;
|
||||
bool hasAnchor;
|
||||
int anchorHeight;
|
||||
|
||||
// Attack
|
||||
AttackPhase attackPhase;
|
||||
|
||||
//Circle
|
||||
float circleAngle;
|
||||
float circleDistance;
|
||||
float circleHeight;
|
||||
float circleClockwise;
|
||||
|
||||
float flySpeed;
|
||||
|
||||
public:
|
||||
Phantom(Level *level);
|
||||
|
||||
int getPhantomSize() const { return phantomSize; }
|
||||
void setPhantomSize(int size);
|
||||
AttackPhase getAttackPhase() const { return attackPhase; }
|
||||
|
||||
virtual bool isPushable();
|
||||
virtual void tick();
|
||||
virtual bool hurt(DamageSource *source, float dmg);
|
||||
virtual void readAdditionalSaveData(CompoundTag *tag);
|
||||
virtual void addAdditonalSaveData(CompoundTag *entityTag);
|
||||
virtual bool canSpawn();
|
||||
virtual bool doHurtTarget(shared_ptr<Entity> target) override;
|
||||
|
||||
protected:
|
||||
virtual void defineSynchedData();
|
||||
virtual void registerAttributes();
|
||||
virtual float getSoundVolume();
|
||||
virtual float getVoicePitch();
|
||||
virtual int getAmbientSound();
|
||||
virtual int getHurtSound();
|
||||
virtual int getDeathSound();
|
||||
|
||||
|
||||
virtual bool useNewAi() override { return true; }
|
||||
|
||||
virtual void doPush(shared_ptr<Entity> e);
|
||||
virtual void pushEntities();
|
||||
virtual bool makeStepSound();
|
||||
virtual void causeFallDamage(float distance);
|
||||
virtual void checkFallDamage(double ya, bool onGround);
|
||||
virtual bool isIgnoringTileTriggers();
|
||||
virtual bool onLadder();
|
||||
|
||||
private:
|
||||
void initAnchor(int bx, int by, int bz);
|
||||
bool isTouchingMoveTarget() const;
|
||||
};
|
||||
|
|
@ -58,14 +58,14 @@ enum eSOUND_TYPE
|
|||
eSoundType_MOB_CAT_PURREOW,
|
||||
eSoundType_MOB_CAT_MEOW,
|
||||
eSoundType_MOB_CAT_HIT,
|
||||
// eSoundType_MOB_IRONGOLEM_THROW,
|
||||
// eSoundType_MOB_IRONGOLEM_HIT,
|
||||
// eSoundType_MOB_IRONGOLEM_DEATH,
|
||||
// eSoundType_MOB_IRONGOLEM_WALK,
|
||||
// eSoundType_MOB_IRONGOLEM_THROW,
|
||||
// eSoundType_MOB_IRONGOLEM_HIT,
|
||||
// eSoundType_MOB_IRONGOLEM_DEATH,
|
||||
// eSoundType_MOB_IRONGOLEM_WALK,
|
||||
eSoundType_RANDOM_BOW,
|
||||
eSoundType_RANDOM_BOW_HIT,
|
||||
eSoundType_RANDOM_EXPLODE,
|
||||
eSoundType_RANDOM_FIZZ,
|
||||
eSoundType_RANDOM_FIZZ,
|
||||
eSoundType_RANDOM_POP,
|
||||
eSoundType_RANDOM_FUSE,
|
||||
eSoundType_RANDOM_DRINK,
|
||||
|
|
@ -217,7 +217,13 @@ enum eSOUND_TYPE
|
|||
eSoundType_MOB_RABBIT_HURT,
|
||||
eSoundType_MOB_RABBIT_DEATH,
|
||||
eSoundType_MOB_RABBIT_HOP,
|
||||
|
||||
|
||||
eSoundType_MOB_PHANTOM_BITE,
|
||||
eSoundType_MOB_PHANTOM_DEATH,
|
||||
eSoundType_MOB_PHANTOM_FLAP,
|
||||
eSoundType_MOB_PHANTOM_HURT,
|
||||
eSoundType_MOB_PHANTOM_IDLE,
|
||||
eSoundType_MOB_PHANTOM_SWOOP,
|
||||
|
||||
eSoundType_MAX
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,4 +27,5 @@
|
|||
#include "Witch.h"
|
||||
|
||||
//TU 31
|
||||
#include "Endermite.h"
|
||||
#include "Endermite.h"
|
||||
#include "Phantom.h"
|
||||
Loading…
Reference in a new issue