aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.World/Arrow.cpp
diff options
context:
space:
mode:
authordaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
committerdaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
commitb691c43c44ff180d10e7d4a9afc83b98551ff586 (patch)
tree3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.World/Arrow.cpp
parentdef8cb415354ac390b7e89052a50605285f1aca9 (diff)
Initial commit
Diffstat (limited to 'Minecraft.World/Arrow.cpp')
-rw-r--r--Minecraft.World/Arrow.cpp531
1 files changed, 531 insertions, 0 deletions
diff --git a/Minecraft.World/Arrow.cpp b/Minecraft.World/Arrow.cpp
new file mode 100644
index 00000000..1b102c65
--- /dev/null
+++ b/Minecraft.World/Arrow.cpp
@@ -0,0 +1,531 @@
+#include "stdafx.h"
+#include "net.minecraft.world.level.h"
+#include "net.minecraft.world.level.tile.h"
+#include "net.minecraft.world.entity.h"
+#include "net.minecraft.world.entity.player.h"
+#include "net.minecraft.world.phys.h"
+#include "net.minecraft.world.item.h"
+#include "net.minecraft.world.damagesource.h"
+#include "net.minecraft.world.item.enchantment.h"
+#include "com.mojang.nbt.h"
+#include "Arrow.h"
+
+// 4J : WESTY : Added for other award, kill creeper with arrow.
+#include "net.minecraft.world.entity.monster.h"
+#include "net.minecraft.stats.h"
+#include "SoundTypes.h"
+
+
+
+// base damage, multiplied with velocity
+const double Arrow::ARROW_BASE_DAMAGE = 2.0f;
+
+// 4J - added common ctor code.
+void Arrow::_init()
+{
+ // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that
+ // the derived version of the function is called
+ this->defineSynchedData();
+
+ xTile = -1;
+ yTile = -1;
+ zTile = -1;
+ lastTile = 0;
+ lastData = 0;
+ inGround = false;
+ pickup = PICKUP_DISALLOWED;
+ shakeTime = 0;
+ flightTime = 0;
+
+ owner = nullptr;
+ life = 0;
+
+ baseDamage = ARROW_BASE_DAMAGE;
+ knockback = 0;
+}
+
+
+Arrow::Arrow(Level *level) : Entity( level )
+{
+ _init();
+
+ this->setSize(0.5f, 0.5f);
+}
+
+Arrow::Arrow(Level *level, shared_ptr<Mob> mob, shared_ptr<Mob> target, float power, float uncertainty) : Entity( level )
+{
+ _init();
+
+ this->owner = mob;
+ if ( dynamic_pointer_cast<Player>( mob ) != NULL) pickup = PICKUP_ALLOWED;
+
+ y = mob->y + mob->getHeadHeight() - 0.1f;
+
+ double xd = target->x - mob->x;
+ double yd = (target->y + target->getHeadHeight() - 0.7f) - y;
+ double zd = target->z - mob->z;
+ double sd = sqrt(xd * xd + zd * zd);
+ if (sd < 0.0000001) return;
+
+ float yRot = (float) (atan2(zd, xd) * 180 / PI) - 90;
+ float xRot = (float) -(atan2(yd, sd) * 180 / PI);
+
+ double xdn = xd / sd;
+ double zdn = zd / sd;
+ moveTo(mob->x + xdn, y, mob->z + zdn, yRot, xRot);
+ heightOffset = 0;
+
+ float yo = (float) sd * 0.2f;
+ shoot(xd, yd + yo, zd, power, uncertainty);
+}
+
+Arrow::Arrow(Level *level, double x, double y, double z) : Entity( level )
+{
+ _init();
+
+ this->setSize(0.5f, 0.5f);
+
+ this->setPos(x, y, z);
+ this->heightOffset = 0;
+}
+
+Arrow::Arrow(Level *level, shared_ptr<Mob> mob, float power) : Entity( level )
+{
+ _init();
+
+ this->owner = mob;
+ if ( dynamic_pointer_cast<Player>( mob ) != NULL) pickup = PICKUP_ALLOWED;
+
+ setSize(0.5f, 0.5f);
+
+ this->moveTo(mob->x, mob->y + mob->getHeadHeight(), mob->z, mob->yRot, mob->xRot);
+
+ x -= Mth::cos(yRot / 180 * PI) * 0.16f;
+ y -= 0.1f;
+ z -= Mth::sin(yRot / 180 * PI) * 0.16f;
+ this->setPos(x, y, z);
+ this->heightOffset = 0;
+
+ xd = -Mth::sin(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI);
+ zd = Mth::cos(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI);
+ yd = -Mth::sin(xRot / 180 * PI);
+
+ shoot(xd, yd, zd, power * 1.5f, 1);
+}
+
+
+void Arrow::defineSynchedData()
+{
+ entityData->define(ID_FLAGS, (byte) 0);
+}
+
+
+void Arrow::shoot(double xd, double yd, double zd, float pow, float uncertainty)
+{
+ float dist = (float) sqrt(xd * xd + yd * yd + zd * zd);
+
+ xd /= dist;
+ yd /= dist;
+ zd /= dist;
+
+ xd += (random->nextGaussian()) * 0.0075f * uncertainty;
+ yd += (random->nextGaussian()) * 0.0075f * uncertainty;
+ zd += (random->nextGaussian()) * 0.0075f * uncertainty;
+
+ xd *= pow;
+ yd *= pow;
+ zd *= pow;
+
+ this->xd = xd;
+ this->yd = yd;
+ this->zd = zd;
+
+ double sd = sqrt(xd * xd + zd * zd);
+
+ yRotO = this->yRot = (float) (atan2(xd, zd) * 180 / PI);
+ xRotO = this->xRot = (float) (atan2(yd, sd) * 180 / PI);
+ life = 0;
+}
+
+void Arrow::lerpTo(double x, double y, double z, float yRot, float xRot, int steps)
+{
+ setPos(x, y, z);
+ setRot(yRot, xRot);
+}
+
+void Arrow::lerpMotion(double xd, double yd, double zd)
+{
+ this->xd = xd;
+ this->yd = yd;
+ this->zd = zd;
+ if (xRotO == 0 && yRotO == 0)
+ {
+ double sd = sqrt(xd * xd + zd * zd);
+ yRotO = this->yRot = (float) (atan2( xd, zd) * 180 / PI);
+ xRotO = this->xRot = (float) (atan2( yd, sd) * 180 / PI);
+ xRotO = xRot;
+ yRotO = yRot;
+ app.DebugPrintf("%f %f : 0x%x\n",xRot,yRot,&yRot);
+ moveTo(x, y, z, yRot, xRot);
+ life = 0;
+ }
+}
+
+void Arrow::tick()
+{
+ Entity::tick();
+
+
+ if (xRotO == 0 && yRotO == 0)
+ {
+ double sd = sqrt(xd * xd + zd * zd);
+ yRotO = this->yRot = (float) (atan2(xd, zd) * 180 / PI);
+ xRotO = this->xRot = (float) (atan2(yd, sd) * 180 / PI);
+ }
+
+
+ {
+ int t = level->getTile(xTile, yTile, zTile);
+ if (t > 0)
+ {
+ Tile::tiles[t]->updateShape(level, xTile, yTile, zTile);
+ AABB *aabb = Tile::tiles[t]->getAABB(level, xTile, yTile, zTile);
+ if (aabb != NULL && aabb->contains(Vec3::newTemp(x, y, z)))
+ {
+ inGround = true;
+ }
+ }
+
+ }
+
+ if (shakeTime > 0) shakeTime--;
+
+ if (inGround)
+ {
+ int tile = level->getTile(xTile, yTile, zTile);
+ int data = level->getData(xTile, yTile, zTile);
+ if (tile != lastTile || data != lastData)
+ {
+ inGround = false;
+
+ xd *= random->nextFloat() * 0.2f;
+ yd *= random->nextFloat() * 0.2f;
+ zd *= random->nextFloat() * 0.2f;
+ life = 0;
+ flightTime = 0;
+ return;
+ }
+
+ else
+ {
+ life++;
+ if (life == 20 * 60) remove();
+ return;
+ }
+ }
+
+ else
+ {
+ flightTime++;
+ }
+
+ Vec3 *from = Vec3::newTemp(x, y, z);
+ Vec3 *to = Vec3::newTemp(x + xd, y + yd, z + zd);
+ HitResult *res = level->clip(from, to, false, true);
+
+ from = Vec3::newTemp(x, y, z);
+ to = Vec3::newTemp(x + xd, y + yd, z + zd);
+ if (res != NULL)
+ {
+ to = Vec3::newTemp(res->pos->x, res->pos->y, res->pos->z);
+ }
+ shared_ptr<Entity> hitEntity = nullptr;
+ vector<shared_ptr<Entity> > *objects = level->getEntities(shared_from_this(), this->bb->expand(xd, yd, zd)->grow(1, 1, 1));
+ double nearest = 0;
+ AUTO_VAR(itEnd, objects->end());
+ for (AUTO_VAR(it, objects->begin()); it != itEnd; it++)
+ {
+ shared_ptr<Entity> e = *it; //objects->at(i);
+ if (!e->isPickable() || (e == owner && flightTime < 5)) continue;
+
+ float rr = 0.3f;
+ AABB *bb = e->bb->grow(rr, rr, rr);
+ HitResult *p = bb->clip(from, to);
+ if (p != NULL)
+ {
+ double dd = from->distanceTo(p->pos);
+ if (dd < nearest || nearest == 0)
+ {
+ hitEntity = e;
+ nearest = dd;
+ }
+ delete p;
+ }
+ }
+
+ if (hitEntity != NULL)
+ {
+ delete res;
+ res = new HitResult(hitEntity);
+ }
+
+ if (res != NULL)
+ {
+ if (res->entity != NULL)
+ {
+ float pow = Mth::sqrt(xd * xd + yd * yd + zd * zd);
+ int dmg = (int) Mth::ceil((float)(pow * baseDamage));
+
+ if(isCritArrow()) dmg += random->nextInt(dmg / 2 + 2);
+
+ DamageSource *damageSource = NULL;
+ if (owner == NULL)
+ {
+ damageSource = DamageSource::arrow(dynamic_pointer_cast<Arrow>(shared_from_this()), shared_from_this());
+ }
+ else
+ {
+ damageSource = DamageSource::arrow(dynamic_pointer_cast<Arrow>(shared_from_this()), owner);
+ }
+
+ if(res->entity->hurt(damageSource, dmg))
+ {
+ // Firx for #67839 - Customer Encountered: Bows enchanted with "Flame" still set things on fire if pvp/attack animals is turned off
+ // 4J Stu - We should not set the entity on fire unless we can cause some damage (this doesn't necessarily mean that the arrow hit lowered their health)
+ // set targets on fire first because we want cooked
+ // pork/chicken/steak
+ if (this->isOnFire())
+ {
+ res->entity->setOnFire(5);
+ }
+
+ shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(res->entity);
+ if (mob != NULL)
+ {
+ mob->arrowCount++;
+ if (knockback > 0)
+ {
+ float pushLen = sqrt(xd * xd + zd * zd);
+ if (pushLen > 0)
+ {
+ res->entity->push(xd * knockback * .6f / pushLen, 0.1, zd * knockback * .6f / pushLen);
+ }
+ }
+
+ if (owner != NULL)
+ {
+ ThornsEnchantment::doThornsAfterAttack(owner, mob, random);
+ }
+ }
+
+ // 4J : WESTY : For award, need to track if creeper was killed by arrow from the player.
+ if ( (dynamic_pointer_cast<Player>(owner) != NULL ) && // arrow owner is a player
+ ( res->entity->isAlive() == false ) && // target is now dead
+ ( dynamic_pointer_cast<Creeper>( res->entity ) != NULL ) ) // target is a creeper
+
+ {
+ dynamic_pointer_cast<Player>(owner)->awardStat(
+ GenericStats::arrowKillCreeper(),
+ GenericStats::param_arrowKillCreeper()
+ );
+ }
+
+ // 4J - sound change brought forward from 1.2.3
+ level->playSound(shared_from_this(), eSoundType_RANDOM_BOW_HIT, 1.0f, 1.2f / (random->nextFloat() * 0.2f + 0.9f));
+ remove();
+ }
+ else
+ {
+ xd *= -0.1f;
+ yd *= -0.1f;
+ zd *= -0.1f;
+ yRot += 180;
+ yRotO += 180;
+ flightTime = 0;
+ }
+
+ delete damageSource;
+ }
+ else
+ {
+ xTile = res->x;
+ yTile = res->y;
+ zTile = res->z;
+ lastTile = level->getTile(xTile, yTile, zTile);
+ lastData = level->getData(xTile, yTile, zTile);
+ xd = (float) (res->pos->x - x);
+ yd = (float) (res->pos->y - y);
+ zd = (float) (res->pos->z - z);
+ float dd = (float) sqrt(xd * xd + yd * yd + zd * zd);
+ // 4J added check - zero dd here was creating NaNs
+ if( dd > 0.0001f )
+ {
+ x -= (xd / dd) * 0.05f;
+ y -= (yd / dd) * 0.05f;
+ z -= (zd / dd) * 0.05f;
+ }
+
+ // 4J - sound change brought forward from 1.2.3
+ level->playSound(shared_from_this(), eSoundType_RANDOM_BOW_HIT, 1.0f, 1.2f / (random->nextFloat() * 0.2f + 0.9f));
+ inGround = true;
+ shakeTime = 7;
+ setCritArrow(false);
+ }
+ }
+ delete res;
+
+ if(isCritArrow())
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ level->addParticle(eParticleType_crit, x + xd * i / 4.0f, y + yd * i / 4.0f, z + zd * i / 4.0f, -xd, -yd + 0.2, -zd);
+ }
+ }
+
+ x += xd;
+ y += yd;
+ z += zd;
+
+ double sd = sqrt(xd * xd + zd * zd);
+ yRot = (float) (atan2(xd, zd) * 180 / PI);
+ xRot = (float) (atan2(yd, sd) * 180 / PI);
+
+ while (xRot - xRotO < -180)
+ xRotO -= 360;
+ while (xRot - xRotO >= 180)
+ xRotO += 360;
+
+ while (yRot - yRotO < -180)
+ yRotO -= 360;
+ while (yRot - yRotO >= 180)
+ yRotO += 360;
+
+ xRot = xRotO + (xRot - xRotO) * 0.2f;
+ yRot = yRotO + (yRot - yRotO) * 0.2f;
+
+
+ float inertia = 0.99f;
+ float gravity = 0.05f;
+
+ if (isInWater())
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ float s = 1 / 4.0f;
+ level->addParticle(eParticleType_bubble, x - xd * s, y - yd * s, z - zd * s, xd, yd, zd);
+ }
+ inertia = 0.80f;
+ }
+
+ xd *= inertia;
+ yd *= inertia;
+ zd *= inertia;
+ yd -= gravity;
+
+ setPos(x, y, z);
+
+ checkInsideTiles();
+}
+
+void Arrow::addAdditonalSaveData(CompoundTag *tag)
+{
+ tag->putShort(L"xTile", (short) xTile);
+ tag->putShort(L"yTile", (short) yTile);
+ tag->putShort(L"zTile", (short) zTile);
+ tag->putByte(L"inTile", (byte) lastTile);
+ tag->putByte(L"inData", (byte) lastData);
+ tag->putByte(L"shake", (byte) shakeTime);
+ tag->putByte(L"inGround", (byte) (inGround ? 1 : 0));
+ tag->putByte(L"pickup", (byte) pickup);
+ tag->putDouble(L"damage", baseDamage);
+}
+
+void Arrow::readAdditionalSaveData(CompoundTag *tag)
+{
+ xTile = tag->getShort(L"xTile");
+ yTile = tag->getShort(L"yTile");
+ zTile = tag->getShort(L"zTile");
+ lastTile = tag->getByte(L"inTile") & 0xff;
+ lastData = tag->getByte(L"inData") & 0xff;
+ shakeTime = tag->getByte(L"shake") & 0xff;
+ inGround = tag->getByte(L"inGround") == 1;
+ if (tag->contains(L"damage"))
+ {
+ baseDamage = tag->getDouble(L"damage");
+ }
+
+ if (tag->contains(L"pickup"))
+ {
+ pickup = tag->getByte(L"pickup");
+ }
+ else if (tag->contains(L"player"))
+ {
+ pickup = tag->getBoolean(L"player") ? PICKUP_ALLOWED : PICKUP_DISALLOWED;
+ }
+}
+
+void Arrow::playerTouch(shared_ptr<Player> player)
+{
+ if (level->isClientSide || !inGround || shakeTime > 0) return;
+
+ bool bRemove = pickup == PICKUP_ALLOWED || (pickup == PICKUP_CREATIVE_ONLY && player->abilities.instabuild);
+
+ if (pickup == PICKUP_ALLOWED)
+ {
+ if (!player->inventory->add( shared_ptr<ItemInstance>( new ItemInstance(Item::arrow, 1) ) ))
+ {
+ bRemove = false;
+ }
+ }
+
+ if (bRemove)
+ {
+ level->playSound(shared_from_this(), eSoundType_RANDOM_POP, 0.2f, ((random->nextFloat() - random->nextFloat()) * 0.7f + 1.0f) * 2.0f);
+ player->take(shared_from_this(), 1);
+ remove();
+ }
+}
+
+float Arrow::getShadowHeightOffs()
+{
+ return 0;
+}
+
+void Arrow::setBaseDamage(double baseDamage)
+{
+ this->baseDamage = baseDamage;
+}
+
+double Arrow::getBaseDamage()
+{
+ return baseDamage;
+}
+
+void Arrow::setKnockback(int knockback)
+{
+ this->knockback = knockback;
+}
+
+bool Arrow::isAttackable()
+{
+ return false;
+}
+
+void Arrow::setCritArrow(bool critArrow)
+{
+ byte flags = entityData->getByte(ID_FLAGS);
+ if (critArrow)
+ {
+ entityData->set(ID_FLAGS, (byte) (flags | FLAG_CRIT));
+ }
+ else
+ {
+ entityData->set(ID_FLAGS, (byte) (flags & ~FLAG_CRIT));
+ }
+}
+
+bool Arrow::isCritArrow()
+{
+ byte flags = entityData->getByte(ID_FLAGS);
+ return (flags & FLAG_CRIT) != 0;
+}