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(): VehicleExtension | MTR 底層之載具——內部 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 欄):
| 式 | 所行 |
|---|---|
SKIP | MTR 四代之預設。不向服端索數據,本地竭力拼停站之表——不保其全。「車一動而鈴即響」之屬,惟觀車速之腳,足用矣 |
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(): long | MTR 底層之物與唯一之 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 亦得貼 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++ 相雜之條、開門方向之記、交通類型之細分,與直寫像素之上傳。