Something went wrong. Try again.
the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
Something went wrong. Try again.
58 kB · 1962 lines
C++
at master
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963#include "stdafx.h"#include "net.minecraft.world.level.h"#include "net.minecraft.world.level.tile.h"#include "net.minecraft.world.entity.h"#include "net.minecraft.world.entity.ai.attributes.h"#include "net.minecraft.world.entity.boss.h"#include "net.minecraft.world.entity.monster.h"#include "net.minecraft.world.entity.projectile.h"#include "net.minecraft.world.phys.h"#include "net.minecraft.world.damagesource.h"#include "BasicTypeContainers.h"#include "..\Minecraft.Client\Textures.h"#include "net.minecraft.world.entity.boss.enderdragon.h"#include "net.minecraft.world.level.pathfinder.h"#include "SharedConstants.h"#include "EnderDragon.h"
#define PRINT_DRAGON_STATE_CHANGE_MESSAGES 1
// 4J Added for new dragon behaviourconst int EnderDragon::CRYSTAL_COUNT = 8;const int EnderDragon::FLAME_TICKS = 60;const float EnderDragon::FLAME_ANGLE = 22.5f;const int EnderDragon::FLAME_PASSES = 4; // How many times it covers FLAME_ANGLE in FLAME_TICKSconst int EnderDragon::FLAME_FREQUENCY = 2; // Every FLAME_FREQUENCY ticks it sets fire to blocks while doing a flame passconst int EnderDragon::FLAME_RANGE = 10;
const int EnderDragon::ATTACK_TICKS = SharedConstants::TICKS_PER_SECOND * 2; // Time for the dragon roar to play
const int EnderDragon::SITTING_ATTACK_Y_VIEW_RANGE = 10; // The player must be now lower and no higher than the dragon by this amountconst int EnderDragon::SITTING_ATTACK_VIEW_RANGE = EnderDragon::FLAME_RANGE * 2;const int EnderDragon::SITTING_ATTACK_RANGE = EnderDragon::FLAME_RANGE * 2;const int EnderDragon::SITTING_POST_ATTACK_IDLE_TICKS = 40;const int EnderDragon::SITTING_SCANNING_IDLE_TICKS = 100;const int EnderDragon::SITTING_FLAME_ATTACKS_COUNT = 4; // How many times the dragons does the scan/roar/flame cycle before flying off
// The percentage of max health that the dragon will take while in the "Sitting" states before flying awayconst float EnderDragon::SITTING_ALLOWED_DAMAGE_PERCENTAGE = 0.25f;
void EnderDragon::_init(){ // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that // the derived version of the function is called this->defineSynchedData(); registerAttributes(); setHealth(getMaxHealth());
xTarget = yTarget = zTarget = 0.0; posPointer = -1; oFlapTime = 0; flapTime = 0; newTarget = false; inWall = false; attackTarget = nullptr; dragonDeathTime = 0; nearestCrystal = nullptr;
// 4J Stu - Added for new dragon behaviour m_remainingCrystalsCount = CRYSTAL_COUNT; m_fireballCharge = 0; m_holdingPatternAngle = 0.0f; m_holdingPatternClockwise = true; setSynchedAction(e_EnderdragonAction_HoldingPattern); m_actionTicks = 0; m_sittingDamageReceived = 0; m_headYRot = 0.0; m_acidArea = AABB::newPermanent(-4,-10,-3,6,3,3); m_flameAttacks = 0;
for (int i = 0; i < positionsLength; i++) { positions[i][0] = 0; positions[i][1] = 0; positions[i][2] = 0; }
m_nodes = new NodeArray(24); openSet = new BinaryHeap(); m_currentPath = NULL;}
EnderDragon::EnderDragon(Level *level) : Mob(level){ _init();
setSize(16, 8);
noPhysics = true; fireImmune = true;
yTarget = 100;
m_iGrowlTimer=100;
noCulling = true;}
// 4J - split off from ctor so we can use shared_from_this()void EnderDragon::AddParts(){ head = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"head", 6, 6) ); neck = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"neck", 6, 6) ); // 4J Added body = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"body", 8, 8) ); tail1 = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"tail", 4, 4) ); tail2 = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"tail", 4, 4) ); tail3 = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"tail", 4, 4) ); wing1 = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"wing", 4, 4) ); wing2 = shared_ptr<MultiEntityMobPart>( new MultiEntityMobPart(dynamic_pointer_cast<MultiEntityMob>(shared_from_this()), L"wing", 4, 4) );
subEntities.push_back(head); subEntities.push_back(neck); // 4J Added subEntities.push_back(body); subEntities.push_back(tail1); subEntities.push_back(tail2); subEntities.push_back(tail3); subEntities.push_back(wing1); subEntities.push_back(wing2);}
EnderDragon::~EnderDragon(){ if(m_nodes->data != NULL) { for(unsigned int i = 0; i < m_nodes->length; ++i) { if(m_nodes->data[i]!=NULL) delete m_nodes->data[i]; } delete [] m_nodes->data; } delete openSet; if( m_currentPath != NULL ) delete m_currentPath;}
void EnderDragon::registerAttributes(){ Mob::registerAttributes();
getAttribute(SharedMonsterAttributes::MAX_HEALTH)->setBaseValue(200);}
void EnderDragon::defineSynchedData(){ Mob::defineSynchedData();
// 4J Added for new dragon behaviour entityData->define(DATA_ID_SYNCHED_ACTION, e_EnderdragonAction_HoldingPattern);}
void EnderDragon::getLatencyPos(doubleArray result, int step, float a){ if (getHealth() <= 0) { a = 0; }
a = 1 - a;
int p0 = (posPointer - step * 1) & 63; int p1 = (posPointer - step * 1 - 1) & 63;
// positions is a ring buffer of size positionsLength (64) storing positional information per tick // positions[i][0] is y rotation // positions[i][1] is y position // positions[i][2] is currently always 0
double yr0 = positions[p0][0]; double yrd = Mth::wrapDegrees(positions[p1][0] - yr0); result[0] = yr0 + yrd * a;
yr0 = positions[p0][1]; yrd = positions[p1][1] - yr0;
result[1] = yr0 + yrd * a; result[2] = positions[p0][2] + (positions[p1][2] - positions[p0][2]) * a;}
void EnderDragon::aiStep(){ if (level->isClientSide) { // 4J Stu - If saved when dead we need to make sure that the actual health is updated correctly on the client // Fix for TU9: Content: Gameplay: Enderdragon respawns after loading game which was previously saved at point of hes death setHealth(getHealth());
float flap = Mth::cos(flapTime * PI * 2); float oldFlap = Mth::cos(oFlapTime * PI * 2);
if (oldFlap <= -0.3f && flap >= -0.3f) { level->playLocalSound(x, y, z, eSoundType_MOB_ENDERDRAGON_MOVE, 1, 0.8f + random->nextFloat() * .3f, false, 100.0f); } // play a growl every now and then if(! (getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || getSynchedAction() == e_EnderdragonAction_Sitting_Attacking)) { m_iGrowlTimer--; if(m_iGrowlTimer<0) { level->playLocalSound(x, y, z, eSoundType_MOB_ENDERDRAGON_GROWL, 0.5f, 0.8f + random->nextFloat() * .3f, false, 100.0f); m_iGrowlTimer=200+(random->nextInt(200)); } } }
oFlapTime = flapTime;
if (getHealth() <= 0) { // level.addParticle("explode", x + random.nextFloat() * bbWidth * 2 - bbWidth, y + random.nextFloat() * bbHeight, z + random.nextFloat() * bbWidth * 2 - bbWidth, 0, 0, 0); float xo = (random->nextFloat() - 0.5f) * 8; float yo = (random->nextFloat() - 0.5f) * 4; float zo = (random->nextFloat() - 0.5f) * 8; level->addParticle(eParticleType_largeexplode, x + xo, y + 2 + yo, z + zo, 0, 0, 0); return; }
checkCrystals();
float flapSpeed = 0.2f / (sqrt(xd * xd + zd * zd) * 10.0f + 1); flapSpeed *= (float) pow(2.0, yd); if ( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) { //app.DebugPrintf("flapSpeed is %f\n", flapSpeed); //flapTime += flapSpeed * 2; flapTime += 0.1f; } else if (inWall) { flapTime += flapSpeed * 0.5f; } else { flapTime += flapSpeed; }
yRot = Mth::wrapDegrees(yRot);
if (posPointer < 0) { for (int i = 0; i < positionsLength; i++) { positions[i][0] = yRot; positions[i][1] = y; } }
if (++posPointer == positionsLength) posPointer = 0; positions[posPointer][0] = yRot; positions[posPointer][1] = y;
if (level->isClientSide) { if (lSteps > 0) { double xt = x + (lx - x) / lSteps; double yt = y + (ly - y) / lSteps; double zt = z + (lz - z) / lSteps;
// 4J Stu - The movement is so small that this head animation doesn't look good //if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ) //{ // double yrd = lyr - (yRot + m_headYRot); // while (yrd < -180) // yrd += 360; // while (yrd >= 180) // yrd -= 360;
// m_headYRot += (yrd) / lSteps; //} //else { double yrd = Mth::wrapDegrees(lyr - yRot);
m_headYRot = 0.0; yRot += (yrd) / lSteps; } xRot += (lxr - xRot) / lSteps;
lSteps--; this->setPos(xt, yt, zt); this->setRot(yRot, xRot);
/* * List<AABB> collisions = level.getCubes(this, bb.shrink(1 / 32.0, 0, 1 / * 32.0)); if (collisions.size() > 0) { double yTop = 0; for (int i = 0; i < * collisions.size(); i++) { AABB ab = collisions.get(i); if (ab.y1 > yTop) yTop * = ab.y1; } yt += yTop - bb.y0; setPos(xt, yt, zt); } */
}
if( getSynchedAction() == e_EnderdragonAction_Landing || (getSynchedAction() == e_EnderdragonAction_Sitting_Flaming && tickCount%2==0) ) { double xP = 0.0; double yP = 0.0; double zP = 0.0; Vec3 *v = getHeadLookVector(1); //getViewVector(1); //app.DebugPrintf("View vector is (%f,%f,%f) - lsteps %d\n", v->x, v->y, v->z, lSteps); //unsigned int d = 0; //for(unsigned int d = 1; d < 3; ++d) { Vec3 *vN = v->normalize(); vN->yRot(-PI/4); for(unsigned int i = 0; i < 8; ++i) { if(getSynchedAction() == e_EnderdragonAction_Landing) { //for(unsigned int j = 0; j < 6; ++j) { xP = head->x;// - vN->x * d; yP = head->bb->y0 + head->bbHeight / 2;// - vN->y * d; //head->y + head->bbHeight / 2 + 0.5f - v->y * d; zP = head->z;// - vN->z * d; xP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2; yP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2; zP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2; level->addParticle(eParticleType_dragonbreath, xP, yP, zP, (-vN->x * 0.08) + xd, (-vN->y * 0.3) + yd, (-vN->z * 0.08) + zd); } } else { double yVelocity = 0.6; double xzVelocity = 0.08; for(unsigned int j = 0; j < 6; ++j) { xP = head->x;// - vN->x * d; yP = head->bb->y0 + head->bbHeight / 2;// - vN->y * d; //head->y + head->bbHeight / 2 + 0.5f - v->y * d; zP = head->z;// - vN->z * d; xP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2; yP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2; zP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2; level->addParticle(eParticleType_dragonbreath, xP, yP, zP, -vN->x * xzVelocity*j, -vN->y * yVelocity, -vN->z * xzVelocity*j); } } vN->yRot(PI/(2*8) ); } } } else if( getSynchedAction() == e_EnderdragonAction_Sitting_Attacking ) { // AP - changed this to use playLocalSound because no sound could be heard with playSound (cos it's a stub function) level->playLocalSound(x, y, z, eSoundType_MOB_ENDERDRAGON_GROWL, 0.5f, 0.8f + random->nextFloat() * .3f, false, 100.0f); } } else { double xdd = xTarget - x; double ydd = yTarget - y; double zdd = zTarget - z;
double dist = xdd * xdd + ydd * ydd + zdd * zdd;
if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ) { --m_actionTicks; if(m_actionTicks <= 0) { if( m_flameAttacks >= SITTING_FLAME_ATTACKS_COUNT) { setSynchedAction(e_EnderdragonAction_Takeoff);#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: Takeoff\n");#endif newTarget = true; } else { setSynchedAction(e_EnderdragonAction_Sitting_Scanning); attackTarget = level->getNearestPlayer( shared_from_this(), SITTING_ATTACK_VIEW_RANGE, SITTING_ATTACK_Y_VIEW_RANGE );#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: SittingScanning\n");#endif } } } else if( getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ) { attackTarget = level->getNearestPlayer( shared_from_this(), SITTING_ATTACK_VIEW_RANGE, SITTING_ATTACK_Y_VIEW_RANGE );
++m_actionTicks; if( attackTarget != NULL ) { if(m_actionTicks > SITTING_SCANNING_IDLE_TICKS/4) { setSynchedAction(e_EnderdragonAction_Sitting_Attacking);#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: SittingAttacking\n");#endif m_actionTicks = ATTACK_TICKS; } } else { if(m_actionTicks >= SITTING_SCANNING_IDLE_TICKS) { setSynchedAction(e_EnderdragonAction_Takeoff);#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: Takeoff\n");#endif newTarget = true; } } } else if( getSynchedAction() == e_EnderdragonAction_Sitting_Attacking ) { --m_actionTicks; if(m_actionTicks <= 0) { ++m_flameAttacks; setSynchedAction(e_EnderdragonAction_Sitting_Flaming); attackTarget = level->getNearestPlayer( shared_from_this(), SITTING_ATTACK_VIEW_RANGE, SITTING_ATTACK_Y_VIEW_RANGE );#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: SittingFlaming\n");#endif m_actionTicks = FLAME_TICKS; } } else if( !newTarget && getSynchedAction() == e_EnderdragonAction_Takeoff) { int eggHeight = level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS); //level->getHeightmap(4,4);
float dist = distanceToSqr(PODIUM_X_POS, eggHeight, PODIUM_Z_POS); if(dist > (10.0f * 10.0f) ) { setSynchedAction(e_EnderdragonAction_HoldingPattern);#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: HoldingPattern\n");#endif } } else if (newTarget || ( (getSynchedAction() != e_EnderdragonAction_Landing && dist < 10 * 10) || dist < 1) || dist > 150 * 150 || horizontalCollision || verticalCollision) { findNewTarget(); }
if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || getSynchedAction() == e_EnderdragonAction_Landing ) { if( m_actionTicks < (FLAME_TICKS - 10) ) { vector<shared_ptr<Entity> > *targets = level->getEntities(shared_from_this(), m_acidArea);
for( AUTO_VAR(it, targets->begin() ); it != targets->end(); ++it) { if ( (*it)->instanceof(eTYPE_LIVINGENTITY) ) { //app.DebugPrintf("Attacking entity with acid\n"); shared_ptr<LivingEntity> e = dynamic_pointer_cast<LivingEntity>( *it ); e->hurt(DamageSource::dragonbreath, 2); } } } } if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ) { // No movement } else if( getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ) { if( attackTarget != NULL) { Vec3 *aim = Vec3::newTemp((attackTarget->x - x), 0, (attackTarget->z - z))->normalize(); Vec3 *dir = Vec3::newTemp(sin(yRot * PI / 180), 0, -cos(yRot * PI / 180))->normalize(); float dot = (float)dir->dot(aim); float angleDegs = acos(dot)*180/PI; angleDegs = angleDegs + 0.5f;
if( angleDegs < 0 || angleDegs > 10 ) { double xdd = attackTarget->x - head->x; //double ydd = (attackTarget->bb->y0 + attackTarget->bbHeight / 2) - (head->y + head->bbHeight / 2); double zdd = attackTarget->z - head->z;
double yRotT = (180) - atan2(xdd, zdd) * 180 / PI; double yRotD = Mth::wrapDegrees(yRotT - yRot);
if (yRotD > 50) yRotD = 50; if (yRotD < -50) yRotD = -50;
double xd = xTarget - x; double zd = zTarget - z; yRotA *= 0.80f;
float rotSpeed = sqrt(xd * xd + zd * zd) * 1 + 1; double distToTarget = sqrt(xd * xd + zd * zd) * 1 + 1; if (distToTarget > 40) distToTarget = 40; yRotA += yRotD * ((0.7f / distToTarget) / rotSpeed); yRot += yRotA; } else { //setSynchedAction(e_EnderdragonAction_Sitting_Flaming);#if PRINT_DRAGON_STATE_CHANGE_MESSAGES //app.DebugPrintf("Dragon action is now : SittingFlaming\n");#endif //m_actionTicks = FLAME_TICKS; } } else { //setSynchedAction(e_EnderdragonAction_Sitting_Flaming); //app.DebugPrintf("Dragon action is now : SittingFlaming\n"); //m_actionTicks = 0; } } else if( getSynchedAction() == e_EnderdragonAction_Sitting_Attacking ) {
} else { // double xTargetO = xTarget; // double yTargetO = yTarget; // double zTargetO = zTarget; if (getSynchedAction() == e_EnderdragonAction_StrafePlayer && attackTarget != NULL && m_currentPath != NULL && m_currentPath->isDone()) { xTarget = attackTarget->x; zTarget = attackTarget->z;
double xd = xTarget - x; double zd = zTarget - z; double sd = sqrt(xd * xd + zd * zd); double ho = 0.4f + sd / 80.0f - 1; if (ho > 10) ho = 10; yTarget = attackTarget->bb->y0 + ho; } else { //xTarget += random->nextGaussian() * 2; //zTarget += random->nextGaussian() * 2; } ydd = ydd / (sqrt(xdd * xdd + zdd * zdd)); float max = 0.6f; if(getSynchedAction() == e_EnderdragonAction_Landing) max = 1.5f; if (ydd < -max) ydd = -max; if (ydd > max) ydd = max; yd += (ydd) * 0.1f; while (yRot < -180) yRot += 180 * 2; while (yRot >= 180) yRot -= 180 * 2;
double yRotT = (180) - atan2(xdd, zdd) * 180 / PI; double yRotD = yRotT - yRot; while (yRotD < -180) yRotD += 180 * 2; while (yRotD >= 180) yRotD -= 180 * 2;
if (yRotD > 50) yRotD = 50; if (yRotD < -50) yRotD = -50;
Vec3 *aim = Vec3::newTemp((xTarget - x), (yTarget - y), (zTarget - z))->normalize(); Vec3 *dir = Vec3::newTemp(sin(yRot * PI / 180), yd, -cos(yRot * PI / 180))->normalize(); float dot = (float) (dir->dot(aim) + 0.5f) / 1.5f; if (dot < 0) dot = 0;
yRotA *= 0.80f;
float rotSpeed = sqrt(xd * xd + zd * zd) * 1 + 1; double distToTarget = sqrt(xd * xd + zd * zd) * 1 + 1; if (distToTarget > 40) distToTarget = 40; if(getSynchedAction() == e_EnderdragonAction_Landing) { yRotA += yRotD * (distToTarget / rotSpeed); } else { yRotA += yRotD * ((0.7f / distToTarget) / rotSpeed); } yRot += yRotA * 0.1f;
float span = (float) (2.0f / (distToTarget + 1)); float speed = 0.06f; moveRelative(0, -1, speed * (dot * span + (1 - span))); if (inWall) { move(xd * 0.8f, yd * 0.8f, zd * 0.8f); } else { move(xd, yd, zd);
}
Vec3 *actual = Vec3::newTemp(xd, yd, zd)->normalize(); float slide = (float) (actual->dot(dir) + 1) / 2.0f; slide = 0.8f + 0.15f * slide;
xd *= slide; zd *= slide; yd *= 0.91f; } }
yBodyRot = yRot;
head->bbWidth = head->bbHeight = 1; // 4J Stu - Replaced what was "head" with "neck" //3; neck->bbWidth = neck->bbHeight = 3; tail1->bbWidth = tail1->bbHeight = 2; tail2->bbWidth = tail2->bbHeight = 2; tail3->bbWidth = tail3->bbHeight = 2; body->bbHeight = 3; body->bbWidth = 5; wing1->bbHeight = 2; wing1->bbWidth = 4; wing2->bbHeight = 3; wing2->bbWidth = 4;
//double latencyPosAcomponents[3],latencyPosBcomponents[3]; //doubleArray latencyPosA = doubleArray(latencyPosAcomponents,3); //doubleArray latencyPosB = doubleArray(latencyPosBcomponents,3); //getLatencyPos(latencyPosA, 5, 1); //getLatencyPos(latencyPosB, 10, 1);
//float tilt = (float) (latencyPosA[1] - latencyPosB[1]) * 10 / 180.0f * PI; float tilt = (float) getTilt(1) / 180.0f * PI; float ccTilt = cos(tilt);
// 4J Stu - ssTilt was negative sin(tilt), but this causes the bounding boxes of the parts to head in the wrong y direction // i.e. head moves up when tilting forward, and down when tilting backwards float ssTilt = sin(tilt);
float rot1 = yRot * PI / 180; float ss1 = sin(rot1); float cc1 = cos(rot1);
body->tick(); body->moveTo(x + ss1 * 0.5f, y, z - cc1 * 0.5f, 0, 0); wing1->tick(); wing1->moveTo(x + cc1 * 4.5f, y + 2, z + ss1 * 4.5f, 0, 0); wing2->tick(); wing2->moveTo(x - cc1 * 4.5f, y + 2, z - ss1 * 4.5f, 0, 0);
if (!level->isClientSide) checkAttack(); if (!level->isClientSide && hurtDuration == 0) { knockBack(level->getEntities(shared_from_this(), wing1->bb->grow(4, 2, 4)->move(0, -2, 0))); knockBack(level->getEntities(shared_from_this(), wing2->bb->grow(4, 2, 4)->move(0, -2, 0))); hurt(level->getEntities(shared_from_this(), neck->bb->grow(1, 1, 1))); hurt(level->getEntities(shared_from_this(), head->bb->grow(1, 1, 1))); }
double p1components[3]; doubleArray p1 = doubleArray(p1components, 3); getLatencyPos(p1, 5, 1);
{ //double p0components[3]; //doubleArray p0 = doubleArray(p0components, 3); //getLatencyPos(p0, 0, 1);
double yRotDiff = getHeadYRotDiff(1);
float ss = sin((yRot + yRotDiff) * PI / 180 - yRotA * 0.01f); float cc = cos((yRot + yRotDiff) * PI / 180 - yRotA * 0.01f); head->tick(); neck->tick(); double yOffset = getHeadYOffset(1); // (p0[1] - p1[1]) * 1
// 4J Stu - Changed the head entity to only be the head, and not include the neck parts head->moveTo(x + ss * 6.5f * ccTilt, y + yOffset + ssTilt * 6.5f, z - cc * 6.5f * ccTilt, 0, 0);
// Neck position is where the java code used to move the "head" object which was head and neck neck->moveTo(x + ss * 5.5f * ccTilt, y + yOffset + ssTilt * 5.5f, z - cc * 5.5f * ccTilt, 0, 0);
double acidX = x + ss * 9.5f * ccTilt; double acidY = y + yOffset + ssTilt * 10.5f; double acidZ = z - cc * 9.5f * ccTilt; m_acidArea->set(acidX - 5, acidY - 17, acidZ - 5, acidX + 5, acidY + 4, acidZ + 5);
//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); //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); //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); //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); //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); }
// Curls/straightens the tail for (int i = 0; i < 3; i++) { shared_ptr<MultiEntityMobPart> part = nullptr;
if (i == 0) part = tail1; if (i == 1) part = tail2; if (i == 2) part = tail3;
double p0components[3]; doubleArray p0 = doubleArray(p0components, 3); getLatencyPos(p0, 12 + i * 2, 1);
float rot = yRot * PI / 180 + rotWrap(p0[0] - p1[0]) * PI / 180 * (1); float ss = sin(rot); float cc = cos(rot);
float dd1 = 1.5f; float dd = (i + 1) * 2.0f; part->tick(); 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); }
// 4J Stu - Fireball attack taken from Ghast if (!level->isClientSide) { double maxDist = 64.0f; if (getSynchedAction() == e_EnderdragonAction_StrafePlayer && attackTarget != NULL && attackTarget->distanceToSqr(shared_from_this()) < maxDist * maxDist) { if (this->canSee(attackTarget)) { m_fireballCharge++; Vec3 *aim = Vec3::newTemp((attackTarget->x - x), 0, (attackTarget->z - z))->normalize(); Vec3 *dir = Vec3::newTemp(sin(yRot * PI / 180), 0, -cos(yRot * PI / 180))->normalize(); float dot = (float)dir->dot(aim); float angleDegs = acos(dot)*180/PI; angleDegs = angleDegs + 0.5f;
if (m_fireballCharge >= 20 && ( angleDegs >= 0 && angleDegs < 10 )) { double d = 1; Vec3 *v = getViewVector(1); float startingX = head->x - v->x * d; float startingY = head->y + head->bbHeight / 2 + 0.5f; float startingZ = head->z - v->z * d;
double xdd = attackTarget->x - startingX; double ydd = (attackTarget->bb->y0 + attackTarget->bbHeight / 2) - (startingY + head->bbHeight / 2); double zdd = attackTarget->z - startingZ;
level->levelEvent(nullptr, LevelEvent::SOUND_GHAST_FIREBALL, (int) x, (int) y, (int) z, 0); shared_ptr<DragonFireball> ie = shared_ptr<DragonFireball>( new DragonFireball(level, dynamic_pointer_cast<Mob>( shared_from_this() ), xdd, ydd, zdd) ); ie->x = startingX; ie->y = startingY; ie->z = startingZ; level->addEntity(ie); m_fireballCharge = 0;
app.DebugPrintf("Finding new target due to having fired a fireball\n"); if( m_currentPath != NULL ) { while(!m_currentPath->isDone()) { m_currentPath->next(); } } newTarget = true; findNewTarget(); } } else { if (m_fireballCharge > 0) m_fireballCharge--; } } else { if (m_fireballCharge > 0) m_fireballCharge--; } } // End fireball attack
if (!level->isClientSide) { inWall = checkWalls(head->bb) | checkWalls(neck->bb) | checkWalls(body->bb); }}
void EnderDragon::checkCrystals(){ if (nearestCrystal != NULL) { if (nearestCrystal->removed) { if (!level->isClientSide) { hurt(head, DamageSource::explosion(NULL), 10); }
nearestCrystal = nullptr; } else if (tickCount % 10 == 0) { if (getHealth() < getMaxHealth()) setHealth(getHealth() + 1); } }
if (random->nextInt(10) == 0) { float maxDist = 32; vector<shared_ptr<Entity> > *crystals = level->getEntitiesOfClass(typeid(EnderCrystal), bb->grow(maxDist, maxDist, maxDist));
shared_ptr<EnderCrystal> crystal = nullptr; double nearest = Double::MAX_VALUE; //for (Entity ec : crystals) for(AUTO_VAR(it, crystals->begin()); it != crystals->end(); ++it) { shared_ptr<EnderCrystal> ec = dynamic_pointer_cast<EnderCrystal>( *it ); double dist = ec->distanceToSqr(shared_from_this() ); if (dist < nearest) { nearest = dist; crystal = ec; } } delete crystals;
nearestCrystal = crystal; }}
void EnderDragon::checkAttack(){ //if (tickCount % 20 == 0) { // Vec3 *v = getViewVector(1); // double xdd = 0; // double ydd = -1; // double zdd = 0;
// double x = (body.bb.x0 + body.bb.x1) / 2; // double y = (body.bb.y0 + body.bb.y1) / 2 - 2; // double z = (body.bb.z0 + body.bb.z1) / 2;
}}
void EnderDragon::knockBack(vector<shared_ptr<Entity> > *entities){ double xm = (body->bb->x0 + body->bb->x1) / 2; // double ym = (body.bb.y0 + body.bb.y1) / 2; double zm = (body->bb->z0 + body->bb->z1) / 2;
//for (Entity e : entities) for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) {
if ( (*it)->instanceof(eTYPE_LIVINGENTITY) )//(e instanceof Mob) { shared_ptr<LivingEntity> e = dynamic_pointer_cast<LivingEntity>( *it ); double xd = e->x - xm; double zd = e->z - zm; double dd = xd * xd + zd * zd; e->push(xd / dd * 4, 0.2f, zd / dd * 4); } }}
void EnderDragon::hurt(vector<shared_ptr<Entity> > *entities){ //for (int i = 0; i < entities->size(); i++) for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) {
if ( (*it)->instanceof(eTYPE_LIVINGENTITY) ) //(e instanceof Mob) { shared_ptr<LivingEntity> e = dynamic_pointer_cast<LivingEntity>( *it );//entities.get(i); DamageSource *damageSource = DamageSource::mobAttack( dynamic_pointer_cast<LivingEntity>( shared_from_this() )); e->hurt(damageSource, 10); delete damageSource; } }}
void EnderDragon::findNewTarget(){ shared_ptr<Player> playerNearestToEgg = nullptr;
// Update current action switch(getSynchedAction()) { case e_EnderdragonAction_Takeoff: case e_EnderdragonAction_HoldingPattern: { if(!newTarget && m_currentPath != NULL && m_currentPath->isDone()) { // Distance is 64, which is the radius of the circle int eggHeight = max(level->seaLevel + 5, level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS)); //level->getHeightmap(4,4); playerNearestToEgg = level->getNearestPlayer(PODIUM_X_POS, eggHeight, PODIUM_Z_POS, 64.0); double dist = 64.0f; if(playerNearestToEgg != NULL) { dist = playerNearestToEgg->distanceToSqr(PODIUM_X_POS, eggHeight, PODIUM_Z_POS); dist /= (8*8*8); } //app.DebugPrintf("Adjusted dist is %f\n", dist);
if( random->nextInt(m_remainingCrystalsCount + 3) == 0 ) { setSynchedAction(e_EnderdragonAction_LandingApproach);#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: LandingApproach\n");#endif } // More likely to strafe a player if they are close to the egg, or there are not many crystals remaining else if( playerNearestToEgg != NULL && (random->nextInt( abs(dist) + 2 ) == 0 || random->nextInt( m_remainingCrystalsCount + 2 ) == 0) ) { setSynchedAction(e_EnderdragonAction_StrafePlayer);#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: StrafePlayer\n");#endif } } } break; case e_EnderdragonAction_StrafePlayer: // Always return to the holding pattern after strafing if(m_currentPath == NULL || (m_currentPath->isDone() && newTarget) ) { setSynchedAction(e_EnderdragonAction_HoldingPattern);#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: HoldingPattern\n");#endif } break; case e_EnderdragonAction_Landing: // setSynchedAction(e_EnderdragonAction_Sitting_Flaming); //#if PRINT_DRAGON_STATE_CHANGE_MESSAGES // app.DebugPrintf("Dragon action is now: SittingFlaming\n"); //#endif // m_actionTicks = FLAME_TICKS;
m_flameAttacks = 0; setSynchedAction(e_EnderdragonAction_Sitting_Scanning); attackTarget = level->getNearestPlayer( shared_from_this(), SITTING_ATTACK_VIEW_RANGE, SITTING_ATTACK_Y_VIEW_RANGE );#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: SittingScanning\n");#endif m_actionTicks = 0; break; };
newTarget = false;
//if (random->nextInt(2) == 0 && level->players.size() > 0) if(getSynchedAction() == e_EnderdragonAction_StrafePlayer && playerNearestToEgg != NULL) { attackTarget = playerNearestToEgg; strafeAttackTarget(); } else if(getSynchedAction() == e_EnderdragonAction_LandingApproach) { // Generate a new path if we don't currently have one if( m_currentPath == NULL || m_currentPath->isDone() ) { int currentNodeIndex = findClosestNode();
// To get the angle to the player correct when landing, head to a node diametrically opposite the player, then swoop in to 4,4 int eggHeight = max( level->seaLevel + 5, level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS) ); //level->getHeightmap(4,4); playerNearestToEgg = level->getNearestPlayer(PODIUM_X_POS, eggHeight, PODIUM_Z_POS, 128.0);
int targetNodeIndex = 0 ; if(playerNearestToEgg != NULL) { Vec3 *aim = Vec3::newTemp(playerNearestToEgg->x, 0, playerNearestToEgg->z)->normalize(); //app.DebugPrintf("Final marker node near (%f,%d,%f)\n", -aim->x*40,105,-aim->z*40 ); targetNodeIndex = findClosestNode(-aim->x*40,105.0,-aim->z*40); } else { targetNodeIndex = findClosestNode(40.0, eggHeight, 0.0); } Node finalNode(PODIUM_X_POS, eggHeight, PODIUM_Z_POS);
if(m_currentPath != NULL) delete m_currentPath; m_currentPath = findPath(currentNodeIndex,targetNodeIndex, &finalNode);
// Always skip the first node (as that's where we are already) if(m_currentPath != NULL) m_currentPath->next(); }
m_actionTicks = 0;
navigateToNextPathNode();
if(m_currentPath != NULL && m_currentPath->isDone()) { setSynchedAction(e_EnderdragonAction_Landing);#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: Landing\n");#endif } } else if(getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || getSynchedAction() == e_EnderdragonAction_Sitting_Attacking || getSynchedAction() == e_EnderdragonAction_Sitting_Scanning) { // Does no movement } else { // Default is e_EnderdragonAction_HoldingPattern // Generate a new path if we don't currently have one if(m_currentPath == NULL || m_currentPath->isDone() ) { int currentNodeIndex = findClosestNode(); int targetNodeIndex = currentNodeIndex; //if(random->nextInt(4) == 0) m_holdingPatternClockwise = !m_holdingPatternClockwise;
if( getSynchedAction() == e_EnderdragonAction_Takeoff) { Vec3 *v = getHeadLookVector(1); targetNodeIndex = findClosestNode(-v->x*40,105.0,-v->z*40); } else { if(random->nextInt(8) == 0) { m_holdingPatternClockwise = !m_holdingPatternClockwise; targetNodeIndex = targetNodeIndex + 6; }
if(m_holdingPatternClockwise) targetNodeIndex = targetNodeIndex + 1; else targetNodeIndex = targetNodeIndex - 1; }
if(m_remainingCrystalsCount <= 0) { // If no crystals left, navigate only between nodes 12-19 targetNodeIndex -= 12; targetNodeIndex = targetNodeIndex&7; // 4J-RR - was %8, but that could create a result of -1 here when targetNodeIndex was 11 targetNodeIndex += 12; } else { // If crystals are left, navigate only between nodes 0-11 targetNodeIndex = targetNodeIndex%12; if(targetNodeIndex < 0) targetNodeIndex += 12; }
if(m_currentPath != NULL) delete m_currentPath; m_currentPath = findPath(currentNodeIndex,targetNodeIndex);
// Always skip the first node (as that's where we are already) if(m_currentPath != NULL) m_currentPath->next(); }
navigateToNextPathNode();
if(getSynchedAction() != e_EnderdragonAction_StrafePlayer) attackTarget = nullptr; }}
float EnderDragon::rotWrap(double d){ while (d >= 180) d -= 360; while (d < -180) d += 360; return (float) d;}
bool EnderDragon::checkWalls(AABB *bb){ int x0 = Mth::floor(bb->x0); int y0 = Mth::floor(bb->y0); int z0 = Mth::floor(bb->z0); int x1 = Mth::floor(bb->x1); int y1 = Mth::floor(bb->y1); int z1 = Mth::floor(bb->z1); bool hitWall = false; bool destroyedTile = false; for (int x = x0; x <= x1; x++) { for (int y = y0; y <= y1; y++) { for (int z = z0; z <= z1; z++) { int t = level->getTile(x, y, z); // 4J Stu - Don't remove fire if (t == 0 || t == Tile::fire_Id) {
} else if (t == Tile::obsidian_Id || t == Tile::endStone_Id || t == Tile::unbreakable_Id || !level->getGameRules()->getBoolean(GameRules::RULE_MOBGRIEFING)) { hitWall = true; } else { destroyedTile = level->removeTile(x, y, z) || destroyedTile; } } } }
if (destroyedTile) { double x = bb->x0 + (bb->x1 - bb->x0) * random->nextFloat(); double y = bb->y0 + (bb->y1 - bb->y0) * random->nextFloat(); double z = bb->z0 + (bb->z1 - bb->z0) * random->nextFloat(); level->addParticle(eParticleType_largeexplode, x, y, z, 0, 0, 0); }
return hitWall;}
bool EnderDragon::hurt(shared_ptr<MultiEntityMobPart> MultiEntityMobPart, DamageSource *source, float damage){ if (MultiEntityMobPart != head) { damage = damage / 4 + 1; }
//float rot1 = yRot * PI / 180; //float ss1 = sin(rot1); //float cc1 = cos(rot1);
//xTarget = x + ss1 * 5 + (random->nextFloat() - 0.5f) * 2; //yTarget = y + random->nextFloat() * 3 + 1; //zTarget = z - cc1 * 5 + (random->nextFloat() - 0.5f) * 2; //attackTarget = NULL;
if ( source->getEntity() != NULL && source->getEntity()->instanceof(eTYPE_PLAYER) || source->isExplosion() ) { int healthBefore = getHealth(); reallyHurt(source, damage);
//if(!level->isClientSide) app.DebugPrintf("Health is now %d\n", health); if( getHealth() <= 0 && !( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) ) { setHealth(1);
if( setSynchedAction(e_EnderdragonAction_LandingApproach) ) { if( m_currentPath != NULL ) { while(!m_currentPath->isDone()) { m_currentPath->next(); } } app.DebugPrintf("Dragon should be dead, so landing.\n");#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: LandingApproach\n");#endif findNewTarget(); } }
if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) { m_sittingDamageReceived += healthBefore - getHealth();
if(m_sittingDamageReceived > (SITTING_ALLOWED_DAMAGE_PERCENTAGE*getMaxHealth() ) ) { m_sittingDamageReceived = 0; setSynchedAction(e_EnderdragonAction_Takeoff); newTarget = true;#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: Takeoff\n");#endif } } } return true;}
bool EnderDragon::hurt(DamageSource *source, float damage){ return false;}
bool EnderDragon::reallyHurt(DamageSource *source, float damage){ return Mob::hurt(source, damage);}
void EnderDragon::tickDeath(){ if( getSynchedAction() != e_EnderdragonAction_Sitting_Flaming && getSynchedAction() != e_EnderdragonAction_Sitting_Scanning && getSynchedAction() != e_EnderdragonAction_Sitting_Attacking) { if(!level->isClientSide) setHealth(1); return; }
dragonDeathTime++; if (dragonDeathTime >= 180 && dragonDeathTime <= 200) { float xo = (random->nextFloat() - 0.5f) * 8; float yo = (random->nextFloat() - 0.5f) * 4; float zo = (random->nextFloat() - 0.5f) * 8; level->addParticle(eParticleType_hugeexplosion, x + xo, y + 2 + yo, z + zo, 0, 0, 0); } if (!level->isClientSide) { if (dragonDeathTime > 150 && dragonDeathTime % 5 == 0) { int xpCount = 1000; while (xpCount > 0) { int newCount = ExperienceOrb::getExperienceValue(xpCount); xpCount -= newCount; level->addEntity(shared_ptr<ExperienceOrb>( new ExperienceOrb(level, x, y, z, newCount) )); } } if (dragonDeathTime == 1) { level->globalLevelEvent(LevelEvent::SOUND_DRAGON_DEATH, (int) x, (int) y, (int) z, 0); } } move(0, 0.1f, 0); yBodyRot = yRot += 20.0f;
if (dragonDeathTime == 200 && !level->isClientSide) { //level->levelEvent(NULL, LevelEvent::ENDERDRAGON_KILLED, (int) x, (int) y, (int) z, 0);
int xpCount = 2000; while (xpCount > 0) { int newCount = ExperienceOrb::getExperienceValue(xpCount); xpCount -= newCount; level->addEntity(shared_ptr<ExperienceOrb>( new ExperienceOrb(level, x, y, z, newCount))); } int xo = 5 + random->nextInt(2) * 2 - 1; int zo = 5 + random->nextInt(2) * 2 - 1; if (random->nextInt(2) == 0) { xo = 0; } else { zo = 0; } // 4J-PB changed to center this between the pillars spawnExitPortal(0,0);//Mth::floor(x), Mth::floor(z)); remove(); }}
void EnderDragon::spawnExitPortal(int x, int z){ int y = level->seaLevel;
TheEndPortal::allowAnywhere(true);
int r = 4; for (int yy = y - 1; yy <= y + 32; yy++) { for (int xx = x - r; xx <= x + r; xx++) { for (int zz = z - r; zz <= z + r; zz++) { double xd = xx - x; double zd = zz - z; double d = sqrt(xd * xd + zd * zd); if (d <= r - 0.5) { if (yy < y) { if (d > r - 1 - 0.5) { } else { level->setTileAndUpdate(xx, yy, zz, Tile::unbreakable_Id); } } else if (yy > y) { level->setTileAndUpdate(xx, yy, zz, 0); } else { if (d > r - 1 - 0.5) { level->setTileAndUpdate(xx, yy, zz, Tile::unbreakable_Id); } else { level->setTileAndUpdate(xx, yy, zz, Tile::endPortalTile_Id); } } } } } }
level->setTileAndUpdate(x, y + 0, z, Tile::unbreakable_Id); level->setTileAndUpdate(x, y + 1, z, Tile::unbreakable_Id); level->setTileAndUpdate(x, y + 2, z, Tile::unbreakable_Id); level->setTileAndUpdate(x - 1, y + 2, z, Tile::torch_Id); level->setTileAndUpdate(x + 1, y + 2, z, Tile::torch_Id); level->setTileAndUpdate(x, y + 2, z - 1, Tile::torch_Id); level->setTileAndUpdate(x, y + 2, z + 1, Tile::torch_Id); level->setTileAndUpdate(x, y + 3, z, Tile::unbreakable_Id); level->setTileAndUpdate(x, y + 4, z, Tile::dragonEgg_Id);
// 4J-PB - The podium can be floating with nothing under it, so put some whiteStone under it if this is the case for (int yy = y - 5; yy < y - 1; yy++) { for (int xx = x - (r - 1); xx <= x + (r - 1); xx++) { for (int zz = z - (r - 1); zz <= z + (r - 1); zz++) { if(level->isEmptyTile(xx,yy,zz)) { level->setTileAndUpdate(xx, yy, zz, Tile::endStone_Id); } } } }
TheEndPortal::allowAnywhere(false);}
void EnderDragon::checkDespawn(){}
vector<shared_ptr<Entity> > *EnderDragon::getSubEntities(){ return &subEntities;}
bool EnderDragon::isPickable(){ return false;}
Level *EnderDragon::getLevel(){ return level;}
int EnderDragon::getAmbientSound(){ return eSoundType_MOB_ENDERDRAGON_GROWL; //"mob.enderdragon.growl";}
int EnderDragon::getHurtSound(){ return eSoundType_MOB_ENDERDRAGON_HIT; //"mob.enderdragon.hit";}
float EnderDragon::getSoundVolume(){ return 5;}
// 4J Added for new dragon behaviourbool EnderDragon::setSynchedAction(EEnderdragonAction action, bool force /*= false*/){ bool validTransition = false; // Check if this is a valid state transition switch(getSynchedAction()) { case e_EnderdragonAction_HoldingPattern: switch(action) { case e_EnderdragonAction_StrafePlayer: case e_EnderdragonAction_LandingApproach: validTransition = true; break; }; break; case e_EnderdragonAction_StrafePlayer: switch(action) { case e_EnderdragonAction_HoldingPattern: case e_EnderdragonAction_LandingApproach: validTransition = true; break; }; break; case e_EnderdragonAction_LandingApproach: switch(action) { case e_EnderdragonAction_Landing: validTransition = true; break; }; break; case e_EnderdragonAction_Landing: switch(action) { case e_EnderdragonAction_Sitting_Flaming: case e_EnderdragonAction_Sitting_Scanning: validTransition = true; break; }; break; case e_EnderdragonAction_Takeoff: switch(action) { case e_EnderdragonAction_HoldingPattern: validTransition = true; break; }; break; case e_EnderdragonAction_Sitting_Flaming: switch(action) { case e_EnderdragonAction_Sitting_Scanning: case e_EnderdragonAction_Sitting_Attacking: case e_EnderdragonAction_Takeoff: validTransition = true; break; }; break; case e_EnderdragonAction_Sitting_Scanning: switch(action) { case e_EnderdragonAction_Sitting_Flaming: case e_EnderdragonAction_Sitting_Attacking: case e_EnderdragonAction_Takeoff: validTransition = true; break; }; break; case e_EnderdragonAction_Sitting_Attacking: switch(action) { case e_EnderdragonAction_Sitting_Flaming: case e_EnderdragonAction_Sitting_Scanning: case e_EnderdragonAction_Takeoff: validTransition = true; break; }; break; };
if( force || validTransition ) { entityData->set(DATA_ID_SYNCHED_ACTION, action); } else { app.DebugPrintf("EnderDragon: Invalid state transition from %d to %d\n", getSynchedAction(), action); }
return force || validTransition;}
EnderDragon::EEnderdragonAction EnderDragon::getSynchedAction(){ return (EEnderdragonAction)entityData->getInteger(DATA_ID_SYNCHED_ACTION);}
void EnderDragon::handleCrystalDestroyed(DamageSource *source){ AABB *tempBB = AABB::newTemp(PODIUM_X_POS,84.0,PODIUM_Z_POS,PODIUM_X_POS+1.0,85.0,PODIUM_Z_POS+1.0); vector<shared_ptr<Entity> > *crystals = level->getEntitiesOfClass(typeid(EnderCrystal), tempBB->grow(48, 40, 48)); m_remainingCrystalsCount = (int)crystals->size() - 1; if(m_remainingCrystalsCount < 0) m_remainingCrystalsCount = 0; delete crystals;
app.DebugPrintf("Crystal count is now %d\n",m_remainingCrystalsCount);
//--m_remainingCrystalsCount;
if(m_remainingCrystalsCount%2 == 0) { if(setSynchedAction(e_EnderdragonAction_LandingApproach)) { if( m_currentPath != NULL ) { while(!m_currentPath->isDone()) { m_currentPath->next(); } } m_actionTicks = 1;#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: LandingApproach\n");#endif } } else if(source->getEntity() != NULL && source->getEntity()->instanceof(eTYPE_PLAYER)) { if(setSynchedAction(e_EnderdragonAction_StrafePlayer)) { attackTarget = dynamic_pointer_cast<Player>(source->getEntity());#if PRINT_DRAGON_STATE_CHANGE_MESSAGES app.DebugPrintf("Dragon action is now: StrafePlayer\n");#endif strafeAttackTarget(); } }}
void EnderDragon::strafeAttackTarget(){ app.DebugPrintf("Setting path to strafe attack target\n"); int currentNodeIndex = findClosestNode(); int targetNodeIndex = findClosestNode(attackTarget->x,attackTarget->y,attackTarget->z);
int finalXTarget = attackTarget->x; int finalZTarget = attackTarget->z;
double xd = finalXTarget - x; double zd = finalZTarget - z; double sd = sqrt(xd * xd + zd * zd); double ho = 0.4f + sd / 80.0f - 1; if (ho > 10) ho = 10; int finalYTarget = attackTarget->bb->y0 + ho;
Node finalNode(finalXTarget, finalYTarget, finalZTarget);
if(m_currentPath != NULL) delete m_currentPath; m_currentPath = findPath(currentNodeIndex,targetNodeIndex, &finalNode);
if(m_currentPath != NULL) { // Always skip the first node (as that's where we are already) m_currentPath->next();
navigateToNextPathNode(); }}
void EnderDragon::navigateToNextPathNode(){ if(m_currentPath != NULL && !m_currentPath->isDone()) { Vec3 *curr = m_currentPath->currentPos();
m_currentPath->next(); xTarget = curr->x;
if(getSynchedAction() == e_EnderdragonAction_LandingApproach && m_currentPath->isDone()) { // When heading to the last node on the landing approach, we want the yCoord to be exact yTarget = curr->y; } else { do { yTarget = curr->y + random->nextFloat() * 20; } while( yTarget < (curr->y) ); } zTarget = curr->z; app.DebugPrintf("Path node pos is (%f,%f,%f)\n",curr->x,curr->y,curr->z); app.DebugPrintf("Setting new target to (%f,%f,%f)\n",xTarget, yTarget, zTarget); }}
int EnderDragon::findClosestNode(){ // Setup all the nodes on the first time this is called if(m_nodes->data[0] == NULL) { // Path nodes for navigation // 0 - 11 are the outer ring at 60 blocks from centre // 12 - 19 are the middle ring at 40 blocks from centre // 20 - 23 are the inner ring at 20 blocks from centre int nodeX=0; int nodeY=0; int nodeZ=0; int multiplier = 0; for(unsigned int i = 0; i < 24; ++i) { int yAdjustment = 5; multiplier = i; if(i < 12) { nodeX=60 * Mth::cos(2*(-PI+(PI/12)*multiplier)); nodeZ=60 * Mth::sin(2*(-PI+(PI/12)*multiplier)); } else if(i < 20) { multiplier -= 12; nodeX=40 * Mth::cos(2*(-PI+(PI/8)*multiplier)); nodeZ=40 * Mth::sin(2*(-PI+(PI/8)*multiplier)); yAdjustment += 10; // Make the target well above the top of the towers } else { multiplier -= 20; nodeX=20 * Mth::cos(2*(-PI+(PI/4)*multiplier)); nodeZ=20 * Mth::sin(2*(-PI+(PI/4)*multiplier)); } // Fix for #77202 - TU9: Content: Gameplay: The Ender Dragon sometimes flies through terrain // Add minimum height nodeY = max( (level->seaLevel + 10), level->getTopSolidBlock(nodeX, nodeZ) + yAdjustment );
app.DebugPrintf("Node %d is at (%d,%d,%d)\n", i, nodeX, nodeY, nodeZ);
m_nodes->data[i] = new Node(nodeX,nodeY,nodeZ);
//level->setTile(nodeX,nodeY,nodeZ,Tile::obsidian_Id); }
m_nodeAdjacency[0] = (1<<11) | (1<<1) | (1<<12); m_nodeAdjacency[1] = (1<<0) | (1<<2) | (1<<13); m_nodeAdjacency[2] = (1<<1) | (1<<3) | (1<<13); m_nodeAdjacency[3] = (1<<2) | (1<<4) | (1<<14); m_nodeAdjacency[4] = (1<<3) | (1<<5) | (1<<15); m_nodeAdjacency[5] = (1<<4) | (1<<6) | (1<<15); m_nodeAdjacency[6] = (1<<5) | (1<<7) | (1<<16); m_nodeAdjacency[7] = (1<<6) | (1<<8) | (1<<17); m_nodeAdjacency[8] = (1<<7) | (1<<9) | (1<<17); m_nodeAdjacency[9] = (1<<8) | (1<<10) | (1<<18); m_nodeAdjacency[10] = (1<<9) | (1<<11) | (1<<19); m_nodeAdjacency[11] = (1<<10) | (1<<0) | (1<<19);
m_nodeAdjacency[12] = (1<<0) | (1<<13) | (1<<20) | (1<<19); m_nodeAdjacency[13] = (1<<1) | (1<<2) | (1<<14) | (1<<21) | (1<<20) | (1<<12); m_nodeAdjacency[14] = (1<<3) | (1<<15) | (1<<21) | (1<<13); m_nodeAdjacency[15] = (1<<4) | (1<<5) | (1<<16) | (1<<22) | (1<<21) | (1<<14); m_nodeAdjacency[16] = (1<<6) | (1<<17) | (1<<22) | (1<<15); m_nodeAdjacency[17] = (1<<7) | (1<<8) | (1<<18) | (1<<23) | (1<<22) | (1<<16); m_nodeAdjacency[18] = (1<<9) | (1<<19) | (1<<23) | (1<<17); m_nodeAdjacency[19] = (1<<10) | (1<<11) | (1<<12) | (1<<20) | (1<<23) | (1<<18);
m_nodeAdjacency[20] = (1<<12) | (1<<13) | (1<<21) | (1<<22) | (1<<23) | (1<<19); m_nodeAdjacency[21] = (1<<14) | (1<<15) | (1<<22) | (1<<23) | (1<<20) | (1<<13); m_nodeAdjacency[22] = (1<<15) | (1<<16) | (1<<17) | (1<<23) | (1<<20) | (1<<21); m_nodeAdjacency[23] = (1<<17) | (1<<18) | (1<<19) | (1<<20) | (1<<21) | (1<<22); }
return findClosestNode(x,y,z);}
int EnderDragon::findClosestNode(double tX, double tY, double tZ){ float closestDist = 100.0f; int closestIndex = 0; Node *currentPos = new Node((int) floor(tX), (int) floor(tY), (int) floor(tZ)); int startIndex = 0; if(m_remainingCrystalsCount <= 0) { // If not crystals are left then we try and stay in the middle ring and avoid the outer ring startIndex = 12; } for(unsigned int i = startIndex; i < 24; ++i) { if( m_nodes->data[i] != NULL ) { float dist = m_nodes->data[i]->distanceTo(currentPos); if(dist < closestDist) { closestDist = dist; closestIndex = i; } } } delete currentPos; return closestIndex;}
// 4J Stu - A* taken from PathFinder and modifiedPath *EnderDragon::findPath(int startIndex, int endIndex, Node *finalNode /* = NULL */){ for(unsigned int i = 0; i < 24; ++i) { Node *n = m_nodes->data[i]; n->closed = false; n->f = 0; n->g = 0; n->h = 0; n->cameFrom = NULL; n->heapIdx = -1; }
Node *from = m_nodes->data[startIndex]; Node *to = m_nodes->data[endIndex];
from->g = 0; from->h = from->distanceTo(to); from->f = from->h;
openSet->clear(); openSet->insert(from);
Node *closest = from;
int minimumNodeIndex = 0; if(m_remainingCrystalsCount <= 0) { // If not crystals are left then we try and stay in the middle ring and avoid the outer ring minimumNodeIndex = 12; }
while (!openSet->isEmpty()) { Node *x = openSet->pop();
if (x->equals(to)) { app.DebugPrintf("Found path from %d to %d\n", startIndex, endIndex); if(finalNode != NULL) { finalNode->cameFrom = to; to = finalNode; } return reconstruct_path(from, to); }
if (x->distanceTo(to) < closest->distanceTo(to)) { closest = x; } x->closed = true;
unsigned int xIndex = 0; for(unsigned int i = 0; i < 24; ++i) { if(m_nodes->data[i] == x) { xIndex = i; break; } }
for (int i = minimumNodeIndex; i < 24; i++) { if(m_nodeAdjacency[xIndex] & (1<<i)) { Node *y = m_nodes->data[i];
if(y->closed) continue;
float tentative_g_score = x->g + x->distanceTo(y); if (!y->inOpenSet() || tentative_g_score < y->g) { y->cameFrom = x; y->g = tentative_g_score; y->h = y->distanceTo(to); if (y->inOpenSet()) { openSet->changeCost(y, y->g + y->h); } else { y->f = y->g + y->h; openSet->insert(y); } } } } }
if (closest == from) return NULL; app.DebugPrintf("Failed to find path from %d to %d\n", startIndex, endIndex); if(finalNode != NULL) { finalNode->cameFrom = closest; closest = finalNode; } return reconstruct_path(from, closest);}
// function reconstruct_path(came_from,current_node)Path *EnderDragon::reconstruct_path(Node *from, Node *to){ int count = 1; Node *n = to; while (n->cameFrom != NULL) { count++; n = n->cameFrom; }
NodeArray nodes = NodeArray(count); n = to; nodes.data[--count] = n; while (n->cameFrom != NULL) { n = n->cameFrom; nodes.data[--count] = n; } Path *ret = new Path(nodes); delete [] nodes.data; return ret;}
void EnderDragon::addAdditonalSaveData(CompoundTag *entityTag) { app.DebugPrintf("Adding EnderDragon additional save data\n"); entityTag->putShort(L"RemainingCrystals", m_remainingCrystalsCount); entityTag->putInt(L"DragonState", (int)getSynchedAction() );
Mob::addAdditonalSaveData(entityTag);}
void EnderDragon::readAdditionalSaveData(CompoundTag *tag) { app.DebugPrintf("Reading EnderDragon additional save data\n"); m_remainingCrystalsCount = tag->getShort(L"RemainingCrystals"); if(!tag->contains(L"RemainingCrystals")) m_remainingCrystalsCount = CRYSTAL_COUNT;
if(tag->contains(L"DragonState")) setSynchedAction( (EEnderdragonAction)tag->getInt(L"DragonState"), true);
Mob::readAdditionalSaveData(tag);}
float EnderDragon::getTilt(float a){ float tilt = 0.0f; //if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || // getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || // getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) //{ // tilt = -25.0f; // xRot = -25.0f; //} //else { double latencyPosAcomponents[3],latencyPosBcomponents[3]; doubleArray latencyPosA = doubleArray(latencyPosAcomponents,3); doubleArray latencyPosB = doubleArray(latencyPosBcomponents,3); getLatencyPos(latencyPosA, 5, a); getLatencyPos(latencyPosB, 10, a);
tilt = (latencyPosA[1] - latencyPosB[1]) * 10; } //app.DebugPrintf("Tilt is %f\n", tilt);
return tilt;}
double EnderDragon::getHeadYOffset(float a){ double headYOffset = 0.0; if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) { headYOffset = -1.0; } else { double p1components[3]; doubleArray p1 = doubleArray(p1components, 3); getLatencyPos(p1, 5, 1);
double p0components[3]; doubleArray p0 = doubleArray(p0components, 3); getLatencyPos(p0, 0, 1);
headYOffset = (p0[1] - p1[1]) * 1; } //app.DebugPrintf("headYOffset is %f\n", headYOffset); return headYOffset;}
double EnderDragon::getHeadYRotDiff(float a){ double result = 0.0; //if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || // getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || // getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) //{ // result = m_headYRot; //} return result;}
double EnderDragon::getHeadPartYOffset(int partIndex, doubleArray bodyPos, doubleArray partPos){ double result = 0.0; if( getSynchedAction() == e_EnderdragonAction_Landing || getSynchedAction() == e_EnderdragonAction_Takeoff ) { int eggHeight = level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS); //level->getHeightmap(4,4); float dist = sqrt( distanceToSqr(PODIUM_X_POS, eggHeight, PODIUM_Z_POS) )/4; if( dist < 1.0f ) dist = 1.0f; result = partIndex / dist; //app.DebugPrintf("getHeadPartYOffset - dist = %f, result = %f (%d)\n", dist, result, partIndex); } else if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) { result = partIndex; } else { if(partIndex == 6) { result = 0.0; } else { result = partPos[1] - bodyPos[1]; } } //app.DebugPrintf("Part %d is at %f\n", partIndex, result); return result;}
double EnderDragon::getHeadPartYRotDiff(int partIndex, doubleArray bodyPos, doubleArray partPos){ double result = 0.0; //if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || // getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || // getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) //{ // result = m_headYRot / (7 - partIndex); //} //else { result = partPos[0] - bodyPos[0]; } //app.DebugPrintf("Part %d is at %f\n", partIndex, result); return result;}
Vec3 *EnderDragon::getHeadLookVector(float a){ Vec3 *result = NULL;
if( getSynchedAction() == e_EnderdragonAction_Landing || getSynchedAction() == e_EnderdragonAction_Takeoff ) { int eggHeight = level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS); //level->getHeightmap(4,4); float dist = sqrt(distanceToSqr(PODIUM_X_POS, eggHeight, PODIUM_Z_POS))/4; if( dist < 1.0f ) dist = 1.0f; // The 6.0f is dragon->getHeadPartYOffset(6, start, p) float yOffset = 6.0f / dist;
double xRotTemp = xRot; double rotScale = 1.5f; xRot = -yOffset * rotScale * 5.0f;
double yRotTemp = yRot; yRot += getHeadYRotDiff(a);
result = getViewVector(a);
xRot = xRotTemp; yRot = yRotTemp; } else if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) { double xRotTemp = xRot; double rotScale = 1.5f; // The 6.0f is dragon->getHeadPartYOffset(6, start, p) xRot = -6.0f * rotScale * 5.0f;
double yRotTemp = yRot; yRot += getHeadYRotDiff(a);
result = getViewVector(a);
xRot = xRotTemp; yRot = yRotTemp; } else { result = getViewVector(a); } return result;}