the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 765 lines 26 kB view raw
1#include "stdafx.h" 2#include "com.mojang.nbt.h" 3#include "net.minecraft.world.entity.ai.attributes.h" 4#include "net.minecraft.world.entity.ai.goal.h" 5#include "net.minecraft.world.entity.ai.navigation.h" 6#include "net.minecraft.world.entity.ai.village.h" 7#include "net.minecraft.world.entity.monster.h" 8#include "net.minecraft.world.entity.player.h" 9#include "net.minecraft.world.effect.h" 10#include "net.minecraft.world.entity.h" 11#include "net.minecraft.world.damagesource.h" 12#include "net.minecraft.world.item.h" 13#include "net.minecraft.world.item.enchantment.h" 14#include "net.minecraft.world.item.trading.h" 15#include "net.minecraft.world.level.tile.h" 16#include "net.minecraft.world.level.h" 17#include "..\Minecraft.Client\Textures.h" 18#include "Villager.h" 19 20unordered_map<int, pair<int,int> > Villager::MIN_MAX_VALUES; 21unordered_map<int, pair<int,int> > Villager::MIN_MAX_PRICES; 22 23void Villager::_init(int profession) 24{ 25 // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that 26 // the derived version of the function is called 27 this->defineSynchedData(); 28 registerAttributes(); 29 setHealth(getMaxHealth()); 30 31 setProfession(profession); 32 setSize(.6f, 1.8f); 33 34 villageUpdateInterval = 0; 35 inLove = false; 36 chasing = false; 37 village = weak_ptr<Village>(); 38 39 tradingPlayer = weak_ptr<Player>(); 40 offers = NULL; 41 updateMerchantTimer = 0; 42 addRecipeOnUpdate = false; 43 riches = 0; 44 lastPlayerTradeName = L""; 45 rewardPlayersOnFirstVillage = false; 46 baseRecipeChanceMod = 0.0f; 47 48 getNavigation()->setCanOpenDoors(true); 49 getNavigation()->setAvoidWater(true); 50 51 goalSelector.addGoal(0, new FloatGoal(this)); 52 goalSelector.addGoal(1, new AvoidPlayerGoal(this, typeid(Zombie), 8, 0.6, 0.6)); 53 goalSelector.addGoal(1, new TradeWithPlayerGoal(this)); 54 goalSelector.addGoal(1, new LookAtTradingPlayerGoal(this)); 55 goalSelector.addGoal(2, new MoveIndoorsGoal(this)); 56 goalSelector.addGoal(3, new RestrictOpenDoorGoal(this)); 57 goalSelector.addGoal(4, new OpenDoorGoal(this, true)); 58 goalSelector.addGoal(5, new MoveTowardsRestrictionGoal(this, 0.6)); 59 goalSelector.addGoal(6, new MakeLoveGoal(this)); 60 goalSelector.addGoal(7, new TakeFlowerGoal(this)); 61 goalSelector.addGoal(8, new PlayGoal(this, 0.32)); 62 goalSelector.addGoal(9, new InteractGoal(this, typeid(Player), 3, 1.f)); 63 goalSelector.addGoal(9, new InteractGoal(this, typeid(Villager), 5, 0.02f)); 64 goalSelector.addGoal(9, new RandomStrollGoal(this, 0.6)); 65 goalSelector.addGoal(10, new LookAtPlayerGoal(this, typeid(Mob), 8)); 66} 67 68Villager::Villager(Level *level) : AgableMob(level) 69{ 70 _init(0); 71} 72 73Villager::Villager(Level *level, int profession) : AgableMob(level) 74{ 75 _init(profession); 76} 77 78Villager::~Villager() 79{ 80 delete offers; 81} 82 83void Villager::registerAttributes() 84{ 85 AgableMob::registerAttributes(); 86 87 getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.5f); 88} 89 90bool Villager::useNewAi() 91{ 92 return true; 93} 94 95void Villager::serverAiMobStep() 96{ 97 if (--villageUpdateInterval <= 0) 98 { 99 level->villages->queryUpdateAround(Mth::floor(x), Mth::floor(y), Mth::floor(z)); 100 villageUpdateInterval = 70 + random->nextInt(50); 101 102 shared_ptr<Village> _village = level->villages->getClosestVillage(Mth::floor(x), Mth::floor(y), Mth::floor(z), Villages::MaxDoorDist); 103 village = _village; 104 if (_village == NULL) clearRestriction(); 105 else 106 { 107 Pos *center = _village->getCenter(); 108 restrictTo(center->x, center->y, center->z, (int)((float)_village->getRadius() * 0.6f)); 109 if (rewardPlayersOnFirstVillage) 110 { 111 rewardPlayersOnFirstVillage = false; 112 _village->rewardAllPlayers(5); 113 } 114 } 115 } 116 117 if (!isTrading() && updateMerchantTimer > 0) 118 { 119 updateMerchantTimer--; 120 if (updateMerchantTimer <= 0) 121 { 122 if (addRecipeOnUpdate) 123 { 124 // improve max uses for all obsolete recipes 125 if (offers->size() > 0) 126 { 127 //for (MerchantRecipe recipe : offers) 128 for(AUTO_VAR(it, offers->begin()); it != offers->end(); ++it) 129 { 130 MerchantRecipe *recipe = *it; 131 if (recipe->isDeprecated()) 132 { 133 recipe->increaseMaxUses(random->nextInt(6) + random->nextInt(6) + 2); 134 } 135 } 136 } 137 addOffers(1); 138 addRecipeOnUpdate = false; 139 140 if (village.lock() != NULL && !lastPlayerTradeName.empty()) 141 { 142 level->broadcastEntityEvent(shared_from_this(), EntityEvent::VILLAGER_HAPPY); 143 village.lock()->modifyStanding(lastPlayerTradeName, 1); 144 } 145 } 146 addEffect(new MobEffectInstance(MobEffect::regeneration->id, SharedConstants::TICKS_PER_SECOND * 10, 0)); 147 } 148 } 149 150 AgableMob::serverAiMobStep(); 151} 152 153bool Villager::mobInteract(shared_ptr<Player> player) 154{ 155 // [EB]: Truly dislike this code but I don't see another easy way 156 shared_ptr<ItemInstance> item = player->inventory->getSelected(); 157 bool holdingSpawnEgg = item != NULL && item->id == Item::spawnEgg_Id; 158 159 if (!holdingSpawnEgg && isAlive() && !isTrading() && !isBaby()) 160 { 161 if (!level->isClientSide) 162 { 163 // note: stop() logic is controlled by trading ai goal 164 setTradingPlayer(player); 165 166 // 4J-JEV: Villagers in PC game don't display professions. 167 player->openTrading(dynamic_pointer_cast<Merchant>(shared_from_this()), getDisplayName() ); 168 } 169 return true; 170 } 171 return AgableMob::mobInteract(player); 172} 173 174void Villager::defineSynchedData() 175{ 176 AgableMob::defineSynchedData(); 177 entityData->define(DATA_PROFESSION_ID, 0); 178} 179 180void Villager::addAdditonalSaveData(CompoundTag *tag) 181{ 182 AgableMob::addAdditonalSaveData(tag); 183 tag->putInt(L"Profession", getProfession()); 184 tag->putInt(L"Riches", riches); 185 if (offers != NULL) 186 { 187 tag->putCompound(L"Offers", offers->createTag()); 188 } 189} 190 191void Villager::readAdditionalSaveData(CompoundTag *tag) 192{ 193 AgableMob::readAdditionalSaveData(tag); 194 setProfession(tag->getInt(L"Profession")); 195 riches = tag->getInt(L"Riches"); 196 if (tag->contains(L"Offers")) 197 { 198 CompoundTag *compound = tag->getCompound(L"Offers"); 199 delete offers; 200 offers = new MerchantRecipeList(compound); 201 } 202} 203 204bool Villager::removeWhenFarAway() 205{ 206 return false; 207} 208 209int Villager::getAmbientSound() 210{ 211 if(isTrading()) 212 { 213 return eSoundType_MOB_VILLAGER_HAGGLE; 214 } 215 return eSoundType_MOB_VILLAGER_IDLE; 216} 217 218int Villager::getHurtSound() 219{ 220 return eSoundType_MOB_VILLAGER_HIT; 221} 222 223int Villager::getDeathSound() 224{ 225 return eSoundType_MOB_VILLAGER_DEATH; 226} 227 228void Villager::setProfession(int profession) 229{ 230 entityData->set(DATA_PROFESSION_ID, profession); 231} 232 233int Villager::getProfession() 234{ 235 return entityData->getInteger(DATA_PROFESSION_ID); 236} 237 238bool Villager::isInLove() 239{ 240 return inLove; 241} 242 243void Villager::setInLove(bool inLove) 244{ 245 this->inLove = inLove; 246} 247 248void Villager::setChasing(bool chasing) 249{ 250 this->chasing = chasing; 251} 252 253bool Villager::isChasing() 254{ 255 return chasing; 256} 257 258void Villager::setLastHurtByMob(shared_ptr<LivingEntity> mob) 259{ 260 AgableMob::setLastHurtByMob(mob); 261 shared_ptr<Village> _village = village.lock(); 262 if (_village != NULL && mob != NULL) 263 { 264 _village->addAggressor(mob); 265 266 if ( mob->instanceof(eTYPE_PLAYER) ) 267 { 268 int amount = -1; 269 if (isBaby()) 270 { 271 amount = -3; 272 } 273 _village->modifyStanding( dynamic_pointer_cast<Player>(mob)->getName(), amount ); 274 if (isAlive()) 275 { 276 level->broadcastEntityEvent(shared_from_this(), EntityEvent::VILLAGER_ANGRY); 277 } 278 } 279 } 280} 281 282void Villager::die(DamageSource *source) 283{ 284 shared_ptr<Village> _village = village.lock(); 285 if (_village != NULL) 286 { 287 shared_ptr<Entity> sourceEntity = source->getEntity(); 288 if (sourceEntity != NULL) 289 { 290 if ( sourceEntity->instanceof(eTYPE_PLAYER) ) 291 { 292 _village->modifyStanding( dynamic_pointer_cast<Player>(sourceEntity)->getName(), -2 ); 293 } 294 else if ( sourceEntity->instanceof(eTYPE_ENEMY) ) 295 { 296 _village->resetNoBreedTimer(); 297 } 298 } 299 else if (sourceEntity == NULL) 300 { 301 // if the villager was killed by the world (such as lava or falling), blame 302 // the nearest player by not reproducing for a while 303 shared_ptr<Player> nearestPlayer = level->getNearestPlayer(shared_from_this(), 16.0f); 304 if (nearestPlayer != NULL) 305 { 306 _village->resetNoBreedTimer(); 307 } 308 } 309 } 310 311 AgableMob::die(source); 312} 313 314void Villager::setTradingPlayer(shared_ptr<Player> player) 315{ 316 tradingPlayer = weak_ptr<Player>(player); 317} 318 319shared_ptr<Player> Villager::getTradingPlayer() 320{ 321 return tradingPlayer.lock(); 322} 323 324bool Villager::isTrading() 325{ 326 return tradingPlayer.lock() != NULL; 327} 328 329void Villager::notifyTrade(MerchantRecipe *activeRecipe) 330{ 331 activeRecipe->increaseUses(); 332 ambientSoundTime = -getAmbientSoundInterval(); 333 playSound(eSoundType_MOB_VILLAGER_YES, getSoundVolume(), getVoicePitch()); 334 335 // when the player buys the latest item, we improve the merchant a little while later 336 if (activeRecipe->isSame(offers->at(offers->size() - 1))) 337 { 338 updateMerchantTimer = SharedConstants::TICKS_PER_SECOND * 2; 339 addRecipeOnUpdate = true; 340 if (tradingPlayer.lock() != NULL) 341 { 342 lastPlayerTradeName = tradingPlayer.lock()->getName(); 343 } 344 else 345 { 346 lastPlayerTradeName = L""; 347 } 348 } 349 350 if (activeRecipe->getBuyAItem()->id == Item::emerald_Id) 351 { 352 riches += activeRecipe->getBuyAItem()->count; 353 } 354} 355 356void Villager::notifyTradeUpdated(shared_ptr<ItemInstance> item) 357{ 358 if (!level->isClientSide && (ambientSoundTime > (-getAmbientSoundInterval() + SharedConstants::TICKS_PER_SECOND))) 359 { 360 ambientSoundTime = -getAmbientSoundInterval(); 361 if (item != NULL) 362 { 363 playSound(eSoundType_MOB_VILLAGER_YES, getSoundVolume(), getVoicePitch()); 364 } 365 else 366 { 367 playSound(eSoundType_MOB_VILLAGER_NO, getSoundVolume(), getVoicePitch()); 368 } 369 } 370} 371 372MerchantRecipeList *Villager::getOffers(shared_ptr<Player> forPlayer) 373{ 374 if (offers == NULL) 375 { 376 addOffers(1); 377 } 378 return offers; 379} 380 381float Villager::getRecipeChance(float baseChance) 382{ 383 float newChance = baseChance + baseRecipeChanceMod; 384 if (newChance > .9f) 385 { 386 return .9f - (newChance - .9f); 387 } 388 return newChance; 389} 390 391void Villager::addOffers(int addCount) 392{ 393 MerchantRecipeList *newOffers = new MerchantRecipeList(); 394 switch (getProfession()) 395 { 396 case PROFESSION_FARMER: 397 addItemForTradeIn(newOffers, Item::wheat_Id, random, getRecipeChance(.9f)); 398 addItemForTradeIn(newOffers, Tile::wool_Id, random, getRecipeChance(.5f)); 399 addItemForTradeIn(newOffers, Item::chicken_raw_Id, random, getRecipeChance(.5f)); 400 addItemForTradeIn(newOffers, Item::fish_cooked_Id, random, getRecipeChance(.4f)); 401 addItemForPurchase(newOffers, Item::bread_Id, random, getRecipeChance(.9f)); 402 addItemForPurchase(newOffers, Item::melon_Id, random, getRecipeChance(.3f)); 403 addItemForPurchase(newOffers, Item::apple_Id, random, getRecipeChance(.3f)); 404 addItemForPurchase(newOffers, Item::cookie_Id, random, getRecipeChance(.3f)); 405 addItemForPurchase(newOffers, Item::shears_Id, random, getRecipeChance(.3f)); 406 addItemForPurchase(newOffers, Item::flintAndSteel_Id, random, getRecipeChance(.3f)); 407 addItemForPurchase(newOffers, Item::chicken_cooked_Id, random, getRecipeChance(.3f)); 408 addItemForPurchase(newOffers, Item::arrow_Id, random, getRecipeChance(.5f)); 409 if (random->nextFloat() < getRecipeChance(.5f)) 410 { 411 newOffers->push_back(new MerchantRecipe(shared_ptr<ItemInstance>( new ItemInstance(Tile::gravel, 10) ), shared_ptr<ItemInstance>( new ItemInstance(Item::emerald) ), shared_ptr<ItemInstance>( new ItemInstance(Item::flint_Id, 4 + random->nextInt(2), 0)))); 412 } 413 break; 414 case PROFESSION_BUTCHER: 415 addItemForTradeIn(newOffers, Item::coal_Id, random, getRecipeChance(.7f)); 416 addItemForTradeIn(newOffers, Item::porkChop_raw_Id, random, getRecipeChance(.5f)); 417 addItemForTradeIn(newOffers, Item::beef_raw_Id, random, getRecipeChance(.5f)); 418 addItemForPurchase(newOffers, Item::saddle_Id, random, getRecipeChance(.1f)); 419 addItemForPurchase(newOffers, Item::chestplate_leather_Id, random, getRecipeChance(.3f)); 420 addItemForPurchase(newOffers, Item::boots_leather_Id, random, getRecipeChance(.3f)); 421 addItemForPurchase(newOffers, Item::helmet_leather_Id, random, getRecipeChance(.3f)); 422 addItemForPurchase(newOffers, Item::leggings_leather_Id, random, getRecipeChance(.3f)); 423 addItemForPurchase(newOffers, Item::porkChop_cooked_Id, random, getRecipeChance(.3f)); 424 addItemForPurchase(newOffers, Item::beef_cooked_Id, random, getRecipeChance(.3f)); 425 break; 426 case PROFESSION_SMITH: 427 addItemForTradeIn(newOffers, Item::coal_Id, random, getRecipeChance(.7f)); 428 addItemForTradeIn(newOffers, Item::ironIngot_Id, random, getRecipeChance(.5f)); 429 addItemForTradeIn(newOffers, Item::goldIngot_Id, random, getRecipeChance(.5f)); 430 addItemForTradeIn(newOffers, Item::diamond_Id, random, getRecipeChance(.5f)); 431 432 addItemForPurchase(newOffers, Item::sword_iron_Id, random, getRecipeChance(.5f)); 433 addItemForPurchase(newOffers, Item::sword_diamond_Id, random, getRecipeChance(.5f)); 434 addItemForPurchase(newOffers, Item::hatchet_iron_Id, random, getRecipeChance(.3f)); 435 addItemForPurchase(newOffers, Item::hatchet_diamond_Id, random, getRecipeChance(.3f)); 436 addItemForPurchase(newOffers, Item::pickAxe_iron_Id, random, getRecipeChance(.5f)); 437 addItemForPurchase(newOffers, Item::pickAxe_diamond_Id, random, getRecipeChance(.5f)); 438 addItemForPurchase(newOffers, Item::shovel_iron_Id, random, getRecipeChance(.2f)); 439 addItemForPurchase(newOffers, Item::shovel_diamond_Id, random, getRecipeChance(.2f)); 440 addItemForPurchase(newOffers, Item::hoe_iron_Id, random, getRecipeChance(.2f)); 441 addItemForPurchase(newOffers, Item::hoe_diamond_Id, random, getRecipeChance(.2f)); 442 addItemForPurchase(newOffers, Item::boots_iron_Id, random, getRecipeChance(.2f)); 443 addItemForPurchase(newOffers, Item::boots_diamond_Id, random, getRecipeChance(.2f)); 444 addItemForPurchase(newOffers, Item::helmet_iron_Id, random, getRecipeChance(.2f)); 445 addItemForPurchase(newOffers, Item::helmet_diamond_Id, random, getRecipeChance(.2f)); 446 addItemForPurchase(newOffers, Item::chestplate_iron_Id, random, getRecipeChance(.2f)); 447 addItemForPurchase(newOffers, Item::chestplate_diamond_Id, random, getRecipeChance(.2f)); 448 addItemForPurchase(newOffers, Item::leggings_iron_Id, random, getRecipeChance(.2f)); 449 addItemForPurchase(newOffers, Item::leggings_diamond_Id, random, getRecipeChance(.2f)); 450 addItemForPurchase(newOffers, Item::boots_chain_Id, random, getRecipeChance(.1f)); 451 addItemForPurchase(newOffers, Item::helmet_chain_Id, random, getRecipeChance(.1f)); 452 addItemForPurchase(newOffers, Item::chestplate_chain_Id, random, getRecipeChance(.1f)); 453 addItemForPurchase(newOffers, Item::leggings_chain_Id, random, getRecipeChance(.1f)); 454 break; 455 case PROFESSION_LIBRARIAN: 456 addItemForTradeIn(newOffers, Item::paper_Id, random, getRecipeChance(.8f)); 457 addItemForTradeIn(newOffers, Item::book_Id, random, getRecipeChance(.8f)); 458 //addItemForTradeIn(newOffers, Item::writtenBook_Id, random, getRecipeChance(0.3f)); 459 addItemForPurchase(newOffers, Tile::bookshelf_Id, random, getRecipeChance(.8f)); 460 addItemForPurchase(newOffers, Tile::glass_Id, random, getRecipeChance(.2f)); 461 addItemForPurchase(newOffers, Item::compass_Id, random, getRecipeChance(.2f)); 462 addItemForPurchase(newOffers, Item::clock_Id, random, getRecipeChance(.2f)); 463 464 if (random->nextFloat() < getRecipeChance(0.07f)) 465 { 466 Enchantment *enchantment = Enchantment::validEnchantments[random->nextInt(Enchantment::validEnchantments.size())]; 467 int level = Mth::nextInt(random, enchantment->getMinLevel(), enchantment->getMaxLevel()); 468 shared_ptr<ItemInstance> book = Item::enchantedBook->createForEnchantment(new EnchantmentInstance(enchantment, level)); 469 int cost = 2 + random->nextInt(5 + (level * 10)) + 3 * level; 470 471 newOffers->push_back(new MerchantRecipe(shared_ptr<ItemInstance>(new ItemInstance(Item::book)), shared_ptr<ItemInstance>(new ItemInstance(Item::emerald, cost)), book)); 472 } 473 break; 474 case PROFESSION_PRIEST: 475 addItemForPurchase(newOffers, Item::eyeOfEnder_Id, random, getRecipeChance(.3f)); 476 addItemForPurchase(newOffers, Item::expBottle_Id, random, getRecipeChance(.2f)); 477 addItemForPurchase(newOffers, Item::redStone_Id, random, getRecipeChance(.4f)); 478 addItemForPurchase(newOffers, Tile::glowstone_Id, random, getRecipeChance(.3f)); 479 { 480 int enchantItems[] = { 481 Item::sword_iron_Id, Item::sword_diamond_Id, Item::chestplate_iron_Id, Item::chestplate_diamond_Id, Item::hatchet_iron_Id, Item::hatchet_diamond_Id, Item::pickAxe_iron_Id, 482 Item::pickAxe_diamond_Id 483 }; 484 for (unsigned int i = 0; i < 8; ++i) 485 { 486 int id = enchantItems[i]; 487 if (random->nextFloat() < getRecipeChance(.05f)) 488 { 489 newOffers->push_back(new MerchantRecipe(shared_ptr<ItemInstance>(new ItemInstance(id, 1, 0)), 490 shared_ptr<ItemInstance>(new ItemInstance(Item::emerald, 2 + random->nextInt(3), 0)), 491 EnchantmentHelper::enchantItem(random, shared_ptr<ItemInstance>(new ItemInstance(id, 1, 0)), 5 + random->nextInt(15)))); 492 } 493 } 494 } 495 break; 496 } 497 498 if (newOffers->empty()) 499 { 500 addItemForTradeIn(newOffers, Item::goldIngot_Id, random, 1.0f); 501 } 502 503 // shuffle the list to make it more interesting 504 std::random_shuffle(newOffers->begin(), newOffers->end()); 505 506 if (offers == NULL) 507 { 508 offers = new MerchantRecipeList(); 509 } 510 for (int i = 0; i < addCount && i < newOffers->size(); i++) 511 { 512 if( offers->addIfNewOrBetter(newOffers->at(i))) 513 { 514 // 4J Added so we can delete newOffers 515 newOffers->erase(newOffers->begin() + i); 516 } 517 } 518 delete newOffers; 519} 520 521void Villager::overrideOffers(MerchantRecipeList *recipeList) 522{ 523} 524 525 526void Villager::staticCtor() 527{ 528 MIN_MAX_VALUES[Item::coal_Id] = pair<int,int>(16, 24); 529 MIN_MAX_VALUES[Item::ironIngot_Id] = pair<int,int>(8, 10); 530 MIN_MAX_VALUES[Item::goldIngot_Id] = pair<int,int>(8, 10); 531 MIN_MAX_VALUES[Item::diamond_Id] = pair<int,int>(4, 6); 532 MIN_MAX_VALUES[Item::paper_Id] = pair<int,int>(24, 36); 533 MIN_MAX_VALUES[Item::book_Id] = pair<int,int>(11, 13); 534 //MIN_MAX_VALUES.insert(Item::writtenBook_Id, pair<int,int>(1, 1)); 535 MIN_MAX_VALUES[Item::enderPearl_Id] = pair<int,int>(3, 4); 536 MIN_MAX_VALUES[Item::eyeOfEnder_Id] = pair<int,int>(2, 3); 537 MIN_MAX_VALUES[Item::porkChop_raw_Id] = pair<int,int>(14, 18); 538 MIN_MAX_VALUES[Item::beef_raw_Id] = pair<int,int>(14, 18); 539 MIN_MAX_VALUES[Item::chicken_raw_Id] = pair<int,int>(14, 18); 540 MIN_MAX_VALUES[Item::fish_cooked_Id] = pair<int,int>(9, 13); 541 MIN_MAX_VALUES[Item::seeds_wheat_Id] = pair<int,int>(34, 48); 542 MIN_MAX_VALUES[Item::seeds_melon_Id] = pair<int,int>(30, 38); 543 MIN_MAX_VALUES[Item::seeds_pumpkin_Id] = pair<int,int>(30, 38); 544 MIN_MAX_VALUES[Item::wheat_Id] = pair<int,int>(18, 22); 545 MIN_MAX_VALUES[Tile::wool_Id] = pair<int,int>(14, 22); 546 MIN_MAX_VALUES[Item::rotten_flesh_Id] = pair<int,int>(36, 64); 547 548 MIN_MAX_PRICES[Item::flintAndSteel_Id] = pair<int,int>(3, 4); 549 MIN_MAX_PRICES[Item::shears_Id] = pair<int,int>(3, 4); 550 MIN_MAX_PRICES[Item::sword_iron_Id] = pair<int,int>(7, 11); 551 MIN_MAX_PRICES[Item::sword_diamond_Id] = pair<int,int>(12, 14); 552 MIN_MAX_PRICES[Item::hatchet_iron_Id] = pair<int,int>(6, 8); 553 MIN_MAX_PRICES[Item::hatchet_diamond_Id] = pair<int,int>(9, 12); 554 MIN_MAX_PRICES[Item::pickAxe_iron_Id] = pair<int,int>(7, 9); 555 MIN_MAX_PRICES[Item::pickAxe_diamond_Id] = pair<int,int>(10, 12); 556 MIN_MAX_PRICES[Item::shovel_iron_Id] = pair<int,int>(4, 6); 557 MIN_MAX_PRICES[Item::shovel_diamond_Id] = pair<int,int>(7, 8); 558 MIN_MAX_PRICES[Item::hoe_iron_Id] = pair<int,int>(4, 6); 559 MIN_MAX_PRICES[Item::hoe_diamond_Id] = pair<int,int>(7, 8); 560 MIN_MAX_PRICES[Item::boots_iron_Id] = pair<int,int>(4, 6); 561 MIN_MAX_PRICES[Item::boots_diamond_Id] = pair<int,int>(7, 8); 562 MIN_MAX_PRICES[Item::helmet_iron_Id] = pair<int,int>(4, 6); 563 MIN_MAX_PRICES[Item::helmet_diamond_Id] = pair<int,int>(7, 8); 564 MIN_MAX_PRICES[Item::chestplate_iron_Id] = pair<int,int>(10, 14); 565 MIN_MAX_PRICES[Item::chestplate_diamond_Id] = pair<int,int>(16, 19); 566 MIN_MAX_PRICES[Item::leggings_iron_Id] = pair<int,int>(8, 10); 567 MIN_MAX_PRICES[Item::leggings_diamond_Id] = pair<int,int>(11, 14); 568 MIN_MAX_PRICES[Item::boots_chain_Id] = pair<int,int>(5, 7); 569 MIN_MAX_PRICES[Item::helmet_chain_Id] = pair<int,int>(5, 7); 570 MIN_MAX_PRICES[Item::chestplate_chain_Id] = pair<int,int>(11, 15); 571 MIN_MAX_PRICES[Item::leggings_chain_Id] = pair<int,int>(9, 11); 572 MIN_MAX_PRICES[Item::bread_Id] = pair<int,int>(-4, -2); 573 MIN_MAX_PRICES[Item::melon_Id] = pair<int,int>(-8, -4); 574 MIN_MAX_PRICES[Item::apple_Id] = pair<int,int>(-8, -4); 575 MIN_MAX_PRICES[Item::cookie_Id] = pair<int,int>(-10, -7); 576 MIN_MAX_PRICES[Tile::glass_Id] = pair<int,int>(-5, -3); 577 MIN_MAX_PRICES[Tile::bookshelf_Id] = pair<int,int>(3, 4); 578 MIN_MAX_PRICES[Item::chestplate_leather_Id] = pair<int,int>(4, 5); 579 MIN_MAX_PRICES[Item::boots_leather_Id] = pair<int,int>(2, 4); 580 MIN_MAX_PRICES[Item::helmet_leather_Id] = pair<int,int>(2, 4); 581 MIN_MAX_PRICES[Item::leggings_leather_Id] = pair<int,int>(2, 4); 582 MIN_MAX_PRICES[Item::saddle_Id] = pair<int,int>(6, 8); 583 MIN_MAX_PRICES[Item::expBottle_Id] = pair<int,int>(-4, -1); 584 MIN_MAX_PRICES[Item::redStone_Id] = pair<int,int>(-4, -1); 585 MIN_MAX_PRICES[Item::compass_Id] = pair<int,int>(10, 12); 586 MIN_MAX_PRICES[Item::clock_Id] = pair<int,int>(10, 12); 587 MIN_MAX_PRICES[Tile::glowstone_Id] = pair<int,int>(-3, -1); 588 MIN_MAX_PRICES[Item::porkChop_cooked_Id] = pair<int,int>(-7, -5); 589 MIN_MAX_PRICES[Item::beef_cooked_Id] = pair<int,int>(-7, -5); 590 MIN_MAX_PRICES[Item::chicken_cooked_Id] = pair<int,int>(-8, -6); 591 MIN_MAX_PRICES[Item::eyeOfEnder_Id] = pair<int,int>(7, 11); 592 MIN_MAX_PRICES[Item::arrow_Id] = pair<int,int>(-12, -8); 593} 594 595/** 596* Adds a merchant recipe that trades items for a single ruby. 597* 598* @param list 599* @param itemId 600* @param random 601* @param likelyHood 602*/ 603void Villager::addItemForTradeIn(MerchantRecipeList *list, int itemId, Random *random, float likelyHood) 604{ 605 if (random->nextFloat() < likelyHood) 606 { 607 list->push_back(new MerchantRecipe(getItemTradeInValue(itemId, random), Item::emerald)); 608 } 609} 610 611shared_ptr<ItemInstance> Villager::getItemTradeInValue(int itemId, Random *random) 612{ 613 return shared_ptr<ItemInstance>(new ItemInstance(itemId, getTradeInValue(itemId, random), 0)); 614} 615 616int Villager::getTradeInValue(int itemId, Random *random) 617{ 618 AUTO_VAR(it,MIN_MAX_VALUES.find(itemId)); 619 if (it == MIN_MAX_VALUES.end()) 620 { 621 return 1; 622 } 623 pair<int, int> minMax = it->second; 624 if (minMax.first >= minMax.second) 625 { 626 return minMax.first; 627 } 628 return minMax.first + random->nextInt(minMax.second - minMax.first); 629} 630 631/** 632* Adds a merchant recipe that trades rubies for an item. If the cost is 633* negative, one ruby will give several of that item. 634* 635* @param list 636* @param itemId 637* @param random 638* @param likelyHood 639*/ 640void Villager::addItemForPurchase(MerchantRecipeList *list, int itemId, Random *random, float likelyHood) 641{ 642 if (random->nextFloat() < likelyHood) 643 { 644 int purchaseCost = getPurchaseCost(itemId, random); 645 shared_ptr<ItemInstance> rubyItem; 646 shared_ptr<ItemInstance> resultItem; 647 if (purchaseCost < 0) 648 { 649 rubyItem = shared_ptr<ItemInstance>( new ItemInstance(Item::emerald_Id, 1, 0) ); 650 resultItem = shared_ptr<ItemInstance>( new ItemInstance(itemId, -purchaseCost, 0) ); 651 } 652 else 653 { 654 rubyItem = shared_ptr<ItemInstance>( new ItemInstance(Item::emerald_Id, purchaseCost, 0) ); 655 resultItem = shared_ptr<ItemInstance>( new ItemInstance(itemId, 1, 0) ); 656 } 657 list->push_back(new MerchantRecipe(rubyItem, resultItem)); 658 } 659} 660 661int Villager::getPurchaseCost(int itemId, Random *random) 662{ 663 AUTO_VAR(it,MIN_MAX_PRICES.find(itemId)); 664 if (it == MIN_MAX_PRICES.end()) 665 { 666 return 1; 667 } 668 pair<int, int> minMax = it->second; 669 if (minMax.first >= minMax.second) 670 { 671 return minMax.first; 672 } 673 return minMax.first + random->nextInt(minMax.second - minMax.first); 674} 675 676void Villager::handleEntityEvent(byte id) 677{ 678 if (id == EntityEvent::LOVE_HEARTS) 679 { 680 addParticlesAroundSelf(eParticleType_heart); 681 } 682 else if (id == EntityEvent::VILLAGER_ANGRY) 683 { 684 addParticlesAroundSelf(eParticleType_angryVillager); 685 } 686 else if (id == EntityEvent::VILLAGER_HAPPY) 687 { 688 addParticlesAroundSelf(eParticleType_happyVillager); 689 } 690 else 691 { 692 AgableMob::handleEntityEvent(id); 693 } 694} 695 696void Villager::addParticlesAroundSelf(ePARTICLE_TYPE particle) 697{ 698 for (int i = 0; i < 5; i++) 699 { 700 double xa = random->nextGaussian() * 0.02; 701 double ya = random->nextGaussian() * 0.02; 702 double za = random->nextGaussian() * 0.02; 703 level->addParticle(particle, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + 1.0f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); 704 } 705} 706 707MobGroupData *Villager::finalizeMobSpawn(MobGroupData *groupData, int extraData /*= 0*/) // 4J Added extraData param 708{ 709 groupData = AgableMob::finalizeMobSpawn(groupData); 710 711 setProfession(level->random->nextInt(PROFESSION_MAX)); 712 713 return groupData; 714} 715 716void Villager::setRewardPlayersInVillage() 717{ 718 rewardPlayersOnFirstVillage = true; 719} 720 721shared_ptr<AgableMob> Villager::getBreedOffspring(shared_ptr<AgableMob> target) 722{ 723 // 4J - added limit to villagers that can be bred 724 if(level->canCreateMore(GetType(), Level::eSpawnType_Breed) ) 725 { 726 shared_ptr<Villager> villager = shared_ptr<Villager>(new Villager(level)); 727 villager->finalizeMobSpawn(NULL); 728 return villager; 729 } 730 else 731 { 732 return nullptr; 733 } 734} 735 736bool Villager::canBeLeashed() 737{ 738 return false; 739} 740 741wstring Villager::getDisplayName() 742{ 743 if (hasCustomName()) return getCustomName(); 744 745 int name = IDS_VILLAGER; 746 switch(getProfession()) 747 { 748 case PROFESSION_FARMER: 749 name = IDS_VILLAGER_FARMER; 750 break; 751 case PROFESSION_LIBRARIAN: 752 name = IDS_VILLAGER_LIBRARIAN; 753 break; 754 case PROFESSION_PRIEST: 755 name = IDS_VILLAGER_PRIEST; 756 break; 757 case PROFESSION_SMITH: 758 name = IDS_VILLAGER_SMITH; 759 break; 760 case PROFESSION_BUTCHER: 761 name = IDS_VILLAGER_BUTCHER; 762 break; 763 }; 764 return app.GetString(name); 765}