2424#include " lua/creature/movement.hpp"
2525#include " utils/pugicast.hpp"
2626#include " creatures/combat/spells.hpp"
27+ #include " creatures/monsters/monsters.hpp"
2728#include " utils/tools.hpp"
2829
2930#include < appearances.pb.h>
@@ -385,7 +386,12 @@ bool Items::loadFromXml() {
385386 monsterRaceId = pugi::cast<uint32_t >(monsterRaceIdAttr.value ());
386387 }
387388
388- setItemBag (itemId, itemName, chance, minAmount, maxAmount, monsterClass, monsterRaceId);
389+ bool bossOnly = false ;
390+ if (const auto bossOnlyAttr = nodeBags.attribute (" bossOnly" )) {
391+ bossOnly = bossOnlyAttr.as_bool ();
392+ }
393+
394+ setItemBag (itemId, itemName, chance, minAmount, maxAmount, monsterClass, monsterRaceId, bossOnly);
389395 }
390396 }
391397
@@ -502,7 +508,7 @@ bool Items::hasItemType(size_t hasId) const {
502508 return false ;
503509}
504510
505- void Items::setItemBag (uint16_t itemId, const std::string &itemName, double chance, uint32_t minAmount, uint32_t maxAmount, const std::string &monsterClass, uint32_t monsterRaceId) {
511+ void Items::setItemBag (uint16_t itemId, const std::string &itemName, double chance, uint32_t minAmount, uint32_t maxAmount, const std::string &monsterClass, uint32_t monsterRaceId, bool bossOnly ) {
506512 BagItemInfo itemInfo;
507513 itemInfo.name = itemName;
508514 itemInfo.id = itemId;
@@ -511,9 +517,59 @@ void Items::setItemBag(uint16_t itemId, const std::string &itemName, double chan
511517 itemInfo.maxAmount = maxAmount;
512518 itemInfo.monsterClass = monsterClass;
513519 itemInfo.monsterRaceId = monsterRaceId;
520+ itemInfo.bossOnly = bossOnly;
514521 bagItems[itemId] = itemInfo;
515522}
516523
524+ std::vector<Items::SurpriseBagDrop> Items::rollSurpriseBagLoot (const std::shared_ptr<MonsterType> &monsterType) const {
525+ std::vector<SurpriseBagDrop> drops;
526+ if (!g_configManager ().getBoolean (SURPRISE_BAGS)) {
527+ return drops;
528+ }
529+
530+ if (!monsterType) {
531+ return drops;
532+ }
533+
534+ const bool isBoss = monsterType->isBoss ();
535+ const uint16_t raceId = monsterType->info .raceid ;
536+ const std::string monsterClass = asLowerCaseString (monsterType->info .bestiaryClass );
537+
538+ for (const auto &[_, bagItem] : bagItems) {
539+ if (bagItem.chance <= 0 ) {
540+ continue ;
541+ }
542+
543+ if (bagItem.bossOnly && !isBoss) {
544+ continue ;
545+ }
546+
547+ if (bagItem.monsterRaceId != 0 && bagItem.monsterRaceId != raceId) {
548+ continue ;
549+ }
550+
551+ if (!bagItem.monsterClass .empty () && monsterClass != asLowerCaseString (bagItem.monsterClass )) {
552+ continue ;
553+ }
554+
555+ double randomChance = normal_random (0 , 100 );
556+ if (randomChance > bagItem.chance ) {
557+ continue ;
558+ }
559+
560+ const uint32_t minAmount = std::max<uint32_t >(1 , bagItem.minAmount );
561+ const uint32_t maxAmount = std::max<uint32_t >(minAmount, bagItem.maxAmount );
562+ uint16_t dropAmount = static_cast <uint16_t >(normal_random (minAmount, maxAmount));
563+ if (dropAmount == 0 ) {
564+ dropAmount = 1 ;
565+ }
566+
567+ drops.push_back ({ bagItem.id , dropAmount });
568+ }
569+
570+ return drops;
571+ }
572+
517573uint32_t Abilities::getHealthGain () const {
518574 return healthGain * g_configManager ().getFloat (RATE_HEALTH_REGEN);
519575}
0 commit comments