Something went wrong. Try again.
the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
Something went wrong. Try again.
48 kB · 2005 lines
C++
at master
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005#include "stdafx.h"#include "JavaMath.h"#include "Mth.h"#include "net.minecraft.network.packet.h"#include "net.minecraft.world.level.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"#include "net.minecraft.world.entity.player.h"#include "net.minecraft.world.entity.animal.h"#include "net.minecraft.world.entity.monster.h"#include "net.minecraft.world.item.h"#include "net.minecraft.world.level.h"#include "net.minecraft.world.level.chunk.h"#include "net.minecraft.world.level.material.h"#include "net.minecraft.world.damagesource.h"#include "net.minecraft.world.effect.h"#include "net.minecraft.world.item.alchemy.h"#include "net.minecraft.world.item.enchantment.h"#include "net.minecraft.world.scores.h"#include "com.mojang.nbt.h"#include "LivingEntity.h"#include "..\Minecraft.Client\Textures.h"#include "..\Minecraft.Client\ServerLevel.h"#include "..\Minecraft.Client\EntityTracker.h"#include "SoundTypes.h"#include "BasicTypeContainers.h"#include "ParticleTypes.h"#include "GenericStats.h"#include "ItemEntity.h"
const double LivingEntity::MIN_MOVEMENT_DISTANCE = 0.005;
AttributeModifier *LivingEntity::SPEED_MODIFIER_SPRINTING = (new AttributeModifier(eModifierId_MOB_SPRINTING, 0.3f, AttributeModifier::OPERATION_MULTIPLY_TOTAL))->setSerialize(false);
void LivingEntity::_init(){ attributes = NULL; combatTracker = new CombatTracker(this); lastEquipment = ItemInstanceArray(5);
swinging = false; swingTime = 0; removeArrowTime = 0; lastHealth = 0.0f;
hurtTime = 0; hurtDuration = 0; hurtDir = 0.0f; deathTime = 0; attackTime = 0; oAttackAnim = attackAnim = 0.0f;
walkAnimSpeedO = 0.0f; walkAnimSpeed = 0.0f; walkAnimPos = 0.0f; invulnerableDuration = 20; oTilt = tilt = 0.0f; timeOffs = 0.0f; rotA = 0.0f; yBodyRot = yBodyRotO = 0.0f; yHeadRot = yHeadRotO = 0.0f; flyingSpeed = 0.02f;
lastHurtByPlayer = nullptr; lastHurtByPlayerTime = 0; dead = false; noActionTime = 0; oRun = run = 0.0f; animStep = animStepO = 0.0f; rotOffs = 0.0f; deathScore = 0; lastHurt = 0.0f; jumping = false;
xxa = 0.0f; yya = 0.0f; yRotA = 0.0f; lSteps = 0; lx = ly = lz = lyr = lxr = 0.0;
effectsDirty = false;
lastHurtByMob = nullptr; lastHurtByMobTimestamp = 0; lastHurtMob = nullptr; lastHurtMobTimestamp = 0;
speed = 0.0f; noJumpDelay = 0; absorptionAmount = 0.0f;}
LivingEntity::LivingEntity( Level* level) : Entity(level){ MemSect(56); _init(); MemSect(0);
// 4J Stu - This will not call the correct derived function, so moving to each derived class //setHealth(0); //registerAttributes();
blocksBuilding = true;
rotA = (float) (Math::random() + 1) * 0.01f; setPos(x, y, z); timeOffs = (float) Math::random() * 12398; yRot = (float) (Math::random() * PI * 2); yHeadRot = yRot;
footSize = 0.5f;}
LivingEntity::~LivingEntity(){ for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it) { delete it->second; }
delete attributes; delete combatTracker;
if(lastEquipment.data != NULL) delete [] lastEquipment.data;}
void LivingEntity::defineSynchedData() { entityData->define(DATA_EFFECT_COLOR_ID, 0); entityData->define(DATA_EFFECT_AMBIENCE_ID, (byte) 0); entityData->define(DATA_ARROW_COUNT_ID, (byte) 0); entityData->define(DATA_HEALTH_ID, 1.0f);}
void LivingEntity::registerAttributes(){ getAttributes()->registerAttribute(SharedMonsterAttributes::MAX_HEALTH); getAttributes()->registerAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE); getAttributes()->registerAttribute(SharedMonsterAttributes::MOVEMENT_SPEED);
if (!useNewAi()) { getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.1f); }}
void LivingEntity::checkFallDamage(double ya, bool onGround){ if (!isInWater()) { // double-check if we've reached water in this move tick updateInWaterState(); }
if (onGround && fallDistance > 0) { int xt = Mth::floor(x); int yt = Mth::floor(y - 0.2f - heightOffset); int zt = Mth::floor(z); int t = level->getTile(xt, yt, zt); if (t == 0) { int renderShape = level->getTileRenderShape(xt, yt - 1, zt); if (renderShape == Tile::SHAPE_FENCE || renderShape == Tile::SHAPE_WALL || renderShape == Tile::SHAPE_FENCE_GATE) { t = level->getTile(xt, yt - 1, zt); } }
if (t > 0) { Tile::tiles[t]->fallOn(level, xt, yt, zt, shared_from_this(), fallDistance); } }
Entity::checkFallDamage(ya, onGround);}
bool LivingEntity::isWaterMob(){ return false;}
void LivingEntity::baseTick() { oAttackAnim = attackAnim; Entity::baseTick();
if (isAlive() && isInWall()) { hurt(DamageSource::inWall, 1); }
if (isFireImmune() || level->isClientSide) clearFire(); shared_ptr<Player> thisPlayer = dynamic_pointer_cast<Player>(shared_from_this()); bool isInvulnerable = (thisPlayer != NULL && thisPlayer->abilities.invulnerable);
if (isAlive() && isUnderLiquid(Material::water)) { if(!isWaterMob() && !hasEffect(MobEffect::waterBreathing->id) && !isInvulnerable) { setAirSupply(decreaseAirSupply(getAirSupply())); if (getAirSupply() == -20) { setAirSupply(0); if(canCreateParticles()) { 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); } } hurt(DamageSource::drown, 2); } }
clearFire(); if ( !level->isClientSide && isRiding() && riding->instanceof(eTYPE_LIVINGENTITY) ) { ride(nullptr); } } else { setAirSupply(TOTAL_AIR_SUPPLY); }
oTilt = tilt;
if (attackTime > 0) attackTime--; if (hurtTime > 0) hurtTime--; if (invulnerableTime > 0) invulnerableTime--; if (getHealth() <= 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(); } } if (lastHurtMob != NULL && !lastHurtMob->isAlive()) { lastHurtMob = nullptr; }
// If lastHurtByMob is dead, remove it if (lastHurtByMob != NULL && !lastHurtByMob->isAlive()) { setLastHurtByMob(nullptr); }
// Update effects tickEffects();
animStepO = animStep;
yBodyRotO = yBodyRot; yHeadRotO = yHeadRot; yRotO = yRot; xRotO = xRot;}
bool LivingEntity::isBaby(){ return false;}
void LivingEntity::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() && level->getGameRules()->getBoolean(GameRules::RULE_DOMOBLOOT)) { 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 LivingEntity::decreaseAirSupply(int currentSupply){ int oxygenBonus = EnchantmentHelper::getOxygenBonus(dynamic_pointer_cast<LivingEntity>(shared_from_this())); if (oxygenBonus > 0) { if (random->nextInt(oxygenBonus + 1) > 0) { // the oxygen bonus prevents us from drowning return currentSupply; } } if(instanceof(eTYPE_PLAYER)) { app.DebugPrintf("++++++++++ %s: Player decreasing air supply to %d\n", level->isClientSide ? "CLIENT" : "SERVER", currentSupply - 1 ); } return currentSupply - 1;}
int LivingEntity::getExperienceReward(shared_ptr<Player> killedBy){ return 0;}
bool LivingEntity::isAlwaysExperienceDropper(){ return false;}
Random *LivingEntity::getRandom(){ return random;}
shared_ptr<LivingEntity> LivingEntity::getLastHurtByMob(){ return lastHurtByMob;}
int LivingEntity::getLastHurtByMobTimestamp(){ return lastHurtByMobTimestamp;}
void LivingEntity::setLastHurtByMob(shared_ptr<LivingEntity> target){ lastHurtByMob = target; lastHurtByMobTimestamp = tickCount;}
shared_ptr<LivingEntity> LivingEntity::getLastHurtMob(){ return lastHurtMob;}
int LivingEntity::getLastHurtMobTimestamp(){ return lastHurtMobTimestamp;}
void LivingEntity::setLastHurtMob(shared_ptr<Entity> target){ if ( target->instanceof(eTYPE_LIVINGENTITY) ) { lastHurtMob = dynamic_pointer_cast<LivingEntity>(target); } else { lastHurtMob = nullptr; } lastHurtMobTimestamp = tickCount;}
int LivingEntity::getNoActionTime(){ return noActionTime;}
void LivingEntity::addAdditonalSaveData(CompoundTag *entityTag){ entityTag->putFloat(L"HealF", getHealth()); entityTag->putShort(L"Health", (short) ceil(getHealth())); entityTag->putShort(L"HurtTime", (short) hurtTime); entityTag->putShort(L"DeathTime", (short) deathTime); entityTag->putShort(L"AttackTime", (short) attackTime); entityTag->putFloat(L"AbsorptionAmount", getAbsorptionAmount());
ItemInstanceArray items = getEquipmentSlots(); for (unsigned int i = 0; i < items.length; ++i) { shared_ptr<ItemInstance> item = items[i]; if (item != NULL) { attributes->removeItemModifiers(item); } }
entityTag->put(L"Attributes", SharedMonsterAttributes::saveAttributes(getAttributes()));
for (unsigned int i = 0; i < items.length; ++i) { shared_ptr<ItemInstance> item = items[i]; if (item != NULL) { attributes->addItemModifiers(item); } }
if (!activeEffects.empty()) { ListTag<CompoundTag> *listTag = new ListTag<CompoundTag>();
for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it) { MobEffectInstance *effect = it->second; listTag->add(effect->save(new CompoundTag())); } entityTag->put(L"ActiveEffects", listTag); }}
void LivingEntity::readAdditionalSaveData(CompoundTag *tag) { setAbsorptionAmount(tag->getFloat(L"AbsorptionAmount"));
if (tag->contains(L"Attributes") && level != NULL && !level->isClientSide) { SharedMonsterAttributes::loadAttributes(getAttributes(), (ListTag<CompoundTag> *) tag->getList(L"Attributes")); }
if (tag->contains(L"ActiveEffects")) { ListTag<CompoundTag> *effects = (ListTag<CompoundTag> *) tag->getList(L"ActiveEffects"); for (int i = 0; i < effects->size(); i++) { CompoundTag *effectTag = effects->get(i); MobEffectInstance *effect = MobEffectInstance::load(effectTag); activeEffects.insert( unordered_map<int, MobEffectInstance *>::value_type( effect->getId(), effect ) ); } }
if (tag->contains(L"HealF")) { setHealth( tag->getFloat(L"HealF") ); } else { Tag *healthTag = tag->get(L"Health"); if (healthTag == NULL) { setHealth(getMaxHealth()); } else if (healthTag->getId() == Tag::TAG_Float) { setHealth(((FloatTag *) healthTag)->data); } else if (healthTag->getId() == Tag::TAG_Short) { // pre-1.6 health setHealth((float) ((ShortTag *) healthTag)->data); } }
hurtTime = tag->getShort(L"HurtTime"); deathTime = tag->getShort(L"DeathTime"); attackTime = tag->getShort(L"AttackTime");}
void LivingEntity::tickEffects(){ bool removed = false; for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end();) { MobEffectInstance *effect = it->second; removed = false; if (!effect->tick(dynamic_pointer_cast<LivingEntity>(shared_from_this()))) { if (!level->isClientSide) { it = activeEffects.erase( it ); onEffectRemoved(effect); delete effect; removed = true; } } else if (effect->getDuration() % (SharedConstants::TICKS_PER_SECOND * 30) == 0) { // update effects every 30 seconds to synchronize client-side // timer onEffectUpdated(effect, false); } if(!removed) { ++it; } } if (effectsDirty) { if (!level->isClientSide) { if (activeEffects.empty()) { entityData->set(DATA_EFFECT_AMBIENCE_ID, (byte) 0); entityData->set(DATA_EFFECT_COLOR_ID, 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); entityData->set(DATA_EFFECT_AMBIENCE_ID, PotionBrewing::areAllEffectsAmbient(&values) ? (byte) 1 : (byte) 0); values.clear(); entityData->set(DATA_EFFECT_COLOR_ID, colorValue); setInvisible(hasEffect(MobEffect::invisibility->id)); setWeakened(hasEffect(MobEffect::weakness->id)); } } effectsDirty = false; } int colorValue = entityData->getInteger(DATA_EFFECT_COLOR_ID); bool ambient = entityData->getByte(DATA_EFFECT_AMBIENCE_ID) > 0;
if (colorValue > 0) { boolean doParticle = false;
if (!isInvisible()) { doParticle = random->nextBoolean(); } else { // much fewer particles when invisible doParticle = random->nextInt(15) == 0; }
if (ambient) doParticle &= random->nextInt(5) == 0;
if (doParticle) { // 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;
level->addParticle(ambient? eParticleType_mobSpellAmbient : eParticleType_mobSpell, x + (random->nextDouble() - 0.5) * bbWidth, y + random->nextDouble() * bbHeight - heightOffset, z + (random->nextDouble() - 0.5) * bbWidth, red, green, blue); } } }}
void LivingEntity::removeAllEffects(){ //Iterator<Integer> effectIdIterator = activeEffects.keySet().iterator(); //while (effectIdIterator.hasNext()) for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ) { //Integer effectId = effectIdIterator.next(); MobEffectInstance *effect = it->second;//activeEffects.get(effectId);
if (!level->isClientSide) { //effectIdIterator.remove(); it = activeEffects.erase(it); onEffectRemoved(effect); delete effect; } else { ++it; } }}
vector<MobEffectInstance *> *LivingEntity::getActiveEffects(){ vector<MobEffectInstance *> *active = new vector<MobEffectInstance *>();
for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it) { active->push_back(it->second); }
return active;}
bool LivingEntity::hasEffect(int id){ return activeEffects.find(id) != activeEffects.end();;}
bool LivingEntity::hasEffect(MobEffect *effect){ return activeEffects.find(effect->id) != activeEffects.end();}
MobEffectInstance *LivingEntity::getEffect(MobEffect *effect){ MobEffectInstance *effectInst = NULL;
AUTO_VAR(it, activeEffects.find(effect->id)); if(it != activeEffects.end() ) effectInst = it->second;
return effectInst;}
void LivingEntity::addEffect(MobEffectInstance *newEffect){ if (!canBeAffected(newEffect)) { return; }
if (activeEffects.find(newEffect->getId()) != activeEffects.end() ) { // replace effect and update MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second; effectInst->update(newEffect); onEffectUpdated(effectInst, true); } else { activeEffects.insert( unordered_map<int, MobEffectInstance *>::value_type( newEffect->getId(), newEffect ) ); onEffectAdded(newEffect); }}
// 4J Addedvoid LivingEntity::addEffectNoUpdate(MobEffectInstance *newEffect){ if (!canBeAffected(newEffect)) { return; }
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 LivingEntity::canBeAffected(MobEffectInstance *newEffect){ if (getMobType() == UNDEAD) { int id = newEffect->getId(); if (id == MobEffect::regeneration->id || id == MobEffect::poison->id) { return false; } }
return true;}
bool LivingEntity::isInvertedHealAndHarm(){ return getMobType() == UNDEAD;}
void LivingEntity::removeEffectNoUpdate(int effectId){ AUTO_VAR(it, activeEffects.find(effectId)); if (it != activeEffects.end()) { MobEffectInstance *effect = it->second; if(effect != NULL) { delete effect; } activeEffects.erase(it); }}
void LivingEntity::removeEffect(int effectId){ AUTO_VAR(it, activeEffects.find(effectId)); if (it != activeEffects.end()) { MobEffectInstance *effect = it->second; if(effect != NULL) { onEffectRemoved(effect); delete effect; } activeEffects.erase(it); }}
void LivingEntity::onEffectAdded(MobEffectInstance *effect){ effectsDirty = true; if (!level->isClientSide) MobEffect::effects[effect->getId()]->addAttributeModifiers(dynamic_pointer_cast<LivingEntity>(shared_from_this()), getAttributes(), effect->getAmplifier());}
void LivingEntity::onEffectUpdated(MobEffectInstance *effect, bool doRefreshAttributes){ effectsDirty = true; if (doRefreshAttributes && !level->isClientSide) { MobEffect::effects[effect->getId()]->removeAttributeModifiers(dynamic_pointer_cast<LivingEntity>(shared_from_this()), getAttributes(), effect->getAmplifier()); MobEffect::effects[effect->getId()]->addAttributeModifiers(dynamic_pointer_cast<LivingEntity>(shared_from_this()), getAttributes(), effect->getAmplifier()); }}
void LivingEntity::onEffectRemoved(MobEffectInstance *effect){ effectsDirty = true; if (!level->isClientSide) MobEffect::effects[effect->getId()]->removeAttributeModifiers(dynamic_pointer_cast<LivingEntity>(shared_from_this()), getAttributes(), effect->getAmplifier());}
void LivingEntity::heal(float heal) { float health = getHealth(); if (health > 0) { setHealth(health + heal); }}
float LivingEntity::getHealth(){ return entityData->getFloat(DATA_HEALTH_ID);}
void LivingEntity::setHealth(float health){ entityData->set(DATA_HEALTH_ID, Mth::clamp(health, 0.0f, getMaxHealth()));}
bool LivingEntity::hurt(DamageSource *source, float dmg) { if (isInvulnerable()) return false;
// 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 (getHealth() <= 0) return false;
if ( source->isFire() && hasEffect(MobEffect::fireResistance) ) { // 4J-JEV, for new achievement Stayin'Frosty, TODO merge with Java version. if ( this->instanceof(eTYPE_PLAYER) && (source == DamageSource::lava) ) // Only award when in lava (not any fire). { shared_ptr<Player> plr = dynamic_pointer_cast<Player>(shared_from_this()); plr->awardStat(GenericStats::stayinFrosty(),GenericStats::param_stayinFrosty()); } return false; }
if ((source == DamageSource::anvil || source == DamageSource::fallingBlock) && getCarried(SLOT_HELM) != NULL) { getCarried(SLOT_HELM)->hurtAndBreak((int) (dmg * 4 + random->nextFloat() * dmg * 2.0f), dynamic_pointer_cast<LivingEntity>( shared_from_this() )); dmg *= 0.75f; }
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 = getHealth(); invulnerableTime = invulnerableDuration; if (!level->isClientSide) actuallyHurt(source, dmg); hurtTime = hurtDuration = 10; }
hurtDir = 0;
shared_ptr<Entity> sourceEntity = source->getEntity(); if (sourceEntity != NULL) { if ( sourceEntity->instanceof(eTYPE_LIVINGENTITY) ) { setLastHurtByMob(dynamic_pointer_cast<LivingEntity>(sourceEntity)); }
if ( sourceEntity->instanceof(eTYPE_PLAYER) ) { lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME; lastHurtByPlayer = dynamic_pointer_cast<Player>(sourceEntity); } else if ( sourceEntity->instanceof(eTYPE_WOLF) ) { 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) 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 (getHealth() <= 0) { if (sound) playSound(getDeathSound(), getSoundVolume(), getVoicePitch()); die(source); } else { if (sound) playSound(getHurtSound(), getSoundVolume(), getVoicePitch()); } MemSect(0);
return true;}
void LivingEntity::breakItem(shared_ptr<ItemInstance> itemInstance){ playSound(eSoundType_RANDOM_BREAK, 0.8f, 0.8f + level->random->nextFloat() * 0.4f);
for (int i = 0; i < 5; i++) { 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); }}
void LivingEntity::die(DamageSource *source) { shared_ptr<Entity> sourceEntity = source->getEntity(); shared_ptr<LivingEntity> killer = getKillCredit(); if (deathScore >= 0 && killer != NULL) killer->awardKillScore(shared_from_this(), deathScore);
if (sourceEntity != NULL) sourceEntity->killed( dynamic_pointer_cast<LivingEntity>( shared_from_this() ) );
dead = true;
if (!level->isClientSide) { int playerBonus = 0;
shared_ptr<Player> player = nullptr; if ( (sourceEntity != NULL) && sourceEntity->instanceof(eTYPE_PLAYER) ) { player = dynamic_pointer_cast<Player>(sourceEntity); playerBonus = EnchantmentHelper::getKillingLootBonus(dynamic_pointer_cast<LivingEntity>(player)); }
if (!isBaby() && level->getGameRules()->getBoolean(GameRules::RULE_DOMOBLOOT)) { dropDeathLoot(lastHurtByPlayerTime > 0, playerBonus); dropEquipment(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);}
void LivingEntity::dropEquipment(bool byPlayer, int playerBonusLevel){}
void LivingEntity::knockback(shared_ptr<Entity> source, float dmg, double xd, double zd) { if (random->nextDouble() < getAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE)->getValue()) { return; }
hasImpulse = true; float dd = Mth::sqrt(xd * xd + zd * zd); float pow = 0.4f;
this->xd /= 2; yd /= 2; this->zd /= 2;
this->xd -= xd / dd * pow; yd += pow; this->zd -= zd / dd * pow;
if (yd > 0.4f) yd = 0.4f;}
int LivingEntity::getHurtSound() { return eSoundType_DAMAGE_HURT;}
int LivingEntity::getDeathSound() { return eSoundType_DAMAGE_HURT;}
/*** 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 LivingEntity::dropRareDeathLoot(int rareLootLevel){
}
void LivingEntity::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) {}
bool LivingEntity::onLadder() { 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);}
bool LivingEntity::isShootable() { return true;}
bool LivingEntity::isAlive() { return !removed && getHealth() > 0;}
void LivingEntity::causeFallDamage(float distance) { Entity::causeFallDamage(distance); MobEffectInstance *jumpBoost = getEffect(MobEffect::jump); float padding = jumpBoost != NULL ? jumpBoost->getAmplifier() + 1 : 0;
int dmg = (int) ceil(distance - 3 - padding); if (dmg > 0) { // 4J - new sounds here brought forward from 1.2.3 if (dmg > 4) { playSound(eSoundType_DAMAGE_FALL_BIG, 1, 1); } else { playSound(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); playSound(soundType->getStepSound(), soundType->getVolume() * 0.5f, soundType->getPitch() * 0.75f); MemSect(0); } }}
void LivingEntity::animateHurt() { hurtTime = hurtDuration = 10; hurtDir = 0;}
/*** Fetches the mob's armor value, from 0 (no armor) to 20 (full armor)* * @return*/int LivingEntity::getArmorValue(){ int val = 0; ItemInstanceArray items = getEquipmentSlots(); for (unsigned int i = 0; i < items.length; ++i) { shared_ptr<ItemInstance> item = items[i]; if (item != NULL && dynamic_cast<ArmorItem *>(item->getItem()) != NULL) { int baseProtection = ((ArmorItem *) item->getItem())->defense; val += baseProtection; } } return val;}
void LivingEntity::hurtArmor(float damage){}
float LivingEntity::getDamageAfterArmorAbsorb(DamageSource *damageSource, float damage){ if (!damageSource->isBypassArmor()) { int absorb = 25 - getArmorValue(); float v = (damage) * absorb; hurtArmor(damage); damage = v / 25; } return damage;}
float LivingEntity::getDamageAfterMagicAbsorb(DamageSource *damageSource, float damage){ // [EB]: Stupid hack :( if ( this->instanceof(eTYPE_ZOMBIE) ) { damage = damage; } if (hasEffect(MobEffect::damageResistance) && damageSource != DamageSource::outOfWorld) { int absorbValue = (getEffect(MobEffect::damageResistance)->getAmplifier() + 1) * 5; int absorb = 25 - absorbValue; float v = (damage) * absorb; damage = v / 25; }
if (damage <= 0) return 0;
int enchantmentArmor = EnchantmentHelper::getDamageProtection(getEquipmentSlots(), damageSource); if (enchantmentArmor > 20) { enchantmentArmor = 20; } if (enchantmentArmor > 0 && enchantmentArmor <= 20) { int absorb = 25 - enchantmentArmor; float v = damage * absorb; damage = v / 25; }
return damage;}
void LivingEntity::actuallyHurt(DamageSource *source, float dmg) { if (isInvulnerable()) return; dmg = getDamageAfterArmorAbsorb(source, dmg); dmg = getDamageAfterMagicAbsorb(source, dmg);
float originalDamage = dmg; dmg = max(dmg - getAbsorptionAmount(), 0.0f); setAbsorptionAmount(getAbsorptionAmount() - (originalDamage - dmg)); if (dmg == 0) return;
float oldHealth = getHealth(); setHealth(oldHealth - dmg); getCombatTracker()->recordDamage(source, oldHealth, dmg); setAbsorptionAmount(getAbsorptionAmount() - dmg);}
CombatTracker *LivingEntity::getCombatTracker(){ return combatTracker;}
shared_ptr<LivingEntity> LivingEntity::getKillCredit(){ if (combatTracker->getKiller() != NULL) return combatTracker->getKiller(); if (lastHurtByPlayer != NULL) return lastHurtByPlayer; if (lastHurtByMob != NULL) return lastHurtByMob; return nullptr;}
float LivingEntity::getMaxHealth(){ return (float) getAttribute(SharedMonsterAttributes::MAX_HEALTH)->getValue();}
int LivingEntity::getArrowCount(){ return entityData->getByte(DATA_ARROW_COUNT_ID);}
void LivingEntity::setArrowCount(int count){ entityData->set(DATA_ARROW_COUNT_ID, (byte) count);}
int LivingEntity::getCurrentSwingDuration(){ if (hasEffect(MobEffect::digSpeed)) { return SWING_DURATION - (1 + getEffect(MobEffect::digSpeed)->getAmplifier()) * 1; } if (hasEffect(MobEffect::digSlowdown)) { return SWING_DURATION + (1 + getEffect(MobEffect::digSlowdown)->getAmplifier()) * 2; } return SWING_DURATION;}
void LivingEntity::swing(){ if (!swinging || swingTime >= getCurrentSwingDuration() / 2 || swingTime < 0) { swingTime = -1; swinging = true;
if (dynamic_cast<ServerLevel *>(level) != NULL) { ((ServerLevel *) level)->getTracker()->broadcast(shared_from_this(), shared_ptr<AnimatePacket>( new AnimatePacket(shared_from_this(), AnimatePacket::SWING))); } }}
void LivingEntity::handleEntityEvent(byte id) { if (id == EntityEvent::HURT) { 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) { playSound(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) { playSound(iDeathSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); } MemSect(0); setHealth(0); die(DamageSource::genericSource); } else { Entity::handleEntityEvent(id); }}
void LivingEntity::outOfWorld() { hurt(DamageSource::outOfWorld, 4);}
void LivingEntity::updateSwingTime(){ int currentSwingDuration = getCurrentSwingDuration(); if (swinging) { swingTime++; if (swingTime >= currentSwingDuration) { swingTime = 0; swinging = false; } } else { swingTime = 0; }
attackAnim = swingTime / (float) currentSwingDuration;}
AttributeInstance *LivingEntity::getAttribute(Attribute *attribute){ return getAttributes()->getInstance(attribute);}
BaseAttributeMap *LivingEntity::getAttributes(){ if (attributes == NULL) { attributes = new ServersideAttributeMap(); }
return attributes;}
MobType LivingEntity::getMobType(){ return UNDEFINED;}
void LivingEntity::setSprinting(bool value){ Entity::setSprinting(value);
AttributeInstance *speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED); if (speed->getModifier(eModifierId_MOB_SPRINTING) != NULL) { speed->removeModifier(eModifierId_MOB_SPRINTING); } if (value) { speed->addModifier(new AttributeModifier(*SPEED_MODIFIER_SPRINTING)); }}
float LivingEntity::getSoundVolume() { return 1;}
float LivingEntity::getVoicePitch(){ if (isBaby()) { return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.5f;
} return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f;}
bool LivingEntity::isImmobile() { return getHealth() <= 0;}
void LivingEntity::teleportTo(double x, double y, double z) { moveTo(x, y, z, yRot, xRot);}
void LivingEntity::findStandUpPosition(shared_ptr<Entity> vehicle){ AABB *boundingBox; double fallbackX = vehicle->x; double fallbackY = vehicle->bb->y0 + vehicle->bbHeight; double fallbackZ = vehicle->z;
for (double xDiff = -1.5; xDiff < 2; xDiff += 1.5) { for (double zDiff = -1.5; zDiff < 2; zDiff += 1.5) { if (xDiff == 0 && zDiff == 0) { continue; }
int xToInt = (int) (x + xDiff); int zToInt = (int) (z + zDiff); boundingBox = bb->cloneMove(xDiff, 1, zDiff);
if (level->getTileCubes(boundingBox, true)->empty()) { if (level->isTopSolidBlocking(xToInt, (int) y, zToInt)) { teleportTo(x + xDiff, y + 1, z + zDiff); return; } else if (level->isTopSolidBlocking(xToInt, (int) y - 1, zToInt) || level->getMaterial(xToInt, (int) y - 1, zToInt) == Material::water) { fallbackX = x + xDiff; fallbackY = y + 1; fallbackZ = z + zDiff; } } } }
teleportTo(fallbackX, fallbackY, fallbackZ);}
bool LivingEntity::shouldShowName(){ return false;}
Icon *LivingEntity::getItemInHandIcon(shared_ptr<ItemInstance> item, int layer) { return item->getIcon();}
void LivingEntity::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;}
void LivingEntity::travel(float xa, float ya) {#ifdef __PSVITA__ // AP - dynamic_pointer_cast is a non-trivial call Player *thisPlayer = NULL; if( this->instanceof(eTYPE_PLAYER) ) { thisPlayer = (Player*) this; }#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;
if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd)) { yd = 0.3f; } } else if (isInLava() && !(thisPlayer && thisPlayer->abilities.flying) ) { 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)) { 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) - 1, 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);
float speed; if (onGround) { speed = getSpeed() * 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; fallDistance = 0; if (yd < -0.15) yd = -0.15; bool playerSneaking = isSneaking() && this->instanceof(eTYPE_PLAYER); if (playerSneaking && yd < 0) yd = 0; }
move(xd, yd, zd);
if (horizontalCollision && onLadder()) { yd = 0.2; }
if (!level->isClientSide || (level->hasChunkAt((int) x, 0, (int) z) && level->getChunkAt((int) x, (int) z)->loaded)) { yd -= 0.08; } else if (y > 0) { yd = -0.1; } else { yd = 0; }
yd *= 0.98f; xd *= friction; zd *= friction; }
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;}
// 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 LivingEntity::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]);}
bool LivingEntity::useNewAi(){ return false;}
float LivingEntity::getSpeed(){ if (useNewAi()) { return speed; } else { return 0.1f; }}
void LivingEntity::setSpeed(float speed){ this->speed = speed;}
bool LivingEntity::doHurtTarget(shared_ptr<Entity> target){ setLastHurtMob(target); return false;}
bool LivingEntity::isSleeping() { return false;}
void LivingEntity::tick() { Entity::tick();
if (!level->isClientSide) { int arrowCount = getArrowCount(); if (arrowCount > 0) { if (removeArrowTime <= 0) { removeArrowTime = SharedConstants::TICKS_PER_SECOND * (30 - arrowCount); } removeArrowTime--; if (removeArrowTime <= 0) { setArrowCount(arrowCount - 1); } }
for (int i = 0; i < 5; i++) { shared_ptr<ItemInstance> previous = lastEquipment[i]; shared_ptr<ItemInstance> current = getCarried(i);
if (!ItemInstance::matches(current, previous)) { ((ServerLevel *) level)->getTracker()->broadcast(shared_from_this(), shared_ptr<SetEquippedItemPacket>( new SetEquippedItemPacket(entityId, i, current))); if (previous != NULL) attributes->removeItemModifiers(previous); if (current != NULL) attributes->addItemModifiers(current); lastEquipment[i] = current == NULL ? nullptr : current->copy(); } } }
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) { tRun = 1; walkSpeed = sqrt(sideDist) * 3; yBodyRotT = ((float) atan2(zd, xd) * 180 / (float) PI - 90); } if (attackAnim > 0) { yBodyRotT = yRot; } if (!onGround) { tRun = 0; } run = run + (tRun - run) * 0.3f;
walkSpeed = tickHeadTurn(yBodyRotT, walkSpeed);
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;}
float LivingEntity::tickHeadTurn(float yBodyRotT, float walkSpeed){ 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; }
return walkSpeed;}
void LivingEntity::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--; setPos(xt, yt, zt); 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); } } else if (!isEffectiveAi()) { // slow down predicted speed, to prevent mobs from sliding through // walls etc xd *= .98; yd *= .98; zd *= .98; }
if (abs(xd) < MIN_MOVEMENT_DISTANCE) xd = 0; if (abs(yd) < MIN_MOVEMENT_DISTANCE) yd = 0; if (abs(zd) < MIN_MOVEMENT_DISTANCE) zd = 0;
if (isImmobile()) { jumping = false; xxa = 0; yya = 0; yRotA = 0; } else { MemSect(25); if (isEffectiveAi()) { if (useNewAi()) { newServerAiStep(); } else { serverAiStep(); yHeadRot = yRot; } } MemSect(0); }
if (jumping) { if (isInWater() || isInLava() ) { yd += 0.04f; } else if (onGround) { if (noJumpDelay == 0) { jumpFromGround(); noJumpDelay = 10; } } } else { noJumpDelay = 0; }
xxa *= 0.98f; yya *= 0.98f; yRotA *= 0.9f;
travel(xxa, yya);
if(!level->isClientSide) { pushEntities(); }}
void LivingEntity::newServerAiStep(){}
void LivingEntity::pushEntities(){
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()); } }}
void LivingEntity::doPush(shared_ptr<Entity> e){ e->push(shared_from_this());}
void LivingEntity::rideTick() { Entity::rideTick(); oRun = run; run = 0; fallDistance = 0;}
void LivingEntity::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 LivingEntity::serverAiMobStep(){}
void LivingEntity::serverAiStep() { noActionTime++;}
void LivingEntity::setJumping(bool jump){ jumping = jump;}
void LivingEntity::take(shared_ptr<Entity> e, int orgCount){ if (!e->removed && !level->isClientSide) { EntityTracker *entityTracker = ((ServerLevel *) level)->getTracker(); if ( e->instanceof(eTYPE_ITEMENTITY) ) { entityTracker->broadcast(e, shared_ptr<TakeItemEntityPacket>( new TakeItemEntityPacket(e->entityId, entityId))); } else if ( e->instanceof(eTYPE_ARROW) ) { entityTracker->broadcast(e, shared_ptr<TakeItemEntityPacket>( new TakeItemEntityPacket(e->entityId, entityId))); } else if ( e->instanceof(eTYPE_EXPERIENCEORB) ) { entityTracker->broadcast(e, shared_ptr<TakeItemEntityPacket>( new TakeItemEntityPacket(e->entityId, entityId))); } }}
bool LivingEntity::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;}
Vec3 *LivingEntity::getLookAngle() { return getViewVector(1);}
Vec3 *LivingEntity::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 LivingEntity::getAttackAnim(float a) { float diff = attackAnim - oAttackAnim; if (diff < 0) diff += 1; return oAttackAnim + diff * a;}
Vec3 *LivingEntity::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);}
HitResult *LivingEntity::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);}
bool LivingEntity::isEffectiveAi(){ return !level->isClientSide;}
bool LivingEntity::isPickable() { return !removed;}
bool LivingEntity::isPushable() { return !removed;}
float LivingEntity::getHeadHeight() { return bbHeight * 0.85f;}
void LivingEntity::markHurt(){ hurtMarked = random->nextDouble() >= getAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE)->getValue();}
float LivingEntity::getYHeadRot(){ return yHeadRot;}
void LivingEntity::setYHeadRot(float yHeadRot){ this->yHeadRot = yHeadRot;}
float LivingEntity::getAbsorptionAmount(){ return absorptionAmount;}
void LivingEntity::setAbsorptionAmount(float absorptionAmount){ if (absorptionAmount < 0) absorptionAmount = 0; this->absorptionAmount = absorptionAmount;}
Team *LivingEntity::getTeam(){ return NULL;}
bool LivingEntity::isAlliedTo(shared_ptr<LivingEntity> other){ return isAlliedTo(other->getTeam());}
bool LivingEntity::isAlliedTo(Team *other){ if (getTeam() != NULL) { return getTeam()->isAlliedTo(other); } return false;}