牛头 发表于 2026-6-17 18:09:49

仿魔兽大秘境系统

本帖最后由 牛头 于 2026-6-17 18:11 编辑

--[[
传奇大秘境系统
模块化:配置区 | 工具库 | 实例管理 | 刷怪系统 | 结算评分 | NPC交互 | 全局触发器
]]
local MythicDungeon = {}

-- 1.全局配置(一键修改所有数值)
local CFG = {
    DUNGEON_MAP_ID = 3001,
    KEY_ITEM_PREFIX = "秘境钥匙",
    TEAM_MIN = 2, TEAM_MAX = 5,
    TEAM_COOLDOWN = 90,
    BASE_LIMIT_TIME = 1500,
    TIME_REDUCE_PER_LAYER = 50,
    MIN_LIMIT_TIME = 480,
    WAVE_COUNT = 6,
    BASE_WAVE_MONSTER = 10,
    MONSTER_SCALE_RATE = 1.1,
    BOSS_NAME = "秘境·时空主宰",
    DEATH_DEDUCT_SCORE = 10,
    STAR_CONFIG = {
      ={layerAdd=3,dropRate=2.2},={layerAdd=2,dropRate=1.7},
      ={layerAdd=1,dropRate=1.3},={layerAdd=0,dropRate=1.0},={layerAdd=-1,dropRate=0.6}
    },
    VAR_PREFIX = "MythicDungeon_"
}

-- 2.私有缓存
local _instanceList = {}
local _playerInstanceMap = {}
local _instanceAutoID = 1

-- 3.工具函数库
local Util = {}
function Util.GetLimitTime(layer)
    local time = CFG.BASE_LIMIT_TIME - (layer-1)*CFG.TIME_REDUCE_PER_LAYER
    return math.max(time, CFG.MIN_LIMIT_TIME)
end
function Util.GetWaveMonsterNum(layer)
    return CFG.BASE_WAVE_MONSTER + (layer-1)*3
end
function Util.ForeachTeam(teamList,cb)
    for _,name in ipairs(teamList) do
      local p = GetPlayerByName(name)
      if p and p:IsOnline() then cb(p) end
    end
end
function Util.SendTeamMsg(teamList,msg,c)
    Util.ForeachTeam(teamList,function(p) p:SendMsg(c or 0,"[大秘境]"..msg) end)
end
function Util.ParseKeyLayer(name)
    local l = string.match(name,"%((%d+)层%)")
    return l and tonumber(l) or nil
end

-- 4.副本实例管理器
local InstanceMgr = {}
function InstanceMgr.CreateInstance(teamList,layer,owner)
    local insID = _instanceAutoID
    _instanceAutoID = insID + 1
    local limit = Util.GetLimitTime(layer)
    local data = {
      insId=insID,layer=layer,owner=owner,team=teamList,
      createStamp=os.time(),deadlineStamp=os.time()+limit,
      currentWave=0,bossSpawn=false,bossDead=false,
      totalTeamDeath=0,isFinished=false
    }
    _instanceList = data
    for _,name in ipairs(teamList) do _playerInstanceMap=insID end

    Util.ForeachTeam(teamList,function(p)
      p:MapMove(CFG.DUNGEON_MAP_ID,90,90)
      p:SetVarNumber(CFG.VAR_PREFIX.."InInstance",insID)
      p:ForbidCmd("回城") p:ForbidCmd("随机传送")
    end)
    Util.SendTeamMsg(teamList,string.format("开启%d层秘境!限时%d分钟",layer,math.floor(limit/60)),1)
    MythicDungeon.WaveSystem.StartNextWave(insID)
    SetTimer(limit*1000,function() InstanceMgr.InstanceTimeOut(insID) end)
    return insID
end
function InstanceMgr.GetInstance(id) return _instanceList end
function InstanceMgr.InstanceTimeOut(id)
    local ins = InstanceMgr.GetInstance(id)
    if not ins or ins.isFinished then return end
    ins.isFinished=true
    Util.SendTeamMsg(ins.team,"秘境超时!挑战失败",2)
    InstanceMgr.ClearInstance(id)
end
function InstanceMgr.ClearInstance(id)
    local ins = InstanceMgr.GetInstance(id)
    if not ins then return end
    Util.ForeachTeam(ins.team,function(p)
      p:MapMove(3,330,330)
      p:SetVarNumber(CFG.VAR_PREFIX.."InInstance",0)
      p:AllowCmd("回城") p:AllowCmd("随机传送")
    end)
    for _,name in ipairs(ins.team) do _playerInstanceMap=nil end
    _instanceList = nil
end

-- 5.波次刷怪系统
MythicDungeon.WaveSystem = {}
function MythicDungeon.WaveSystem.StartNextWave(id)
    local ins = InstanceMgr.GetInstance(id)
    if not ins or ins.isFinished then return end
    ins.currentWave = ins.currentWave +1

    if ins.currentWave > CFG.WAVE_COUNT then
      ins.bossSpawn=true
      Util.SendTeamMsg(ins.team,"最终BOSS即将刷新!",1)
      local boss = SpawnMonster(CFG.DUNGEON_MAP_ID,110,110,CFG.BOSS_NAME)
      local scale = math.pow(CFG.MONSTER_SCALE_RATE,ins.layer)
      boss:SetHpPercent(100*scale) boss:SetAttackPercent(100*scale)
      return
    end

    local num = Util.GetWaveMonsterNum(ins.layer)
    Util.SendTeamMsg(ins.team,string.format("第%d波来袭,剩余%d波",ins.currentWave,CFG.WAVE_COUNT-ins.currentWave+1))
    local scale = math.pow(CFG.MONSTER_SCALE_RATE,ins.layer)
    for i=1,num do
      local x,y = math.random(70,130),math.random(70,130)
      local m = SpawnMonster(CFG.DUNGEON_MAP_ID,x,y,"秘境畸变怪")
      m:SetHpPercent(100*scale) m:SetAttackPercent(100*scale)
    end
end
function MythicDungeon.WaveSystem.OnMonsterDead(m,killer)
    local ok,err = pcall(function()
      if not killer then return end
      local id = killer:GetVarNumber(CFG.VAR_PREFIX.."InInstance")
      local ins = InstanceMgr.GetInstance(id)
      if not ins or ins.isFinished then return end
      if m:GetName() == CFG.BOSS_NAME then
            ins.bossDead=true
            MythicDungeon.Settlement.DoSettlement(id)
            return
      end
      if GetMapMonsterCount(CFG.DUNGEON_MAP_ID) <=1 then
            SetTimer(2000,function() MythicDungeon.WaveSystem.StartNextWave(id) end)
      end
    end)
    if not ok then print("[秘境报错]"..err) end
end

-- 6.结算评分系统
MythicDungeon.Settlement = {}
function MythicDungeon.Settlement.DoSettlement(id)
    local ins = InstanceMgr.GetInstance(id)
    if not ins or ins.isFinished then return end
    ins.isFinished=true
    local remain = ins.deadlineStamp - os.time()
    local score = (remain/12)+(100-ins.totalTeamDeath*CFG.DEATH_DEDUCT_SCORE)
    score = math.max(score,1)
    local star=1
    if score>=90 then star=5 elseif score>=70 then star=4
    elseif score>=50 then star=3 elseif score>=30 then star=2 end
    local rule = CFG.STAR_CONFIG
    local newLayer = math.max(1,ins.layer+rule.layerAdd)

    Util.ForeachTeam(ins.team,function(p)
      p:GiveYB(math.floor(ins.layer*1000*rule.dropRate))
      p:GiveGold(ins.layer*15000)
      p:GiveItem("秘境通关宝箱",1)
      local max = p:GetVarNumber(CFG.VAR_PREFIX.."MaxLayer")
      if ins.layer>max then p:SetVarNumber(CFG.VAR_PREFIX.."MaxLayer",ins.layer) end
    end)

    local owner = GetPlayerByName(ins.owner)
    if owner then
      owner:DelItem(CFG.KEY_ITEM_PREFIX.."("..ins.layer.."层)",1)
      owner:GiveItem(CFG.KEY_ITEM_PREFIX.."("..newLayer.."层)",1)
    end

    Util.SendTeamMsg(ins.team,string.format("通关%d层!%d星,钥匙变为%d层",ins.layer,star,newLayer),1)
    SendAllMsg(string.format("[秘境公告]%d层秘境通关,%d星评价!",ins.layer,star))
    SetTimer(3000,function() InstanceMgr.ClearInstance(id) end)
end

-- 7.NPC交互
function MythicDungeon.OnTalk(p)
    if p:GetVarNumber(CFG.VAR_PREFIX.."InInstance")>0 then
      p:SendMsg(2,"副本中无法交互!") return
    end
    p:SendMsg(0,"秘境使者:开启大秘境挑战")
    p:AddSelectMenu("开启秘境挑战","MythicDungeon.StartDungeon")
    p:AddSelectMenu("查询个人记录","MythicDungeon.ShowRecord")
    p:AddSelectMenu("查看秘境规则","MythicDungeon.ShowRule")
end
function MythicDungeon.StartDungeon(p)
    local ok,err=pcall(function()
      local team = p:GetTeamMemberList()
      local n=#team
      if n<CFG.TEAM_MIN or n>CFG.TEAM_MAX then
            p:SendMsg(2,string.format("需%d-%d人,当前%d人",CFG.TEAM_MIN,CFG.TEAM_MAX,n)) return
      end
      local now=os.time()
      if now-p:GetVarNumber(CFG.VAR_PREFIX.."LastOpenTime")<CFG.TEAM_COOLDOWN then
            p:SendMsg(2,"队伍冷却中!") return
      end
      local layer=nil
      for _,item in ipairs(p:GetBagItemList()) do
            layer=Util.ParseKeyLayer(item:GetName())
            if layer then break end
      end
      if not layer then p:SendMsg(2,"无秘境钥匙!") return end
      p:SetVarNumber(CFG.VAR_PREFIX.."LastOpenTime",now)
      InstanceMgr.CreateInstance(team,layer,p:GetName())
    end)
    if not ok then p:SendMsg(2,"开启失败:"..err) end
end
function MythicDungeon.ShowRecord(p)
    p:SendMsg(0,"=====个人记录=====")
    p:SendMsg(0,"最高层数:"..p:GetVarNumber(CFG.VAR_PREFIX.."MaxLayer").."层")
end
function MythicDungeon.ShowRule(p)
    p:SendMsg(0,"【规则】2-5人组队+钥匙开启|限时清怪杀BOSS|星级升降钥匙|禁止回城")
end

-- 8.全局触发器
function MythicDungeon.OnGlobalMonsterDead(m,k) MythicDungeon.WaveSystem.OnMonsterDead(m,k) end
function MythicDungeon.OnPlayerDead(p)
    local ok,err=pcall(function()
      local ins = InstanceMgr.GetInstance(p:GetVarNumber(CFG.VAR_PREFIX.."InInstance"))
      if ins then ins.totalTeamDeath=ins.totalTeamDeath+1;Util.SendTeamMsg(ins.team,"队伍死亡+1",2) end
    end)
end
function MythicDungeon.OnLeaveMap(p,map)
    if map~=CFG.DUNGEON_MAP_ID then return end
    local ok,err=pcall(function()
      local ins = InstanceMgr.GetInstance(p:GetVarNumber(CFG.VAR_PREFIX.."InInstance"))
      if ins then p:SendMsg(2,"中途离场,挑战作废!");InstanceMgr.ClearInstance(ins.insId) end
    end)
end

return MythicDungeon新建文件:MythicDungeon.lua 放入服务端 LuaScript 文件夹NPC 绑定:土城秘境使者 NPC 脚本
lualocal MD = require("MythicDungeon")
function OnTalk(player) MD.OnTalk(player) end

[*]触发器绑定

[*]怪物死亡:MythicDungeon.OnGlobalMonsterDead(monster, killer)
[*]玩家死亡:MythicDungeon.OnPlayerDead(player)
[*]离开地图:MythicDungeon.OnLeaveMap(player, mapId)

[*]配套配置

[*]地图 ID:3001(秘境专属地图)
[*]道具:秘境钥匙(X层)、秘境通关宝箱


我爱嘟嘟 发表于 2026-6-17 20:43:34

秘境通关宝箱

牛头 发表于 2026-6-17 21:21:13

我爱嘟嘟 发表于 2026-6-17 20:43
秘境通关宝箱

可以自己再另外设置···包括通关的其他奖励

qq154886255 发表于 2026-6-18 10:29:48

论坛发了这么多的lua脚本但是从来没见过任何一个人发相关图片或者视频 这些所谓的lua是能正常用还是直接复制ai的|?????????????????

goodsnowv 发表于 2026-6-18 11:21:25

谢谢楼主分享!

40731803 发表于 2026-6-20 08:36:53

你确定是现在VV的lua语法格式?

牛头 发表于 2026-6-20 21:01:56

40731803 发表于 2026-6-20 08:36
你确定是现在VV的lua语法格式?

只是用LUA语法写的···VV我都还没拿到引擎····也没有办法测试:P·····所我只能给个框架,懂的人自己可以去测试····等拿到引擎了,后面我会用VV再写一版直接能用的

40731803 发表于 2026-6-21 08:52:12

牛头 发表于 2026-6-20 21:01
只是用LUA语法写的···VV我都还没拿到引擎····也没有办法测试·····所我只能给个框架,懂的人 ...

可以,贡献达到500后,加我QQ314110995,然后我拉你进群拿最新包就行了

初见ol 发表于 2026-6-21 17:14:37

顶顶顶顶

永无止境 发表于 2026-7-4 18:34:10

等我水够了VV也公测了!
页: [1] 2
查看完整版本: 仿魔兽大秘境系统