--- --- Generated by EmmyLua(https://github.com/EmmyLua) --- Created by zhangkai. --- DateTime: 2024/7/25 下午8:55 --- MonsterScript = {} local this = {} ---玩家击杀怪物记录 local MONSTER_DROP_RECORD = "T$monster_drop_record" --全服道具掉落记录(每日清空数据) local GLOBAL_ITEM_DROP = "Q$global_item_record" ---怪物组掉落记录 local DECAY_GROUP_RECORD = "T$decay_group_record" ---怪物组记录重置时间 local DECAY_GROUP_REST_TIME = "T$decay_group_rest_time" ---怪物首刀信息记录 local MONSTER_FIRST_ATTACK_INFO = "@monster_first_attack_info" ---怪物伤害列表 local MONSTER_HURT_LIST = "@monster_hurt_list" ---道具掉落限制类型 local ItemLimitType = { -- 小时限制 HOUR = 1, -- 天限制 DAY = 2, } local MonsterType = { ---普通怪 NORMAL = 1, } ---怪物掉落类型 local DropType = { ---无归属 NOT_OWNER = 0, ---最大伤害 MAX_THREAT = 1, ---尾刀(击杀者) LAST_ATTACK = 2, ---最大伤害 MAX_HURT = 3, ---参与者 PARTAKE = 4, ---获取首刀玩家 FIRST_ATTACK = 5, } ---怪物仇恨类型 local ThreatType = { ---累计仇恨值越高,优先级越高 MAX_HATE = 1, ---目标对怪物的总伤害越高,优先级越高 MAX_HURT = 2, } function this.GetMonsterDropRecord(actor) local dropRecord = getplaydef(actor, MONSTER_DROP_RECORD) if dropRecord == nil then dropRecord = {} end return dropRecord end function this.SaveMonsterDropRecord(actor,dropRecord) setplaydef(actor, MONSTER_DROP_RECORD, dropRecord) end function this.GetGlobalItemDrop() local itemDrop = getsysvar(GLOBAL_ITEM_DROP) if itemDrop == nil then itemDrop = {} end return itemDrop end function this.SaveGlobalItemDrop(itemDrop) setsysvar(GLOBAL_ITEM_DROP, itemDrop) end function this.GetDecayGroupRecord(actor) local record = getplaydef(actor, DECAY_GROUP_RECORD) if record == nil then record = {} end return record end function this.SaveDecayGroupRecord(actor, record) setplaydef(actor, DECAY_GROUP_RECORD, record) end function this.GetDecayGroupRestTime(actor) local restTime = getplaydef(actor, DECAY_GROUP_REST_TIME) if restTime == nil then return 0 end return restTime end function this.SaveDecayGroupRestTime(actor, restTime) setplaydef(actor, DECAY_GROUP_REST_TIME, restTime) end function this.GetMonsterFirstAttack(monsterActor) local firstAttackInfo = getplaydef(monsterActor, MONSTER_FIRST_ATTACK_INFO) if firstAttackInfo == nil then firstAttackInfo = {} end return firstAttackInfo end function this.SaveMonsterFirstAttack(monsterActor,firstAttackInfo) setplaydef(monsterActor, MONSTER_FIRST_ATTACK_INFO, firstAttackInfo) end function this.GetMonsterHurtList(monsterActor) local hurtList = getplaydef(monsterActor, MONSTER_HURT_LIST) if hurtList == nil then hurtList = {} end return hurtList end function this.SaveMonsterHurtList(monsterActor,hurtList) setplaydef(monsterActor, MONSTER_HURT_LIST, hurtList) end ---获取怪物首刀归属时长限制 function this.GetMonsterFirstAttackTimeLimit() local timeLimit = tonumber(ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.MONSTER_FIRST_ATTACK_TIME_LIMIT)) if timeLimit == nil then return 0 end return timeLimit end ---获取怪物最高伤害归属时长限制 function this.GetMonsterMaxHurtTimeLimit() local timeLimit = tonumber(ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.MONSTER_MAX_HURT_TIME_LIMIT)) if timeLimit == nil then return 0 end return timeLimit end ---怪物死亡触发 function MonsterScript.MonsterDieTrigger(monsterActor,dieParam) local success, errorInfo = xpcall(this.MonsterDieTrigger, debug.traceback, monsterActor, dieParam) gameDebug.assertPrint(success, "怪物死亡触发事件异常:", monsterActor, dieParam, errorInfo) end ---怪物死亡触发的功能 function this.MonsterDieTrigger(monsterActor,dieParam) local mapId = tonumber(dieParam["mapid"]) local monsterCfgId = tonumber(dieParam["monstercfg"]) local killerId = tonumber(dieParam["killer"]) local mongenCfgId = tonumber(dieParam["mongencfg"]) local maxHurtRid = tonumber(dieParam["maxhurt"]) local ownerId = tonumber(dieParam["owner"]) --更新怪物复活时间 MonsterScript.updateMonsterReliveTime(monsterActor, monsterCfgId) local killerActor = getactor(killerId,mapId) local ownerActor = getactor(ownerId, mapId) local type = getbaseinfo(monsterActor, "mapobjecttype") if type == MapObjectType.PET then --宠物死亡 Pet.PetDie(monsterActor,monsterCfgId) return end if (killerId == nil or killerId == 0) and (ownerId == nil or ownerId == 0) then error("怪物死亡触发事件参数异常, killId、ownerId为空:", monsterActor, dieParam) return end if killerId == nil or killerId == 0 or killerActor == nil then killerActor = ownerActor end --更新黄金任务 GoldTask.UpdateTaskProgress(killerActor,monsterCfgId) --更新boss悬赏 BossBounty.KillMonster(killerActor,monsterCfgId) --更新开服首杀 OpenServerAct.UpdateFirstKill(killerActor,maxHurtRid,0,monsterCfgId) --更新黄金首杀 GoldFirstKill.UpdateTaskProgress(killerActor, monsterCfgId) --更新大天使福利任务 AngelBenefit.UpdateTaskProgress(killerActor, AngelBenefit.TASK_TYPE.MONSTER,monsterCfgId) --更新诸神降临活动数据 GodsDescended.monsterDie(monsterActor, ownerActor, monsterCfgId) --更新圣域BOSS数据 SanctuaryBoss.monsterDie(monsterActor) --更新猎魔勋章数据 MonsterHunt.MonsterDie(ownerId, monsterCfgId) --触发任务目标刷新 this.TriggerMonsterDieTaskGoal(monsterActor,killerActor,monsterCfgId, mongenCfgId) end ---更新怪物死亡任务目标 function this.TriggerMonsterDieTaskGoal(monsterActor,actor,monsterCfgId, mongenCfgId) local owner = this.GetFirstAttack(monsterActor, actor) TaskHandler.TriggerTaskGoal(owner, TaskTargetType.KILL_LEVEL_MONSTER, monsterCfgId) -- 触发任务目标 local cfg_map_id = getbaseinfo(actor, "mapid") local param = { [1] = monsterCfgId, [2] = cfg_map_id, [3] = mongenCfgId } TaskHandler.TriggerTaskGoal(owner, TaskTargetType.KILL_TYPE_MONSTER, param) TaskHandler.TriggerTaskGoal(actor, TaskTargetType.LAST_KILL_MONSTER, param) TaskHandler.TriggerTaskGoal(owner, TaskTargetType.FIRST_ATTACK_MONSTER, param) end ---获取首刀玩家 function this.GetFirstAttack(monsterActor,killer) local owner = nil --改为首刀归属 local firstAttack = this.GetMonsterFirstAttack(monsterActor) if not table.isNullOrEmpty(firstAttack) then local mapId = getbaseinfo(killer, "unimapid") owner = getactor(firstAttack["rid"],mapId) end if owner == nil then owner = killer end return owner end function MonsterScript.updateMonsterReliveTime(monsterActor, monsterCfgId) local success, errorInfo = xpcall(this.updateMonsterReliveTime, debug.traceback, monsterActor, monsterCfgId) gameDebug.assertPrint(success, "怪物死亡更新复活时间异常:", monsterActor, monsterCfgId, errorInfo) end --- 更新怪物复活时间 function this.updateMonsterReliveTime(monsterActor, monsterCfgId) local ai = ConfigDataManager.getTableValue("cfg_monster", "ai", "id", monsterCfgId) if ai == nil then error("updateMonsterReliveTime cfg_monster表ai字段为空 monsterCfgId:" .. monsterCfgId) return end local aiConfig = ConfigDataManager.getById("cfg_monster_ai", ai) if aiConfig == nil then error("updateMonsterReliveTime cfg_monster_ai为nil id:" .. ai) return end local reliveType = tonumber(aiConfig['relivetype']) --local str = aiConfig['relivedelay'] if reliveType == 4 then local str = aiConfig['reliveservicetime'] if string.isNullOrEmpty(str) then return end -- 1#50|3#100|6#150 local strShuXian = string.split(str, "|") --local serverOpenDays1 = tonumber(getserveropendays(monsterActor)) local serverOpenDays = getbaseinfo(monsterActor, "serveropendays") local cfg = {} for _, v in pairs(strShuXian) do local strJinHao = string.split(v, "#") local day = tonumber(strJinHao[1]) if tonumber(serverOpenDays) >= day then cfg = strJinHao end end if table.isNullOrEmpty(cfg) then error("updateMonsterReliveTime 未找到匹配的数据 开服天数:" .. tostring(serverOpenDays).. ",reliveDelay字段内容:".. str) return end local cfgTime = tonumber(cfg[2]) local curTime = tonumber(getbaseinfo(monsterActor, "now")) local reliveTime = curTime + cfgTime setmonsterrelivetime(monsterActor, reliveTime) --jprint("复活时间:" , reliveTime,",怪物信息:" , monsterActor,",开服天数:",serverOpenDays, ",当前时间:", curTime,",配置时间:", cfgTime, ",配置:", cfg, ",str:", str) end end ---怪物掉落道具 function MonsterScript.MonsterDieCulDrop(monsterActor,dieParam) local success, dropResult = xpcall(this.MonsterDieCulDrop, debug.traceback, monsterActor,dieParam) if not success then gameDebug.assertPrint(success, "怪物死亡掉落执行异常:", dropResult, "参数", monsterActor,dieParam) return nil end return dropResult end function this.MonsterDieCulDrop(monsterActor,dieParam) local mapId = tonumber(dieParam["mapid"]) local monsterCfgId = tonumber(dieParam["monstercfg"]) local killerId = tonumber(dieParam["killer"]) local killerActor = getactor(killerId,mapId) local owner = this.GetFirstAttack(monsterActor, killerActor) local itemDecay,itemGroupDecay = this.GetDecayCfg(owner, monsterCfgId) --jprint("获取掉落衰减配置:", itemDecay,itemGroupDecay) local dropRecord = this.GetMonsterDropRecord(owner) local allDropCfg = this.GetAllDropCfg(owner, monsterCfgId, dropRecord,itemGroupDecay) if table.isNullOrEmpty(allDropCfg) then return nil end --掉落道具结果 local dropResult = {} --全服道具掉落记录 local globalItemDrop = this.GetGlobalItemDrop() --先检查重置记录 this.CheckDropItemReset(globalItemDrop) --计算掉落结果 this.GetDropResult(dieParam,owner,monsterActor,allDropCfg,globalItemDrop,dropRecord,dropResult,itemDecay,false) --保存掉落记录 this.SaveMonsterDropRecord(owner,dropRecord) --更新衰减组记录 this.RecordDecayGroupCount(owner,monsterCfgId) --jprint("怪物掉落记录:", dropRecord) --清理怪物伤害列表、首刀信息 this.ClearMonsterAttackInfo(monsterActor) if table.isNullOrEmpty(dropResult) then return end --保存道具记录 this.SaveGlobalItemDrop(globalItemDrop) -- 特权BOSS奖励弹窗 PrivilegeBoss.resRewardPanel(owner, monsterCfgId, mapId, dropResult) return dropResult end ---清理怪物伤害列表、首刀信息 function this.ClearMonsterAttackInfo(monsterActor) this.SaveMonsterFirstAttack(monsterActor,nil) this.SaveMonsterHurtList(monsterActor,nil) end ---获取所有掉落组配置 function this.GetAllDropCfg(actor,monsterCfgId,dropRecord,itemGroupDecayCfg) local mGroupList = ConfigDataManager.getTableValue("cfg_monster", "mgroup", "id", monsterCfgId) if string.isNullOrEmpty(mGroupList) then return nil end local groupArray = string.split(mGroupList, "#") local monsterLv = tonumber(ConfigDataManager.getTableValue("cfg_monster", "level", "id", monsterCfgId)) local killerLv = tonumber(getbaseinfo(actor, "level")) --衰减配置 local decayCfg = 0 local mDecayGroup = ConfigDataManager.getTableValue("cfg_monster", "mdecaygroup", "id", monsterCfgId) if not string.isNullOrEmpty(mDecayGroup) then decayCfg = tonumber(mDecayGroup) end local dropGroupList = {} --玩家掉落加成倍率 local bonusRate = this.GetRoleBonusRate(actor,monsterCfgId) for _, mGroup in pairs(groupArray) do local group = tonumber(mGroup) local dropTimes = dropRecord[group] if dropTimes == nil then dropTimes = 0; end local startCount = dropTimes + 1 local dropCfgList = this.GetDropCfgList(killerLv, monsterLv, group, startCount, bonusRate,itemGroupDecayCfg) --此处不判空后续要计次 dropGroupList[group] = dropCfgList end return dropGroupList end ---获取角色综合加成倍率 function this.GetRoleBonusRate(actor,monsterCfgId) local bonusRate = 1 -- --三倍收益倍率 -- local tripleRate = TripleIncome.GetRate(actor) -- --属性倍率 -- local attrRate = this.GetAttrDropRate(actor) -- bonusRate = tripleRate * attrRate --jprint("获取玩家掉落倍率:", bonusRate, tripleRate,attrRate) return bonusRate end ---获取属性倍率 function this.GetAttrDropRate(actor) local attrRate = tonumber(getattrinfo(actor, "propdropincrease")) if attrRate == nil then return 1 end return 1 + attrRate end ---获取一个掉落组中的配置列表 function this.GetDropCfgList(killerLv,monsterLv,group,dropTimes,roleRate,itemGroupDecayCfg) local cfgList = ConfigDataManager.getTable('cfg_boss_drop', 'mgroup', group) if table.isNullOrEmpty(cfgList) then return nil end local dropCfgList = {} for _, dropBossCfg in pairs(cfgList) do local dropParam = this.GetDropCfg(killerLv, monsterLv, dropBossCfg, dropTimes,roleRate,itemGroupDecayCfg) if not table.isNullOrEmpty(dropParam) then local dropCfgId = tonumber(dropBossCfg["id"]) dropCfgList[dropCfgId] = dropParam end end return dropCfgList end ---获取一个掉落配置 function this.GetDropCfg(killerLv,monsterLv,dropCfg,dropTimes,roleRate,itemGroupDecayCfg) local numLimit = string.split(dropCfg['dropnum'], "#") local min = tonumber(numLimit[1]) local max = tonumber(numLimit[2]) if dropTimes < min or dropTimes > max then return nil end local dropRate = this.CulDropRate(killerLv, monsterLv, dropCfg, roleRate) local extractNum = tonumber(dropCfg["extractnum"]) local itemGroup = dropCfg["itemgroup"] if string.isNullOrEmpty(itemGroup) then local randomGroupCfg = dropCfg["itemgrouprandom"] local tableName,addExtract = this.GetRandomItemGroup(randomGroupCfg,dropRate,itemGroupDecayCfg) if addExtract ~= nil and addExtract > 0 then itemGroup = tableName extractNum = extractNum * addExtract end end if string.isNullOrEmpty(itemGroup) then return nil end local dropParam = { id = tonumber(dropCfg["id"]), table_name = itemGroup, extract_times = extractNum, rate = dropRate } return dropParam end ---获取配置的随机道具组 function this.GetRandomItemGroup(randomGroupCfg,dropRate,itemGroupDecayCfg) if string.isNullOrEmpty(randomGroupCfg) then return nil end local itemGroupCfg = string.split(randomGroupCfg, "#") local tableName = itemGroupCfg[1] ---随机概率万分比 local probabilityCfg = tonumber(itemGroupCfg[2]) local percent = 10000 local extractNum = 0 local decayRate = this.GetItemGroupDecayRate(tableName, itemGroupDecayCfg) local realProbability = probabilityCfg * dropRate * decayRate --jprint("随机掉落组配置:", tableName,dropRate,decayRate, realProbability) if realProbability >= percent then extractNum = math.floor(realProbability / percent) realProbability = math.fmod(realProbability, percent) end local randomNum = math.random(1, percent) if randomNum <= realProbability then extractNum = extractNum + 1 end --jprint("随机掉落组配置:", tableName, extractNum,realProbability) return tableName,extractNum end function this.GetItemGroupDecayRate(tableName,itemGroupDecayCfg) if table.isNullOrEmpty(itemGroupDecayCfg) then return 1 end for tableNames, rate in pairs(itemGroupDecayCfg) do if string.contains(tableNames, tableName) then return rate end end return 1 end ---计算最终掉落倍率 function this.CulDropRate(killerLv, monsterLv,dropCfg,roleRate) local finalRate = roleRate local lvDecaySign = dropCfg["decaysign"] --掉落等级衰减 finalRate = this.CulLevelRate(killerLv,monsterLv,lvDecaySign,finalRate) return finalRate end ---获取配置掉落概率 function this.GetLevelRateCfg(killerLv,monsterLv,decayCfg) local roleSection = string.split(decayCfg["rolesection"],"#") local roleLvMin = tonumber(roleSection[1]) local roleLvMax = tonumber(roleSection[2]) if killerLv < roleLvMin or killerLv > roleLvMax then return nil end local monsterSection = string.split(decayCfg["monstersection"], "#") local monsterLvMin = tonumber(monsterSection[1]) local monsterLvMax = tonumber(monsterSection[2]) if monsterLv < monsterLvMin or monsterLv > monsterLvMax then return nil end return tonumber(decayCfg["dropdecayrate"]) end function this.CulLevelRate(killerLv,monsterLv,decaySign,roleRate) local cfgList = ConfigDataManager.getTable('cfg_boss_dropDecay', 'decaysign', decaySign) if table.isNullOrEmpty(cfgList) then return roleRate end for _, decayCfg in pairs(cfgList) do local rateCfg = this.GetLevelRateCfg(killerLv, monsterLv, decayCfg) if rateCfg ~= nil then return roleRate * (rateCfg / 10000) end end return roleRate end ---计算掉落结果 function this.GetDropResult(dieParam,actor,monsterActor,allDropCfg,globalItemDrop,dropRecord,dropResult,itemDecay,gm) --随机道具 for dropGroup, dropCfgList in pairs(allDropCfg) do local dropCount = dropRecord[dropGroup] if dropCount == nil then dropCount = 0 end if not table.isNullOrEmpty(dropCfgList) then for dropCfgId, dropCfg in pairs(dropCfgList) do this.CulDropItem(dieParam,actor,monsterActor,dropCfg,dropCount,globalItemDrop,dropResult,itemDecay,gm) end end --记录掉落次数 dropRecord[dropGroup] = dropCount + 1 end end function this.CulDropItem(dieParam,actor,monsterActor,dropCfg,dropCount,globalItemDrop,dropResult,itemDecay,gm) local extractTimes = dropCfg.extract_times --按配置次数随机 for i = 1, extractTimes do this.RandomDropItem(dieParam,actor,monsterActor,dropCfg,dropCount,globalItemDrop,dropResult,itemDecay,gm) end end ---随机掉落道具 function this.RandomDropItem(dieParam,actor,monsterActor,dropCfg,dropCount,globalItemDrop,dropResult,itemDecay,gm) local tableName = dropCfg.table_name local bossDropCfgId = dropCfg.id local dropRate = dropCfg.rate local dropRet = culdroptablebyname(actor,tableName,dropRate,dropCount,itemDecay) if dropRet == false or table.isNullOrEmpty(dropRet) then return end --掉落道具数量 local dropItem = this.GetDropItem(dropRet, globalItemDrop) if table.isNullOrEmpty(dropItem) then return end --掉落道具 if gm == false then local firstAttack = tonumber(getbaseinfo(actor, "rid")) local mapId = tonumber(dieParam["mapid"]) local mainOwner, owners = this.SettingOwners(monsterActor,dieParam,bossDropCfgId,firstAttack) local ret = monsterdiedroptomap(monsterActor, mapId, bossDropCfgId, dropItem,mainOwner,owners) if ret == false or ret == nil then return end end --记录道具抽取次数 this.MergeDropItem(dropItem,globalItemDrop,dropResult) end ---计算掉落归属 function this.SettingOwners(monsterActor,dieParam,bossDropCfgId,firstAttack) local owners = {} local mainOwner = firstAttack local mapId = tonumber(dieParam["mapid"]) local dropType = tonumber(ConfigDataManager.getTableValue('cfg_boss_drop', 'droptype', 'id', bossDropCfgId)) if dropType == DropType.NOT_OWNER then return mainOwner, owners elseif dropType == DropType.MAX_THREAT then local maxThreat = tonumber(dieParam["maxthreat"]) mainOwner = maxThreat elseif dropType == DropType.LAST_ATTACK then local killerId = tonumber(dieParam["killer"]) mainOwner = killerId elseif dropType == DropType.MAX_HURT then local maxHurtRid = this.GetMaxHurtPlayer(monsterActor) if maxHurtRid ~= nil and maxHurtRid > 0 then mainOwner = maxHurtRid end elseif dropType == DropType.PARTAKE then local threatList = dieParam["threatlist"] this.AddOwners(owners,threatList) elseif dropType == DropType.FIRST_ATTACK then mainOwner = firstAttack end table.insert(owners,mainOwner) --添加队友 local ownerActor = getactor(mainOwner,mapId) local memberRids = Team.GetAllMemberRids(ownerActor) this.AddOwners(owners,memberRids) return mainOwner, owners end function this.AddOwners(owners,ridList) if table.isNullOrEmpty(ridList) then return end for _, rid in pairs(ridList) do if not table.contains(owners,rid) then table.insert(owners,rid) end end end ---获取实际掉落的道具 function this.GetDropItem(dropRet,globalItemDrop) if table.isNullOrEmpty(dropRet) then return nil end local itemList = {} for itemCfgId, itemCount in pairs(dropRet) do local realCount = this.GetRealDropCount(itemCfgId, itemCount, globalItemDrop) if realCount > 0 then itemList[itemCfgId] = realCount end end return itemList end ---合并掉落道具 function this.MergeDropItem(dropItem,globalItemDrop,dropResult) --记录道具抽取次数 for itemCfgId, timeCount in pairs(dropItem) do --全服道具记录(无限制不记录) this.UpdateItemDropRecord(itemCfgId,timeCount,globalItemDrop) --怪物掉落结果 this.MergeItemRecord(itemCfgId,timeCount,dropResult) end end ---更新限制道具掉落数量 function this.UpdateItemDropRecord(itemCfgId,timeCount,globalItemDrop) --小时记录 local hourLimit = ConfigDataManager.getTableValue("cfg_item", "droplimithour", "id", itemCfgId) if not string.isNullOrEmpty(hourLimit) and tonumber(hourLimit) > 0 then this.MergeItemDropRecord(itemCfgId,timeCount,globalItemDrop,ItemLimitType.HOUR) end --每日记录 local dayLimit = ConfigDataManager.getTableValue("cfg_item", "droplimitday", "id", itemCfgId) if not string.isNullOrEmpty(dayLimit) and tonumber(dayLimit) > 0 then this.MergeItemDropRecord(itemCfgId,timeCount,globalItemDrop,ItemLimitType.DAY) end end function this.MergeItemDropRecord(itemCfgId,timeCount,globalItemDrop,limitType) local record = globalItemDrop[limitType] if record == nil then local nowTime = tonumber((getbaseinfo("nowsec"))) record = { item_record = {}, reset_time = nowTime, } end this.MergeItemRecord(itemCfgId,timeCount,record.item_record) globalItemDrop[limitType] = record --jprint("更新每日道具记录,",record) end function this.MergeItemRecord(itemCfgId,timeCount,itemRecord) local recordCount = itemRecord[itemCfgId] if recordCount == nil then recordCount = 0 end itemRecord[itemCfgId] = recordCount + timeCount end ---获取道具实际掉落数量 function this.GetRealDropCount(itemCfgId,itemCount,globalItemDrop) local realCount = itemCount --小时限制 local hourLimit = ConfigDataManager.getTableValue("cfg_item", "droplimithour", "id", itemCfgId) if not string.isNullOrEmpty(hourLimit) and tonumber(hourLimit) then local hourRecord = globalItemDrop[ItemLimitType.HOUR] local dropCount = this.CanDropCount(itemCfgId,itemCount,hourRecord,tonumber(hourLimit)) realCount = math.min(dropCount,realCount) end --每日限制 local dayLimit = ConfigDataManager.getTableValue("cfg_item", "droplimitday", "id", itemCfgId) if not string.isNullOrEmpty(dayLimit) and tonumber(dayLimit) and realCount > 0 then local dayRecord = globalItemDrop[ItemLimitType.DAY] local dropCount = this.CanDropCount(itemCfgId,itemCount,dayRecord,tonumber(dayLimit)) realCount = math.min(dropCount,realCount) end return realCount end ---获取可掉落数量 function this.CanDropCount(itemCfgId,itemCount,record,dropLimit) if table.isNullOrEmpty(record) or table.isNullOrEmpty(record.item_record) then return math.min(dropLimit,itemCount) end local recordCount = 0 if record.item_record[itemCfgId] ~= nil then recordCount = record.item_record[itemCfgId] end local canDrop = dropLimit - recordCount return math.min(canDrop,itemCount) end ---检查掉落记录重置 function this.CheckDropItemReset(globalItemDrop) if table.isNullOrEmpty(globalItemDrop) then return end local nowTime = tonumber((getbaseinfo("nowsec"))) for limitType, typeRecord in pairs(globalItemDrop) do local resetTime = typeRecord.reset_time local flush = this.TryResetItemRecord(resetTime, nowTime, limitType) if flush then typeRecord.item_record = {} typeRecord.reset_time = nowTime end end end ---重置掉落记录 function this.TryResetItemRecord(resetTime,nowSec,limitType) --每天重置 if limitType == ItemLimitType.DAY then if not TimeUtil.isSameDay(nowSec,resetTime) then return true end end --小时重置 if limitType == ItemLimitType.HOUR then if not TimeUtil.isSameHour4Sec(nowSec, resetTime) then return true end end return false end ---记录怪物组 function this.RecordDecayGroupCount(actor,monsterCfgId) local mDecayGroup = ConfigDataManager.getTableValue("cfg_monster", "mdecaygroup", "id", monsterCfgId) if string.isNullOrEmpty(mDecayGroup) or tonumber(mDecayGroup) == 0 then return end local decayGroup = tonumber(mDecayGroup) local decayRecord = this.GetDecayGroupRecord(actor) local oldCount = decayRecord[decayGroup] if oldCount == nil then oldCount = 0 end decayRecord[decayGroup] = oldCount + 1 this.SaveDecayGroupRecord(actor,decayRecord) --jprint("保存怪物组衰减次数",decayRecord) end ---获取衰减配置 function this.GetDecayCfg(actor,monsterCfgId) local mDecayGroup = ConfigDataManager.getTableValue("cfg_monster", "mdecaygroup", "id", monsterCfgId) if string.isNullOrEmpty(mDecayGroup) or tonumber(mDecayGroup) == 0 then return nil end local decayGroup = tonumber(mDecayGroup) local decayRecord = this.GetDecayGroupRecord(actor) local nowTime = tonumber((getbaseinfo("nowsec"))) local resetTime = this.GetDecayGroupRestTime(actor) local sameDay = true if resetTime > 0 then sameDay = TimeUtil.isSameDayWithNum(nowTime, resetTime, 5) else sameDay = false end if not sameDay then --重置衰减记录 decayRecord = {} this.SaveDecayGroupRestTime(actor,nowTime) this.SaveDecayGroupRecord(actor,decayRecord) --jprint("衰减记录重置",decayRecord) end local oldCount = decayRecord[decayGroup] if oldCount == nil then oldCount = 0 end local itemDecay = {} local groupDecay = {} this.GetDecayGroup(decayGroup,oldCount + 1,itemDecay,groupDecay) return itemDecay,groupDecay end function this.GetDecayGroup(decayGroup,recordCount,itemDecay,groupDecay) local allCfgList = ConfigDataManager.getTable("cfg_boss_numdecay", "mdecaygroup", decayGroup) if table.isNullOrEmpty(allCfgList) then return nil end for _, decayCfg in pairs(allCfgList) do this.GetDecayCfgParam(decayCfg,recordCount,itemDecay,groupDecay) end end function this.GetDecayCfgParam(decayCfg,recordCount,itemDecay,groupDecay) local limitArray = string.split(decayCfg["monstersection"], "#") local minLimit = tonumber(limitArray[1]) local maxLimit = tonumber(limitArray[2]) if recordCount < minLimit or recordCount > maxLimit then return nil end local decayRate = tonumber(decayCfg["dropdecayrate"]) / 10000 if not string.isNullOrEmpty(decayCfg["item"]) then local itemCfgId = tonumber(decayCfg["item"]) itemDecay[itemCfgId] = decayRate end if not string.isNullOrEmpty(decayCfg["itemgroup"]) then local itemGroup = decayCfg["itemgroup"] groupDecay[itemGroup] = decayRate end end ---获取怪物经验衰减倍率 function MonsterScript.GetExpDecayRate(actor,monsterCfgId) local decaySign = ConfigDataManager.getTableValue("cfg_monster", "decaysign", "id", monsterCfgId) if string.isNullOrEmpty(decaySign) or tonumber(decaySign) == 0 then return 1 end local allDecayCfg = ConfigDataManager.getTable("cfg_boss_dropdecay", "decaysign", decaySign) if table.isNullOrEmpty(allDecayCfg) then return 1 end local monsterLv = tonumber(ConfigDataManager.getTableValue("cfg_monster", "level", "id", monsterCfgId)) local roleLv = tonumber(getbaseinfo(actor, "level")) for _, decayCfg in pairs(allDecayCfg) do local rate = this.GetExpDecayCfg(roleLv, monsterLv, decayCfg) if rate ~= nil then return rate end end return 1 end function this.GetExpDecayCfg(roleLv, monsterLv,decayCfg) local roleSection = string.split(decayCfg["rolesection"],"#") local roleLvMin = tonumber(roleSection[1]) local roleLvMax = tonumber(roleSection[2]) if roleLv < roleLvMin or roleLv > roleLvMax then return nil end local monsterSection = string.split(decayCfg["monstersection"], "#") local monsterLvMin = tonumber(monsterSection[1]) local monsterLvMax = tonumber(monsterSection[2]) if monsterLv < monsterLvMin or monsterLv > monsterLvMax then return nil end return tonumber(decayCfg["dropdecayrate"]) / 10000 end ---gm掉落测试 ------@param actor table 玩家对象 -----@param monsterCfgId number 怪物配置id -----@param killNum number 击杀数量 -----@param isSum boolean 掉落是否汇总 function gmmonsterdroptest(actor,isSum,monster,count) if monster == nil or tonumber(monster) < 1 then return end if count == nil or tonumber(count) < 1 then return end local monsterCfgId = tonumber(monster) local killNum = tonumber(count) local dropResult = {} for i = 1, killNum do local dropRet = this.GmDropTest(actor, monsterCfgId) if dropRet ~= nil then for itemCfgId, itemCount in pairs(dropRet) do this.MergeItemRecord(itemCfgId, itemCount,dropResult) end end end if table.isNullOrEmpty(dropResult) then tipinfo(actor,"未掉落道具") return end local monsterName = ConfigDataManager.getTableValue("cfg_monster", "name", "id", monsterCfgId) local title = "掉落测试:击杀"..killNum.."只"..monsterName local content = this.GetDropStringContent(dropResult, isSum) sendmail(actor,title,content,nil) end function this.GetDropStringContent(dropResult,isSum) if table.isNullOrEmpty(dropResult) then return "未掉落道具" end local resultStr = {} for itemCfgId, itemCount in pairs(dropResult) do local itemName = ConfigDataManager.getTableValue("cfg_item", "name", "id", itemCfgId) local itemStr = itemName .. ":".. itemCount if itemStr then table.insert(resultStr, itemStr) end end return table.concat(resultStr, " |") end function this.GmDropTest(actor,monsterCfgId) local itemDecay,itemGroupDecay = this.GetDecayCfg(actor, monsterCfgId) --jprint("获取掉落衰减配置:", itemDecay,itemGroupDecay) local dropRecord = this.GetMonsterDropRecord(actor) local allDropCfg = this.GetAllDropCfg(actor, monsterCfgId, dropRecord,itemGroupDecay) if table.isNullOrEmpty(allDropCfg) then return nil end --掉落道具结果 local dropResult = {} --全服道具掉落记录 local globalItemDrop = this.GetGlobalItemDrop() --先检查重置记录 this.CheckDropItemReset(globalItemDrop) --计算掉落结果 this.GetDropResult(nil,actor,nil,allDropCfg,globalItemDrop,dropRecord,dropResult,itemDecay,true) --保存掉落记录 this.SaveMonsterDropRecord(actor,dropRecord) --更新衰减组记录 this.RecordDecayGroupCount(actor,monsterCfgId) --jprint("怪物掉落记录:", dropRecord) if table.isNullOrEmpty(dropResult) then return nil end --保存道具记录 this.SaveGlobalItemDrop(globalItemDrop) --jprint("全服道具限制记录:", globalItemDrop) --jprint("掉落结果:", dropResult) return dropResult; end --被攻击时触发 function MonsterScript.OnUnderAttack(targetActor,fightParam) --this.OnUnderAttack(targetActor,fightParam) this.UpdateMonsterAttackInfo(targetActor, fightParam) end function this.OnUnderAttack(actor,fightParam) local type = getbaseinfo(actor,"mapobjecttype") if type ~= MapObjectType.MONSTER and type ~= MapObjectType.PET then return end --local monInfo = getmonsterinfo(actor) --local monsterId = monInfo["cfgid"] monsterId = fightParam["targetcfgid"] local aiId = ConfigDataManager.getTableValue("cfg_monster", "ai", "id", monsterId) local beattackTime = ConfigDataManager.getTableValue("cfg_monster_ai", "beattacktime", "id", aiId) jprint("怪物被攻击时设置休眠时间",monsterId, aiId, beattackTime) setsleeptime(actor, beattackTime) end ---更新怪物受击信息 function this.UpdateMonsterAttackInfo(targetActor, fightParam) local targetType = tonumber(fightParam["targettype"]) if targetType ~= MapObjectType.MONSTER then return end local playerActor = nil local casterType = tonumber(fightParam["castertype"]) if casterType == MapObjectType.PLAYER then playerActor = fightParam["caster"] end if casterType == MapObjectType.PET or casterType == MapObjectType.PARTNER then local masterId = tonumber(fightParam["masterid"]) if masterId == nil or masterId == 0 then return end local mapId = tonumber(fightParam["mapid"]) playerActor = getactor(masterId,mapId) end if playerActor == nil then return end local maxHurtRid = this.UpdateHurtList(targetActor, playerActor, fightParam) this.TryUpdateMonsterFirstAttackInfo(targetActor, playerActor, fightParam,maxHurtRid) end ---尝试更新怪物首刀归属 function this.TryUpdateMonsterFirstAttackInfo(monsterActor, playerActor, fightParam,maxHurtRid) local rid = tonumber(getbaseinfo(playerActor, "rid")) local nowTime = tonumber((getbaseinfo("nowsec"))) local firstAttackInfo = this.GetMonsterFirstAttack(monsterActor) local timeLimit = this.GetMonsterFirstAttackTimeLimit() local updateFirstAttack = true local oldMaxHurt = nil if not table.isNullOrEmpty(firstAttackInfo) then local attackTime = tonumber(firstAttackInfo["attack_time"]) local roleId = tonumber(firstAttackInfo["rid"]) oldMaxHurt = tonumber(firstAttackInfo["max_hurt"]) if rid ~= roleId and attackTime + timeLimit > nowTime then updateFirstAttack = false end end --更新首刀记录 if updateFirstAttack then firstAttackInfo["rid"] = rid firstAttackInfo["attack_time"] = nowTime end --更新最大伤害玩家 local maxHurtChange = false if maxHurtRid > 0 and maxHurtRid ~= oldMaxHurt then firstAttackInfo["max_hurt"] = maxHurtRid maxHurtChange = true end --jprint("更新怪物首刀归属", targetActor,rid) local monsterCfgId = tonumber(fightParam["targetcfgid"]) local send = this.NeedSendOwnerMsg(monsterCfgId) local isMaxHurt = this.IsMaxHurtTarget(monsterCfgId) if send then local msgTime = tonumber(firstAttackInfo["msg_time"]) if maxHurtChange or msgTime == nil or nowTime - msgTime >= timeLimit then --发送首刀归属信息 local endTime = nowTime + timeLimit local maxHurtId = 0 if isMaxHurt == true then maxHurtId = maxHurtRid rid = 0 end local ownerInfo = { target = monsterActor, owner = rid, endTime = endTime, maxHurt = maxHurtId } this.SendFirstAttackOwnerMsg(nil,monsterActor,ownerInfo) --保存发送时间 firstAttackInfo["msg_time"] = nowTime end end --保存首刀归属记录 this.SaveMonsterFirstAttack(monsterActor, firstAttackInfo) end ---怪物进入视野 function MonsterScript.MonsterEnterView(actor, monsterActor,monsterCfgId) local send = this.NeedSendOwnerMsg(monsterCfgId) if not send then return end this.SendFirstAttackOwnerMsg(actor,monsterActor) end ---是否通知客户端归属信息 function this.NeedSendOwnerMsg(monsterCfgId) local monsterType = tonumber(ConfigDataManager.getTableValue("cfg_monster", "type", "id", monsterCfgId)) if monsterType == nil or monsterType == MonsterType.NORMAL then return false end return true end ---发送首刀归属信息 function this.SendFirstAttackOwnerMsg(actor,targetActor,ownerInfo) --只发生boss归属 if ownerInfo == nil then ownerInfo = this.GetFirstAttackOwner(targetActor) end --jprint("发送首刀归属信息", ownerInfo) if actor == nil then sendrefluamsg(targetActor, LuaMessageIdToClient.RES_MONSTER_FIRST_ATTACK_OWNER_CHANGE,ownerInfo) else sendluamsg(actor, LuaMessageIdToClient.RES_MONSTER_FIRST_ATTACK_OWNER_CHANGE,ownerInfo) end end ---获取首刀归属信息 function this.GetFirstAttackOwner(targetActor) local firstAttackInfo = this.GetMonsterFirstAttack(targetActor) local rid = 0 local endTime = 0 if not table.isNullOrEmpty(firstAttackInfo) then rid = tonumber(firstAttackInfo["rid"]) endTime = tonumber(firstAttackInfo["attack_time"]) end if endTime > 0 then local timeLimit = this.GetMonsterFirstAttackTimeLimit() endTime = endTime + timeLimit end local maxHurtRid = 0 local monsterInfo = getmonsterinfo(targetActor) if not table.isNullOrEmpty(monsterInfo) then local monsterCfgId = monsterInfo["cfgid"] local isMaxHurt = this.IsMaxHurtTarget(monsterCfgId) if isMaxHurt == true then maxHurtRid = this.GetMaxHurtPlayer(targetActor, nil) rid = 0 end end local ownerInfo = { target = targetActor, owner = rid, endTime = endTime, maxHurt = maxHurtRid } return ownerInfo end ---更新伤害列表 function this.UpdateHurtList(monsterActor, playerActor, fightParam) local targetHurt = tonumber(fightParam["targethurt"]) if targetHurt == nil or targetHurt < 0 then return 0 end local hurtList = MonsterScript.GetHurtList(monsterActor) local rid = tonumber(getbaseinfo(playerActor, "rid")) local oldHurt = 0 if not table.isNullOrEmpty(hurtList) then if hurtList[rid] ~= nil then oldHurt = hurtList[rid] end end hurtList[rid] = targetHurt + oldHurt this.SaveMonsterHurtList(monsterActor, hurtList) return this.GetMaxHurtPlayer(monsterActor,hurtList) end ---获取最大伤害玩家 ---@param hurtList table 伤害列表 function this.GetMaxHurtPlayer(monsterActor,hurtList) if table.isNullOrEmpty(hurtList) then hurtList = MonsterScript.GetHurtList(monsterActor) end if table.isNullOrEmpty(hurtList) then return 0 end local maxHurt = 0 local maxRid = 0 for rid, hurt in pairs(hurtList) do if hurt > maxHurt then maxHurt = hurt maxRid = rid end end return maxRid end function MonsterScript.GetHurtList(monsterActor) local hurtList = this.GetMonsterHurtList(monsterActor) if table.isNullOrEmpty(hurtList) then return hurtList end local mapId = getbaseinfo(monsterActor, "unimapid") local removeList = {} for rid, hurt in pairs(hurtList) do local isRemove = this.needRemoveHurt(rid, mapId) if isRemove == true then table.insert(removeList, rid) end end if not table.isNullOrEmpty(removeList) then for _, rid in pairs(removeList) do hurtList[rid] = nil end this.SaveMonsterHurtList(monsterActor, hurtList) end return hurtList end ---是否需要移除伤害 function this.needRemoveHurt(rid,monsterMapId) local playerActor = getactor(rid) if playerActor == nil then return true end local mapId = getbaseinfo(playerActor, "unimapid") or getplayermaininfo(playerActor)["mapid"] if mapId == nil or mapId ~= monsterMapId then return true end playerActor = getactor(rid,mapId) local dead = isdead(playerActor) if dead == nil or dead == true then return true end return false end ---移除伤害列表玩家 function MonsterScript.RemoveHurtListRid(monsterActor, rid) if monsterActor == nil or rid == nil then return end local hurtList = this.GetMonsterHurtList(monsterActor) if table.isNullOrEmpty(hurtList) then return end hurtList[rid] = nil this.SaveMonsterHurtList(monsterActor, hurtList) this.SendFirstAttackOwnerMsg(nil,monsterActor) end ---仇恨目标是否为最大伤害 function this.IsMaxHurtTarget(monsterCfgId) local threatType = tonumber(ConfigDataManager.getTableValue("cfg_monster_ai", "hate", "id", monsterCfgId)) if threatType == nil then return false end if threatType == ThreatType.MAX_HURT then return true end return false end