Skip to content

Commit 7e044b8

Browse files
committed
feat: surprise bags boss
1 parent 59e6f92 commit 7e044b8

File tree

8 files changed

+121
-42
lines changed

8 files changed

+121
-42
lines changed

data/XML/groups.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@
147147
<flag notgainskill="1" />
148148
<flag setmaxspeed="1" />
149149
<flag specialvip="1" />
150-
<flag notgenerateloot="0" />
150+
<flag notgenerateloot="1" />
151151
<flag cantalkredchannelanonymous="1" />
152152
<flag ignoreprotectionzone="1" />
153153
<flag ignorespellcheck="1" />

data/items/bags.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
- `maxAmount`: (Optional) Sets the maximum quantity of the item that can drop, if applicable.
1111
- `class`: (Optional) Restricts the item drop to certain monster classes.
1212
- `raceId`: (Optional) Links the drop to a specific race or creature.
13+
- `bossOnly`: (Optional) Set to "true" to make the drop available exclusively on boss monsters.
1314
-->
1415
<bag itemid="6571" name="Red Surprise Bag" chance="5" />
1516
<bag itemid="6570" name="Blue Surprise Bag" chance="10" />
1617
<bag itemid="3079" name="Boots of Haste" chance="15" class="Dragon" />
1718
<bag itemid="6578" name="Party Hat" chance="100" raceId="1938" /> <!-- Infernal Demon -->
19+
<bag itemid="6578" name="Party Hat" chance="100" bossOnly="true" />
1820
<bag itemid="3043" name="Crystal Coin" chance="100" maxAmount="100" />
1921
</bags>

data/libs/functions/monster.lua

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ do
209209
return lootTable or {}
210210
end
211211

212-
return self:generateLootRoll({
212+
local bossLoot = self:generateLootRoll({
213213
factor = lootFactor,
214214
gut = false,
215215
filter = function(itemType, unique)
@@ -222,5 +222,15 @@ do
222222
return true
223223
end,
224224
}, lootTable, player)
225+
226+
local bagLoot = self:getSurpriseBagLoot() or {}
227+
for itemId, info in pairs(bagLoot) do
228+
if info.count and info.count > 0 then
229+
bossLoot[itemId] = bossLoot[itemId] or { count = 0, gut = false }
230+
bossLoot[itemId].count = (bossLoot[itemId].count or 0) + info.count
231+
end
232+
end
233+
234+
return bossLoot
225235
end
226236
end

src/creatures/monsters/monster.cpp

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2489,46 +2489,25 @@ void Monster::dropLoot(const std::shared_ptr<Container> &corpse, const std::shar
24892489
}
24902490

24912491
if (g_configManager().getBoolean(SURPRISE_BAGS)) {
2492-
const auto allBagItems = Item::items.getAllBagItems();
2492+
const auto surpriseDrops = Item::items.rollSurpriseBagLoot(mType);
2493+
for (const auto &drop : surpriseDrops) {
2494+
if (drop.itemId == 0) {
2495+
continue;
2496+
}
24932497

2494-
// Filter out items with chance <= 0
2495-
std::vector<const Items::BagItemInfo*> validBagItems;
2496-
for (const auto &bagItem : allBagItems) {
2497-
if (bagItem->chance > 0 && (bagItem->monsterRaceId == 0 || bagItem->monsterRaceId == mType->info.raceid) && (bagItem->monsterClass.empty() || asLowerCaseString(mType->info.bestiaryClass) == asLowerCaseString(bagItem->monsterClass))) {
2498-
validBagItems.push_back(bagItem);
2498+
std::shared_ptr<Item> newItem;
2499+
if (drop.count > 1) {
2500+
newItem = Item::CreateItem(drop.itemId, drop.count);
2501+
} else {
2502+
newItem = Item::CreateItem(drop.itemId, 1);
24992503
}
2500-
}
25012504

2502-
if (!validBagItems.empty()) {
2503-
// Iterate through valid items and drop them based on probability
2504-
for (const auto &bagItem : validBagItems) {
2505-
double randomChance = normal_random(0, 100);
2506-
2507-
if (randomChance <= bagItem->chance) {
2508-
auto chosenBagId = bagItem->id;
2509-
auto minAmount = bagItem->minAmount;
2510-
auto maxAmount = bagItem->maxAmount;
2511-
auto dropAmount = static_cast<uint16_t>(normal_random(minAmount, maxAmount));
2512-
2513-
if (chosenBagId != 0) {
2514-
std::shared_ptr<Item> newItem = nullptr;
2515-
if (dropAmount > 1) {
2516-
newItem = Item::CreateItem(chosenBagId, dropAmount);
2517-
if (newItem) {
2518-
if (g_game().internalAddItem(corpse, newItem) != RETURNVALUE_NOERROR) {
2519-
corpse->internalAddThing(newItem);
2520-
}
2521-
}
2522-
} else {
2523-
newItem = Item::CreateItem(chosenBagId, 1);
2524-
if (newItem) {
2525-
if (g_game().internalAddItem(corpse, newItem) != RETURNVALUE_NOERROR) {
2526-
corpse->internalAddThing(newItem);
2527-
}
2528-
}
2529-
}
2530-
}
2531-
}
2505+
if (!newItem) {
2506+
continue;
2507+
}
2508+
2509+
if (g_game().internalAddItem(corpse, newItem) != RETURNVALUE_NOERROR) {
2510+
corpse->internalAddThing(newItem);
25322511
}
25332512
}
25342513
}

src/items/items.cpp

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
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+
517573
uint32_t Abilities::getHealthGain() const {
518574
return healthGain * g_configManager().getFloat(RATE_HEALTH_REGEN);
519575
}

src/items/items.hpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#include <memory>
2+
3+
class MonsterType;
14
////////////////////////////////////////////////////////////////////////
25
// Crystal Server - an opensource roleplaying game
36
////////////////////////////////////////////////////////////////////////
@@ -397,6 +400,12 @@ class Items {
397400
uint32_t maxAmount = 1;
398401
std::string monsterClass = "";
399402
uint32_t monsterRaceId = 0;
403+
bool bossOnly = false;
404+
};
405+
406+
struct SurpriseBagDrop {
407+
uint16_t itemId = 0;
408+
uint16_t count = 0;
400409
};
401410

402411
using NameMap = std::unordered_multimap<std::string, uint16_t>;
@@ -484,7 +493,8 @@ class Items {
484493
return allBagItems;
485494
}
486495

487-
void setItemBag(uint16_t itemId, const std::string &itemName, double chance, uint32_t minAmount, uint32_t maxAmount, const std::string &monsterClass, uint32_t monsterRaceId);
496+
void 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);
497+
std::vector<SurpriseBagDrop> rollSurpriseBagLoot(const std::shared_ptr<MonsterType> &monsterType) const;
488498

489499
private:
490500
std::vector<ItemType> items;

src/lua/functions/creatures/monster/monster_type_functions.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "creatures/combat/spells.hpp"
2222
#include "creatures/monsters/monster.hpp"
2323
#include "creatures/monsters/monsters.hpp"
24+
#include "items/items.hpp"
2425
#include "game/game.hpp"
2526
#include "io/io_bosstiary.hpp"
2627
#include "lua/scripts/scripts.hpp"
@@ -103,6 +104,7 @@ void MonsterTypeFunctions::init(lua_State* L) {
103104

104105
Lua::registerMethod(L, "MonsterType", "getLoot", MonsterTypeFunctions::luaMonsterTypeGetLoot);
105106
Lua::registerMethod(L, "MonsterType", "addLoot", MonsterTypeFunctions::luaMonsterTypeAddLoot);
107+
Lua::registerMethod(L, "MonsterType", "getSurpriseBagLoot", MonsterTypeFunctions::luaMonsterTypeGetSurpriseBagLoot);
106108

107109
Lua::registerMethod(L, "MonsterType", "getCreatureEvents", MonsterTypeFunctions::luaMonsterTypeGetCreatureEvents);
108110
Lua::registerMethod(L, "MonsterType", "registerEvent", MonsterTypeFunctions::luaMonsterTypeRegisterEvent);
@@ -1182,6 +1184,25 @@ int MonsterTypeFunctions::luaMonsterTypeGetLoot(lua_State* L) {
11821184
return 1;
11831185
}
11841186

1187+
int MonsterTypeFunctions::luaMonsterTypeGetSurpriseBagLoot(lua_State* L) {
1188+
// monsterType:getSurpriseBagLoot()
1189+
const auto &monsterType = Lua::getUserdataShared<MonsterType>(L, 1);
1190+
if (!monsterType) {
1191+
lua_pushnil(L);
1192+
return 1;
1193+
}
1194+
1195+
const auto drops = Item::items.rollSurpriseBagLoot(monsterType);
1196+
lua_createtable(L, 0, drops.size());
1197+
for (const auto &drop : drops) {
1198+
lua_pushnumber(L, drop.itemId);
1199+
lua_createtable(L, 0, 1);
1200+
Lua::setField(L, "count", drop.count);
1201+
lua_settable(L, -3);
1202+
}
1203+
return 1;
1204+
}
1205+
11851206
int MonsterTypeFunctions::luaMonsterTypeAddLoot(lua_State* L) {
11861207
// monsterType:addLoot(loot)
11871208
const auto &monsterType = Lua::getUserdataShared<MonsterType>(L, 1);

src/lua/functions/creatures/monster/monster_type_functions.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ class MonsterTypeFunctions {
9797

9898
static int luaMonsterTypeGetLoot(lua_State* L);
9999
static int luaMonsterTypeAddLoot(lua_State* L);
100+
static int luaMonsterTypeGetSurpriseBagLoot(lua_State* L);
100101

101102
static int luaMonsterTypeGetCreatureEvents(lua_State* L);
102103
static int luaMonsterTypeRegisterEvent(lua_State* L);

0 commit comments

Comments
 (0)