Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/improve: create "custom loot" for monsters #3080

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 74 additions & 126 deletions data/scripts/lib/register_monster_type.lua
Original file line number Diff line number Diff line change
Expand Up @@ -347,140 +347,88 @@ function SortLootByChance(loot)
end)
end

registerMonsterType.loot = function(mtype, mask)
if type(mask.loot) == "table" then
SortLootByChance(mask.loot)
local lootError = false
for _, loot in pairs(mask.loot) do
local parent = Loot()
if loot.name then
if not parent:setIdFromName(loot.name) then
lootError = true
end
else
if not isInteger(loot.id) or loot.id < 1 then
lootError = true
end
parent:setId(loot.id)
end
if loot.subType or loot.charges then
parent:setSubType(loot.subType or loot.charges)
else
local lType = ItemType(loot.name and loot.name or loot.id)
if lType and lType:getCharges() > 1 then
parent:setSubType(lType:getCharges())
end
end
if loot.chance then
parent:setChance(loot.chance)
end
if loot.minCount then
parent:setMinCount(loot.minCount)
end
if loot.maxCount then
parent:setMaxCount(loot.maxCount)
end
if loot.aid or loot.actionId then
parent:setActionId(loot.aid or loot.actionId)
end
if loot.text or loot.description then
parent:setText(loot.text or loot.description)
end
if loot.name then
parent:setNameItem(loot.name)
end
if loot.article then
parent:setArticle(loot.article)
end
if loot.attack then
parent:setAttack(loot.attack)
end
if loot.defense then
parent:setDefense(loot.defense)
end
if loot.extraDefense or loot.extraDef then
parent:setExtraDefense(loot.extraDefense or loot.extraDef)
end
if loot.armor then
parent:setArmor(loot.armor)
local function configureLootAttributes(lootObject, lootProperties)
if lootProperties.subType or lootProperties.charges then
lootObject:setSubType(lootProperties.subType or lootProperties.charges)
else
local itemType = ItemType(lootProperties.name and lootProperties.name or lootProperties.itemId)
if itemType and itemType:getCharges() > 1 then
lootObject:setSubType(itemType:getCharges())
end
end

lootObject:setChance(lootProperties.chance or 0)
lootObject:setMinCount(lootProperties.minCount or 0)
lootObject:setMaxCount(lootProperties.maxCount or 0)
lootObject:setActionId(lootProperties.aid or lootProperties.actionId or 0)
lootObject:setText(lootProperties.text or lootProperties.description or "")
lootObject:setNameItem(lootProperties.name or "")
lootObject:setArticle(lootProperties.article or "")
lootObject:setAttack(lootProperties.attack or 0)
lootObject:setDefense(lootProperties.defense or 0)
lootObject:setExtraDefense(lootProperties.extraDefense or lootProperties.extraDef or 0)
lootObject:setArmor(lootProperties.armor or 0)
lootObject:setShootRange(lootProperties.shootRange or lootProperties.range or 0)
lootObject:setUnique(lootProperties.unique or false)
end

local function addChildrenLoot(parent, childrenLoot)
SortLootByChance(childrenLoot)
for _, child in pairs(childrenLoot) do
local childLoot = Loot()
if child.name then
if not childLoot:setIdFromName(child.name) then
return true
end
if loot.shootRange or loot.range then
parent:setShootRange(loot.shootRange or loot.range)
else
if not isInteger(child.id) or child.id < 1 then
return true
end
if loot.unique then
parent:setUnique(loot.unique)
childLoot:setId(child.id)
end
configureLootAttributes(childLoot, child)
parent:addChildLoot(childLoot)
end
return false
end

function MonsterType:createLoot(lootTable)
SortLootByChance(lootTable)
local lootError = false

for _, loot in pairs(lootTable) do
local parent = Loot()
if loot.name then
if not parent:setIdFromName(loot.name) then
lootError = true
end
if loot.child then
SortLootByChance(loot.child)
for _, children in pairs(loot.child) do
local child = Loot()
if children.name then
if not child:setIdFromName(children.name) then
lootError = true
end
else
if not isInteger(children.id) or children.id < 1 then
lootError = true
end
child:setId(children.id)
end
if children.subType or children.charges then
child:setSubType(children.subType or children.charges)
else
local cType = ItemType(children.name and children.name or children.id)
if cType and cType:getCharges() > 1 then
child:setSubType(cType:getCharges())
end
end
if children.chance then
child:setChance(children.chance)
end
if children.minCount then
child:setMinCount(children.minCount)
end
if children.maxCount then
child:setMaxCount(children.maxCount)
end
if children.aid or children.actionId then
child:setActionId(children.aid or children.actionId)
end
if children.text or children.description then
child:setText(children.text or children.description)
end
if loot.name then
child:setNameItem(loot.name)
end
if children.article then
child:setArticle(children.article)
end
if children.attack then
child:setAttack(children.attack)
end
if children.defense then
child:setDefense(children.defense)
end
if children.extraDefense or children.extraDef then
child:setExtraDefense(children.extraDefense or children.extraDef)
end
if children.armor then
child:setArmor(children.armor)
end
if children.shootRange or children.range then
child:setShootRange(children.shootRange or children.range)
end
if children.unique then
child:setUnique(children.unique)
end
parent:addChildLoot(child)
end
else
if not isInteger(loot.id) or loot.id < 1 then
lootError = true
end
mtype:addLoot(parent)
parent:setId(loot.id)
end
if lootError then
logger.warn("[registerMonsterType.loot] - Monster: {} loot could not correctly be load", mtype:name())

configureLootAttributes(parent, loot)

if loot.child and addChildrenLoot(parent, loot.child) then
lootError = true
end

self:addLoot(parent)
end

if lootError then
logger.warn("[MonsterType:createLoot] - Monster: {} loot could not be loaded correctly", self:getName())
end
end

registerMonsterType.loot = function(mtype, mask)
if type(mask.loot) == "table" then
mtype:createLoot(mask.loot)
end
end

local playerElements = { COMBAT_PHYSICALDAMAGE, COMBAT_ENERGYDAMAGE, COMBAT_EARTHDAMAGE, COMBAT_FIREDAMAGE, COMBAT_ICEDAMAGE, COMBAT_HOLYDAMAGE, COMBAT_DEATHDAMAGE }
registerMonsterType.elements = function(mtype, mask)
local min = configManager.getNumber(configKeys.MIN_ELEMENTAL_RESISTANCE)
Expand Down
38 changes: 38 additions & 0 deletions data/scripts/systems/custom_monster_loot.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
-- Drops custom loot for all monsters
local allLootConfig = {
{ id = 6526, chance = 100000, minCount = 1, maxCount = 10 }, -- Example of loot (100% chance)
}

-- Custom loot for specific monsters (this has the same usage options as normal monster loot)
local customLootConfig = {
["Dragon"] = { items = {
{ name = "platinum coin", chance = 1000, maxCount = 1 },
} },
}

local customMonsterLoot = GlobalEvent("CreateCustomMonsterLoot")

function customMonsterLoot.onStartup()
for monsterName, lootTable in pairs(customLootConfig) do
local mtype = Game.getMonsterTypeByName(monsterName)
if mtype then
if lootTable and lootTable.items and #lootTable.items > 0 then
mtype:createLoot(lootTable.items)
logger.debug("[customMonsterLoot.onStartup] - Custom loot registered for monster: {}", mtype:getName())
end
else
logger.error("[customMonsterLoot.onStartup] - Monster type not found: {}", monsterName)
end
end

if #allLootConfig > 0 then
for monsterName, mtype in pairs(Game.getMonsterTypes()) do
if mtype then
mtype:createLoot(allLootConfig)
logger.debug("[customMonsterLoot.onStartup] - Global loot registered for monster: {}", mtype:getName())
end
end
end
end

customMonsterLoot:register()
4 changes: 1 addition & 3 deletions src/creatures/monsters/monsters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,7 @@ void Monsters::clear() {
std::shared_ptr<MonsterType> Monsters::getMonsterType(const std::string &name, bool silent /* = false*/) const {
std::string lowerCaseName = asLowerCaseString(name);
if (auto it = monsters.find(lowerCaseName);
it != monsters.end()
// We will only return the MonsterType if it match the exact name of the monster
&& it->first.find(lowerCaseName) != std::basic_string<char>::npos) {
it != monsters.end()) {
return it->second;
}
if (!silent) {
Expand Down
2 changes: 1 addition & 1 deletion src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ void Game::loadBoostedCreature() {
}

const auto oldRace = result->getNumber<uint16_t>("raceid");
const auto monsterlist = getBestiaryList();
const auto &monsterlist = getBestiaryList();

struct MonsterRace {
uint16_t raceId { 0 };
Expand Down
2 changes: 1 addition & 1 deletion src/io/iobestiary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ uint16_t IOBestiary::getBestiaryRaceUnlocked(const std::shared_ptr<Player> &play
}

uint16_t count = 0;
std::map<uint16_t, std::string> besty_l = g_game().getBestiaryList();
const std::map<uint16_t, std::string> &besty_l = g_game().getBestiaryList();

for (const auto &it : besty_l) {
const auto mtype = g_monsters().getMonsterType(it.second);
Expand Down
4 changes: 2 additions & 2 deletions src/io/ioprey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ void TaskHuntingSlot::reloadMonsterGrid(std::vector<uint16_t> blackList, uint32_
// Disabling task hunting system if the server have less then 36 registered monsters on bestiary because:
// - Impossible to generate random lists without duplications on slots.
// - Stress the server with unnecessary loops.
std::map<uint16_t, std::string> bestiary = g_game().getBestiaryList();
const std::map<uint16_t, std::string> &bestiary = g_game().getBestiaryList();
if (bestiary.size() < 36) {
return;
}
Expand Down Expand Up @@ -574,7 +574,7 @@ void IOPrey::initializeTaskHuntOptions() {
}

msg.addByte(0xBA);
std::map<uint16_t, std::string> bestiaryList = g_game().getBestiaryList();
const std::map<uint16_t, std::string> &bestiaryList = g_game().getBestiaryList();
msg.add<uint16_t>(static_cast<uint16_t>(bestiaryList.size()));
std::for_each(bestiaryList.begin(), bestiaryList.end(), [&msg](auto mType) {
const auto mtype = g_monsters().getMonsterType(mType.second);
Expand Down
19 changes: 19 additions & 0 deletions src/lua/functions/core/game/game_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

Lua::registerMethod(L, "Game", "createNpcType", GameFunctions::luaGameCreateNpcType);
Lua::registerMethod(L, "Game", "createMonsterType", GameFunctions::luaGameCreateMonsterType);
Lua::registerMethod(L, "Game", "getMonsterTypeByName", GameFunctions::luaGameGetMonsterTypeByName);

Lua::registerMethod(L, "Game", "getSpectators", GameFunctions::luaGameGetSpectators);

Expand Down Expand Up @@ -164,6 +165,24 @@
return NpcTypeFunctions::luaNpcTypeCreate(L);
}

int GameFunctions::luaGameGetMonsterTypeByName(lua_State* L) {
if (!isString(L, 1)) {

Check failure on line 169 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-22.04-linux-release

‘isString’ was not declared in this scope; did you mean ‘trimString’?

Check failure on line 169 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-22.04-linux-debug

‘isString’ was not declared in this scope; did you mean ‘trimString’?

Check failure on line 169 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-24.04-linux-release

‘isString’ was not declared in this scope; did you mean ‘trimString’?

Check failure on line 169 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / windows-2022-windows-release

'isString': identifier not found

Check failure on line 169 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-24.04-linux-debug

‘isString’ was not declared in this scope; did you mean ‘trimString’?
reportErrorFunc("First argument must be a string");

Check failure on line 170 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / windows-2022-windows-release

'reportError': identifier not found
return 1;
}

const auto name = getString(L, 1);

Check failure on line 174 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-22.04-linux-release

‘getString’ was not declared in this scope

Check failure on line 174 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-22.04-linux-debug

‘getString’ was not declared in this scope

Check failure on line 174 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-24.04-linux-release

‘getString’ was not declared in this scope

Check failure on line 174 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / windows-2022-windows-release

'getString': identifier not found

Check failure on line 174 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-24.04-linux-debug

‘getString’ was not declared in this scope
const auto &mType = g_monsters().getMonsterType(name);
if (!mType) {
reportErrorFunc(fmt::format("MonsterType with name {} not found", name));

Check failure on line 177 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / windows-2022-windows-release

'fmt::v11::format': no matching overloaded function found

Check failure on line 177 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / windows-2022-windows-release

'reportError': identifier not found
return 1;
}

pushUserdata<MonsterType>(L, mType);

Check failure on line 181 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-22.04-linux-release

‘pushUserdata’ was not declared in this scope

Check failure on line 181 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-22.04-linux-debug

‘pushUserdata’ was not declared in this scope

Check failure on line 181 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-24.04-linux-release

‘pushUserdata’ was not declared in this scope

Check failure on line 181 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / windows-2022-windows-release

'pushUserdata': identifier not found

Check failure on line 181 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-24.04-linux-debug

‘pushUserdata’ was not declared in this scope
setMetatable(L, -1, "MonsterType");

Check failure on line 182 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-22.04-linux-release

‘setMetatable’ was not declared in this scope; did you mean ‘lua_setmetatable’?

Check failure on line 182 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-22.04-linux-debug

‘setMetatable’ was not declared in this scope; did you mean ‘lua_setmetatable’?

Check failure on line 182 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-24.04-linux-release

‘setMetatable’ was not declared in this scope; did you mean ‘lua_setmetatable’?

Check failure on line 182 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / windows-2022-windows-release

'setMetatable': identifier not found

Check failure on line 182 in src/lua/functions/core/game/game_functions.cpp

View workflow job for this annotation

GitHub Actions / ubuntu-24.04-linux-debug

‘setMetatable’ was not declared in this scope; did you mean ‘lua_setmetatable’?
return 1;
}

int GameFunctions::luaGameGetSpectators(lua_State* L) {
// Game.getSpectators(position[, multifloor = false[, onlyPlayer = false[, minRangeX = 0[, maxRangeX = 0[, minRangeY = 0[, maxRangeY = 0]]]]]])
const Position &position = Lua::getPosition(L, 1);
Expand Down
1 change: 1 addition & 0 deletions src/lua/functions/core/game/game_functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class GameFunctions {
private:
static int luaGameCreateMonsterType(lua_State* L);
static int luaGameCreateNpcType(lua_State* L);
static int luaGameGetMonsterTypeByName(lua_State* L);

static int luaGameGetSpectators(lua_State* L);

Expand Down
Loading
Loading