Author SHA1 Message Date
haylanandClaude Bot 9edc9c065a feat(mod): add hello-world script mod
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 22:33:47 +02:00
haylanandClaude Bot 0ef87ebab7 chore: add Teal/VS Code config, tl check workflow and modding notes
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 22:32:59 +02:00
haylan 86a48e75c9 Merge branch 'research/mod-tooling' 2026-10-04 22:31:12 +02:00
haylan 94ea772fa8 Merge branch 'research/overtake-feasibility' 2026-10-04 22:31:12 +02:00
haylanandClaude Bot 035adb5762 chore: add agent-skills config, glossary and example mod
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 22:31:12 +02:00
haylanandClaude Bot 8e145a2870 docs(research): TpF3 overtaking feasibility
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 21:58:04 +02:00
13 changed files with 247 additions and 0 deletions

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
tlconfig.lua
.claude/settings.local.json
+5
View File
@@ -0,0 +1,5 @@
{
"recommendations": [
"pdesaulniers.vscode-teal"
]
}
+20
View File
@@ -0,0 +1,20 @@
## Agent skills
### Issue tracker
Issues live in Gitea (`git.arthurerlich.de`), driven with the `tea` CLI. See `docs/agents/issue-tracker.md`.
### Triage labels
Default five-role vocabulary (`needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`). See `docs/agents/triage-labels.md`.
### Domain docs
Single-context: one `CONTEXT.md` + `docs/adr/` at the repo root. See `docs/agents/domain.md`.
## Modding
- The mod is `mod/` (junctioned into the game's `staging_area` as `haylan_tf3_overtake`). Scripts are Teal (`.tl`, typed Lua); `mod.json` goes at the root of `mod/`.
- Type-check with `tl check mod/<file>.tl` (run from the repo root; it reads `tlconfig.lua`). `tl` is type-check only; whether the game compiles `.tl` itself is undocumented.
- `tlconfig.lua` and `.claude/settings.local.json` are machine-local and gitignored. Copy `tlconfig.template.lua` to `tlconfig.lua` and replace `<tf3_install_folder>` with the game's install path (here `D:/SteamLibrary/steamapps/common/Transport Fever 3/`).
- Authoritative API types: `<install>/api/tealdef` and `<install>/base/tealdef`. Read them, and the offline docs in `docs/reference/`, before guessing an API. Research notes are in `docs/research/`; vocabulary in `CONTEXT.md`.
+27
View File
@@ -0,0 +1,27 @@
# TpF3 Overtake
A Transport Fever 3 script-mod that lets road vehicles pass slower vehicles instead of queueing behind them.
## Language
**Overtaking**:
A faster vehicle getting past a slower one ahead of it on the same road, as seen by the player. It may be a visual illusion (the passer drives through the blocker) rather than a real lane change.
_Avoid_: Passing, bypass
**Blocker**:
The slower vehicle ahead that the passer would otherwise queue behind.
_Avoid_: Obstacle, lead vehicle
**Passer**:
The faster vehicle that overtakes the blocker.
_Avoid_: Overtaker
**Player-owned vehicle**:
A vehicle belonging to one of the player's lines. Only these are in scope for overtaking.
_Avoid_: AI vehicle, ambient traffic
**Same-direction multilane road**:
A road with two or more lanes in the passer's direction, where overtaking needs no oncoming-traffic check.
**Two-way road**:
A road with one lane per direction, where overtaking is only safe if no oncoming vehicle is in the way.
+5
View File
@@ -0,0 +1,5 @@
require "api_def"
require "content_def"
-- list your mods here, if you provide your own d.tl definitions
-- require "example_mod_1"
+51
View File
@@ -0,0 +1,51 @@
# Domain Docs
How the engineering skills should consume this repo's domain documentation when exploring the codebase.
## Before exploring, read these
- **`CONTEXT.md`** at the repo root, or
- **`CONTEXT-MAP.md`** at the repo root if it exists — it points at one `CONTEXT.md` per context. Read each one relevant to the topic.
- **`docs/adr/`** — read ADRs that touch the area you're about to work in. In multi-context repos, also check `src/<context>/docs/adr/` for context-scoped decisions.
If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `/domain-modeling` skill (reached via `/grill-with-docs` and `/improve-codebase-architecture`) creates them lazily when terms or decisions actually get resolved.
## File structure
Single-context repo (most repos):
```
/
├── CONTEXT.md
├── docs/adr/
│ ├── 0001-event-sourced-orders.md
│ └── 0002-postgres-for-write-model.md
└── src/
```
Multi-context repo (presence of `CONTEXT-MAP.md` at the root):
```
/
├── CONTEXT-MAP.md
├── docs/adr/ ← system-wide decisions
└── src/
├── ordering/
│ ├── CONTEXT.md
│ └── docs/adr/ ← context-specific decisions
└── billing/
├── CONTEXT.md
└── docs/adr/
```
## Use the glossary's vocabulary
When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.
If the concept you need isn't in the glossary yet, that's a signal — either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/domain-modeling`).
## Flag ADR conflicts
If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
> _Contradicts ADR-0007 (event-sourced orders) — but worth reopening because…_
+35
View File
@@ -0,0 +1,35 @@
# Issue tracker: Gitea
Issues and specs for this repo live as Gitea issues on `git.arthurerlich.de` (repo `haylan/tpf3-overtake`). Use the `tea` CLI for all operations (see the `gitea-tea` skill).
## Conventions
- **Create an issue**: `tea issues create --title "..." --description "..."`. Use a heredoc for multi-line bodies.
- **Read an issue**: `tea issues <number> --comments`
- **List issues**: `tea issues list --state open --fields index,title,body,labels` with `--labels` filters as needed.
- **Comment on an issue**: `tea comment <number> "..."`
- **Apply / remove labels**: `tea issues edit <number> --add-labels "..."` / `--remove-labels "..."`
- **Close**: `tea issues close <number>` (comment first with `tea comment`)
Infer the repo from `git remote -v` — `tea` does this inside a clone.
## Pull requests as a triage surface
**PRs as a request surface: no.**
## When a skill says "publish to the issue tracker"
Create a Gitea issue.
## When a skill says "fetch the relevant ticket"
Run `tea issues <number> --comments`.
## Wayfinding operations
Used by `/wayfinder`. The **map** is a single issue labelled `wayfinder:map`; **child** tickets are issues labelled `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`) with `Part of #<map>` at the top of the body and listed in the map's task list.
- **Blocking**: a `Blocked by: #<n>, #<n>` line at the top of the child body. Unblocked when every blocker is closed.
- **Frontier query**: open children of the map, minus any with an open blocker or an assignee; first in map order wins.
- **Claim**: assign the issue to yourself (`tea issues edit <n> --add-assignees <user>`).
- **Resolve**: comment the answer, close the issue, then append a pointer to the map's Decisions-so-far.
+15
View File
@@ -0,0 +1,15 @@
# Triage Labels
The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
| Label in mattpocock/skills | Label in our tracker | Meaning |
| -------------------------- | -------------------- | ---------------------------------------- |
| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
| `needs-info` | `needs-info` | Waiting on reporter for more information |
| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
| `ready-for-human` | `ready-for-human` | Requires human implementation |
| `wontfix` | `wontfix` | Will not be actioned |
When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
Edit the right-hand column to match whatever vocabulary you actually use.
+37
View File
@@ -0,0 +1,37 @@
# Can TpF3 scripting make cars overtake?
Issue #2 (part of map #1). Researched 2026-10-04.
**Verdict: not feasible with the documented API.** Scripts can observe vehicles and scale a vehicle's top speed, but nothing documented lets a script change a road vehicle's lane, path position or per-tick movement. Overtaking would need engine support that the docs do not show.
## Sources
- Wiki, modifiers/filters: https://wiki.transportfever3.com/doku.php?id=modding:scripting:modifiersfilters
- Wiki, base config: https://wiki.transportfever3.com/doku.php?id=modding:scripting:baseconfig
- Script-doc index: https://wiki.transportfever3.com/script-doc/ (pages `api/engine.html`, `api/engine/system.html`, `api/cmd.html`, `content/scripts/gamescript.html`)
- Local example mod: `.examples/ug_tf3_example_config_change` (uses `preRunFn` to set `baseConfig.trainAccelerationFactor`)
## Findings
1. **The "modifiers/filters" page is about resource loading, not runtime behaviour.** `addModifier(category, fn)` edits resource data (`fileName`, `data`) as it loads, and `addFileFilter` toggles resource visibility. Vehicle-related categories are only `loadModule`, `loadMultipleUnit`, plus the `model/vehicle` and `model/car` filters. They cannot touch live vehicle movement. The page says it "is not yet adapted for Transport Fever 3", so it may be outdated.
2. **BaseConfig has no road-vehicle or traffic-behaviour fields.** The vehicle-related ones are `trainAccelerationFactor`, `trainBrakeDeceleration`, `simulateCargoWeight`, `loadspeedPenaltyForTrainOverlength` and `maxVehicleCloneAmount`. All are global and train-oriented. This runs once in `preRunFn`.
3. **Game script phases.** `gamescript.html` defines `update`, `postUpdate` and `handleEvent`, which run on the engine state with mutable script state, and `guiUpdate` and `guiHandleEvent` for the GUI. "Mutable" here means the script's own state, not engine entities. `api.engine` is documented as "for reading the game engine state". So per-tick script code can read the world but only change it through `api.cmd` commands.
4. **Read access to vehicles exists but is thin.** `api.engine.system.landVehicleMoveSystem` exposes `getVehicleInfo(vehicleEntity)`, whose `LandVehicleMoveInfo` return type is not expanded in the docs. It also has `getBlockedTrains`, `isTrainWaitingForFreePath` and `forEach` (train oriented). Components include `Engine.Component.MovePath` (`path`, `dyn.pathPos`, `dyn.speed`, `dyn.accel`, `blocked`, `occupiedFrontIndex`, `occupiedBackIndex`) and `Engine.Component.LandVehicle` (`isTram`, `dyn.brakingTimer`, `turnLeft`, `turnRight`). `BaseEdge` carries `laneConfig` and `BaseNodeConfig.laneConnections`.
5. **Write access is limited to commands.** The relevant `api.cmd` entries are `makeVehicleSetModifiersCmd(vehicle, TransportVehicle.Modifiers)`, `makeVehicleSetStoppedByUserCmd`, `makeVehicleSendToDepotCmd`, `makeVehicleReverseCmd`, `makeVehicleSetLineCmd` and `makeVehicleTryToDepartCmd`. There is no command to set a `MovePath`, speed, lane or position on a land vehicle. `makeComponentExchangeCmd` is marked debug and its doc says it accepts only `EmissionEmitter` or `ModelInstanceList`. `makeCustomVehicleCreateOrUpdateCmd` takes a `Carrier` and an optional `movePathAircraft`, so it is aimed at aircraft or custom vehicles rather than road traffic.
6. **No overtaking concept appears anywhere.** A text search of `api.engine`, `api.engine.system` and `api.cmd` found no "overtake" or "passing" terms.
## Candidate mechanisms
| Mechanism | What it gives | Overtaking? |
|---|---|---|
| `makeVehicleSetModifiersCmd` with `topSpeedScale` | Per-vehicle top speed scaling | No. A faster car still queues behind a slower one. |
| `BaseConfig` in `preRunFn` | Global acceleration or braking (trains only) | No |
| Lane config (`BaseEdge.laneConfig`, `laneConnections`) via construction or street definitions | Static lane layout, for example an extra lane built into the road | Only as static road design. Whether the engine's car AI uses a spare lane to pass is undocumented. |
| `landVehicleMoveSystem` / `MovePath` reads | Observe speed, position and blockage per tick | Detection only |
| Custom entities / custom vehicles (`makeCustomEntity*`, `makeCustomVehicleCreateOrUpdateCmd`) | Script-driven fake visuals | Theoretically a script could despawn a real car and animate a fake one past the blocker. This is a heavy hack with unproven viability for road vehicles. |
## Open questions (not answered by the docs)
- The fields of `LandVehicleMoveInfo`.
- Whether the engine already supports passing on multi-lane or bus-lane roads, outside of script control.
- Whether anything outside the published script-doc (native mod interfaces) exists. Testing in-game would be the next step.
+15
View File
@@ -0,0 +1,15 @@
{
"authors": [
{
"name": "haylan",
"role": "CREATOR"
}
],
"description": "Lets road vehicles overtake slower vehicles (work in progress).",
"summary": "Vehicle overtaking (WIP)",
"name": "Overtake",
"tags": [
"script"
],
"url": ""
}
+11
View File
@@ -0,0 +1,11 @@
local mod = {}
mod.preRunFn = function(_configDict : {{string, string}}, _allModParams : {string : {string : integer}}, _baseConfig : BaseConfig)
print("[haylan_tf3_overtake] hello from preRunFn")
end
mod.postRunFn = function(_configDict : {{string, string}}, _allModParams : {string : {string : integer}})
print("[haylan_tf3_overtake] hello from postRunFn")
end
return mod
+13
View File
@@ -0,0 +1,13 @@
{
"modId": "haylan_tf3_overtake",
"revision": 1,
"severityAdd": "None",
"severityRemove": "Warning",
"cosmetic": false,
"preRunScript": {
"fileName": "haylan_tf3_overtake::mod.script@preRunFn"
},
"postRunScript": {
"fileName": "haylan_tf3_overtake::mod.script@postRunFn"
}
}
+11
View File
@@ -0,0 +1,11 @@
return {
include_dir = {
-- ensure to set proper paths for the definitions for the base game
"<tf3_install_folder>api/tealdef",
"<tf3_install_folder>base/tealdef",
-- list your mods here, if you provide your own d.tl definitions
-- "example_mod_1",
},
global_env_def = "all_def"
}