// 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 iend: 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";