the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 493 lines 15 kB view raw
1#include "stdafx.h" 2#include "net.minecraft.world.level.h" 3#include "net.minecraft.world.level.tile.h" 4#include "net.minecraft.world.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}