diff options
| author | daoge <3523206925@qq.com> | 2026-03-03 03:04:10 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-03 03:04:10 +0800 |
| commit | b3feddfef372618c8a9d7a0abcaf18cfad866c18 (patch) | |
| tree | 267761c3bb39241ba5c347bfbe2254d06686e287 /Minecraft.World/Mob.cpp | |
| parent | 84c31a2331f7a0ec85b9d438992e244f60e5020f (diff) | |
feat: TU19 (Dec 2014) Features & Content (#155)
* try to resolve merge conflict
* feat: TU19 (Dec 2014) Features & Content (#32)
* December 2014 files
* Working release build
* Fix compilation issues
* Add sound to Windows64Media
* Add DLC content and force Tutorial DLC
* Revert "Add DLC content and force Tutorial DLC"
This reverts commit 97a43994725008e35fceb984d5549df9c8cea470.
* Disable broken light packing
* Disable breakpoint during DLC texture map load
Allows DLC loading but the DLC textures are still broken
* Fix post build not working
* ...
* fix vs2022 build
* fix cmake build
---------
Co-authored-by: Loki <lokirautio@gmail.com>
Diffstat (limited to 'Minecraft.World/Mob.cpp')
| -rw-r--r-- | Minecraft.World/Mob.cpp | 1936 |
1 files changed, 535 insertions, 1401 deletions
diff --git a/Minecraft.World/Mob.cpp b/Minecraft.World/Mob.cpp index d2dcddfb..cac25ddb 100644 --- a/Minecraft.World/Mob.cpp +++ b/Minecraft.World/Mob.cpp @@ -1,8 +1,10 @@ #include "stdafx.h" #include "JavaMath.h" +#include "net.minecraft.network.packet.h" #include "net.minecraft.world.level.tile.h" #include "net.minecraft.world.phys.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.navigation.h" #include "net.minecraft.world.entity.ai.sensing.h" @@ -16,6 +18,9 @@ #include "net.minecraft.world.effect.h" #include "net.minecraft.world.item.alchemy.h" #include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.h" +#include "..\Minecraft.Client\ServerLevel.h" +#include "..\Minecraft.Client\EntityTracker.h" #include "com.mojang.nbt.h" #include "Mob.h" #include "..\Minecraft.Client\Textures.h" @@ -25,134 +30,63 @@ #include "GenericStats.h" #include "ItemEntity.h" -const double Mob::MIN_MOVEMENT_DISTANCE = 0.005; +const float Mob::MAX_WEARING_ARMOR_CHANCE = 0.15f; +const float Mob::MAX_PICKUP_LOOT_CHANCE = 0.55f; +const float Mob::MAX_ENCHANTED_ARMOR_CHANCE = 0.50f; +const float Mob::MAX_ENCHANTED_WEAPON_CHANCE = 0.25f; void Mob::_init() { - invulnerableDuration = 20; - timeOffs = 0.0f; - - yBodyRot = 0; - yBodyRotO = 0; - yHeadRot = 0; - yHeadRotO = 0; - - oRun = 0.0f; - run = 0.0f; - - animStep = 0.0f; - animStepO = 0.0f; - - MemSect(31); - hasHair = true; - textureIdx = TN_MOB_CHAR; // 4J was L"/mob/char.png"; - allowAlpha = true; - rotOffs = 0; - modelName = L""; - bobStrength = 1; - deathScore = 0; - renderOffset = 0; - MemSect(0); - - walkingSpeed = 0.1f; - flyingSpeed = 0.02f; - - oAttackAnim = 0.0f; - attackAnim = 0.0f; - - lastHealth = 0; - dmgSpill = 0; - ambientSoundTime = 0; - - hurtTime = 0; - hurtDuration = 0; - hurtDir = 0; - deathTime = 0; - attackTime = 0; - oTilt = 0; - tilt = 0; - - dead = false; xpReward = 0; - - modelNum = -1; - animSpeed = (float) (Math::random() * 0.9f + 0.1f); - - walkAnimSpeedO = 0.0f; - walkAnimSpeed = 0.0f; - walkAnimPos = 0.0f; - - lastHurtByPlayer = nullptr; - lastHurtByPlayerTime = 0; - lastHurtByMob = nullptr; - lastHurtByMobTime = 0; - lastHurtMob = nullptr; - - arrowCount = 0; - removeArrowTime = 0; - - lSteps = 0; - lx = ly = lz = lyr = lxr = 0.0; - - fallTime = 0.0f; - - lastHurt = 0; - - noActionTime = 0; - xxa = yya = yRotA = 0.0f; - jumping = false; defaultLookAngle = 0.0f; - runSpeed = 0.7f; - noJumpDelay = 0; - lookingAt = nullptr; lookTime = 0; - - effectsDirty = true; - effectColor = 0; - target = nullptr; sensing = NULL; - speed = 0.0f; - restrictCenter = new Pos(0, 0, 0); - restrictRadius = -1.0f; + equipment = ItemInstanceArray(5); + dropChances = floatArray(5); + for(unsigned int i = 0; i < 5; ++i) + { + equipment[i] = nullptr; + dropChances[i] = 0.0f; + } + + _canPickUpLoot = false; + persistenceRequired = false; + + _isLeashed = false; + leashHolder = nullptr; + leashInfoTag = NULL; } -Mob::Mob( Level* level) : Entity(level) +Mob::Mob( Level* level) : LivingEntity(level) { + MemSect(57); _init(); + MemSect(0); - // 4J Stu - This will not call the correct derived function, so moving to each derived class - //health = getMaxHealth(); - health = 0; - - blocksBuilding = true; + MemSect(58); + // 4J Stu - We call this again in the derived classes, but need to do it here for some internal members + registerAttributes(); + MemSect(0); lookControl = new LookControl(this); moveControl = new MoveControl(this); jumpControl = new JumpControl(this); bodyControl = new BodyControl(this); - navigation = new PathNavigation(this, level, 16); + navigation = new PathNavigation(this, level); sensing = new Sensing(this); - rotA = (float) (Math::random() + 1) * 0.01f; - setPos(x, y, z); - timeOffs = (float) Math::random() * 12398; - yRot = (float) (Math::random() * PI * 2); - yHeadRot = yRot; - - this->footSize = 0.5f; + for (int i = 0; i < 5; i++) + { + dropChances[i] = 0.085f; + } } Mob::~Mob() { - for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it) - { - delete it->second; - } - if(lookControl != NULL) delete lookControl; if(moveControl != NULL) delete moveControl; if(jumpControl != NULL) delete jumpControl; @@ -160,7 +94,17 @@ Mob::~Mob() if(navigation != NULL) delete navigation; if(sensing != NULL) delete sensing; - delete restrictCenter; + if(leashInfoTag != NULL) delete leashInfoTag; + + if(equipment.data != NULL) delete [] equipment.data; + delete [] dropChances.data; +} + +void Mob::registerAttributes() +{ + LivingEntity::registerAttributes(); + + getAttributes()->registerAttribute(SharedMonsterAttributes::FOLLOW_RANGE)->setBaseValue(16); } LookControl *Mob::getLookControl() @@ -188,65 +132,12 @@ Sensing *Mob::getSensing() return sensing; } -Random *Mob::getRandom() -{ - return random; -} - -shared_ptr<Mob> Mob::getLastHurtByMob() -{ - return lastHurtByMob; -} - -shared_ptr<Mob> Mob::getLastHurtMob() -{ - return lastHurtMob; -} - -void Mob::setLastHurtMob(shared_ptr<Entity> target) -{ - shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(target); - if (mob != NULL) lastHurtMob = mob; -} - -int Mob::getNoActionTime() -{ - return noActionTime; -} - -float Mob::getYHeadRot() -{ - return yHeadRot; -} - -void Mob::setYHeadRot(float yHeadRot) -{ - this->yHeadRot = yHeadRot; -} - -float Mob::getSpeed() -{ - return speed; -} - -void Mob::setSpeed(float speed) -{ - this->speed = speed; - setYya(speed); -} - -bool Mob::doHurtTarget(shared_ptr<Entity> target) -{ - setLastHurtMob(target); - return false; -} - -shared_ptr<Mob> Mob::getTarget() +shared_ptr<LivingEntity> Mob::getTarget() { return target; } -void Mob::setTarget(shared_ptr<Mob> target) +void Mob::setTarget(shared_ptr<LivingEntity> target) { this->target = target; } @@ -261,81 +152,11 @@ void Mob::ate() { } -// might move to navigation, might make area -bool Mob::isWithinRestriction() -{ - return isWithinRestriction(Mth::floor(x), Mth::floor(y), Mth::floor(z)); -} - -bool Mob::isWithinRestriction(int x, int y, int z) -{ - if (restrictRadius == -1) return true; - return restrictCenter->distSqr(x, y, z) < restrictRadius * restrictRadius; -} - -void Mob::restrictTo(int x, int y, int z, int radius) -{ - restrictCenter->set(x, y, z); - restrictRadius = radius; -} - -Pos *Mob::getRestrictCenter() -{ - return restrictCenter; -} - -float Mob::getRestrictRadius() -{ - return restrictRadius; -} - -void Mob::clearRestriction() -{ - restrictRadius = -1; -} - -bool Mob::hasRestriction() -{ - return restrictRadius != -1; -} - -void Mob::setLastHurtByMob(shared_ptr<Mob> hurtBy) -{ - lastHurtByMob = hurtBy; - lastHurtByMobTime = lastHurtByMob != NULL ? PLAYER_HURT_EXPERIENCE_TIME : 0; -} - void Mob::defineSynchedData() { - entityData->define(DATA_EFFECT_COLOR_ID, effectColor); -} - -bool Mob::canSee(shared_ptr<Entity> target) -{ - HitResult *hres = level->clip(Vec3::newTemp(x, y + getHeadHeight(), z), Vec3::newTemp(target->x, target->y + target->getHeadHeight(), target->z)); - bool retVal = (hres == NULL); - delete hres; - return retVal; -} - -int Mob::getTexture() -{ - return textureIdx; -} - -bool Mob::isPickable() -{ - return !removed; -} - -bool Mob::isPushable() -{ - return !removed; -} - -float Mob::getHeadHeight() -{ - return bbHeight * 0.85f; + LivingEntity::defineSynchedData(); + entityData->define(DATA_CUSTOM_NAME_VISIBLE, (byte) 0); + entityData->define(DATA_CUSTOM_NAME, L""); } int Mob::getAmbientSoundInterval() @@ -349,15 +170,14 @@ void Mob::playAmbientSound() int ambient = getAmbientSound(); if (ambient != -1) { - level->playSound(shared_from_this(), ambient, getSoundVolume(), getVoicePitch()); + playSound(ambient, getSoundVolume(), getVoicePitch()); } MemSect(0); } void Mob::baseTick() { - oAttackAnim = attackAnim; - Entity::baseTick(); + LivingEntity::baseTick(); if (isAlive() && random->nextInt(1000) < ambientSoundTime++) { @@ -365,126 +185,30 @@ void Mob::baseTick() playAmbientSound(); } +} - if (isAlive() && isInWall()) +int Mob::getExperienceReward(shared_ptr<Player> killedBy) +{ + if (xpReward > 0) { - hurt(DamageSource::inWall, 1); - } - - if (isFireImmune() || level->isClientSide) clearFire(); + int result = xpReward; - if (isAlive() && isUnderLiquid(Material::water) && !isWaterMob() && activeEffects.find(MobEffect::waterBreathing->id) == activeEffects.end()) - { - setAirSupply(decreaseAirSupply(getAirSupply())); - if (getAirSupply() == -20) + ItemInstanceArray slots = getEquipmentSlots(); + for (int i = 0; i < slots.length; i++) { - setAirSupply(0); - if(canCreateParticles()) + if (slots[i] != NULL && dropChances[i] <= 1) { - 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(eParticleType_bubble, x + xo, y + yo, z + zo, xd, yd, zd); - } + result += 1 + random->nextInt(3); } - hurt(DamageSource::drown, 2); } - clearFire(); - } - else - { - setAirSupply(TOTAL_AIR_SUPPLY); + return result; } - - oTilt = tilt; - - if (attackTime > 0) attackTime--; - if (hurtTime > 0) hurtTime--; - if (invulnerableTime > 0) invulnerableTime--; - if (health <= 0) - { - tickDeath(); - } - - if (lastHurtByPlayerTime > 0) lastHurtByPlayerTime--; else { - // Note - this used to just set to nullptr, but that has to create a new shared_ptr and free an old one, when generally this won't be doing anything at all. This - // is the lightweight but ugly alternative - if( lastHurtByPlayer ) - { - lastHurtByPlayer.reset(); - } + return xpReward; } - if (lastHurtMob != NULL && !lastHurtMob->isAlive()) lastHurtMob = nullptr; - - if (lastHurtByMob != NULL) - { - if (!lastHurtByMob->isAlive()) setLastHurtByMob(nullptr); - else if (lastHurtByMobTime > 0) lastHurtByMobTime--; - else setLastHurtByMob(nullptr); - } - - // update effects - tickEffects(); - - animStepO = animStep; - - yBodyRotO = yBodyRot; - yHeadRotO = yHeadRot; - yRotO = yRot; - xRotO = xRot; } - -void Mob::tickDeath() -{ - deathTime++; - if (deathTime == 20) - { - // 4J Stu - Added level->isClientSide check from 1.2 to fix XP orbs being created client side - if(!level->isClientSide && (lastHurtByPlayerTime > 0 || isAlwaysExperienceDropper()) ) - { - if (!isBaby()) - { - int xpCount = this->getExperienceReward(lastHurtByPlayer); - while (xpCount > 0) - { - int newCount = ExperienceOrb::getExperienceValue(xpCount); - xpCount -= newCount; - level->addEntity(shared_ptr<ExperienceOrb>( new ExperienceOrb(level, x, y, z, newCount) ) ); - } - } - } - - remove(); - for (int i = 0; i < 20; i++) - { - double xa = random->nextGaussian() * 0.02; - double ya = random->nextGaussian() * 0.02; - double za = random->nextGaussian() * 0.02; - level->addParticle(eParticleType_explode, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); - } - } -} - -int Mob::decreaseAirSupply(int currentSupply) -{ - return currentSupply - 1; -} - -int Mob::getExperienceReward(shared_ptr<Player> killedBy) -{ - return xpReward; -} - -bool Mob::isAlwaysExperienceDropper() -{ - return false; -} - void Mob::spawnAnim() { for (int i = 0; i < 20; i++) @@ -498,325 +222,27 @@ void Mob::spawnAnim() } } -void Mob::rideTick() -{ - Entity::rideTick(); - oRun = run; - run = 0; - fallDistance = 0; -} - -void Mob::lerpTo(double x, double y, double z, float yRot, float xRot, int steps) -{ - heightOffset = 0; - lx = x; - ly = y; - lz = z; - lyr = yRot; - lxr = xRot; - - lSteps = steps; -} - -void Mob::superTick() -{ - Entity::tick(); -} - void Mob::tick() { - Entity::tick(); + LivingEntity::tick(); - if (arrowCount > 0) - { - if (removeArrowTime <= 0) - { - removeArrowTime = 20 * 3; - } - removeArrowTime--; - if (removeArrowTime <= 0) - { - arrowCount--; - } - } - - aiStep(); - - double xd = x - xo; - double zd = z - zo; - - float sideDist = xd * xd + zd * zd; - - float yBodyRotT = yBodyRot; - - float walkSpeed = 0; - oRun = run; - float tRun = 0; - if (sideDist <= 0.05f * 0.05f) - { - // animStep = 0; - } - else - { - tRun = 1; - walkSpeed = sqrt(sideDist) * 3; - yBodyRotT = ((float) atan2(zd, xd) * 180 / (float) PI - 90); - } - if (attackAnim > 0) - { - yBodyRotT = yRot; - } - if (!onGround) + if (!level->isClientSide) { - tRun = 0; + tickLeash(); } - 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 Mob::tickHeadTurn(float yBodyRotT, float walkSpeed) +{ if (useNewAi()) { bodyControl->clientTick(); + return walkSpeed; } else { - float yBodyRotD = Mth::wrapDegrees(yBodyRotT - yBodyRot); - yBodyRot += yBodyRotD * 0.3f; - - float headDiff = Mth::wrapDegrees(yRot - yBodyRot); - 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; - - while (yHeadRot - yHeadRotO < -180) - yHeadRotO -= 360; - while (yHeadRot - yHeadRotO >= 180) - yHeadRotO += 360; - - animStep += walkSpeed; -} - -void Mob::heal(int heal) -{ - if (health <= 0) return; - health += heal; - if (health > getMaxHealth()) health = getMaxHealth(); - invulnerableTime = invulnerableDuration / 2; -} - -int Mob::getHealth() -{ - return health; -} - -void Mob::setHealth(int health) -{ - this->health = health; - if (health > getMaxHealth()) - { - health = getMaxHealth(); - } -} - -bool Mob::hurt(DamageSource *source, int dmg) -{ - // 4J Stu - Reworked this function a bit to show hurt damage on the client before the server responds. - // Fix for #8823 - Gameplay: Confirmation that a monster or animal has taken damage from an attack is highly delayed - // 4J Stu - Change to the fix to only show damage when attacked, rather than collision damage - // Fix for #10299 - When in corners, passive mobs may show that they are taking damage. - // 4J Stu - Change to the fix for TU6, as source is never NULL due to changes in 1.8.2 to what source actually is - if (level->isClientSide && dynamic_cast<EntityDamageSource *>(source) == NULL) return false; - noActionTime = 0; - if (health <= 0) return false; - - if ( source->isFire() && hasEffect(MobEffect::fireResistance) ) - { - // 4J-JEV, for new achievement Stayin'Frosty, TODO merge with Java version. - shared_ptr<Player> plr = dynamic_pointer_cast<Player>(shared_from_this()); - if ( plr != NULL && source == DamageSource::lava ) // Only award when in lava (not any fire). - { - plr->awardStat(GenericStats::stayinFrosty(),GenericStats::param_stayinFrosty()); - } - return false; - } - - this->walkAnimSpeed = 1.5f; - - bool sound = true; - if (invulnerableTime > invulnerableDuration / 2.0f) - { - if (dmg <= lastHurt) return false; - if(!level->isClientSide) actuallyHurt(source, dmg - lastHurt); - lastHurt = dmg; - sound = false; - } - else - { - lastHurt = dmg; - lastHealth = health; - invulnerableTime = invulnerableDuration; - if (!level->isClientSide) actuallyHurt(source, dmg); - hurtTime = hurtDuration = 10; - } - - hurtDir = 0; - - shared_ptr<Entity> sourceEntity = source->getEntity(); - if (sourceEntity != NULL) - { - if (dynamic_pointer_cast<Mob>(sourceEntity) != NULL) { - setLastHurtByMob(dynamic_pointer_cast<Mob>(sourceEntity)); - - } - if (dynamic_pointer_cast<Player>(sourceEntity) != NULL) - { - lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME; - lastHurtByPlayer = dynamic_pointer_cast<Player>(sourceEntity); - } - else if (dynamic_pointer_cast<Wolf>(sourceEntity)) - { - shared_ptr<Wolf> w = dynamic_pointer_cast<Wolf>(sourceEntity); - if (w->isTame()) - { - lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME; - lastHurtByPlayer = nullptr; - } - } - } - - if (sound && level->isClientSide) - { - return false; - } - - if (sound) - { - level->broadcastEntityEvent(shared_from_this(), EntityEvent::HURT); - if (source != DamageSource::drown && source != DamageSource::controlledExplosion) markHurt(); - if (sourceEntity != NULL) - { - double xd = sourceEntity->x - x; - double zd = sourceEntity->z - z; - while (xd * xd + zd * zd < 0.0001) - { - xd = (Math::random() - Math::random()) * 0.01; - zd = (Math::random() - Math::random()) * 0.01; - } - hurtDir = (float) (atan2(zd, xd) * 180 / PI) - yRot; - knockback(sourceEntity, dmg, xd, zd); - } - else - { - hurtDir = (float) (int) ((Math::random() * 2) * 180); // 4J This cast is the same as Java - } - } - - MemSect(31); - if (health <= 0) - { - if (sound) level->playSound(shared_from_this(), getDeathSound(), getSoundVolume(), getVoicePitch()); - die(source); - } - else - { - if (sound) level->playSound(shared_from_this(), getHurtSound(), getSoundVolume(), getVoicePitch()); - } - MemSect(0); - - return true; -} - -float Mob::getVoicePitch() -{ - if (isBaby()) - { - return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.5f; - - } - return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f; -} - -void Mob::animateHurt() -{ - hurtTime = hurtDuration = 10; - hurtDir = 0; -} - -int Mob::getArmorValue() -{ - return 0; -} - -void Mob::hurtArmor(int damage) -{ -} - -int Mob::getDamageAfterArmorAbsorb(DamageSource *damageSource, int damage) -{ - if (!damageSource->isBypassArmor()) - { - int absorb = 25 - getArmorValue(); - int v = (damage) * absorb + dmgSpill; - hurtArmor(damage); - damage = v / 25; - dmgSpill = v % 25; - } - return damage; -} - -int Mob::getDamageAfterMagicAbsorb(DamageSource *damageSource, int damage) -{ - if (hasEffect(MobEffect::damageResistance)) - { - int absorbValue = (getEffect(MobEffect::damageResistance)->getAmplifier() + 1) * 5; - int absorb = 25 - absorbValue; - int v = (damage) * absorb + dmgSpill; - damage = v / 25; - dmgSpill = v % 25; + return LivingEntity::tickHeadTurn(yBodyRotT, walkSpeed); } - return damage; -} - -void Mob::actuallyHurt(DamageSource *source, int dmg) -{ - dmg = getDamageAfterArmorAbsorb(source, dmg); - dmg = getDamageAfterMagicAbsorb(source, dmg); - health -= dmg; -} - - -float Mob::getSoundVolume() -{ - return 1; } int Mob::getAmbientSound() @@ -824,82 +250,9 @@ int Mob::getAmbientSound() return -1; } -int Mob::getHurtSound() +int Mob::getDeathLoot() { - return eSoundType_DAMAGE_HURT; -} - -int Mob::getDeathSound() -{ - return eSoundType_DAMAGE_HURT; -} - -void Mob::knockback(shared_ptr<Entity> source, int dmg, double xd, double zd) -{ - hasImpulse = true; - float dd = (float) sqrt(xd * xd + zd * zd); - float pow = 0.4f; - - this->xd /= 2; - this->yd /= 2; - this->zd /= 2; - - this->xd -= xd / dd * pow; - this->yd += pow; - this->zd -= zd / dd * pow; - - if (this->yd > 0.4f) this->yd = 0.4f; -} - -void Mob::die(DamageSource *source) -{ - shared_ptr<Entity> sourceEntity = source->getEntity(); - if (deathScore >= 0 && sourceEntity != NULL) sourceEntity->awardKillScore(shared_from_this(), deathScore); - - if (sourceEntity != NULL) sourceEntity->killed( dynamic_pointer_cast<Mob>( shared_from_this() ) ); - - dead = true; - - if (!level->isClientSide) - { - int playerBonus = 0; - shared_ptr<Player> player = dynamic_pointer_cast<Player>(sourceEntity); - if (player != NULL) - { - playerBonus = EnchantmentHelper::getKillingLootBonus(player->inventory); - } - if (!isBaby()) - { - dropDeathLoot(lastHurtByPlayerTime > 0, playerBonus); - if (lastHurtByPlayerTime > 0) - { - int rareLoot = random->nextInt(200) - playerBonus; - if (rareLoot < 5) - { - dropRareDeathLoot((rareLoot <= 0) ? 1 : 0); - } - } - } - - // 4J-JEV, hook for Durango mobKill event. - if (player != NULL) - { - player->awardStat(GenericStats::killMob(),GenericStats::param_mobKill(player, dynamic_pointer_cast<Mob>(shared_from_this()), source)); - } - } - - level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH); -} - -/** -* Drop extra rare loot. Only occurs roughly 5% of the time, rareRootLevel -* is set to 1 (otherwise 0) 1% of the time. -* -* @param rareLootLevel -*/ -void Mob::dropRareDeathLoot(int rareLootLevel) -{ - + return 0; } void Mob::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) @@ -917,268 +270,85 @@ void Mob::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) } } -int Mob::getDeathLoot() +void Mob::addAdditonalSaveData(CompoundTag *entityTag) { - return 0; -} + LivingEntity::addAdditonalSaveData(entityTag); + entityTag->putBoolean(L"CanPickUpLoot", canPickUpLoot()); + entityTag->putBoolean(L"PersistenceRequired", persistenceRequired); -void Mob::causeFallDamage(float distance) -{ - Entity::causeFallDamage(distance); - int dmg = (int) ceil(distance - 3); - if (dmg > 0) + ListTag<CompoundTag> *gear = new ListTag<CompoundTag>(); + for (int i = 0; i < equipment.length; i++) { - // 4J - new sounds here brought forward from 1.2.3 - if (dmg > 4) - { - level->playSound(shared_from_this(), eSoundType_DAMAGE_FALL_BIG, 1, 1); - } - else - { - level->playSound(shared_from_this(), eSoundType_DAMAGE_FALL_SMALL, 1, 1); - } - hurt(DamageSource::fall, 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; - MemSect(31); - level->playSound(shared_from_this(), soundType->getStepSound(), soundType->getVolume() * 0.5f, soundType->getPitch() * 0.75f); - MemSect(0); - } + CompoundTag *tag = new CompoundTag(); + if (equipment[i] != NULL) equipment[i]->save(tag); + gear->add(tag); } -} + entityTag->put(L"Equipment", gear); -void Mob::travel(float xa, float ya) -{ -#ifdef __PSVITA__ - // AP - dynamic_pointer_cast is a non-trivial call - Player *thisPlayer = NULL; - if( (GetType() & eTYPE_PLAYER) == eTYPE_PLAYER ) + ListTag<FloatTag> *dropChanceList = new ListTag<FloatTag>(); + for (int i = 0; i < dropChances.length; i++) { - thisPlayer = (Player*) this; + dropChanceList->add(new FloatTag( _toString(i), dropChances[i])); } -#else - shared_ptr<Player> thisPlayer = dynamic_pointer_cast<Player>(shared_from_this()); -#endif - if (isInWater() && !(thisPlayer && thisPlayer->abilities.flying) ) - { - double yo = y; - moveRelative(xa, ya, useNewAi() ? 0.04f : 0.02f); - move(xd, yd, zd); - - xd *= 0.80f; - yd *= 0.80f; - zd *= 0.80f; - yd -= 0.02; + entityTag->put(L"DropChances", dropChanceList); + entityTag->putString(L"CustomName", getCustomName()); + entityTag->putBoolean(L"CustomNameVisible", isCustomNameVisible()); - if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd)) - { - yd = 0.3f; - } - } - else if (isInLava() && !(thisPlayer && thisPlayer->abilities.flying) ) + // leash info + entityTag->putBoolean(L"Leashed", _isLeashed); + if (leashHolder != NULL) { - double yo = y; - moveRelative(xa, ya, 0.02f); - move(xd, yd, zd); - xd *= 0.50f; - yd *= 0.50f; - zd *= 0.50f; - yd -= 0.02; - - if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd)) + CompoundTag *leashTag = new CompoundTag(L"Leash"); + if ( leashHolder->instanceof(eTYPE_LIVINGENTITY) ) { - yd = 0.3f; + // a walking, talking, leash holder + leashTag->putString(L"UUID", leashHolder->getUUID()); } - } - else - { - float friction = 0.91f; - if (onGround) + else if ( leashHolder->instanceof(eTYPE_HANGING_ENTITY) ) { - friction = 0.6f * 0.91f; - int t = level->getTile(Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z)); - if (t > 0) - { - friction = Tile::tiles[t]->friction * 0.91f; - } + // a fixed holder (that doesn't save itself) + shared_ptr<HangingEntity> hangInThere = dynamic_pointer_cast<HangingEntity>(leashHolder); + leashTag->putInt(L"X", hangInThere->xTile); + leashTag->putInt(L"Y", hangInThere->yTile); + leashTag->putInt(L"Z", hangInThere->zTile); } - - float friction2 = (0.6f * 0.6f * 0.91f * 0.91f * 0.6f * 0.91f) / (friction * friction * friction); - - float speed; - if (onGround) - { - if (useNewAi()) speed = getSpeed(); - else speed = walkingSpeed; - speed *= friction2; - } - else speed = flyingSpeed; - - moveRelative(xa, ya, speed); - - friction = 0.91f; - if (onGround) - { - friction = 0.6f * 0.91f; - int t = level->getTile( Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z)); - if (t > 0) - { - friction = Tile::tiles[t]->friction * 0.91f; - } - } - if (onLadder()) - { - float max = 0.15f; - if (xd < -max) xd = -max; - if (xd > max) xd = max; - if (zd < -max) zd = -max; - if (zd > max) zd = max; - this->fallDistance = 0; - if (yd < -0.15) yd = -0.15; - bool playerSneaking = isSneaking() && dynamic_pointer_cast<Player>(shared_from_this()) != NULL; - if (playerSneaking && yd < 0) yd = 0; - } - - move(xd, yd, zd); - - if (horizontalCollision && onLadder()) - { - yd = 0.2; - } - - yd -= 0.08; - yd *= 0.98f; - xd *= friction; - zd *= friction; + entityTag->put(L"Leash", leashTag); } - - walkAnimSpeedO = walkAnimSpeed; - double xxd = x - xo; - double 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() +void Mob::readAdditionalSaveData(CompoundTag *tag) { - int xt = Mth::floor(x); - int yt = Mth::floor(bb->y0); - int zt = Mth::floor(z); - - // 4J-PB - TU9 - add climbable vines - int iTile = level->getTile(xt, yt, zt); - return (iTile== Tile::ladder_Id) || (iTile== Tile::vine_Id); -} + LivingEntity::readAdditionalSaveData(tag); + setCanPickUpLoot(tag->getBoolean(L"CanPickUpLoot")); + persistenceRequired = tag->getBoolean(L"PersistenceRequired"); + if (tag->contains(L"CustomName") && tag->getString(L"CustomName").length() > 0) setCustomName(tag->getString(L"CustomName")); + setCustomNameVisible(tag->getBoolean(L"CustomNameVisible")); -bool Mob::isShootable() -{ - return true; -} - -void Mob::addAdditonalSaveData(CompoundTag *entityTag) -{ - entityTag->putShort(L"Health", (short) health); - entityTag->putShort(L"HurtTime", (short) hurtTime); - entityTag->putShort(L"DeathTime", (short) deathTime); - entityTag->putShort(L"AttackTime", (short) attackTime); - - if (!activeEffects.empty()) + if (tag->contains(L"Equipment")) { - ListTag<CompoundTag> *listTag = new ListTag<CompoundTag>(); + ListTag<CompoundTag> *gear = (ListTag<CompoundTag> *) tag->getList(L"Equipment"); - for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it) + for (int i = 0; i < equipment.length; i++) { - MobEffectInstance *effect = it->second; - - CompoundTag *tag = new CompoundTag(); - tag->putByte(L"Id", (BYTE) effect->getId()); - tag->putByte(L"Amplifier", (char) effect->getAmplifier()); - tag->putInt(L"Duration", effect->getDuration()); - listTag->add(tag); + equipment[i] = ItemInstance::fromTag(gear->get(i)); } - entityTag->put(L"ActiveEffects", listTag); } -} - -void Mob::readAdditionalSaveData(CompoundTag *tag) -{ - if (health < Short::MIN_VALUE) health = Short::MIN_VALUE; - health = tag->getShort(L"Health"); - if (!tag->contains(L"Health")) health = getMaxHealth(); - hurtTime = tag->getShort(L"HurtTime"); - deathTime = tag->getShort(L"DeathTime"); - attackTime = tag->getShort(L"AttackTime"); - if (tag->contains(L"ActiveEffects")) + if (tag->contains(L"DropChances")) { - ListTag<CompoundTag> *effects = (ListTag<CompoundTag> *) tag->getList(L"ActiveEffects"); - for (int i = 0; i < effects->size(); i++) + ListTag<FloatTag> *items = (ListTag<FloatTag> *) tag->getList(L"DropChances"); + for (int i = 0; i < items->size(); i++) { - CompoundTag *effectTag = effects->get(i); - int id = effectTag->getByte(L"Id"); - int amplifier = effectTag->getByte(L"Amplifier"); - int duration = effectTag->getInt(L"Duration"); - - activeEffects.insert( unordered_map<int, MobEffectInstance *>::value_type( id, new MobEffectInstance(id, duration, amplifier) ) ); + dropChances[i] = items->get(i)->data; } } -} -bool Mob::isAlive() -{ - return !removed && health > 0; -} - -bool Mob::isWaterMob() -{ - return false; -} - -// 4J - added for more accurate lighting of mobs. Takes a weighted average of all tiles touched by the bounding volume of the entity - the method in the Entity class (which used to be used for -// mobs too) simply gets a single tile's lighting value causing sudden changes of lighting values when entities go in and out of lit areas, for example when bobbing in the water. -int Mob::getLightColor(float a) -{ - float accum[2] = {0,0}; - float totVol = ( bb->x1 - bb->x0 ) * ( bb->y1 - bb->y0 ) * ( bb->z1 - bb->z0 ); - int xmin = Mth::floor(bb->x0); - int xmax = Mth::floor(bb->x1); - int ymin = Mth::floor(bb->y0); - int ymax = Mth::floor(bb->y1); - int zmin = Mth::floor(bb->z0); - int zmax = Mth::floor(bb->z1); - for( int xt = xmin; xt <= xmax; xt++ ) - for( int yt = ymin; yt <= ymax; yt++ ) - for( int zt = zmin; zt <= zmax; zt++ ) - { - float tilexmin = (float)xt; - float tilexmax = (float)(xt+1); - float tileymin = (float)yt; - float tileymax = (float)(yt+1); - float tilezmin = (float)zt; - float tilezmax = (float)(zt+1); - if( tilexmin < bb->x0 ) tilexmin = bb->x0; - if( tilexmax > bb->x1 ) tilexmax = bb->x1; - if( tileymin < bb->y0 ) tileymin = bb->y0; - if( tileymax > bb->y1 ) tileymax = bb->y1; - if( tilezmin < bb->z0 ) tilezmin = bb->z0; - if( tilezmax > bb->z1 ) tilezmax = bb->z1; - float tileVol = ( tilexmax - tilexmin ) * ( tileymax - tileymin ) * ( tilezmax - tilezmin ); - float frac = tileVol / totVol; - int lc = level->getLightColor(xt, yt, zt, 0); - accum[0] += frac * (float)( lc & 0xffff ); - accum[1] += frac * (float)( lc >> 16 ); - } - - if( accum[0] > 240.0f ) accum[0] = 240.0f; - if( accum[1] > 240.0f ) accum[1] = 240.0f; - - return ( ( (int)accum[1])<<16) | ((int)accum[0]); + _isLeashed = tag->getBoolean(L"Leashed"); + if (_isLeashed && tag->contains(L"Leash")) + { + leashInfoTag = (CompoundTag *)tag->getCompound(L"Leash")->copy(); + } } void Mob::setYya(float yya) @@ -1186,123 +356,99 @@ void Mob::setYya(float yya) this->yya = yya; } -void Mob::setJumping(bool jump) +void Mob::setSpeed(float speed) { - jumping = jump; + LivingEntity::setSpeed(speed); + setYya(speed); } void Mob::aiStep() { - if (noJumpDelay > 0) noJumpDelay--; - if (lSteps > 0) - { - double xt = x + (lx - x) / lSteps; - double yt = y + (ly - y) / lSteps; - double zt = z + (lz - z) / lSteps; - - double yrd = Mth::wrapDegrees(lyr - yRot); - double xrd = Mth::wrapDegrees(lxr - xRot); - - yRot += (float) ( (yrd) / lSteps ); - xRot += (float) ( (xrd) / lSteps ); - - lSteps--; - this->setPos(xt, yt, zt); - this->setRot(yRot, xRot); - - // 4J - this collision is carried out to try and stop the lerping push the mob through the floor, - // in which case gravity can then carry on moving the mob because the collision just won't work anymore. - // BB for collision used to be calculated as: bb->shrink(1 / 32.0, 0, 1 / 32.0) - // now using a reduced BB to try and get rid of some issues where mobs pop up the sides of walls, undersides of - // trees etc. - AABB *shrinkbb = bb->shrink(0.1, 0, 0.1); - shrinkbb->y1 = shrinkbb->y0 + 0.1; - AABBList *collisions = level->getCubes(shared_from_this(), shrinkbb); - if (collisions->size() > 0) - { - double yTop = 0; - AUTO_VAR(itEnd, collisions->end()); - for (AUTO_VAR(it, collisions->begin()); it != itEnd; it++) - { - AABB *ab = *it; //collisions->at(i); - if (ab->y1 > yTop) yTop = ab->y1; - } - - yt += yTop - bb->y0; - setPos(xt, yt, zt); - } - if (abs(xd) < MIN_MOVEMENT_DISTANCE) xd = 0; - if (abs(yd) < MIN_MOVEMENT_DISTANCE) yd = 0; - if (abs(zd) < MIN_MOVEMENT_DISTANCE) zd = 0; - } + LivingEntity::aiStep(); - if (isImmobile()) - { - jumping = false; - xxa = 0; - yya = 0; - yRotA = 0; - } - else + if (!level->isClientSide && canPickUpLoot() && !dead && level->getGameRules()->getBoolean(GameRules::RULE_MOBGRIEFING)) { - MemSect(25); - if (isEffectiveAI()) + vector<shared_ptr<Entity> > *entities = level->getEntitiesOfClass(typeid(ItemEntity), bb->grow(1, 0, 1)); + for (AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) { - if (useNewAi()) - { - newServerAiStep(); - } - else - { - serverAiStep(); - yHeadRot = yRot; - } - } - MemSect(0); - } + shared_ptr<ItemEntity> entity = dynamic_pointer_cast<ItemEntity>(*it); + if (entity->removed || entity->getItem() == NULL) continue; + shared_ptr<ItemInstance> item = entity->getItem(); + int slot = getEquipmentSlotForItem(item); - if (jumping) - { - if (isInWater() || isInLava() ) - { - yd += 0.04f; - } - else if (onGround) - { - if (noJumpDelay == 0) + if (slot > -1) { - jumpFromGround(); - noJumpDelay = 10; - } - } - } - else - { - noJumpDelay = 0; - } - + bool replace = true; + shared_ptr<ItemInstance> current = getCarried(slot); - xxa *= 0.98f; - yya *= 0.98f; - yRotA *= 0.9f; - - float normalSpeed = walkingSpeed; - walkingSpeed *= getWalkingSpeedModifier(); - travel(xxa, yya); - walkingSpeed = normalSpeed; + if (current != NULL) + { + if (slot == SLOT_WEAPON) + { + WeaponItem *newWeapon = dynamic_cast<WeaponItem *>(item->getItem()); + WeaponItem *oldWeapon = dynamic_cast<WeaponItem *>(current->getItem()); + if ( newWeapon != NULL && oldWeapon == NULL) + { + replace = true; + } + else if (newWeapon != NULL && oldWeapon != NULL) + { + if (newWeapon->getTierDamage() == oldWeapon->getTierDamage()) + { + replace = item->getAuxValue() > current->getAuxValue() || item->hasTag() && !current->hasTag(); + } + else + { + replace = newWeapon->getTierDamage() > oldWeapon->getTierDamage(); + } + } + else + { + replace = false; + } + } + else + { + ArmorItem *newArmor = dynamic_cast<ArmorItem *>(item->getItem()); + ArmorItem *oldArmor = dynamic_cast<ArmorItem *>(current->getItem()); + if (newArmor != NULL && oldArmor == NULL) + { + replace = true; + } + else if (newArmor != NULL && oldArmor != NULL) + { + if (newArmor->defense == oldArmor->defense) + { + replace = item->getAuxValue() > current->getAuxValue() || item->hasTag() && !current->hasTag(); + } + else + { + replace = newArmor->defense > oldArmor->defense; + } + } + else + { + replace = false; + } + } + } - if(!level->isClientSide) - { - vector<shared_ptr<Entity> > *entities = level->getEntities(shared_from_this(), this->bb->grow(0.2f, 0, 0.2f)); - if (entities != NULL && !entities->empty()) - { - AUTO_VAR(itEnd, entities->end()); - for (AUTO_VAR(it, entities->begin()); it != itEnd; it++) - { - shared_ptr<Entity> e = *it; //entities->at(i); - if (e->isPushable()) e->push(shared_from_this()); + if (replace) + { + if (current != NULL && random->nextFloat() - 0.1f < dropChances[slot]) + { + spawnAtLocation(current, 0); + } + + setEquippedSlot(slot, item); + dropChances[slot] = 2; + persistenceRequired = true; + take(entity, 1); + entity->remove(); + } } } + delete entities; } } @@ -1311,38 +457,6 @@ bool Mob::useNewAi() return false; } -bool Mob::isEffectiveAI() -{ - return !level->isClientSide; -} - -bool Mob::isImmobile() -{ - return health <= 0; -} - -bool Mob::isBlocking() -{ - return false; -} - -void Mob::jumpFromGround() -{ - yd = 0.42f; - if (hasEffect(MobEffect::jump)) - { - yd += (getEffect(MobEffect::jump)->getAmplifier() + 1) * .1f; - } - if (isSprinting()) - { - float rr = yRot * Mth::RAD_TO_GRAD; - - xd -= Mth::sin(rr) * 0.2f; - zd += Mth::cos(rr) * 0.2f; - } - this->hasImpulse = true; -} - bool Mob::removeWhenFarAway() { return true; @@ -1350,6 +464,11 @@ bool Mob::removeWhenFarAway() void Mob::checkDespawn() { + if (persistenceRequired) + { + noActionTime = 0; + return; + } shared_ptr<Entity> player = level->getNearestPlayer(shared_from_this(), -1); if (player != NULL) { @@ -1376,36 +495,54 @@ void Mob::checkDespawn() void Mob::newServerAiStep() { + PIXBeginNamedEvent(0,"Tick target selector for %d",GetType()); MemSect(51); noActionTime++; + PIXBeginNamedEvent(0,"Check despawn"); checkDespawn(); - sensing->tick(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Tick sensing"); + sensing->tick(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Tick target selector"); targetSelector.tick(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Tick goal selectors"); goalSelector.tick(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Tick navigation"); navigation->tick(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Tick server ai mob step"); serverAiMobStep(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Tick move"); moveControl->tick(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Tick look"); lookControl->tick(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Tick jump"); jumpControl->tick(); + PIXEndNamedEvent(); // Consider this for extra strolling if it is protected against despawning. We aren't interested in ones that aren't protected as the whole point of this // extra wandering is to potentially transition from protected to not protected. + PIXBeginNamedEvent(0,"Consider extra wandering"); considerForExtraWandering( isDespawnProtected() ); + PIXEndNamedEvent(); MemSect(0); -} - -void Mob::serverAiMobStep() -{ + PIXEndNamedEvent(); } void Mob::serverAiStep() { - noActionTime++; - - checkDespawn(); + LivingEntity::serverAiStep(); xxa = 0; yya = 0; + checkDespawn(); + float lookDistance = 8; if (random->nextFloat() < 0.02f) { @@ -1454,11 +591,12 @@ void Mob::lookAt(shared_ptr<Entity> e, float yMax, float xMax) double xd = e->x - x; double yd; double zd = e->z - z; + - shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(e); - if(mob != NULL) + if ( e->instanceof(eTYPE_LIVINGENTITY) ) { - yd = (y + getHeadHeight()) - (mob->y + mob->getHeadHeight()); + shared_ptr<LivingEntity> mob = dynamic_pointer_cast<LivingEntity>(e); + yd = (mob->y + mob->getHeadHeight()) - (y + getHeadHeight()); } else { @@ -1469,7 +607,7 @@ void Mob::lookAt(shared_ptr<Entity> e, float yMax, float xMax) float yRotD = (float) (atan2(zd, xd) * 180 / PI) - 90; float xRotD = (float) -(atan2(yd, sd) * 180 / PI); - xRot = -rotlerp(xRot, xRotD, xMax); + xRot = rotlerp(xRot, xRotD, xMax); yRot = rotlerp(yRot, yRotD, yMax); } @@ -1503,59 +641,6 @@ bool Mob::canSpawn() return level->isUnobstructed(bb) && level->getCubes(shared_from_this(), bb)->empty() && !level->containsAnyLiquid_NoLoad(bb); } -void Mob::outOfWorld() -{ - hurt(DamageSource::outOfWorld, 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::newTemp(x, y, z); - } - double x = xo + (this->x - xo) * a; - double y = yo + (this->y - yo) * a; - double z = zo + (this->z - zo) * a; - - return Vec3::newTemp(x, y, z); -} - -Vec3 *Mob::getLookAngle() -{ - return getViewVector(1); -} - -Vec3 *Mob::getViewVector(float a) -{ - if (a == 1) - { - float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI); - float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI); - float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD); - float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD); - - return Vec3::newTemp(ySin * xCos, xSin, yCos * xCos); - } - float xRot = xRotO + (this->xRot - xRotO) * a; - float yRot = yRotO + (this->yRot - yRotO) * a; - - float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI); - float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI); - float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD); - float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD); - - return Vec3::newTemp(ySin * xCos, xSin, yCos * xCos); -} - float Mob::getSizeScale() { return 1.0f; @@ -1566,362 +651,421 @@ float Mob::getHeadSizeScale() return 1.0f; } -HitResult *Mob::pick(double range, float a) -{ - Vec3 *from = getPos(a); - Vec3 *b = getViewVector(a); - Vec3 *to = from->add(b->x * range, b->y * range, b->z * range); - return level->clip(from, to); -} - int Mob::getMaxSpawnClusterSize() { return 4; } -shared_ptr<ItemInstance> Mob::getCarriedItem() +int Mob::getMaxFallDistance() { - return nullptr; + if (getTarget() == NULL) return 3; + int sacrifice = (int) (getHealth() - (getMaxHealth() * 0.33f)); + sacrifice -= (3 - level->difficulty) * 4; + if (sacrifice < 0) sacrifice = 0; + return sacrifice + 3; } -shared_ptr<ItemInstance> Mob::getArmor(int pos) +shared_ptr<ItemInstance> Mob::getCarriedItem() { - // 4J Stu - Not implemented yet - return nullptr; - //return equipment[pos + 1]; + return equipment[SLOT_WEAPON]; } -void Mob::handleEntityEvent(byte id) +shared_ptr<ItemInstance> Mob::getCarried(int slot) { - if (id == EntityEvent::HURT) - { - this->walkAnimSpeed = 1.5f; - - invulnerableTime = invulnerableDuration; - hurtTime = hurtDuration = 10; - hurtDir = 0; - - MemSect(31); - // 4J-PB -added because villagers have no sounds - int iHurtSound=getHurtSound(); - if(iHurtSound!=-1) - { - level->playSound(shared_from_this(), iHurtSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); - } - MemSect(0); - hurt(DamageSource::genericSource, 0); - } - else if (id == EntityEvent::DEATH) - { - MemSect(31); - // 4J-PB -added because villagers have no sounds - int iDeathSound=getDeathSound(); - if(iDeathSound!=-1) - { - level->playSound(shared_from_this(), iDeathSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); - } - MemSect(0); - health = 0; - die(DamageSource::genericSource); - } - else - { - Entity::handleEntityEvent(id); - } + return equipment[slot]; } -bool Mob::isSleeping() +shared_ptr<ItemInstance> Mob::getArmor(int pos) { - return false; + return equipment[pos + 1]; } -Icon *Mob::getItemInHandIcon(shared_ptr<ItemInstance> item, int layer) +void Mob::setEquippedSlot(int slot, shared_ptr<ItemInstance> item) { - return item->getIcon(); + equipment[slot] = item; } -// 4J added so we can not render mobs before their chunks are loaded - to resolve bug 10327 :Gameplay: NPCs can spawn over chunks that have not yet been streamed and display jitter. -bool Mob::shouldRender(Vec3 *c) +ItemInstanceArray Mob::getEquipmentSlots() { - if( !level->reallyHasChunksAt( Mth::floor(bb->x0), Mth::floor(bb->y0), Mth::floor(bb->z0), Mth::floor(bb->x1), Mth::floor(bb->y1), Mth::floor(bb->z1))) - { - return false; - } - return Entity::shouldRender(c); + return equipment; } -void Mob::tickEffects() +void Mob::dropEquipment(bool byPlayer, int playerBonusLevel) { - bool removed = false; - for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end();) + for (int slot = 0; slot < getEquipmentSlots().length; slot++) { - MobEffectInstance *effect = it->second; - removed = false; - if (!effect->tick(dynamic_pointer_cast<Mob>(shared_from_this()))) + shared_ptr<ItemInstance> item = getCarried(slot); + bool preserve = dropChances[slot] > 1; + + if (item != NULL && (byPlayer || preserve) && random->nextFloat() - playerBonusLevel * 0.01f < dropChances[slot]) { - if (!level->isClientSide) + if (!preserve && item->isDamageableItem()) { - it = activeEffects.erase( it ); - onEffectRemoved(effect); - delete effect; - removed = true; + int _max = max(item->getMaxDamage() - 25, 1); + int damage = item->getMaxDamage() - random->nextInt(random->nextInt(_max) + 1); + if (damage > _max) damage = _max; + if (damage < 1) damage = 1; + item->setAuxValue(damage); } - } - if(!removed) - { - ++it; + spawnAtLocation(item, 0); } } - if (effectsDirty) +} + +void Mob::populateDefaultEquipmentSlots() +{ + if (random->nextFloat() < MAX_WEARING_ARMOR_CHANCE * level->getDifficulty(x, y, z)) { - if (!level->isClientSide) + int armorType = random->nextInt(2); + float partialChance = level->difficulty == Difficulty::HARD ? 0.1f : 0.25f; + if (random->nextFloat() < 0.095f) armorType++; + if (random->nextFloat() < 0.095f) armorType++; + if (random->nextFloat() < 0.095f) armorType++; + + for (int i = 3; i >= 0; i--) { - if (activeEffects.empty()) + shared_ptr<ItemInstance> item = getArmor(i); + if (i < 3 && random->nextFloat() < partialChance) break; + if (item == NULL) { - entityData->set(DATA_EFFECT_COLOR_ID, (int) 0); - setInvisible(false); - setWeakened(false); - } - else - { - vector<MobEffectInstance *> values; - for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end();++it) - { - values.push_back(it->second); - } - int colorValue = PotionBrewing::getColorValue(&values); - values.clear(); - entityData->set(DATA_EFFECT_COLOR_ID, colorValue); - setInvisible(hasEffect(MobEffect::invisibility->id)); - setWeakened(hasEffect(MobEffect::weakness->id)); + Item *equip = getEquipmentForSlot(i + 1, armorType); + if (equip != NULL) setEquippedSlot(i + 1, shared_ptr<ItemInstance>(new ItemInstance(equip))); } } - effectsDirty = false; } - if (random->nextBoolean()) +} + +int Mob::getEquipmentSlotForItem(shared_ptr<ItemInstance> item) +{ + if (item->id == Tile::pumpkin_Id || item->id == Item::skull_Id) { - int colorValue = entityData->getInteger(DATA_EFFECT_COLOR_ID); - if (colorValue > 0) - { - double red = (double) ((colorValue >> 16) & 0xff) / 255.0; - double green = (double) ((colorValue >> 8) & 0xff) / 255.0; - double blue = (double) ((colorValue >> 0) & 0xff) / 255.0; + return SLOT_HELM; + } - level->addParticle(eParticleType_mobSpell, x + (random->nextDouble() - 0.5) * bbWidth, y + random->nextDouble() * bbHeight - heightOffset, z + (random->nextDouble() - 0.5) * bbWidth, red, green, blue); + ArmorItem *armorItem = dynamic_cast<ArmorItem *>(item->getItem()); + if (armorItem != NULL) + { + switch (armorItem->slot) + { + case ArmorItem::SLOT_FEET: + return SLOT_BOOTS; + case ArmorItem::SLOT_LEGS: + return SLOT_LEGGINGS; + case ArmorItem::SLOT_TORSO: + return SLOT_CHEST; + case ArmorItem::SLOT_HEAD: + return SLOT_HELM; } } + + return SLOT_WEAPON; } -void Mob::removeAllEffects() +Item *Mob::getEquipmentForSlot(int slot, int type) { - //Iterator<Integer> effectIdIterator = activeEffects.keySet().iterator(); - //while (effectIdIterator.hasNext()) - for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ) + switch (slot) { - //Integer effectId = effectIdIterator.next(); - MobEffectInstance *effect = it->second;//activeEffects.get(effectId); + case SLOT_HELM: + if (type == 0) return Item::helmet_leather; + if (type == 1) return Item::helmet_gold; + if (type == 2) return Item::helmet_chain; + if (type == 3) return Item::helmet_iron; + if (type == 4) return Item::helmet_diamond; + case SLOT_CHEST: + if (type == 0) return Item::chestplate_leather; + if (type == 1) return Item::chestplate_gold; + if (type == 2) return Item::chestplate_chain; + if (type == 3) return Item::chestplate_iron; + if (type == 4) return Item::chestplate_diamond; + case SLOT_LEGGINGS: + if (type == 0) return Item::leggings_leather; + if (type == 1) return Item::leggings_gold; + if (type == 2) return Item::leggings_chain; + if (type == 3) return Item::leggings_iron; + if (type == 4) return Item::leggings_diamond; + case SLOT_BOOTS: + if (type == 0) return Item::boots_leather; + if (type == 1) return Item::boots_gold; + if (type == 2) return Item::boots_chain; + if (type == 3) return Item::boots_iron; + if (type == 4) return Item::boots_diamond; + } - if (!level->isClientSide) - { - //effectIdIterator.remove(); - it = activeEffects.erase(it); - onEffectRemoved(effect); - delete effect; - } - else + return NULL; +} + +void Mob::populateDefaultEquipmentEnchantments() +{ + float difficulty = level->getDifficulty(x, y, z); + + if (getCarriedItem() != NULL && random->nextFloat() < MAX_ENCHANTED_WEAPON_CHANCE * difficulty) { + EnchantmentHelper::enchantItem(random, getCarriedItem(), (int) (5 + difficulty * random->nextInt(18))); + } + + for (int i = 0; i < 4; i++) + { + shared_ptr<ItemInstance> item = getArmor(i); + if (item != NULL && random->nextFloat() < MAX_ENCHANTED_ARMOR_CHANCE * difficulty) { - ++it; + EnchantmentHelper::enchantItem(random, item, (int) (5 + difficulty * random->nextInt(18))); } } } -vector<MobEffectInstance *> *Mob::getActiveEffects() +/** +* Added this method so mobs can handle their own spawn settings instead of +* hacking MobSpawner.java +* +* @param groupData +* TODO +* @return TODO +*/ +MobGroupData *Mob::finalizeMobSpawn(MobGroupData *groupData, int extraData /*= 0*/) // 4J Added extraData param { - vector<MobEffectInstance *> *active = new vector<MobEffectInstance *>(); + // 4J Stu - Take this out, it's not great and nobody will notice. Also not great for performance. + //getAttribute(SharedMonsterAttributes::FOLLOW_RANGE)->addModifier(new AttributeModifier(random->nextGaussian() * 0.05, AttributeModifier::OPERATION_MULTIPLY_BASE)); - for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it) - { - active->push_back(it->second); - } + return groupData; +} - return active; +void Mob::finalizeSpawnEggSpawn(int extraData) +{ } -bool Mob::hasEffect(int id) +bool Mob::canBeControlledByRider() { - return activeEffects.find(id) != activeEffects.end();; + return false; } -bool Mob::hasEffect(MobEffect *effect) +wstring Mob::getAName() { - return activeEffects.find(effect->id) != activeEffects.end(); + if (hasCustomName()) return getCustomName(); + return LivingEntity::getAName(); } -MobEffectInstance *Mob::getEffect(MobEffect *effect) +void Mob::setPersistenceRequired() { - MobEffectInstance *effectInst = NULL; + persistenceRequired = true; +} - AUTO_VAR(it, activeEffects.find(effect->id)); - if(it != activeEffects.end() ) effectInst = it->second; +void Mob::setCustomName(const wstring &name) +{ + entityData->set(DATA_CUSTOM_NAME, name); +} - return effectInst; +wstring Mob::getCustomName() +{ + return entityData->getString(DATA_CUSTOM_NAME); } -void Mob::addEffect(MobEffectInstance *newEffect) +bool Mob::hasCustomName() { - if (!canBeAffected(newEffect)) - { - return; - } + return entityData->getString(DATA_CUSTOM_NAME).length() > 0; +} - if (activeEffects.find(newEffect->getId()) != activeEffects.end() ) - { - // replace effect and update - MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second; - effectInst->update(newEffect); - onEffectUpdated(effectInst); - } - else - { - activeEffects.insert( unordered_map<int, MobEffectInstance *>::value_type( newEffect->getId(), newEffect ) ); - onEffectAdded(newEffect); - } +void Mob::setCustomNameVisible(bool visible) +{ + entityData->set(DATA_CUSTOM_NAME_VISIBLE, visible ? (byte) 1 : (byte) 0); } -// 4J Added -void Mob::addEffectNoUpdate(MobEffectInstance *newEffect) +bool Mob::isCustomNameVisible() { - if (!canBeAffected(newEffect)) - { - return; - } + return entityData->getByte(DATA_CUSTOM_NAME_VISIBLE) == 1; +} - if (activeEffects.find(newEffect->getId()) != activeEffects.end() ) - { - // replace effect and update - MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second; - effectInst->update(newEffect); - } - else - { - activeEffects.insert( unordered_map<int, MobEffectInstance *>::value_type( newEffect->getId(), newEffect ) ); - } +bool Mob::shouldShowName() +{ + return isCustomNameVisible(); } -bool Mob::canBeAffected(MobEffectInstance *newEffect) +void Mob::setDropChance(int slot, float pct) { - if (getMobType() == UNDEAD) - { - int id = newEffect->getId(); - if (id == MobEffect::regeneration->id || id == MobEffect::poison->id) - { - return false; - } - } + dropChances[slot] = pct; +} - return true; +bool Mob::canPickUpLoot() +{ + return _canPickUpLoot; } -bool Mob::isInvertedHealAndHarm() +void Mob::setCanPickUpLoot(bool canPickUpLoot) { - return getMobType() == UNDEAD; + _canPickUpLoot = canPickUpLoot; } -void Mob::removeEffectNoUpdate(int effectId) +bool Mob::isPersistenceRequired() { - AUTO_VAR(it, activeEffects.find(effectId)); - if (it != activeEffects.end()) - { - MobEffectInstance *effect = it->second; - if(effect != NULL) - { - delete effect; - } - activeEffects.erase(it); - } + return persistenceRequired; } -void Mob::removeEffect(int effectId) +bool Mob::interact(shared_ptr<Player> player) { - AUTO_VAR(it, activeEffects.find(effectId)); - if (it != activeEffects.end()) + + if (isLeashed() && getLeashHolder() == player) + { + dropLeash(true, !player->abilities.instabuild); + return true; + } + + shared_ptr<ItemInstance> itemstack = player->inventory->getSelected(); + if (itemstack != NULL) { - MobEffectInstance *effect = it->second; - if(effect != NULL) + // it's inconvenient to have the leash code here, but it's because + // the mob.interact(player) method has priority over + // item.interact(mob) + if (itemstack->id == Item::lead_Id) { - onEffectRemoved(effect); - delete effect; + if (canBeLeashed()) + { + shared_ptr<TamableAnimal> tamableAnimal = nullptr; + if ( shared_from_this()->instanceof(eTYPE_TAMABLE_ANIMAL) + && (tamableAnimal = dynamic_pointer_cast<TamableAnimal>(shared_from_this()))->isTame() ) // 4J-JEV: excuse the assignment operator in here, don't want to dyn-cast if it's avoidable. + { + if (player->getUUID().compare(tamableAnimal->getOwnerUUID()) == 0) + { + setLeashedTo(player, true); + itemstack->count--; + return true; + } + } + else + { + setLeashedTo(player, true); + itemstack->count--; + return true; + } + } } - activeEffects.erase(it); } -} -void Mob::onEffectAdded(MobEffectInstance *effect) -{ - effectsDirty = true; -} + if (mobInteract(player)) + { + return true; + } -void Mob::onEffectUpdated(MobEffectInstance *effect) -{ - effectsDirty = true; + return LivingEntity::interact(player); } -void Mob::onEffectRemoved(MobEffectInstance *effect) +bool Mob::mobInteract(shared_ptr<Player> player) { - effectsDirty = true; + return false; } -float Mob::getWalkingSpeedModifier() +void Mob::tickLeash() { - float speed = 1.0f; - if (hasEffect(MobEffect::movementSpeed)) + if (leashInfoTag != NULL) { - speed *= 1.0f + .2f * (getEffect(MobEffect::movementSpeed)->getAmplifier() + 1); + restoreLeashFromSave(); } - if (hasEffect(MobEffect::movementSlowdown)) + if (!_isLeashed) { - speed *= 1.0f - .15f * (getEffect(MobEffect::movementSlowdown)->getAmplifier() + 1); + return; + } + + if (leashHolder == NULL || leashHolder->removed) + { + dropLeash(true, true); + return; } - return speed; } -void Mob::teleportTo(double x, double y, double z) +void Mob::dropLeash(bool synch, bool createItemDrop) { - moveTo(x, y, z, yRot, xRot); + if (_isLeashed) + { + _isLeashed = false; + leashHolder = nullptr; + if (!level->isClientSide && createItemDrop) + { + spawnAtLocation(Item::lead_Id, 1); + } + + ServerLevel *serverLevel = dynamic_cast<ServerLevel *>(level); + if (!level->isClientSide && synch && serverLevel != NULL) + { + serverLevel->getTracker()->broadcast(shared_from_this(), shared_ptr<SetEntityLinkPacket>(new SetEntityLinkPacket(SetEntityLinkPacket::LEASH, shared_from_this(), nullptr))); + } + } } -bool Mob::isBaby() +bool Mob::canBeLeashed() { - return false; + return !isLeashed() && !shared_from_this()->instanceof(eTYPE_ENEMY); } -MobType Mob::getMobType() +bool Mob::isLeashed() { - return UNDEFINED; + return _isLeashed; } -void Mob::breakItem(shared_ptr<ItemInstance> itemInstance) +shared_ptr<Entity> Mob::getLeashHolder() { - level->playSound(shared_from_this(), eSoundType_RANDOM_BREAK, 0.8f, 0.8f + level->random->nextFloat() * 0.4f); + return leashHolder; +} - for (int i = 0; i < 5; i++) +void Mob::setLeashedTo(shared_ptr<Entity> holder, bool synch) +{ + _isLeashed = true; + leashHolder = holder; + + ServerLevel *serverLevel = dynamic_cast<ServerLevel *>(level); + if (!level->isClientSide && synch && serverLevel) { - Vec3 *d = Vec3::newTemp((random->nextFloat() - 0.5) * 0.1, Math::random() * 0.1 + 0.1, 0); - d->xRot(-xRot * PI / 180); - d->yRot(-yRot * PI / 180); - - Vec3 *p = Vec3::newTemp((random->nextFloat() - 0.5) * 0.3, -random->nextFloat() * 0.6 - 0.3, 0.6); - p->xRot(-xRot * PI / 180); - p->yRot(-yRot * PI / 180); - p = p->add(x, y + getHeadHeight(), z); - level->addParticle(PARTICLE_ICONCRACK(itemInstance->getItem()->id,0), p->x, p->y, p->z, d->x, d->y + 0.05, d->z); + serverLevel->getTracker()->broadcast(shared_from_this(), shared_ptr<SetEntityLinkPacket>( new SetEntityLinkPacket(SetEntityLinkPacket::LEASH, shared_from_this(), leashHolder))); } } -bool Mob::isInvulnerable() +void Mob::restoreLeashFromSave() { - // 4J-JEV: I have no idea what was going on here (it gets changed in a later java version). - return invulnerableTime > 0; // invulnerableTime <= invulnerableTime / 2; + // after being added to the world, attempt to recreate leash bond + if (_isLeashed && leashInfoTag != NULL) + { + if (leashInfoTag->contains(L"UUID")) + { + wstring leashUuid = leashInfoTag->getString(L"UUID"); + vector<shared_ptr<Entity> > *livingEnts = level->getEntitiesOfClass(typeid(LivingEntity), bb->grow(10, 10, 10)); + for(AUTO_VAR(it, livingEnts->begin()); it != livingEnts->end(); ++it) + { + shared_ptr<LivingEntity> le = dynamic_pointer_cast<LivingEntity>(*it); + if (le->getUUID().compare(leashUuid) == 0) + { + leashHolder = le; + setLeashedTo(leashHolder, true); + break; + } + } + delete livingEnts; + } + else if (leashInfoTag->contains(L"X") && leashInfoTag->contains(L"Y") && leashInfoTag->contains(L"Z")) + { + int x = leashInfoTag->getInt(L"X"); + int y = leashInfoTag->getInt(L"Y"); + int z = leashInfoTag->getInt(L"Z"); + + shared_ptr<LeashFenceKnotEntity> activeKnot = LeashFenceKnotEntity::findKnotAt(level, x, y, z); + if (activeKnot == NULL) + { + activeKnot = LeashFenceKnotEntity::createAndAddKnot(level, x, y, z); + } + leashHolder = activeKnot; + setLeashedTo(leashHolder, true); + } + else + { + dropLeash(false, true); + } + } + leashInfoTag = NULL; +} + +// 4J added so we can not render mobs before their chunks are loaded - to resolve bug 10327 :Gameplay: NPCs can spawn over chunks that have not yet been streamed and display jitter. +bool Mob::shouldRender(Vec3 *c) +{ + if( !level->reallyHasChunksAt( Mth::floor(bb->x0), Mth::floor(bb->y0), Mth::floor(bb->z0), Mth::floor(bb->x1), Mth::floor(bb->y1), Mth::floor(bb->z1))) + { + return false; + } + return Entity::shouldRender(c); } void Mob::setLevel(Level *level) @@ -1930,14 +1074,4 @@ void Mob::setLevel(Level *level) navigation->setLevel(level); goalSelector.setLevel(level); targetSelector.setLevel(level); -} - -void Mob::finalizeMobSpawn() -{ - -} - -bool Mob::canBeControlledByRider() -{ - return false; -} +}
\ No newline at end of file |
