NTE supports full control of train rendering via JavaScript. You can either fully control the rendering of all elements using JavaScript, or overlay JavaScript-driven displays or objects on an existing train model.
You can add a model that uses JavaScript to control rendering by writing something like this in the mtr_custom_resources.json
file:
{ "custom_trains": { "s_train_suspend": { "name": "JS Test Train", "base_type": "train_9_2", "color": "2AF0AD", "script_files": [ "mtr:js_train/main.js" ], "bve_sound_base_id": "optonix1500" } } }
In general, the parameters are the same as those required for regular resourcepacks for MTR, except for the script_files
parameter.
If the base_type parameter is not specified, the train appearance specified by the other parameters will be displayed as is, and then JavaScript-driven elements will be overlaid on top of it. Otherwise, if the base_type parameter is specified, the appearance will be controlled solely by JavaScript.
Parameter | Description |
---|---|
base_type | Train type, length and width. For example, train_19_2 . If this attribute is specified, the appearance of the train will be controlled solely by JavaScript, i.e. without taking base_train_type , model and other parameters into account. Otherwise, the appearance of the train will be based on base_train_type , model and others, and then JavaScript-driven elements will be overlaid on top of it. |
script_files | An array containing the locations of .js scripts. Multiple scripts can be specified. |
script_texts | Optional parameter. An array containing the JavaScript text to be executed before script_files. Can be used when the same script is used for different trains, but you need to set variables that are different for each train. |
has_gangway_connection | Is it possible to pass between cars? Without this parameter the base_train_type setting is taken. |
is_jacobs_bogie | Whether the Jacobs bogie is used. Only affects sounds when using the BVE format. |
bogie_position | The distance between the normal bogie and the center of the car. Only affects sounds when using the BVE format. |
All trains of the same type use the same working environment (global variables and etc).
Code written in top-level space outside of functions will run when a resource package is loaded, and can be used to load resources such as models and textures. It is recommended to store resources (such as models, fonts and textures) in global variables, which do not need to be different for each train, to avoid excessive memory usage caused by loading a copy of the same content for each train.
Your script should include the following functions that the NTE will call as needed:
function create(ctx, state, train) { ... } function render(ctx, state, train) { ... } function dispose(ctx, state, train) { ... }
Functions | Description |
---|---|
create | It is called when the train is loaded at the client and can be used to perform some initialization operations, for example, to create dynamic textures. |
render | It is called approximately once per frame. It is used for basic display logic. In practice however, the code is executed in a separate thread so as not to slow down FPS. If it takes too long to execute the code, it can be called once every few frames instead of every frame. |
dispose | Called when a train goes out of sight. Can be used for things like releasing the dynamic textures to free up memory. |
The NTE calls these functions with three parameters, each of which is described below.
Parameter | Description |
---|---|
First (ctx ) | Used to pass train rendering actions to the NTE. Type — TrainScriptContext. |
Second (state ) | A JavaScript object associated with a train. The initial value is {}, and its content can be set arbitrarily to store what should be different for each train. |
Third (train ) | Used to get the status of the train. Type — Train. |
The following lists all the rendering control operations that can be performed and all the information that can be obtained about the train.
The following functions are called to control rendering. The functions for rendering models should be called each time render
is called.
TrainScriptContext.drawCarModel(model: ModelCluster, carIndex: int, poseStack: Matrices): void
carIndex
: defines the car whose origin point will be used for rendering (0
— first car in front, train.trainCars() - 1
— last). The origin point is in the middle of the car, 1 block above ground level.poseStack
: transformation of model placement. If passed null, the model will be placed in the center without transformation.TrainScriptContext.drawConnModel(model: ModelCluster, carIndex: int, poseStack: Matrices): void
carIndex
: defines the car connection whose origin point will be used for rendering (0
— between the 1st and 2nd car in front, train.trainCars() - 2
— last). The origin point is in the middle of the connection, 1 block above ground level.poseStack
: transformation of model placement. If passed null, the model will be placed in the center without transformation.TrainScriptContext.drawConnStretchTexture(location: ResourceLocation, carIndex: int): void
Call the following functions to play sounds. Call only when you need to start playback, repeated calls will cause multiple sounds to stack.
TrainScriptContext.playCarSound(sound: ResourceLocation, carIndex: int, x: float, y: float, z: float, volume: float, pitch: float): void
TrainScriptContext.playAnnSound(sound: ResourceLocation, volume: float, pitch: float): void
In addition, there is a set of functions to aid development and debugging.
TrainScriptContext.setDebugInfo(key: String, value: Object)
key
is the name of the value, value
is the content (GraphicsTexture
type will be displayed, others will be converted to string).Functions And Objects | Description |
---|---|
train.shouldRender(): boolean | Whether this train should be shown now. When the setting to hide the train the player is on is enabled, the script will run as normal in order for features such as broadcasting to still work. This function will return false to disable features such as particle effects. Note that this function is not needed to stop drawCarModel , NTE will automatically call it. |
train.shouldRenderDetail(): boolean | Whether the train is within the detailed drawing radius (32 blocks). To save resources, it is recommended to stop processing things like broadcasts, displays, detailed models, etc. when the value is false . |
train.trainTypeId(): String | Train Type ID. |
train.baseTrainType(): String | Based on the model ID. |
train.id(): long | The unique number of the vehicle inside the MTR. It's a random 64-bit integer. [Nemo's note: Maybe in JavaScript the last few bits become 0 because there are not enough valid digits?] |
train.transportMode(): TransportMode | Mode of transportation. |
train.spacing(): int | Length of each car + 1. |
train.width(): int | Width. Since the player also has a width [what?], a wagon that is 3 blocks wide has a value of 2 for this attribute. |
train.trainCars(): int | Number of cars. |
train.accelerationConstant(): float | Acceleration. The unit of measurement is m/tick/tick, which is 1/400 m/s2 |
train.manualAllowed(): boolean | Whether manual operation is allowed. |
train.maxManualSpeed(): int | The maximum speed at manual control. It corresponds to the ordering of track types from low to high. [what?] |
train.manualToAutomaticTime(): int | Time to switch back to automatic control after being unmanned. |
train.path(): List<PathData> | A travel path representing a list of each section of the path to be traveled. Each element represents one PathData . |
train.railProgress(): double | Distance from the siding. |
train.speed(): float | Speed, is measured in m/tick, which is equal to 1/20 m/s. |
train.doorValue(): float | Door status. 0 - fully closed, 1 - fully open. Values between - smooth transition between states. |
train.isDoorOpening(): boolean | Are doors opening? |
train.doorLeftOpen[carIndex: int]: boolean | Whether left doors of a particular car can be opened. |
train.doorRightOpen[carIndex: int]: boolean | Whether right doors of a particular car can be opened. |
train.isCurrentlyManual(): boolean | Is it manually operated right now? |
train.isReversed(): boolean | Is the train reversed? That is, is the first car on the rear? |
train.isOnRoute(): boolean | Is the train on the route? |
train.getRailProgress(car: int): double
car
from the moment of leaving the depot. Car counting starts from 0, the starting point is considered to be their front when leaving the depot. For example, getRailProgress(1) will return the distance to the coupling between cars 1 and 2train.getRailIndex(railProgress: double, roundDown: boolean): int
railProgress
is on at the first entry in path()
. If this position is at the junction of two tracks, roundDown
is true
to return the one further to the rear, otherwise it returns the one further to the front.train.getRailSpeed(railIndex: int): float
railIndex
entry in path()
. It correctly handles the case where a train should not enter a station and accelerate when this section of track is a platform. The unit is m/tick, which is 1/20 m/s.train.getAllPlatforms(): List<PlatformInfo>
PlatformInfo
. A turn on a platform counts as one element.train.getAllPlatformsNextIndex(): int
getAllPlatforms()
list at which the train will stop (this value is updated when the train departs, so when it stops at a platform, it is the same station). A value of 0 means the train has just left the depot and is traveling to the first platform, and a value equal to getAllPlatforms().size()
means it is returning to the depot. Note that it is therefore not necessarily within the index range of `getAllPlatforms()`, which needs to be determined.train.getThisRoutePlatforms(): List<PlatformInfo>
PlatformInfo
.train.getThisRoutePlatformsNextIndex(): int
getThisRoutePlatforms()
list where the train will stop. A value of 0 means that the train is going to the first platform, and a value equal to getThisRoutePlatforms().size()
means that it is leaving for the next route or returning to the depot. Note that it is therefore not necessarily within the index range of `getThisRoutePlatforms()`, which needs to be determined.Functions And Objects | Description |
---|---|
PathData.rail: Rail | Rail. For more details, see the Rail.java source code from MTR. |
PathData.rail.railType: RailType | Rail type (wood, stone, iron…) See the RailType.java source code from MTR. |
PathData.rail.getModelKey(): String | The NTE path model used for this section.This will return an empty string if no NTE path model are assigned, and “null” if the model in use are the (Hidden) track model. |
PathData.dwellTime: int | The amount of time a train has to stop if this section of track is a platform. 0 if it is not a platform, in *0.5s. |
Functions And Objects | Description |
---|---|
platformInfo.route: Route | The route to which this platform belongs. If the reverse happens at the platform — the route after reversal is returned. See Route.java source code from MTR. |
PlatformInfo.route.name: String | The name of the route to which this platform belongs. |
PlatformInfo.station: Station | The station to which the platform belongs. See the Station.java source code from the MTR. |
PlatformInfo.station.name: String | The name of the station to which the platform belongs. |
PlatformInfo.platform: Platform | This platform. See the Platform.java source code from the MTR. |
PlatformInfo.platform.name: String | Platform Name. |
PlatformInfo.platform.dwellTime: int | Train dwell time, indicated in ticks (1/20 s). |
PlatformInfo.destinationStation: Station | The end station of the route to which this platform belongs. See the Station.java source code from the MTR. |
PlatformInfo.destinationStation.name: String | The name of the terminus of the route to which the platform belongs. |
PlatformInfo.destinationName: String | Name of the final station (including the possibility of customizing the text). |
PlatformInfo.distance: double | The distance from the depot to the platform, can be compared with railProgress . |
PlatformInfo.reverseAtPlatform: boolean | Does the train reverse on the platform? |