JS 之腳類型

車輛腳本

MTR 四代之「車輛」者,一節車廂模型之條目也;玩家於側線之界面,自如編組以成列。腳本不繫乎單節之廂,亦不繫乎整列,乃繫乎一腳本之條:車輛之條以 scriptId 引之,多車得復用同一之腳——混編、疾徐、NTE 相容,三美兼焉。

回呼凡三(ctx 者 VehicleScriptContext 也,vehicle 者 VehicleWrapper 也):

function create(ctx, state, vehicle) { /* 首繪之前:建紋理、註回呼 */ }
function render(ctx, state, vehicle) { /* 每幀:讀狀態、納繪製與聲音之令 */ }
function dispose(ctx, state, vehicle) { /* 退場:釋紋理 */ }

VehicleScriptContext(ctx)

成員釋義
getCarRenderManager(carIndex): RenderManager?取第 carIndex 節車廂之渲染管理器。基準者,車廂之中、底面上 1 m。非本條所懸之車,返 null
getCarBogieRenderManager(carIndex, bogieIndex): RenderManager?轉向架之渲染:基準相對第 bogieIndex 轉向架(0 前 1 後;單轉向架之廂為 0)
getCarSoundManager(carIndex): SoundManager?取車廂之聲源(基準者,車廂之中)
playAnnSound(id, volume, pitch)播報站之廣播——惟玩家乘本列時可聞,不定位
getScriptEntryId(): String腳本條目之 id
getMyCars(): int[]懸於本條名下之車廂索引陣列
setDebugInfo(key, value) / setDebugInfo(value)除錯覆層之顯(諸類之 ctx 皆有;GraphicsTexture 則徑以貼圖顯之)

已廢 drawCarModel(...) / playCarSound(...) / setDataFetchMode(...) 仍可用;然新碼請用上列之 RenderManager / SoundManager / 宣告之欄。drawConnModel(車廂連接之件)有兩式,呼之皆擲 NotImplementedException,以其未實現也;drawConnStretchTexture 則未設於此上下文。二者皆不可用。

VehicleWrapper(vehicle)

成員釋義
getStops() / getThisRouteStops() / getNextRouteStops()全部 / 本線路 / 下一線路 Stop 之列
getNextStopIndex(stopList, overrunTolerance=0.5): int下一停站於列中之索引;越界之容差以米計;既全過,則返 stopList.size()
isStopsDataFetched(): boolean停站之數據,已自服端取之否(SKIP 之式恆 false)
isRendered() / isCarRendered(cars...)整列 / 指定之廂方渲染否(未見剔)
isClientPlayerRiding(): boolean本機之玩家乘本列否
getId(): long · getSiding(): Siding? · getTransportMode(): TransportMode唯一之 id / 所屬之側線 / 交通之模式
getVehicleId(carIndex): String? · getLength(carIndex) · getWidth(carIndex) · getCarCount()資源包車型之 id / 車廂之長廣 / 車廂之數
getServiceAcceleration() · getServiceDeceleration() · getMaxManualSpeed() · isManualAllowed()側線所配之加 / 減速與手動駕駛之上限
getSpeedMs() / getSpeedKmh() · getNotchLevel() / getNotchPosition()車行之速;檔位(-5..5 尋常,±7 者 140%,-8 者緊急制動)與歸一之位
getDoorValue(): double · isDoorOpenable(car, pos[, pos2]) · isDoorOpen(car, pos[, pos2])車門之開度 0..1;按車廂內相對之坐標,察門可開 / 已開否
getPathData() · getRailProgress(carIndex=0) · getRailIndex(progress, roundDown) · getRailSpeed(pathIndex)路徑與進度(MTR 四代惟同步部分之路徑)
isCurrentlyManual() · isReversed() · isOnRoute() · getManualToAutomaticTime()手動駕駛 / 反向 / 在線路上 / 無人駕駛復歸 ATO 之時限(毫秒)
getDepartureIndex(): long · getTotalDwellTime() / getElapsedDwellTime()發車之序(UTC 零點歸零,-1 者,未發或人工也);停站之總時與既停之時(毫秒;不在站台為 -1;既停之時,按服端所同步原樣輸出)
getMtrVehicle(): VehicleExtensionMTR 底層之載具——內部 API 疆界之下,慎入
晏陽 getDirectionOfDoorOpening(): String開門之向所記:"not" / "left" / "right" / "all",詳見 晏陽增補
晏陽 getTransportType(): String規範交通類型之 id(含 MAGIC 之細分),詳見 晏陽增補

已廢:NTE 同名之舊口(getAllPlatforms / trainCars / speed / doorValue / mtrTrain 之屬)悉留可用,逐一與上表新口等價。

Stop(停站)

欄 / 法釋義
route: SimplifiedRoute? · station: Station? · platform: Platform?所屬之線路 / 車站 / 站台;數據未至者為 null(記得判其空)
destinationName: String · customDestination: String?終點顯示之名與自定終點之原文
routeInterchanges: List<RouteInterchange>本站轉乘之線路(不含本線,不含隱藏之線)
connectingInterchanges: Map<String, List<RouteInterchange>>按「相連車站之名」分組之轉乘表(同名之站併之)
dwellTimeMillis: long · distance: double停站之時(未知為 -1);停點軌道之進(SKIP 之式為 -1)
isRouteSwitchoverStop: boolean · roundUpRoute: Stop線路交接之站否;是,則經 roundUpRoute 取「下一線路之視」同一站之副
turnbackPlatform: boolean本站折返否

RouteInterchange(轉乘之線路)

color: int(RGB)· name: String;晏陽 增益之欄 getTransportMode(): TransportMode?、getTransportType(): String(含 MAGIC 之細分)、getRouteId(): long(來源線路之 id,缺數據為 0)——見 晏陽增補。

隱原版之顯牌

本乎原版模型之車輛,得以 hideDisplayParts(MTR 4)/ hide_display_cube(MTR 3)隱其自帶終點顯示之塊,改由腳本自繪。

車輛數據獲取之式

MTR 四代惟同步鄰近之數據,而車輛腳本往往須整趟線路之站名。JCM 乃設三級之式(dataFetchMode 欄):

式所行
SKIPMTR 四代之預設。不向服端索數據,本地竭力拼停站之表——不保其全。「車一動而鈴即響」之屬,惟觀車速之腳,足用矣
ALL先取整趟停站之表,復按表中之 id 逐一取車站 / 線路之數據。未取之先,所行同 SKIP;停站之表雖至手,Stop.station / Stop.route 仍或為 null——判其空
MANDATORY同 ALL,然數據悉至之前,腳本全然不啟。MTR 三代之式,此為預設(舊腳本預設數據即到即用,不耐 null)

裝飾腳本

懸於 MTR 裝飾(Decoration Object)模型之條:資源包中選該模型之裝飾方塊方被渲染,腳本乃始其工。註冊用 objects 加 objectScripts(MTR 三代舊式書於 eyecandy.json);內容條上 isScriptControlled: true,可隱原模型,悉以畫面付腳本。

回呼凡三:ctx 者 EyeCandyScriptContext 也,eyecandy 者 EyecandyWrapper 也。

EyeCandyScriptContext(ctx)

成員釋義
getRenderManager(): RenderManager基準者,方塊之位加 GUI 之移,含 GUI 之旋
getSoundManager(): SoundManager基準者,方塊之位;別有便捷之 ctx.playSound(id, volume, pitch),逕於方塊之處播之
drawModel(model, matrices?)畫模型(亦收 DynamicModelHolder)——與 getRenderManager().drawModel(...) 等價
events(): EyecandyEvents取事件:右鍵 events().onBlockUse.occurred() → 處之 → events().handled() 為確認;事件之詳 .detail().player() 予操作者之 PlayerEntity(手執刷者,右鍵弗計)
setOutlineShape(shape) / setCollisionShape(shape)自定輪廓 / 碰撞之匣(VoxelShape);玩刷之時則無效
setDebugInfo(...)與他類同

EyecandyWrapper(eyecandy)

getModelId(): String(當前模型條目之 id)· getTranslateX/Y/Z(): float(GUI 之移,米)· getRotateX/Y/Z(): float(GUI 之旋,弧度)· pos(): Vector3f(方塊加移)· blockPos(): Vector3f · facing(): Direction · facingAngle(): double(朝向之角)· isCrosshairTarget(): boolean(準星所指,可用以彈提示)· getFullBrightness(): boolean · redstoneLevel(): int(0–15)。

已廢:欄直讀之式(eyecandy.translateX、eyecandy.prefabId 之屬)仍可用。

例:右鍵而暗之循環樂

以 TickableSoundInstance.create(...) 加 setRepeatable(true) 佐 play() 懸循環之樂;右鍵五秒之內 setSoundVolume(0.25),屆時復歸 1.0;dispose 之中 stop——全碼見上游 sounds 之檔。

電梯腳本

懸於電梯轎廂之樣(材質)之條;玩家以電梯刷新器選其樣,乃效。註冊用 lifts 加 liftScripts;樣條之上 isScriptControlled: true,隱原模型。

回呼凡三:ctx 者 LiftScriptContext 也,lift 者 LiftWrapper 也。

LiftScriptContext(ctx)

getRenderManager(): RenderManager(基準者,電梯之基位加偏移,隨電梯而旋)· getSoundManager(): SoundManager(基準同上)· setDebugInfo(...)。

LiftWrapper(lift)與 Floor

成員釋義
getMtrLift(): Lift · getId(): longMTR 底層之物與唯一之 id
getWidth() / getHeight() / getDepth()轎廂之尺寸(米,刷新器所配)
getOffset(): Vector3f · getPos() / getRawPos()偏移;基位加偏移 / 基位
getAngleDegrees() / getAngleRadians() · isDoubleSided()朝向之角與雙開之門
getDoorValue(): float · getDirection(): int門之開度 0..1;行之向:-1 下行 / 0 靜止 / 1 上行
isDoorway1Openable() / isDoorway1Open() / isDoorway2...前 / 後門可開與已開
isClientPlayerRiding(): boolean · shouldRender(): boolean乘坐之中;未為遮擋所剔
getFloors(): List<Floor>所停之樓層;每項 getIndex()(0 為最底)· getNumber() / getDescription()(層軌刷之所配)· getPos() · isCurrentFloor() / isTargetFloor()

PIDS 腳本

別自為體

此制與 PIDS Layout Editor / PIDS Modularization / PIDS JSON(EpicPuppy),暨 MTR 四代早期測試版信息屏之自定,互不相容,格式莫能互導——其門雖峻,然能為更繁之版面。

joban_custom_resources.json 之 pids_images 陣列中,預設之物件帶 scriptFiles 或 scriptTexts,即為腳本之預設(scriptInput / scriptRateLimit 之義與他類同;Yanyang 亦受 scriptLocations 混入 C++,見 相雜之條):

{
    "pids_images": [
        {
            "id": "pids_demo",
            "name": "Demo Scripted PIDS",
            "scriptFiles": ["jsblock:scripts/pids_demo.js"],
            "scriptInput": { "theme": "dark" },
            "scriptRateLimit": 0.75
        }
    ]
}

預設之欄 outOfBound: boolean(上游 v2.3.0-prerelease.3):屏出相機視界之外,仍續繪之不輟——投影機之預設(pids_projector),須臾不可停者,賴乎此。

PIDS 之上所畫者,世界中之多邊形也,非 2D 之畫布——元素得出屏之界,界內之裁剪,腳本自任其責(誠須繁難裁剪 / 圖像之處理,以 GraphicsTexture 畫成而貼之)。繪之序,即其層:後畫者蓋先畫者;預設每繪一次,沿 z 之向移一小步(0.0002)以防 z-fighting;得以 ctx.setAutoZOrdering(false) 閉之,改手動 zOrder()(ctx.setZOrderStep(m) 調其步長)。

PIDSScriptContext(ctx)與 PIDSBlockEntity(pids)

ctx.getRenderManager() / ctx.getSoundManager()(基準者,方塊之位)· ctx.setAutoZOrdering(b) · ctx.setZOrderStep(m) · ctx.setDebugInfo(...)。

pids 成員釋義
type: String屏之型:rv_pids / rv_pids_sil_1 / rv_pids_sil_2 / lcd_pids / pids_projector / pids_1a
width / height / rows屏之寬高與到站之列數
isRowHidden(i) / getCustomMessage(i)第 i 行為方塊之配所隱否;行自定之文(未設則空串)
getTargetPlatformIds() · isPlatformAutoDetected() · isPlatformNumberHidden()所偵之站台 id 之列;自偵與否;站台之號隱與否
blockPos() · isKeyBlock() · station(): Station?方塊之坐標;雙面屏「鍵」塊與否;所在之車站(不在站中,或客戶端未知,為 null)
arrivals(): ArrivalEntries到站之數據,見下

ArrivalEntries 與 ArrivalEntry

ArrivalEntries:get(i): ArrivalEntry?(每站台至多取到站十條)· length(): int(到站之總數,上游 v2.3.0-prerelease.3)· mixedCarLength(): boolean · platforms(): List<Platform> · forEach(consumer) / forEach(platformId, consumer)(逐條而歷,免手動判空)。

ArrivalEntry:destination(): String · arrivalTime() / departureTime()(Unix 毫秒,以 new Date(v) 化為 JS 之期)· arrived() / departed() · deviation(): long(相對圖定:正者晚點,負者早點)· realtime(): boolean(圖定,或實時所推)· departureIndex(): long · terminating(): boolean(終到於本站)· route(): SimplifiedRoute? / routeId() / routeName() / routeNumber() / routeColor() / circularState() · platform(): Platform? / platformId() / platformName() · carCount() / cars(): List<CarDetails>。

Text / Texture / Rect(繪事三具)

三者皆「造 → 鏈式之配 → draw(ctx)」流式之物也;Rect 者,貼圖硬編為純白 PNG 之 Texture 也:

通用釋義
create() / create(comment)新建(註釋惟便於讀碼)
pos(x, y) · size(w, h)位與尺寸(屏像素之單位)
color(rgb)染色(RGB 整數)
naturalLight() · renderType(str) · matrices(m) · zOrder(n) · draw(ctx)按世界之光取其亮(預設全亮);渲染之型(同 QuadDrawCall);附加之變換;手動 z 序(既閉自動 z 乃效);納其繪
Text 專屬釋義
text(str) · scale(i) · fontMC() / font(id)內容;整體之縮放(預設 1,配 size 須自換算);原生之字體 / 資源之字體(預設 mtr:mtr;MTR 設置「自定字體」既閉,則無效)
leftAlign() / centerAlign() / rightAlign()對齊(預設左)
shadowed() · italic() · bold() · lineHeight(factor)影 / 斜 / 粗;行距之因(換行所資,預設 1,即 9 單位)
stretchXY() / scaleXY() / wrapText() / marquee() / marquee(durationTick) / withMarqueeProgress(p)溢出之策,四擇其一:沿軸拉伸 / 等比縮放 / 換行 / 跑馬燈(可指其期,或徑予進度 0..1)
measureWidth(): int按當前之配而量其寬——返 int,鏈至此而止

Texture 專屬:texture(id)(PNG 或 mcmeta)、uv(u1, v1, u2, v2)(0–1)。

動態紋理上乎 PIDS

PIDS 亦得貼 GraphicsTexture:create 之中 new GraphicsTexture(pids.width, pids.height);render 之中,既畫 upload(),復 Texture.create().texture(state.tex.identifier).size(pids.width, pids.height).draw(ctx);dispose 之中 close()——文字排版所不能為時之兜底之策,其代價則疾徐與記憶體也。

不須腳本乎?pids_images 中惟書 background / color / fonts / showWeather / showClock / hidePlatform / hideRow / topPadding / textOverflowMode 諸 JSON 之欄,即為「JSON 預設」——簡易改容,足用矣;且與腳本預設二擇其一,不可混書。

PSD 之腳(屏蔽之門)

為屏蔽之門之玻璃與其上之板作腳。於 mtr_custom_resources.json 增 psdScripts 之目——其式同 vehicleScripts(scriptLocations 之列,JS 與 C++ 可相混,逐條自辨其語),別立 type 之欄以擇所繫之面:

"psdScripts": [
    {
        "id": "psd_glass_pids",
        "type": "glass",
        "scriptLocations": [
            "mtr:my_pack/js/psd_glass_pids/main.js",
            "mtr:my_pack/native/psd_glass_pids"
        ],
        "dataFetchMode": "ALL"
    }
]

入遊戲,持新物「屏蔽門腳本編輯器」(與 MTR 之「刷」同列於創造之分類),於任意屏蔽門之玻璃(端玻璃、半高之 APG 閘門皆可;MTR、天津地鐵諸模組,凡本於 MTR PSD 之類者咸宜)或其上之板右擊之,界面既出,可擇同類之目或任一腳本 PIDS 之預設(PIDS 之腳如舊而行,畫布自適其面);擇「無腳本」則解其繫。所繫存於伺服器,同維之眾咸得同步。

  • 玻璃:衬以不透明之底——月台之側玄色(仿真 PIDS 深屏之觀),軌道之側純白(車中乘客惟見白板,不見鏡像之文)。腳之畫布居月台一側、藏於玻璃之內,自裁於玻璃透明之窗(依資源包模型與貼圖 alpha 而推;門框拉筋等不透明者仍顯於前而蔽其文);兩門之間連續之玻璃共為一段、同用一布,整柱兩格者上下合而為一。畫布隨窗自適(每方 96 像素)。
  • 其上之板:同一門區之上整段 psd_top 共用一布,其段不復顯舊有之箭頭 / 線圖——所顯者一由腳司之;餘段如故。畫布每方寬 96 像素(左右各留 0.125/16 之際)× 高 42 像素。

玻璃或頂板相連,未逾六十四格者,仍共一全幅,雖跨座標之界亦不分。逾此數者,循畫布右行之有號世界座標,以六十四格為齊界而分段,首尾可短;編輯器之繫解與頂板舊圖之隱,皆以同一子段為限。

繪畫之法與 PIDS 全同(Text / Texture / Rect 佐 ctx.draw,畫布之原點在可見面之左上)。數據之法(JS 之 psd 參 / C++ 之 mtr::Psd,訪問器之全備者見 數據快照 · Psd):

JS釋義
psd.type() / psd.width() / psd.height()所繫之面("glass" / "top")與畫布之廣袤
psd.platform()站台:其號 / 其名(number() / name())、所屬車站、停站之時
psd.routes()經此站台諸線之全況;dataFetchMode 為 ALL,則每線悉載其諸站(轉乘 / 出口)
psd.arrivals()將至之車:訖站、線、車廂之數、倒數之時(arrivalCountdown())、將啟之門號 doorNumbers()、實時 / 訖 / 環線之態
psd.stop()車站全備之訊(ABI 13):其名、其色(ARGB 及十六進位之文)、價區一至三、泊此站之站之諸線、鄰站之線(以站名為群)、實體相通之站名、諸出口與其所往、站台之號與其總數——無站台 / 車站,則返 null。C++ 之側:has_station() 佐 mtr::PsdStation 諸訪問器(見 數據快照 · Psd)
psd.doors() / psd.doorValue() / psd.isDoorOpen()門單元之列(自一而號,即時啟閉 / 加鎖之狀)與全牆之啟度

C++ 之側,此第五類之基 mtr::PsdScript<State> 也(MTR_REGISTER_PSD_SCRIPT),數據快照曰 JcmPsdSnapshot(ABI 12;車站之目,自 ABI 13 始),例見 native/examples/psd_glass.cpp。

DisplayHelper(內建多屏之輔)

車載多屏現成之案:include(Resources.id("mtrsteamloco:scripts/display_helper.js")) 之後——

let slotCfg = {
    "version": 1,
    "texSize": [2048, 1024],
    "slots": [
        { "name": "lcd_north", "texArea": [0, 0, 2048, 274],
          "pos": [[[-0.59, 2.125, -1.75], [-0.755, 2.03, -1.75],
                   [-0.755, 2.03, -3.25], [-0.59, 2.125, -3.25]]],
          "offsets": [[0, 0, -5], [0, 0, 0], [0, 0, 5], [0, 0, 10]] }
    ]
};
let dhBase = new DisplayHelper(slotCfg);

function create(ctx, state, vehicle) {
    state.rl = new RateLimit(0.05);
    state.dh = dhBase.create();          // 立實例而繫其紋理
}
function render(ctx, state, vehicle) {
    if (state.rl.shouldUpdate()) {
        let g = state.dh.graphicsFor("lcd_north");  // 槽位之畫布(自為平移)
        // ... AWT 之繪 ...
        state.dh.upload();
    }
    for (let car of ctx.getMyCars())
        ctx.getCarRenderManager(car).drawModel(state.dh.model, null);  // 每幀必畫
}
function dispose(ctx, state, vehicle) {
    state.dh.close();
}
  • 諸槽位共享 texSize 一張巨紋理;texArea 指槽位所用之域(x, y, w, h)。
  • pos 者,三維之陣列:每屏四角之坐標(左上 → 左下 → 右下 → 右上),其單位米(Blockbench 1/16 之單位,須除以 16);原點在列車之中、地板之高;+X 左 / +Y 上 / +Z 後。
  • offsets 複製同內容之屏(門上小屏連排,可省紋理);graphicsFor(name) 取既自動平移之畫布,graphics() 取整張。
  • 上傳置 RateLimit 之內,畫模型置其外——模型必逐幀納之。

繼之:晏陽增補 —— JS 與 C++ 相雜之條、開門方向之記、交通類型之細分,與直寫像素之上傳。