--[[
传奇大秘境系统
模块化:配置区 | 工具库 | 实例管理 | 刷怪系统 | 结算评分 | 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
owner
elItem(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