the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "net.minecraft.world.level.h"
3#include "net.minecraft.world.level.tile.h"
4#include "net.minecraft.world.entity.h"
5#include "net.minecraft.world.entity.player.h"
6#include "net.minecraft.world.phys.h"
7#include "net.minecraft.world.item.h"
8#include "net.minecraft.world.damagesource.h"
9#include "net.minecraft.world.item.enchantment.h"
10#include "net.minecraft.network.packet.h"
11#include "..\Minecraft.Client\ServerPlayer.h"
12#include "..\Minecraft.Client\PlayerConnection.h"
13#include "com.mojang.nbt.h"
14#include "Arrow.h"
15
16// 4J : WESTY : Added for other award, kill creeper with arrow.
17#include "net.minecraft.world.entity.monster.h"
18#include "net.minecraft.stats.h"
19#include "SoundTypes.h"
20
21
22
23// base damage, multiplied with velocity
24const double Arrow::ARROW_BASE_DAMAGE = 2.0f;
25
26// 4J - added common ctor code.
27void Arrow::_init()
28{
29 // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that
30 // the derived version of the function is called
31 this->defineSynchedData();
32
33 xTile = -1;
34 yTile = -1;
35 zTile = -1;
36 lastTile = 0;
37 lastData = 0;
38 inGround = false;
39 pickup = PICKUP_DISALLOWED;
40 shakeTime = 0;
41 flightTime = 0;
42
43 owner = nullptr;
44 life = 0;
45
46 baseDamage = ARROW_BASE_DAMAGE;
47 knockback = 0;
48}
49
50
51Arrow::Arrow(Level *level) : Entity( level )
52{
53 _init();
54
55 viewScale = 10;
56 setSize(0.5f, 0.5f);
57}
58
59Arrow::Arrow(Level *level, shared_ptr<LivingEntity> mob, shared_ptr<LivingEntity> target, float power, float uncertainty) : Entity( level )
60{
61 _init();
62
63 viewScale = 10;
64 owner = mob;
65 if ( mob->instanceof(eTYPE_PLAYER) ) pickup = PICKUP_ALLOWED;
66
67 y = mob->y + mob->getHeadHeight() - 0.1f;
68
69 double xd = target->x - mob->x;
70 double yd = (target->y + target->getHeadHeight() - 0.7f) - y;
71 double zd = target->z - mob->z;
72 double sd = sqrt(xd * xd + zd * zd);
73 if (sd < 0.0000001) return;
74
75 float yRot = (float) (atan2(zd, xd) * 180 / PI) - 90;
76 float xRot = (float) -(atan2(yd, sd) * 180 / PI);
77
78 double xdn = xd / sd;
79 double zdn = zd / sd;
80 moveTo(mob->x + xdn, y, mob->z + zdn, yRot, xRot);
81 heightOffset = 0;
82
83 float yo = (float) sd * 0.2f;
84 shoot(xd, yd + yo, zd, power, uncertainty);
85}
86
87Arrow::Arrow(Level *level, double x, double y, double z) : Entity( level )
88{
89 _init();
90
91 viewScale = 10;
92 setSize(0.5f, 0.5f);
93
94 setPos(x, y, z);
95 heightOffset = 0;
96}
97
98Arrow::Arrow(Level *level, shared_ptr<LivingEntity> mob, float power) : Entity( level )
99{
100 _init();
101
102 viewScale = 10;
103 owner = mob;
104 if ( mob->instanceof(eTYPE_PLAYER) ) pickup = PICKUP_ALLOWED;
105
106 setSize(0.5f, 0.5f);
107
108 moveTo(mob->x, mob->y + mob->getHeadHeight(), mob->z, mob->yRot, mob->xRot);
109
110 x -= Mth::cos(yRot / 180 * PI) * 0.16f;
111 y -= 0.1f;
112 z -= Mth::sin(yRot / 180 * PI) * 0.16f;
113 setPos(x, y, z);
114 heightOffset = 0;
115
116 xd = -Mth::sin(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI);
117 zd = Mth::cos(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI);
118 yd = -Mth::sin(xRot / 180 * PI);
119
120 shoot(xd, yd, zd, power * 1.5f, 1);
121}
122
123
124void Arrow::defineSynchedData()
125{
126 entityData->define(ID_FLAGS, (byte) 0);
127}
128
129
130void Arrow::shoot(double xd, double yd, double zd, float pow, float uncertainty)
131{
132 float dist = (float) sqrt(xd * xd + yd * yd + zd * zd);
133
134 xd /= dist;
135 yd /= dist;
136 zd /= dist;
137
138 xd += (random->nextGaussian() * (random->nextBoolean() ? -1 : 1)) * 0.0075f * uncertainty;
139 yd += (random->nextGaussian() * (random->nextBoolean() ? -1 : 1)) * 0.0075f * uncertainty;
140 zd += (random->nextGaussian() * (random->nextBoolean() ? -1 : 1)) * 0.0075f * uncertainty;
141
142 xd *= pow;
143 yd *= pow;
144 zd *= pow;
145
146 this->xd = xd;
147 this->yd = yd;
148 this->zd = zd;
149
150 double sd = sqrt(xd * xd + zd * zd);
151
152 yRotO = yRot = (float) (atan2(xd, zd) * 180 / PI);
153 xRotO = xRot = (float) (atan2(yd, sd) * 180 / PI);
154 life = 0;
155}
156
157void Arrow::lerpTo(double x, double y, double z, float yRot, float xRot, int steps)
158{
159 setPos(x, y, z);
160 setRot(yRot, xRot);
161}
162
163void Arrow::lerpMotion(double xd, double yd, double zd)
164{
165 this->xd = xd;
166 this->yd = yd;
167 this->zd = zd;
168 if (xRotO == 0 && yRotO == 0)
169 {
170 double sd = sqrt(xd * xd + zd * zd);
171 yRotO = yRot = (float) (atan2( xd, zd) * 180 / PI);
172 xRotO = xRot = (float) (atan2( yd, sd) * 180 / PI);
173 xRotO = xRot;
174 yRotO = yRot;
175 app.DebugPrintf("%f %f : 0x%x\n",xRot,yRot,&yRot);
176 moveTo(x, y, z, yRot, xRot);
177 life = 0;
178 }
179}
180
181void Arrow::tick()
182{
183 Entity::tick();
184
185
186 if (xRotO == 0 && yRotO == 0)
187 {
188 double sd = sqrt(xd * xd + zd * zd);
189 yRotO = yRot = (float) (atan2(xd, zd) * 180 / PI);
190 xRotO = xRot = (float) (atan2(yd, sd) * 180 / PI);
191 }
192
193
194 {
195 int t = level->getTile(xTile, yTile, zTile);
196 if (t > 0)
197 {
198 Tile::tiles[t]->updateShape(level, xTile, yTile, zTile);
199 AABB *aabb = Tile::tiles[t]->getAABB(level, xTile, yTile, zTile);
200 if (aabb != NULL && aabb->contains(Vec3::newTemp(x, y, z)))
201 {
202 inGround = true;
203 }
204 }
205
206 }
207
208 if (shakeTime > 0) shakeTime--;
209
210 if (inGround)
211 {
212 int tile = level->getTile(xTile, yTile, zTile);
213 int data = level->getData(xTile, yTile, zTile);
214 if (tile != lastTile || data != lastData)
215 {
216 inGround = false;
217
218 xd *= random->nextFloat() * 0.2f;
219 yd *= random->nextFloat() * 0.2f;
220 zd *= random->nextFloat() * 0.2f;
221 life = 0;
222 flightTime = 0;
223 return;
224 }
225
226 else
227 {
228 life++;
229 if (life == 20 * 60) remove();
230 return;
231 }
232 }
233
234 else
235 {
236 flightTime++;
237 }
238
239 Vec3 *from = Vec3::newTemp(x, y, z);
240 Vec3 *to = Vec3::newTemp(x + xd, y + yd, z + zd);
241 HitResult *res = level->clip(from, to, false, true);
242
243 from = Vec3::newTemp(x, y, z);
244 to = Vec3::newTemp(x + xd, y + yd, z + zd);
245 if (res != NULL)
246 {
247 to = Vec3::newTemp(res->pos->x, res->pos->y, res->pos->z);
248 }
249 shared_ptr<Entity> hitEntity = nullptr;
250 vector<shared_ptr<Entity> > *objects = level->getEntities(shared_from_this(), this->bb->expand(xd, yd, zd)->grow(1, 1, 1));
251 double nearest = 0;
252 AUTO_VAR(itEnd, objects->end());
253 for (AUTO_VAR(it, objects->begin()); it != itEnd; it++)
254 {
255 shared_ptr<Entity> e = *it; //objects->at(i);
256 if (!e->isPickable() || (e == owner && flightTime < 5)) continue;
257
258 float rr = 0.3f;
259 AABB *bb = e->bb->grow(rr, rr, rr);
260 HitResult *p = bb->clip(from, to);
261 if (p != NULL)
262 {
263 double dd = from->distanceTo(p->pos);
264 if (dd < nearest || nearest == 0)
265 {
266 hitEntity = e;
267 nearest = dd;
268 }
269 delete p;
270 }
271 }
272
273 if (hitEntity != NULL)
274 {
275 delete res;
276 res = new HitResult(hitEntity);
277 }
278
279 if ( (res != NULL) && (res->entity != NULL) && res->entity->instanceof(eTYPE_PLAYER))
280 {
281 shared_ptr<Player> player = dynamic_pointer_cast<Player>(res->entity);
282 // 4J: Check for owner being null
283 if ( player->abilities.invulnerable || ((owner != NULL) && (owner->instanceof(eTYPE_PLAYER) && !dynamic_pointer_cast<Player>(owner)->canHarmPlayer(player))))
284 {
285 res = NULL;
286 }
287 }
288
289 if (res != NULL)
290 {
291 if (res->entity != NULL)
292 {
293 float pow = Mth::sqrt(xd * xd + yd * yd + zd * zd);
294 int dmg = (int) Mth::ceil((float)(pow * baseDamage));
295
296 if(isCritArrow()) dmg += random->nextInt(dmg / 2 + 2);
297
298 DamageSource *damageSource = NULL;
299 if (owner == NULL)
300 {
301 damageSource = DamageSource::arrow(dynamic_pointer_cast<Arrow>(shared_from_this()), shared_from_this());
302 }
303 else
304 {
305 damageSource = DamageSource::arrow(dynamic_pointer_cast<Arrow>(shared_from_this()), owner);
306 }
307
308 if(res->entity->hurt(damageSource, dmg))
309 {
310 // Firx for #67839 - Customer Encountered: Bows enchanted with "Flame" still set things on fire if pvp/attack animals is turned off
311 // 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)
312 // set targets on fire first because we want cooked
313 // pork/chicken/steak
314 if (isOnFire() && res->entity->GetType() != eTYPE_ENDERMAN)
315 {
316 res->entity->setOnFire(5);
317 }
318
319 if (res->entity->instanceof(eTYPE_LIVINGENTITY))
320 {
321 shared_ptr<LivingEntity> mob = dynamic_pointer_cast<LivingEntity>(res->entity);
322
323 if (!level->isClientSide)
324 {
325 mob->setArrowCount(mob->getArrowCount() + 1);
326 }
327 if (knockback > 0)
328 {
329 float pushLen = sqrt(xd * xd + zd * zd);
330 if (pushLen > 0)
331 {
332 res->entity->push(xd * knockback * .6f / pushLen, 0.1, zd * knockback * .6f / pushLen);
333 }
334 }
335
336 if (owner != NULL)
337 {
338 ThornsEnchantment::doThornsAfterAttack(owner, mob, random);
339 }
340
341 if (owner != NULL && res->entity != owner && owner->GetType() == eTYPE_SERVERPLAYER)
342 {
343 dynamic_pointer_cast<ServerPlayer>(owner)->connection->send( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::SUCCESSFUL_BOW_HIT, 0)) );
344 }
345 }
346
347 // 4J : WESTY : For award, need to track if creeper was killed by arrow from the player.
348 if (owner != NULL && owner->instanceof(eTYPE_PLAYER) // arrow owner is a player
349 && !res->entity->isAlive() // target is now dead
350 && (res->entity->GetType() == eTYPE_CREEPER)) // target is a creeper
351
352 {
353 dynamic_pointer_cast<Player>(owner)->awardStat(
354 GenericStats::arrowKillCreeper(),
355 GenericStats::param_arrowKillCreeper()
356 );
357 }
358
359 playSound( eSoundType_RANDOM_BOW_HIT, 1.0f, 1.2f / (random->nextFloat() * 0.2f + 0.9f));
360 if (res->entity->GetType() != eTYPE_ENDERDRAGON) remove();
361 }
362 else
363 {
364 xd *= -0.1f;
365 yd *= -0.1f;
366 zd *= -0.1f;
367 yRot += 180;
368 yRotO += 180;
369 flightTime = 0;
370 }
371
372 delete damageSource;
373 }
374 else
375 {
376 xTile = res->x;
377 yTile = res->y;
378 zTile = res->z;
379 lastTile = level->getTile(xTile, yTile, zTile);
380 lastData = level->getData(xTile, yTile, zTile);
381 xd = (float) (res->pos->x - x);
382 yd = (float) (res->pos->y - y);
383 zd = (float) (res->pos->z - z);
384 float dd = (float) sqrt(xd * xd + yd * yd + zd * zd);
385 // 4J added check - zero dd here was creating NaNs
386 if( dd > 0.0001f )
387 {
388 x -= (xd / dd) * 0.05f;
389 y -= (yd / dd) * 0.05f;
390 z -= (zd / dd) * 0.05f;
391 }
392
393 playSound(eSoundType_RANDOM_BOW_HIT, 1.0f, 1.2f / (random->nextFloat() * 0.2f + 0.9f));
394 inGround = true;
395 shakeTime = 7;
396 setCritArrow(false);
397
398 if (lastTile != 0)
399 {
400 Tile::tiles[lastTile]->entityInside(level, xTile, yTile, zTile, shared_from_this() );
401 }
402 }
403 }
404 delete res;
405
406 if(isCritArrow())
407 {
408 for (int i = 0; i < 4; i++)
409 {
410 level->addParticle(eParticleType_crit, x + xd * i / 4.0f, y + yd * i / 4.0f, z + zd * i / 4.0f, -xd, -yd + 0.2, -zd);
411 }
412 }
413
414 x += xd;
415 y += yd;
416 z += zd;
417
418 double sd = sqrt(xd * xd + zd * zd);
419 yRot = (float) (atan2(xd, zd) * 180 / PI);
420 xRot = (float) (atan2(yd, sd) * 180 / PI);
421
422 while (xRot - xRotO < -180)
423 xRotO -= 360;
424 while (xRot - xRotO >= 180)
425 xRotO += 360;
426
427 while (yRot - yRotO < -180)
428 yRotO -= 360;
429 while (yRot - yRotO >= 180)
430 yRotO += 360;
431
432 xRot = xRotO + (xRot - xRotO) * 0.2f;
433 yRot = yRotO + (yRot - yRotO) * 0.2f;
434
435
436 float inertia = 0.99f;
437 float gravity = 0.05f;
438
439 if (isInWater())
440 {
441 for (int i = 0; i < 4; i++)
442 {
443 float s = 1 / 4.0f;
444 level->addParticle(eParticleType_bubble, x - xd * s, y - yd * s, z - zd * s, xd, yd, zd);
445 }
446 inertia = 0.80f;
447 }
448
449 xd *= inertia;
450 yd *= inertia;
451 zd *= inertia;
452 yd -= gravity;
453
454 setPos(x, y, z);
455
456 checkInsideTiles();
457}
458
459void Arrow::addAdditonalSaveData(CompoundTag *tag)
460{
461 tag->putShort(L"xTile", (short) xTile);
462 tag->putShort(L"yTile", (short) yTile);
463 tag->putShort(L"zTile", (short) zTile);
464 tag->putByte(L"inTile", (byte) lastTile);
465 tag->putByte(L"inData", (byte) lastData);
466 tag->putByte(L"shake", (byte) shakeTime);
467 tag->putByte(L"inGround", (byte) (inGround ? 1 : 0));
468 tag->putByte(L"pickup", (byte) pickup);
469 tag->putDouble(L"damage", baseDamage);
470}
471
472void Arrow::readAdditionalSaveData(CompoundTag *tag)
473{
474 xTile = tag->getShort(L"xTile");
475 yTile = tag->getShort(L"yTile");
476 zTile = tag->getShort(L"zTile");
477 lastTile = tag->getByte(L"inTile") & 0xff;
478 lastData = tag->getByte(L"inData") & 0xff;
479 shakeTime = tag->getByte(L"shake") & 0xff;
480 inGround = tag->getByte(L"inGround") == 1;
481 if (tag->contains(L"damage"))
482 {
483 baseDamage = tag->getDouble(L"damage");
484 }
485
486 if (tag->contains(L"pickup"))
487 {
488 pickup = tag->getByte(L"pickup");
489 }
490 else if (tag->contains(L"player"))
491 {
492 pickup = tag->getBoolean(L"player") ? PICKUP_ALLOWED : PICKUP_DISALLOWED;
493 }
494}
495
496void Arrow::playerTouch(shared_ptr<Player> player)
497{
498 if (level->isClientSide || !inGround || shakeTime > 0) return;
499
500 bool bRemove = pickup == PICKUP_ALLOWED || (pickup == PICKUP_CREATIVE_ONLY && player->abilities.instabuild);
501
502 if (pickup == PICKUP_ALLOWED)
503 {
504 if (!player->inventory->add( shared_ptr<ItemInstance>( new ItemInstance(Item::arrow, 1) ) ))
505 {
506 bRemove = false;
507 }
508 }
509
510 if (bRemove)
511 {
512 playSound(eSoundType_RANDOM_POP, 0.2f, ((random->nextFloat() - random->nextFloat()) * 0.7f + 1.0f) * 2.0f);
513 player->take(shared_from_this(), 1);
514 remove();
515 }
516}
517
518bool Arrow::makeStepSound()
519{
520 return false;
521}
522
523float Arrow::getShadowHeightOffs()
524{
525 return 0;
526}
527
528void Arrow::setBaseDamage(double baseDamage)
529{
530 this->baseDamage = baseDamage;
531}
532
533double Arrow::getBaseDamage()
534{
535 return baseDamage;
536}
537
538void Arrow::setKnockback(int knockback)
539{
540 this->knockback = knockback;
541}
542
543bool Arrow::isAttackable()
544{
545 return false;
546}
547
548void Arrow::setCritArrow(bool critArrow)
549{
550 byte flags = entityData->getByte(ID_FLAGS);
551 if (critArrow)
552 {
553 entityData->set(ID_FLAGS, (byte) (flags | FLAG_CRIT));
554 }
555 else
556 {
557 entityData->set(ID_FLAGS, (byte) (flags & ~FLAG_CRIT));
558 }
559}
560
561bool Arrow::isCritArrow()
562{
563 byte flags = entityData->getByte(ID_FLAGS);
564 return (flags & FLAG_CRIT) != 0;
565}