diff --git a/projects/VehicleFramework/docs/configuration.md b/projects/VehicleFramework/docs/configuration.md index 0d71f42..363d736 100644 --- a/projects/VehicleFramework/docs/configuration.md +++ b/projects/VehicleFramework/docs/configuration.md @@ -128,7 +128,7 @@ The component key is the type. A vehicle has one of each type. `steering` is not Shared component fields: `health`, `repair-time` (ticks), `damage-chance` (0 to 1), `alias`, `fatal`, `vfx`, `armor`, `role`, and an optional `damage` overlay. Damage numbers are multipliers for a hit type. With `armor` or `role` set, those templates merge and a local `damage` map overrides them. Without templates, `damage` is a list of `type(amount)` entries. -Engine fields: `max` and `min` throttle (negative `min` is reverse), `speed` at full throttle, `turn-rate`, `requires-start`, `fuel`, `fuel-capacity`, `fuel-burn-rate`, and `refuel-states`. Sounds and particles on the engine play while it runs. `particle-bones` is a list of `bone.bone` vectors. +Engine fields: `max` and `min` throttle (negative `min` is reverse), `speed` at 100% throttle (scaled linearly with throttle), `turn-rate`, `requires-start`, `fuel`, `fuel-capacity`, `fuel-burn-rate`, and `refuel-states`. Sounds and particles on the engine play while it runs. `particle-bones` is a list of `bone.bone` vectors. A geared engine uses a `gears` list. Each gear has `name`, `max`, `min`, `speed`, and `acceleration`. `start-gear` picks the initial gear. diff --git a/projects/VehicleFramework/docs/models.md b/projects/VehicleFramework/docs/models.md index 931c0d8..8c04b1a 100644 --- a/projects/VehicleFramework/docs/models.md +++ b/projects/VehicleFramework/docs/models.md @@ -2,7 +2,7 @@ Vehicles are ModelEngine models. The plugin moves specific bones itself, so those bones must not be animated in Blockbench. Looping clips such as a spinning propeller or a moving track should be set to loop. -Bone names in the model and in the vehicle YAML have to match. A skin can use a different `.bbmodel`, but every bone the config names has to exist on that model too. +Bone names in the model and in the vehicle YAML have to match. A skin can use a different `.bbmodel`, but every bone the config names has to exist on that model too. Train bogie bones are the exception: a skin without them places the car rigid. ## Body @@ -30,6 +30,8 @@ A `fixed: true` weapon does not use body and head bones. Fixed vehicles such as `behaviour.train.front-connector` and `back-connector` are bones at the couplers. The distance between a car's coupler and the next car's coupler is the spacing along the track. +`behaviour.train.wheel-bones` are the frontmost and rearmost axle pivots; their positions along the model, times its scale, set where the car loses rail support. `behaviour.train.bogies` names two bogie bones, each pivoting at its bogie's centre, with the body rotator pivoting at the model's origin. Wheel animations for `wheel-diameter` are mirrored `forward` and `backward` loops of one wheel turn. See [Trains](using-trains.md#marking-a-vehicle-as-a-train). + `towing.bone` is the hitch bone on the vehicle that pulls. ## Effects diff --git a/projects/VehicleFramework/docs/trains.md b/projects/VehicleFramework/docs/trains.md index 395b280..d0eadaf 100644 --- a/projects/VehicleFramework/docs/trains.md +++ b/projects/VehicleFramework/docs/trains.md @@ -68,7 +68,7 @@ Track items, lay rules, and train debug logging live in [`trains.yml`](https://g One spline per track (no stored sections). A **stroke** is one lay with the configured layer item (`item-layer`, default `m.utils.train_track_layer`): - Left-click: **start location** (block or existing track). Click an existing end to join that track. -- Right-click: **end location**. New track is a straight line in XZ from start to end (player look is ignored). Click within `join-distance` of an existing **end** to join: same track extends, or **two tracks link into one** if start is on one end and end is on another. Join curves from the **track** heading. Crossing the middle of a track still refuses (use the junction item for a turnout). Joins keep the direction (`+s`) of any track a train is on, so each train keeps its saved orientation: closing a loop never reverses the track, and linking two tracks reverses at most one track that has no train and is not a branch. Linking two occupied tracks start to start, or end to end, is refused. Trains saved in unloaded chunks count as occupying their track. +- Right-click: **end location**. New track is a straight line in XZ from start to end (player look is ignored); extending an end curves from the track heading as described in [Curves](using-trains.md#curves). Click within `join-distance` of an existing **end** to join: same track extends, or **two tracks link into one** if start is on one end and end is on another. Join curves from the **track** heading. Crossing the middle of a track still refuses (use the junction item for a turnout). Joins keep the direction (`+s`) of any track a train is on, so each train keeps its saved orientation: closing a loop never reverses the track, and linking two tracks reverses at most one track that has no train and is not a branch. Linking two occupied tracks start to start, or end to end, is refused. Trains saved in unloaded chunks count as occupying their track. - **Creative / spectator:** the spline is saved, then displays rebake in one step. One place sound + particles at the last sample (`build` in `trains.yml`). - **Survival / adventure:** same save, but displays grow along the new stroke one sample every `build.interval-ticks` (default 4, five per second). Prefix rebakes so collinear runs become medium then large. Each step plays `build.sound` and particles at the new sample, and swings the main hand if `build.swing` is true. Set `build.interval-ticks` to `0` to always place instantly. Connecting two tracks or closing a loop is still instant plus one burst. - Remover item (`item-remover`, default `m.utils.train_track_remover`): left-click **digs** a sample (interior dig **splits** into two tracks). On a branch, digging any part of the **initial turnout lay** (stored as `turnoutS` on the junction) removes the whole turnout (junction, switch, and that stub). Digging past that initial lay uses normal dig/split rules; a longer branch extension is kept as plain track. The through stem stays. The remover refuses to dig track that a bound consist occupies, measured from each car out to its couplers. That includes a branch turnout the dig would drop because its frog ends up on a piece shorter than `min-lay-distance`. When a dig or lay rebuilds a spline, trains on it keep their world position on the new spline or pieces. A train in an unloaded chunk checks its saved spline and `s` against where it respawns, and re-finds the track under it (or unbinds if the track is gone). @@ -79,6 +79,7 @@ One spline per track (no stored sections). A **stroke** is one lay with the conf - `item-switch` (default `ia.tfmc:railroad_switch`) plus `switch.offset-along` / `offset-out` / `offset-y` / `yaw-inward` / `throw-degrees` / `throw-degrees-per-second` place and animate the ItemDisplay on the through side of the frog. Chunk load respawns it at the saved pose; the entity is not persistent. - A junction branch still needs a 3-wide by 3-tall corridor of passable blocks. It may cross existing track (including the stem); overlapping tracks do not refuse a turnout. - `max-turn-degrees` (default 35) and `min-lay-distance` (default 8): refuse if the stroke is too short, or if a **join** turn (heading change from the existing end) is too sharp. +- `curve-radius` (default 32, at least 1): bend radius for extensions and joins ([`TrackCurve`](https://github.com/TF-Minecraft/VehicleFramework/blob/main/src/main/java/net/tfminecraft/vehicleframework/tracks/TrackCurve.java)). Strokes too short for it bend tighter, down to `min-lay-distance / (2 sin(max-turn-degrees / 2))`. - Grade: stay **flat** as long as possible, then climb at `desired-grade-degrees` (default 6), never steeper than `max-grade-degrees` (default 10). Chat says **slope is too steep** if the end is too high for the run. - Clearance: a 3-wide by 3-tall corridor must be passable (air and plants are fine; solids and overlapping tracks are not). - Punching track in survival or adventure, and explosions (TNT, creepers, VF ammunition), mark edges broken and drop one `item-track` per newly broken edge. Creative punch and the remover dig do not drop. @@ -115,7 +116,7 @@ On save, write at least: On load, cars use the normal vehicle spawning path. When both ends of a link exist in memory, `setChild` / `setParent` again. If the child chunk loads first, the car waits; it does not need a special global spawn. -Do **not** require spawning the whole consist when one chunk loads. Accept temporary split until the other chunks load; retain occupied routes until missing links resolve. Old saves remain readable. Before downgrading to a plugin without body orientation, restore matching vehicle-data backups: an older reader cannot place a train saved facing −s correctly. +Do **not** require spawning the whole consist when one chunk loads. Accept temporary split until the other chunks load; retain occupied routes until missing links resolve. Old saves remain readable. Before downgrading below 2.9.0 (the first release with body orientation), restore matching vehicle-data backups: an older reader cannot place a train saved facing −s correctly. See [Upgrading](using-trains.md#upgrading). ## Cargo, recorder and tickets diff --git a/projects/VehicleFramework/docs/using-trains.md b/projects/VehicleFramework/docs/using-trains.md index 9173b0a..788789b 100644 --- a/projects/VehicleFramework/docs/using-trains.md +++ b/projects/VehicleFramework/docs/using-trains.md @@ -1,6 +1,6 @@ # Trains -A train is a vehicle whose `behaviour` section contains `train`. Locomotives and cars are separate vehicle ids. They couple at bones and follow a spline track, not vanilla rails. The spline layout and the display entities are described in [Spline tracks](trains.md). This page is how to lay track and drive. +A train is a vehicle whose `behaviour` section contains `train`. Locomotives and cars are separate vehicle ids. They couple at bones and follow a spline track, not vanilla rails. The spline layout and the display entities are described in [Spline tracks](trains.md). This page is how to set up train vehicles, lay track and drive. ## Marking a vehicle as a train @@ -9,18 +9,63 @@ behaviour: rotator: body_controller vector: move.movealign train: + locomotive: true + wheel-diameter: 1.875 + wheel-bones: + - front_axle + - rear_axle front-connector: connector_front back-connector: connector_back fuel-cars: - coal_car ``` -`front-connector` and `back-connector` are optional. A locomotive often has only a back coupler. A car has the coupler that faces the locomotive. `fuel-cars` lists vehicle ids whose fuel the locomotive may burn. +| Key | Meaning | +| --- | --- | +| `front-connector`, `back-connector` | Coupler bones. Optional. A locomotive often has only a back coupler. A car has the coupler that faces the locomotive | +| `fuel-cars` | Vehicle ids whose fuel the locomotive may burn | +| `locomotive` | `true` for a locomotive. See [Locomotive overdrive](#locomotive-overdrive). Default `false` | +| `wheel-diameter` | Blocks across the wheels. See [Wheel animations](#wheel-animations). Default `0` (off) | +| `wheel-bones` | Bones at the frontmost and rearmost axle pivots. See [Track ends](#track-ends) | +| `bogies` | Two bogie bones the car rests on. See [Bogies](#bogies) | +| `walkable` | A deck players can stand on. See [Walkable decks](#walkable-decks) | Drive keybinds belong on the `ground` state. `A` and `D` should be `JUNCTION_LEFT` and `JUNCTION_RIGHT` when the locomotive should throw switches. Throttle still uses `THROTTLE_UP` and `THROTTLE_DOWN`. Negative engine `min` is reverse. Sneak and right-click a car, then sneak and right-click the locomotive, to couple them. Spacing along the track is the distance between the coupler bones. +The plugin does not copy the example vehicles. Copy the train YAML you want (`simple_locomotive`, `coal_car`, `passenger_car`, `flat_car`) from [`src/main/resources/vehicles`](https://github.com/TF-Minecraft/VehicleFramework/tree/main/src/main/resources/vehicles) into `plugins/VehicleFramework/vehicles/`, or use the server's own config set. The TFMC configs and models are kept in ServerAssets; there the locomotive, coal car, passenger car and flat car also set `wheel-bones`. + +## Wheel animations + +With `wheel-diameter` set, the `forward` and `backward` animations of the `ground` state turn the wheels at the speed the train is travelling. Without it, they play as normal move animations. + +The two animation lists pair by order: the first `forward` animation with the first `backward` animation, and so on. Each pair must be mirrored looping animations of one full wheel turn. The wheels hold their pose while the train is stopped, and keep it when the train changes direction. + +## Bogies + +`bogies` names two bogie bones. The car rests on the rail under each, so the body lies along the line between them, and each bogie turns and tilts to follow the rail under it. Each bogie bone must pivot at the bogie's centre, and the body rotator must pivot at the model's origin. The two bones must be at least a quarter of a block apart along the car. + +A vehicle's skins share its `bogies` setting, so each skin's model is checked for the bones. A skin built without them is placed as a rigid car. Changing skin sets the bogies up again for the new model; if both models have the bogie bones, the bogies keep their angles. + +## Walkable decks + +A car with `walkable` has a deck that players can walk on, and players standing on it ride along with the train. The shipped `flat_car` is an open deck with no seats, for riders to stand on. The deck is set in the model's blocks from its origin: + +```yaml +walkable: + x: [-1.5, 1.5] # across the car + z: [-4.5, 4.5] # along it; +z faces the front + top: 1.3125 # height of the deck + box-size: 1.5 # optional; default and maximum 3 +``` + +`x`, `z` and `top` are required, and each span must be at least a quarter of a block; otherwise the plugin logs a warning and ignores the deck. `box-size` is also limited to the narrower span. + +The deck is made of invisible shulkers, which players can stand on. Minecraft does not rotate their boxes, so on bends they overhang the car's corners a little; smaller boxes overhang less. Clicks and hits on the deck go to the car. Shots pass through the boxes to the car, and explosions do not damage them. + +Players are carried up to `walkable.carry-max-speed` in `trains.yml`, 1.0 blocks a tick by default (`0` carries nobody). Faster than that, the deck slides out from under them. The simple locomotive runs at 0.72 blocks a tick, or 0.864 at full overdrive. The boxes are never saved. Any left behind when a car unloads are removed when their chunk loads again. + ## Track items `trains.yml` names the items. The shipped file uses ItemsAdder and MMOItems paths. Point them at your own items. @@ -39,9 +84,9 @@ Sneak and right-click a car, then sneak and right-click the locomotive, to coupl ## Laying track -Left-click with the layer item to set the start. Right-click to set the end. The path is straight in X and Z from start to end. Look direction is ignored. Click solid ground. Grass and plants are refused. +Left-click with the layer item to set the start. Right-click to set the end. A new track is straight in X and Z from start to end. Look direction is ignored. Click solid ground. Grass and plants are refused. -- Click an existing end to extend that track, or to join two tracks into one when the start and end are on different tracks. +- Click an existing end to extend that track, or to join two tracks into one when the start and end are on different tracks. An extension leaves the end along the track's heading; see [Curves](#curves). - Ends within `join-distance` (default 1.5) can join. A join that turns more than `max-turn-degrees` (default 35) is refused. - Joining never turns a train round. If both tracks have trains on them and one would have to run the other way, the join is refused; move a train first. - A new stroke shorter than `min-lay-distance` (default 8) is refused. Loops are exempt. @@ -49,6 +94,22 @@ Left-click with the layer item to set the start. Right-click to set the end. The - The corridor is 3 blocks wide and 3 tall. Solids and overlapping track refuse the lay. Plants do not. - Survival and adventure place one sample every `build.interval-ticks` (default 4) and consume `item-track`. Creative and spectator place the whole stroke at once. `interval-ticks: 0` always places at once. +### Curves + +Extending a track keeps its bends local. A stroke curves at `curve-radius` (`trains.yml`, default 32 blocks) and runs straight elsewhere. A stroke too short for that radius curves tighter, down to the sharpest turn that `max-turn-degrees` allows over `min-lay-distance`. + +Rows are the block-aligned headings: the four axes and the diagonals. + +- A click within half a block of the track's line continues it straight. +- A click just beside the row the rail is on (up to 3 blocks across, within 8 degrees) keeps the rail on its row and shifts it across on a reverse curve just before the click. Laying 1,000 blocks with the end one block over leaves 990 blocks on the row. +- Turning onto a row curves at the corner where the two headings meet, with straight track either side. +- A track end left off the rows turns onto the row first when a corner would leave a long run off it, then shifts across near the click. +- Joining two track ends meets the far track along its own heading, without a kink. + +Other clicks lay a single arc from the track's heading. Laying never reshapes existing track. + +### Junctions and digging + Right-click existing track with the junction item to start a turnout, then right-click with the layer to lay one branch. Left-click with the layer cancels the pending junction. One branch per junction. Branches cannot be longer than `max-junction-length` (default 32). Junctions along the same track must be at least `min-junction-spacing` (default 16) apart. The remover digs a sample. Digging the middle splits the track. Digging the initial turnout lay removes that turnout. Track past that first lay stays. You cannot dig track under a train, or next to a junction when that would remove a turnout a train is on; move the train first. Trains elsewhere on the track stay where they are. @@ -57,11 +118,38 @@ The remover digs a sample. Digging the middle splits the track. Digging the init Bind a locomotive by driving it onto the track, or with `/vf track bind` while seated or standing within 8 blocks. `/vf track unbind` releases it. -Hold `A` or `D` within `junction-arm-distance` (default 16) of the next frog to arm that side. The arm stays until the frog. The matching side diverges. The other side stays on the through route. Chat reports which way you armed. Reverse clears the arm. Backing off a branch returns to the stem. +Tickets and the whitelist use the locomotive while cars are coupled. -A broken segment stops the train. The train stays on the spline. +### Junctions -Tickets and the whitelist use the locomotive while cars are coupled. +Hold `A` or `D` to set the next facing turnout within `junction-arm-distance` (default 16) of the leading wheels. Left and right are seen in the direction the train is travelling; when reversing, the last car's wheels lead. The key on the turnout's side sets the branch, and the other key sets the through route. The switch moves at once and chat reports the setting. While the train is stopped, the throttle selects the approach direction; at zero throttle, the train keeps its last direction. + +Choose before the first wheels enter. The points then stay locked until the whole train clears, including when the last carriage leads while reversing. Stopping, reversing midway, or saving and loading keeps every coupled car on the same route. Closely spaced junctions keep their own settings while the train spans them. Coming out of a branch follows its connection back onto the main track. + +### Track ends + +The whole train stops before a checked wheel runs past an open track end, in either direction. A car's checked points are its `wheel-bones` and, when it rests on bogies, its bogie pivots; a car with neither is checked at its centre. Wheel bone positions come from the model and its scale. A car that already overhangs an end can drive back onto the rails. Connected junctions and loop seams are not ends. + +A broken segment also stops the train. The train stays on the spline. + +## Locomotive overdrive + +A vehicle with `locomotive: true` leads its train as a locomotive: + +- Seated riders anywhere on the train take a quarter of incoming damage, up to 18 per hit. Riders in other vehicles take half, with the same cap. +- With a fully healthy engine, `W` raises the throttle up to 120% (the engine's `max` must allow it). A damaged engine is limited to its health percentage in both directions, so overdrive needs full engine health. Reverse is limited by the engine's `min`. +- Overdrive draws on a shared boost budget that lasts about 20 seconds at 110% or 10 seconds at 120%. Any throttle above 100% spends the same budget. +- Running out of boost, or returning to 100% or below, starts a five-minute cooldown. Throttle up to 100% stays available throughout. +- The scoreboard shows the boost time left, the cooldown, or that overdrive is ready. +- Boost and cooldown are saved with the locomotive. Their timers run in real time, so they include time spent unloaded. + +Above 100%, the engine's fuel use per cycle is multiplied by `1 + (throttle - 100)² / 400`. With the shipped simple locomotive (`speed: 0.72`, `fuel-burn-rate: 1.25`, `max: 120`, `min: -100`): + +| Throttle | Fuel per cycle | Extra fuel | +| --- | --- | --- | +| 100% | 1.25 | 0% | +| 110% | 1.5625 | 25% | +| 120% | 2.5 | 100% | ## Admin track commands @@ -81,3 +169,19 @@ All of these need `vf.admin`. Except for `resync`, they must be run by a player. | `/vf track resync` | Rebuilds loaded rail displays from `trains.yml` | Track JSON is stored under `data/tracks/`. + +## Upgrading + +Plugin updates never overwrite vehicle YAML or an existing `trains.yml`. Missing `trains.yml` keys use their defaults. Install the plugin before vehicle YAML that uses newer `behaviour.train` keys. Older releases ignore keys they do not know, so the feature silently stays off. Before 2.4.4, a skin without the bogie bones caused errors, so a vehicle with such a skin needs 2.4.4 or later before it lists `bogies`. + +| Key or behaviour | First release | +| --- | --- | +| `locomotive` | 2.1.0 | +| `wheel-diameter`, `bogies` | 2.3.0 | +| `walkable`, `walkable.carry-max-speed` | 2.4.0 | +| `wheel-bones` | 2.4.1 | +| Skins without the bogie bones placed as rigid cars | 2.4.4 | +| `curve-radius` | 2.5.0 | +| Saved train facing and per-junction routes | 2.9.0 | + +Saves and throttle tapes from earlier releases remain readable. Before downgrading below 2.9.0, restore the matching vehicle-data backup: older releases cannot represent a train facing the opposite way along a track.