--[[ Illarion Server This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . ]] local common = require("base.common") local quests = require("monster.base.quests") local hooks = require("monster.base.hooks") local treasure = require("item.base.treasure") local arena = require("base.arena") local mugWithLid = require("item.id_310_mug_with_lid") local magicPuzzle = require("item.magicSphere") local character = require("base.character") local M = {} local killers = {} local function _isNumber(value) return type(value) == "number" end local function _isTable(value) return type(value) == "table" end local function _isRange(value) if not _isTable(value) then return false end if _isNumber(value.min) and _isNumber(value.max) then return true elseif _isNumber(value[1]) and _isNumber(value[2]) then return true else return false end end local function _getValueFromRange(value) if not _isRange(value) then error("This function requires a range.") end local min = value.min or value[1] local max = value.max or value[2] if min > max then error("The minimal value of the range is larger then the maximal value.") end return math.random() * (max - min) + min end local function performRandomTalk(monster, msgs) local langSkill = monster:getSkill(Character.commonLanguage) if langSkill < 100 then monster:increaseSkill(Character.commonLanguage, 100 - langSkill) end local germanMessage, englishMessage = msgs:getRandomMessage() --choses a random message common.TalkNLS(monster, Character.say, germanMessage, englishMessage ) --does the talking in both languages end local function performRegeneration(monster) if (math.random() < 0.3) and character.GetHP(monster) < 10000 then local con = monster:increaseAttrib("constitution", 0) local healAmount = 2 * con character.ChangeHP(monster, healAmount) end end local function reportAttack(monster, enemy) killers[monster.id] = enemy end local function cleanupMonster(monster) killers[monster.id] = nil hooks.cleanHooks(monster) end local function reportMonsterDeath(monster) local killer = killers[monster.id] if killer ~= nil and (not isValidChar(killer) or not killer:isInRange(monster, 12)) then killer = nil end if killer ~= nil and not arena.isArenaMonster(monster) then quests.checkQuest(killer, monster) end hooks.executeOnDeath(monster, killer) end local function copyMergeTables(table1, table2) local dataCopy = {} for key, value in pairs(table1) do dataCopy[key] = value end for key, value in pairs(table2) do dataCopy[key] = value end return dataCopy end local function dropLootItem(monster, lootItemData) local amount = math.random(lootItemData.minAmount, lootItemData.maxAmount) local quality = math.random(lootItemData.minQuality, lootItemData.maxQuality) local durability = math.random(lootItemData.minDurability, lootItemData.maxDurability) local data = lootItemData.data if lootItemData.itemId == 505 then -- It's a treasure map! Populate it with a valid location. local mapData = treasure.createMapData() if _isTable(mapData) then data = copyMergeTables(data, mapData) end elseif lootItemData.itemId == 310 then -- mug with lid; give it a proper collector's mug id data = {mugId = mugWithLid.getRandomMugId()} end local createdItem = world:createItemFromId(lootItemData.itemId, amount, monster.pos, true, quality * 100 + durability, data) createdItem.wear = 4 world:changeItem(createdItem) end local function dropLootCategory(monster, lootData) local randomTry = math.random() for _, itemInfo in pairs(lootData) do if itemInfo.probability >= randomTry then dropLootItem(monster, itemInfo) return else randomTry = randomTry - itemInfo.probability end end end local function performDrop(monster) if not arena.isArenaMonster(monster) and not hooks.isNoDrop(monster) and not magicPuzzle.isBunbun(monster) then local loot = monster:getLoot() for _, category in pairs(loot) do dropLootCategory(monster, category) end end end function M.generateCallbacks(msgs) local t = {} function t.enemyNear(monster, _) if math.random() < 3e-4 then --once each 5 minutes (3e-4) in average a message is spoken (is called very often) performRandomTalk(monster, msgs) end return false end function t.enemyOnSight(monster, _) performRegeneration(monster) if math.random() < 3e-3 then --once each 5 minutes (3e-3) in average a message is spoken performRandomTalk(monster, msgs) end return false end t.onAttacked = reportAttack t.onCasted = reportAttack function t.onDeath(monster) performDrop(monster) reportMonsterDeath(monster) cleanupMonster(monster) end return t end M.SKIN_COLOR = 0 M.HAIR_COLOR = 1 function M.white() return {255, 255, 255} end function M.setColor(params) if not _isTable(params) then error("The parameter for the setColor function are not set.") end if params.monster == nil or not isValidChar(params.monster) then error("The referenced monster is not set or not valid.") end if not _isNumber(params.target) then error("The required target value is not set.") end local colorTarget = tonumber(params.target) if colorTarget ~= M.SKIN_COLOR and colorTarget ~= M.HAIR_COLOR then error("The required target is not set to a valid target.") end local red, green, blue, alpha if params.hue ~= nil and params.saturation ~= nil and params.value ~= nil then -- HSV Mode local hue, saturation, value if _isRange(params.hue) then hue = _getValueFromRange(params.hue) elseif _isNumber(params.hue) then hue = tonumber(params.hue) else error("Hue parameter contains a unexpected value.") end if _isRange(params.saturation) then saturation = _getValueFromRange(params.saturation) elseif _isNumber(params.saturation) then saturation = tonumber(params.saturation) else error("Saturation parameter contains a unexpected value.") end if _isRange(params.value) then value = _getValueFromRange(params.value) elseif _isNumber(params.value) then value = tonumber(params.value) else error("Value parameter contains a unexpected value.") end red, green, blue = common.HSVtoRGB(hue, saturation, value) elseif params.red ~= nil and params.green ~= nil and params.blue ~= nil then if _isRange(params.red) then red = _getValueFromRange(params.red) elseif _isNumber(params.red) then red = tonumber(params.red) else error("Red parameter contains a unexpected value.") end if _isRange(params.green) then green = _getValueFromRange(params.green) elseif _isNumber(params.green) then green = tonumber(params.green) else error("Green parameter contains a unexpected value.") end if _isRange(params.blue) then blue = _getValueFromRange(params.blue) elseif _isNumber(params.blue) then blue = tonumber(params.blue) else error("Blue parameter contains a unexpected value.") end elseif _isTable(params.color) then if _isNumber(params.color[1]) then red = tonumber(params.color[1]) else error("Color parameter is not a valid color.") end if _isNumber(params.color[2]) then green = tonumber(params.color[2]) else error("Color parameter is not a valid color.") end if _isNumber(params.color[3]) then blue = tonumber(params.color[3]) else error("Color parameter is not a valid color.") end else error("No colour was set.") end if params.alpha ~= nil then if _isRange(params.alpha) then alpha = _getValueFromRange(params.alpha) elseif _isNumber(params.alpha) then alpha = tonumber(params.alpha) else error("Alpha parameter contains a unexpected value.") end else alpha = 255 end red = math.floor(red + 0.5) green = math.floor(green + 0.5) blue = math.floor(blue + 0.5) alpha = math.floor(alpha + 0.5) if colorTarget == M.SKIN_COLOR then params.monster:setSkinColour(colour(red, green, blue, alpha)) else params.monster:setHairColour(colour(red, green, blue, alpha)) end end return M