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.ai.attributes.h"
6#include "net.minecraft.world.entity.boss.h"
7#include "net.minecraft.world.entity.monster.h"
8#include "net.minecraft.world.entity.projectile.h"
9#include "net.minecraft.world.phys.h"
10#include "net.minecraft.world.damagesource.h"
11#include "BasicTypeContainers.h"
12#include "..\Minecraft.Client\Textures.h"
13#include "net.minecraft.world.entity.boss.enderdragon.h"
14#include "net.minecraft.world.level.pathfinder.h"
15#include "SharedConstants.h"
16#include "EnderDragon.h"
17
18#define PRINT_DRAGON_STATE_CHANGE_MESSAGES 1
19
20
21
22// 4J Added for new dragon behaviour
23const int EnderDragon::CRYSTAL_COUNT = 8;
24const int EnderDragon::FLAME_TICKS = 60;
25const float EnderDragon::FLAME_ANGLE = 22.5f;
26const int EnderDragon::FLAME_PASSES = 4; // How many times it covers FLAME_ANGLE in FLAME_TICKS
27const int EnderDragon::FLAME_FREQUENCY = 2; // Every FLAME_FREQUENCY ticks it sets fire to blocks while doing a flame pass
28const int EnderDragon::FLAME_RANGE = 10;
29
30const int EnderDragon::ATTACK_TICKS = SharedConstants::TICKS_PER_SECOND * 2; // Time for the dragon roar to play
31
32const int EnderDragon::SITTING_ATTACK_Y_VIEW_RANGE = 10; // The player must be now lower and no higher than the dragon by this amount
33const int EnderDragon::SITTING_ATTACK_VIEW_RANGE = EnderDragon::FLAME_RANGE * 2;
34const int EnderDragon::SITTING_ATTACK_RANGE = EnderDragon::FLAME_RANGE * 2;
35const int EnderDragon::SITTING_POST_ATTACK_IDLE_TICKS = 40;
36const int EnderDragon::SITTING_SCANNING_IDLE_TICKS = 100;
37const int EnderDragon::SITTING_FLAME_ATTACKS_COUNT = 4; // How many times the dragons does the scan/roar/flame cycle before flying off
38
39// The percentage of max health that the dragon will take while in the "Sitting" states before flying away
40const float EnderDragon::SITTING_ALLOWED_DAMAGE_PERCENTAGE = 0.25f;
41
42void EnderDragon::_init()
43{
44 // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that
45 // the derived version of the function is called
46 this->defineSynchedData();
47 registerAttributes();
48 setHealth(getMaxHealth());
49
50 xTarget = yTarget = zTarget = 0.0;
51 posPointer = -1;
52 oFlapTime = 0;
53 flapTime = 0;
54 newTarget = false;
55 inWall = false;
56 attackTarget = nullptr;
57 dragonDeathTime = 0;
58 nearestCrystal = nullptr;
59
60 // 4J Stu - Added for new dragon behaviour
61 m_remainingCrystalsCount = CRYSTAL_COUNT;
62 m_fireballCharge = 0;
63 m_holdingPatternAngle = 0.0f;
64 m_holdingPatternClockwise = true;
65 setSynchedAction(e_EnderdragonAction_HoldingPattern);
66 m_actionTicks = 0;
67 m_sittingDamageReceived = 0;
68 m_headYRot = 0.0;
69 m_acidArea = AABB::newPermanent(-4,-10,-3,6,3,3);
70 m_flameAttacks = 0;
71
72 for (int i = 0; i < positionsLength; i++)
73 {
74 positions[i][0] = 0;
75 positions[i][1] = 0;
76 positions[i][2] = 0;
77 }
78
79 m_nodes = new NodeArray(24);
80 openSet = new BinaryHeap();
81 m_currentPath = NULL;
82}
83
84EnderDragon::EnderDragon(Level *level) : Mob(level)
85{
86 _init();
87
88 setSize(16, 8);
89
90 noPhysics = true;
91 fireImmune = true;
92
93 yTarget = 100;
94
95 m_iGrowlTimer=100;
96
97 noCulling = true;
98}
99
100// 4J - split off from ctor so we can use shared_from_this()
101void EnderDragon::AddParts()
102{
103 head = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"head", 6, 6) );
104 neck = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"neck", 6, 6) ); // 4J Added
105 body = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"body", 8, 8) );
106 tail1 = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"tail", 4, 4) );
107 tail2 = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"tail", 4, 4) );
108 tail3 = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"tail", 4, 4) );
109 wing1 = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"wing", 4, 4) );
110 wing2 = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"wing", 4, 4) );
111
112 subEntities.push_back(head);
113 subEntities.push_back(neck); // 4J Added
114 subEntities.push_back(body);
115 subEntities.push_back(tail1);
116 subEntities.push_back(tail2);
117 subEntities.push_back(tail3);
118 subEntities.push_back(wing1);
119 subEntities.push_back(wing2);
120}
121
122EnderDragon::~EnderDragon()
123{
124 if(m_nodes->data != NULL)
125 {
126 for(unsigned int i = 0; i < m_nodes->length; ++i)
127 {
128 if(m_nodes->data[i]!=NULL) delete m_nodes->data[i];
129 }
130 delete [] m_nodes->data;
131 }
132 delete openSet;
133 if( m_currentPath != NULL ) delete m_currentPath;
134}
135
136void EnderDragon::registerAttributes()
137{
138 Mob::registerAttributes();
139
140 getAttribute(SharedMonsterAttributes::MAX_HEALTH)->setBaseValue(200);
141}
142
143void EnderDragon::defineSynchedData()
144{
145 Mob::defineSynchedData();
146
147 // 4J Added for new dragon behaviour
148 entityData->define(DATA_ID_SYNCHED_ACTION, e_EnderdragonAction_HoldingPattern);
149}
150
151void EnderDragon::getLatencyPos(doubleArray result, int step, float a)
152{
153 if (getHealth() <= 0)
154 {
155 a = 0;
156 }
157
158 a = 1 - a;
159
160 int p0 = (posPointer - step * 1) & 63;
161 int p1 = (posPointer - step * 1 - 1) & 63;
162
163 // positions is a ring buffer of size positionsLength (64) storing positional information per tick
164 // positions[i][0] is y rotation
165 // positions[i][1] is y position
166 // positions[i][2] is currently always 0
167
168 double yr0 = positions[p0][0];
169 double yrd = Mth::wrapDegrees(positions[p1][0] - yr0);
170 result[0] = yr0 + yrd * a;
171
172 yr0 = positions[p0][1];
173 yrd = positions[p1][1] - yr0;
174
175 result[1] = yr0 + yrd * a;
176 result[2] = positions[p0][2] + (positions[p1][2] - positions[p0][2]) * a;
177}
178
179void EnderDragon::aiStep()
180{
181 if (level->isClientSide)
182 {
183 // 4J Stu - If saved when dead we need to make sure that the actual health is updated correctly on the client
184 // Fix for TU9: Content: Gameplay: Enderdragon respawns after loading game which was previously saved at point of hes death
185 setHealth(getHealth());
186
187 float flap = Mth::cos(flapTime * PI * 2);
188 float oldFlap = Mth::cos(oFlapTime * PI * 2);
189
190 if (oldFlap <= -0.3f && flap >= -0.3f)
191 {
192 level->playLocalSound(x, y, z, eSoundType_MOB_ENDERDRAGON_MOVE, 1, 0.8f + random->nextFloat() * .3f, false, 100.0f);
193 }
194 // play a growl every now and then
195 if(! (getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ||
196 getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ||
197 getSynchedAction() == e_EnderdragonAction_Sitting_Attacking))
198 {
199 m_iGrowlTimer--;
200 if(m_iGrowlTimer<0)
201 {
202 level->playLocalSound(x, y, z, eSoundType_MOB_ENDERDRAGON_GROWL, 0.5f, 0.8f + random->nextFloat() * .3f, false, 100.0f);
203 m_iGrowlTimer=200+(random->nextInt(200));
204 }
205 }
206 }
207
208 oFlapTime = flapTime;
209
210 if (getHealth() <= 0)
211 {
212 // level.addParticle("explode", x + random.nextFloat() * bbWidth * 2 - bbWidth, y + random.nextFloat() * bbHeight, z + random.nextFloat() * bbWidth * 2 - bbWidth, 0, 0, 0);
213 float xo = (random->nextFloat() - 0.5f) * 8;
214 float yo = (random->nextFloat() - 0.5f) * 4;
215 float zo = (random->nextFloat() - 0.5f) * 8;
216 level->addParticle(eParticleType_largeexplode, x + xo, y + 2 + yo, z + zo, 0, 0, 0);
217 return;
218 }
219
220 checkCrystals();
221
222 float flapSpeed = 0.2f / (sqrt(xd * xd + zd * zd) * 10.0f + 1);
223 flapSpeed *= (float) pow(2.0, yd);
224 if ( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ||
225 getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ||
226 getSynchedAction() == e_EnderdragonAction_Sitting_Attacking)
227 {
228 //app.DebugPrintf("flapSpeed is %f\n", flapSpeed);
229 //flapTime += flapSpeed * 2;
230 flapTime += 0.1f;
231 }
232 else if (inWall)
233 {
234 flapTime += flapSpeed * 0.5f;
235 }
236 else
237 {
238 flapTime += flapSpeed;
239 }
240
241 yRot = Mth::wrapDegrees(yRot);
242
243 if (posPointer < 0)
244 {
245 for (int i = 0; i < positionsLength; i++)
246 {
247 positions[i][0] = yRot;
248 positions[i][1] = y;
249 }
250 }
251
252 if (++posPointer == positionsLength) posPointer = 0;
253 positions[posPointer][0] = yRot;
254 positions[posPointer][1] = y;
255
256
257 if (level->isClientSide)
258 {
259 if (lSteps > 0)
260 {
261 double xt = x + (lx - x) / lSteps;
262 double yt = y + (ly - y) / lSteps;
263 double zt = z + (lz - z) / lSteps;
264
265 // 4J Stu - The movement is so small that this head animation doesn't look good
266 //if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming )
267 //{
268 // double yrd = lyr - (yRot + m_headYRot);
269 // while (yrd < -180)
270 // yrd += 360;
271 // while (yrd >= 180)
272 // yrd -= 360;
273
274 // m_headYRot += (yrd) / lSteps;
275 //}
276 //else
277 {
278 double yrd = Mth::wrapDegrees(lyr - yRot);
279
280 m_headYRot = 0.0;
281 yRot += (yrd) / lSteps;
282 }
283 xRot += (lxr - xRot) / lSteps;
284
285 lSteps--;
286 this->setPos(xt, yt, zt);
287 this->setRot(yRot, xRot);
288
289 /*
290 * List<AABB> collisions = level.getCubes(this, bb.shrink(1 / 32.0, 0, 1 /
291 * 32.0)); if (collisions.size() > 0) { double yTop = 0; for (int i = 0; i <
292 * collisions.size(); i++) { AABB ab = collisions.get(i); if (ab.y1 > yTop) yTop
293 * = ab.y1; } yt += yTop - bb.y0; setPos(xt, yt, zt); }
294 */
295
296 }
297
298 if( getSynchedAction() == e_EnderdragonAction_Landing || (getSynchedAction() == e_EnderdragonAction_Sitting_Flaming && tickCount%2==0) )
299 {
300 double xP = 0.0;
301 double yP = 0.0;
302 double zP = 0.0;
303 Vec3 *v = getHeadLookVector(1); //getViewVector(1);
304 //app.DebugPrintf("View vector is (%f,%f,%f) - lsteps %d\n", v->x, v->y, v->z, lSteps);
305 //unsigned int d = 0;
306 //for(unsigned int d = 1; d < 3; ++d)
307 {
308 Vec3 *vN = v->normalize();
309 vN->yRot(-PI/4);
310 for(unsigned int i = 0; i < 8; ++i)
311 {
312 if(getSynchedAction() == e_EnderdragonAction_Landing)
313 {
314 //for(unsigned int j = 0; j < 6; ++j)
315 {
316 xP = head->x;// - vN->x * d;
317 yP = head->bb->y0 + head->bbHeight / 2;// - vN->y * d; //head->y + head->bbHeight / 2 + 0.5f - v->y * d;
318 zP = head->z;// - vN->z * d;
319 xP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2;
320 yP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2;
321 zP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2;
322 level->addParticle(eParticleType_dragonbreath, xP, yP, zP, (-vN->x * 0.08) + xd, (-vN->y * 0.3) + yd, (-vN->z * 0.08) + zd);
323 }
324 }
325 else
326 {
327 double yVelocity = 0.6;
328 double xzVelocity = 0.08;
329 for(unsigned int j = 0; j < 6; ++j)
330 {
331 xP = head->x;// - vN->x * d;
332 yP = head->bb->y0 + head->bbHeight / 2;// - vN->y * d; //head->y + head->bbHeight / 2 + 0.5f - v->y * d;
333 zP = head->z;// - vN->z * d;
334 xP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2;
335 yP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2;
336 zP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2;
337 level->addParticle(eParticleType_dragonbreath, xP, yP, zP, -vN->x * xzVelocity*j, -vN->y * yVelocity, -vN->z * xzVelocity*j);
338 }
339 }
340 vN->yRot(PI/(2*8) );
341 }
342 }
343 }
344 else if( getSynchedAction() == e_EnderdragonAction_Sitting_Attacking )
345 {
346 // AP - changed this to use playLocalSound because no sound could be heard with playSound (cos it's a stub function)
347 level->playLocalSound(x, y, z, eSoundType_MOB_ENDERDRAGON_GROWL, 0.5f, 0.8f + random->nextFloat() * .3f, false, 100.0f);
348 }
349 }
350 else
351 {
352 double xdd = xTarget - x;
353 double ydd = yTarget - y;
354 double zdd = zTarget - z;
355
356 double dist = xdd * xdd + ydd * ydd + zdd * zdd;
357
358 if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming )
359 {
360 --m_actionTicks;
361 if(m_actionTicks <= 0)
362 {
363 if( m_flameAttacks >= SITTING_FLAME_ATTACKS_COUNT)
364 {
365 setSynchedAction(e_EnderdragonAction_Takeoff);
366#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
367 app.DebugPrintf("Dragon action is now: Takeoff\n");
368#endif
369 newTarget = true;
370 }
371 else
372 {
373 setSynchedAction(e_EnderdragonAction_Sitting_Scanning);
374 attackTarget = level->getNearestPlayer( shared_from_this(), SITTING_ATTACK_VIEW_RANGE, SITTING_ATTACK_Y_VIEW_RANGE );
375#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
376 app.DebugPrintf("Dragon action is now: SittingScanning\n");
377#endif
378 }
379 }
380 }
381 else if( getSynchedAction() == e_EnderdragonAction_Sitting_Scanning )
382 {
383 attackTarget = level->getNearestPlayer( shared_from_this(), SITTING_ATTACK_VIEW_RANGE, SITTING_ATTACK_Y_VIEW_RANGE );
384
385 ++m_actionTicks;
386 if( attackTarget != NULL )
387 {
388 if(m_actionTicks > SITTING_SCANNING_IDLE_TICKS/4)
389 {
390 setSynchedAction(e_EnderdragonAction_Sitting_Attacking);
391#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
392 app.DebugPrintf("Dragon action is now: SittingAttacking\n");
393#endif
394 m_actionTicks = ATTACK_TICKS;
395 }
396 }
397 else
398 {
399 if(m_actionTicks >= SITTING_SCANNING_IDLE_TICKS)
400 {
401 setSynchedAction(e_EnderdragonAction_Takeoff);
402#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
403 app.DebugPrintf("Dragon action is now: Takeoff\n");
404#endif
405 newTarget = true;
406 }
407 }
408 }
409 else if( getSynchedAction() == e_EnderdragonAction_Sitting_Attacking )
410 {
411 --m_actionTicks;
412 if(m_actionTicks <= 0)
413 {
414 ++m_flameAttacks;
415 setSynchedAction(e_EnderdragonAction_Sitting_Flaming);
416 attackTarget = level->getNearestPlayer( shared_from_this(), SITTING_ATTACK_VIEW_RANGE, SITTING_ATTACK_Y_VIEW_RANGE );
417#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
418 app.DebugPrintf("Dragon action is now: SittingFlaming\n");
419#endif
420 m_actionTicks = FLAME_TICKS;
421 }
422 }
423 else if( !newTarget && getSynchedAction() == e_EnderdragonAction_Takeoff)
424 {
425 int eggHeight = level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS); //level->getHeightmap(4,4);
426
427 float dist = distanceToSqr(PODIUM_X_POS, eggHeight, PODIUM_Z_POS);
428 if(dist > (10.0f * 10.0f) )
429 {
430 setSynchedAction(e_EnderdragonAction_HoldingPattern);
431#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
432 app.DebugPrintf("Dragon action is now: HoldingPattern\n");
433#endif
434 }
435 }
436 else if (newTarget || ( (getSynchedAction() != e_EnderdragonAction_Landing && dist < 10 * 10) || dist < 1) || dist > 150 * 150 || horizontalCollision || verticalCollision)
437 {
438 findNewTarget();
439 }
440
441 if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || getSynchedAction() == e_EnderdragonAction_Landing )
442 {
443 if( m_actionTicks < (FLAME_TICKS - 10) )
444 {
445 vector<shared_ptr<Entity> > *targets = level->getEntities(shared_from_this(), m_acidArea);
446
447 for( AUTO_VAR(it, targets->begin() ); it != targets->end(); ++it)
448 {
449 if ( (*it)->instanceof(eTYPE_LIVINGENTITY) )
450 {
451 //app.DebugPrintf("Attacking entity with acid\n");
452 shared_ptr<LivingEntity> e = dynamic_pointer_cast<LivingEntity>( *it );
453 e->hurt(DamageSource::dragonbreath, 2);
454 }
455 }
456 }
457 }
458 if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming )
459 {
460 // No movement
461 }
462 else if( getSynchedAction() == e_EnderdragonAction_Sitting_Scanning )
463 {
464 if( attackTarget != NULL)
465 {
466 Vec3 *aim = Vec3::newTemp((attackTarget->x - x), 0, (attackTarget->z - z))->normalize();
467 Vec3 *dir = Vec3::newTemp(sin(yRot * PI / 180), 0, -cos(yRot * PI / 180))->normalize();
468 float dot = (float)dir->dot(aim);
469 float angleDegs = acos(dot)*180/PI;
470 angleDegs = angleDegs + 0.5f;
471
472 if( angleDegs < 0 || angleDegs > 10 )
473 {
474 double xdd = attackTarget->x - head->x;
475 //double ydd = (attackTarget->bb->y0 + attackTarget->bbHeight / 2) - (head->y + head->bbHeight / 2);
476 double zdd = attackTarget->z - head->z;
477
478 double yRotT = (180) - atan2(xdd, zdd) * 180 / PI;
479 double yRotD = Mth::wrapDegrees(yRotT - yRot);
480
481 if (yRotD > 50) yRotD = 50;
482 if (yRotD < -50) yRotD = -50;
483
484 double xd = xTarget - x;
485 double zd = zTarget - z;
486 yRotA *= 0.80f;
487
488 float rotSpeed = sqrt(xd * xd + zd * zd) * 1 + 1;
489 double distToTarget = sqrt(xd * xd + zd * zd) * 1 + 1;
490 if (distToTarget > 40) distToTarget = 40;
491 yRotA += yRotD * ((0.7f / distToTarget) / rotSpeed);
492 yRot += yRotA;
493 }
494 else
495 {
496 //setSynchedAction(e_EnderdragonAction_Sitting_Flaming);
497#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
498 //app.DebugPrintf("Dragon action is now : SittingFlaming\n");
499#endif
500 //m_actionTicks = FLAME_TICKS;
501 }
502 }
503 else
504 {
505 //setSynchedAction(e_EnderdragonAction_Sitting_Flaming);
506 //app.DebugPrintf("Dragon action is now : SittingFlaming\n");
507 //m_actionTicks = 0;
508 }
509 }
510 else if( getSynchedAction() == e_EnderdragonAction_Sitting_Attacking )
511 {
512
513 }
514 else
515 {
516 // double xTargetO = xTarget;
517 // double yTargetO = yTarget;
518 // double zTargetO = zTarget;
519 if (getSynchedAction() == e_EnderdragonAction_StrafePlayer && attackTarget != NULL && m_currentPath != NULL && m_currentPath->isDone())
520 {
521 xTarget = attackTarget->x;
522 zTarget = attackTarget->z;
523
524 double xd = xTarget - x;
525 double zd = zTarget - z;
526 double sd = sqrt(xd * xd + zd * zd);
527 double ho = 0.4f + sd / 80.0f - 1;
528 if (ho > 10) ho = 10;
529 yTarget = attackTarget->bb->y0 + ho;
530 }
531 else
532 {
533 //xTarget += random->nextGaussian() * 2;
534 //zTarget += random->nextGaussian() * 2;
535 }
536 ydd = ydd / (sqrt(xdd * xdd + zdd * zdd));
537 float max = 0.6f;
538 if(getSynchedAction() == e_EnderdragonAction_Landing) max = 1.5f;
539 if (ydd < -max) ydd = -max;
540 if (ydd > max) ydd = max;
541 yd += (ydd) * 0.1f;
542 while (yRot < -180)
543 yRot += 180 * 2;
544 while (yRot >= 180)
545 yRot -= 180 * 2;
546
547
548 double yRotT = (180) - atan2(xdd, zdd) * 180 / PI;
549 double yRotD = yRotT - yRot;
550 while (yRotD < -180)
551 yRotD += 180 * 2;
552 while (yRotD >= 180)
553 yRotD -= 180 * 2;
554
555
556 if (yRotD > 50) yRotD = 50;
557 if (yRotD < -50) yRotD = -50;
558
559 Vec3 *aim = Vec3::newTemp((xTarget - x), (yTarget - y), (zTarget - z))->normalize();
560 Vec3 *dir = Vec3::newTemp(sin(yRot * PI / 180), yd, -cos(yRot * PI / 180))->normalize();
561 float dot = (float) (dir->dot(aim) + 0.5f) / 1.5f;
562 if (dot < 0) dot = 0;
563
564 yRotA *= 0.80f;
565
566 float rotSpeed = sqrt(xd * xd + zd * zd) * 1 + 1;
567 double distToTarget = sqrt(xd * xd + zd * zd) * 1 + 1;
568 if (distToTarget > 40) distToTarget = 40;
569 if(getSynchedAction() == e_EnderdragonAction_Landing)
570 {
571 yRotA += yRotD * (distToTarget / rotSpeed);
572 }
573 else
574 {
575 yRotA += yRotD * ((0.7f / distToTarget) / rotSpeed);
576 }
577 yRot += yRotA * 0.1f;
578
579 float span = (float) (2.0f / (distToTarget + 1));
580 float speed = 0.06f;
581 moveRelative(0, -1, speed * (dot * span + (1 - span)));
582 if (inWall)
583 {
584 move(xd * 0.8f, yd * 0.8f, zd * 0.8f);
585 }
586 else
587 {
588 move(xd, yd, zd);
589
590 }
591
592 Vec3 *actual = Vec3::newTemp(xd, yd, zd)->normalize();
593 float slide = (float) (actual->dot(dir) + 1) / 2.0f;
594 slide = 0.8f + 0.15f * slide;
595
596
597 xd *= slide;
598 zd *= slide;
599 yd *= 0.91f;
600 }
601 }
602
603 yBodyRot = yRot;
604
605 head->bbWidth = head->bbHeight = 1; // 4J Stu - Replaced what was "head" with "neck" //3;
606 neck->bbWidth = neck->bbHeight = 3;
607 tail1->bbWidth = tail1->bbHeight = 2;
608 tail2->bbWidth = tail2->bbHeight = 2;
609 tail3->bbWidth = tail3->bbHeight = 2;
610 body->bbHeight = 3;
611 body->bbWidth = 5;
612 wing1->bbHeight = 2;
613 wing1->bbWidth = 4;
614 wing2->bbHeight = 3;
615 wing2->bbWidth = 4;
616
617 //double latencyPosAcomponents[3],latencyPosBcomponents[3];
618 //doubleArray latencyPosA = doubleArray(latencyPosAcomponents,3);
619 //doubleArray latencyPosB = doubleArray(latencyPosBcomponents,3);
620 //getLatencyPos(latencyPosA, 5, 1);
621 //getLatencyPos(latencyPosB, 10, 1);
622
623 //float tilt = (float) (latencyPosA[1] - latencyPosB[1]) * 10 / 180.0f * PI;
624 float tilt = (float) getTilt(1) / 180.0f * PI;
625 float ccTilt = cos(tilt);
626
627 // 4J Stu - ssTilt was negative sin(tilt), but this causes the bounding boxes of the parts to head in the wrong y direction
628 // i.e. head moves up when tilting forward, and down when tilting backwards
629 float ssTilt = sin(tilt);
630
631
632 float rot1 = yRot * PI / 180;
633 float ss1 = sin(rot1);
634 float cc1 = cos(rot1);
635
636 body->tick();
637 body->moveTo(x + ss1 * 0.5f, y, z - cc1 * 0.5f, 0, 0);
638 wing1->tick();
639 wing1->moveTo(x + cc1 * 4.5f, y + 2, z + ss1 * 4.5f, 0, 0);
640 wing2->tick();
641 wing2->moveTo(x - cc1 * 4.5f, y + 2, z - ss1 * 4.5f, 0, 0);
642
643 if (!level->isClientSide) checkAttack();
644 if (!level->isClientSide && hurtDuration == 0)
645 {
646 knockBack(level->getEntities(shared_from_this(), wing1->bb->grow(4, 2, 4)->move(0, -2, 0)));
647 knockBack(level->getEntities(shared_from_this(), wing2->bb->grow(4, 2, 4)->move(0, -2, 0)));
648 hurt(level->getEntities(shared_from_this(), neck->bb->grow(1, 1, 1)));
649 hurt(level->getEntities(shared_from_this(), head->bb->grow(1, 1, 1)));
650 }
651
652 double p1components[3];
653 doubleArray p1 = doubleArray(p1components, 3);
654 getLatencyPos(p1, 5, 1);
655
656 {
657 //double p0components[3];
658 //doubleArray p0 = doubleArray(p0components, 3);
659 //getLatencyPos(p0, 0, 1);
660
661 double yRotDiff = getHeadYRotDiff(1);
662
663 float ss = sin((yRot + yRotDiff) * PI / 180 - yRotA * 0.01f);
664 float cc = cos((yRot + yRotDiff) * PI / 180 - yRotA * 0.01f);
665 head->tick();
666 neck->tick();
667 double yOffset = getHeadYOffset(1); // (p0[1] - p1[1]) * 1
668
669 // 4J Stu - Changed the head entity to only be the head, and not include the neck parts
670 head->moveTo(x + ss * 6.5f * ccTilt, y + yOffset + ssTilt * 6.5f, z - cc * 6.5f * ccTilt, 0, 0);
671
672 // Neck position is where the java code used to move the "head" object which was head and neck
673 neck->moveTo(x + ss * 5.5f * ccTilt, y + yOffset + ssTilt * 5.5f, z - cc * 5.5f * ccTilt, 0, 0);
674
675 double acidX = x + ss * 9.5f * ccTilt;
676 double acidY = y + yOffset + ssTilt * 10.5f;
677 double acidZ = z - cc * 9.5f * ccTilt;
678 m_acidArea->set(acidX - 5, acidY - 17, acidZ - 5, acidX + 5, acidY + 4, acidZ + 5);
679
680 //app.DebugPrintf("\nDragon is %s, yRot = %f, yRotA = %f, ss = %f, cc = %f, ccTilt = %f\n",level->isClientSide?"client":"server", yRot, yRotA, ss, cc, ccTilt);
681 //app.DebugPrintf("Body (%f,%f,%f) to (%f,%f,%f)\n", body->bb->x0, body->bb->y0, body->bb->z0, body->bb->x1, body->bb->y1, body->bb->z1);
682 //app.DebugPrintf("Neck (%f,%f,%f) to (%f,%f,%f)\n", neck->bb->x0, neck->bb->y0, neck->bb->z0, neck->bb->x1, neck->bb->y1, neck->bb->z1);
683 //app.DebugPrintf("Head (%f,%f,%f) to (%f,%f,%f)\n", head->bb->x0, head->bb->y0, head->bb->z0, head->bb->x1, head->bb->y1, head->bb->z1);
684 //app.DebugPrintf("Acid (%f,%f,%f) to (%f,%f,%f)\n\n", m_acidArea->x0, m_acidArea->y0, m_acidArea->z0, m_acidArea->x1, m_acidArea->y1, m_acidArea->z1);
685 }
686
687 // Curls/straightens the tail
688 for (int i = 0; i < 3; i++)
689 {
690 shared_ptr<MultiEntityMobPart> part = nullptr;
691
692 if (i == 0) part = tail1;
693 if (i == 1) part = tail2;
694 if (i == 2) part = tail3;
695
696 double p0components[3];
697 doubleArray p0 = doubleArray(p0components, 3);
698 getLatencyPos(p0, 12 + i * 2, 1);
699
700 float rot = yRot * PI / 180 + rotWrap(p0[0] - p1[0]) * PI / 180 * (1);
701 float ss = sin(rot);
702 float cc = cos(rot);
703
704 float dd1 = 1.5f;
705 float dd = (i + 1) * 2.0f;
706 part->tick();
707 part->moveTo(x - (ss1 * dd1 + ss * dd) * ccTilt, y + (p0[1] - p1[1]) * 1 - (dd + dd1) * ssTilt + 1.5f, z + (cc1 * dd1 + cc * dd) * ccTilt, 0, 0);
708 }
709
710
711 // 4J Stu - Fireball attack taken from Ghast
712 if (!level->isClientSide)
713 {
714 double maxDist = 64.0f;
715 if (getSynchedAction() == e_EnderdragonAction_StrafePlayer && attackTarget != NULL && attackTarget->distanceToSqr(shared_from_this()) < maxDist * maxDist)
716 {
717 if (this->canSee(attackTarget))
718 {
719 m_fireballCharge++;
720 Vec3 *aim = Vec3::newTemp((attackTarget->x - x), 0, (attackTarget->z - z))->normalize();
721 Vec3 *dir = Vec3::newTemp(sin(yRot * PI / 180), 0, -cos(yRot * PI / 180))->normalize();
722 float dot = (float)dir->dot(aim);
723 float angleDegs = acos(dot)*180/PI;
724 angleDegs = angleDegs + 0.5f;
725
726 if (m_fireballCharge >= 20 && ( angleDegs >= 0 && angleDegs < 10 ))
727 {
728 double d = 1;
729 Vec3 *v = getViewVector(1);
730 float startingX = head->x - v->x * d;
731 float startingY = head->y + head->bbHeight / 2 + 0.5f;
732 float startingZ = head->z - v->z * d;
733
734 double xdd = attackTarget->x - startingX;
735 double ydd = (attackTarget->bb->y0 + attackTarget->bbHeight / 2) - (startingY + head->bbHeight / 2);
736 double zdd = attackTarget->z - startingZ;
737
738 level->levelEvent(nullptr, LevelEvent::SOUND_GHAST_FIREBALL, (int) x, (int) y, (int) z, 0);
739 shared_ptr<DragonFireball> ie = shared_ptr<DragonFireball>( new DragonFireball(level, dynamic_pointer_cast<Mob>( shared_from_this() ), xdd, ydd, zdd) );
740 ie->x = startingX;
741 ie->y = startingY;
742 ie->z = startingZ;
743 level->addEntity(ie);
744 m_fireballCharge = 0;
745
746 app.DebugPrintf("Finding new target due to having fired a fireball\n");
747 if( m_currentPath != NULL )
748 {
749 while(!m_currentPath->isDone())
750 {
751 m_currentPath->next();
752 }
753 }
754 newTarget = true;
755 findNewTarget();
756 }
757 }
758 else
759 {
760 if (m_fireballCharge > 0) m_fireballCharge--;
761 }
762 }
763 else
764 {
765 if (m_fireballCharge > 0) m_fireballCharge--;
766 }
767 }
768 // End fireball attack
769
770 if (!level->isClientSide)
771 {
772 inWall = checkWalls(head->bb) | checkWalls(neck->bb) | checkWalls(body->bb);
773 }
774}
775
776void EnderDragon::checkCrystals()
777{
778 if (nearestCrystal != NULL)
779 {
780 if (nearestCrystal->removed)
781 {
782 if (!level->isClientSide)
783 {
784 hurt(head, DamageSource::explosion(NULL), 10);
785 }
786
787 nearestCrystal = nullptr;
788 }
789 else if (tickCount % 10 == 0)
790 {
791 if (getHealth() < getMaxHealth()) setHealth(getHealth() + 1);
792 }
793 }
794
795 if (random->nextInt(10) == 0)
796 {
797 float maxDist = 32;
798 vector<shared_ptr<Entity> > *crystals = level->getEntitiesOfClass(typeid(EnderCrystal), bb->grow(maxDist, maxDist, maxDist));
799
800 shared_ptr<EnderCrystal> crystal = nullptr;
801 double nearest = Double::MAX_VALUE;
802 //for (Entity ec : crystals)
803 for(AUTO_VAR(it, crystals->begin()); it != crystals->end(); ++it)
804 {
805 shared_ptr<EnderCrystal> ec = dynamic_pointer_cast<EnderCrystal>( *it );
806 double dist = ec->distanceToSqr(shared_from_this() );
807 if (dist < nearest)
808 {
809 nearest = dist;
810 crystal = ec;
811 }
812 }
813 delete crystals;
814
815
816 nearestCrystal = crystal;
817 }
818}
819
820void EnderDragon::checkAttack()
821{
822 //if (tickCount % 20 == 0)
823 {
824 // Vec3 *v = getViewVector(1);
825 // double xdd = 0;
826 // double ydd = -1;
827 // double zdd = 0;
828
829 // double x = (body.bb.x0 + body.bb.x1) / 2;
830 // double y = (body.bb.y0 + body.bb.y1) / 2 - 2;
831 // double z = (body.bb.z0 + body.bb.z1) / 2;
832
833 }
834}
835
836void EnderDragon::knockBack(vector<shared_ptr<Entity> > *entities)
837{
838 double xm = (body->bb->x0 + body->bb->x1) / 2;
839 // double ym = (body.bb.y0 + body.bb.y1) / 2;
840 double zm = (body->bb->z0 + body->bb->z1) / 2;
841
842 //for (Entity e : entities)
843 for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it)
844 {
845
846 if ( (*it)->instanceof(eTYPE_LIVINGENTITY) )//(e instanceof Mob)
847 {
848 shared_ptr<LivingEntity> e = dynamic_pointer_cast<LivingEntity>( *it );
849 double xd = e->x - xm;
850 double zd = e->z - zm;
851 double dd = xd * xd + zd * zd;
852 e->push(xd / dd * 4, 0.2f, zd / dd * 4);
853 }
854 }
855}
856
857void EnderDragon::hurt(vector<shared_ptr<Entity> > *entities)
858{
859 //for (int i = 0; i < entities->size(); i++)
860 for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it)
861 {
862
863 if ( (*it)->instanceof(eTYPE_LIVINGENTITY) ) //(e instanceof Mob)
864 {
865 shared_ptr<LivingEntity> e = dynamic_pointer_cast<LivingEntity>( *it );//entities.get(i);
866 DamageSource *damageSource = DamageSource::mobAttack( dynamic_pointer_cast<LivingEntity>( shared_from_this() ));
867 e->hurt(damageSource, 10);
868 delete damageSource;
869 }
870 }
871}
872
873void EnderDragon::findNewTarget()
874{
875 shared_ptr<Player> playerNearestToEgg = nullptr;
876
877 // Update current action
878 switch(getSynchedAction())
879 {
880 case e_EnderdragonAction_Takeoff:
881 case e_EnderdragonAction_HoldingPattern:
882 {
883 if(!newTarget && m_currentPath != NULL && m_currentPath->isDone())
884 {
885 // Distance is 64, which is the radius of the circle
886 int eggHeight = max(level->seaLevel + 5, level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS)); //level->getHeightmap(4,4);
887 playerNearestToEgg = level->getNearestPlayer(PODIUM_X_POS, eggHeight, PODIUM_Z_POS, 64.0);
888 double dist = 64.0f;
889 if(playerNearestToEgg != NULL)
890 {
891 dist = playerNearestToEgg->distanceToSqr(PODIUM_X_POS, eggHeight, PODIUM_Z_POS);
892 dist /= (8*8*8);
893 }
894 //app.DebugPrintf("Adjusted dist is %f\n", dist);
895
896 if( random->nextInt(m_remainingCrystalsCount + 3) == 0 )
897 {
898 setSynchedAction(e_EnderdragonAction_LandingApproach);
899#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
900 app.DebugPrintf("Dragon action is now: LandingApproach\n");
901#endif
902 }
903 // More likely to strafe a player if they are close to the egg, or there are not many crystals remaining
904 else if( playerNearestToEgg != NULL && (random->nextInt( abs(dist) + 2 ) == 0 || random->nextInt( m_remainingCrystalsCount + 2 ) == 0) )
905 {
906 setSynchedAction(e_EnderdragonAction_StrafePlayer);
907#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
908 app.DebugPrintf("Dragon action is now: StrafePlayer\n");
909#endif
910 }
911 }
912 }
913 break;
914 case e_EnderdragonAction_StrafePlayer:
915 // Always return to the holding pattern after strafing
916 if(m_currentPath == NULL || (m_currentPath->isDone() && newTarget) )
917 {
918 setSynchedAction(e_EnderdragonAction_HoldingPattern);
919#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
920 app.DebugPrintf("Dragon action is now: HoldingPattern\n");
921#endif
922 }
923 break;
924 case e_EnderdragonAction_Landing:
925 // setSynchedAction(e_EnderdragonAction_Sitting_Flaming);
926 //#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
927 // app.DebugPrintf("Dragon action is now: SittingFlaming\n");
928 //#endif
929 // m_actionTicks = FLAME_TICKS;
930
931 m_flameAttacks = 0;
932 setSynchedAction(e_EnderdragonAction_Sitting_Scanning);
933 attackTarget = level->getNearestPlayer( shared_from_this(), SITTING_ATTACK_VIEW_RANGE, SITTING_ATTACK_Y_VIEW_RANGE );
934#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
935 app.DebugPrintf("Dragon action is now: SittingScanning\n");
936#endif
937 m_actionTicks = 0;
938 break;
939 };
940
941 newTarget = false;
942
943 //if (random->nextInt(2) == 0 && level->players.size() > 0)
944 if(getSynchedAction() == e_EnderdragonAction_StrafePlayer && playerNearestToEgg != NULL)
945 {
946 attackTarget = playerNearestToEgg;
947 strafeAttackTarget();
948 }
949 else if(getSynchedAction() == e_EnderdragonAction_LandingApproach)
950 {
951 // Generate a new path if we don't currently have one
952 if( m_currentPath == NULL || m_currentPath->isDone() )
953 {
954 int currentNodeIndex = findClosestNode();
955
956 // To get the angle to the player correct when landing, head to a node diametrically opposite the player, then swoop in to 4,4
957 int eggHeight = max( level->seaLevel + 5, level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS) ); //level->getHeightmap(4,4);
958 playerNearestToEgg = level->getNearestPlayer(PODIUM_X_POS, eggHeight, PODIUM_Z_POS, 128.0);
959
960 int targetNodeIndex = 0 ;
961 if(playerNearestToEgg != NULL)
962 {
963 Vec3 *aim = Vec3::newTemp(playerNearestToEgg->x, 0, playerNearestToEgg->z)->normalize();
964 //app.DebugPrintf("Final marker node near (%f,%d,%f)\n", -aim->x*40,105,-aim->z*40 );
965 targetNodeIndex = findClosestNode(-aim->x*40,105.0,-aim->z*40);
966 }
967 else
968 {
969 targetNodeIndex = findClosestNode(40.0, eggHeight, 0.0);
970 }
971 Node finalNode(PODIUM_X_POS, eggHeight, PODIUM_Z_POS);
972
973 if(m_currentPath != NULL) delete m_currentPath;
974 m_currentPath = findPath(currentNodeIndex,targetNodeIndex, &finalNode);
975
976 // Always skip the first node (as that's where we are already)
977 if(m_currentPath != NULL) m_currentPath->next();
978 }
979
980 m_actionTicks = 0;
981
982 navigateToNextPathNode();
983
984 if(m_currentPath != NULL && m_currentPath->isDone())
985 {
986 setSynchedAction(e_EnderdragonAction_Landing);
987#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
988 app.DebugPrintf("Dragon action is now: Landing\n");
989#endif
990 }
991 }
992 else if(getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ||
993 getSynchedAction() == e_EnderdragonAction_Sitting_Attacking ||
994 getSynchedAction() == e_EnderdragonAction_Sitting_Scanning)
995 {
996 // Does no movement
997 }
998 else
999 {
1000 // Default is e_EnderdragonAction_HoldingPattern
1001 // Generate a new path if we don't currently have one
1002 if(m_currentPath == NULL || m_currentPath->isDone() )
1003 {
1004 int currentNodeIndex = findClosestNode();
1005 int targetNodeIndex = currentNodeIndex;
1006 //if(random->nextInt(4) == 0) m_holdingPatternClockwise = !m_holdingPatternClockwise;
1007
1008 if( getSynchedAction() == e_EnderdragonAction_Takeoff)
1009 {
1010 Vec3 *v = getHeadLookVector(1);
1011 targetNodeIndex = findClosestNode(-v->x*40,105.0,-v->z*40);
1012 }
1013 else
1014 {
1015 if(random->nextInt(8) == 0)
1016 {
1017 m_holdingPatternClockwise = !m_holdingPatternClockwise;
1018 targetNodeIndex = targetNodeIndex + 6;
1019 }
1020
1021 if(m_holdingPatternClockwise) targetNodeIndex = targetNodeIndex + 1;
1022 else targetNodeIndex = targetNodeIndex - 1;
1023 }
1024
1025 if(m_remainingCrystalsCount <= 0)
1026 {
1027 // If no crystals left, navigate only between nodes 12-19
1028 targetNodeIndex -= 12;
1029 targetNodeIndex = targetNodeIndex&7; // 4J-RR - was %8, but that could create a result of -1 here when targetNodeIndex was 11
1030 targetNodeIndex += 12;
1031 }
1032 else
1033 {
1034 // If crystals are left, navigate only between nodes 0-11
1035 targetNodeIndex = targetNodeIndex%12;
1036 if(targetNodeIndex < 0) targetNodeIndex += 12;
1037 }
1038
1039 if(m_currentPath != NULL) delete m_currentPath;
1040 m_currentPath = findPath(currentNodeIndex,targetNodeIndex);
1041
1042 // Always skip the first node (as that's where we are already)
1043 if(m_currentPath != NULL) m_currentPath->next();
1044 }
1045
1046 navigateToNextPathNode();
1047
1048 if(getSynchedAction() != e_EnderdragonAction_StrafePlayer) attackTarget = nullptr;
1049 }
1050}
1051
1052float EnderDragon::rotWrap(double d)
1053{
1054 while (d >= 180)
1055 d -= 360;
1056 while (d < -180)
1057 d += 360;
1058 return (float) d;
1059}
1060
1061bool EnderDragon::checkWalls(AABB *bb)
1062{
1063 int x0 = Mth::floor(bb->x0);
1064 int y0 = Mth::floor(bb->y0);
1065 int z0 = Mth::floor(bb->z0);
1066 int x1 = Mth::floor(bb->x1);
1067 int y1 = Mth::floor(bb->y1);
1068 int z1 = Mth::floor(bb->z1);
1069 bool hitWall = false;
1070 bool destroyedTile = false;
1071 for (int x = x0; x <= x1; x++)
1072 {
1073 for (int y = y0; y <= y1; y++)
1074 {
1075 for (int z = z0; z <= z1; z++)
1076 {
1077 int t = level->getTile(x, y, z);
1078 // 4J Stu - Don't remove fire
1079 if (t == 0 || t == Tile::fire_Id)
1080 {
1081
1082 }
1083 else if (t == Tile::obsidian_Id || t == Tile::endStone_Id || t == Tile::unbreakable_Id || !level->getGameRules()->getBoolean(GameRules::RULE_MOBGRIEFING))
1084 {
1085 hitWall = true;
1086 }
1087 else
1088 {
1089 destroyedTile = level->removeTile(x, y, z) || destroyedTile;
1090 }
1091 }
1092 }
1093 }
1094
1095 if (destroyedTile)
1096 {
1097 double x = bb->x0 + (bb->x1 - bb->x0) * random->nextFloat();
1098 double y = bb->y0 + (bb->y1 - bb->y0) * random->nextFloat();
1099 double z = bb->z0 + (bb->z1 - bb->z0) * random->nextFloat();
1100 level->addParticle(eParticleType_largeexplode, x, y, z, 0, 0, 0);
1101 }
1102
1103 return hitWall;
1104}
1105
1106bool EnderDragon::hurt(shared_ptr<MultiEntityMobPart> MultiEntityMobPart, DamageSource *source, float damage)
1107{
1108 if (MultiEntityMobPart != head)
1109 {
1110 damage = damage / 4 + 1;
1111 }
1112
1113 //float rot1 = yRot * PI / 180;
1114 //float ss1 = sin(rot1);
1115 //float cc1 = cos(rot1);
1116
1117 //xTarget = x + ss1 * 5 + (random->nextFloat() - 0.5f) * 2;
1118 //yTarget = y + random->nextFloat() * 3 + 1;
1119 //zTarget = z - cc1 * 5 + (random->nextFloat() - 0.5f) * 2;
1120 //attackTarget = NULL;
1121
1122 if ( source->getEntity() != NULL && source->getEntity()->instanceof(eTYPE_PLAYER) || source->isExplosion() )
1123 {
1124 int healthBefore = getHealth();
1125 reallyHurt(source, damage);
1126
1127 //if(!level->isClientSide) app.DebugPrintf("Health is now %d\n", health);
1128 if( getHealth() <= 0 &&
1129 !( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ||
1130 getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ||
1131 getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) )
1132 {
1133 setHealth(1);
1134
1135 if( setSynchedAction(e_EnderdragonAction_LandingApproach) )
1136 {
1137 if( m_currentPath != NULL )
1138 {
1139 while(!m_currentPath->isDone())
1140 {
1141 m_currentPath->next();
1142 }
1143 }
1144 app.DebugPrintf("Dragon should be dead, so landing.\n");
1145#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
1146 app.DebugPrintf("Dragon action is now: LandingApproach\n");
1147#endif
1148 findNewTarget();
1149 }
1150 }
1151
1152 if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ||
1153 getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ||
1154 getSynchedAction() == e_EnderdragonAction_Sitting_Attacking)
1155 {
1156 m_sittingDamageReceived += healthBefore - getHealth();
1157
1158 if(m_sittingDamageReceived > (SITTING_ALLOWED_DAMAGE_PERCENTAGE*getMaxHealth() ) )
1159 {
1160 m_sittingDamageReceived = 0;
1161 setSynchedAction(e_EnderdragonAction_Takeoff);
1162 newTarget = true;
1163#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
1164 app.DebugPrintf("Dragon action is now: Takeoff\n");
1165#endif
1166 }
1167 }
1168 }
1169 return true;
1170}
1171
1172bool EnderDragon::hurt(DamageSource *source, float damage)
1173{
1174 return false;
1175}
1176
1177bool EnderDragon::reallyHurt(DamageSource *source, float damage)
1178{
1179 return Mob::hurt(source, damage);
1180}
1181
1182void EnderDragon::tickDeath()
1183{
1184 if( getSynchedAction() != e_EnderdragonAction_Sitting_Flaming &&
1185 getSynchedAction() != e_EnderdragonAction_Sitting_Scanning &&
1186 getSynchedAction() != e_EnderdragonAction_Sitting_Attacking)
1187 {
1188 if(!level->isClientSide) setHealth(1);
1189 return;
1190 }
1191
1192 dragonDeathTime++;
1193 if (dragonDeathTime >= 180 && dragonDeathTime <= 200)
1194 {
1195 float xo = (random->nextFloat() - 0.5f) * 8;
1196 float yo = (random->nextFloat() - 0.5f) * 4;
1197 float zo = (random->nextFloat() - 0.5f) * 8;
1198 level->addParticle(eParticleType_hugeexplosion, x + xo, y + 2 + yo, z + zo, 0, 0, 0);
1199 }
1200 if (!level->isClientSide)
1201 {
1202 if (dragonDeathTime > 150 && dragonDeathTime % 5 == 0)
1203 {
1204 int xpCount = 1000;
1205 while (xpCount > 0)
1206 {
1207 int newCount = ExperienceOrb::getExperienceValue(xpCount);
1208 xpCount -= newCount;
1209 level->addEntity(shared_ptr<ExperienceOrb>( new ExperienceOrb(level, x, y, z, newCount) ));
1210 }
1211 }
1212 if (dragonDeathTime == 1)
1213 {
1214 level->globalLevelEvent(LevelEvent::SOUND_DRAGON_DEATH, (int) x, (int) y, (int) z, 0);
1215 }
1216 }
1217 move(0, 0.1f, 0);
1218 yBodyRot = yRot += 20.0f;
1219
1220 if (dragonDeathTime == 200 && !level->isClientSide)
1221 {
1222 //level->levelEvent(NULL, LevelEvent::ENDERDRAGON_KILLED, (int) x, (int) y, (int) z, 0);
1223
1224 int xpCount = 2000;
1225 while (xpCount > 0)
1226 {
1227 int newCount = ExperienceOrb::getExperienceValue(xpCount);
1228 xpCount -= newCount;
1229 level->addEntity(shared_ptr<ExperienceOrb>( new ExperienceOrb(level, x, y, z, newCount)));
1230 }
1231 int xo = 5 + random->nextInt(2) * 2 - 1;
1232 int zo = 5 + random->nextInt(2) * 2 - 1;
1233 if (random->nextInt(2) == 0)
1234 {
1235 xo = 0;
1236 }
1237 else
1238 {
1239 zo = 0;
1240 }
1241 // 4J-PB changed to center this between the pillars
1242 spawnExitPortal(0,0);//Mth::floor(x), Mth::floor(z));
1243 remove();
1244 }
1245}
1246
1247void EnderDragon::spawnExitPortal(int x, int z)
1248{
1249 int y = level->seaLevel;
1250
1251 TheEndPortal::allowAnywhere(true);
1252
1253 int r = 4;
1254 for (int yy = y - 1; yy <= y + 32; yy++)
1255 {
1256 for (int xx = x - r; xx <= x + r; xx++)
1257 {
1258 for (int zz = z - r; zz <= z + r; zz++)
1259 {
1260 double xd = xx - x;
1261 double zd = zz - z;
1262 double d = sqrt(xd * xd + zd * zd);
1263 if (d <= r - 0.5)
1264 {
1265 if (yy < y)
1266 {
1267 if (d > r - 1 - 0.5)
1268 {
1269 }
1270 else
1271 {
1272 level->setTileAndUpdate(xx, yy, zz, Tile::unbreakable_Id);
1273 }
1274 }
1275 else if (yy > y)
1276 {
1277 level->setTileAndUpdate(xx, yy, zz, 0);
1278 }
1279 else
1280 {
1281 if (d > r - 1 - 0.5)
1282 {
1283 level->setTileAndUpdate(xx, yy, zz, Tile::unbreakable_Id);
1284 }
1285 else
1286 {
1287 level->setTileAndUpdate(xx, yy, zz, Tile::endPortalTile_Id);
1288 }
1289 }
1290 }
1291 }
1292 }
1293 }
1294
1295 level->setTileAndUpdate(x, y + 0, z, Tile::unbreakable_Id);
1296 level->setTileAndUpdate(x, y + 1, z, Tile::unbreakable_Id);
1297 level->setTileAndUpdate(x, y + 2, z, Tile::unbreakable_Id);
1298 level->setTileAndUpdate(x - 1, y + 2, z, Tile::torch_Id);
1299 level->setTileAndUpdate(x + 1, y + 2, z, Tile::torch_Id);
1300 level->setTileAndUpdate(x, y + 2, z - 1, Tile::torch_Id);
1301 level->setTileAndUpdate(x, y + 2, z + 1, Tile::torch_Id);
1302 level->setTileAndUpdate(x, y + 3, z, Tile::unbreakable_Id);
1303 level->setTileAndUpdate(x, y + 4, z, Tile::dragonEgg_Id);
1304
1305 // 4J-PB - The podium can be floating with nothing under it, so put some whiteStone under it if this is the case
1306 for (int yy = y - 5; yy < y - 1; yy++)
1307 {
1308 for (int xx = x - (r - 1); xx <= x + (r - 1); xx++)
1309 {
1310 for (int zz = z - (r - 1); zz <= z + (r - 1); zz++)
1311 {
1312 if(level->isEmptyTile(xx,yy,zz))
1313 {
1314 level->setTileAndUpdate(xx, yy, zz, Tile::endStone_Id);
1315 }
1316 }
1317 }
1318 }
1319
1320 TheEndPortal::allowAnywhere(false);
1321}
1322
1323void EnderDragon::checkDespawn()
1324{
1325}
1326
1327vector<shared_ptr<Entity> > *EnderDragon::getSubEntities()
1328{
1329 return &subEntities;
1330}
1331
1332bool EnderDragon::isPickable()
1333{
1334 return false;
1335}
1336
1337Level *EnderDragon::getLevel()
1338{
1339 return level;
1340}
1341
1342int EnderDragon::getAmbientSound()
1343{
1344 return eSoundType_MOB_ENDERDRAGON_GROWL; //"mob.enderdragon.growl";
1345}
1346
1347int EnderDragon::getHurtSound()
1348{
1349 return eSoundType_MOB_ENDERDRAGON_HIT; //"mob.enderdragon.hit";
1350}
1351
1352float EnderDragon::getSoundVolume()
1353{
1354 return 5;
1355}
1356
1357// 4J Added for new dragon behaviour
1358bool EnderDragon::setSynchedAction(EEnderdragonAction action, bool force /*= false*/)
1359{
1360 bool validTransition = false;
1361 // Check if this is a valid state transition
1362 switch(getSynchedAction())
1363 {
1364 case e_EnderdragonAction_HoldingPattern:
1365 switch(action)
1366 {
1367 case e_EnderdragonAction_StrafePlayer:
1368 case e_EnderdragonAction_LandingApproach:
1369 validTransition = true;
1370 break;
1371 };
1372 break;
1373 case e_EnderdragonAction_StrafePlayer:
1374 switch(action)
1375 {
1376 case e_EnderdragonAction_HoldingPattern:
1377 case e_EnderdragonAction_LandingApproach:
1378 validTransition = true;
1379 break;
1380 };
1381 break;
1382 case e_EnderdragonAction_LandingApproach:
1383 switch(action)
1384 {
1385 case e_EnderdragonAction_Landing:
1386 validTransition = true;
1387 break;
1388 };
1389 break;
1390 case e_EnderdragonAction_Landing:
1391 switch(action)
1392 {
1393 case e_EnderdragonAction_Sitting_Flaming:
1394 case e_EnderdragonAction_Sitting_Scanning:
1395 validTransition = true;
1396 break;
1397 };
1398 break;
1399 case e_EnderdragonAction_Takeoff:
1400 switch(action)
1401 {
1402 case e_EnderdragonAction_HoldingPattern:
1403 validTransition = true;
1404 break;
1405 };
1406 break;
1407 case e_EnderdragonAction_Sitting_Flaming:
1408 switch(action)
1409 {
1410 case e_EnderdragonAction_Sitting_Scanning:
1411 case e_EnderdragonAction_Sitting_Attacking:
1412 case e_EnderdragonAction_Takeoff:
1413 validTransition = true;
1414 break;
1415 };
1416 break;
1417 case e_EnderdragonAction_Sitting_Scanning:
1418 switch(action)
1419 {
1420 case e_EnderdragonAction_Sitting_Flaming:
1421 case e_EnderdragonAction_Sitting_Attacking:
1422 case e_EnderdragonAction_Takeoff:
1423 validTransition = true;
1424 break;
1425 };
1426 break;
1427 case e_EnderdragonAction_Sitting_Attacking:
1428 switch(action)
1429 {
1430 case e_EnderdragonAction_Sitting_Flaming:
1431 case e_EnderdragonAction_Sitting_Scanning:
1432 case e_EnderdragonAction_Takeoff:
1433 validTransition = true;
1434 break;
1435 };
1436 break;
1437 };
1438
1439 if( force || validTransition )
1440 {
1441 entityData->set(DATA_ID_SYNCHED_ACTION, action);
1442 }
1443 else
1444 {
1445 app.DebugPrintf("EnderDragon: Invalid state transition from %d to %d\n", getSynchedAction(), action);
1446 }
1447
1448 return force || validTransition;
1449}
1450
1451EnderDragon::EEnderdragonAction EnderDragon::getSynchedAction()
1452{
1453 return (EEnderdragonAction)entityData->getInteger(DATA_ID_SYNCHED_ACTION);
1454}
1455
1456void EnderDragon::handleCrystalDestroyed(DamageSource *source)
1457{
1458 AABB *tempBB = AABB::newTemp(PODIUM_X_POS,84.0,PODIUM_Z_POS,PODIUM_X_POS+1.0,85.0,PODIUM_Z_POS+1.0);
1459 vector<shared_ptr<Entity> > *crystals = level->getEntitiesOfClass(typeid(EnderCrystal), tempBB->grow(48, 40, 48));
1460 m_remainingCrystalsCount = (int)crystals->size() - 1;
1461 if(m_remainingCrystalsCount < 0) m_remainingCrystalsCount = 0;
1462 delete crystals;
1463
1464 app.DebugPrintf("Crystal count is now %d\n",m_remainingCrystalsCount);
1465
1466 //--m_remainingCrystalsCount;
1467
1468 if(m_remainingCrystalsCount%2 == 0)
1469 {
1470 if(setSynchedAction(e_EnderdragonAction_LandingApproach))
1471 {
1472 if( m_currentPath != NULL )
1473 {
1474 while(!m_currentPath->isDone())
1475 {
1476 m_currentPath->next();
1477 }
1478 }
1479 m_actionTicks = 1;
1480#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
1481 app.DebugPrintf("Dragon action is now: LandingApproach\n");
1482#endif
1483 }
1484 }
1485 else if(source->getEntity() != NULL && source->getEntity()->instanceof(eTYPE_PLAYER))
1486 {
1487 if(setSynchedAction(e_EnderdragonAction_StrafePlayer))
1488 {
1489 attackTarget = dynamic_pointer_cast<Player>(source->getEntity());
1490#if PRINT_DRAGON_STATE_CHANGE_MESSAGES
1491 app.DebugPrintf("Dragon action is now: StrafePlayer\n");
1492#endif
1493 strafeAttackTarget();
1494 }
1495 }
1496}
1497
1498void EnderDragon::strafeAttackTarget()
1499{
1500 app.DebugPrintf("Setting path to strafe attack target\n");
1501 int currentNodeIndex = findClosestNode();
1502 int targetNodeIndex = findClosestNode(attackTarget->x,attackTarget->y,attackTarget->z);
1503
1504 int finalXTarget = attackTarget->x;
1505 int finalZTarget = attackTarget->z;
1506
1507 double xd = finalXTarget - x;
1508 double zd = finalZTarget - z;
1509 double sd = sqrt(xd * xd + zd * zd);
1510 double ho = 0.4f + sd / 80.0f - 1;
1511 if (ho > 10) ho = 10;
1512 int finalYTarget = attackTarget->bb->y0 + ho;
1513
1514 Node finalNode(finalXTarget, finalYTarget, finalZTarget);
1515
1516 if(m_currentPath != NULL) delete m_currentPath;
1517 m_currentPath = findPath(currentNodeIndex,targetNodeIndex, &finalNode);
1518
1519 if(m_currentPath != NULL)
1520 {
1521 // Always skip the first node (as that's where we are already)
1522 m_currentPath->next();
1523
1524 navigateToNextPathNode();
1525 }
1526}
1527
1528void EnderDragon::navigateToNextPathNode()
1529{
1530 if(m_currentPath != NULL && !m_currentPath->isDone())
1531 {
1532 Vec3 *curr = m_currentPath->currentPos();
1533
1534 m_currentPath->next();
1535 xTarget = curr->x;
1536
1537 if(getSynchedAction() == e_EnderdragonAction_LandingApproach && m_currentPath->isDone())
1538 {
1539 // When heading to the last node on the landing approach, we want the yCoord to be exact
1540 yTarget = curr->y;
1541 }
1542 else
1543 {
1544 do
1545 {
1546 yTarget = curr->y + random->nextFloat() * 20;
1547 } while( yTarget < (curr->y) );
1548 }
1549 zTarget = curr->z;
1550 app.DebugPrintf("Path node pos is (%f,%f,%f)\n",curr->x,curr->y,curr->z);
1551 app.DebugPrintf("Setting new target to (%f,%f,%f)\n",xTarget, yTarget, zTarget);
1552 }
1553}
1554
1555int EnderDragon::findClosestNode()
1556{
1557 // Setup all the nodes on the first time this is called
1558 if(m_nodes->data[0] == NULL)
1559 {
1560 // Path nodes for navigation
1561 // 0 - 11 are the outer ring at 60 blocks from centre
1562 // 12 - 19 are the middle ring at 40 blocks from centre
1563 // 20 - 23 are the inner ring at 20 blocks from centre
1564 int nodeX=0;
1565 int nodeY=0;
1566 int nodeZ=0;
1567 int multiplier = 0;
1568 for(unsigned int i = 0; i < 24; ++i)
1569 {
1570 int yAdjustment = 5;
1571 multiplier = i;
1572 if(i < 12)
1573 {
1574 nodeX=60 * Mth::cos(2*(-PI+(PI/12)*multiplier));
1575 nodeZ=60 * Mth::sin(2*(-PI+(PI/12)*multiplier));
1576 }
1577 else if(i < 20)
1578 {
1579 multiplier -= 12;
1580 nodeX=40 * Mth::cos(2*(-PI+(PI/8)*multiplier));
1581 nodeZ=40 * Mth::sin(2*(-PI+(PI/8)*multiplier));
1582 yAdjustment += 10; // Make the target well above the top of the towers
1583 }
1584 else
1585 {
1586 multiplier -= 20;
1587 nodeX=20 * Mth::cos(2*(-PI+(PI/4)*multiplier));
1588 nodeZ=20 * Mth::sin(2*(-PI+(PI/4)*multiplier));
1589 }
1590 // Fix for #77202 - TU9: Content: Gameplay: The Ender Dragon sometimes flies through terrain
1591 // Add minimum height
1592 nodeY = max( (level->seaLevel + 10), level->getTopSolidBlock(nodeX, nodeZ) + yAdjustment );
1593
1594 app.DebugPrintf("Node %d is at (%d,%d,%d)\n", i, nodeX, nodeY, nodeZ);
1595
1596 m_nodes->data[i] = new Node(nodeX,nodeY,nodeZ);
1597
1598 //level->setTile(nodeX,nodeY,nodeZ,Tile::obsidian_Id);
1599 }
1600
1601 m_nodeAdjacency[0] = (1<<11) | (1<<1) | (1<<12);
1602 m_nodeAdjacency[1] = (1<<0) | (1<<2) | (1<<13);
1603 m_nodeAdjacency[2] = (1<<1) | (1<<3) | (1<<13);
1604 m_nodeAdjacency[3] = (1<<2) | (1<<4) | (1<<14);
1605 m_nodeAdjacency[4] = (1<<3) | (1<<5) | (1<<15);
1606 m_nodeAdjacency[5] = (1<<4) | (1<<6) | (1<<15);
1607 m_nodeAdjacency[6] = (1<<5) | (1<<7) | (1<<16);
1608 m_nodeAdjacency[7] = (1<<6) | (1<<8) | (1<<17);
1609 m_nodeAdjacency[8] = (1<<7) | (1<<9) | (1<<17);
1610 m_nodeAdjacency[9] = (1<<8) | (1<<10) | (1<<18);
1611 m_nodeAdjacency[10] = (1<<9) | (1<<11) | (1<<19);
1612 m_nodeAdjacency[11] = (1<<10) | (1<<0) | (1<<19);
1613
1614 m_nodeAdjacency[12] = (1<<0) | (1<<13) | (1<<20) | (1<<19);
1615 m_nodeAdjacency[13] = (1<<1) | (1<<2) | (1<<14) | (1<<21) | (1<<20) | (1<<12);
1616 m_nodeAdjacency[14] = (1<<3) | (1<<15) | (1<<21) | (1<<13);
1617 m_nodeAdjacency[15] = (1<<4) | (1<<5) | (1<<16) | (1<<22) | (1<<21) | (1<<14);
1618 m_nodeAdjacency[16] = (1<<6) | (1<<17) | (1<<22) | (1<<15);
1619 m_nodeAdjacency[17] = (1<<7) | (1<<8) | (1<<18) | (1<<23) | (1<<22) | (1<<16);
1620 m_nodeAdjacency[18] = (1<<9) | (1<<19) | (1<<23) | (1<<17);
1621 m_nodeAdjacency[19] = (1<<10) | (1<<11) | (1<<12) | (1<<20) | (1<<23) | (1<<18);
1622
1623 m_nodeAdjacency[20] = (1<<12) | (1<<13) | (1<<21) | (1<<22) | (1<<23) | (1<<19);
1624 m_nodeAdjacency[21] = (1<<14) | (1<<15) | (1<<22) | (1<<23) | (1<<20) | (1<<13);
1625 m_nodeAdjacency[22] = (1<<15) | (1<<16) | (1<<17) | (1<<23) | (1<<20) | (1<<21);
1626 m_nodeAdjacency[23] = (1<<17) | (1<<18) | (1<<19) | (1<<20) | (1<<21) | (1<<22);
1627 }
1628
1629 return findClosestNode(x,y,z);
1630}
1631
1632int EnderDragon::findClosestNode(double tX, double tY, double tZ)
1633{
1634 float closestDist = 100.0f;
1635 int closestIndex = 0;
1636 Node *currentPos = new Node((int) floor(tX), (int) floor(tY), (int) floor(tZ));
1637 int startIndex = 0;
1638 if(m_remainingCrystalsCount <= 0)
1639 {
1640 // If not crystals are left then we try and stay in the middle ring and avoid the outer ring
1641 startIndex = 12;
1642 }
1643 for(unsigned int i = startIndex; i < 24; ++i)
1644 {
1645 if( m_nodes->data[i] != NULL )
1646 {
1647 float dist = m_nodes->data[i]->distanceTo(currentPos);
1648 if(dist < closestDist)
1649 {
1650 closestDist = dist;
1651 closestIndex = i;
1652 }
1653 }
1654 }
1655 delete currentPos;
1656 return closestIndex;
1657}
1658
1659// 4J Stu - A* taken from PathFinder and modified
1660Path *EnderDragon::findPath(int startIndex, int endIndex, Node *finalNode /* = NULL */)
1661{
1662 for(unsigned int i = 0; i < 24; ++i)
1663 {
1664 Node *n = m_nodes->data[i];
1665 n->closed = false;
1666 n->f = 0;
1667 n->g = 0;
1668 n->h = 0;
1669 n->cameFrom = NULL;
1670 n->heapIdx = -1;
1671 }
1672
1673 Node *from = m_nodes->data[startIndex];
1674 Node *to = m_nodes->data[endIndex];
1675
1676 from->g = 0;
1677 from->h = from->distanceTo(to);
1678 from->f = from->h;
1679
1680 openSet->clear();
1681 openSet->insert(from);
1682
1683 Node *closest = from;
1684
1685 int minimumNodeIndex = 0;
1686 if(m_remainingCrystalsCount <= 0)
1687 {
1688 // If not crystals are left then we try and stay in the middle ring and avoid the outer ring
1689 minimumNodeIndex = 12;
1690 }
1691
1692 while (!openSet->isEmpty())
1693 {
1694 Node *x = openSet->pop();
1695
1696 if (x->equals(to))
1697 {
1698 app.DebugPrintf("Found path from %d to %d\n", startIndex, endIndex);
1699 if(finalNode != NULL)
1700 {
1701 finalNode->cameFrom = to;
1702 to = finalNode;
1703 }
1704 return reconstruct_path(from, to);
1705 }
1706
1707 if (x->distanceTo(to) < closest->distanceTo(to))
1708 {
1709 closest = x;
1710 }
1711 x->closed = true;
1712
1713 unsigned int xIndex = 0;
1714 for(unsigned int i = 0; i < 24; ++i)
1715 {
1716 if(m_nodes->data[i] == x)
1717 {
1718 xIndex = i;
1719 break;
1720 }
1721 }
1722
1723 for (int i = minimumNodeIndex; i < 24; i++)
1724 {
1725 if(m_nodeAdjacency[xIndex] & (1<<i))
1726 {
1727 Node *y = m_nodes->data[i];
1728
1729 if(y->closed) continue;
1730
1731 float tentative_g_score = x->g + x->distanceTo(y);
1732 if (!y->inOpenSet() || tentative_g_score < y->g)
1733 {
1734 y->cameFrom = x;
1735 y->g = tentative_g_score;
1736 y->h = y->distanceTo(to);
1737 if (y->inOpenSet())
1738 {
1739 openSet->changeCost(y, y->g + y->h);
1740 }
1741 else
1742 {
1743 y->f = y->g + y->h;
1744 openSet->insert(y);
1745 }
1746 }
1747 }
1748 }
1749 }
1750
1751 if (closest == from) return NULL;
1752 app.DebugPrintf("Failed to find path from %d to %d\n", startIndex, endIndex);
1753 if(finalNode != NULL)
1754 {
1755 finalNode->cameFrom = closest;
1756 closest = finalNode;
1757 }
1758 return reconstruct_path(from, closest);
1759}
1760
1761// function reconstruct_path(came_from,current_node)
1762Path *EnderDragon::reconstruct_path(Node *from, Node *to)
1763{
1764 int count = 1;
1765 Node *n = to;
1766 while (n->cameFrom != NULL)
1767 {
1768 count++;
1769 n = n->cameFrom;
1770 }
1771
1772 NodeArray nodes = NodeArray(count);
1773 n = to;
1774 nodes.data[--count] = n;
1775 while (n->cameFrom != NULL)
1776 {
1777 n = n->cameFrom;
1778 nodes.data[--count] = n;
1779 }
1780 Path *ret = new Path(nodes);
1781 delete [] nodes.data;
1782 return ret;
1783}
1784
1785void EnderDragon::addAdditonalSaveData(CompoundTag *entityTag)
1786{
1787 app.DebugPrintf("Adding EnderDragon additional save data\n");
1788 entityTag->putShort(L"RemainingCrystals", m_remainingCrystalsCount);
1789 entityTag->putInt(L"DragonState", (int)getSynchedAction() );
1790
1791 Mob::addAdditonalSaveData(entityTag);
1792}
1793
1794void EnderDragon::readAdditionalSaveData(CompoundTag *tag)
1795{
1796 app.DebugPrintf("Reading EnderDragon additional save data\n");
1797 m_remainingCrystalsCount = tag->getShort(L"RemainingCrystals");
1798 if(!tag->contains(L"RemainingCrystals")) m_remainingCrystalsCount = CRYSTAL_COUNT;
1799
1800 if(tag->contains(L"DragonState")) setSynchedAction( (EEnderdragonAction)tag->getInt(L"DragonState"), true);
1801
1802 Mob::readAdditionalSaveData(tag);
1803}
1804
1805float EnderDragon::getTilt(float a)
1806{
1807 float tilt = 0.0f;
1808 //if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ||
1809 // getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ||
1810 // getSynchedAction() == e_EnderdragonAction_Sitting_Attacking)
1811 //{
1812 // tilt = -25.0f;
1813 // xRot = -25.0f;
1814 //}
1815 //else
1816 {
1817 double latencyPosAcomponents[3],latencyPosBcomponents[3];
1818 doubleArray latencyPosA = doubleArray(latencyPosAcomponents,3);
1819 doubleArray latencyPosB = doubleArray(latencyPosBcomponents,3);
1820 getLatencyPos(latencyPosA, 5, a);
1821 getLatencyPos(latencyPosB, 10, a);
1822
1823 tilt = (latencyPosA[1] - latencyPosB[1]) * 10;
1824 }
1825 //app.DebugPrintf("Tilt is %f\n", tilt);
1826
1827 return tilt;
1828}
1829
1830double EnderDragon::getHeadYOffset(float a)
1831{
1832 double headYOffset = 0.0;
1833 if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ||
1834 getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ||
1835 getSynchedAction() == e_EnderdragonAction_Sitting_Attacking)
1836 {
1837 headYOffset = -1.0;
1838 }
1839 else
1840 {
1841 double p1components[3];
1842 doubleArray p1 = doubleArray(p1components, 3);
1843 getLatencyPos(p1, 5, 1);
1844
1845 double p0components[3];
1846 doubleArray p0 = doubleArray(p0components, 3);
1847 getLatencyPos(p0, 0, 1);
1848
1849 headYOffset = (p0[1] - p1[1]) * 1;
1850 }
1851 //app.DebugPrintf("headYOffset is %f\n", headYOffset);
1852 return headYOffset;
1853}
1854
1855double EnderDragon::getHeadYRotDiff(float a)
1856{
1857 double result = 0.0;
1858 //if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ||
1859 // getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ||
1860 // getSynchedAction() == e_EnderdragonAction_Sitting_Attacking)
1861 //{
1862 // result = m_headYRot;
1863 //}
1864 return result;
1865}
1866
1867double EnderDragon::getHeadPartYOffset(int partIndex, doubleArray bodyPos, doubleArray partPos)
1868{
1869 double result = 0.0;
1870 if( getSynchedAction() == e_EnderdragonAction_Landing || getSynchedAction() == e_EnderdragonAction_Takeoff )
1871 {
1872 int eggHeight = level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS); //level->getHeightmap(4,4);
1873 float dist = sqrt( distanceToSqr(PODIUM_X_POS, eggHeight, PODIUM_Z_POS) )/4;
1874 if( dist < 1.0f ) dist = 1.0f;
1875 result = partIndex / dist;
1876 //app.DebugPrintf("getHeadPartYOffset - dist = %f, result = %f (%d)\n", dist, result, partIndex);
1877 }
1878 else if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ||
1879 getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ||
1880 getSynchedAction() == e_EnderdragonAction_Sitting_Attacking)
1881 {
1882 result = partIndex;
1883 }
1884 else
1885 {
1886 if(partIndex == 6)
1887 {
1888 result = 0.0;
1889 }
1890 else
1891 {
1892 result = partPos[1] - bodyPos[1];
1893 }
1894 }
1895 //app.DebugPrintf("Part %d is at %f\n", partIndex, result);
1896 return result;
1897}
1898
1899double EnderDragon::getHeadPartYRotDiff(int partIndex, doubleArray bodyPos, doubleArray partPos)
1900{
1901 double result = 0.0;
1902 //if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ||
1903 // getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ||
1904 // getSynchedAction() == e_EnderdragonAction_Sitting_Attacking)
1905 //{
1906 // result = m_headYRot / (7 - partIndex);
1907 //}
1908 //else
1909 {
1910 result = partPos[0] - bodyPos[0];
1911 }
1912 //app.DebugPrintf("Part %d is at %f\n", partIndex, result);
1913 return result;
1914}
1915
1916Vec3 *EnderDragon::getHeadLookVector(float a)
1917{
1918 Vec3 *result = NULL;
1919
1920 if( getSynchedAction() == e_EnderdragonAction_Landing || getSynchedAction() == e_EnderdragonAction_Takeoff )
1921 {
1922 int eggHeight = level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS); //level->getHeightmap(4,4);
1923 float dist = sqrt(distanceToSqr(PODIUM_X_POS, eggHeight, PODIUM_Z_POS))/4;
1924 if( dist < 1.0f ) dist = 1.0f;
1925 // The 6.0f is dragon->getHeadPartYOffset(6, start, p)
1926 float yOffset = 6.0f / dist;
1927
1928 double xRotTemp = xRot;
1929 double rotScale = 1.5f;
1930 xRot = -yOffset * rotScale * 5.0f;
1931
1932 double yRotTemp = yRot;
1933 yRot += getHeadYRotDiff(a);
1934
1935 result = getViewVector(a);
1936
1937 xRot = xRotTemp;
1938 yRot = yRotTemp;
1939 }
1940 else if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ||
1941 getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ||
1942 getSynchedAction() == e_EnderdragonAction_Sitting_Attacking)
1943 {
1944 double xRotTemp = xRot;
1945 double rotScale = 1.5f;
1946 // The 6.0f is dragon->getHeadPartYOffset(6, start, p)
1947 xRot = -6.0f * rotScale * 5.0f;
1948
1949 double yRotTemp = yRot;
1950 yRot += getHeadYRotDiff(a);
1951
1952 result = getViewVector(a);
1953
1954 xRot = xRotTemp;
1955 yRot = yRotTemp;
1956 }
1957 else
1958 {
1959 result = getViewVector(a);
1960 }
1961 return result;
1962}