the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "net.minecraft.world.level.h"
3#include "net.minecraft.world.level.tile.h"
4#include "net.minecraft.world.h"
5#include "net.minecraft.world.item.h"
6#include "net.minecraft.world.damagesource.h"
7#include "net.minecraft.world.effect.h"
8#include "net.minecraft.world.entity.ai.attributes.h"
9#include "net.minecraft.world.entity.ai.goal.h"
10#include "net.minecraft.world.entity.ai.goal.target.h"
11#include "net.minecraft.world.entity.ai.navigation.h"
12#include "net.minecraft.world.entity.monster.h"
13#include "net.minecraft.world.entity.npc.h"
14#include "net.minecraft.world.entity.player.h"
15#include "Zombie.h"
16#include "GenericStats.h"
17#include "..\Minecraft.Client\Textures.h"
18#include "net.minecraft.world.entity.h"
19#include "JavaMath.h"
20#include "SoundTypes.h"
21
22 Attribute *Zombie::SPAWN_REINFORCEMENTS_CHANCE = (new RangedAttribute(eAttributeId_ZOMBIE_SPAWNREINFORCEMENTS, 0, 0, 1));
23 AttributeModifier *Zombie::SPEED_MODIFIER_BABY = new AttributeModifier(eModifierId_MOB_ZOMBIE_BABYSPEED, 0.5f, AttributeModifier::OPERATION_MULTIPLY_BASE);
24
25const float Zombie::ZOMBIE_LEADER_CHANCE = 0.05f;
26
27
28Zombie::Zombie(Level *level) : Monster( level )
29{
30 // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that
31 // the derived version of the function is called
32 this->defineSynchedData();
33 registerAttributes();
34 setHealth(getMaxHealth());
35
36 villagerConversionTime = 0;
37
38 getNavigation()->setCanOpenDoors(true);
39 goalSelector.addGoal(0, new FloatGoal(this));
40 goalSelector.addGoal(1, new BreakDoorGoal(this));
41 goalSelector.addGoal(2, new MeleeAttackGoal(this, eTYPE_PLAYER, 1.0, false));
42 goalSelector.addGoal(3, new MeleeAttackGoal(this, eTYPE_VILLAGER, 1.0, true));
43 goalSelector.addGoal(4, new MoveTowardsRestrictionGoal(this, 1.0));
44 goalSelector.addGoal(5, new MoveThroughVillageGoal(this, 1.0, false));
45 goalSelector.addGoal(6, new RandomStrollGoal(this, 1.0));
46 goalSelector.addGoal(7, new LookAtPlayerGoal(this, typeid(Player), 8));
47 goalSelector.addGoal(7, new RandomLookAroundGoal(this));
48
49 targetSelector.addGoal(1, new HurtByTargetGoal(this, true));
50 targetSelector.addGoal(2, new NearestAttackableTargetGoal(this, typeid(Player), 0, true));
51 targetSelector.addGoal(2, new NearestAttackableTargetGoal(this, typeid(Villager), 0, false));
52}
53
54void Zombie::registerAttributes()
55{
56 Monster::registerAttributes();
57
58 // 4J Stu - Don't make it so far!
59 //getAttribute(SharedMonsterAttributes::FOLLOW_RANGE)->setBaseValue(40);
60
61 getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.23f);
62 getAttribute(SharedMonsterAttributes::ATTACK_DAMAGE)->setBaseValue(3);
63
64 getAttributes()->registerAttribute(SPAWN_REINFORCEMENTS_CHANCE)->setBaseValue(random->nextDouble() * 0.10f);
65}
66
67void Zombie::defineSynchedData()
68{
69 Monster::defineSynchedData();
70
71 getEntityData()->define(DATA_BABY_ID, (byte) 0);
72 getEntityData()->define(DATA_VILLAGER_ID, (byte) 0);
73 getEntityData()->define(DATA_CONVERTING_ID, (byte) 0);
74}
75
76int Zombie::getArmorValue()
77{
78 int value = Monster::getArmorValue() + 2;
79 if (value > 20) value = 20;
80 return value;
81}
82
83bool Zombie::useNewAi()
84{
85 return true;
86}
87
88bool Zombie::isBaby()
89{
90 return getEntityData()->getByte(DATA_BABY_ID) == (byte) 1;
91}
92
93void Zombie::setBaby(bool baby)
94{
95 getEntityData()->set(DATA_BABY_ID, (byte) (baby ? 1 : 0));
96
97 if (level != NULL && !level->isClientSide)
98 {
99 AttributeInstance *speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED);
100 speed->removeModifier(SPEED_MODIFIER_BABY);
101 if (baby)
102 {
103 speed->addModifier(new AttributeModifier(*SPEED_MODIFIER_BABY));
104 }
105 }
106}
107
108bool Zombie::isVillager()
109{
110 return getEntityData()->getByte(DATA_VILLAGER_ID) == (byte) 1;
111}
112
113void Zombie::setVillager(bool villager)
114{
115 getEntityData()->set(DATA_VILLAGER_ID, (byte) (villager ? 1 : 0));
116}
117
118void Zombie::aiStep()
119{
120 if (level->isDay() && !level->isClientSide && !isBaby())
121 {
122 float br = getBrightness(1);
123 if (br > 0.5f && random->nextFloat() * 30 < (br - 0.4f) * 2 && level->canSeeSky(Mth::floor(x), (int)floor( y + 0.5 ), Mth::floor(z)))
124 {
125 bool burn = true;
126
127 shared_ptr<ItemInstance> helmet = getCarried(SLOT_HELM);
128 if (helmet != NULL)
129 {
130 if (helmet->isDamageableItem())
131 {
132 helmet->setAuxValue(helmet->getDamageValue() + random->nextInt(2));
133 if (helmet->getDamageValue() >= helmet->getMaxDamage())
134 {
135 breakItem(helmet);
136 setEquippedSlot(SLOT_HELM, nullptr);
137 }
138 }
139
140 burn = false;
141 }
142
143 if (burn)
144 {
145 setOnFire(8);
146 }
147 }
148 }
149 Monster::aiStep();
150}
151
152bool Zombie::hurt(DamageSource *source, float dmg)
153{
154 if (Monster::hurt(source, dmg))
155 {
156 shared_ptr<LivingEntity> target = getTarget();
157 if ( (target == NULL) && getAttackTarget() != NULL && getAttackTarget()->instanceof(eTYPE_LIVINGENTITY) ) target = dynamic_pointer_cast<LivingEntity>( getAttackTarget() );
158 if ( (target == NULL) && source->getEntity() != NULL && source->getEntity()->instanceof(eTYPE_LIVINGENTITY) ) target = dynamic_pointer_cast<LivingEntity>( source->getEntity() );
159
160 if ( (target != NULL) && level->difficulty >= Difficulty::HARD && random->nextFloat() < getAttribute(SPAWN_REINFORCEMENTS_CHANCE)->getValue())
161 {
162 int x = Mth::floor(this->x);
163 int y = Mth::floor(this->y);
164 int z = Mth::floor(this->z);
165 shared_ptr<Zombie> reinforcement = shared_ptr<Zombie>( new Zombie(level) );
166
167 for (int i = 0; i < REINFORCEMENT_ATTEMPTS; i++)
168 {
169 int xt = x + Mth::nextInt(random, REINFORCEMENT_RANGE_MIN, REINFORCEMENT_RANGE_MAX) * Mth::nextInt(random, -1, 1);
170 int yt = y + Mth::nextInt(random, REINFORCEMENT_RANGE_MIN, REINFORCEMENT_RANGE_MAX) * Mth::nextInt(random, -1, 1);
171 int zt = z + Mth::nextInt(random, REINFORCEMENT_RANGE_MIN, REINFORCEMENT_RANGE_MAX) * Mth::nextInt(random, -1, 1);
172
173 if (level->isTopSolidBlocking(xt, yt - 1, zt) && level->getRawBrightness(xt, yt, zt) < 10)
174 {
175 reinforcement->setPos(xt, yt, zt);
176
177 if (level->isUnobstructed(reinforcement->bb) && level->getCubes(reinforcement, reinforcement->bb)->empty() && !level->containsAnyLiquid(reinforcement->bb))
178 {
179 level->addEntity(reinforcement);
180 reinforcement->setTarget(target);
181 reinforcement->finalizeMobSpawn(NULL);
182
183 getAttribute(SPAWN_REINFORCEMENTS_CHANCE)->addModifier(new AttributeModifier(-0.05f, AttributeModifier::OPERATION_ADDITION));
184 reinforcement->getAttribute(SPAWN_REINFORCEMENTS_CHANCE)->addModifier(new AttributeModifier(-0.05f, AttributeModifier::OPERATION_ADDITION));
185 break;
186 }
187 }
188 }
189 }
190
191 return true;
192 }
193
194 return false;
195}
196
197void Zombie::tick()
198{
199 if (!level->isClientSide && isConverting())
200 {
201 int amount = getConversionProgress();
202
203 villagerConversionTime -= amount;
204
205 if (villagerConversionTime <= 0)
206 {
207 finishConversion();
208 }
209 }
210
211 Monster::tick();
212}
213
214bool Zombie::doHurtTarget(shared_ptr<Entity> target)
215{
216 bool result = Monster::doHurtTarget(target);
217
218 if (result)
219 {
220 if (getCarriedItem() == NULL && isOnFire() && random->nextFloat() < level->difficulty * 0.3f)
221 {
222 target->setOnFire(2 * level->difficulty);
223 }
224 }
225
226 return result;
227}
228
229int Zombie::getAmbientSound()
230{
231 return eSoundType_MOB_ZOMBIE_AMBIENT;
232}
233
234int Zombie::getHurtSound()
235{
236 return eSoundType_MOB_ZOMBIE_HURT;
237}
238
239int Zombie::getDeathSound()
240{
241 return eSoundType_MOB_ZOMBIE_DEATH;
242}
243
244int Zombie::getDeathLoot()
245{
246 return Item::rotten_flesh_Id;
247}
248
249void Zombie::playStepSound(int xt, int yt, int zt, int t)
250{
251 playSound(eSoundType_MOB_ZOMBIE_STEP, 0.15f, 1);
252}
253
254MobType Zombie::getMobType()
255{
256 return UNDEAD;
257}
258
259void Zombie::dropRareDeathLoot(int rareLootLevel)
260{
261 switch (random->nextInt(3))
262 {
263 case 0:
264 spawnAtLocation(Item::ironIngot_Id, 1);
265 break;
266 case 1:
267 spawnAtLocation(Item::carrots_Id, 1);
268 break;
269 case 2:
270 spawnAtLocation(Item::potato_Id, 1);
271 break;
272 }
273}
274
275void Zombie::populateDefaultEquipmentSlots()
276{
277 Monster::populateDefaultEquipmentSlots();
278
279 if (random->nextFloat() < (level->difficulty == Difficulty::HARD ? 0.05f : 0.01f))
280 {
281 int rand = random->nextInt(3);
282 if (rand == 0)
283 {
284 setEquippedSlot(SLOT_WEAPON, shared_ptr<ItemInstance>( new ItemInstance(Item::sword_iron)) );
285 }
286 else
287 {
288 setEquippedSlot(SLOT_WEAPON, shared_ptr<ItemInstance>( new ItemInstance(Item::shovel_iron)) );
289 }
290 }
291}
292
293void Zombie::addAdditonalSaveData(CompoundTag *tag)
294{
295 Monster::addAdditonalSaveData(tag);
296
297 if (isBaby()) tag->putBoolean(L"IsBaby", true);
298 if (isVillager()) tag->putBoolean(L"IsVillager", true);
299 tag->putInt(L"ConversionTime", isConverting() ? villagerConversionTime : -1);
300}
301
302void Zombie::readAdditionalSaveData(CompoundTag *tag)
303{
304 Monster::readAdditionalSaveData(tag);
305
306 if (tag->getBoolean(L"IsBaby")) setBaby(true);
307 if (tag->getBoolean(L"IsVillager")) setVillager(true);
308 if (tag->contains(L"ConversionTime") && tag->getInt(L"ConversionTime") > -1) startConverting(tag->getInt(L"ConversionTime"));
309}
310
311void Zombie::killed(shared_ptr<LivingEntity> mob)
312{
313 Monster::killed(mob);
314
315 if ( level->difficulty >= Difficulty::NORMAL && (mob->GetType() == eTYPE_VILLAGER) ) // 4J-JEV: Villager isn't a non-terminal class, no need to instanceof.
316 {
317 if (level->difficulty == Difficulty::NORMAL && random->nextBoolean()) return;
318
319 shared_ptr<Zombie> zombie = shared_ptr<Zombie>(new Zombie(level));
320 zombie->copyPosition(mob);
321 level->removeEntity(mob);
322 zombie->finalizeMobSpawn(NULL);
323 zombie->setVillager(true);
324 if (mob->isBaby()) zombie->setBaby(true);
325 level->addEntity(zombie);
326
327 level->levelEvent(nullptr, LevelEvent::SOUND_ZOMBIE_INFECTED, (int) x, (int) y, (int) z, 0);
328 }
329}
330
331MobGroupData *Zombie::finalizeMobSpawn(MobGroupData *groupData, int extraData /*= 0*/) // 4J Added extraData param
332{
333 groupData = Monster::finalizeMobSpawn(groupData);
334 float difficulty = level->getDifficulty(x, y, z);
335
336 setCanPickUpLoot(random->nextFloat() < MAX_PICKUP_LOOT_CHANCE * difficulty);
337
338 if (groupData == NULL)
339 {
340 groupData = new ZombieGroupData(level->random->nextFloat() < 0.05f, level->random->nextFloat() < 0.05f);
341 }
342
343 if ( dynamic_cast<ZombieGroupData *>( groupData ) != NULL)
344 {
345 ZombieGroupData *zombieData = (ZombieGroupData *) groupData;
346
347 if (zombieData->isVillager)
348 {
349 setVillager(true);
350 }
351
352 if (zombieData->isBaby)
353 {
354 setBaby(true);
355 }
356 }
357
358 populateDefaultEquipmentSlots();
359 populateDefaultEquipmentEnchantments();
360
361 if (getCarried(SLOT_HELM) == NULL)
362 {
363 // [EB]: We have this code in quite some places, shouldn't we set
364 // something like this globally?
365 if (Calendar::GetMonth() + 1 == 10 && Calendar::GetDayOfMonth() == 31 && random->nextFloat() < 0.25f)
366 {
367 // Halloween! OooOOo! 25% of all skeletons/zombies can wear
368 // pumpkins on their heads.
369 setEquippedSlot(SLOT_HELM, shared_ptr<ItemInstance>( new ItemInstance(random->nextFloat() < 0.1f ? Tile::litPumpkin : Tile::pumpkin) ));
370 dropChances[SLOT_HELM] = 0;
371 }
372 }
373
374 getAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE)->addModifier(new AttributeModifier(random->nextDouble() * 0.05f, AttributeModifier::OPERATION_ADDITION));
375
376 // 4J Stu - Take this out, it's not good and nobody will notice. Also not great for performance.
377 //getAttribute(SharedMonsterAttributes::FOLLOW_RANGE)->addModifier(new AttributeModifier(random->nextDouble() * 1.50f, AttributeModifier::OPERATION_MULTIPLY_TOTAL));
378
379 if (random->nextFloat() < difficulty * ZOMBIE_LEADER_CHANCE)
380 {
381 getAttribute(SPAWN_REINFORCEMENTS_CHANCE)->addModifier(new AttributeModifier(random->nextDouble() * 0.25f + 0.50f, AttributeModifier::OPERATION_ADDITION));
382 getAttribute(SharedMonsterAttributes::MAX_HEALTH)->addModifier(new AttributeModifier(random->nextDouble() * 3.0f + 1.0f, AttributeModifier::OPERATION_MULTIPLY_TOTAL));
383 }
384
385 return groupData;
386}
387
388bool Zombie::mobInteract(shared_ptr<Player> player)
389{
390 shared_ptr<ItemInstance> item = player->getSelectedItem();
391
392 if (item != NULL && item->getItem() == Item::apple_gold && item->getAuxValue() == 0 && isVillager() && hasEffect(MobEffect::weakness))
393 {
394 if (!player->abilities.instabuild) item->count--;
395 if (item->count <= 0)
396 {
397 player->inventory->setItem(player->inventory->selected, nullptr);
398 }
399
400 if (!level->isClientSide)
401 {
402 startConverting(random->nextInt(VILLAGER_CONVERSION_WAIT_MAX - VILLAGER_CONVERSION_WAIT_MIN + 1) + VILLAGER_CONVERSION_WAIT_MIN);
403
404 // 4J-JEV, award achievement here, as it is impractical to award when the zombie is actually cured.
405 player->awardStat(GenericStats::zombieDoctor(),GenericStats::param_zombieDoctor());
406 }
407
408 return true;
409 }
410
411 return false;
412}
413
414void Zombie::startConverting(int time)
415{
416 villagerConversionTime = time;
417 getEntityData()->set(DATA_CONVERTING_ID, (byte) 1);
418
419 removeEffect(MobEffect::weakness->id);
420 addEffect(new MobEffectInstance(MobEffect::damageBoost->id, time, min(level->difficulty - 1, 0)));
421
422 level->broadcastEntityEvent(shared_from_this(), EntityEvent::ZOMBIE_CONVERTING);
423}
424
425void Zombie::handleEntityEvent(byte id)
426{
427 if (id == EntityEvent::ZOMBIE_CONVERTING)
428 {
429 level->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_MOB_ZOMBIE_REMEDY, 1 + random->nextFloat(), random->nextFloat() * 0.7f + 0.3f, false);
430 }
431 else
432 {
433 Monster::handleEntityEvent(id);
434 }
435}
436
437bool Zombie::removeWhenFarAway()
438{
439 return !isConverting();
440}
441
442bool Zombie::isConverting()
443{
444 return getEntityData()->getByte(DATA_CONVERTING_ID) == (byte) 1;
445}
446
447void Zombie::finishConversion()
448{
449 shared_ptr<Villager> villager = shared_ptr<Villager>(new Villager(level));
450 villager->copyPosition(shared_from_this());
451 villager->finalizeMobSpawn(NULL);
452 villager->setRewardPlayersInVillage();
453 if (isBaby()) villager->setAge(-20 * 60 * 20);
454 level->removeEntity(shared_from_this());
455 level->addEntity(villager);
456
457 villager->addEffect(new MobEffectInstance(MobEffect::confusion->id, SharedConstants::TICKS_PER_SECOND * 10, 0));
458 level->levelEvent(nullptr, LevelEvent::SOUND_ZOMBIE_CONVERTED, (int) x, (int) y, (int) z, 0);
459}
460
461int Zombie::getConversionProgress()
462{
463 int amount = 1;
464
465 if (random->nextFloat() < 0.01f)
466 {
467 int specialBlocksCount = 0;
468
469 for (int xx = (int) x - SPECIAL_BLOCK_RADIUS; xx < (int) x + SPECIAL_BLOCK_RADIUS && specialBlocksCount < MAX_SPECIAL_BLOCKS_COUNT; xx++)
470 {
471 for (int yy = (int) y - SPECIAL_BLOCK_RADIUS; yy < (int) y + SPECIAL_BLOCK_RADIUS && specialBlocksCount < MAX_SPECIAL_BLOCKS_COUNT; yy++)
472 {
473 for (int zz = (int) z - SPECIAL_BLOCK_RADIUS; zz < (int) z + SPECIAL_BLOCK_RADIUS && specialBlocksCount < MAX_SPECIAL_BLOCKS_COUNT; zz++)
474 {
475 int tile = level->getTile(xx, yy, zz);
476
477 if (tile == Tile::ironFence_Id || tile == Tile::bed_Id)
478 {
479 if (random->nextFloat() < 0.3f) amount++;
480 specialBlocksCount++;
481 }
482 }
483 }
484 }
485 }
486 return amount;
487}
488
489Zombie::ZombieGroupData::ZombieGroupData(bool baby, bool villager)
490{
491 isBaby = baby;
492 isVillager = villager;
493}