Files
metin2-client/src/platform/ScriptLib/DungeonScript.inc
T
shenandshen 507e687867 feat: add MeshPort overlay networking, item actions window, and AVA2 client enhancements
- add MeshPort peer-to-peer overlay networking SDK, C++ integration, and Python bindings
- add item actions quick use/split/delete/lock overlay window (mt_item_actions)
- add shop name range and NPC name visibility settings to system display options
- support battlepass ticket activation and month validation in mt_battlepass
- support boss tracking teleport destination button in mt_bosses
- support asynchronous account registration cancellation and pending login cancellation
- update AVA2 item conversion for attr lock scrolls and battlepass tickets
- update .gitignore for local build outputs and caches
2026-10-16 01:29:57 -07:00

195 lines
9.5 KiB
PHP

// PORT: dungeon information from installed quests; original NPC admission remains authoritative.
static const char c_szDungeonScript[] = R"PY(# -*- coding: utf-8 -*-
import app
import chat
import game
import grp
import ui
import wndMgr
import mt_feature_service
import mt_wiki
from mt_wiki import B
from mt_biolog import Action, Text, L
g_window=None
def Duration(seconds):
seconds=max(0,int(seconds))
return '%d天 %02d:%02d:%02d'%(seconds//86400,(seconds//3600)%24,(seconds//60)%60,seconds%60) if seconds>=86400 else '%02d:%02d:%02d'%(seconds//3600,(seconds//60)%60,seconds%60)
class DungeonWindow(ui.Window):
def __init__(self):
ui.Window.__init__(self);self.SetWindowName('DungeonInformationWindow');self.AddFlag('float');self.AddFlag('movable')
self.values=None;self.pending=False;self.generation=0;self.received=0;self.serverTime=0;self.nextQuery=0
self.lastTick=app.GetTime();self.reminder=None;self.tab=0;self.dimensions=None;self.stale=True
self.heading=Text(self,18,18,'副本信息',0xffffd68b)
self.closeButton=Action(self,0,6,60,'关闭',self.Hide)
self.lairButton=Action(self,18,52,150,'龙穴',lambda:self.Select(0))
self.weeklyButton=Action(self,180,52,150,'龙穴周任务',lambda:self.Select(1))
self.refreshButton=Action(self,342,52,80,'刷新',self.Query)
self.status=Text(self,18,108,'正在读取任务状态')
self.rows=[Text(self,24,142+32*i,'',0xffefdfc3) for i in range(6)]
self.remindButton=Action(self,18,0,146,'到期提醒:关',self.Remind)
self.entryButton=Action(self,180,0,146,'查看入口',self.Entry)
self.travelButton=Action(self,342,0,146,'传送到入口',self.Travel)
self.note=Text(self,18,0,'当前仅接入已核实的龙穴及相关周任务。',0xffb2aa9a)
self.Reflow();self.Hide()
def Open(self):
if mt_wiki.g_window:mt_wiki.g_window.Load()
self.Reflow();self.SetCenterPosition();self.Show();self.SetTop();self.Query()
def Select(self,tab):
self.tab=tab;self.RenderRows()
def Query(self):
if self.pending:return
self.pending=True;self.generation+=1;generation=self.generation
self.status.SetText(L('正在同步服务器任务…'));self.refreshButton.SetEnabled(False)
def received(data,error):
if generation!=self.generation:return
self.pending=False;self.refreshButton.SetEnabled(True);self.nextQuery=app.GetTime()+15
if error:
self.stale=True;self.values=None
if error=='unsupported':self.reminder=None
self.status.SetText(L('服务端未启用此副本查询' if error=='unsupported' else '状态未同步,请稍后刷新'))
else:
self.values=dict(data['rows']);self.received=app.GetTime();self.serverTime=data['time'];self.stale=False
self.status.SetText(L('已同步任务状态;入场仍由原 NPC 检查'))
self.CheckReminder()
self.RenderRows()
if mt_feature_service.g_service:mt_feature_service.g_service.Request('dungeons',0,0,received)
else:received(None,'unsupported')
def Now(self):
return self.serverTime+max(0,app.GetTime()-self.received)
def RenderRows(self):
lines=[];v=self.values
if v is not None and not self.stale:
now=self.Now()
if self.tab==0:
if now<=v['lair_join_end']:state='队伍加入窗口(需要口令)'
elif now<=v['lair_end']:state='场次占用中'
else:state='可向入口 NPC 申请新场次'
catalogue=mt_wiki.g_window.catalogue if mt_wiki.g_window else None
ticket=catalogue.FindItem(v['lair_ticket']) if catalogue else None
name=B(ticket['name']) if ticket else '门票'
lines=['状态:'+state,'新场次等待:'+Duration(v['lair_end']+1-now),
'加入窗口:'+Duration(v['lair_join_end']+1-now),
'%s:需要 %d,持有 %d'%(name,v['lair_tickets'],v['lair_owned']),
'场次为全服共享;不是个人通关冷却。','入场口令、门票及 NPC 锁由原任务校验。']
elif 'weekly_end' in v:
lines=['个人周任务等待:'+Duration(v['weekly_end']+1-now),
'当前任务:'+('收集中' if v.get('weekly_active') else '未在收集阶段'),
'收集剩余:'+(Duration(v['weekly_hunt_end']-now) if v.get('weekly_active') else '—'),
'周任务与龙穴入场计时独立。','接取时开始计时,完成后不会重置冷却。','资格、收集材料和奖励由原任务检查。']
else:lines=['服务端未安装或未提供周任务查询。']
for i,row in enumerate(self.rows):
if i<len(lines):row.SetText(L(lines[i]));row.Show()
else:row.Hide()
self.remindButton.SetText('到期提醒:'+('开' if self.reminder is not None and self.reminder[0]==self.tab else '关'))
self.remindButton.SetEnabled(v is not None and not self.stale and (self.tab==0 or 'weekly_end' in v))
self.entryButton.SetEnabled(v is not None and not self.stale and (self.tab==0 or 'weekly_npc' in v))
service=mt_feature_service.g_service
if self.tab==0 and service and service.mask is not None and service.mask&512:self.travelButton.Show()
else:self.travelButton.Hide()
self.travelButton.SetEnabled(v is not None and not self.stale)
def Travel(self):
if self.values is None or self.stale or self.tab!=0:return
import mt_boss
mt_boss.Travel(2493)
def Entry(self):
if self.values is None or self.stale:return
v=self.values
text='请前往流放者洞窟深处,与修罗之魂对话进入龙穴。' if self.tab==0 else '请前往流放者洞窟二层,与猛将之魂对话接取周任务。'
self.status.SetText(L(text))
def Remind(self):
if self.values is None or self.stale:return
if self.reminder is not None and self.reminder[0]==self.tab:self.reminder=None
else:
end=self.values.get('lair_end' if self.tab==0 else 'weekly_end',0)
if end<self.Now():return
self.reminder=(self.tab,end)
self.RenderRows()
def CheckReminder(self):
if self.reminder is None or self.values is None or self.stale:return
tab,old=self.reminder;key='lair_end' if tab==0 else 'weekly_end'
if key not in self.values:self.reminder=None;return
end=self.values[key]
# A later run/quest extends the authoritative deadline; never announce the old one.
self.reminder=(tab,end)
if self.Now()>end:
chat.AppendChat(chat.CHAT_TYPE_INFO,L('龙穴新场次等待已结束,请向入口申请。' if tab==0 else '龙穴周任务等待已结束,请向 NPC 确认资格。'))
self.reminder=None
def Reflow(self):
dims=(wndMgr.GetScreenWidth(),wndMgr.GetScreenHeight())
if dims==self.dimensions:return
self.dimensions=dims;width=min(660,max(500,dims[0]-16));height=min(450,max(300,dims[1]-16));self.SetSize(width,height)
self.closeButton.SetPosition(width-76,6)
self.travelButton.SetPosition(342,height-74)
spacing=min(32,max(16,(height-250)//5))
for i,row in enumerate(self.rows):row.SetPosition(24,132+i*spacing)
self.remindButton.SetPosition(18,height-74);self.entryButton.SetPosition(180,height-74);self.note.SetPosition(18,height-18)
self.SetCenterPosition()
def OnUpdate(self):
now=app.GetTime();gap=now-self.lastTick;self.lastTick=now
if gap>5 or gap<0:self.stale=True;self.nextQuery=0
active=self.IsShow() or self.reminder is not None
if active and not self.pending and now>=self.nextQuery and (self.values is not None or self.reminder is not None or self.stale and self.nextQuery==0):self.Query()
if self.IsShow():self.Reflow();self.RenderRows()
# Reminders only fire after a fresh server query confirms expiry.
if self.reminder is not None and not self.pending and self.values is not None and self.Now()>self.reminder[1] and now>=self.nextQuery:self.Query()
def OnRender(self):
x,y=self.GetGlobalPosition();grp.SetColor(0xf51b1916);grp.RenderBar(x,y,self.GetWidth(),self.GetHeight());grp.SetColor(0xff9b7b48);grp.RenderBox(x,y,self.GetWidth(),self.GetHeight())
def Hide(self):
ui.Window.Hide(self)
def OnPressEscapeKey(self):self.Hide();return True
def Destroy(self):
self.generation+=1;self.reminder=None;self.values=None;self.Hide()
for b in (self.closeButton,self.lairButton,self.weeklyButton,self.refreshButton,self.remindButton,self.entryButton,self.travelButton):b.event=None
class ReminderTick(ui.Window):
def __init__(self):
ui.Window.__init__(self);self.AddFlag('not_pick');self.SetSize(0,0);self.Show()
def OnUpdate(self):
if g_window and not g_window.IsShow():g_window.OnUpdate()
def Toggle():
if g_window:
if g_window.IsShow():g_window.Hide()
else:g_window.Open()
_open=game.GameWindow.Open
_close=game.GameWindow.Close
g_tick=None
def Open(self):
global g_window,g_tick
_open(self);g_window=DungeonWindow();g_tick=ReminderTick()
def Close(self):
global g_window,g_tick
if g_tick:g_tick.Hide();g_tick=None
if g_window:g_window.Destroy();g_window=None
_close(self)
game.GameWindow.Open=Open
game.GameWindow.Close=Close
)PY";