the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "net.minecraft.world.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}