mirror of
https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1.git
synced 2026-03-30 20:13:31 +00:00
1185 lines
26 KiB
C++
Executable File
1185 lines
26 KiB
C++
Executable File
#include "Mob.h"
|
|
|
|
#include <cmath>
|
|
|
|
#include "player/Player.h"
|
|
#include "SharedFlags.h"
|
|
#include "../entity/EntityEvent.h"
|
|
#include "../level/Level.h"
|
|
#include "../level/material/Material.h"
|
|
#include "../level/tile/Tile.h"
|
|
#include "../../util/Random.h"
|
|
#include "../../util/PerfTimer.h"
|
|
|
|
#include "../../nbt/CompoundTag.h"
|
|
|
|
#include "../../network/RakNetInstance.h"
|
|
#include "../../network/packet/MoveEntityPacket.h"
|
|
#include "ai/control/MoveControl.h"
|
|
#include "ai/control/JumpControl.h"
|
|
#include "ai/PathNavigation.h"
|
|
#include "ai/Sensing.h"
|
|
#include "ai/goal/GoalSelector.h"
|
|
#include "../../network/packet/SetEntityMotionPacket.h"
|
|
#include "../item/ArmorItem.h"
|
|
|
|
|
|
Mob::Mob(Level* level)
|
|
: super(level),
|
|
invulnerableDuration(20),
|
|
//hasHair(false),
|
|
textureName("mob/char.png"),
|
|
capeTextureName(""),
|
|
allowAlpha(true),
|
|
modelName(""),
|
|
bobStrength(1),
|
|
deathScore(0),
|
|
renderOffset(0),
|
|
//interpolateOnly(false),
|
|
lookingAtId(0),
|
|
jumping(false),
|
|
defaultLookAngle(0.0f),
|
|
runSpeed(0.7f),
|
|
walkingSpeed(0.1f),
|
|
flyingSpeed(0.02f),
|
|
run(0), oRun(0),
|
|
animStep(0), animStepO(0),
|
|
lx(0), ly(0), lz(0), lxr(0), lyr(0),
|
|
lSteps(0),
|
|
walkAnimSpeed(0), walkAnimSpeedO(0), walkAnimPos(0),
|
|
attackAnim(0), oAttackAnim(0),
|
|
|
|
autoSendPosRot(true),
|
|
sentX(0), sentY(0), sentZ(0),
|
|
sentRotX(0), sentRotY(0),
|
|
sentXd(0), sentYd(0), sentZd(0),
|
|
arrowCount(0),
|
|
removeArrowTime(0),
|
|
speed(0),
|
|
moveControl(NULL),
|
|
jumpControl(NULL),
|
|
navigation(NULL),
|
|
goalSelector(NULL),
|
|
targetSelector(NULL),
|
|
sensing(NULL),
|
|
swinging(false),
|
|
swingTime(0),
|
|
lastHurt(0),
|
|
dmgSpill(0),
|
|
bypassArmor(false)
|
|
{
|
|
entityData.define(SharedFlagsInformation::DATA_SHARED_FLAGS_ID, (SynchedEntityData::TypeChar) 0);
|
|
entityData.define(DATA_AIR_SUPPLY_ID, (SynchedEntityData::TypeShort) TOTAL_AIR_SUPPLY);
|
|
|
|
_init();
|
|
health = getMaxHealth();
|
|
|
|
blocksBuilding = true;
|
|
ambientSoundTime = -level->random.nextInt(100);
|
|
|
|
rotA = (float) (Mth::random() + 1) * 0.01f;
|
|
setPos(x, y, z);
|
|
timeOffs = (float) Mth::random() * 12398;
|
|
yRot = (float) (Mth::random() * Mth::PI * 2);
|
|
|
|
this->footSize = 0.5f;
|
|
|
|
// Initialize cape inertia positions
|
|
xCape = x;
|
|
yCape = y;
|
|
zCape = z;
|
|
|
|
xc = xCape;
|
|
yc = yCape;
|
|
zc = zCape;
|
|
}
|
|
|
|
Mob::~Mob() {
|
|
//LOGI("destroying an entity! %p\n", this);
|
|
}
|
|
|
|
float Mob::getWalkingSpeedModifier() {
|
|
float speed = 0.7f; // is 1.0f in desktop Minecraft
|
|
|
|
//if (hasEffect(MobEffect.movementSpeed)) {
|
|
// speed *= 1.0f + .2f * (getEffect(MobEffect.movementSpeed).getAmplifier() + 1);
|
|
//}
|
|
//if (hasEffect(MobEffect.movementSlowdown)) {
|
|
// speed *= 1.0f - .15f * (getEffect(MobEffect.movementSlowdown).getAmplifier() + 1);
|
|
//}
|
|
return speed;
|
|
}
|
|
|
|
bool Mob::canSee( Entity* target )
|
|
{
|
|
return !level->clip(Vec3(x, y + getHeadHeight(), z), Vec3(target->x, target->y + target->getHeadHeight(), target->z)).isHit();
|
|
}
|
|
|
|
std::string Mob::getTexture()
|
|
{
|
|
return textureName;
|
|
}
|
|
|
|
void Mob::setTextureName(const std::string& name)
|
|
{
|
|
textureName = name;
|
|
}
|
|
|
|
std::string Mob::getCapeTexture()
|
|
{
|
|
return capeTextureName;
|
|
}
|
|
|
|
void Mob::setCapeTextureName(const std::string& name)
|
|
{
|
|
capeTextureName = name;
|
|
}
|
|
|
|
bool Mob::isPickable()
|
|
{
|
|
return !removed;
|
|
}
|
|
|
|
bool Mob::isPushable()
|
|
{
|
|
return !removed;
|
|
}
|
|
|
|
float Mob::getHeadHeight()
|
|
{
|
|
return bbHeight * 0.85f;
|
|
}
|
|
|
|
float Mob::getVoicePitch() {
|
|
if (isBaby()) {
|
|
return (random.nextFloat() - random.nextFloat()) * 0.2f + 1.5f;
|
|
}
|
|
return (random.nextFloat() - random.nextFloat()) * 0.2f + 1.0f;
|
|
}
|
|
|
|
int Mob::getAmbientSoundInterval()
|
|
{
|
|
return 8 * SharedConstants::TicksPerSecond;
|
|
}
|
|
|
|
void Mob::playAmbientSound()
|
|
{
|
|
const char* ambient = getAmbientSound();
|
|
if (ambient != NULL) {
|
|
level->playSound(this, ambient, getSoundVolume(), getVoicePitch());
|
|
}
|
|
}
|
|
|
|
void Mob::baseTick()
|
|
{
|
|
oAttackAnim = attackAnim;
|
|
super::baseTick();
|
|
|
|
TIMER_PUSH("mobBaseTick");
|
|
|
|
if (((ambientSoundTime++ & 15) == 0) && random.nextInt(2000) < ambientSoundTime) {
|
|
ambientSoundTime = -getAmbientSoundInterval();
|
|
playAmbientSound();
|
|
}
|
|
|
|
if (isAlive() && isInWall()) {
|
|
hurt(NULL, 1);
|
|
}
|
|
|
|
//if (fireImmune || level.isOnline) onFire = 0;
|
|
|
|
if (isAlive() && isUnderLiquid(Material::water) && !isWaterMob()) {
|
|
airSupply--;
|
|
if (airSupply == -20) {
|
|
airSupply = 0;
|
|
for (int i = 0; i < 8; i++) {
|
|
float xo = random.nextFloat() - random.nextFloat();
|
|
float yo = random.nextFloat() - random.nextFloat();
|
|
float zo = random.nextFloat() - random.nextFloat();
|
|
level->addParticle(PARTICLETYPE(bubble), x + xo, y + yo, z + zo, xd, yd, zd);
|
|
}
|
|
hurt(NULL, 2);
|
|
}
|
|
//onFire = 0;
|
|
} else {
|
|
airSupply = airCapacity;
|
|
}
|
|
|
|
oTilt = tilt;
|
|
if (attackTime > 0) attackTime--;
|
|
if (hurtTime > 0) hurtTime--;
|
|
if (invulnerableTime > 0) invulnerableTime--;
|
|
|
|
if (health <= 0) {
|
|
deathTime++;
|
|
if (deathTime > SharedConstants::TicksPerSecond) {
|
|
beforeRemove();
|
|
remove();
|
|
for (int i = 0; i < 20; i++) {
|
|
float xa = (2.0f * random.nextFloat() - 1.0f) * (2.0f * random.nextFloat() - 1.0f) * 0.02f;
|
|
float ya = (2.0f * random.nextFloat() - 1.0f) * (2.0f * random.nextFloat() - 1.0f) * 0.02f;
|
|
float za = (2.0f * random.nextFloat() - 1.0f) * (2.0f * random.nextFloat() - 1.0f) * 0.02f;
|
|
level->addParticle(PARTICLETYPE(explode), x + random.nextFloat() * bbWidth * 2 - bbWidth, y + random.nextFloat() * bbHeight, z + random.nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za);
|
|
}
|
|
}
|
|
}
|
|
|
|
animStepO = animStep;
|
|
|
|
yBodyRotO = yBodyRot;
|
|
yRotO = yRot;
|
|
xRotO = xRot;
|
|
|
|
TIMER_POP();
|
|
|
|
if (!level->isClientSide) {
|
|
if (autoSendPosRot) {
|
|
if (autoSendPosRot && (std::abs(x - sentX) > .1f || std::abs(y - sentY) > .05f || std::abs(z - sentZ) > .1f || std::abs(sentRotX - xRot) > 1 || std::abs(sentRotY - yRot) > 1)) {
|
|
MoveEntityPacket_PosRot packet(this);
|
|
level->raknetInstance->send(packet);
|
|
sentX = x;
|
|
sentY = y;
|
|
sentZ = z;
|
|
sentRotX = xRot;
|
|
sentRotY = yRot;
|
|
}
|
|
}
|
|
|
|
float ddx = std::abs(xd - sentXd);
|
|
float ddy = std::abs(yd - sentYd);
|
|
float ddz = std::abs(zd - sentZd);
|
|
const float max = 0.02f;
|
|
const float diff = ddx*ddx + ddy*ddy + ddz*ddz;
|
|
if (diff > max*max || (diff > 0 && xd == 0 && yd == 0 && zd == 0)) {
|
|
sentXd = xd;
|
|
sentYd = yd;
|
|
sentZd = zd;
|
|
SetEntityMotionPacket packet(this);
|
|
level->raknetInstance->send(packet);
|
|
//LOGI("Motion-packet: %d\n", entityId);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Mob::spawnAnim()
|
|
{
|
|
/*
|
|
for (int i = 0; i < 20; i++) {
|
|
float xa = random.nextGaussian() * 0.02;
|
|
float ya = random.nextGaussian() * 0.02;
|
|
float za = random.nextGaussian() * 0.02;
|
|
float dd = 10;
|
|
level.addParticle(PARTICLETYPE(explode), x + random.nextFloat() * bbWidth * 2 - bbWidth - xa * dd, y + random.nextFloat() * bbHeight - ya * dd, z + random.nextFloat() * bbWidth * 2 - bbWidth - za
|
|
* dd, xa, ya, za);
|
|
}
|
|
*/
|
|
}
|
|
|
|
void Mob::lerpTo( float x, float y, float z, float yRot, float xRot, int steps )
|
|
{
|
|
//heightOffset = 0;
|
|
lx = x;
|
|
ly = y + heightOffset;
|
|
lz = z;
|
|
lyr = yRot;
|
|
lxr = xRot;
|
|
|
|
lSteps = steps;
|
|
}
|
|
|
|
void Mob::superTick()
|
|
{
|
|
super::tick();
|
|
}
|
|
|
|
void Mob::tick()
|
|
{
|
|
xc = xCape;
|
|
yc = yCape;
|
|
zc = zCape;
|
|
|
|
super::tick();
|
|
|
|
if (arrowCount > 0) {
|
|
if (removeArrowTime <= 0) {
|
|
removeArrowTime = 20 * 3;
|
|
}
|
|
removeArrowTime--;
|
|
if (removeArrowTime <= 0) {
|
|
arrowCount--;
|
|
}
|
|
}
|
|
|
|
if (lSteps > 0) {
|
|
float xt = x + (lx - x) / lSteps;
|
|
float yt = y + (ly - y) / lSteps;
|
|
float zt = z + (lz - z) / lSteps;
|
|
|
|
float yrd = lyr - yRot;
|
|
while (yrd < -180)
|
|
yrd += 360;
|
|
while (yrd >= 180)
|
|
yrd -= 360;
|
|
|
|
yRot += (yrd) / lSteps;
|
|
xRot += (lxr - xRot) / lSteps;
|
|
|
|
lSteps--;
|
|
this->setPos(xt, yt, zt);
|
|
this->setRot(yRot, xRot);
|
|
}
|
|
|
|
aiStep();
|
|
|
|
updateWalkAnim();
|
|
|
|
float xd = x - xo;
|
|
float zd = z - zo;
|
|
|
|
float sideDist = (float) Mth::sqrt(xd * xd + zd * zd);
|
|
|
|
float yBodyRotT = yBodyRot;
|
|
|
|
float walkSpeed = 0;
|
|
oRun = run;
|
|
float tRun = 0;
|
|
if (sideDist <= 0.05f) {
|
|
// animStep = 0;
|
|
} else {
|
|
tRun = 1;
|
|
walkSpeed = sideDist * 3;
|
|
yBodyRotT = Mth::atan2(zd, xd) * Mth::RADDEG - 90;
|
|
}
|
|
if (attackAnim > 0) {
|
|
yBodyRotT = yRot;
|
|
}
|
|
if (!onGround) {
|
|
tRun = 0;
|
|
}
|
|
run = run + (tRun - run) * 0.3f;
|
|
|
|
/*
|
|
* float yBodyRotD = yRot-yBodyRot; while (yBodyRotD < -180) yBodyRotD
|
|
* += 360; while (yBodyRotD >= 180) yBodyRotD -= 360; yBodyRot +=
|
|
* yBodyRotD * 0.1f;
|
|
*/
|
|
|
|
float yBodyRotD = yBodyRotT - yBodyRot;
|
|
while (yBodyRotD < -180)
|
|
yBodyRotD += 360;
|
|
while (yBodyRotD >= 180)
|
|
yBodyRotD -= 360;
|
|
yBodyRot += yBodyRotD * 0.3f;
|
|
|
|
float headDiff = yRot - yBodyRot;
|
|
while (headDiff < -180)
|
|
headDiff += 360;
|
|
while (headDiff >= 180)
|
|
headDiff -= 360;
|
|
bool behind = headDiff < -90 || headDiff >= 90;
|
|
if (headDiff < -75) headDiff = -75;
|
|
if (headDiff >= 75) headDiff = +75;
|
|
yBodyRot = yRot - headDiff;
|
|
if (headDiff * headDiff > 50 * 50) {
|
|
yBodyRot += headDiff * 0.2f;
|
|
}
|
|
|
|
if (behind) {
|
|
walkSpeed *= -1;
|
|
}
|
|
while (yRot - yRotO < -180)
|
|
yRotO -= 360;
|
|
while (yRot - yRotO >= 180)
|
|
yRotO += 360;
|
|
|
|
while (yBodyRot - yBodyRotO < -180)
|
|
yBodyRotO -= 360;
|
|
while (yBodyRot - yBodyRotO >= 180)
|
|
yBodyRotO += 360;
|
|
|
|
while (xRot - xRotO < -180)
|
|
xRotO -= 360;
|
|
while (xRot - xRotO >= 180)
|
|
xRotO += 360;
|
|
animStep += walkSpeed;
|
|
|
|
// Reduce jitter by using a smaller interpolation factor (more lag, smoother motion)
|
|
double dxCape = x - xCape;
|
|
double dyCape = y - yCape;
|
|
double dzCape = z - zCape;
|
|
|
|
const double interp = 0.15; // small value for smoother cape motion
|
|
const double interpY = 0.12; // extra smoothing on vertical movement
|
|
|
|
xCape += dxCape * interp;
|
|
yCape += dyCape * interpY;
|
|
zCape += dzCape * interp;
|
|
}
|
|
|
|
void Mob::setSize( float w, float h )
|
|
{
|
|
super::setSize(w, h);
|
|
}
|
|
|
|
void Mob::heal( int heal )
|
|
{
|
|
if (health <= 0) return;
|
|
health += heal;
|
|
if (health > 20) health = 20;
|
|
invulnerableTime = invulnerableDuration / 2;
|
|
}
|
|
|
|
bool Mob::hurt( Entity* source, int dmg )
|
|
{
|
|
if (level->isClientSide) return false;
|
|
noActionTime = 0;
|
|
if (health <= 0) return false;
|
|
|
|
this->walkAnimSpeed = 1.5f;
|
|
|
|
bool sound = true;
|
|
if (invulnerableTime > invulnerableDuration / 2.0f) {
|
|
if (dmg <= lastHurt) return false;
|
|
actuallyHurt(dmg - lastHurt);
|
|
lastHurt = dmg;
|
|
sound = false;
|
|
} else {
|
|
lastHurt = dmg;
|
|
lastHealth = health;
|
|
invulnerableTime = invulnerableDuration;
|
|
actuallyHurt(dmg);
|
|
hurtTime = hurtDuration = 10;
|
|
hurtDuration = 10;
|
|
}
|
|
|
|
hurtDir = 0;
|
|
|
|
if (sound) {
|
|
level->broadcastEntityEvent(this, EntityEvent::HURT);
|
|
markHurt();
|
|
if (source != NULL) {
|
|
float xd = source->x - x;
|
|
float zd = source->z - z;
|
|
while (xd * xd + zd * zd < 0.0001) {
|
|
xd = (Mth::random() - Mth::random()) * 0.01f;
|
|
zd = (Mth::random() - Mth::random()) * 0.01f;
|
|
}
|
|
hurtDir = (float) (std::atan2(zd, xd) * Mth::RADDEG) - yRot;
|
|
knockback(source, dmg, xd, zd);
|
|
} else {
|
|
hurtDir = (int) (Mth::random() * 2.0f) * 180.0f;
|
|
}
|
|
}
|
|
|
|
if (health <= 0) {
|
|
if (sound) level->playSound(this, getDeathSound(), getSoundVolume(), getVoicePitch());
|
|
die(source);
|
|
} else {
|
|
if (sound) level->playSound(this, getHurtSound(), getSoundVolume(), getVoicePitch());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Mob::actuallyHurt( int dmg )
|
|
{
|
|
if (!bypassArmor) {
|
|
dmg = getDamageAfterArmorAbsorb(dmg);
|
|
}
|
|
health -= dmg;
|
|
}
|
|
|
|
float Mob::getSoundVolume()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
const char* Mob::getAmbientSound()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
std::string Mob::getHurtSound()
|
|
{
|
|
return "random.hurt";
|
|
}
|
|
|
|
std::string Mob::getDeathSound()
|
|
{
|
|
return "random.hurt";
|
|
}
|
|
|
|
void Mob::knockback( Entity* source, int dmg, float xd, float zd )
|
|
{
|
|
float dd = Mth::invSqrt(xd * xd + zd * zd);
|
|
float pow = 0.4f;
|
|
|
|
this->xd *= 0.5f;
|
|
this->yd *= 0.5f;
|
|
this->zd *= 0.5f;
|
|
|
|
this->xd -= xd * dd * pow;
|
|
this->yd += 0.4f;
|
|
this->zd -= zd * dd * pow;
|
|
|
|
if (this->yd > 0.4f) this->yd = 0.4f;
|
|
}
|
|
|
|
void Mob::die( Entity* source )
|
|
{
|
|
if (deathScore > 0 && source != NULL) source->awardKillScore(this, deathScore);
|
|
|
|
if (!level->isClientSide) {
|
|
if (!isBaby()) {
|
|
dropDeathLoot();
|
|
}
|
|
level->broadcastEntityEvent(this, EntityEvent::DEATH);
|
|
}
|
|
}
|
|
|
|
void Mob::dropDeathLoot()
|
|
{
|
|
int loot = getDeathLoot();
|
|
if (loot > 0) {
|
|
int count = random.nextInt(3);
|
|
for (int i = 0; i < count; i++)
|
|
spawnAtLocation(loot, 1);
|
|
}
|
|
}
|
|
|
|
int Mob::getDeathLoot()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void Mob::reset() {
|
|
super::reset();
|
|
this->_init();
|
|
}
|
|
|
|
void Mob::_init() {
|
|
yBodyRot = 0;
|
|
yBodyRotO = 0;
|
|
rotOffs = 0;
|
|
fallTime = 0;
|
|
lastHurt = 0;
|
|
deathTime = 0;
|
|
attackTime = 0;
|
|
tilt = 0;
|
|
oTilt = 0;
|
|
hurtDuration = 0;
|
|
hurtTime = 0;
|
|
hurtDir = 0;
|
|
lookTime = 0;
|
|
noActionTime = 0;
|
|
xxa = 0; yya = 0;
|
|
yRotA = 0;
|
|
|
|
health = 10;
|
|
lastHealth = 20;
|
|
}
|
|
|
|
void Mob::causeFallDamage( float distance )
|
|
{
|
|
int dmg = (int) ceil((distance - 3));
|
|
|
|
if (dmg > 0) {
|
|
level->playSound(this, (dmg > 4)?"damage.fallbig":"damage.fallsmall", 0.75f, 1);
|
|
|
|
hurt(NULL, dmg);
|
|
|
|
int t = level->getTile(Mth::floor(x), Mth::floor(y - 0.2f - this->heightOffset), Mth::floor(z));
|
|
if (t > 0) {
|
|
const Tile::SoundType* soundType = Tile::tiles[t]->soundType;
|
|
level->playSound(this, soundType->getStepSound(), soundType->getVolume() * 0.5f, soundType->getPitch() * 0.75f);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Mob::travel( float xa, float ya )
|
|
{
|
|
if (isInWater()) {
|
|
float yo = y;
|
|
moveRelative(xa, ya, 0.02f);
|
|
move(xd, yd, zd);
|
|
|
|
xd *= 0.80f;
|
|
yd *= 0.80f;
|
|
zd *= 0.80f;
|
|
yd -= 0.02f;
|
|
|
|
if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd)) {
|
|
yd = 0.3f;
|
|
}
|
|
} else if (isInLava()) {
|
|
float yo = y;
|
|
moveRelative(xa, ya, 0.02f);
|
|
move(xd, yd, zd);
|
|
xd *= 0.50f;
|
|
yd *= 0.50f;
|
|
zd *= 0.50f;
|
|
yd -= 0.02f;
|
|
|
|
if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd)) {
|
|
yd = 0.3f;
|
|
}
|
|
} else {
|
|
float friction = 0.91f;
|
|
if (onGround) {
|
|
friction = 0.6f * 0.91f;
|
|
int t = level->getTile(Mth::floor(x), Mth::floor(bb.y0 - 0.5f), Mth::floor(z));
|
|
if (t > 0) {
|
|
friction = Tile::tiles[t]->friction * 0.91f;
|
|
}
|
|
}
|
|
|
|
float friction2 = (0.6f * 0.6f * 0.91f * 0.91f * 0.6f * 0.91f) / (friction * friction * friction);
|
|
moveRelative(xa, ya, (onGround ? walkingSpeed * friction2 : flyingSpeed));
|
|
|
|
friction = 0.91f;
|
|
if (onGround) {
|
|
friction = 0.6f * 0.91f;
|
|
int t = level->getTile(Mth::floor(x), Mth::floor(bb.y0 - 0.5f), Mth::floor(z));
|
|
if (t > 0) {
|
|
friction = Tile::tiles[t]->friction * 0.91f;
|
|
}
|
|
}
|
|
//@todo: make it easier to climb ladders
|
|
if (onLadder()) {
|
|
this->fallDistance = 0;
|
|
if (yd < -0.15) yd = -0.15f;
|
|
if (isSneaking() && yd < 0) yd = 0;
|
|
}
|
|
|
|
move(xd, yd, zd);
|
|
|
|
//@todo: make it easier to climb ladders
|
|
if (horizontalCollision && onLadder()) {
|
|
yd = 0.2f;
|
|
}
|
|
|
|
yd -= 0.08f;
|
|
yd *= 0.98f;
|
|
xd *= friction;
|
|
zd *= friction;
|
|
}
|
|
|
|
}
|
|
|
|
void Mob::updateWalkAnim()
|
|
{
|
|
walkAnimSpeedO = walkAnimSpeed;
|
|
float xxd = x - xo;
|
|
float zzd = z - zo;
|
|
float wst = Mth::sqrt(xxd * xxd + zzd * zzd) * 4;
|
|
if (wst > 1) wst = 1;
|
|
walkAnimSpeed += (wst - walkAnimSpeed) * 0.4f;
|
|
walkAnimPos += walkAnimSpeed;
|
|
}
|
|
|
|
bool Mob::onLadder()
|
|
{
|
|
int xt = Mth::floor(x);
|
|
int yt = Mth::floor(bb.y0);
|
|
int zt = Mth::floor(z);
|
|
return level->getTile(xt, yt, zt) == Tile::ladder->id || level->getTile(xt, yt + 1, zt) == Tile::ladder->id;
|
|
}
|
|
|
|
bool Mob::isShootable()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void Mob::addAdditonalSaveData( CompoundTag* entityTag )
|
|
{
|
|
entityTag->putShort("Health", (short) health);
|
|
entityTag->putShort("HurtTime", (short) hurtTime);
|
|
entityTag->putShort("DeathTime", (short) deathTime);
|
|
entityTag->putShort("AttackTime", (short) attackTime);
|
|
|
|
//if (isPlayer()) LOGI("Saving %d, %d, %d, %d\n", health, hurtTime, deathTime, attackTime);
|
|
}
|
|
|
|
void Mob::readAdditionalSaveData( CompoundTag* tag )
|
|
{
|
|
health = tag->getShort("Health");
|
|
hurtTime = tag->getShort("HurtTime");
|
|
deathTime = tag->getShort("DeathTime");
|
|
attackTime = tag->getShort("AttackTime");
|
|
|
|
//if (isPlayer()) LOGI("Reading %d, %d, %d, %d\n", health, hurtTime, deathTime, attackTime);
|
|
}
|
|
|
|
void Mob::animateHurt()
|
|
{
|
|
hurtTime = hurtDuration = 10;
|
|
hurtDir = 0;
|
|
}
|
|
|
|
bool Mob::isAlive()
|
|
{
|
|
return !removed && health > 0;
|
|
}
|
|
|
|
bool Mob::isWaterMob()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void Mob::aiStep()
|
|
{
|
|
//@todo? 30 lines of code here in java version
|
|
TIMER_PUSH("ai");
|
|
if (isImmobile()) {
|
|
jumping = false;
|
|
xxa = 0;
|
|
yya = 0;
|
|
yRotA = 0;
|
|
} else {
|
|
if (!interpolateOnly()) {
|
|
if (useNewAi()) {
|
|
TIMER_PUSH("newAi");
|
|
newServerAiStep();
|
|
TIMER_POP();
|
|
}
|
|
else {
|
|
TIMER_PUSH("oldAi");
|
|
updateAi();
|
|
TIMER_POP();
|
|
}
|
|
}
|
|
}
|
|
TIMER_POP_PUSH("move");
|
|
|
|
bool inWater = isInWater();
|
|
bool inLava = isInLava();
|
|
|
|
if (jumping) {
|
|
if (inWater) {
|
|
yd += 0.04f;
|
|
} else if (inLava) {
|
|
yd += 0.04f;
|
|
} else if (onGround) {
|
|
jumpFromGround();
|
|
}
|
|
}
|
|
|
|
xxa *= 0.98f;
|
|
yya *= 0.98f;
|
|
yRotA *= 0.9f;
|
|
|
|
float normalSpeed = walkingSpeed;
|
|
float normalAirControllSpeed = flyingSpeed;
|
|
walkingSpeed *= getWalkingSpeedModifier();
|
|
flyingSpeed *= getWalkingSpeedModifier();
|
|
travel(xxa, yya);
|
|
walkingSpeed = normalSpeed;
|
|
flyingSpeed = normalAirControllSpeed;
|
|
|
|
TIMER_POP_PUSH("push");
|
|
Player* player = NULL;
|
|
if(isPlayer()) {
|
|
player = (Player*)this;
|
|
}
|
|
if(player == NULL || !player->isSleeping()) {
|
|
EntityList& entities = level->getEntities(this, this->bb.grow(0.2f, 0, 0.2f));
|
|
for (unsigned int i = 0; i < entities.size(); i++) {
|
|
Entity* e = entities[i];
|
|
if(e->isPlayer()) {
|
|
Player* p = (Player*)e;
|
|
if(p->isSleeping())
|
|
continue;
|
|
}
|
|
if (e->isPushable()) e->push(this);
|
|
}
|
|
}
|
|
TIMER_POP();
|
|
}
|
|
|
|
/*protected*/
|
|
void Mob::updateAi() {
|
|
noActionTime++;
|
|
|
|
Player* player = level->getNearestPlayer(this, -1);
|
|
checkDespawn(player);
|
|
|
|
xxa = 0;
|
|
yya = 0;
|
|
|
|
float lookDistance = 8;
|
|
if (random.nextFloat() < 0.02f) {
|
|
player = level->getNearestPlayer(this, lookDistance);
|
|
if (player != NULL) {
|
|
lookingAtId = player->entityId;
|
|
lookTime = 10 + random.nextInt(20);
|
|
} else {
|
|
yRotA = (random.nextFloat() - 0.5f) * 20;
|
|
}
|
|
}
|
|
|
|
if (lookingAtId != 0) {
|
|
Entity* lookingAt = level->getEntity(lookingAtId);
|
|
lookingAtId = 0;
|
|
if (lookingAt != NULL) {
|
|
lookAt(lookingAt, 10, (float)getMaxHeadXRot());
|
|
if (lookTime-- <= 0 || lookingAt->removed || lookingAt->distanceToSqr(this) > lookDistance * lookDistance) {
|
|
lookingAtId = 0;
|
|
} else {
|
|
lookingAtId = lookingAt->entityId;
|
|
}
|
|
}
|
|
} else {
|
|
if (random.nextFloat() < 0.05f) {
|
|
yRotA = (random.nextFloat() - 0.5f) * 20;
|
|
}
|
|
yRot += yRotA;
|
|
xRot = defaultLookAngle;
|
|
}
|
|
|
|
bool inWater = isInWater();
|
|
bool inLava = isInLava();
|
|
if (inWater || inLava) jumping = random.nextFloat() < 0.8f;
|
|
}
|
|
|
|
void Mob::newServerAiStep() {
|
|
noActionTime++;
|
|
|
|
TIMER_PUSH("checkDespawn");
|
|
checkDespawn();
|
|
jumping = false;
|
|
|
|
TIMER_POP_PUSH("sensing");
|
|
if (sensing) sensing->tick();
|
|
|
|
if (random.nextInt(5) == 0) {
|
|
TIMER_POP_PUSH("targetSelector");
|
|
//LOGI("tg1\n");
|
|
if (targetSelector) targetSelector->tick();
|
|
//LOGI("tg2\n");
|
|
|
|
TIMER_POP_PUSH("goalSelector");
|
|
//LOGI("gs1\n");
|
|
if (goalSelector) goalSelector->tick();
|
|
//LOGI("gs2\n");
|
|
}
|
|
|
|
TIMER_POP_PUSH("navigation");
|
|
if (navigation) navigation->tick();
|
|
|
|
//TIMER_POP_PUSH("mob tick");
|
|
//serverAiMobStep();
|
|
|
|
TIMER_POP_PUSH("controls");
|
|
if (moveControl) moveControl->tick(); //@todo; we shouldn't be here if not
|
|
if (jumpControl) jumpControl->tick();
|
|
|
|
TIMER_POP();
|
|
}
|
|
|
|
bool Mob::isImmobile()
|
|
{
|
|
return health <= 0;
|
|
}
|
|
|
|
void Mob::jumpFromGround()
|
|
{
|
|
yd = 0.42f;
|
|
}
|
|
|
|
bool Mob::removeWhenFarAway()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
int Mob::getMaxHeadXRot()
|
|
{
|
|
return 10;
|
|
}
|
|
|
|
void Mob::lookAt( Entity* e, float yMax, float xMax )
|
|
{
|
|
float xd = e->x - x;
|
|
float yd;
|
|
float zd = e->z - z;
|
|
|
|
if (false) { //if (e->isMob()) { //@todo?
|
|
Mob* mob = (Mob*) e;
|
|
yd = (y + getHeadHeight()) - (mob->y + mob->getHeadHeight());
|
|
} else {
|
|
yd = (e->bb.y0 + e->bb.y1) / 2 - (y + getHeadHeight());
|
|
}
|
|
|
|
float sd = Mth::sqrt(xd * xd + zd * zd);
|
|
|
|
float yRotD = (float) (atan2(zd, xd) * 180 / Mth::PI) - 90;
|
|
float xRotD = (float) (atan2(yd, sd) * 180 / Mth::PI);
|
|
xRot = -rotlerp(xRot, xRotD, xMax);
|
|
yRot = rotlerp(yRot, yRotD, yMax);
|
|
}
|
|
|
|
bool Mob::isLookingAtAnEntity()
|
|
{
|
|
return lookingAtId != 0;
|
|
}
|
|
|
|
//Entity* Mob::getLookingAt()
|
|
//{
|
|
// return lookingAt;
|
|
//}
|
|
|
|
float Mob::rotlerp( float a, float b, float max )
|
|
{
|
|
float diff = b - a;
|
|
while (diff < -180)
|
|
diff += 360;
|
|
while (diff >= 180)
|
|
diff -= 360;
|
|
if (diff > max) {
|
|
diff = max;
|
|
}
|
|
if (diff < -max) {
|
|
diff = -max;
|
|
}
|
|
return a + diff;
|
|
}
|
|
|
|
void Mob::beforeRemove()
|
|
{
|
|
}
|
|
|
|
bool Mob::canSpawn()
|
|
{
|
|
return level->isUnobstructed(bb) && level->getCubes(this, bb).size() == 0 && !level->containsAnyLiquid(bb);
|
|
}
|
|
|
|
void Mob::outOfWorld()
|
|
{
|
|
int oldHealth = health;
|
|
hurt(NULL, 4);
|
|
if (health >= oldHealth)
|
|
actuallyHurt(4);
|
|
}
|
|
|
|
float Mob::getAttackAnim( float a )
|
|
{
|
|
float diff = attackAnim - oAttackAnim;
|
|
if (diff < 0) diff += 1;
|
|
return oAttackAnim + diff * a;
|
|
}
|
|
|
|
Vec3 Mob::getPos( float a )
|
|
{
|
|
if (a == 1) {
|
|
return Vec3(x, y, z);
|
|
}
|
|
float x = xo + (this->x - xo) * a;
|
|
float y = yo + (this->y - yo) * a;
|
|
float z = zo + (this->z - zo) * a;
|
|
|
|
return Vec3(x, y, z);
|
|
}
|
|
|
|
Vec3 Mob::getLookAngle()
|
|
{
|
|
return getViewVector(1);
|
|
}
|
|
|
|
Vec3 Mob::getViewVector( float a )
|
|
{
|
|
if (a == 1) {
|
|
float yCos = (float) Mth::cos(-yRot * Mth::DEGRAD - Mth::PI);
|
|
float ySin = (float) Mth::sin(-yRot * Mth::DEGRAD - Mth::PI);
|
|
float xCos = (float) -Mth::cos(-xRot * Mth::DEGRAD);
|
|
float xSin = (float) Mth::sin(-xRot * Mth::DEGRAD);
|
|
|
|
return Vec3(ySin * xCos, xSin, yCos * xCos); // newTemp
|
|
}
|
|
float xRot = xRotO + (this->xRot - xRotO) * a;
|
|
float yRot = yRotO + (this->yRot - yRotO) * a;
|
|
|
|
float yCos = (float) Mth::cos(-yRot * Mth::DEGRAD - Mth::PI);
|
|
float ySin = (float) Mth::sin(-yRot * Mth::DEGRAD - Mth::PI);
|
|
float xCos = (float) -Mth::cos(-xRot * Mth::DEGRAD);
|
|
float xSin = (float) Mth::sin(-xRot * Mth::DEGRAD);
|
|
|
|
return Vec3(ySin * xCos, xSin, yCos * xCos); // newTemp
|
|
}
|
|
|
|
HitResult Mob::pick( float range, float a )
|
|
{
|
|
Vec3 from = getPos(a);
|
|
return level->clip(from, from + getViewVector(a) * range);
|
|
}
|
|
|
|
int Mob::getMaxSpawnClusterSize()
|
|
{
|
|
return 4;
|
|
}
|
|
|
|
bool Mob::interpolateOnly()
|
|
{
|
|
return level->isClientSide;
|
|
}
|
|
|
|
//ItemInstance getCarriedItem() {
|
|
// return NULL;
|
|
//}
|
|
|
|
void Mob::handleEntityEvent(char id) {
|
|
if (id == EntityEvent::HURT) {
|
|
this->walkAnimSpeed = 1.5f;
|
|
|
|
invulnerableTime = invulnerableDuration;
|
|
hurtTime = hurtDuration = 10;
|
|
hurtDir = 0;
|
|
|
|
level->playSound(this, getHurtSound(), getSoundVolume(), getVoicePitch());
|
|
hurt(NULL, 0);
|
|
} else if (id == EntityEvent::DEATH) {
|
|
level->playSound(this, getDeathSound(), getSoundVolume(), getVoicePitch());
|
|
health = 0;
|
|
die(NULL);
|
|
} else {
|
|
super::handleEntityEvent(id);
|
|
}
|
|
}
|
|
|
|
SynchedEntityData* Mob::getEntityData() {
|
|
return &entityData;
|
|
}
|
|
|
|
const SynchedEntityData* Mob::getEntityData() const {
|
|
return &entityData;
|
|
}
|
|
|
|
MoveControl* Mob::getMoveControl() {
|
|
return moveControl;
|
|
}
|
|
|
|
JumpControl* Mob::getJumpControl() {
|
|
return jumpControl;
|
|
}
|
|
|
|
void Mob::setYya( float yya )
|
|
{
|
|
this->yya = yya;
|
|
}
|
|
|
|
float Mob::getSpeed() {
|
|
return speed;
|
|
}
|
|
|
|
void Mob::setSpeed( float speed ) {
|
|
this->speed = speed;
|
|
this->yya = speed;
|
|
}
|
|
|
|
void Mob::setJumping(bool jump) {
|
|
jumping = jump;
|
|
}
|
|
|
|
bool Mob::useNewAi() {
|
|
return false;
|
|
}
|
|
|
|
void Mob::checkDespawn() {
|
|
checkDespawn(level->getNearestPlayer(this, -1));
|
|
}
|
|
|
|
void Mob::checkDespawn(Mob* nearestBlocking) {
|
|
if (nearestBlocking != NULL) {
|
|
const bool removeIfFar = removeWhenFarAway();
|
|
float xd = nearestBlocking->x - x;
|
|
float yd = nearestBlocking->y - y;
|
|
float zd = nearestBlocking->z - z;
|
|
float sd = xd * xd + yd * yd + zd * zd;
|
|
|
|
if (removeIfFar && sd > 96 * 96) {
|
|
//LOGI("removed some angry mob more than 10 meters away!\n");
|
|
remove();
|
|
}
|
|
|
|
if (noActionTime > 30 * SharedConstants::TicksPerSecond && random.nextInt(800) == 0 && removeIfFar && sd > 32 * 32)
|
|
remove();
|
|
else
|
|
noActionTime = 0;
|
|
}
|
|
}
|
|
bool Mob::getSharedFlag(int flag) {
|
|
return entityData.getFlag<SharedFlagsInformation::SharedFlagsInformationType> (SharedFlagsInformation::DATA_SHARED_FLAGS_ID, flag);
|
|
}
|
|
void Mob::setSharedFlag(int flag, bool value) {
|
|
if(value) {
|
|
entityData.setFlag<SharedFlagsInformation::SharedFlagsInformationType>(SharedFlagsInformation::DATA_SHARED_FLAGS_ID, flag);
|
|
}
|
|
else {
|
|
entityData.clearFlag<SharedFlagsInformation::SharedFlagsInformationType>(SharedFlagsInformation::DATA_SHARED_FLAGS_ID, flag);
|
|
}
|
|
}
|
|
|
|
void Mob::updateAttackAnim() {
|
|
if (swinging) {
|
|
if (++swingTime >= SWING_DURATION) {
|
|
swingTime = 0;
|
|
swinging = false;
|
|
//LOGI("Swinging! %d/%d (%f)\n", swingTime, SWING_DURATION, attackAnim);
|
|
}
|
|
} else {
|
|
swingTime = 0;
|
|
}
|
|
|
|
attackAnim = swingTime / (float) SWING_DURATION;
|
|
}
|
|
|
|
void Mob::swing() {
|
|
if (!swinging || swingTime >= 3 || swingTime < 0) {
|
|
swingTime = -1;
|
|
swinging = true;
|
|
}
|
|
}
|
|
|
|
bool Mob::isSneaking() {
|
|
return getSharedFlag(SharedFlagsInformation::FLAG_SNEAKING);
|
|
}
|
|
|
|
bool Mob::isSleeping() {
|
|
return false;
|
|
}
|
|
|
|
void Mob::setSneaking(bool value) {
|
|
setSharedFlag(SharedFlagsInformation::FLAG_SNEAKING, value);
|
|
}
|
|
|
|
//
|
|
// Armor
|
|
//
|
|
int Mob::getDamageAfterArmorAbsorb(int damage) {
|
|
int absorb = 25 - getArmorValue();
|
|
int v = (damage) * absorb + dmgSpill;
|
|
hurtArmor(damage);
|
|
damage = v / 25;
|
|
dmgSpill = v % 25;
|
|
return damage;
|
|
}
|
|
|
|
static std::vector<ItemInstance*> getEquipmentSlots() {
|
|
return std::vector<ItemInstance*>();
|
|
}
|
|
|
|
int Mob::getArmorValue() {
|
|
int val = 0;
|
|
/*
|
|
const std::vector<ItemInstance*> items = getEquipmentSlots();
|
|
for (unsigned int i = 0; i < items.size(); ++i) {
|
|
ItemInstance* item = items[i];
|
|
if (item != NULL && ItemInstance::isArmorItem(item)) {
|
|
int baseProtection = ((ArmorItem*) item->getItem())->defense;
|
|
val += baseProtection;
|
|
}
|
|
}
|
|
*/
|
|
return val;
|
|
}
|
|
|
|
void Mob::hurtArmor(int damage) {}
|