Add feature queries, dungeon information and event calendar UI
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// PORT: AVA2-inspired encyclopedia, adapted to current server templates and touch controls.
|
||||
// Gameplay/loot is never computed here. The schema explicitly advertises static data only.
|
||||
// Static templates plus negotiated read-only server views; no gameplay mutation or loot RNG here.
|
||||
static const char c_szWikiScript[] = R"PY(# -*- coding: utf-8 -*-
|
||||
import app
|
||||
import game
|
||||
@@ -10,13 +10,22 @@ import player
|
||||
import ui
|
||||
import wndMgr
|
||||
import weakref
|
||||
import mt_feature_service
|
||||
from mt_biolog import Action, Text, L
|
||||
from mt_wiki_data import Catalogue
|
||||
|
||||
PATH='wiki/catalogue.txt'
|
||||
g_window=None
|
||||
g_entry=None
|
||||
CAPABILITIES={'schema':1,'static_catalogue':True,'live_drops':False,'chest_preview':False,'model_preview':False}
|
||||
def ServerCapabilities():
|
||||
mask=mt_feature_service.g_service.mask if mt_feature_service.g_service else None
|
||||
return {'schema':1,'static_catalogue':True,'model_preview':False,
|
||||
'live_drops':None if mask is None else bool(mask&1),
|
||||
'chest_preview':None if mask is None else bool(mask&2),
|
||||
'attributes':None if mask is None else bool(mask&4),
|
||||
'sources':None if mask is None else bool(mask&8),
|
||||
'dungeons':None if mask is None else bool(mask&16),
|
||||
'calendar':None if mask is None else bool(mask&32)}
|
||||
|
||||
|
||||
def U(text):
|
||||
@@ -46,7 +55,11 @@ class WikiWindow(ui.Window):
|
||||
self.mode='items';self.category=0;self.page=0;self.detailPage=0
|
||||
self.catalogue=None;self.error='';self.selected=None;self.results=[];self.links=[]
|
||||
self.dimensions=None;self.lastQuery=None;self.history=[]
|
||||
self.remote=None;self.remoteData=None;self.remoteError=None;self.remoteSerial=0
|
||||
self.heading=Text(self,18,18,'游戏百科',0xffffd68b)
|
||||
self.detailsButton=Action(self,130,6,120,'详细属性',self.OpenDetails)
|
||||
self.dungeonButton=Action(self,258,6,96,'副本信息',self.OpenDungeons)
|
||||
self.calendarButton=Action(self,362,6,80,'活动日历',self.OpenCalendar)
|
||||
self.closeButton=Action(self,0,6,52,'关闭',self.Hide)
|
||||
self.backButton=Action(self,0,6,72,'返回',self.Back)
|
||||
self.searchBox=ui.SlotBar();self.searchBox.SetParent(self);self.searchBox.SetPosition(18,54)
|
||||
@@ -72,6 +85,18 @@ class WikiWindow(ui.Window):
|
||||
self.Reflow()
|
||||
self.Hide()
|
||||
|
||||
def OpenCalendar(self):
|
||||
import mt_calendar
|
||||
mt_calendar.Toggle()
|
||||
|
||||
def OpenDungeons(self):
|
||||
import mt_dungeons
|
||||
mt_dungeons.Toggle()
|
||||
|
||||
def OpenDetails(self):
|
||||
import mt_details
|
||||
mt_details.Toggle()
|
||||
|
||||
def Load(self):
|
||||
if self.catalogue:return True
|
||||
try:
|
||||
@@ -112,6 +137,13 @@ class WikiWindow(ui.Window):
|
||||
self.page+=step;self.Refresh()
|
||||
|
||||
def DetailPage(self,step):
|
||||
if self.remote and self.remoteData:
|
||||
pages=max(1,(len(self.links)+self.rowCount-1)//self.rowCount)
|
||||
kind,subject,offset=self.remote
|
||||
if step>0 and self.detailPage+1>=pages and offset+len(self.remoteData['rows'])<self.remoteData['total']:
|
||||
self.QueryRemote(kind,subject,offset+8);return
|
||||
if step<0 and self.detailPage==0 and offset>0:
|
||||
self.QueryRemote(kind,subject,max(0,offset-8));return
|
||||
self.detailPage+=step;self.RefreshDetail()
|
||||
|
||||
def Refresh(self):
|
||||
@@ -132,9 +164,12 @@ class WikiWindow(ui.Window):
|
||||
|
||||
def Select(self,mode,row,remember=True):
|
||||
if remember and self.selected:self.history=(self.history+[self.selected])[-24:]
|
||||
self.remote=None;self.remoteData=None;self.remoteSerial+=1
|
||||
self.selected=(mode,row['id']);self.detailPage=0;self.RefreshDetail()
|
||||
|
||||
def Back(self):
|
||||
if self.remote:
|
||||
self.remote=None;self.remoteData=None;self.remoteSerial+=1;self.detailPage=0;self.RefreshDetail();return
|
||||
if self.history:
|
||||
self.selected=self.history.pop();self.detailPage=0;self.RefreshDetail()
|
||||
|
||||
@@ -142,6 +177,14 @@ class WikiWindow(ui.Window):
|
||||
offset=self.detailPage*self.rowCount+index
|
||||
if offset<len(self.links) and self.links[offset][1]:
|
||||
vnum=self.links[offset][1]
|
||||
if isinstance(vnum,tuple):
|
||||
if vnum[0]=='query':self.QueryRemote(vnum[1],vnum[2],0)
|
||||
elif vnum[0]=='mobs':
|
||||
row=self.catalogue.mobs.get(vnum[1])
|
||||
if row:self.Select('mobs',row)
|
||||
else:self.QueryRemote('mob',vnum[1],0)
|
||||
elif vnum[0]=='items':self.Open(vnum[1])
|
||||
return
|
||||
row=self.catalogue.FindItem(vnum)
|
||||
if row:self.Select('items',row)
|
||||
else:self.status.SetText(L(B('目标物品未收录:#%d'%vnum)))
|
||||
@@ -149,7 +192,7 @@ class WikiWindow(ui.Window):
|
||||
def RefreshDetail(self):
|
||||
for b in self.detailButtons:b.Hide()
|
||||
self.links=[];self.icon.Hide()
|
||||
self.backButton.SetEnabled(bool(self.history))
|
||||
self.backButton.SetEnabled(bool(self.history) or self.remote is not None)
|
||||
if not self.selected or not self.catalogue:
|
||||
self.dprev.SetEnabled(False);self.dnext.SetEnabled(False)
|
||||
return
|
||||
@@ -189,18 +232,74 @@ class WikiWindow(ui.Window):
|
||||
else:
|
||||
cost=self.catalogue.FindItem(offer['cost'])
|
||||
lines.append((u'研究兑换:'+(cost['name'] if cost else u'#%d'%offer['cost'])+u' ×%d'%offer['amount'],offer['cost'] if cost else 0))
|
||||
lines.append((u'怪物掉落来源:尚未接入',0))
|
||||
if row['kind']=='ITEM_GIFTBOX':lines.append((u'宝箱奖励预览:尚未接入',0))
|
||||
lines.append((u'查看获取来源',('query','sources',vnum)))
|
||||
if row['kind'] in ('ITEM_GIFTBOX','ITEM_TREASURE_BOX'):lines.append((u'宝箱奖励预览',('query','chest',vnum)))
|
||||
else:
|
||||
lines=[(u'类型:'+U('矿石' if row['kind']=='STONE' else '怪物')+u' / '+U(RANK_NAMES.get(row['rank'],'普通')),0),(u'等级:%d 生命:%d'%(row['level'],row['hp']),0),
|
||||
(u'基础攻击:%d—%d'%(row['attack_min'],row['attack_max']),0),
|
||||
(u'基础防御:%d 经验:%d'%(row['defense'],row['exp']),0),(u'掉落列表:尚未接入',0)]
|
||||
(u'基础防御:%d 经验:%d'%(row['defense'],row['exp']),0),(u'查看服务端掉落列表',('query','mob',vnum))]
|
||||
if self.remote:lines=self.RemoteLines()
|
||||
self.links=lines
|
||||
pages=max(1,(len(lines)+self.rowCount-1)//self.rowCount)
|
||||
self.detailPage=max(0,min(self.detailPage,pages-1))
|
||||
for i,(label,link) in enumerate(lines[self.detailPage*self.rowCount:(self.detailPage+1)*self.rowCount]):
|
||||
self.detailButtons[i].SetText(B(label,max(15,(self.split-40)//12)));self.detailButtons[i].SetEnabled(bool(link));self.detailButtons[i].label.SetPackedFontColor(0xffefdbac if link else 0xffc8bea8);self.detailButtons[i].Show()
|
||||
self.dprev.SetEnabled(self.detailPage>0);self.dnext.SetEnabled(self.detailPage+1<pages)
|
||||
before=bool(self.remoteData and self.remote and self.remote[2]>0)
|
||||
after=bool(self.remoteData and self.remote and self.remote[2]+len(self.remoteData['rows'])<self.remoteData['total'])
|
||||
self.dprev.SetEnabled(self.detailPage>0 or before);self.dnext.SetEnabled(self.detailPage+1<pages or after)
|
||||
|
||||
def QueryRemote(self,kind,subject,offset=0):
|
||||
self.remote=(kind,subject,offset);self.remoteData=None;self.remoteError=None;self.detailPage=0;self.remoteSerial+=1
|
||||
serial=self.remoteSerial
|
||||
self.RefreshDetail()
|
||||
def received(data,error):
|
||||
if self.remoteSerial!=serial or self.remote!=(kind,subject,offset):return
|
||||
self.remoteData=data;self.remoteError=error;self.RefreshDetail()
|
||||
if mt_feature_service.g_service:mt_feature_service.g_service.Request(kind,subject,offset,received)
|
||||
else:received(None,'unsupported')
|
||||
|
||||
def RemoteLines(self):
|
||||
if self.remoteError:
|
||||
text={'unsupported':'服务端尚未启用此查询','unsupported_chest':'此道具未接入通用宝箱预览',
|
||||
'missing':'服务端不存在此目标','timeout':'查询超时,请返回后重试','bad_reply':'查询数据异常,请更新客户端',
|
||||
'rate':'操作过快,请稍后重试','too_many':'来源过多,请按怪物分别查询'}.get(self.remoteError,'查询失败,请返回后重试')
|
||||
return [(U(text),0)]
|
||||
if self.remoteData is None:return [(U('正在向服务器查询…'),0)]
|
||||
data=self.remoteData;kind,subject,offset=self.remote
|
||||
lines=[]
|
||||
if kind=='chest':
|
||||
lines.append((U('各项独立判定,可能多项或无奖励' if data['mode']==11 else '按权重随机抽取一项'),0))
|
||||
lines.append((U('仅预览;条件、钥匙与消耗仍由服务器校验'),0))
|
||||
else:
|
||||
lines.append((U('已加载常规掉落;任务/活动/矿石额外掉落未全覆盖'),0))
|
||||
lines.append((U('列出候选及限制,不表示最终中奖率'),0))
|
||||
for vnum,count,rule,low,high,weight,total,rare in data['rows']:
|
||||
target=0
|
||||
if kind=='chest' and vnum<=7:
|
||||
label={1:U('金币'),2:U('经验'),3:U('召唤怪物'),4:U('减速效果'),5:U('生命损失'),6:U('中毒效果'),7:U('召唤怪物组')}.get(vnum,U('特殊效果'))
|
||||
label+=u' %d'%count
|
||||
elif kind=='sources' and rule<20:
|
||||
row=self.catalogue.mobs.get(vnum);label=row['name'] if row else u'怪物 #%d'%vnum
|
||||
target=('mobs',vnum);label+=u' ×%d'%count
|
||||
else:
|
||||
row=self.catalogue.FindItem(vnum);label=row['name'] if row else u'物品 #%d'%vnum
|
||||
target=('items',vnum)
|
||||
if kind!='sources':label+=u' ×%d'%count
|
||||
lines.append((label,target))
|
||||
if kind=='chest':
|
||||
detail=(u'独立判定:%d/100'%min(weight,100)) if rule==11 else (u'权重:%d / %d'%(weight,total))
|
||||
if rare:detail+=u' 可能附带随机属性'
|
||||
elif rule>=20:detail=U('宝箱来源;点击可继续查看预览')
|
||||
elif rule==0:detail=u'通用掉落;角色等级 %d—%d'%(low,high)
|
||||
elif rule==2:detail=u'击杀组候选权重 %d/%d'%(weight,total)
|
||||
elif rule==3:detail=u'等级组;角色至少 %d 级'%low
|
||||
elif rule==4:detail=U('需掉宝手套或对应增益资格')
|
||||
elif rule==5:detail=U('怪物模板附加掉落')
|
||||
else:detail=U('怪物独立掉落组')
|
||||
lines.append((detail,0))
|
||||
if not data['rows']:lines.append((U('当前查询范围内无配置;不代表所有途径均不存在'),0))
|
||||
lines.append((U('第 %d—%d 条,共 %d 条')%(offset+1 if data['total'] else 0,offset+len(data['rows']),data['total']),0))
|
||||
return lines
|
||||
|
||||
def Reflow(self):
|
||||
dims=(wndMgr.GetScreenWidth(),wndMgr.GetScreenHeight())
|
||||
@@ -243,8 +342,8 @@ class WikiWindow(ui.Window):
|
||||
|
||||
def Destroy(self):
|
||||
self.Hide();self.search.SetReturnEvent(None);self.search.SetEscapeEvent(None)
|
||||
for b in self.listButtons+self.detailButtons+[self.closeButton,self.backButton,self.searchButton,self.itemTab,self.mobTab,self.categoryButton,self.prev,self.next,self.dprev,self.dnext]:b.event=None
|
||||
self.catalogue=None
|
||||
for b in self.listButtons+self.detailButtons+[self.detailsButton,self.dungeonButton,self.calendarButton,self.closeButton,self.backButton,self.searchButton,self.itemTab,self.mobTab,self.categoryButton,self.prev,self.next,self.dprev,self.dnext]:b.event=None
|
||||
self.remoteSerial+=1;self.catalogue=None
|
||||
|
||||
|
||||
class Entry(Action):
|
||||
|
||||
Reference in New Issue
Block a user