the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 1962 lines 58 kB view raw
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}