Files
minecraft-pe-0.6.1/src/world/entity/projectile/Arrow.cpp
2026-03-02 22:04:18 +03:00

347 lines
8.2 KiB
C++
Executable File

#include "Arrow.h"
#include "../Mob.h"
#include "../player/Player.h"
#include "../../item/Item.h"
#include "../../item/ItemInstance.h"
#include "../../level/Level.h"
#include "../../level/tile/Tile.h"
#include "../../../util/Mth.h"
#include "../../../nbt/CompoundTag.h"
const float Arrow::ARROW_BASE_DAMAGE = 2.0f;
Arrow::Arrow( Level* level )
: super(level),
playerArrow(false),
ownerId(0)
{
_init();
}
Arrow::Arrow( Level* level, float x, float y, float z )
: super(level),
playerArrow(false),
ownerId(0)
{
_init();
this->setPos(x, y, z);
this->heightOffset = 0;
}
Arrow::Arrow( Level* level, Mob* mob, float power )
: super(level),
ownerId(mob->entityId),
playerArrow(mob->isPlayer())
{
_init();
moveTo(mob->x, mob->y + mob->getHeadHeight(), mob->z, mob->yRot, mob->xRot);
const float dx = Mth::cos(yRot * Mth::DEGRAD);
const float dz = Mth::sin(yRot * Mth::DEGRAD);
x -= dx * 0.16f;
y -= 0.1f;
z -= dz * 0.16f;
this->setPos(x, y, z);
this->heightOffset = 0;
const float dxx = Mth::cos(xRot / 180 * Mth::PI);
xd = -dz * dxx;
zd = dx * dxx;
yd = -Mth::sin(xRot / 180 * Mth::PI);
shoot(xd, yd, zd, power * 1.5f, 1);
}
void Arrow::_init()
{
entityRendererId = ER_ARROW_RENDERER;
setSize(0.5f, 0.5f);
shakeTime = 0;
critArrow = false;
xTile = -1;
yTile = -1;
zTile = -1;
lastTile = 0;
lastData = 0;
inGround = false;
life = 0;
flightTime = 0;
}
void Arrow::shoot( float xd, float yd, float zd, float pow, float uncertainty )
{
float dist = Mth::sqrt(xd * xd + yd * yd + zd * zd);
xd /= dist;
yd /= dist;
zd /= dist;
const float radius = 0.0075f * uncertainty;
xd += sharedRandom.nextGaussian() * radius;
yd += sharedRandom.nextGaussian() * radius;
zd += sharedRandom.nextGaussian() * radius;
xd *= pow;
yd *= pow;
zd *= pow;
this->xd = xd;
this->yd = yd;
this->zd = zd;
float sd = (float) Mth::sqrt(xd * xd + zd * zd);
yRotO = this->yRot = (float) (std::atan2(xd, zd) * Mth::RADDEG);
xRotO = this->xRot = (float) (std::atan2(yd, sd) * Mth::RADDEG);
life = 0;
}
void Arrow::lerpMotion( float xd, float yd, float zd )
{
this->xd = xd;
this->yd = yd;
this->zd = zd;
if (xRotO == 0 && yRotO == 0) {
float sd = (float) Mth::sqrt(xd * xd + zd * zd);
yRotO = this->yRot = (float) (std::atan2(xd, zd) * Mth::RADDEG);
xRotO = this->xRot = (float) (std::atan2(yd, sd) * Mth::RADDEG);
moveTo(x, y, z, yRot, xRot);
life = 0;
}
}
void Arrow::tick()
{
super::tick();
if (xRotO == 0 && yRotO == 0) {
float sd = (float) Mth::sqrt(xd * xd + zd * zd);
yRotO = this->yRot = (float) (std::atan2(xd, zd) * Mth::RADDEG);
xRotO = this->xRot = (float) (std::atan2(yd, sd) * Mth::RADDEG);
}
{
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(x, y, z))) {
inGround = true;
}
}
}
if (shakeTime > 0) shakeTime--;
if (inGround) {
// xd = 0;
// yd = 0;
// zd = 0;
int tile = level->getTile(xTile, yTile, zTile);
int data = level->getData(xTile, yTile, zTile);
if (tile != lastTile || data != lastData) {
inGround = false;
xd *= sharedRandom.nextFloat() * 0.2f;
yd *= sharedRandom.nextFloat() * 0.2f;
zd *= sharedRandom.nextFloat() * 0.2f;
life = 0;
flightTime = 0;
return;
} else {
if (++life == 60 * SharedConstants::TicksPerSecond) remove();
return;
}
} else {
flightTime++;
}
Vec3 from(x, y, z);
Vec3 to(x + xd, y + yd, z + zd);
HitResult res = level->clip(from, to, false, true);
from.set(x, y, z);
to.set(x + xd, y + yd, z + zd);
if (res.isHit()) {
to.set(res.pos.x, res.pos.y, res.pos.z);
}
Entity* hitEntity = NULL;
EntityList& objects = level->getEntities(this, this->bb.expand(xd, yd, zd).grow(1, 1, 1));
float nearest = 0;
for (unsigned int i = 0; i < objects.size(); i++) {
Entity* e = objects[i];
if (!e->isPickable() || (e->entityId == ownerId && flightTime < 5)) continue;
float rr = 0.3f;
AABB bb = e->bb.grow(rr, rr, rr);
HitResult p = bb.clip(from, to);
if (p.isHit()) {
float dd = from.distanceTo(p.pos);
if (dd < nearest || nearest == 0) {
hitEntity = e;
nearest = dd;
}
}
}
if (hitEntity != NULL) {
res = HitResult(hitEntity);
}
if (res.isHit()) {
if (res.type == ENTITY) {
float pow = Mth::sqrt(xd * xd + yd * yd + zd * zd);
int dmg = (int) std::ceil(pow * ARROW_BASE_DAMAGE);
if (critArrow) dmg += sharedRandom.nextInt(dmg / 2 + 2);
//@todo
//DamageSource damageSource = NULL;
//if (owner == NULL) {
// damageSource = DamageSource.arrow(this, this);
//} else {
// damageSource = DamageSource.arrow(this, owner);
//}
//if (res.entity->hurt(damageSource, dmg)) {
// if (res.entity instanceof Mob) {
if (res.entity->hurt(this, dmg)) {
if (res.entity->isMob()) {
((Mob*) res.entity)->arrowCount++;
}
level->playSound(this, "random.bowhit", 1.0f, 1.2f / (sharedRandom.nextFloat() * 0.2f + 0.9f));
remove();
} else {
xd *= -0.1f;
yd *= -0.1f;
zd *= -0.1f;
yRot += 180;
yRotO += 180;
flightTime = 0;
}
} 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 = Mth::sqrt(xd * xd + yd * yd + zd * zd);
x -= (xd / dd) * 0.05f;
y -= (yd / dd) * 0.05f;
z -= (zd / dd) * 0.05f;
level->playSound(this, "random.bowhit", 1.0f, 1.2f / (sharedRandom.nextFloat() * 0.2f + 0.9f));
inGround = true;
shakeTime = 7;
critArrow = false;
}
}
if (critArrow) {
for (int i = 0; i < 4; i++) {
level->addParticle(PARTICLETYPE(crit), x + xd * i / 4.0f, y + yd * i / 4.0f, z + zd * i / 4.0f, -xd, -yd + 0.2f, -zd);
}
}
x += xd;
y += yd;
z += zd;
float sd = Mth::sqrt(xd * xd + zd * zd);
yRot = std::atan2(xd, zd) * Mth::RADDEG;
xRot = std::atan2(yd, sd) * Mth::RADDEG;
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(PARTICLETYPE(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);
}
void Arrow::addAdditonalSaveData( CompoundTag* tag )
{
tag->putShort("xTile", (short) xTile);
tag->putShort("yTile", (short) yTile);
tag->putShort("zTile", (short) zTile);
tag->putByte("inTile", (char) lastTile);
tag->putByte("inData", (char) lastData);
tag->putByte("shake", (char) shakeTime);
tag->putByte("inGround", (char) (inGround ? 1 : 0));
tag->putBoolean("player", playerArrow);
}
void Arrow::readAdditionalSaveData( CompoundTag* tag )
{
xTile = tag->getShort("xTile");
yTile = tag->getShort("yTile");
zTile = tag->getShort("zTile");
lastTile = tag->getByte("inTile") & 0xff;
lastData = tag->getByte("inData") & 0xff;
shakeTime = tag->getByte("shake") & 0xff;
inGround = tag->getByte("inGround") == 1;
playerArrow = tag->getBoolean("player");
}
void Arrow::playerTouch( Player* player )
{
if (level->isClientSide) return;
if (inGround && playerArrow && shakeTime <= 0) {
ItemInstance item(Item::arrow, 1);
if (player->inventory->add(&item)) {
level->playSound(this, "random.pop", 0.2f, ((sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 0.7f + 1.0f) * 2.f);
player->take(this, 1);
remove();
}
}
}
float Arrow::getShadowHeightOffs() {
return 0;
}
int Arrow::getEntityTypeId() const {
return EntityTypes::IdArrow;
}
int Arrow::getAuxData() {
return ownerId;
}