Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ obj/
dist/
UI/node_modules/
UI/build/
Library/
.vs/
*.user
.vs/
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

All notable changes follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and Semantic Versioning prerelease conventions.

## 1.0.1 — 2026-08-19

### Fixed

- Prevented the bottom application controls from being clipped by the panel at supported viewport and UI scales.
- Kept the in-panel version label on one line beside the localized title.
- Shortened the forbidden-selection notice so it states only that selected assets will be blocked.

### 中文

- 修复受支持视口与 UI 缩放下底部应用操作按钮被面板裁切的问题。
- 游戏内版本号固定在本地化标题旁单行显示。
- 精简禁行提示,仅保留“选中的资产将被禁止通行”。

## 1.0.0 — 2026-08-19

### Added
Expand Down
2 changes: 1 addition & 1 deletion Mod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace RouteFilter;
public sealed class Mod : IMod
{
public const string Id = "RouteFilter";
public const string Version = "1.0.0";
public const string Version = "1.0.1";
public const string ToggleToolAction = "ToggleRestrictionTool";
public const string ApplyAction = "ApplyRestriction";
public const string ClearAction = "ClearRestriction";
Expand Down
17 changes: 7 additions & 10 deletions Properties/PublishConfiguration.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,13 @@ RouteFilter is an independent GPL-3.0-only community project by Daotie. It is no
<!-- <Screenshot Value="Properties/Screenshots/04-rerouting-result.png" /> -->
<Tag Value="Code Mod" />
<Tag Value="Traffic" />
<ModVersion Value="1.0.0" />
<ModVersion Value="1.0.1" />
<GameVersion Value="1.5.*" />
<ChangeLog>## RouteFilter 1.0.0

- First stable release.
- Opens the panel and selection tool together; default shortcut is Ctrl+Shift+N.
- Keeps the asset list and application controls visible in one viewport-aware panel.
- Supports exact asset restrictions on road and rail nodes or full segments.
- Matches complete recognized vehicle consists, fixed-route rail vehicles, and outside traffic.
- Requests native pathfinding detours and stops matching vehicles when no alternative exists.
- Preserves the asset-level V1 save schema used by the public beta.</ChangeLog>
<ChangeLog>## RouteFilter 1.0.1

- Prevents the bottom application controls from being clipped by the panel.
- Keeps the version label on one line beside the localized title.
- Shortens the forbidden-selection notice to state only that selected assets will be blocked.
- Retains the asset-level V1 save schema used by RouteFilter 1.0.0 and the public beta.</ChangeLog>
<ExternalLink Type="github" Url="https://github.com/Daotie/CS2-RouteFilter" />
</Publish>
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

Exact vehicle-asset access control for road and rail networks in **Cities: Skylines II**.

![Version](https://img.shields.io/badge/version-1.0.0-2d8b70)
![Version](https://img.shields.io/badge/version-1.0.1-2d8b70)
![Status](https://img.shields.io/badge/status-stable-1976d2)
![License](https://img.shields.io/badge/license-GPL--3.0--only-blue)

Expand Down Expand Up @@ -99,7 +99,7 @@ The shortcut is remappable in the game's settings. Closing the panel also closes

## Save data and upgrades

RouteFilter `1.0.0` uses the asset-level V1 save schema introduced during the public beta. Each restricted target stores exact prefab entity references. Saves created with `0.4.0-beta.1` or `0.5.0-beta.1` remain on the same schema.
RouteFilter `1.0.1` uses the asset-level V1 save schema introduced during the public beta. Each restricted target stores exact prefab entity references. Saves created with `0.4.0-beta.1`, `0.5.0-beta.1`, or `1.0.0` remain on the same schema.

Rebuilding, replacing, or deleting a road or track segment creates new game entities and may remove restrictions attached to the original target. Review restrictions after substantial network reconstruction.

Expand Down
4 changes: 2 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

为 **《都市:天际线 II》** 道路与轨道路网提供精确到车辆资产的通行控制。

![版本](https://img.shields.io/badge/版本-1.0.0-2d8b70)
![版本](https://img.shields.io/badge/版本-1.0.1-2d8b70)
![状态](https://img.shields.io/badge/状态-正式版-1976d2)
![许可证](https://img.shields.io/badge/许可证-GPL--3.0--only-blue)

Expand Down Expand Up @@ -99,7 +99,7 @@ Paradox Mods 页面上线后,订阅 **RouteFilter** 并将其加入当前播

## 存档数据与升级

RouteFilter `1.0.0` 使用公开测试阶段引入的逐资产 V1 存档结构。每个受限目标保存准确的车辆预制实体引用;使用 `0.4.0-beta.1``0.5.0-beta.1` 创建的限制沿用相同结构。
RouteFilter `1.0.1` 使用公开测试阶段引入的逐资产 V1 存档结构。每个受限目标保存准确的车辆预制实体引用;使用 `0.4.0-beta.1``0.5.0-beta.1` 或 `1.0.0` 创建的限制沿用相同结构。

重建、替换或删除道路、轨道路段会使游戏创建新实体,与原目标关联的限制可能因此丢失。大规模改造路网后请重新检查限制。

Expand Down
58 changes: 17 additions & 41 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,33 @@
# RouteFilter 1.0.0
# RouteFilter 1.0.1

## English

RouteFilter 1.0.0 is the first stable release of the exact vehicle-asset access tool for Cities: Skylines II road and rail networks.
RouteFilter 1.0.1 is a maintenance release focused on keeping the complete restriction workflow visible and unambiguous across supported viewport and UI scales.

### Player-facing changes
### Fixed

- Opening the RouteFilter panel now activates the selection tool immediately. The redundant in-panel tool activation button has been removed, and closing the panel closes the tool.
- The default remappable panel shortcut is now `Ctrl+Shift+N`.
- The panel uses viewport-aware top and bottom boundaries. The target selector, asset list, paging controls, and application buttons remain on one screen; only the asset list scrolls.
- Maximum speed is displayed in km/h, with acceleration and braking available on asset hover.
- Each selected node or segment loads its own saved forbidden list. Bulk selection changes remain pending until the player explicitly applies them to that target.
- Road and rail catalogs are filtered by target compatibility, and recognized engines, carriages, and trailers can be controlled as a group or individually.
- The panel now reserves enough flexible space for the bottom application controls, preventing **Apply list to selected target**, **Clear target restrictions**, and **Cancel selection** from being clipped.
- The version label stays on one line beside the localized RouteFilter title.
- The forbidden-selection notice now states only: “Selected assets will be blocked.”

### Restriction enforcement
### Compatibility

- Exact prefab matching covers the vehicle controller and all recognized assets in its consist.
- Detection checks current lanes, upcoming navigation lanes, underlying path elements, and restricted node endpoints.
- Enforcement runs after road and rail navigation updates but before vehicle movement.
- Matching vehicles request a new path while the selected target is temporarily presented as unavailable to the native pathfinder.
- If a fixed transit route or disconnected network offers no valid detour, the matching vehicle is stopped and may retry instead of continuing normally through the restriction.
- Outside traffic is evaluated through the same exact-asset path.
This release does not change restriction behavior or save data. Cities and target-specific asset restrictions saved by RouteFilter `1.0.0`, `0.5.0-beta.1`, and `0.4.0-beta.1` remain compatible.

### Save compatibility

Version 1.0.0 retains the asset-level V1 save schema used by `0.4.0-beta.1` and `0.5.0-beta.1`. Existing restrictions created by those RouteFilter versions remain structurally compatible.

Please report reproducible compatibility issues with the game version, active traffic/network mods, reproduction steps, and `Player.log`.
Please report reproducible UI issues with the game version, display resolution, UI scale, RouteFilter version, and a screenshot.

## 中文

RouteFilter 1.0.0 是首个正式版本,为《都市:天际线 II》道路与轨道路网提供精确到车辆资产的通行控制。

### 玩家可见改动

- 打开 RouteFilter 面板时会立即启用选择工具,不再显示重复的“打开工具”按钮;关闭面板时工具也会关闭。
- 默认且可重新绑定的面板快捷键改为 `Ctrl+Shift+N`。
- 面板根据视口上下边界自适应,目标选择、资产列表、分页和应用按钮保持在同一页面,只有资产列表内部滚动。
- 最高速度以 km/h 显示,悬浮资产时同时显示加速度和制动减速度。
- 每个节点或路段会加载自己保存的禁行清单;批量选择仅修改待应用清单,明确点击应用后才写入目标。
- 道路与轨道资产会按目标兼容性筛选,已识别车头、车厢和挂车可分组或逐项控制。
RouteFilter 1.0.1 是维护版本,重点确保在受支持的视口与 UI 缩放下,完整禁行操作流程始终可见且语义明确。

### 禁行执行
### 修复

- 准确预制资产匹配覆盖车辆控制实体及编组内所有已识别资产。
- 检测覆盖当前车道、前方导航车道、底层路径元素和受限节点端点。
- 执行顺序位于道路、轨道导航更新之后以及车辆实际移动之前。
- 命中车辆请求新路径时,目标会短暂以不可用状态提供给原生寻路系统。
- 固定公共交通线路或断开路网没有有效绕行路线时,匹配车辆会停车并可能重试,而不是继续正常穿过限制。
- 过境交通使用相同的逐资产检测链路。
- 面板现在为底部操作区域保留足够的弹性空间,避免“应用到所选目标”“清除目标限制”和“取消选中”按钮被裁切。
- 版本号固定在本地化 RouteFilter 标题旁单行显示。
- 禁行提示精简为:“选中的资产将被禁止通行。”

### 存档兼容
### 兼容性

1.0.0 保留 `0.4.0-beta.1` `0.5.0-beta.1` 使用的逐资产 V1 存档结构,这些 RouteFilter 版本创建的限制在结构上保持兼容
本版本不更改禁行逻辑或存档数据。RouteFilter `1.0.0`、`0.5.0-beta.1` `0.4.0-beta.1` 保存的城市及逐目标资产限制保持兼容

报告可复现的兼容性问题时,请提供游戏版本、当前交通/路网模组、复现步骤和 `Player.log`
报告可复现的界面问题时,请附上游戏版本、显示分辨率、UI 缩放比例、RouteFilter 版本和截图
6 changes: 3 additions & 3 deletions RouteFilter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<Configurations>Debug;Release</Configurations>
<AssemblyName>RouteFilter</AssemblyName>
<RootNamespace>RouteFilter</RootNamespace>
<Version>1.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<Version>1.0.1</Version>
<AssemblyVersion>1.0.1.0</AssemblyVersion>
<FileVersion>1.0.1.0</FileVersion>
<Authors>Daotie</Authors>
<Description>Asset-level vehicle access restrictions for road and rail nodes or segments in Cities: Skylines II.</Description>
<Title>RouteFilter</Title>
Expand Down
2 changes: 1 addition & 1 deletion Setting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public IEnumerable<KeyValuePair<string, string>> ReadEntries(IList<IDictionaryEn
["RouteFilter.UI.Search"] = Chinese ? "搜索车辆资产" : "Search vehicle assets",
["RouteFilter.UI.Empty"] = Chinese ? "没有匹配的车辆资产" : "No matching vehicle assets",
["RouteFilter.UI.ForbiddenTitle"] = Chinese ? "禁行资产" : "Forbidden assets",
["RouteFilter.UI.ForbiddenHint"] = Chinese ? "选中的资产将被禁止通行;未选中的资产保持允许通行。" : "Selected assets will be blocked. Unselected assets remain allowed.",
["RouteFilter.UI.ForbiddenHint"] = Chinese ? "选中的资产将被禁止通行。" : "Selected assets will be blocked.",
["RouteFilter.UI.ForbiddenCount"] = Chinese ? "项禁行" : "forbidden",
["RouteFilter.UI.RoadAssets"] = Chinese ? "道路车辆" : "Road vehicles",
["RouteFilter.UI.RailAssets"] = Chinese ? "轨道车辆" : "Rail vehicles",
Expand Down
2 changes: 1 addition & 1 deletion UI/mod.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "RouteFilter",
"author": "Daotie",
"version": "1.0.0",
"version": "1.0.1",
"dependencies": []
}
4 changes: 2 additions & 2 deletions UI/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion UI/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "cs2-routefilter-ui",
"private": true,
"version": "1.0.0",
"version": "1.0.1",
"scripts": { "build": "webpack" },
"dependencies": { "classnames": "2.3.2", "react": "18.3.1", "react-dom": "18.3.1" },
"devDependencies": {
Expand Down
15 changes: 8 additions & 7 deletions UI/src/routeFilterUI.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
.toggle:hover { background: #354650; }
.selected { box-shadow: 0 0 0 3rem rgba(255, 94, 66, .72); }
.panel {
position: fixed; left: 18rem; top: 50rem; bottom: 20rem; z-index: 1000; width: 470rem; height: auto; max-height: 700rem;
position: fixed; left: 18rem; top: 50rem; bottom: 20rem; z-index: 1000; width: 470rem; height: calc(100vh - 70rem); max-height: 735rem;
padding: 16rem; border-radius: 12rem; color: var(--textColor); background: rgba(28, 35, 42, .98);
box-sizing: border-box; overflow: hidden; box-shadow: 0 8rem 28rem rgba(0,0,0,.48); display: flex; flex-direction: column; pointer-events: auto;
header { display: flex; justify-content: space-between; align-items: center; flex: 0 0 auto; }
header div { display: flex; align-items: center; }
header small { opacity: .65; font-size: 11rem; margin-left: 10rem; }
header button { border: 0; background: transparent; color: inherit; font-size: 24rem; }
header div { display: flex; min-width: 0; align-items: center; }
header strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
header small { flex: 0 0 auto; opacity: .65; font-size: 11rem; margin-left: 10rem; white-space: nowrap; }
header button { flex: 0 0 auto; border: 0; background: transparent; color: inherit; font-size: 24rem; }
}
.warning { flex: 0 0 auto; margin-top: 8rem; padding: 9rem 11rem; border-left: 3rem solid #ff5e42; background: rgba(255,94,66,.12); }
.warning strong, .warning span { display: block; }
Expand All @@ -27,15 +28,15 @@
.assetHeader { display: flex; flex: 0 0 auto; justify-content: space-between; align-items: center; margin-top: 10rem; }
.assetHeader small { opacity: .72; }
.search { width: 100%; margin-top: 7rem; padding: 8rem 10rem; border: 1rem solid rgba(255,255,255,.18); border-radius: 6rem; color: white; background: rgba(0,0,0,.22); }
.details { flex: 0 0 65rem; margin-top: 7rem; padding: 7rem 9rem; border-radius: 6rem; background: rgba(0,0,0,.2); font-size: 12rem; overflow: hidden; }
.details { flex: 0 0 58rem; margin-top: 7rem; padding: 7rem 9rem; border-radius: 6rem; background: rgba(0,0,0,.2); font-size: 12rem; overflow: hidden; }
.details div { display: flex; align-items: center; margin-bottom: 5rem; }
.details span { display: inline-block; margin-right: 12rem; opacity: .82; }
.details b { color: #fff; }
.actions { display: flex; flex: 0 0 auto; margin-top: 8rem; }
.actions button { flex: 1 1 auto; padding: 7rem; }
.actions button + button { margin-left: 7rem; }
.actions .refresh { flex: 0 0 38rem; }
.assetList { flex: 1 1 140rem; min-height: 90rem; margin-top: 7rem; overflow-y: auto; overflow-x: hidden; padding-right: 5rem; }
.assetList { flex: 1 1 140rem; min-height: 60rem; margin-top: 7rem; overflow-y: auto; overflow-x: hidden; padding-right: 5rem; }
.assetRow { display: flex; width: 100%; min-height: 34rem; margin-bottom: 5rem; align-items: center; border-radius: 6rem; background-color: rgba(255,255,255,.07); }
.assetRow.child { margin-left: 24rem; width: auto; }
.assetRow.forbidden { background-color: rgba(190, 58, 40, .78); }
Expand All @@ -55,7 +56,7 @@
.pager button { width: 42rem; height: 28rem; }
.pager span { min-width: 150rem; text-align: center; font-size: 11rem; opacity: .78; }
.pager button:disabled, .applyActions button:disabled { opacity: .35; }
.applyActions { display: flex; flex: 0 0 auto; margin-top: 7rem; }
.applyActions { display: flex; flex: 0 0 38rem; min-height: 38rem; margin-top: 7rem; }
.applyActions button { flex: 1 1 auto; padding: 8rem 5rem; }
.applyActions button + button { margin-left: 6rem; }
.applyActions .applyButton { background-color: #287a98; }
2 changes: 1 addition & 1 deletion UI/src/routeFilterUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export const RouteFilterUI = () => {
<Button src={icon} variant="floating" className={classNames(styles.toggle, { [styles.selected]: active })} onSelect={togglePanel} />
{open && <Portal><section className={styles.panel} onMouseEnter={() => trigger(mod.id, "setPointerOverUi", true)} onMouseLeave={() => trigger(mod.id, "setPointerOverUi", false)}>
<header><div><strong>{tr("RouteFilter.UI.Title", "RouteFilter")}</strong><small>v{mod.version}</small></div><button onClick={() => trigger(mod.id, "toggleTool")}>×</button></header>
<div className={styles.warning}><strong>{tr("RouteFilter.UI.ForbiddenTitle", "Forbidden assets")}</strong><span>{tr("RouteFilter.UI.ForbiddenHint", "Selected assets will be blocked. Unselected assets remain allowed.")}</span></div>
<div className={styles.warning}><strong>{tr("RouteFilter.UI.ForbiddenTitle", "Forbidden assets")}</strong><span>{tr("RouteFilter.UI.ForbiddenHint", "Selected assets will be blocked.")}</span></div>
<div className={styles.modes}>
<button className={classNames({ [styles.modeSelected]: targetMode === 0 })} onClick={() => trigger(mod.id, "setTargetMode", 0)}>{tr("RouteFilter.UI.Node", "Node")}</button>
<button className={classNames({ [styles.modeSelected]: targetMode === 1 })} onClick={() => trigger(mod.id, "setTargetMode", 1)}>{tr("RouteFilter.UI.Segment", "Segment")}</button>
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.0
1.0.1