the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 584 lines 14 kB view raw
1#include "stdafx.h" 2#include "net.minecraft.world.h" 3#include "net.minecraft.world.damagesource.h" 4#include "net.minecraft.world.entity.ai.attributes.h" 5#include "net.minecraft.world.entity.ai.goal.h" 6#include "net.minecraft.world.entity.ai.goal.target.h" 7#include "net.minecraft.world.entity.ai.navigation.h" 8#include "net.minecraft.world.entity.monster.h" 9#include "net.minecraft.world.entity.projectile.h" 10#include "net.minecraft.world.entity.h" 11#include "net.minecraft.world.item.h" 12#include "net.minecraft.world.level.h" 13#include "net.minecraft.world.level.tile.h" 14#include "net.minecraft.world.phys.h" 15#include "Mth.h" 16 17#include "SoundTypes.h" 18 19#include "WitherBoss.h" 20 21bool LivingEntitySelector::matches(shared_ptr<Entity> entity) const 22{ 23 if ( entity->instanceof(eTYPE_LIVINGENTITY) ) 24 { 25 return dynamic_pointer_cast<LivingEntity>(entity)->getMobType() != UNDEAD; 26 } 27 else 28 { 29 return false; 30 } 31} 32 33EntitySelector *WitherBoss::livingEntitySelector = new LivingEntitySelector(); 34 35WitherBoss::WitherBoss(Level *level) : Monster(level) 36{ 37 // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that 38 // the derived version of the function is called 39 this->defineSynchedData(); 40 registerAttributes(); 41 setHealth(getMaxHealth()); 42 43 for(unsigned int i = 0; i < 2; ++i) 44 { 45 xRotHeads[i] = 0.0f; 46 yRotHeads[i] = 0.0f; 47 xRotOHeads[i] = 0.0f; 48 yRotOHeads[i] = 0.0f; 49 nextHeadUpdate[i] = 0; 50 idleHeadUpdates[i] = 0; 51 } 52 destroyBlocksTick = 0; 53 54 setSize(.9f, 4); 55 56 // noPhysics = true; 57 fireImmune = true; 58 59 // noCulling = true; 60 61 getNavigation()->setCanFloat(true); 62 63 goalSelector.addGoal(0, new FloatGoal(this)); 64 goalSelector.addGoal(2, new RangedAttackGoal(this, this, 1.0, SharedConstants::TICKS_PER_SECOND * 2, 20)); 65 66 goalSelector.addGoal(5, new RandomStrollGoal(this, 1.0)); 67 goalSelector.addGoal(6, new LookAtPlayerGoal(this, typeid(Player), 8)); 68 goalSelector.addGoal(7, new RandomLookAroundGoal(this)); 69 70 targetSelector.addGoal(1, new HurtByTargetGoal(this, false)); 71 targetSelector.addGoal(2, new NearestAttackableTargetGoal(this, typeid(Mob), 0, false, false, livingEntitySelector)); 72 73 xpReward = Enemy::XP_REWARD_BOSS; 74} 75 76void WitherBoss::defineSynchedData() 77{ 78 Monster::defineSynchedData(); 79 80 entityData->define(DATA_TARGET_A, (int)0); 81 entityData->define(DATA_TARGET_B, (int)0); 82 entityData->define(DATA_TARGET_C, (int)0); 83 entityData->define(DATA_ID_INV, (int)0); 84} 85 86void WitherBoss::addAdditonalSaveData(CompoundTag *entityTag) 87{ 88 Monster::addAdditonalSaveData(entityTag); 89 90 entityTag->putInt(L"Invul", getInvulnerableTicks()); 91} 92 93void WitherBoss::readAdditionalSaveData(CompoundTag *tag) 94{ 95 Monster::readAdditionalSaveData(tag); 96 97 setInvulnerableTicks(tag->getInt(L"Invul")); 98} 99 100float WitherBoss::getShadowHeightOffs() 101{ 102 return bbHeight / 8; 103} 104 105int WitherBoss::getAmbientSound() 106{ 107 return eSoundType_MOB_WITHER_IDLE; //"mob.wither.idle"; 108} 109 110int WitherBoss::getHurtSound() 111{ 112 return eSoundType_MOB_WITHER_HURT; //"mob.wither.hurt"; 113} 114 115int WitherBoss::getDeathSound() 116{ 117 return eSoundType_MOB_WITHER_DEATH; //"mob.wither.death"; 118} 119 120void WitherBoss::aiStep() 121{ 122 yd *= 0.6f; 123 124 if (!level->isClientSide && getAlternativeTarget(0) > 0) 125 { 126 shared_ptr<Entity> e = level->getEntity(getAlternativeTarget(0)); 127 if (e != NULL) 128 { 129 if ((y < e->y) || (!isPowered() && y < (e->y + 5))) 130 { 131 if (yd < 0) 132 { 133 yd = 0; 134 } 135 yd += (.5f - yd) * .6f; 136 } 137 138 double xdist = e->x - x; 139 double zdist = e->z - z; 140 double distSqr = xdist * xdist + zdist * zdist; 141 if (distSqr > 9) 142 { 143 double sd = Mth::sqrt(distSqr); 144 xd += ((xdist / sd) * .5f - xd) * .6f; 145 zd += ((zdist / sd) * .5f - zd) * .6f; 146 } 147 } 148 } 149 if ((xd * xd + zd * zd) > .05f) 150 { 151 yRot = (float) atan2(zd, xd) * Mth::RADDEG - 90; 152 } 153 Monster::aiStep(); 154 155 156 for (int i = 0; i < 2; i++) 157 { 158 yRotOHeads[i] = yRotHeads[i]; 159 xRotOHeads[i] = xRotHeads[i]; 160 } 161 162 for (int i = 0; i < 2; i++) 163 { 164 int entityId = getAlternativeTarget(i + 1); 165 shared_ptr<Entity> e = nullptr; 166 if (entityId > 0) 167 { 168 e = level->getEntity(entityId); 169 } 170 if (e != NULL) 171 { 172 double hx = getHeadX(i + 1); 173 double hy = getHeadY(i + 1); 174 double hz = getHeadZ(i + 1); 175 176 double xd = e->x - hx; 177 double yd = e->y + e->getHeadHeight() - hy; 178 double zd = e->z - hz; 179 double sd = Mth::sqrt(xd * xd + zd * zd); 180 181 float yRotD = (float) (atan2(zd, xd) * 180 / PI) - 90; 182 float xRotD = (float) -(atan2(yd, sd) * 180 / PI); 183 xRotHeads[i] = rotlerp(xRotHeads[i], xRotD, 40); 184 yRotHeads[i] = rotlerp(yRotHeads[i], yRotD, 10); 185 186 187 } 188 else 189 { 190 yRotHeads[i] = rotlerp(yRotHeads[i], yBodyRot, 10); 191 } 192 } 193 bool _isPowered = isPowered(); 194 for (int i = 0; i < 3; i++) 195 { 196 double hx = getHeadX(i); 197 double hy = getHeadY(i); 198 double hz = getHeadZ(i); 199 200 level->addParticle(eParticleType_smoke, hx + random->nextGaussian() * .3f, hy + random->nextGaussian() * .3f, hz + random->nextGaussian() * .3f, 0, 0, 0); 201 if (_isPowered && level->random->nextInt(4) == 0) 202 { 203 level->addParticle(eParticleType_mobSpell, hx + random->nextGaussian() * .3f, hy + random->nextGaussian() * .3f, hz + random->nextGaussian() * .3f, .7f, .7f, .5f); 204 } 205 } 206 if (getInvulnerableTicks() > 0) 207 { 208 for (int i = 0; i < 3; i++) 209 { 210 level->addParticle(eParticleType_mobSpell, x + random->nextGaussian() * 1.0f, y + random->nextFloat() * 3.3f, z + random->nextGaussian() * 1.0f, .7f, .7f, .9f); 211 } 212 } 213} 214 215void WitherBoss::newServerAiStep() 216{ 217 if (getInvulnerableTicks() > 0) 218 { 219 int newCount = getInvulnerableTicks() - 1; 220 221 if (newCount <= 0) 222 { 223 level->explode(shared_from_this(), x, y + getHeadHeight(), z, 7, false, level->getGameRules()->getBoolean(GameRules::RULE_MOBGRIEFING)); 224 level->globalLevelEvent(LevelEvent::SOUND_WITHER_BOSS_SPAWN, (int) x, (int) y, (int) z, 0); 225 } 226 227 setInvulnerableTicks(newCount); 228 if (tickCount % 10 == 0) 229 { 230 heal(10); 231 } 232 233 return; 234 } 235 236 Monster::newServerAiStep(); 237 238 for (int i = 1; i < 3; i++) 239 { 240 if (tickCount >= nextHeadUpdate[i - 1]) 241 { 242 nextHeadUpdate[i - 1] = tickCount + SharedConstants::TICKS_PER_SECOND / 2 + random->nextInt(SharedConstants::TICKS_PER_SECOND / 2); 243 244 if (level->difficulty >= Difficulty::NORMAL && idleHeadUpdates[i - 1]++ > 15) 245 { 246 float hrange = 10; 247 float vrange = 5; 248 double xt = Mth::nextDouble(random, x - hrange, x + hrange); 249 double yt = Mth::nextDouble(random, y - vrange, y + vrange); 250 double zt = Mth::nextDouble(random, z - hrange, z + hrange); 251 performRangedAttack(i + 1, xt, yt, zt, true); 252 idleHeadUpdates[i - 1] = 0; 253 } 254 255 int headTarget = getAlternativeTarget(i); 256 if (headTarget > 0) 257 { 258 shared_ptr<Entity> current = level->getEntity(headTarget); 259 260 // 4J: Added check for instance of living entity, had a problem with IDs being recycled to other entities 261 if (current == NULL || !current->instanceof(eTYPE_LIVINGENTITY) || !current->isAlive() || distanceToSqr(current) > 30 * 30 || !canSee(current)) 262 { 263 setAlternativeTarget(i, 0); 264 } 265 else 266 { 267 performRangedAttack(i + 1, dynamic_pointer_cast<LivingEntity>(current) ); 268 nextHeadUpdate[i - 1] = tickCount + SharedConstants::TICKS_PER_SECOND * 2 + random->nextInt(SharedConstants::TICKS_PER_SECOND); 269 idleHeadUpdates[i - 1] = 0; 270 } 271 } 272 else 273 { 274 vector<shared_ptr<Entity> > *entities = level->getEntitiesOfClass(typeid(LivingEntity), bb->grow(20, 8, 20), livingEntitySelector); 275 // randomly try to find a target 10 times 276 for (int attempt = 0; attempt < 10 && !entities->empty(); attempt++) 277 { 278 int randomIndex = random->nextInt(entities->size()); 279 shared_ptr<LivingEntity> selected = dynamic_pointer_cast<LivingEntity>( entities->at(randomIndex) ); 280 281 if (selected != shared_from_this() && selected->isAlive() && canSee(selected)) 282 { 283 if ( selected->instanceof(eTYPE_PLAYER) ) 284 { 285 if (!dynamic_pointer_cast<Player>(selected)->abilities.invulnerable) 286 { 287 assert(selected->instanceof(eTYPE_LIVINGENTITY)); 288 setAlternativeTarget(i, selected->entityId); 289 } 290 break; 291 } 292 else 293 { 294 assert(selected->instanceof(eTYPE_LIVINGENTITY)); 295 setAlternativeTarget(i, selected->entityId); 296 break; 297 } 298 } 299 // don't pick this again 300 entities->erase(entities->begin() + randomIndex); 301 } 302 delete entities; 303 } 304 } 305 } 306 if (getTarget() != NULL) 307 { 308 assert(getTarget()->instanceof(eTYPE_LIVINGENTITY)); 309 setAlternativeTarget(0, getTarget()->entityId); 310 } 311 else 312 { 313 setAlternativeTarget(0, 0); 314 } 315 316 if (destroyBlocksTick > 0) 317 { 318 destroyBlocksTick--; 319 320 if (destroyBlocksTick == 0 && level->getGameRules()->getBoolean(GameRules::RULE_MOBGRIEFING)) 321 { 322 // destroy all blocks that are within 1 range, counting from 323 // feet and 3 blocks up 324 325 int feet = Mth::floor(y); 326 int ox = Mth::floor(x); 327 int oz = Mth::floor(z); 328 bool destroyed = false; 329 330 for (int xStep = -1; xStep <= 1; xStep++) 331 { 332 for (int zStep = -1; zStep <= 1; zStep++) 333 { 334 for (int yStep = 0; yStep <= 3; yStep++) 335 { 336 int tx = ox + xStep; 337 int ty = feet + yStep; 338 int tz = oz + zStep; 339 int tile = level->getTile(tx, ty, tz); 340 if (tile > 0 && tile != Tile::unbreakable_Id && tile != Tile::endPortalTile_Id && tile != Tile::endPortalFrameTile_Id) 341 { 342 destroyed = level->destroyTile(tx, ty, tz, true) || destroyed; 343 } 344 } 345 } 346 } 347 if (destroyed) 348 { 349 level->levelEvent(nullptr, LevelEvent::SOUND_ZOMBIE_DOOR_CRASH, (int) x, (int) y, (int) z, 0); 350 } 351 } 352 } 353 354 if ((tickCount % (SharedConstants::TICKS_PER_SECOND)) == 0) 355 { 356 heal(1); 357 } 358} 359 360void WitherBoss::makeInvulnerable() 361{ 362 setInvulnerableTicks(SharedConstants::TICKS_PER_SECOND * 11); 363 setHealth(getMaxHealth() / 3); 364} 365 366void WitherBoss::makeStuckInWeb() 367{ 368} 369 370int WitherBoss::getArmorValue() 371{ 372 return 4; 373} 374 375double WitherBoss::getHeadX(int index) 376{ 377 if (index <= 0) 378 { 379 return x; 380 } 381 float headAngle = (yBodyRot + 180 * (index - 1)) / 180.0f * PI; 382 float cos = Mth::cos(headAngle); 383 return x + cos * 1.3; 384} 385 386double WitherBoss::getHeadY(int index) 387{ 388 if (index <= 0) 389 { 390 return y + 3; 391 } 392 else 393 { 394 return y + 2.2; 395 } 396} 397 398double WitherBoss::getHeadZ(int index) 399{ 400 if (index <= 0) 401 { 402 return z; 403 } 404 float headAngle = (yBodyRot + 180 * (index - 1)) / 180.0f * PI; 405 float sin = Mth::sin(headAngle); 406 return z + sin * 1.3; 407} 408 409float WitherBoss::rotlerp(float a, float b, float max) 410{ 411 float diff = Mth::wrapDegrees(b - a); 412 if (diff > max) 413 { 414 diff = max; 415 } 416 if (diff < -max) 417 { 418 diff = -max; 419 } 420 return a + diff; 421} 422 423void WitherBoss::performRangedAttack(int head, shared_ptr<LivingEntity> target) 424{ 425 performRangedAttack(head, target->x, target->y + target->getHeadHeight() * .5, target->z, head == 0 && random->nextFloat() < 0.001f); 426} 427 428void WitherBoss::performRangedAttack(int head, double tx, double ty, double tz, bool dangerous) 429{ 430 level->levelEvent(nullptr, LevelEvent::SOUND_WITHER_BOSS_SHOOT, (int) x, (int) y, (int) z, 0); 431 432 double hx = getHeadX(head); 433 double hy = getHeadY(head); 434 double hz = getHeadZ(head); 435 436 double xd = tx - hx; 437 double yd = ty - hy; 438 double zd = tz - hz; 439 440 shared_ptr<WitherSkull> ie = shared_ptr<WitherSkull>( new WitherSkull(level, dynamic_pointer_cast<LivingEntity>(shared_from_this()), xd, yd, zd) ); 441 if (dangerous) ie->setDangerous(true); 442 ie->y = hy; 443 ie->x = hx; 444 ie->z = hz; 445 level->addEntity(ie); 446} 447 448void WitherBoss::performRangedAttack(shared_ptr<LivingEntity> target, float power) 449{ 450 performRangedAttack(0, target); 451} 452 453bool WitherBoss::hurt(DamageSource *source, float dmg) 454{ 455 if (isInvulnerable()) return false; 456 if (source == DamageSource::drown) return false; 457 if (getInvulnerableTicks() > 0) 458 { 459 return false; 460 } 461 462 if (isPowered()) 463 { 464 shared_ptr<Entity> directEntity = source->getDirectEntity(); 465 if (directEntity != NULL && directEntity->GetType() == eTYPE_ARROW) 466 { 467 return false; 468 } 469 } 470 471 shared_ptr<Entity> sourceEntity = source->getEntity(); 472 if (sourceEntity != NULL) 473 { 474 if ( sourceEntity->instanceof(eTYPE_PLAYER) ) 475 { 476 } 477 else if ( sourceEntity->instanceof(eTYPE_LIVINGENTITY) && dynamic_pointer_cast<LivingEntity>(sourceEntity)->getMobType() == getMobType()) 478 { 479 // can't be harmed by other undead 480 return false; 481 } 482 } 483 if (destroyBlocksTick <= 0) 484 { 485 destroyBlocksTick = SharedConstants::TICKS_PER_SECOND; 486 } 487 488 for (int i = 0; i < IDLE_HEAD_UPDATES_SIZE; i++) 489 { 490 idleHeadUpdates[i] += 3; 491 } 492 493 return Monster::hurt(source, dmg); 494} 495 496void WitherBoss::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) 497{ 498 spawnAtLocation(Item::netherStar_Id, 1); 499} 500 501void WitherBoss::checkDespawn() 502{ 503 noActionTime = 0; 504} 505 506int WitherBoss::getLightColor(float a) 507{ 508 return SharedConstants::FULLBRIGHT_LIGHTVALUE; 509} 510 511bool WitherBoss::isPickable() 512{ 513 return !removed; 514} 515 516void WitherBoss::causeFallDamage(float distance) 517{ 518} 519 520void WitherBoss::addEffect(MobEffectInstance *newEffect) 521{ 522 // do nothing 523} 524 525bool WitherBoss::useNewAi() 526{ 527 return true; 528} 529 530void WitherBoss::registerAttributes() 531{ 532 Monster::registerAttributes(); 533 534 getAttribute(SharedMonsterAttributes::MAX_HEALTH)->setBaseValue(300); 535 getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.6f); 536 537 // 4J Stu - Don't make it so far! 538 //getAttribute(SharedMonsterAttributes::FOLLOW_RANGE)->setBaseValue(40); 539} 540 541float WitherBoss::getHeadYRot(int i) 542{ 543 return yRotHeads[i]; 544} 545 546float WitherBoss::getHeadXRot(int i) 547{ 548 return xRotHeads[i]; 549} 550 551int WitherBoss::getInvulnerableTicks() 552{ 553 return entityData->getInteger(DATA_ID_INV); 554} 555 556void WitherBoss::setInvulnerableTicks(int invulnerableTicks) 557{ 558 entityData->set(DATA_ID_INV, invulnerableTicks); 559} 560 561int WitherBoss::getAlternativeTarget(int headIndex) 562{ 563 return entityData->getInteger(DATA_TARGET_A + headIndex); 564} 565 566void WitherBoss::setAlternativeTarget(int headIndex, int entityId) 567{ 568 entityData->set(DATA_TARGET_A + headIndex, entityId); 569} 570 571bool WitherBoss::isPowered() 572{ 573 return getHealth() <= getMaxHealth() / 2; 574} 575 576MobType WitherBoss::getMobType() 577{ 578 return UNDEAD; 579} 580 581void WitherBoss::ride(shared_ptr<Entity> e) 582{ 583 riding = nullptr; 584}