查看: 58|回复: 8

[Lua] 仿魔兽大秘境系统

[复制链接]

16

主题

13

回帖

555

积分

高级会员

积分
555
发表于 4 天前 | 显示全部楼层 |阅读模式
本帖最后由 牛头 于 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 = {
        [5]={layerAdd=3,dropRate=2.2},[4]={layerAdd=2,dropRate=1.7},
        [3]={layerAdd=1,dropRate=1.3},[2]={layerAdd=0,dropRate=1.0},[1]={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[insID] = data
    for _,name in ipairs(teamList) do _playerInstanceMap[name]=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[id] 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[name]=nil end
    _instanceList[id] = 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[star]
    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
        ownerelItem(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层)、秘境通关宝箱


2

主题

68

回帖

596

积分

高级会员

积分
596
发表于 4 天前 | 显示全部楼层
秘境通关宝箱

16

主题

13

回帖

555

积分

高级会员

积分
555
楼主 发表于 4 天前 | 显示全部楼层

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

1

主题

230

回帖

1669

积分

金牌会员

积分
1669
发表于 3 天前 | 显示全部楼层
论坛发了这么多的lua脚本  但是从来没见过任何一个人发相关图片或者视频 这些所谓的lua是能正常用还是直接复制ai的|?????????????????

1

主题

39

回帖

6638

积分

论坛元老

积分
6638
发表于 3 天前 | 显示全部楼层
谢谢楼主分享!

15

主题

46

回帖

1205

积分

管理员

积分
1205
发表于 昨天 08:36 | 显示全部楼层
你确定是现在VV的lua语法格式?

16

主题

13

回帖

555

积分

高级会员

积分
555
楼主 发表于 昨天 21:01 | 显示全部楼层
40731803 发表于 2026-6-20 08:36
你确定是现在VV的lua语法格式?

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

15

主题

46

回帖

1205

积分

管理员

积分
1205
发表于 15 小时前 | 显示全部楼层
牛头 发表于 2026-6-20 21:01
只是用LUA语法写的···VV我都还没拿到引擎····也没有办法测试·····所我只能给个框架,懂的人 ...

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

0

主题

10

回帖

123

积分

注册会员

积分
123
发表于 6 小时前 | 显示全部楼层
顶顶顶顶
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表