diff --git a/.gitignore b/.gitignore
index cf1a6dc28..4c81b0cbf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -125,7 +125,7 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
-# TODO: Comment the next line if you want to checkin your web deploy settings
+# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
@@ -183,9 +183,9 @@ UpgradeLog*.htm
FakesAssemblies/
/logo/
-# MSVS 2017 artifacts
-/.vs/slnx.sqlite
-/TLM/.vs/TMPE/*
+# MSVS 2017, IntelliJ IDEA/Rider artifacts
+.vs/
+.idea/
# Dependecies game dlls
-/TLM/dependencies
\ No newline at end of file
+/TLM/dependencies
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5bdb514a7..96b255ecb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,897 +1,1793 @@
# Cities: Skylines - Traffic Manager: *President Edition* [](https://discord.gg/faKUnST)
# Changelog
-10.20, 21/05/2019
-- Updated for game version 1.12.0-f5
-- Updated Korean translation (thanks Twotoolus-FLY-LShst) (#294)
-- Updated French translation (thanks PierreTSE) (#311)
-
-10.19, 20/04/2019
-- Bugfix: Mod options overlapping issue (#250, #266).
-- Added: Japanese language (thanks mashitaro) (#258).
-- Update: Chinese language (thanks Emphasia) (#285, #286).
-- Update: "Vanilla Trees Remover" as incompatible mod (it breaks mod options screen) (#271, #290).
-- Update: Moved "Delete" step button on timed traffic lights (#283, #285).
-- Update: Mod incompatibility checker can now be disabled, or skip disabled mods (#264, #284, #286).
-
-10.18, 29/03/2019
-- Bugfix: Parking AI: Cars do not spawn at outside connections (#245)
-- Bugfix: Trams perform turns on red (#248)
-- Update: Service Radius Adjuster mod by Egi removed from incompatible mods list (#255)
-
-10.17, 23/03/2019
-- Introduced new versioning scheme (10.17 instead of 1.10.17)
-- Synchronized code and version with stable version
-- Updated russian translation (thanks to @vitalii201 for translating) (#207)
-- Updated list of incompatible mods (#115)
-- Removed stable version from list of incompatible mods (#168)
-- Turn-on-red can now be toggled for unpreferred turns between one-ways
-- Improved train behavior at shunts: Trains now prefer to stay on their track (#230)
-- Fixed and optimized lane selection for u-turns and at dead ends (#101)
-- Parking AI: Improved public transport (PT) usage patterns, mixed car/PT paths are now possible (#218)
-- Bugfix: Parking AI: Tourist cars despawn because they assume they are at an outside connection (#218)
-- Bugfix: Parking AI: Return path calculation did not accept beautification segments (#218)
-- Bugfix: Parking AI: Cars/Citizens waiting for a path might jump around (#218)
-- Bugfix: Vanilla lane randomization does not work as intended at highway transitions (#112)
-- Bugfix: Vehicles change lanes at tollbooths (#225)
-- Bugfix: Path-finding: Array index is out of range due to a race condition (#221)
-- Bugfix: Citizen not found errors when using walking tours (#219)
-- Bugfix: Timed light indicator only visible when any timed light node is selected (#222)
-
-1.10.16, 24/02/2019
-- Gameplay: Fixed problem with vehicle despawn after road upgrade/remove (thanks @pcfantasy for implementation suggestion)(#86, #101)
-- Gameplay: Fixed problem with vehicles unable to choose lane when u-turn at dead-end (thanks @pcfantasy for implementation and @aubergine10 for neccesary tests)(#101)
-- Gameplay: Fixed problem when user couldn't change state of 'Turn on Red' while enabled_by_default option not selected (thanks @Sp3ctre18 for bug confirmation) (#102)
-- Gameplay: Added missing logic for noise density calculations (thanks to @pcfantasy for fix) (#66)
-- UI: New icons for empty and remove_priority_sign settings (thanks @aubergine10 for those icons) (#75, #77)
-- Other: Greatly improved incompatible mod scanner, added dialog to list and unsubscribe incompatible mods (#91)
-- Other: Changed mod name in Content Manager to __TM:PE__
-- Other: Discord server was set up by @FireController1847 - link in mod description
-- Other: Fixed 'silent error' inside log related with "Esc key handler" (#92)
-- Contribution: Added project building instructions and PR review
-
-1.10.15, 10/02/2019
-- Enhancement: Now you can use Escape key to close Traffic Manager without returning to Pause Menu (thanks to @aubergine10 for suggestion) (#16)
-- Gameplay: Updated pathfinding with missing vanilla logic
-- Gameplay: Tweaked values in CargoTruckAI path finding (thanks to @pcfantasy for improvement suggestion)
-- Gameplay: Tweaked speed multiplier of reckless drivers to get more realistic speed range (thanks to @aubergine10 for suggestion) (#23)
-- UI: New icons for cargo and passenger train restriction (thanks to @aubergine10) (#17)
-- Translations: Simplified Chinese translation updated (thanks to @Emphasia for translating)
-- Other: Added notification if user is still subscribed to old original TM:PE
-- [Experimental feature] Turn on red (thanks to @FireController1847 for implementation and to @pcfantasy for source code base)
-
-1.10.14, 27/01/2019
-- Bugfix: Added missing Car AI type (postVanAI) - now post vans and post trucks are assigned to service vehicles group
-- Bugfix: Vehicles doesn't stop when driving through toll booth - fixes toll booth income too
-- Bugfix: Cargo Airport doesn't work (Cargo planes not spawning and not arriving)
-- Updated Polish translation
-- Updated Korean translation (thanks to @Toothless FLY [ROK]LSh.st for translating)
-- Fixed Mod Options layout (text label overlaps slider control if too wide)
-
-1.10.13, 31/10/2018
-- Bugfix: Tollbooth fix
-
-1.10.12, 08/12/2018
-- Added the option to allow/disallow vehicles to enter a blocked junction at transition and pedestrian crossing nodes (#195)
-- Updated Russian translation (thanks to vitalii2011 for translating)
-- Bent nodes do not allow for u-turns by default (#170)
-- Bugfix: Emergency vehicles pass closed barriers at level crossings
-- Bugfix: Bus lines render u-turn where they should not (#207)
-- Bugfix: Parking AI: Cims leaving the city despawn their car at public transport stations (#214)
-- Bugfix: Crossing restrictions do not work at intersection between road and highway (#212)
-
-1.10.11, 21/07/2018
-- U-turn lane connections are represented by appropriate lane arrow (#201)
-- Bugfix: Heavy vehicles are unable to u-turn at dead ends (#194)
-- Bugfix: Routing & Priority rules do not work properly for acute (< 30°)/obtuse(> 150°) segment angles (#199)
-- Bugfix: Buses do not prefer lanes with correct lane arrow (#206)
-- Bugfix: Race condition in path-finding might cause paths to be assigned to wrong vehicle/citizen (#205)
-- Bugfix: Vehicles are unable to perform u-turns when setting off on multi-lane roads (#197)
-
-1.10.10, 14/07/2018
-- Parking AI: Improved park & ride behavior
-- Parking AI: Walking paths from parking position to destination building take public transportation into account
-- Bugfix: Parking AI causes unnecessary path-findings (#183, thanks to Sipke82 for reporting)
-- Bugfix: Prohibiting cims from crossing the road also affect paths where crossing is unnecessary (#168, thanks to aubergine10 for reporting)
-
-1.10.9, 13/07/2018
-- Updated for game version 1.10.1-f3
-- Re-implemented path-finding algorithm
-- Updated French translation (thanks to mjm92150 for translating!)
-
-1.10.8, 01/07/2018
-- Updated Korean translation (thanks to @Toothless FLY [ROK]LSh.st for translating)
-- Updated Polish translation (thanks to @Krzychu1245 for translating)
-- Added button to remove parked vehicles (in options dialog, see maintenance tab)
-- Parking AI: Removed check for distance between parked vehicle and target building
-- Bugfix: Parking AI: Cims spawn pocket cars when they originate from an outside connection
-- Bugfix: Incorrect speed limits returned for pedestrian lanes
-- Bugfix: Routing is not updated while the game is paused (thanks to @Oh My Lawwwd! for reporting)
-- Bugfix: Vanilla traffic lights are ignored when either the priority signs or timed traffic light features are disabled (thanks to @aubergine10 for reporting)
-- Bugfix: Park maintenance vehicles are not recognized as service vehicles
-- Bugfix: Cars leaving city state "thinking of a good parking spot" (thanks to @aubergine10 for reporting)
-
-1.10.7, 28/05/2018
-- Bugfix: U-turn routing is inconsistent on transport lines vs. bus paths (#137, thanks to @Zorgoth for reporting this issue)
-- Bugfix: Junction restrictions for pedestrian crossings are sometimes not preserved (#142, thanks to Anrew and @wizardrazer for reporting this issue)
+
+> **Legend:**
+>
+> * **C:SL** = Cities: Skylines game updates
+> * **TM:PE** = Traffic Manager: President Edition
+> * **TPP2** = Traffic++ V2
+> * **TMPlus** = Traffic Manager Plus
+> * **TPP:AI** - Traffic++ Improved AI
+> * **TPP** = Traffic++
+> * **TM** = Traffic Manager
+
+### TM:PE [10.21.1 hotfix](https://github.com/krzychu124/Cities-Skylines-Traffic-Manager-President-Edition/compare/10.21...10.21.1), 06/07/2019
+
+- Fixed: Speed panel tanks fps if train tracks on screen (thanks rlas & DaEgi01!) (#411, #413)
+- Meta: Main changelog refactored (#412)
+
+### TM:PE [10.21](https://github.com/krzychu124/Cities-Skylines-Traffic-Manager-President-Edition/compare/10.20...10.21), 02/07/2019
+
+- Added: Cims have individual driving styles to determine lane changes and driving speed (#263 #334)
+- Added: Miles Per Hour option for speed limits (thanks kvakvs) (#384)
+- Added: Selectable style (US, UK, EU) of speed sign in speed limits UI (thanks kvakvs) (#384)
+- Added: Differentiate LABS, STABLE and DEBUG branches in UI (#326, #333)
+- Added: Keybinds tab in mod options - choose your own shortcuts! (thanks kvakvs) (#382)
+- Added: Show keyboard shortcuts in button tooltips where applicable (thanks kvakvs) (#382)
+- Added: Basic support of offline mode for users playing on EA's Origin service (#333, #400)
+- Improved:: Avoid setting loss due to duplicate TM:PE subscriptions (#333, #306, #149, #190, #211, #400)
+- Fixed: Vehicle limit count; compatibility with More Vehicles mod (thanks Dymanoid) (#362)
+- Fixed: Mail trucks ignoring lane arrows (thanks Subaru & eudyptula for feedback) (#307, #338)
+- Fixed: Vehicles stop in road trying to find parking (thanks eudyptula for investigating) (#259, #359)
+- Fixed: Random parking broken (thanks s2500111 for beta testing) (#259, #359)
+- Fixed: Pedestrian crossing restriction affects road-side parking (#259, #359)
+- Fixed: 'Vanilla Trees Remover' is now compatible (thanks TPB for fixing) (#331, #332)
+- Fixed: Single-lane bunching on DLS higher than 50% (#263 #334)
+- Fixed: Lane changes at toll booths (also notified CO of bug in vanilla) (#225, #355)
+- Fixed: Minor issues regarding saving/loading junction restrictions (#358)
+- Fixed: Changes of default junction restrictions not reflected in UI overlay (#358)
+- Fixed: Resetting stuck cims unpauses the simulation (#358, #351)
+- Fixed: Treat duplicate TM:PE subscriptions as mod conflicts (#333, #306, #149, #190, #400)
+- Fixed: TargetInvocationException in mod compatibility checker (#386, #333)
+- Fixed: Issue with Paradox login blurring compatibility checker dialog (#404)
+- Updated: Game version 1.12.1-f1 compatible (#403)
+- Updated: Chinese translation (thanks Emphasia) (#375, #336)
+- Updated: German translation (thanks kvakvs) (#384)
+- Updated: Polish translation (thanks krzychu124) (#384, #333)
+- Updated: Russian translation (thanks vitalii201 & kvakvs) (#327, #328)
+- Updated: Renamed 'Realistic driving speeds' to 'Individual driving styles' (#334)
+- Removed: Obsolete `TMPE.GlobalConfigGenerator` module (#367, #374)
+- Meta: Separate binaries for Stable and Labs on GitHub release pages (#360)
+- Meta: Initial documentation for release process in wiki (see `Contributing` page) (#360)
+- Meta: Added GitHub issue templates for bugs, features, translations. (#272)
+- Meta: Added `.editorconfig` file for IDE code indenting standardisation (#392, #384)
+- Meta: Added entire `.vs/` and `.idea/` folders to `.gitignore` (#395, #382)
+- Meta: Updated install guide to include section for EA Origin users (#333)
+- Meta: Enable latest C# `LangVersion` in all projects (#398)
+
+### C:SL 1.12.1-f2, 04/06/2019
+
+- Fixed: Numerous bugs in Campus DLC
+
+### TM:PE 10.20, 21/05/2019
+
+- Updated: Compatible with C:SL 1.12.0-f5
+- Updated: Korean translation (thanks Twotoolus-FLY-LShst) (#294)
+- Updated: French translation (thanks PierreTSE) (#311)
+
+### C:SL 1.12.0-f5 (Campus), 21/05/2019
+
+- Added: University areas & lots of buildings
+- Added: Choose bus to use on bus lines
+
+### TM:PE 10.19, 20/04/2019
+
+- Fixed: Mod options overlapping issue (#250, #266).
+- Updated: Moved "Delete" step button on timed traffic lights (#283, #285).
+- Updated: "Vanilla Trees Remover" as incompatible mod (it breaks mod options screen) (#271, #290).
+- Updated: Mod incompatibility checker can now be disabled, or skip disabled mods (#264, #284, #286).
+- Updated: Chinese language (thanks Emphasia) (#285, #286).
+- Updated: Japanese language (thanks mashitaro) (#258).
+
+### TM:PE 10.18, 29/03/2019
+
+- Fixed: Parking AI: Cars do not spawn at outside connections (#245)
+- Fixed: Trams perform turns on red (#248)
+- Updated: Service Radius Adjuster mod by Egi removed from incompatible mods list (#255)
+
+### TM:PE 10.17, 23/03/2019
+
+- Added: Turn-on-red can now be toggled for unpreferred turns between one-ways
+- Improved: Train behavior at shunts: Trains now prefer to stay on their track (#230)
+- Improved: Parking AI - Improved public transport (PT) usage patterns, mixed car/PT paths are now possible (#218)
+- Fixed: Lane selection for u-turns and at dead ends (also optimised) (#101)
+- Fixed: Parking AI - Tourist cars despawn because they assume they are at an outside connection (#218)
+- Fixed: Parking AI - Return path calculation did not accept beautification segments (#218)
+- Fixed: Parking AI - Cars/Citizens waiting for a path might jump around (#218)
+- Fixed: Vanilla lane randomization does not work as intended at highway transitions (#112)
+- Fixed: Vehicles change lanes at tollbooths (#225)
+- Fixed: Path-finding: Array index is out of range due to a race condition (#221)
+- Fixed: Citizen not found errors when using walking tours (#219)
+- Fixed: Timed light indicator only visible when any timed light node is selected (#222)
+- Updated: Compatible with C:SL 1.11.1-f4
+- Updated: Synchronized code and version with stable version
+- Updated: List of incompatible mods (#115)
+- Updated: Russian translation (thanks to vitalii201 for translating) (#207)
+- Removed: Stable version from list of incompatible mods (#168)
+- Meta: Introduced new versioning scheme (10.17 instead of 1.10.17)
+
+### C:SL 1.11.1-f4, 27/02/2019
+
+- Fixed: Remove duplicate map
+
+### TM:PE 1.10.16, 24/02/2019
+
+- Improved: New icons for empty and remove_priority_sign settings (thanks aubergine10 for those icons) (#75, #77)
+- Fixed: Problem with vehicle despawn after road upgrade/remove (thanks pcfantasy for implementation suggestion)(#86, #101)
+- Fixed: problem with vehicles unable to choose lane when u-turn at dead-end (thanks pcfantasy for implementation and aubergine10 for neccesary tests)(#101)
+- Fixed: problem when user couldn't change state of 'Turn on Red' while enabled_by_default option not selected (thanks Sp3ctre18 for bug confirmation) (#102)
+- Fixed: Fixed 'silent error' inside log related with "Esc key handler" (#92)
+- Updated: Greatly improved incompatible mod scanner, added dialog to list and unsubscribe incompatible mods (#91)
+- Updated: Changed mod name in Content Manager to __TM:PE__
+- Updated: Added missing logic for noise density calculations (thanks to pcfantasy for fix) (#66)
+- Meta: Discord server was set up by FireController1847 - link in mod description
+- Meta: Added project building instructions and PR review
+
+### TM:PE 1.10.15, 10/02/2019
+
+- Added: (Experimental) Turn on red (thanks to FireController1847 for implementation and to pcfantasy for source code base)
+- Added: Notification if user is still subscribed to old original TM:PE
+- Improved: Use Escape key to close Traffic Manager without returning to Pause Menu (thanks to aubergine10 for suggestion) (#16)
+- Improved: New icons for cargo and passenger train restriction (thanks to aubergine10) (#17)
+- Updated: Updated pathfinding with missing vanilla logic
+- Updated: Tweaked values in CargoTruckAI path finding (thanks to pcfantasy for improvement suggestion)
+- Updated: Tweaked speed multiplier of reckless drivers to get more realistic speed range (thanks to aubergine10 for suggestion) (#23)
+- Updated: Simplified Chinese translation updated (thanks to Emphasia for translating)
+
+### TM:PE 1.10.14, 27/01/2019
+
+- Fixed: Added missing Car AI type (postVanAI) - now post vans and post trucks are assigned to service vehicles group
+- Fixed: Vehicles doesn't stop when driving through toll booth - fixes toll booth income too
+- Fixed: Cargo Airport doesn't work (Cargo planes not spawning and not arriving)
+- Fixed: Mod Options layout (text label overlaps slider control if too wide)
+- Updated: Compatible with C:SL 1.11.1-f2
+- Updated: Polish translation
+- Updated: Korean translation (thanks to Toothless FLY [ROK]LSh.st for translating)
+
+### C:SL 1.11.1-f2 (Holiday Surprise Patch), 13/12/2018
+
+- Fixed: Cargo planes circle and use buildings that do not otherwise have any plane traffic
+- Fixed: Vehicles can't cross the center line of Small Industry roads
+- Fixed: Various bugs in game and DLCs
+
+### TM:PE 1.10.13, 31/10/2018
+
+- Fixed: Toll booth not working
+- Meta: Roads United Core also breaks toll booths
+
+### TM:PE 1.10.12, 08/12/2018
+
+- Added: Allow/disallow vehicles to enter a blocked junction at transition and pedestrian crossing nodes (#195)
+- Fixed: Emergency vehicles pass closed barriers at level crossings
+- Fixed: Bus lines render u-turn where they should not (#207)
+- Fixed: Parking AI - Cims leaving the city despawn their car at public transport stations (#214)
+- Fixed: Crossing restrictions do not work at intersection between road and highway (#212)
+- Updated: Compatible with C:SL 1.11.0-f3
+- Updated: Bent nodes do not allow for u-turns by default (#170)
+- Updated: Russian translation (thanks to vitalii2011 for translating)
+
+### C:SL 1.11.0-f3 (Industries), 23/10/2018
+
+- Added: Toll booths
+- Added: Postal service, vans and trucks
+- Added: Additional industry vehicles
+- Added: Cargo airport and planes
+- Added: Warehouses and storage buildings
+- Fixed: Bugs in various DLCs
+
+### TM:PE 1.10.11, 21/07/2018
+
+- Updated: U-turn lane connections are represented by appropriate lane arrow (#201)
+- Fixed: Heavy vehicles are unable to u-turn at dead ends (#194)
+- Fixed: Routing & Priority rules do not work properly for acute (< 30°)/obtuse(> 150°) segment angles (#199)
+- Fixed: Buses do not prefer lanes with correct lane arrow (#206)
+- Fixed: Race condition in path-finding might cause paths to be assigned to wrong vehicle/citizen (#205)
+- Fixed: Vehicles are unable to perform u-turns when setting off on multi-lane roads (#197)
+
+### TM:PE 1.10.10, 14/07/2018
+
+- Improved: Parking AI - Improved park & ride behaviour
+- Fixed: Parking AI causes unnecessary path-findings (#183, thanks to Sipke82 for reporting)
+- Fixed: Prohibiting cims from crossing the road also affect paths where crossing is unnecessary (#168, thanks to aubergine10 for reporting)
+- Updated: Parking AI - Walking paths from parking position to destination building take public transportation into account
+
+### TM:PE 1.10.9, 13/07/2018
+
+- Updated: Compatible with C:SL 1.10.1-f3
+- Updated: Re-implemented path-finding algorithm
+- Updated: French translation (thanks to mjm92150 for translating!)
+
+### C:SL 1.10.1-f3, 04/07/2018
+
+- Fixed: Various bugs in game and DLCs
+
+### TM:PE 1.10.8, 01/07/2018
+
+- Added: Button to remove parked vehicles (in options dialog, see maintenance tab)
+- Fixed: Parking AI - Cims spawn pocket cars when they originate from an outside connection
+- Fixed: Incorrect speed limits returned for pedestrian lanes
+- Fixed: Routing is not updated while the game is paused (thanks to Oh My Lawwwd! for reporting)
+- Fixed: Vanilla traffic lights are ignored when either the priority signs or timed traffic light features are disabled (thanks to aubergine10 for reporting)
+- Fixed: Park maintenance vehicles are not recognized as service vehicles
+- Fixed: Cars leaving city state "thinking of a good parking spot" (thanks to aubergine10 for reporting)
+- Updated: Parking AI - Removed check for distance between parked vehicle and target building
+- Updated: Korean translation (thanks to Toothless FLY [ROK]LSh.st for translating)
+- Updated: Polish translation (thanks to Krzychu1245 for translating)
+
+### TM:PE 1.10.7, 28/05/2018
+
+- Fixed: U-turn routing is inconsistent on transport lines vs. bus paths (#137, thanks to Zorgoth for reporting this issue)
+- Fixed: Junction restrictions for pedestrian crossings are sometimes not preserved (#142, thanks to Anrew and wizardrazer for reporting this issue)
- Fixed: Geometry subscription feature may cause performance issues (#145)
-- Fixed: Parking AI: Transport mode storage causes performance issues during loading (#147, thanks to @hannebambel002 and @oneeyets for reporting and further for providing logs and savegames)
-
-1.10.6, 24/05/2018
-- Updated for game version 1.10.0-f3
-- Accessibility: New option: Main menu size can be controlled
-- Accessibility: New option: GUI and overlay transparency can be controlled
-- New option: Penalties for switching between different public transport lines can be toggled
-- Cims can now be removed from the game
-- Improved window design
-- Path-finding: Service vehicles are now allowed to ignore lane arrows right after leaving their source building, thus service buildings should now work properly at dead-end roads with median
-- Lane connector can be used on monorail tracks
-- Advanced Vehicle AI: Tuned parameters
-- Dynamic Lane Selection: Absolute speed measurements are used instead of relative measurements
-- Improved randomization for realistic speeds such that vehicles may change their target velocity over time
-- Improved vehicle position tracking
-- Improved mod compatibility checks
-- Parking AI: Improved behavior in situations where vehicles are parked near public transport hubs and road connections are partially unavailable
-- Bugfix: Parking AI: Not all possible paths are regarded during path-finding
-- Bugfix: Parking AI: Cims become confused when trying to return their abandoned car back home (special thanks to Wildcard-25 for reporting and solving this issue)
-- Bugfix: Parking AI: Cims do not search for parking building when road-side parking spaces are found
-- Bugfix: Parking AI: Parked vehicles are spawned near the source building even when cims are already en route
-- Bugfix: Parking AI: Cims sometimes get stuck in an infinite loop while trying to enter their parked car
-- Bugfix: Lane connector does not work for roads with more than ten lanes
-- Bugfix: Allowing/Disallowing vehicles to enter a blocked junction does not work for certain junctions
-- Updated Korean translation (thanks to @Toothless FLY [ROK]LSh.st for translating)
-
-1.10.5, 06/01/2018
-- UI scaling removed
-- Simplified Chinese translation updated (thanks to Emphasia for translating)
-- Polish translation updated (thanks to @Krzychu1245 for translating)
-- Introduced randomization for lane changing costs
-- Introduced randomization for "trucks prefer innermost lanes on highways" costs
-- Removed unnecessary calculations in path-finding
-- Added path-finding costs for public transport transitions
-- Pedestrian traffic lights do not show up if crossing the street is prohibited
-- Busses are allowed to switch multiple lanes after leaving a bus stop
-- Bugfix: Main menu button might be out of view
-- Bugfix: Division by zero occurs for low speed roads
-- Bugfix: Automatic pedestrian lights at railroad do not work as expected
-- Bugfix: Timed traffic lights show up for bicycles (they should not)
-- Bugfix: Due to a multi-threading issue junction restrictions may cause the game state to become inconsistent
-- Bugfix: Routing rules prevents vehicles from spawning when starting building lies too close to an intersection/road end
-- Bugfix: Disabling tutorial message has no effect
-- Bugfix: "Stay on lane" feature does not work as intended for certain nodes
-
-1.10.4, 19/10/2017
-- Updated for game version 1.9.0-f5
-- Added possibility to add priority signs at multiple junctions at once (press Shift)
-- Added tutorials (can be disabled in the options window globally)
-
-1.10.3, 18/08/2017
-- Bugfix: Setting unlimited speed limit causes vehicles to crawl at low speed (thanks to @sethisuwan for reporting this issue)
-- Bugfix: Vehicle-separated traffic lights do not show up for trams & monorails (thanks to @thecitiesdork for reporting this issue)
-
-1.10.2, 17/08/2017
-- Updated for game version 1.8.0-f3
-- Improved performance
-- Bugfix: Pedestrians sometimes ignore red traffic light signals (thanks to @(c)RIKUPI™ for reporting this issue)
-- Bugfix: Timed traffic lights do not correctly recognize set vehicle restrictions (thanks to @alborzka for reporting this issue)
-
-1.10.1, 05/08/2017
-- Updated Polish, Korean, and Simplified Chinese translations
-- Bugfix: Default routing is disabled if the lane connector is used on a subset of all available lanes only
-- Bugfix: Parking AI cannot be enabled/disabled
-- Bugfix: Lane connection points can connected to themselves
-
-1.10.0, 30/07/2017
-- New feature: Dynamic Lane Selection
-- New feature: Adaptive step switching
-- New feature: Individual vehicles may be removed from the game
-- New option: Vehicle restrictions aggression
-- New option: Vehicles follow priority rules at junctions with timed traffic lights
-- Improved path-finding performance
-- Improved traffic measurement engine performance
-- Reorganized global configuration file (sorry, your main menu and main button positions are reset)
-- The option "Road condition has a bigger impact on vehicle speed" is only shown if the Snowfall DLC is owned
-- The flow/wait calculation mode to be used is now configurable via the global configuration file
-- Added path-find statistics label
-- Added confirmation dialog for "Clear Traffic" button
-- Currently active timed traffic light step is remembered
-- Trains do not wait for each other anymore near timed traffic lights
-- It is now possible to connect train station tracks and outside connections with the lane connector
-- Disabling the Parking AI triggers graceful clean up procedure
-- Relocated some options
-- Improved vehicle state tracking
-- Workaround for a base game issue that causes trams to get stuck
-- Trains do not longer stop in front of green timed traffic lights
-- Vehicles use queue skipping to prioritize path-finding runs that are caused by road modifications
-- Adding a vehicle separate light to a timed traffic lights applies the main light configuration
-- Parking AI: Vehicles can now find parking spaces at the opposite road side
-- Parking AI: Included an improved fallback logic for some edge cases
-- Parking AI: Citizens should now be more successful in returning their cars back home
-- Parking AI: Tuned parking radius parameters
-- Parking AI: If the limit for parked vehicles is reached and parking fails due to it, no alternative parking space is queried
-- Vehicle AI: Busses prefer lanes with correct lane arrow over incorrect ones
-- Bugfix: Using the bulldozer tool might lead to inconsistent road geometry information
-- Bugfix: Citizens that fail to approach their parked car fly towards their target building
-- Bugfix: Parking AI: Path-finding fails if cars are parked too far away from a road
-- Bugfix: Parking AI: Citizens approaching a car start to float away
-- Bugfix: "Heavy vehicles prefer outer lanes on highways" does not work
-- Bugfix: The lane connector does not allow connecting all available lane end points at train stations and on bidirectional one-lane train tracks
-- Bugfix: Vehicles may get stuck in several situations
-- Upgrading to a road with bus lanes now copies an already existing traffic light state to the new traffic light
-
-1.9.6, 28/05/2017
-- Updated Simplified Chinese translation
-- Bugfix: Vehicles cannot perform u-turns at junctions with only one outgoing segment (thanks to @Sunbird for reporting this issue)
-- Bugfix: Path-finding costs for large distances exceed the maximum allowed value (thanks to @Huitsi for reporting this issue)
-- Bugfix: Under certain circumstances path-finding at railroad crossings allow switching from road to rail tracks.
-
-1.9.5, 24/05/2017
-- Updated for game version 1.7.1-f1
-- Updated Polish, Korean and Italian translation
-- Language can now be switched without requiring a game restart
-- Bugfix: Routing calculation does not work as expected for one-way roads with tram tracks (thanks to @bigblade66, @Battelman2 and @AS_ for reporting and providing extensive information)
-- Bugfix: Copying timed traffic lights lead to inconsistent internal states which causes timed traffic lights to be omitted during the save process (thanks to @jakeroot and @t1a2l for reporting this issue)
-- Bugfix: In certain situations unnecessary vehicle-seperate traffic lights are being created
-- Bugfix: Upgrading a train track segment next to a timed traffic light causes trains to ignore the traffic light
-- Hotfix: Cable cars despawn at end-of-line stations
-
-1.9.4, 23/05/2017
-- New option: Ban private cars and trucks on bus lanes
-- Updated Spanish and French translation
-- Optimized path-finding
-- Increased path-finding cost for private cars driving on bus lanes
-- Increased path-finding cost for disregarding vehicle restrictions
-- Bugfix: Path-finding is unable to calculate certain paths after modifying the road network
-
-1.9.3, 22/05/2017
-- Disabled notification of route recalculating because some players report crashes
-- Removed default vehicle restrictions from bus lanes
-- Modified junction restrictions come into effect instantaneously
-- UI: Saving a timed step does not reset the timed traffic light to the first state
-- Bugfix: AI: Segment traffic data is not taken into account
-- Bugfix: Priority rules are not properly obeyed
-- Bugfix: Under certain circumstances priority signs cannot be removed
-- Bugfix: Path-finding is unable to calculate certain paths
-
-1.9.2, 20/05/2017
-- UI: Main menu & UI tools performance improved
-- Bugfix: Traffic lights can be removed from junctions that are controlled by a timed traffic light program
-
-1.9.1, 19/05/2017
-- Updated French, Dutch and Korean translation
-- Bugfix: Using the vanilla traffic light toggling feature crashes the game if TMPE's main menu has not been opened at least once
-- Bugfix: AI: More car traffic and less public transportation present than in vanilla
-
-1.9.0, 18/05/2017
-- Updated for game version 1.7.0-f5
-- New feature: Parking restrictions
-- New feature: Speed limits can be set up for individual lanes with the Control key
-- New feature: Added timed traffic light and speed limit support for monorails
-- New feature: Copy & paste for individual timed traffic lights
-- New feature: Rotate individual timed traffic lights
-- New feature: Lane customizations may come into effect instantaneously
-- Unified traffic light toggling feature with game code
-- Performance improvements
-- Reworked the way that traffic measurements are performed
-- Advanced Vehicle AI: Algorithm updated, performance improved - Possible routing decisions are now being precalculated
-- Path-finding cost multiplicator for vehicle restrictions is now configurable in TMPE_GlobalConfig.xml
-- UI: More compact, movable main menu UI
-- Added support for custom languages
-- Added Korean translation (thanks to @Toothless FLY [ROK]LSh.st for translating)
-- Updated translations: German, Polish, Russian, Portuguese, Traditional Chinese
-- Major code refactorings
-- AI: Tuned path-finding parameters
-- New option: Main button position can be locked
-- New option: Main menu position can be locked
-- New option: Added language selection in options dialog
-- New option: Customization of lane arrows, lane connections and vehicle restrictions can now come into effect instantaneously
-- Bugfix: Cars sometimes get stuck forever when the Advanced Parking AI is activated (thanks to @cmfcmf for reporting this issue)
-- Bugfix: Busses do not perform u-turns even if the transport line show u-turns (thanks to @dymanoid for reporting this issue)
-- Bugfix: Timed traffic lights do not work as expected on single-direction train tracks (thanks to @DaEgi01 for reporting this issue)
-- Bugfix: Vehicle restriction and speed limit signs overlay is displayed on the wrong side of inverted road segments
-- Bugfix: Influx statistics value is zero (thanks to @hjo for reporting this issue)
-
-1.8.16, 20/03/2017
-- Lane connections can now also be removed by pressing the backspace key
-- Improved lane selection for busses if the option "Busses may ignore lane arrows" is activated
-- Bugfix: The game sometimes freezes when using the timed traffic light tool
-- Bugfix: Lane connections are not correctly removed after modifying/removing a junction
-- Bugfix: Selecting a junction for setting up junction restrictions toggles the currently hovered junction restriction icon
-
-1.8.15, 27/01/2017
-- Updated for game version 1.6.3-f1
-
-1.8.14, 07/01/2017
-- Bugfix: Wait/flow ratio at timed traffic lights is sometimes not correctly calculated
-- Bugfix: A deadlock situation can arise at junctions with priority signs such that no vehicle enters the junction
-- Bugfix: When adding a junction to a timed traffic light, sometimes light states given by user input are not correctly stored
-- Bugfix: Joining two timed traffic lights sets the minimum time to "1" for steps with zero minimum time assigned
-- Bugfix: Modifications of timed traffic light states are sometimes not visible while editting the light (but they are applied nonetheless)
-- Bugfix: Button background is not always correctly changed after clicking on a button within the main menu
-- Tram lanes can now be customized by using the lane connector tool
-- Minor performance optimizations for priority sign simulation
-
-1.8.13, 05/01/2017
-- Bugfix: Timed traffic ligt data can become corrupt when upgrading a road segment next to a traffic light, leading to faulty UI behavior (thanks to @Brain for reporting this issue)
-- Bugfix: The position of the main menu button resets after switching to the free camera mode (thanks to @Impact and @gravage for reporting this issue)
-- Bugfix: A division by zero exception can occur when calculating the average number of waiting/floating vehicles
-- Improved selection of overlay markers on underground roads (thanks to @Padi for reminding me of that issue)
-- Minor performance improvements
-
-1.8.12, 02/01/2017
-- Updated for game version 1.6.2-f1
-- Bugfix: After leaving the "Manual traffic lights" mode the traffic light simulation is not cleaned up correctly (thanks to @diezelunderwood for reporting this issue)
-- Bugfix: Insufficient access rights to log file causes the mod to crash
-
-1.8.11, 02/01/2017
-- Bugfix: Speed limits for elevated/underground road segments are sometimes not correctly loaded (thanks to @Pirazel and @[P.A.N] Uf0 for reporting this issue)
-
-1.8.10, 31/12/2016
-- Improved path-finding performance (a bit)
-- Added a check for invalid road thumbnails in the "custom default speed limits" dialog
-
-1.8.9, 29/12/2016
-- It is now possible to set speed limits for metro tracks
-- Custom default speed limits may now be defined for train and metro tracks
-- Junction restrictions may now be controlled at bend road segments
-- Customizable junctions are now highlighted by the lane connector tool
-- Improved UI behavior
-- Performance improvements
-- Bugfix: Selecting a junction to set up priority signs sometimes does not work (thanks to @Artemis *Seven* for reporting this issue)
-- Bugfix: Automatic pedestrian lights do not work as expected at junctions with incoming one-ways and on left-hand traffic maps
-
-1.8.8, 25/12/2016
-- Bugfix: Taxis are not being used
-- Bugfix: Prohibiting u-turns with the junction restriction tool does not work (thanks to @Kisoe for reporting this issue)
-- Bugfix: Cars are sometimes floating across the map while trying to park (thanks to @[Delta ²k5] for reporting this issue)
-
-1.8.7, 24/12/2016
-- Bugfix: Parking AI: Cims that try to reach their parked car are sometimes teleported to another location where they start to fly through the map in order to reach their car
-- Bugfix: Parking AI: Cims owning a parked car do not consider using other means of transportation
-- Bugfix: Parking AI: Residents are unable to leave the city through a highway outside connection
-- Bugfix: Trains/Trams are sometimes not detected at timed traffic lights
-- Advanced AI: Improved lane selection
-- The position of the main menu button is now forced inside screen bounds on startup
-- Improved overall user interface performance
-- Improved overlay behavior
-- Improved traffic measurement
-- Auto pedestrian lights at timed traffic lights behave more intelligently now
-- A timed traffic light step with zero minimum time assigned can now be skipped automatically
-- Using the lane connector to create a u-turn now automatically enables the "u-turn allowed" junction restriction
-- Updated French translation (thanks to @simon.royer007 for translating)
-- Added Italian translation (thanks to @Admix for translating)
-
-1.8.6, 12/12/2016
-- Added Korean language (thanks to @Toothless FLY [ROK]LSh.st for translating)
-- Updated Chinese language code (zh-cn -> zh) in order to make it compatible with the game (thanks to @Lost丶青柠 for reporting this issue)
-
-1.8.5, 11/12/2016
-- Updated to game version 1.6.1-f2
-- Removed option "Evacuation busses may only be used to reach a shelter" (CO fixed this issue)
-- Bugfix: Average speed limits are not correctly calculated for road segments with bicycle lanes (thanks to @Toothless FLY [ROK]LSh.st for reporting this issue)
-
-1.8.4, 11/12/2016
-- New feature: "Stay on lane": By pressing Shift + S in the Lane Connector tool you can now link connected lanes such that vehicles are not allowed to change lanes at this point. Press Shift + S again to restrict "stay on lane" to either road direction.
-- U-turns are now only allowed to be performed from the innermost lane
-- TMPE now detects if the number of spawned vehicles is reaching its limit (16384). If so, spawning of service/emergency vehicles is prioritized over spawning other vehicles.
-- Bugfix: Bicycles cannot change from bicycle lanes to pedestrian lanes
-- Bugfix: Travel probabilities set in the "Citizen Lifecycle Rebalance v2.1" mod are not obeyed (thanks to @informmanuel, @shaundoddmusic for reporting this issue)
-- Bugfix: Number of tourists seems to drop when activating the mod (statistics were not updated, thanks to @hpp7117, @wjrohn for reporting this issue)
-- Bugfix: When loading a second savegame a second main menu button is displayed (thanks to @Cpt. Whitepaw for reporting this issue)
-- Bugfix: While path-finding is in progress vehicles do "bungee-jumping" on the current segment (thanks to @mxolsenx, @Howzitworld for reporting this issue)
-- Bugfix: Cims leaving the city search for parking spaces near the outside connection which is obviously not required
-
-1.8.3, 4/12/2016
-- Bugfix: Despite having the Parking AI activated, cims sometimes still spawn pocket cars.
-- Bugfix: When the Parking AI is active, bicycle lanes are not used (thanks to @informmanuel for reporting this issue)
-- Tweaked u-turn behavior
-- Improved info views
-
-1.8.2, 3/12/2016
-- Bugfix: Taxis were not used (thanks to @[Delta ²k5] for reporting)
-- Bugfix: Minor UI fix in Default speed limits dialog
-
-1.8.1, 1/12/2016
-- Updated translations: Polish, Chinese (simplified)
-- Bugfix: Mod crashed when loading a second savegame
-
-1.8.0, 29/11/2016
-- Updated to game version 1.6.0-f4
-- New feature: Default speed limits
-- New feature: Parking AI (replaces "Prohibit cims from spawning pocket cars")
-- New option: Heavy vehicles prefer outer lanes on highways
-- New option: Realistic speeds
-- New option: Evacuation busses may ignore traffic rules (Natural Disasters DLC required)
-- New option: Evacuation busses may only be used to reach a shelter (Natural Disasters DLC required)
-- AI: Improved lane selection, especially on busy roads
-- AI: Improved mean lane speed measurement
-- Traffic info view shows parking space demand if Parking AI is activated
-- Public transport info view shows transport demand if Parking AI is activated
-- Added info texts for citizen and vehicle tool tips if Parking AI is activated
-- Extracted internal configuration to XML configuration file
-- Changed main menu button due to changes in the game's user interface
-- Main menu button is now moveable
-- Removed compatibility check for Traffic++ V2 (Traffic++ V2 is no longer compatible with TMPE because maintaining compatibility is no longer feasible due to the high effort)
-- Updated translations: German, Portuguese, Russian, Dutch, Chinese (traditional)
-
-1.7.15, 26/10/2016
-- Bugfix: Timed traffic lights window disappears when clicking on it with the middle mouse button (thanks to @Nexus and @Mariobro14 for helping me identifying the cause of this bug)
-
-1.7.14, 18/10/2016
-- Updated for game version 1.5.2-f3
-
-1.7.13, 15/09/2016
-- Implemented a permanent fix to solve problems with stuck vehicles/cims caused by third party mods
-- Added a button to reset stuck vehicles/cims (see mod settings menu)
-- AI: Improved lane selection algorithm
-- Bugfix: AI: Lane merging was not working as expected
-- Bugfix: Pedestrian light states were sometimes not being stored correctly (thanks to Filip for pointing out this problem)
-
-1.7.12, 09/09/2016
-- AI: Lane changes are reduced on congested road segments
-- Timed traffic lights should now correctly detect trains and trams
-- Bugfix: GUI: Junction restriction icons sometimes disappear
-- Updated Chinese (simplified) translation
-
-1.7.11, 01/09/2016
-- Updated to game version 1.5.1-f3
-
-1.7.10, 31/08/2016
-- Players can now disable spawning of pocket cars
-- Updated Chinese (simplified) translation
-- Bugfix: Timed traffic lights were flickering
-- Bugfix: Pedestrian traffic lights were not working as expected
-- Bugfix: When upgrading/removing/adding a road segment, nearby junction restrictions were removed
-- Bugfix: Setting up vehicle restrictions affects trams (thanks to @chem for reporting)
-- Bugfix: Manual pedestrian traffic light states were not correctly handled
-- Bugfix: Junction restrictions overlay did not show all restricted junctions
-
-1.7.9, 22/08/2016
-- In-game traffic light states are now correctly rendered when showing "yellow"
-- Removed negative effects on public transport usage
-- GUI: Traffic light states do not flicker anymore
-- Performance improvements
-
-1.7.8, 18/08/2016:
-- Bugfix: Cims sometimes got stuck (thanks to all reports and especially to @Thilawyn for providing a savegame)
-- GUI: Improved traffic light arrow display
-- Improved performance while saving
-
-1.7.7, 16/08/2016:
-- AI: Instead of walking long distances, citizens now use a car
-- AI: Citizens will remember their last used mode of transport (e.g. they will not drive to work and come return by bus anymore)
-- AI: Increased path-finding costs for traversing over restricted road segments
-- Added "110" speed limit
-- GUI: Windows are draggable
-- GUI: Improved window scaling on lower resolutions
-- Improved performance while saving
-
-1.7.6, 14/08/2016:
-- New feature: Players may now prohibit cims from crossing the street
-- AI: Tuned randomization of lane changing behavior
-- AI: Introduced path-finding costs for leaving main highway (should reduce amount of detours taken)
-- UI: Clicking with the secondary mouse button now deselects the currently selected node/segment for all tools
-- Added the possibility to connect train track lanes with the lane connector (as requested by @pilot.patrick93)
-- Moved options from "Change lane arrows" to "Vehicle restrictions" tool
-- Updated Russian translation
-- Bugfix: AI: At specific junctions, vehicles were not obeying lane connections correctly (thanks to @Mariobro14 for pointing out this problem)
-- Bugfix: AI: Path-finding costs for u-turns were not correctly calculated (thanks to @Mariobro14 for pointing out this problem)
-- Bugfix: Vehicles were endlessly waiting for each other at junctions with certain priority sign configurations
-- Bugfix: AI: Lane changing costs corrected
-
-1.7.5, 07/08/2016:
-- Bugfix: AI: Cims were using pocket cars whenever possible
-- Bugfix: AI: Path-finding failures led to much less vehicles spawning
-- Bugfix: AI: Lane selection at junctions with custom lane connection was not always working properly (e.g. for Network Extensions roads with middle lane)
-- Bugfix: While editing a timed traffic light it could happen that the traffic light was deleted
-
-1.7.4, 31/07/2016:
-- AI: Switched from relative to absolute traffic density measurement
-- AI: Tuned new parameters
-- Bugfix: Activated/Disabled features were not loaded correctly
-- Bugfix: AI: At specific junctions the lane changer did not work as intended
-- Possible fix for OSX performance issues
-- Code improvements
-- Added French translations (thanks to @simon.royer007 for translating!)
-
-1.7.3, 29/07/2016:
-- Added the ability to enable/disable mod features (e.g. for performance reasons)
-- Bugfix: Vehicle type determination was incorrect (fixed u-turning trams/trains, stuck vehicles)
-- Bugfix: Clicking on a train/tram node with the lane connector tool led to an uncorrectable error (thanks to @noaccount for reporting this problem)
-- Further code improvements
-
-1.7.2, 26/07/2016:
-- Optimized UI overlay performance
-
-1.7.1, 24/07/2016:
-- Reverted "Busses now may only ignore lane arrows if driving on a bus lane" for now
-- Bugfix: Trains were not despawning if no path could be calculated
-- Workaround for third-party issue: TM:PE now detects if the calculation of total vehicle length fails
-
-1.7.0, 23/07/2016:
-- New feature: Traffic++ lane connector
-- Busses now may only ignore lane arrows if driving on a bus lane
-- Rewritten and simplified vehicle position tracking near timed traffic lights and priority signs for performance reasons
-- Improved performance of priority sign rules
-- AI: Cims now ignore junctions where pedestrian lights never change to green
-- AI: Removed the need to define a lane changing probability
-- AI: Tweaked lane changing parameters
-- AI: Highway rules are automatically disabled at complex junctions (= more than 1 incoming and more than 1 outgoing roads)
-- Improved UI performance if overlays are deactivated
-- Simulation accuracy now also controls time intervals between traffic measurements
-- Added compatibility detection for the Rainfall mod
-- Improved fault-tolerance of the load/save system
-- Default wait-flow balance is set to 0.8
-- Bugfix: Taxis were allowed to ignore lane arrows
-- Bugfix: AI: Highway rules on left-hand traffic maps did not work the same as on right-hand traffic maps
-- Bugfix: Upgrading a road segment next to a timed traffic light removed the traffic light leading to an inconsistent state (thanks to @ad.vissers for pointing out this problem)
-
-1.6.22, 29/06/2016:
-- AI: Taxis now may not ignore lane arrows and are using bus lanes whenever possible (thanks to @Cochy for pointing out this issue)
-- AI: Busses may only ignore lane arrows while driving on a bus lane
-- Bugfix: Traffic measurement at timed traffic lights was incorrect
-
-1.6.22, 21/06/2016:
-- Speed/vehicle restrictions may now be applied to all road segments between two junctions by holding the shift key
-- Reworked how changes in the road network are recognized
-- Advanced Vehicle AI: Improved lane selection at junctions where bus lanes end
-- Advanced Vehicle AI: Improved lane selection of busses
-- Improved automatic pedestrian lights
-- Improved separate traffic lights: Traffic lights now control traffic lane-wise
-- UI: Sensitivity slider is only available while adding/editing a step or while in test mode
-- Bugfix: Lane selection on maps with left-hand traffic was incorrect
-- Bugfix: While building in pause mode, changes in the road network were not always recognized causing vehicles to stop/despawn
-- Bugfix: Police cars off-duty were ignoring lane arrows
-- Bugfix: If public transport stops were near a junction, trams/busses were not counted by timed traffic lights (many thanks to Filip for identifying this problem)
-- Bugfix: Trains/Trams were sometimes ignoring timed traffic lights (many thanks to Filip for identifying this problem)
-- Bugfix: Building roads with bus lanes caused garbage, bodies, etc. to pile up
-
-1.6.21, 14/06/2016:
-- Bugfix: Too few cargo trains were spawning (thanks to @Scratch, @toruk_makto1, @Mr.Miyagi, @mottoh and @Syparo for pointing out this problem)
-- Bugfix: Vehicle restrictions did not work as expected (thanks to @nordlaser for pointing out this problem)
-
-1.6.20, 11/06/2016:
-- Bugfix: Priority signs were not working correctly (thanks to @mottoth, @Madgemade for pointing out this problem)
-
-1.6.19, 11/06/2016
-- Bugfix: Timed traffic lights UI not working as expected (thanks to @Madgemade for pointing out this problem)
-
-1.6.18, 09/06/2016
-- Updated for game patch 1.5.0-f4
-- Improved performance of priority signs and timed traffic lights
-- Players can now select elevated rail segments/nodes
-- Trams and trains now follow priority signs
-- Improved UI behavior when setting up priority signs
-
-1.6.17, 20/04/2016
-- Hotfix for reported path-finding problems
-
-1.6.16, 19/04/2016
-- Updated for game patch 1.4.1-f2
-
-1.6.15, 22/03/2016
-- Updated for game path 1.4.0-f3
-- Possible fix for crashes described by @cosminel1982
-- Added traditional Chinese translation
-
-1.6.14, 17/03/2016
-- Bugfix: Cargo trucks did not obey vehicle restrictions (thanks to @ad.vissers for pointing out this problem)
-- Bugfix: When Advanced AI was deactivated, u-turns did not have costs assigned
-
-1.6.13, 16/03/2016
-- Added Dutch translation
-- The pedestrian light mode of a traffic light can now be switched back to automatic
-- Vehicles approaching a different speed limit change their speed more gradually
-- The size of signs and symbols in the overlay is determined by screen resolution height, not by width
-- Path-finding: Performance improvements
-- Path-finding: Fine-tuned lane changing behaviour
-- Bugfix: After loading another savegame, timed traffic lights stopped working for a certain time
-- Bugfix: Lane speed calculation corrected
-
-1.6.12, 03/03/2016
-- Improved memory usage
-- Bugfix: Adding/removing junctions to/from existing timed traffic lights did not work (thanks to @nieksen for pointing out this problem)
-- Bugfix: Separate timed traffic lights were sometimes not saved (thanks to @nieksen for pointing out this problem)
-- Bugfix: Fixed an initialization error (thanks to @GordonDry for pointing out this problem)
-
-1.6.11, 03/03/2016
-- Added Chinese translation
-- By pressing "Page up"/"Page down" you can now switch between traffic and default map view
-- Size of information icons and signs is now based on your screen resolution
-- UI code refactored
-
-1.6.10, 02/03/2016
-- Additional controls for vehicle restrictions added
-- Bugfix: Clicking on a Traffic Manager overlay resulted in vanilla game components (e.g. houses, vehicles) being activated
-
-1.6.9, 02/03/2016
-- Updated for game patch 1.3.2-f1
-
-1.6.8, 01/03/2016
-- Path-finding: Major performance improvements
-- Updated Japanese translation (thanks to @Akira Ishizaki for translating!)
-- Added Spanish translation
-
-1.6.7, 27/02/2016
-- Tuned AI parameters
-- Improved traffic density measurements
-- Improved lane changing near junctions: Reintroduced costs for lane changing before junctions
-- Improved vehicle behavior near blocked roads (e.g. while a building is burning)
-- Bugfix: Automatic pedestrian lights for outgoing one-ways fixed
-- Bugfix: U-turns did not have appropriate costs assigned
-- Bugfix: The time span between AI traffic measurements was too high
-
-1.6.6, 27/02/2016
-- It should now be easier to select segment ends in order to change lane arrows.
-- Priority signs now cannot be setup at outgoing one-ways.
-- Updated French translation (thanks to @simon.royer007 for translating!)
-- Updated Polish translation (thanks to @Krzychu1245 for translating!)
-- Updated Portuguese translation (thanks to @igordeeoliveira for translating!)
-- Updated Russian translation (thanks to @FireGames for translating!)
-- Bugfix: U-turning vehicles were not obeying the correct directional traffic light (thanks to @t1a2l for pointing out this problem)
-
-1.6.5, 24/02/2016
-- Added despawning setting to options dialog
-- Improved detection of Traffic++ V2
-
-1.6.4, 23/02/2016
-- Minor performance improvements
-- Bugfix: Path-finding calculated erroneous traffic density values
-- Bugfix: Cims left the bus just to hop on a bus of the same line again (thanks to @kamzik911 for pointing out this problem)
-- Bugfix: Despawn control did not work (thanks to @xXHistoricalxDemoXx for pointing out this problem)
-- Bugfix: State of new settings was not displayed corretly (thanks to @Lord_Assaultーさま for pointing out this problem)
-- Bugfix: Default settings for vehicle restrictions on bus lanes corrected
-- Bugfix: Pedestrian lights at railway junctions fixed (they are still invisible but are derived from the car traffic light state automatically)
-
-1.6.3, 22/02/2016
-- Bugfix: Using the "Old Town" policy led to vehicles not spawning.
-- Bugfix: Planes, cargo trains and ship were sometimes not arriving
-- Bugfix: Trams are not doing u-turns anymore
-
-1.6.2, 20/02/2016
-- Trams are now obeying speed limits (thanks to @Clausewitz for pointing out the issue)
-- Bugfix: Clear traffic sometimes throwed an error
-- Bugfix: Vehicle restrctions did not work as expected (thanks to @[Delta ²k5] for pointing out this problem)
-- Bugfix: Transition of automatic pedestrian lights fixed
-
-1.6.1, 20/02/2016
-- Improved performance
-- Bugfix: Fixed UI issues
-- Modifying mod options through the main menu now gives an annoying warning message instead of a blank page.
-
-1.6.0, 18/02/2016
-- New feature: Separate traffic lights for different vehicle types
-- New feature: Vehicle restrictions
-- Snowfall compatibility
-- Better handling of vehicle bans
-- Improved the method for calculating lane traffic densities
-- Ambulances, fire trucks and police cars on duty are now ignoring lane arrows
-- Timed traffic lights may now be setup at arbitrary nodes on railway tracks
-- Reckless drivers now do not enter railroad crossings if the barrier is down
-- Option dialog is disabled if accessed through the main menu
-- Performance optimizations
-- Advanced Vehicle AI: Improved lane spreading
-- The option "Vehicles may enter blocked junctions" may now be defined for each junction separately
-- Vehicles going straight may now change lanes at junctions
-- Vehicles may now perform u-turns at junctions that have an appropriate lane arrow configuration
-- Road conditions (snow, maintenance state) may now have a higher impact on vehicle speed (see "Options" menu)
-- Emergency vehicles on duty now always aim for the the fastest route
-- Bugfix: Path-finding costs for crossing a junction fixed
-- Bugfix: Vehicle detection at timed traffic lights did not work as expected
-- Bugfix: Not all valid traffic light arrow modes were reachable
-
-1.5.2, 01/02/2016
-- Traffic lights may now be added to/removed from underground junctions
-- Traffic lights may now be setup at *some* points of railway tracks (there seems to be a game-internal bug that prevents selecting arbitrary railway nodes)
-- Display of priority signs, speed limits and timed traffic lights may now be toggled via the options dialog
-- Bugfix: Reckless driving does not apply for trains (thanks to @GordonDry for pointing out this problem)
-- Bugfix: Manual traffic lights were not working (thanks to @Mas71 for pointing out this problem)
-- Bugfix: Pedestrians were ignoring timed traffic lights (thanks to @Hannes8910 for pointing out this problem)
-- Bugfix: Sometimes speed limits were not saved (thanks to @cca_mikeman for pointing out this problem)
-
-1.5.1, 31/01/2016
-- Trains are now following speed limits
-
-1.5.0, 30/01/2016
-- New feature: Speed restrictions (as requested by @Gfurst)
-- AI: Parameters tuned
-- Code improvements
-- Lane arrow changer window is now positioned near the edited junction (as requested by @GordonDry)
-- Bugfix: Flowing/Waiting vehicles count corrected
-
-1.4.9, 27/01/2016
-- Junctions may now be added to/removed from timed traffic lights after they are created
-- When viewing/moving a timed step, the displayed/moved step is now highlighted (thanks to Joe for this idea)
-- Performance improvements
-- Bugfix (AI): Fixed a division by zero error (thanks to @GordonDry for pointing out this problem)
-- Bugfix (AI): Near highway exits vehicles tended to use the outermost lane (thanks to @Zake for pointing out this problem)
-- Bugfix: Some lane arrows disappeared on maps using left-hand traffic systems (thanks to @Mas71 for pointing out this problem)
-- Bugfix: In lane arrow edit mode, the order of arrows was sometimes incorrect (thanks to @Glowstrontium for pointing out this problem)
-- Bugfix: Lane merging in left-hand traffic systems fixed
-- Bugfix: Turning priority roads fixed (thanks to @GordonDry for pointing out this problem)
-
-1.4.8, 25/01/2016
-- AI: Parameters have been tuned
-- AI: Added traffic density measurements
-- Performance improvements
-- Added translation to Polish (thanks to @Krzychu1245 for working on this!)
-- Added translation to Russian (thanks to @FireGames for working on this!)
-- Bugfix: After removing a timed or manual light the traffic light was deleted (thanks to @Mas71 for pointing out this problem)
-- Bugfix: Segment geometries were not always calculated
-- Bugfix: In highway rule mode, lane arrows sometimes flickered
-- Bugfix: Some traffic light arrows were sometimes not selectable
-
-1.4.7, 22/01/2016
-- Added translation to Portuguese (thanks to @igordeeoliveira for working on this!)
-- Reduced mean size of files can become quite big (thanks to @GordonDry for reporting this problem)
-- Bugfix: Freight ships/trains were not coming in (thanks to @Mas71 and @clus for reporting this problem)
-- Bugfix: The toggle "Vehicles may enter blocked junctions" did not work properly (thanks for @exxonic for reporting this problem)
-- Bugfix: If a timed traffic light is being edited the segment geometry information is not updated (thanks to @GordonDry for reporting this problem)
-
-1.4.6, 22/01/2016
-- Running average lane speeds are measured now
-- Minor fixes
-
-1.4.5, 22/01/2016
-- The option "Vehicles may enter blocked junctions" may now be defined for each junction separately
-- Bugfix: A deadlock in the path-finding is fixed
-- Bugfix: Small timed light sensitivity values (< 0.1) were not saved correctly
-- Bugfix: Timed traffic lights were not working for some players
-- Refactored segment geometry calculation
-
-1.4.4, 21/01/2016
-- Added localization support
-
-1.4.3, 20/01/2016
-- Several performance improvements
-- Improved calculation of segment geometries
-- Improved load balancing
-- Police cars, ambulances, fire trucks and hearses are now also controlled by the AI
-- Bugfix: Vehicles did not always take the shortest path
-- Bugfix: Vehicles disappeared after deleting/upgrading a road segment
-- Bugfix: Fixed an error in path-finding cost calculation
-- Bugfix: Outgoing roads were treated as ingoing roads when highway rules were activated
-
-1.4.2, 16/01/2016
-- Several major performance improvements (thanks to @sci302 for pointing out those issues)
-- Improved the way traffic lights are saved/loaded
-- Lane-wise traffic density is only measured if Advanced AI is activated
-- Bugfix: AI did not consider speed limits/road types during path calculation (thanks to @bhanhart, @sa62039 for pointing out this problem)
-- Connecting a city road to a highway road that does not supply enough lanes for merging leads to behavior people do not understand (see manual). Option added to disable highway rules.
-- Bugfix: Vehicles were stopping in front of green traffic lights
-- Bugfix: Stop/Yield signs were not working properly (thanks to @GordonDry, @Glowstrontium for pointing out this problem)
-- Bugfix: Cargo trucks were ignoring the "Heavy ban" policy, they should do now (thanks to @Scratch for pointing out this problem)
-
-1.4.1, 15/01/2016
-- Bugfix: Path-finding near junctions fixed
-
-1.4.0, 15/01/2016
-- Introducing Advanced Vehicle AI (disabled by default! Go to "Options" and enable it if you want to use it.)
-- Bugfix: Traffic lights were popping up in the middle of roads
-- Bugfix: Fixed the lane changer for left-hand traffic systems (thanks to @Phishie for pointing out this problem)
-- Bugfix: Traffic lights on invalid nodes are not saved anymore
-
-1.3.24, 13/01/2016
-- Improved handling of priority signs
-- Priority signs: After adding two main road signs the next offered sign is a yield sign
-- Priority signs: Vehicles now should notice earlier that they can enter a junction
-- Removed the legacy XML file save system
-- Invalid (not created) lanes are not saved/loaded anymore
-- Added a configuration option that allows vehicles to enter blocked junctions
-- Bugfix: Some priority signs were not saved
-- Bugfix: Priority signs on deleted segments are now deleted too
-- Bugfix: Lane arrows on removed lanes are now removed too
-- Bugfix: Adding a priority sign to a junction having more than one main sign creates a yield sign (thanks to @GordonDry for pointing out this problem)
-- Bugfix: If reckless driving was set to "The Holy City (0 %)", vehicles blocked intersections with traffic light.
-- Bugfix: Traffic light arrow modes were sometimes not correctly saved
-
-1.3.23, 09/01/2016
-- Bugfix: Corrected an issue where toggled traffic lights would not be saved/loaded correctly (thanks to @Jeffrios and @AOD_War_2g for pointing out this problem)
-- Option added to forget all toggled traffic lights
-
-1.3.22, 08/01/2016
-- Added an option allowing busses to ignore lane arrows
-- Added an option to display nodes and segments
-
-1.3.21, 06/01/2016
-- New feature: Traffic Sensitivity Tuning
-- UI improvements: When adding a new step to a timed traffic light the lights are inverted.
-- Timed traffic light status symbols should now be less annoying
-- Bugfix: Deletion of junctions that were members of a traffic light group is now handled correctly
-
-1.3.20, 04/01/2016
-- Bugfix: Timed traffic lights are not saved correctly after upgrading a road nearby
-- UI improvements
-- New feature: Reckless driving
-
-1.3.19, 04/01/2016
-- Timed traffic lights: Absolute minimum time changed to 1
-- Timed traffic lights: Velocity of vehicles is being measured to detect traffic jams
-- Improved traffic flow measurement
-- Improved path finding: Cims may now choose their lanes more independently
-- Bugfix: Upgrading a road resets the traffic light arrow mode
-
-1.3.18, 03/01/2016
-- Provided a fix for unconnected junctions caused by other mods
-- Crosswalk feature removed. If you need to add/remove crosswalks please use the "Crossings" mod.
-- UI improvements: You can now switch between activated timed traffic lights without clicking on the menu button again
-
-1.3.17, 03/01/2016
-- Bugfix: Timed traffic lights cannot be added again after removal, toggling traffic lights does not work (thanks to @Fabrice, @ChakyHH, @sensual.heathen for pointing out this problem)
-- Bugfix: After using the "Manual traffic lights" option, toggling lights does not work (thanks to @Timso113 for pointing out this problem)
-
-1.3.16, 03/01/2016
-- Bugfix: Traffic light settings on roads of the Network Extensions mods are not saved (thanks to @Scarface, @martintech and @Sonic for pointing out this problem)
-- Improved save data management
-
-1.3.15, 02/01/2016
-- Simulation accuracy (and thus performance) is now controllable through the game options dialog
-- Bugfix: Vehicles on a priority road sometimes stop without an obvious reason
-
-1.3.14, 01/01/2016
-- Improved performance
-- UI: Non-timed traffic lights are now automatically removed when adding priority signs to a junction
-- Adjusted the adaptive traffic light decision formula (vehicle lengths are considered now)
-- Traffic two road segments in front of a timed traffic light is being measured now
-
-1.3.13, 01/01/2016
-- Bugfix: Lane arrows are not correctly translated into path finding decisions (thanks to @bvoice360 for pointing out this problem)
-- Bugfix: Priority signs are sometimes undeletable (thank to @Blackwolf for pointing out this problem)
-- Bugfix: Errors occur when other mods without namespace definitions are loaded (thanks to @Arch Angel for pointing out this problem)
-- Connecting a new road segment to a junction that already has priority signs now allows modification of the new priority sign
-
-1.3.12, 30/12/2015
-- Bugfix: Priority signs are not editable (thanks to @ningcaohan for pointing out this problem)
-
-1.3.11, 30/12/2015
-- Road segments next to a timed traffic light may now be deleted/upgraded/added without leading to deletion of the light
-- Priority signs and Timed traffic light state symbols are now visible as soon as the menu is opened
-
-1.3.10, 29/12/2015
-- Fixed an issue where timed traffic light groups were not deleted after deleting an adjacent segment
-
-1.3.9, 29/12/2015
-- Introduced information icons for timed traffic lights
-- Mod is now compatible with "Improved AI" (Lane changer is deactivated if "Improved AI" is active)
-
-1.3.8, 29/12/2015
-- Articulated busses are now simulated correctly (thanks to @nieksen for pointing out this problem)
-- UI improvements
-
-1.3.7, 28/12/2015
-- When setting up a new timed traffic light, yellow lights from the real-world state are not taken over
-- When loading another save game via the escape menu, Traffic Manager does not crash
-- When loading another save game via the escape menu, Traffic++ detection works as intended
-- Lane arrows are saved correctly
-
-1.3.6, 28/12/2015
-- Bugfix: wrong flow value taken when comparing flowing vehicles
-- Forced node rendering after modifying a crosswalk
-
-1.3.5, 28/12/2015
-- Fixed pedestrian traffic Lights (thanks to @Glowstrontium for pointing out this problem)
-- Better fix for: Deleting a segment with a timed traffic light does not cause a NullReferenceException
-- Adjusted the comparison between flowing (green light) and waiting (red light) traffic
-
-1.3.4, 27/12/2015
-- Better traffic jam handling
-
-1.3.3, 27/12/2015
-- (Temporary) hotfix: Deleting a segment with a timed traffic light does not cause a NullReferenceException
-- If priority signs are located behind the camera they are not rendered anymore
-
-1.3.2, 27/12/2015
-- Priority signs are persistently visible when Traffic Manager is in "Add priority sign" mode
-- Synchronized traffic light rendering: In-game Traffic lights display the correct color (Thanks to @Fabrice for pointing out this problem)
-- Traffic lights switch between green, yellow and red. Not only between green and red.
-- UI tool tips are more explanatory and are shown longer.
-
-1.3.1, 26/12/2015
-- Minimum time units may be zero now
-- Timed traffic lights of deleted/modified junctions get properly disposed
-
-1.3.0, 25/12/2015
-- **Adaptive Timed Traffic Lights** (automatically adjusted based on traffic amount)
-
-1.2.0 (iMarbot)
-- Updated for 1.2.2-f2 game patch.
+- Fixed: Parking AI: Transport mode storage causes performance issues during loading (#147, thanks to hannebambel002 and oneeyets for reporting and further for providing logs and savegames)
+
+### TM:PE 1.10.6, 24/05/2018
+
+- Added: Lane connector can be used on monorail tracks
+- Added: Mod option - Main menu size can be controlled
+- Added: Mod option - GUI and overlay transparency can be controlled
+- Added: Mod option - Penalties for switching between different public transport lines can be toggled
+- Added: Cims can now be removed from the game
+- Improved: Advanced Vehicle AI - Tuned parameters
+- Improved: Randomization for realistic speeds such that vehicles may change their target velocity over time
+- Improved: Vehicle position tracking
+- Improved: Mod compatibility checks
+- Improved: Parking AI - Improved behaviour in situations where vehicles are parked near public transport hubs and road connections are partially unavailable
+- Improved: Window design
+- Fixed: Parking AI - Not all possible paths are regarded during path-finding
+- Fixed: Parking AI - Cims become confused when trying to return their abandoned car back home (special thanks to Wildcard-25 for reporting and solving this issue)
+- Fixed: Parking AI - Cims do not search for parking building when road-side parking spaces are found
+- Fixed: Parking AI - Parked vehicles are spawned near the source building even when cims are already en route
+- Fixed: Parking AI - Cims sometimes get stuck in an infinite loop while trying to enter their parked car
+- Fixed: Lane connector does not work for roads with more than ten lanes
+- Fixed: Allowing/Disallowing vehicles to enter a blocked junction does not work for certain junctions
+- Updated: Compatible with C:SL 1.9.2-f1
+- Updated: Compatible with C:SL 1.9.3-f1
+- Updated: Compatible with C:SL 1.10.0-f3
+- Updated: Dynamic Lane Selection: Absolute speed measurements are used instead of relative measurements
+- Updated: Service vehicles now allowed to ignore lane arrows when leaving their source building; better for dead-end roads with median
+- Updated: Korean translation (thanks to Toothless FLY [ROK]LSh.st for translating)
+
+### C:SL 1.10.0-f3 (Park Life), 24/05/2018
+
+- Added: Park maintenance service and vehicle
+- Added: Walking tours
+- Added: Sightseeing bus tours and depot
+- Added: Hot air balloons
+- Fixed: Confused ships rotating forever
+- Fixed: All traffic use Bus & Taxi lane when Old Town policy is active
+- Fixed: Lots of other game and DLC bugs
+- Updated: Trees reduce noise pollution
+
+### C:SL 1.9.3-f1, 23/03/2018
+
+- Fixed: Bugs caused by prior patch
+
+### C:SL 1.9.2-f1, 09/03/2018
+
+- Fixed: Minor bugs
+
+### TM:PE 1.10.5, 06/01/2018
+
+- Added: Randomization for lane changing costs
+- Added: Randomization for "trucks prefer innermost lanes on highways" costs
+- Added: path-finding costs for public transport transitions
+- Fixed: Main menu button might be out of view
+- Fixed: Division by zero occurs for low speed roads
+- Fixed: Automatic pedestrian lights at railroad do not work as expected
+- Fixed: Timed traffic lights show up for bicycles (they should not)
+- Fixed: Due to a multi-threading issue junction restrictions may cause the game state to become inconsistent
+- Fixed: Routing rules prevents vehicles from spawning when starting building lies too close to an intersection/road end
+- Fixed: Disabling tutorial message has no effect
+- Fixed: "Stay on lane" feature does not work as intended for certain nodes
+- Updated: Compatible with C:SL 1.9.1
+- Updated: Busses are allowed to switch multiple lanes after leaving a bus stop
+- Updated: Pedestrian traffic lights do not show up if crossing the street is prohibited
+- Updated: Simplified Chinese translation updated (thanks to Emphasia for translating)
+- Updated: Polish translation updated (thanks to Krzychu1245 for translating)
+- Removed: Unnecessary calculations in path-finding
+- Removed: UI scaling
+
+### C:SL 1.9.1, 05/12/2017
+
+- Fixed: Various game bugs
+- Updated: Updated Unity to 5.6.4p2
+
+### TM:PE 1.10.4, 19/10/2017
+
+- Added: Possibility to add priority signs at multiple junctions at once (press Shift)
+- Added: Tutorials (can be disabled in the options window globally)
+- Updated: Compatible with C:SL 1.9.0-f5
+
+### C:SL 1.9.0-f5 (Green Cities), 19/10/2017
+
+- Added: Biofuel busses and recycling trucks
+- Added: Electric cars and parking spaces with chargers
+- Fixed: Huge number of game and localisation bugs
+- Updated: Noise Pollution overhaul
+- Updated: Train track intersection rules
+- Updated: Unity version has been updated to 5.6.3p4
+
+### TM:PE 1.10.3, 18/08/2017
+
+- Fixed: Setting unlimited speed limit causes vehicles to crawl at low speed (thanks to sethisuwan for reporting this issue)
+- Fixed: Vehicle-separated traffic lights do not show up for trams & monorails (thanks to thecitiesdork for reporting this issue)
+
+### TM:PE 1.10.2, 17/08/2017
+
+- Improved: performance
+- Fixed: Pedestrians sometimes ignore red traffic light signals (thanks to (c)RIKUPI™ for reporting this issue)
+- Fixed: Timed traffic lights do not correctly recognize set vehicle restrictions (thanks to alborzka for reporting this issue)
+- Updated: Compatible with C:SL 1.8.0-f3
+
+### C:SL 1.8.0-f3 (Concerts), 17/08/2017
+
+- Added: Festival areas
+- Fixed: Pathfinder causing problems with multiple policies
+- Fixed: Traffic routes info view does not show bicycles for players who don't own After Dark
+- Fixed: Ships can travel on land and through dam
+- Fixed: Cargo Train Terminal not working when build next to road with bicycle lanes
+- Fixed: Large number of other game bugs
+
+### TM:PE 1.10.1, 05/08/2017
+
+- Fixed: Default routing is disabled if the lane connector is used on a subset of all available lanes only
+- Fixed: Parking AI cannot be enabled/disabled
+- Fixed: Lane connection points can connected to themselves
+- Updated: Polish, Korean, and Simplified Chinese translations
+
+### TM:PE 1.10.0, 30/07/2017
+
+- Added: Dynamic Lane Selection
+- Added: Adaptive step switching
+- Added: Individual vehicles may be removed from the game
+- Added: Mod option - Vehicle restrictions aggression
+- Added: Mod option - Vehicles follow priority rules at junctions with timed traffic lights
+- Added: Path-find statistics label
+- Added: Confirmation dialog for "Clear Traffic" button
+- Improved: Path-finding performance
+- Improved: Traffic measurement engine performance
+- Improved: Currently active timed traffic light step is remembered
+- Improved: Disabling the Parking AI triggers graceful clean up procedure
+- Improved: Vehicle state tracking
+- Improved: Parking AI - Vehicles can now find parking spaces at the opposite road side
+- Improved: Parking AI - Included an improved fallback logic for some edge cases
+- Improved: Parking AI - Citizens should now be more successful in returning their cars back home
+- Improved: Parking AI - Tuned parking radius parameters
+- Improved: Parking AI - If the limit for parked vehicles is reached and parking fails due to it, no alternative parking space is queried
+- Improved: Vehicle AI - Busses prefer lanes with correct lane arrow over incorrect ones
+- Fixed: Workaround for a base game issue that causes trams to get stuck
+- Fixed: Using the bulldozer tool might lead to inconsistent road geometry information
+- Fixed: Citizens that fail to approach their parked car fly towards their target building
+- Fixed: Parking AI: Path-finding fails if cars are parked too far away from a road
+- Fixed: Parking AI: Citizens approaching a car start to float away
+- Fixed: "Heavy vehicles prefer outer lanes on highways" does not work
+- Fixed: The lane connector does not allow connecting all available lane end points at train stations and on bidirectional one-lane train tracks
+- Fixed: Vehicles may get stuck in several situations
+- Updated: Compatible with C:SL 1.7.2-f1
+- Updated: Upgrading to a road with bus lanes now copies an already existing traffic light state to the new traffic light
+- Updated: Adding a vehicle separate light to a timed traffic lights applies the main light configuration
+- Updated: Vehicles use queue skipping to prioritize path-finding runs that are caused by road modifications
+- Updated: Trains do not longer stop in front of green timed traffic lights
+- Updated: Relocated some mod options
+- Updated: It is now possible to connect train station tracks and outside connections with the lane connector
+- Updated: Trains do not wait for each other anymore near timed traffic lights
+- Updated: The option "Road condition has a bigger impact on vehicle speed" is only shown if the Snowfall DLC is owned
+- Updated: Reorganized global configuration file (sorry, your main menu and main button positions are reset)
+- Updated: The flow/wait calculation mode to be used is now configurable via the global configuration file
+
+### C:SL 1.7.2-f1, 01/06/2017
+
+- Fixed: Multiple public transport stops at the same location causing division by zero / crashing the UI
+- Fixed: Double clicking creating multiple stops at the same place
+- Fixed: Various other bugs
+
+### TM:PE 1.9.6, 28/05/2017
+
+- Fixed: Vehicles cannot perform u-turns at junctions with only one outgoing segment (thanks to Sunbird for reporting this issue)
+- Fixed: Path-finding costs for large distances exceed the maximum allowed value (thanks to Huitsi for reporting this issue)
+- Fixed: Under certain circumstances path-finding at railroad crossings allow switching from road to rail tracks.
+- Updated: Simplified Chinese translation
+
+### TM:PE 1.9.5, 24/05/2017
+
+- Improved: Language can now be switched without requiring a game restart
+- Fixed: Routing calculation does not work as expected for one-way roads with tram tracks (thanks to bigblade66, Battelman2 and AS_ for reporting and providing extensive information)
+- Fixed: Copying timed traffic lights causes timed traffic lights to be omitted during the save process (thanks to jakeroot and t1a2l for reporting this issue)
+- Fixed: In certain situations unnecessary vehicle-separate traffic lights are being created
+- Fixed: Upgrading a train track segment next to a timed traffic light causes trains to ignore the traffic light
+- Fixed: Hotfix - Cable cars despawn at end-of-line stations
+- Updated: Compatible with C:SL 1.7.1-f1
+- Updated: Polish, Korean and Italian translation
+
+### TM:PE 1.9.4, 23/05/2017
+
+- Added: Mod option - Ban private cars and trucks on bus lanes
+- Improved: Optimized path-finding
+- Fixed: Path-finding is unable to calculate certain paths after modifying the road network
+- Updated: Increased path-finding cost for private cars driving on bus lanes
+- Updated: Increased path-finding cost for disregarding vehicle restrictions
+- Updated: Spanish and French translation
+
+### C:SL 1.7.1-f1, 23/05/2017
+
+- Fixed: Minor bugs
+
+### TM:PE 1.9.3, 22/05/2017
+
+- Improved: Modified junction restrictions come into effect instantaneously
+- Fixed: AI: Segment traffic data is not taken into account
+- Fixed: Priority rules are not properly obeyed
+- Fixed: Under certain circumstances priority signs cannot be removed
+- Fixed: Path-finding is unable to calculate certain paths
+- Updated: UI - Saving a timed step does not reset the timed traffic light to the first state
+- Removed: Default vehicle restrictions from bus lanes
+- Removed: Disabled notification of route recalculating because some players report crashes
+
+### TM:PE 1.9.2, 20/05/2017
+
+- Improved: UI - Main menu & UI tools performance improved
+- Fixed: Traffic lights can be removed from junctions that are controlled by a timed traffic light program
+
+### TM:PE 1.9.1, 19/05/2017
+
+- Fixed: Using the vanilla traffic light toggling feature crashes the game if TMPE's main menu has not been opened at least once
+- Fixed: AI - More car traffic and less public transportation present than in vanilla
+- Updated: French, Dutch and Korean translation
+
+### TM:PE 1.9.0, 18/05/2017
+
+- Added: Parking restrictions
+- Added: Speed limits can be set up for individual lanes with the Control key
+- Added: Added timed traffic light and speed limit support for monorails
+- Added: Copy & paste for individual timed traffic lights
+- Added: Rotate individual timed traffic lights
+- Added: Lane customizations may come into effect instantaneously
+- Added: Mod option - Main button position can be locked
+- Added: Mod option - Main menu position can be locked
+- Added: Mod option - Added language selection in options dialog
+- Added: Mod option - Customization of lane arrows, lane connections and vehicle restrictions can now come into effect instantaneously
+- Added: Support for custom languages
+- Added: Korean translation (thanks to Toothless FLY [ROK]LSh.st for translating)
+- Improved: Performance improvements
+- Improved: Advanced Vehicle AI - Algorithm updated, performance improved - Possible routing decisions are now being pre-calculated
+- Improved: AI - Tuned path-finding parameters
+- Fixed: Cars sometimes get stuck forever when the Advanced Parking AI is activated (thanks to cmfcmf for reporting this issue)
+- Fixed: Busses do not perform u-turns even if the transport line show u-turns (thanks to dymanoid for reporting this issue)
+- Fixed: Timed traffic lights do not work as expected on single-direction train tracks (thanks to DaEgi01 for reporting this issue)
+- Fixed: Vehicle restriction and speed limit signs overlay is displayed on the wrong side of inverted road segments
+- Fixed: Influx statistics value is zero (thanks to hjo for reporting this issue)
+- Updated: Compatible with C:SL 1.7.0-f5
+- Updated: Major code refactorings
+- Updated: UI - More compact, movable main menu UI
+- Updated: translations: German, Polish, Russian, Portuguese, Traditional Chinese
+- Updated: Path-finding cost multiplicator for vehicle restrictions is now configurable in TMPE_GlobalConfig.xml
+- Updated: Unified traffic light toggling feature with game code
+- Updated: Reworked the way that traffic measurements are performed
+
+### C:SL 1.7.0-f5 (Mass Transit), 18/05/2017
+
+- Added: Ferries
+- Added: Cable Cars
+- Added: Elevated monorail
+- Added: Blimps
+- Added: New stations, transport hubs and service buildings
+- Added: Named routes
+- Added: One-way train tracks
+- Added: Emergency vehicles choose free lane if available, otherwise lane with least traffic
+- Added: More public transport info views
+- Added: Choose if rail stations accept intercity traffic
+- Added: Stop signs at intersections
+- Added: Toggle traffic lights at intersections
+- Added: Vehicles have show / hide routes button
+- Added: Automatic public transport vehicle unbunching
+- Fixed: Train tracks could be updated after disaster by upgrading
+- Fixed: Helicopters still won't visit buildings with no road connection
+- Fixed: Lots of errors, particularly localisation
+- Updated: Increased emergency vehicles speed
+- Updated: Smaller outside connection capacity for smaller roads
+- Updated: Upgraded to Unity 5.5.3f1
+
+### TM:PE 1.8.16, 20/03/2017
+
+- Improved: lane selection for busses if the option "Busses may ignore lane arrows" is activated
+- Fixed: The game sometimes freezes when using the timed traffic light tool
+- Fixed: Lane connections are not correctly removed after modifying/removing a junction
+- Fixed: Selecting a junction for setting up junction restrictions toggles the currently hovered junction restriction icon
+- Updated: Lane connections can now also be removed by pressing the backspace key
+
+### TM:PE 1.8.15, 27/01/2017
+
+- Updated: Compatible with C:SL 1.6.3-f1
+
+### C:SL 1.6.3-f1, 26/01/2017
+
+- Fixed: Helicopter not used if building has no road connection
+- Fixed: Various other game bugs
+
+### TM:PE 1.8.14, 07/01/2017
+
+- Added: Tram lanes can now be customized by using the lane connector tool
+- Improved: Minor performance optimizations for priority sign simulation
+- Fixed: Wait/flow ratio at timed traffic lights is sometimes not correctly calculated
+- Fixed: A deadlock situation can arise at junctions with priority signs such that no vehicle enters the junction
+- Fixed: When adding a junction to a timed traffic light, sometimes light states given by user input are not correctly stored
+- Fixed: Joining two timed traffic lights sets the minimum time to "1" for steps with zero minimum time assigned
+- Fixed: Modifications of timed traffic light states are sometimes not visible while editing the light (but they are applied nonetheless)
+- Fixed: Button background is not always correctly changed after clicking on a button within the main menu
+
+### TM:PE 1.8.13, 05/01/2017
+
+- Improved: Selection of overlay markers on underground roads (thanks to Padi for reminding me of that issue)
+- Improved: Minor performance improvements
+- Fixed: Timed traffic light data can become corrupt when upgrading a road segment next to a traffic light, leading to faulty UI behaviour (thanks to Brain for reporting this issue)
+- Fixed: The position of the main menu button resets after switching to the free camera mode (thanks to Impact and gravage for reporting this issue)
+- Fixed: A division by zero exception can occur when calculating the average number of waiting/floating vehicles
+
+### TM:PE 1.8.12, 02/01/2017
+
+- Fixed: After leaving the "Manual traffic lights" mode the traffic light simulation is not cleaned up correctly (thanks to diezelunderwood for reporting this issue)
+- Fixed: Insufficient access rights to log file causes the mod to crash
+- Updated: Compatible with C:SL 1.6.2-f1
+
+### TM:PE 1.8.11, 02/01/2017
+
+- Fixed: Speed limits for elevated/underground road segments are sometimes not correctly loaded (thanks to Pirazel and [P.A.N] Uf0 for reporting this issue)
+
+### TM:PE 1.8.10, 31/12/2016
+
+- Improved: Path-finding performance (a bit)
+- Fixed: Check for invalid road thumbnails in the "custom default speed limits" dialog
+
+### TM:PE 1.8.9, 29/12/2016
+
+- Added: It is now possible to set speed limits for metro tracks
+- Added: Custom default speed limits may now be defined for train and metro tracks
+- Improved: Customizable junctions are now highlighted by the lane connector tool
+- Improved: UI behaviour
+- Improved: Performance improvements
+- Fixed: Selecting a junction to set up priority signs sometimes does not work (thanks to Artemis *Seven* for reporting this issue)
+- Fixed: Automatic pedestrian lights do not work as expected at junctions with incoming one-ways and on left-hand traffic maps
+- Updated: Junction restrictions may now be controlled at bend road segments
+
+### TM:PE 1.8.8, 25/12/2016
+
+- Fixed: Taxis are not being used
+- Fixed: Prohibiting u-turns with the junction restriction tool does not work (thanks to Kisoe for reporting this issue)
+- Fixed: Cars are sometimes floating across the map while trying to park (thanks to [Delta ²k5] for reporting this issue)
+
+### TM:PE 1.8.7, 24/12/2016
+
+- Added: Italian translation (thanks to Admix for translating)
+- Improved: Advanced AI: Improved lane selection
+- Improved: Overall user interface performance
+- Improved: Overlay behaviour
+- Improved: Traffic measurement
+- Improved: Auto pedestrian lights at timed traffic lights behave more intelligently now
+- Fixed: Parking AI - Cims that try to reach their parked car are sometimes teleported to another location where they start to fly through the map in order to reach their car
+- Fixed: Parking AI - Cims owning a parked car do not consider using other means of transportation
+- Fixed: Parking AI - Residents are unable to leave the city through a highway outside connection
+- Fixed: Trains/Trams are sometimes not detected at timed traffic lights
+- Updated: Compatible with C:SL 1.6.2-f1
+- Updated: The position of the main menu button is now forced inside screen bounds on startup
+- Updated: A timed traffic light step with zero minimum time assigned can now be skipped automatically
+- Updated: Using the lane connector to create a u-turn now automatically enables the "u-turn allowed" junction restriction
+- Updated: French translation (thanks to simon.royer007 for translating)
+
+### C:SL 1.6.2-f1, 21/12/2016
+
+- Fixed: Various errors in game
+
+### TM:PE 1.8.6, 12/12/2016
+
+- Added: Korean language (thanks to Toothless FLY [ROK]LSh.st for translating)
+- Updated: Chinese language code (zh-cn -> zh) in order to make it compatible with the game (thanks to Lost丶青柠 for reporting this issue)
+
+### TM:PE 1.8.5, 11/12/2016
+
+- Fixed: Average speed limits are not correctly calculated for road segments with bicycle lanes (thanks to Toothless FLY [ROK]LSh.st for reporting this issue)
+- Removed: "Evacuation busses may only be used to reach a shelter" (CO fixed this issue)
+- Updated: Compatible with C:SL 1.6.1-f2
+
+### TM:PE 1.8.4, 11/12/2016
+
+- Added: "Stay on lane" - Press Shift + S in the Lane Connector tool can cycle through lane directions.
+- Fixed: Bicycles cannot change from bicycle lanes to pedestrian lanes
+- Fixed: Travel probabilities set in the "Citizen Lifecycle Rebalance v2.1" mod are not obeyed (thanks to informmanuel, shaundoddmusic for reporting this issue)
+- Fixed: Number of tourists seems to drop when activating the mod (statistics were not updated, thanks to hpp7117, wjrohn for reporting this issue)
+- Fixed: When loading a second savegame a second main menu button is displayed (thanks to Cpt. Whitepaw for reporting this issue)
+- Fixed: While path-finding is in progress vehicles do "bungee-jumping" on the current segment (thanks to mxolsenx, Howzitworld for reporting this issue)
+- Fixed: Cims leaving the city search for parking spaces near the outside connection which is obviously not required
+- Updated: U-turns are now only allowed to be performed from the innermost lane
+- Updated: TMPE now detects if the number of spawned vehicles is reaching its limit (16384). If so, spawning of service/emergency vehicles is prioritized over spawning other vehicles.
+
+### C:SL 1.6.1-f2, 11/12/2016
+
+- Added: Missing service enumerators to modding API
+- Fixed: Relocating emergency shelter breaks evacuation route
+- Fixed: Citizens using evacuation routes like a bus route
+- Fixed: Lots of other bug fixes to game and Disasters DLC
+- Updated: Chinese localisation added
+
+### TM:PE 1.8.3, 4/12/2016
+
+- Improved: Tweaked u-turn behaviour
+- Improved: Info views
+- Fixed: Despite having the Parking AI activated, cims sometimes still spawn pocket cars.
+- Fixed: When the Parking AI is active, bicycle lanes are not used (thanks to informmanuel for reporting this issue)
+
+### TM:PE 1.8.2, 3/12/2016
+
+- Fixed: Taxis were not used (thanks to [Delta ²k5] for reporting)
+- Fixed: Minor UI fix in Default speed limits dialog
+
+### TM:PE 1.8.1, 1/12/2016
+
+- Fixed: Mod crashed when loading a second savegame
+- Updated: translations: Polish, Chinese (simplified)
+
+### TPP2 2.0.12, 10/06/2016
+
+- Updated: Compatible with C:SL 1.6.0-f4
+- Meta: This was the final release of TPP2
+- Meta: TM:PE continued as the main traffic mod for the game
+- Meta: The TPP/TPP2 can still be found in the Network Extensions 2 project
+
+### TM:PE 1.8.0, 29/11/2016
+
+- Added: Default speed limits
+- Added: Parking AI (replaces "Prohibit cims from spawning pocket cars")
+- Added: Main menu button is now moveable
+- Added: Mod option - Heavy vehicles prefer outer lanes on highways
+- Added: Mod option - Realistic speeds
+- Added: Mod option - Evacuation busses may ignore traffic rules (Natural Disasters DLC required)
+- Added: Mod option - Evacuation busses may only be used to reach a shelter (Natural Disasters DLC required)
+- Added: Traffic info view shows parking space demand if Parking AI is activated
+- Added: Public transport info view shows transport demand if Parking AI is activated
+- Added: Info texts for citizen and vehicle tool tips if Parking AI is activated
+- Improved: AI - Improved lane selection, especially on busy roads
+- Improved: AI - Improved mean lane speed measurement
+- Updated: Compatible with C:SL 1.6.0-f4
+- Updated: Extracted internal configuration to XML configuration file
+- Updated: Changed main menu button due to changes in the game's user interface
+- Updated: Translations for German, Portuguese, Russian, Dutch, Chinese (traditional)
+- Removed: Compatibility check for Traffic++ V2 due to excessive workload
+
+### C:SL 1.6.0-f4 (Natural Disasters), 29/11/2016
+
+- Added: Disasters
+- Added: Disaster Recovery Service (Van and Helicopter)
+- Added: Police / Fire / Ambulance Helicopters
+- Added: Pumping Service & Trucks
+- Added: Emergency Shelter & Evacuation Bus
+- Added: Additional policies (eg. Helicopter Priority)
+- Added: Ability for roads to be destroyed by disasters
+
+### TM:PE 1.7.15, 26/10/2016
+
+- Fixed: Timed traffic lights window disappears when clicking on it with the middle mouse button (thanks to Nexus and Mariobro14 for helping me identifying the cause of this bug)
+
+### TM:PE 1.7.14, 18/10/2016
+
+- Updated: Compatible with C:SL 1.5.2-f3
+
+### TM:PE 1.7.13, 15/09/2016
+
+- Added: Button to reset stuck vehicles/cims (see mod settings menu)
+- Fixed: Implemented a permanent fix to solve problems with stuck vehicles/cims caused by third party mods
+- Fixed: AI: Lane merging was not working as expected
+- Fixed: Pedestrian light states were sometimes not being stored correctly (thanks to Filip for pointing out this problem)
+- Updated: AI - Improved lane selection algorithm
+
+### TM:PE 1.7.12, 09/09/2016
+
+- Fixed: Timed traffic lights should now correctly detect trains and trams
+- Fixed: GUI: Junction restriction icons sometimes disappear
+- Updated: AI - Lane changes are reduced on congested road segments
+- Updated: Chinese (simplified) translation
+
+### TM:PE 1.7.11, 01/09/2016
+
+- Updated: Compatible with C:SL 1.5.1-f3
+
+### TM:PE 1.7.10, 31/08/2016
+
+- Added: Players can now disable spawning of pocket cars
+- Fixed: Timed traffic lights were flickering
+- Fixed: Pedestrian traffic lights were not working as expected
+- Fixed: When upgrading/removing/adding a road segment, nearby junction restrictions were removed
+- Fixed: Setting up vehicle restrictions affects trams (thanks to chem for reporting)
+- Fixed: Manual pedestrian traffic light states were not correctly handled
+- Fixed: Junction restrictions overlay did not show all restricted junctions
+- Updated: Chinese (simplified) translation
+
+### TM:PE 1.7.9, 22/08/2016
+
+- Improved: Performance improvements
+- Fixed: In-game traffic light states are now correctly rendered when showing "yellow"
+- Fixed: GUI - Traffic light states do not flicker anymore
+- Removed: Negative effects on public transport usage
+
+### TM:PE 1.7.8, 18/08/2016:
+
+- Fixed: Cims sometimes got stuck (thanks to all reports and especially to Thilawyn for providing a savegame)
+- Improved: GUI - Better traffic light arrow display
+- Improved: Performance while saving
+
+### TM:PE 1.7.7, 16/08/2016:
+
+- Added: "110" speed limit
+- Improved: Performance while saving
+- Improved: GUI - Windows are draggable
+- Improved: GUI - Improved window scaling on lower resolutions
+- Updated: AI - Instead of walking long distances, citizens now use a car
+- Updated: AI - Citizens will remember their last used mode of transport (e.g. they will not drive to work and come return by bus anymore)
+- Updated: AI - Increased path-finding costs for traversing over restricted road segments
+
+### TM:PE 1.7.6, 14/08/2016:
+
+- Added: Players may now prohibit cims from crossing the street
+- Added: the possibility to connect train track lanes with the lane connector (as requested by pilot.patrick93)
+- Improved: UI - Clicking with the secondary mouse button now deselects the currently selected node/segment for all tools
+- Improved: AI - Tuned randomization of lane changing behaviour
+- Fixed: AI: At specific junctions, vehicles were not obeying lane connections correctly (thanks to Mariobro14 for pointing out this problem)
+- Fixed: AI: Path-finding costs for u-turns were not correctly calculated (thanks to Mariobro14 for pointing out this problem)
+- Fixed: Vehicles were endlessly waiting for each other at junctions with certain priority sign configurations
+- Fixed: AI: Lane changing costs corrected
+- Updated: AI - Introduced path-finding costs for leaving main highway (should reduce amount of detours taken)
+- Updated: Moved options from "Change lane arrows" to "Vehicle restrictions" tool
+- Updated: Russian translation
+
+### TM:PE 1.7.5, 07/08/2016:
+
+- Fixed: AI - Cims were using pocket cars whenever possible
+- Fixed: AI - Path-finding failures led to much less vehicles spawning
+- Fixed: AI - Lane selection at junctions with custom lane connection was not always working properly (e.g. for Network Extensions roads with middle lane)
+- Fixed: While editing a timed traffic light it could happen that the traffic light was deleted
+
+### TM:PE 1.7.4, 31/07/2016:
+
+- Added: French translations (thanks to simon.royer007 for translating!)
+- Improved: AI - Tuned new parameters
+- Improved: Various code improvements
+- Fixed: Activated/Disabled features were not loaded correctly
+- Fixed: AI - At specific junctions the lane changer did not work as intended
+- Fixed: Possible fix for OSX performance issues
+- Updated: AI - Switched from relative to absolute traffic density measurement
+
+### TM:PE 1.7.3, 29/07/2016:
+
+- Added: Ability to enable/disable mod features (e.g. for performance reasons)
+- Improved: Further code improvements
+- Fixed: Vehicle type determination was incorrect (fixed u-turning trams/trains, stuck vehicles)
+- Fixed: Clicking on a train/tram node with the lane connector tool led to an uncorrectable error (thanks to noaccount for reporting this problem)
+
+### TM:PE 1.7.2, 26/07/2016:
+
+- Improved: Optimized UI overlay performance
+
+### TM:PE 1.7.1, 24/07/2016:
+- Fixed: Trains were not despawning if no path could be calculated
+- Fixed: Workaround for third-party issue: TM:PE now detects if the calculation of total vehicle length fails
+- Removed: "Busses now may only ignore lane arrows if driving on a bus lane"
+
+### TM:PE 1.7.0, 23/07/2016:
+
+- Added: Traffic++ lane connector
+- Added: Compatibility detection for the Rainfall mod
+- Fixed: Busses now may only ignore lane arrows if driving on a bus lane
+- Improved: performance of priority sign rules
+- Improved: Better UI performance if overlays are deactivated
+- Improved: Better fault-tolerance of the load/save system
+- Fixed: Taxis were allowed to ignore lane arrows
+- Fixed: AI - Highway rules on left-hand traffic maps did not work the same as on right-hand traffic maps
+- Fixed: Upgrading a road segment next to a timed traffic light removed the traffic light leading to an inconsistent state (thanks to ad.vissers for pointing out this problem)
+- Updated: AI - Cims now ignore junctions where pedestrian lights never change to green
+- Updated: AI - Removed the need to define a lane changing probability
+- Updated: AI - Tweaked lane changing parameters
+- Updated: AI - Highway rules are automatically disabled at complex junctions (= more than 1 incoming and more than 1 outgoing roads)
+- Updated: Simulation accuracy now also controls time intervals between traffic measurements
+- Updated: Default wait-flow balance is set to 0.8
+- Updated: Rewritten and simplified vehicle position tracking near timed traffic lights and priority signs for performance reasons
+
+### TM:PE 1.6.22, 29/06/2016:
+
+- Fixed: Traffic measurement at timed traffic lights was incorrect
+- Updated: AI - Taxis now may not ignore lane arrows and are using bus lanes whenever possible (thanks to Cochy for pointing out this issue)
+- Updated: AI - Busses may only ignore lane arrows while driving on a bus lane
+
+### TM:PE 1.6.22, 21/06/2016:
+
+- Improved: Advanced Vehicle AI - Improved lane selection at junctions where bus lanes end
+- Improved: Advanced Vehicle AI - Improved lane selection of busses
+- Improved: Speed/vehicle restrictions may now be applied to all road segments between two junctions by holding the shift key
+- Improved: Automatic pedestrian lights
+- Improved: Separate traffic lights: Traffic lights now control traffic lane-wise
+- Fixed: Lane selection on maps with left-hand traffic was incorrect
+- Fixed: While building in pause mode, changes in the road network were not always recognized causing vehicles to stop/despawn
+- Fixed: Police cars off-duty were ignoring lane arrows
+- Fixed: If public transport stops were near a junction, trams/busses were not counted by timed traffic lights (many thanks to Filip for identifying this problem)
+- Fixed: Trains/Trams were sometimes ignoring timed traffic lights (many thanks to Filip for identifying this problem)
+- Fixed: Building roads with bus lanes caused garbage, bodies, etc. to pile up
+- Updated: Reworked how changes in the road network are recognized
+- Updated: Sensitivity slider is only available while adding/editing a step or while in test mode
+
+### TM:PE 1.6.21, 14/06/2016:
+
+- Fixed: Too few cargo trains were spawning (thanks to Scratch, toruk_makto1, Mr.Miyagi, mottoh and Syparo for pointing out this problem)
+- Fixed: Vehicle restrictions did not work as expected (thanks to nordlaser for pointing out this problem)
+
+### TM:PE 1.6.20, 11/06/2016:
+
+- Fixed: Priority signs were not working correctly (thanks to mottoth, Madgemade for pointing out this problem)
+
+### TM:PE 1.6.19, 11/06/2016
+
+- Fixed: Timed traffic lights UI not working as expected (thanks to Madgemade for pointing out this problem)
+
+### TPP2 2.0.11, 10/06/2016
+
+- Updated: Compatible with C:SL 1.5.0-f4
+
+### TM:PE 1.6.18, 09/06/2016
+
+- Improved: Players can now select elevated rail segments/nodes
+- Improved: Trams and trains now follow priority signs
+- Improved: performance of priority signs and timed traffic lights
+- Improved: UI behaviour when setting up priority signs
+- Updated: Compatible with C:SL 1.5.0-f4
+
+### C:SL 1.5.0-f4 (Match Day), 09/06/2016
+
+- Added: Football stadium (causes heavy traffic in city on match day)
+- Fixed: Various bugs in game and DLCs
+
+### TPP2 2.0.10, 02/06/2016
+
+- Fixed: NullPointerException
+
+### TPP2 2.0.9, 31/05/2016
+
+- Update: Compatbile with Network Extensions 2.5
+
+### TM:PE 1.6.17, 20/04/2016
+
+- Fixed: Hotfix for reported path-finding problems
+
+### TM:PE 1.6.16, 19/04/2016
+
+- Updated: Compatible with C:SL 1.4.1-f2
+
+### C:SL 1.4.1-f2, 19/04/2016
+
+- Fixed: Busses could enter/exit bus stations at highways with sound barriers
+- Fixed: Various other bugs
+
+### TPP2 2.0.8, 22/03/2016
+
+- Updated: Cpde from TM:PE 1.6.10
+- Updated: Compatible with C:SL 1.4.0-f3
+- Removed: Old code from TPP
+
+### TM:PE 1.6.15, 22/03/2016
+
+- Added: Traditional Chinese translation
+- Improved: Possible fix for crashes described by cosminel1982
+- Updated: Compatible with C:SL 1.4.0-f3
+
+### C:SL 1.4.0-f3, 22/03/2016
+
+- Fixed: Lots of bugs in game and DLCs
+- Updated: Lots of stuff in game and DLCs
+
+### TM:PE 1.6.14, 17/03/2016
+
+- Fixed: Cargo trucks did not obey vehicle restrictions (thanks to ad.vissers for pointing out this problem)
+- Fixed: When Advanced AI was deactivated, u-turns did not have costs assigned
+
+### TM:PE 1.6.13, 16/03/2016
+
+- Added: Dutch translation
+- Improved: The pedestrian light mode of a traffic light can now be switched back to automatic
+- Improved: Vehicles approaching a different speed limit change their speed more gradually
+- Improved: Path-finding performance improvements
+- Improved: Fine-tuned path-finding lane changing behaviour
+- Fixed: After loading another savegame, timed traffic lights stopped working for a certain time
+- Fixed: Lane speed calculation corrected
+- Updated: The size of signs and symbols in the overlay is determined by screen resolution height, not by width
+
+### TM:PE 1.6.12, 03/03/2016
+
+- Improved: Reduced memory usage
+- Fixed: Adding/removing junctions to/from existing timed traffic lights did not work (thanks to nieksen for pointing out this problem)
+- Fixed: Separate timed traffic lights were sometimes not saved (thanks to nieksen for pointing out this problem)
+- Fixed: Fixed an initialization error (thanks to GordonDry for pointing out this problem)
+
+### TM:PE 1.6.11, 03/03/2016
+
+- Added: Chinese translation
+- Added: By pressing "Page up"/"Page down" you can now switch between traffic and default map view
+- Improved: Size of information icons and signs is now based on your screen resolution
+- Updated: UI code refactored
+
+### TM:PE 1.6.10, 02/03/2016
+
+- Added: Additional controls for vehicle restrictions added
+- Fixed: Clicking on a Traffic Manager overlay resulted in vanilla game components (e.g. houses, vehicles) being activated
+
+### TM:PE 1.6.9, 02/03/2016
+
+- Updated: Compatibility with C:SL 1.3.2-f1
+
+### C:SL 1.3.2-f1, 02/03/2016
+
+- Fixed: Stuck pedestrian issues
+- Fixed: Short roads warning
+- Fixed: Cyclists despawn when changing from bike path to bike lane
+
+### TPP2 2.0.7, 01/03/2016
+
+- Improved: Performance
+- Updated: Vehicle AIs
+- Updated: Code from TM:PE 1.16 merged in to TPP2
+
+### TM:PE 1.6.8, 01/03/2016
+
+- Added: Spanish translation
+- Improved: Major path-finding performance improvements
+- Updated: Japanese translation (thanks to Akira Ishizaki for translating!)
+
+### TPP2 2.0.6, 28/02/2016
+
+- Updated: Cargo trucks pathfinder
+- Updated: Stations and ports pathfinder
+- Updated: Code from TM:PE 1.6.6 & 1.6.7 merged in to TPP2
+
+### TM:PE 1.6.7, 27/02/2016
+
+- Improved: Tuned AI parameters
+- Improved: Traffic density measurements
+- Improved: Lane changing near junctions - reintroduced costs for lane changing before junctions
+- Improved: Vehicle behaviour near blocked roads (e.g. while a building is burning)
+- Fixed: Automatic pedestrian lights for outgoing one-ways fixed
+- Fixed: U-turns did not have appropriate costs assigned
+- Fixed: The time span between AI traffic measurements was too high
+
+### TM:PE 1.6.6, 27/02/2016
+
+- Improved: Easier to select segment ends in order to change lane arrows.
+- Fixed: U-turning vehicles were not obeying the correct directional traffic light (thanks to t1a2l for pointing out this problem)
+- Updated: Priority signs now cannot be setup at outgoing one-ways.
+- Updated: French translation (thanks to simon.royer007 for translating!)
+- Updated: Polish translation (thanks to Krzychu1245 for translating!)
+- Updated: Portuguese translation (thanks to igordeeoliveira for translating!)
+- Updated: Russian translation (thanks to FireGames for translating!)
+
+### TPP2 2.0.5, 26/02/2016
+
+- Improved: Service vehicle pathfinding
+- Fixed: Cars disappearing underground
+
+### TPP2 2.0.4, 24/02/2016
+
+- Improved: Vehicle restrictions
+- Updated: Removed typecasting from Car AI
+- Updated: Major pathfinder refactor
+- Updated: Code from TM:PE 1.6.0 to 1.6.5 merged in to TPP2
+
+### TM:PE 1.6.5, 24/02/2016
+
+- Added: Despawning setting to options dialog
+- Improved: Detection of Traffic++ V2
+
+### TPP2 2.0.3, 23/02/2016
+
+- Added: Medium pedestrianised road
+- Fixed: Busses not using custom pathfinder
+- Fixed: Crash bug when Snowfall DLC subscribed
+- Fixed: Trams not working
+- Fixed: Transport lines not respecting restrictions
+- Fixed: Broken bus lanes on roads
+- Fixed: Path finder not respecting vehicle types
+- Fixed: Car AI broken when using Snowfall DLC
+- Fixed: Bug loading options
+- Updated: Improved how extended vehicle types are defined
+- Updated: Custom path manager refactored
+- Updated: Namespace and project refactor & clean-up
+- Updated: Separate extensions and AIs
+- Updated: Imrpoved options definition and storage
+- Removed: Ghost Mode
+
+### TM:PE 1.6.4, 23/02/2016
+
+- Improved: Minor performance improvements
+- Fixed: Path-finding calculated erroneous traffic density values
+- Fixed: Cims left the bus just to hop on a bus of the same line again (thanks to kamzik911 for pointing out this problem)
+- Fixed: Despawn control did not work (thanks to xXHistoricalxDemoXx for pointing out this problem)
+- Fixed: State of new settings was not displayed correctly (thanks to Lord_Assaultーさま for pointing out this problem)
+- Fixed: Default settings for vehicle restrictions on bus lanes corrected
+- Fixed: Pedestrian lights at railway junctions fixed (they are still invisible but are derived from the car traffic light state automatically)
+
+### C:SL 1.3.1-f1, 23/02/2016
+
+- Fixed: Minor bug fixes and updates
+
+### TM:PE 1.6.3, 22/02/2016
+
+- Fixed: Using the "Old Town" policy led to vehicles not spawning
+- Fixed: Planes, cargo trains and ship were sometimes not arriving
+- Fixed: Trams are not doing u-turns anymore
+
+### TPP2 2.0.2, 21/02/2016
+
+- Fixed: Bus routing & bus stops
+- Fixed: European theme incompatible (thanks BloodyPenguin for help!)
+- Fixed: Traffic doesn't stop for flooded tunnels
+- Updated: Serialise for lane data
+
+### TM:PE 1.6.2, 20/02/2016
+
+- Added: Trams are now obeying speed limits (thanks to Clausewitz for pointing out the issue)
+- Fixed: Clear traffic sometimes throwed an error
+- Fixed: Vehicle restrictions did not work as expected (thanks to [Delta ²k5] for pointing out this problem)
+- Fixed: Transition of automatic pedestrian lights fixed
+
+### TM:PE 1.6.1, 20/02/2016
+
+- Improved: Performance
+- Improved: Modifying mod options through the main menu now gives an annoying warning message instead of a blank page.
+- Fixed: Various UI issues
+
+### TPP2 2.0.1, 19/02/2016
+
+- Fixed: Default vehicle restrictions always include bus
+- Updated: Compatible with C:SL 1.3.0-f4
+
+### TPP2 2.0.0, 18/02/2016
+
+- Added: Allow restrictions on pedestrianised roads
+- Fixed: Bus lines not working
+- Fixed: Traffic++ roads
+- Fixed: Options screen now saves options properly
+- Improved: Better compatibility with TPP
+- Improved: Detection of incompatible mods
+- Updated: Lots of code clean-up
+- Meta: First sable release of TPP2
+
+### TM:PE 1.6.0, 18/02/2016
+
+- Added: Separate traffic lights for different vehicle types
+- Added: Vehicle restrictions
+- Added: "Vehicles may enter blocked junctions" may now be defined for each junction separately (again)
+- Added: Road conditions (snow, maintenance state) may now have a higher impact on vehicle speed (see "Options" menu)
+- Improved: Better handling of vehicle bans
+- Improved: Method for calculating lane traffic densities
+- Improved: Performance optimizations
+- Improved: Advanced Vehicle AI: Improved lane spreading
+- Fixed: Reckless drivers now do not enter railroad crossings if the barrier is down
+- Fixed: Path-finding costs for crossing a junction fixed
+- Fixed: Vehicle detection at timed traffic lights did not work as expected
+- Fixed: Not all valid traffic light arrow modes were reachable
+- Updated: Compatible with C:SL 1.3.0-f4
+- Updated: Ambulances, fire trucks and police cars on duty are now ignoring lane arrows
+- Updated: Timed traffic lights may now be setup at arbitrary nodes on railway tracks
+- Updated: Option dialog is disabled if accessed through the main menu
+- Updated: Vehicles going straight may now change lanes at junctions
+- Updated: Vehicles may now perform u-turns at junctions that have an appropriate lane arrow configuration
+- Updated: Emergency vehicles on duty now always aim for the fastest route
+
+### C:SL 1.3.0-f4 (Snowfall), 18/02/2016
+
+- Added: Winter biome
+- Added: Trams
+- Added: Snow ploughs
+- Added: Road maintenance
+- Added: Road conditions affect vehicle speed
+- Added: Priority routes
+
+### TPP2 0.0.1, 17/02/2016
+
+- Added: Tool - Lane Connector (from TPP)
+- Added: Tool - Vehicle Restrictions (from TPP)
+- Added: Tool - Speed Restrictions (from TPP)
+- Added: No Despawn (from TPP)
+- Added: Improved AI (from TPP:AI)
+- Meta: Released as version 0.0a
+
+### TPP2 0.0, 17/02/2016
+
+- Meta: Development started 4th October 2015
+- Meta: Was planned to replace TM:PE, TPP, etc
+- Meta: Dev team: LinuxFan, Katalyst6, Lazarus*Man
+- Meta: GitHub Repository: [Katalyst6/CSL.TransitAddonMod](https://github.com/Katalyst6/CSL.TransitAddonMod)
+- Meta: Steam Workshop: [626024868 - Traffic++ V2](https://steamcommunity.com/sharedfiles/filedetails/?id=626024868)
+
+### TM:PE 1.5.2, 01/02/2016
+
+- Added: Traffic lights may now be added to/removed from underground junctions
+- Added: Traffic lights may now be setup at some points of railway tracks (there seems to be a game-internal bug that prevents selecting arbitrary railway nodes)
+- Added: Display of priority signs, speed limits and timed traffic lights may now be toggled via the options dialog
+- Fixed: Reckless driving does not apply for trains (thanks to GordonDry for pointing out this problem)
+- Fixed: Manual traffic lights were not working (thanks to Mas71 for pointing out this problem)
+- Fixed: Pedestrians were ignoring timed traffic lights (thanks to Hannes8910 for pointing out this problem)
+- Fixed: Sometimes speed limits were not saved (thanks to cca_mikeman for pointing out this problem)
+
+### TM:PE 1.5.1, 31/01/2016
+
+- Added: Trains are now following speed limits
+
+### TM:PE 1.5.0, 30/01/2016
+
+- Added: Speed restrictions (as requested by Gfurst)
+- Improved: AI - Parameters tuned
+- Improved: Code improvements
+- Fixed: Flowing/Waiting vehicles count corrected
+- Updated: Lane arrow changer window is now positioned near the edited junction (as requested by GordonDry)
+
+### TM:PE 1.4.9, 27/01/2016
+
+- Added: Junctions can be added to/removed from timed traffic lights after they are created
+- Improved: When viewing/moving a timed step, the displayed/moved step is now highlighted (thanks to Joe for this idea)
+- Improved: Performance improvements
+- Fixed: AI - Fixed a division by zero error (thanks to GordonDry for pointing out this problem)
+- Fixed: AI - Near highway exits vehicles tended to use the outermost lane (thanks to Zake for pointing out this problem)
+- Fixed: Some lane arrows disappeared on maps using left-hand traffic systems (thanks to Mas71 for pointing out this problem)
+- Fixed: In lane arrow edit mode, the order of arrows was sometimes incorrect (thanks to Glowstrontium for pointing out this problem)
+- Fixed: Lane merging in left-hand traffic systems fixed
+- Fixed: Turning priority roads fixed (thanks to GordonDry for pointing out this problem)
+
+### TM:PE 1.4.8, 25/01/2016
+
+- Added: translation to Polish (thanks to Krzychu1245 for working on this!)
+- Added: translation to Russian (thanks to FireGames for working on this!)
+- Improved: AI - Parameters have been tuned
+- Improved: AI - Added traffic density measurements
+- Improved: Performance improvements
+- Fixed: After removing a timed or manual light the traffic light was deleted (thanks to Mas71 for pointing out this problem)
+- Fixed: Segment geometries were not always calculated
+- Fixed: In highway rule mode, lane arrows sometimes flickered
+- Fixed: Some traffic light arrows were sometimes not selectable
+
+### TM:PE 1.4.7, 22/01/2016
+
+- Added: Translation to Portuguese (thanks to igordeeoliveira for working on this!)
+- Improved: Reduced file size (thanks to GordonDry for reporting this problem)
+- Fixed: Freight ships/trains were not coming in (thanks to Mas71 and clus for reporting this problem)
+- Fixed: The toggle "Vehicles may enter blocked junctions" did not work properly (thanks for exxonic for reporting this problem)
+- Fixed: If a timed traffic light is being edited the segment geometry information is not updated (thanks to GordonDry for reporting this problem)
+
+### TM:PE 1.4.6, 22/01/2016
+
+- Added: Running average lane speeds are measured now
+- Fixed: Minor bug fixes
+
+### TM:PE 1.4.5, 22/01/2016
+
+- Added: "Vehicles may enter blocked junctions" may now be defined for each junction separately
+- Fixed: A deadlock in the path-finding is fixed
+- Fixed: Small timed light sensitivity values (< 0.1) were not saved correctly
+- Fixed: Timed traffic lights were not working for some players
+- Updated: Refactored segment geometry calculation
+
+### TM:PE 1.4.4, 21/01/2016
+
+- Added: Localization support
+
+### TM:PE 1.4.3, 20/01/2016
+
+- Improved: Several performance improvements
+- Improved: Calculation of segment geometries
+- Improved: Load balancing
+- Improved: Police cars, ambulances, fire trucks and hearses are now also controlled by the AI
+- Fixed: Vehicles did not always take the shortest path
+- Fixed: Vehicles disappeared after deleting/upgrading a road segment
+- Fixed: Fixed an error in path-finding cost calculation
+- Fixed: Outgoing roads were treated as ingoing roads when highway rules were activated
+
+### TM:PE 1.4.2, 16/01/2016
+
+- Added: Option added to disable highway rules
+- Improved: Several major performance improvements (thanks to sci302 for pointing out those issues)
+- Improved: Saving/loading of timed traffic lights
+- Fixed: AI did not consider speed limits/road types during path calculation (thanks to bhanhart, sa62039 for pointing out this problem)
+- Fixed: Vehicles were stopping in front of green traffic lights
+- Fixed: Stop/Yield signs were not working properly (thanks to GordonDry, Glowstrontium for pointing out this problem)
+- Fixed: Cargo trucks were ignoring the "Heavy ban" policy, they should do now (thanks to Scratch for pointing out this problem)
+- Updated: Lane-wise traffic density is only measured if Advanced AI is activated
+- Meta: Connecting a city road to a highway road that does not supply enough lanes for merging leads to behaviour people do not understand (see manual).
+
+### TM:PE 1.4.1, 15/01/2016
+
+- Fixed: Path-finding near junctions fixed
+
+### TM:PE 1.4.0, 15/01/2016
+
+- Added: Advanced Vehicle AI (disabled by default! Go to "Options" and enable it if you want to use it.)
+- Fixed: Traffic lights were popping up in the middle of roads
+- Fixed: Fixed the lane changer for left-hand traffic systems (thanks to Phishie for pointing out this problem)
+- Fixed: Traffic lights on invalid nodes are not saved anymore
+
+### TM:PE 1.3.24, 13/01/2016
+
+- Added: Configuration option that allows vehicles to enter blocked junctions
+- Improved: Priority signs: After adding two main road signs the next offered sign is a yield sign
+- Improved: Priority signs: Vehicles now should notice earlier that they can enter a junction
+- Fixed: Invalid (not created) lanes are not saved/loaded anymore
+- Fixed: Some priority signs were not saved
+- Fixed: Priority signs on deleted segments are now deleted too
+- Fixed: Lane arrows on removed lanes are now removed too
+- Fixed: Adding a priority sign to a junction having more than one main sign creates a yield sign (thanks to GordonDry for pointing out this problem)
+- Fixed: If reckless driving was set to "The Holy City (0 %)", vehicles blocked intersections with traffic light.
+- Fixed: Traffic light arrow modes were sometimes not correctly saved
+- Removed: Legacy XML file save system
+
+### TM:PE 1.3.23, 09/01/2016
+
+- Added: Option added to forget all toggled traffic lights
+- Fixed: Corrected an issue where toggled traffic lights would not be saved/loaded correctly (thanks to Jeffrios and AOD_War_2g for pointing out this problem)
+
+### TM:PE 1.3.22, 08/01/2016
+
+- Added: Option allowing busses to ignore lane arrows
+- Added: Option to display nodes and segments
+
+### TM:PE 1.3.21, 06/01/2016
+
+- Added: Traffic Sensitivity Tuning
+- Improved: When adding a new step to a timed traffic light the lights are inverted.
+- Improved: Timed traffic light status symbols should now be less annoying
+- Fixed: Deletion of junctions that were members of a traffic light group is now handled correctly
+
+### TM:PE 1.3.20, 04/01/2016
+
+- Added: Reckless driving
+- Improved: User interface
+- Fixed: Timed traffic lights are not saved correctly after upgrading a road nearby
+
+### TM:PE 1.3.19, 04/01/2016
+- Improved: Timed traffic lights: Velocity of vehicles is being measured to detect traffic jams
+- Improved: Traffic flow measurement
+- Improved: Path finding - Cims may now choose their lanes more independently
+- Fixed: Upgrading a road resets the traffic light arrow mode
+- Updated: Timed traffic lights: Absolute minimum time changed to 1
+
+### TM:PE 1.3.18, 03/01/2016
+
+- Improved: You can now switch between activated timed traffic lights without clicking on the menu button again
+- Fixed: Provided a fix for unconnected junctions caused by other mods
+- Removed: Crosswalk feature removed. If you need to add/remove crosswalks please use the "Crossings" mod.
+
+### TM:PE 1.3.17, 03/01/2016
+
+- Fixed: Timed traffic lights cannot be added again after removal, toggling traffic lights does not work (thanks to Fabrice, ChakyHH, sensual.heathen for pointing out this problem)
+- Fixed: After using the "Manual traffic lights" option, toggling lights does not work (thanks to Timso113 for pointing out this problem)
+
+### TM:PE 1.3.16, 03/01/2016
+
+- Fixed: Traffic light settings on roads of the Network Extensions mods are not saved (thanks to Scarface, martintech and Sonic for pointing out this problem)
+- Improved: Save data management
+
+### TM:PE 1.3.15, 02/01/2016
+
+- Added: Simulation accuracy (and thus performance) is now controllable through the game options dialog
+- Fixed: Vehicles on a priority road sometimes stop without an obvious reason
+
+### TM:PE 1.3.14, 01/01/2016
+
+- Improved: Performance
+- Improved: Non-timed traffic lights are now automatically removed when adding priority signs to a junction
+- Improved: Adjusted the adaptive traffic light decision formula (vehicle lengths are considered now)
+- Improved: Traffic two road segments in front of a timed traffic light is being measured now
+
+### TM:PE 1.3.13, 01/01/2016
+
+- Fixed: Lane arrows are not correctly translated into path finding decisions (thanks to bvoice360 for pointing out this problem)
+- Fixed: Priority signs are sometimes undeletable (thank to Blackwolf for pointing out this problem)
+- Fixed: Errors occur when other mods without namespace definitions are loaded (thanks to Arch Angel for pointing out this problem)
+- Fixed: Connecting a new road segment to a junction that already has priority signs now allows modification of the new priority sign
+
+### TM:PE 1.3.12, 30/12/2015
+
+- Fixed: Priority signs are not editable (thanks to ningcaohan for pointing out this problem)
+
+### TM:PE 1.3.11, 30/12/2015
+
+- Improved: Road segments next to a timed traffic light may now be deleted/upgraded/added without leading to deletion of the light
+- Updated: Priority signs and Timed traffic light state symbols are now visible as soon as the menu is opened
+
+### TM:PE 1.3.10, 29/12/2015
+- Fixed: an issue where timed traffic light groups were not deleted after deleting an adjacent segment
+
+### TM:PE 1.3.9, 29/12/2015
+
+- Added: Introduced information icons for timed traffic lights
+- Updated: Mod is now compatible with "Improved AI" (Lane changer is deactivated if "Improved AI" is active)
+
+### TM:PE 1.3.8, 29/12/2015
+
+- Improved: UI improvements
+- Fixed: Articulated busses are now simulated correctly (thanks to nieksen for pointing out this problem)
+
+### TM:PE 1.3.7, 28/12/2015
+
+- Fixed: When setting up a new timed traffic light, yellow lights from the real-world state are not taken over
+- Fixed: When loading another save game via the escape menu, Traffic Manager does not crash
+- Fixed: When loading another save game via the escape menu, Traffic++ detection works as intended
+- Fixed: Lane arrows are saved correctly
+
+### TM:PE 1.3.6, 28/12/2015
+
+- Fixed: wrong flow value taken when comparing flowing vehicles
+- Updated: Forced node rendering after modifying a crosswalk
+
+### TM:PE 1.3.5, 28/12/2015
+
+- Improved: Adjusted the comparison between flowing (green light) and waiting (red light) traffic
+- Fixed: Pedestrian traffic Lights (thanks to Glowstrontium for pointing out this problem)
+- Fixed: Deleting a segment with a timed traffic light does not cause a NullReferenceException
+
+### TM:PE 1.3.4, 27/12/2015
+
+- Improved: Better traffic jam handling
+
+### TM:PE 1.3.3, 27/12/2015
+
+- Improved: Hotfix - Deleting a segment with a timed traffic light does not cause a NullReferenceException
+- Updated: If priority signs are located behind the camera they are not rendered anymore
+
+### TM:PE 1.3.2, 27/12/2015
+
+- Fixed: Synchronized traffic light rendering: In-game Traffic lights display the correct color (Thanks to Fabrice for pointing out this problem)
+- Fixed: Traffic lights switch between green, yellow and red. Not only between green and red.
+- Updated: Priority signs are persistently visible when Traffic Manager is in "Add priority sign" mode
+- Updated: UI tool tips are more explanatory and are shown longer.
+
+### TM:PE 1.3.1, 26/12/2015
+
+- Fixed: Timed traffic lights of deleted/modified junctions get properly disposed
+- Updated: Minimum time units may be zero now (timed traffic lights)
+
+### TM:PE 1.3.0, 25/12/2015
+
+- Added: Adaptive Timed Traffic Lights (automatically adjusted based on traffic amount)
+- Meta: First release in Steam Workshop under name "Traffic Manager: President Edition"
+- Meta: Fork of TMPlus 1.2.0 created by LinuxFan (github user: VictorPhilipp)
+
+### TMPlus 1.2.0, 04/12/2015
+
+- Updated: Game version 1.2.2-f2 compatible
+
+### C:SL 1.2.2-f2, 05/11/2015
+
+- Updated: Limits for zoning, buildings, road segments increased
+- Fixed: Various bugfixes in game and asset editor
+
+### TM 1.0.9rc, 12/10/2015
+
+- Removed: Tool - Pedestrian crosswalks (too buggy)
+- Meta: Released as version 1.09rc
+- Meta: Last release of original Traffic Manager mod by CBeTHaX (aka SvetlozarValchev)
+
+### TM 1.0.9rc, 12/10/2015
+
+- Fixed: Minor bugs
+- Meta: Released as version 1.09rc
+
+### TM 1.0.9rc, 12/10/2015
+
+- Update: Compatible with C:SL 1.2.1-f1
+- Meta: Released as version 1.09rc
+
+### C:SL 1.2.1-f1, 01/10/2015
+
+- Fixed: Minor bugfixes with game and asset editor
+
+### TPP 1.6.1, 25/09/2015
+
+- Removed: In-game log messages
+- Meta: This was the last release of Traffic++ mod
+- Meta: It was later continued in Traffic++ V2 (TPP2)
+- Meta: It's features have since been merged in to TM:PE (tools & AIs) and NExt2 (roads)
+
+### TPP 1.6.0, 25/09/2015
+
+- Fixed: Rendering of road customiser in underground view
+- Updated: Integrated latest Improved Vehicle AI
+- Updated: Lamps on pedestrian roads
+
+### TPP 1.5.6, 24/09/2015
+
+- Fixed: Disable custom roads option not working
+
+### TPP 1.5.5, 24/09/2015
+
+- Update: Compatible with C:SL 1.2.0 (thanks javitonino)
+
+### C:SL 1.2 (After Dark), 24/09/2015
+
+- Added: Bicycles
+- Added: Bus and bike lanes
+- Added: Prison Vans
+- Added: Taxis
+
+### TMPlus 1.1.1, 09/08/2015
+
+- Fixed: Load/save system
+- Fixed: Saving no longer opens dev console
+
+### TM 1.0.6, 07/08/2015
+
+- Update: Revert to old version with C:S 1.1.1 compatibility
+- Meta: Released as version 1.06
+
+### TMPlus 1.1.1, 06/08/2015
+
+- Fixed: Null reference error when loading old savegames
+- Updated: New save system
+- Updated: New load system
+- Meta: Lots of code refactoring
+
+### TPP:AI 1.0.0, 02/08/2015
+
+- Added: Improved Vehicle AI
+- Meta: This was a preview mod by Jfarias, developer of TPP
+- Meta: It made vehicles use more lanes, and also change lanes (later becoming "DLS" in TM:PE)
+- Meta: Steam Workshop: [492391912 - Improved AI Traffic++](https://steamcommunity.com/sharedfiles/filedetails/?id=492391912)
+- Meta: Source code no longer avialable
+- Meta: The feature was merged in to later version of TPP and, later, TM:PE (Advanced Vehicle AI and DLS)
+
+### TM 1.0.5, 28/07/2015
+
+- Fixed: Pathfinder bugs
+- Fixed: Lane changer and traffic remover features sometimes disabled even without Traffic++
+- Fixed: Turn direction not matching lane marking logic (thanks klparrot!)
+- Updated: Code refactor and clean-up
+- Updated: Compatible with C:SL 1.1.1
+- Meta: Released as version 1.05
+
+### TMPlus 1.1.1, 26/07/2015
+
+- Fixed: Stop signs not working
+
+### TMPlus 1.1.1, 15/07/2015
+
+- Fixed: PathManager. Should now work for people without Traffic++
+- Meta: Last confirmed Workshop release of "Traffic Manager Plus"
+
+### TMPlus 1.1.1, 15/07/2015
+
+- Updated: Reverted PathFinder as it's causing Null Reference exceptions. Need to refactor it.
+
+### TMPlus 1.1.1, 14/07/2015
+
+- Added: Support for tunnels
+- Fixed: Turn direction not matching lane marking logic (thanks klparrot!)
+- Fixed: Compilation errors
+- Improved: Custom AI code refactoring and clean-up
+- Improved: Tool code refactoring and clean-up
+- Improved: UI code refactoring and clean-up
+- Updated: Compatible with C:SL 1.1.1
+- Meta: Project fork by sieggy
+- Meta: First release in Steam Workshop under name "Traffic Manager Plus"
+
+### TPP 1.5.4, 06/07/2015
+
+- Updated: Compatible with C:SL 1.1.1
+
+### C:SL 1.1.1, 01/07/2015
+
+- Added: Tunnels for pedestrian paths
+- Added: Autosave feature
+- Fixed: Lots of stuff
+- Improved: Asset editor
+
+### TPP 1.5.3, 02/06/2015
+
+- Fixed: Crash on Linux
+
+### TPP 1.5.2, 02/06/2015
+
+- Fixed: Trucks not allowed on new roads
+
+### TPP 1.5.1, 01/06/2015
+
+- Fixed: Bug preventing options loading if button not shown in content manager
+- Fixed: Duplicate prefab bug in Ghost Mode
+- Fixed: Duplicate roads in roads panel in Ghost Mode
+- Fixed: Vehicles have wrong AI when returning to main menu
+- Fixed: Vehicles using restricted lanes
+- Fixed: Bug preventing the Scrollable Toolbar mod from working in the entirety of the speed customizer panel
+- Updated: Added debug logs to the game's Debug Panel (F7) - thanks to Nefarion for implementing it
+
+### TM 1.0.4, 29/05/2015
+
+- Fixed: Negative timers (thanks XaBBoK!)
+- Improved: Pathfinder cleanup (thanks dornathal!)
+
+### TPP 1.5.0 hotfix, 28/05/2015
+
+- Fixed: Vehicles stopping in road
+- Fixed: Realistic speeds not stopping without restarting the game
+- Fixed: Road costs (thanks Archomeda)
+
+### TPP 1.5.0, 27/05/2015
+
+- Added: More roads
+- Added: Option for Improved Vehicle AI
+- Fixed: Bug cauing crashes when loading map through pause menu
+- Fixed: Tunnels from new roads turning in to normal tunnels
+- Fixed: Custom vehicles ignoring lane restricitons
+
+### TPP 1.4.0 hotfix, 23/05/2015
+
+- Fixed: Few more bugs
+
+### TPP 1.4.0 hotfix, 23/05/2015
+
+- Fixed: Wrong lane usage in highways
+
+### TPP 1.4.0, 22/05/2015
+
+- Added: Tool - Speed Limits
+- Added: Underground view for customisation tools
+- Added: Mod Option - No Despawn (from TM:PE)
+- Added: Multi-track station enabler
+- Improved: New UI for road customisation tools
+- Fixed: Road customisation tools appearing in asset editor
+- Fixed: Objects under the map bug
+- Fixed: Options button on all resolutions
+- Fixed: Train tracks should not be selectable
+
+### TPP 1.3.2 hotfix, 20/05/2015
+
+- Improved: Compatibility with C:SL 1.1.0b
+
+### TPP 1.3.2 hotfix, 19/05/2015
+
+- Update: Compatible with C:SL 1.1.0b
+
+### TM 1.0.4 hotfix, 19/05/2015
+
+- Fixed: Support for tunnels in AIs and pathfinder
+- Update: Compatible with C:SL 1.1.0b
+- Update: Traffic++ Compatibility - No despawn or lane changer available in compatibility mode
+
+### C:SL 1.1.0b, 19/05/2015
+
+- Added: European theme
+- Added: Tunnels for roads and rail
+- Improved: Metro tunnels can be built at different heights
+
+### TPP 1.3.2, 05/05/2015
+
+- Added: Ability to set restrictions lane by lane
+- Added: Ability to customise multiple lanes at same time
+- Fixed: But that prevented settings saving
+- Fixed: Bug that prevented short roads being selected
+- Fixed: Vehicle restrictions now show correctly on all resolutions
+- Updated: Road customiser tool button moved
+- Updated: Customisation overlays visible at all times when tool active
+
+### TPP 1.3.1, 29/04/2015
+
+- Improved: Perfomance issues reduced
+- Fixed: Strange vehicle behaviours
+- Fixed: Prevent road tool from selecting vehicles
+
+### TPP 1.3.0, 28/04/2015
+
+- Added: More roads
+- Added: Services overlay (vehicle restrictions) for pedestrian roads
+- Added: Tool - Vehicle Restrictions
+- Added: Tool - Lane Connections
+- Fixed: Left hand traffic issues
+- Fixed: Ghost mode caused crash if busway bridges on map
+- Fixed: Several crashes caused by mod incompatibilities
+
+### TM 1.0.4 beta, 22/04/2015
+
+- Added: Buttons for timed traffic light states: skip and move up/down
+- Fixed: Null reference exceptions when placing a road
+- Fixed: Pathfinder issues
+- Improved: Lane changes
+- Updated: Prevent lane changer being used on non-node segments
+- Meta: Released as version 1.04rc
+
+### TM 1.0.1, 21/04/2015
+
+- Fixed: Errors on very close segments added to timed traffic lights
+- Fixed: Save/load traffic lights going negative (finally)
+- Meta: Released as version 1.01rc
+
+### TM 1.0.0, 21/04/2015
+
+- Fixed: Wrong lane change (finally)
+- Fixed: Timed traffic lights should no longer go below zero
+- Fixed: UI problems and positioning
+- Meta: First release in Steam Workshop under name "Traffic Manager"
+- Meta: Released as version 1.0rc
+
+### TM 0.9.0, 21/04/2015
+
+- Fixed: Cars and service stop working
+- Fixed: Wrong lane use
+- Updated: Manager no longer loads in map editor
+- Fixed: Errors on adding additional segment add
+- Fixed: Negative numbers for traffic lights
+- Added: Thumbnail to workshop page
+- Meta: New save id
+- Meta: Released as version 0.9b
+
+### TM 0.8.5, 19/04/2015
+
+- Fixed lane merges in Left Hand Traffic maps
+- Meta: Versions 0.8.3 and 0.8.4 were skipped
+
+### TM 0.8.2, 19/04/2015
+
+- Updated: Rename 'Manual Control' to 'Manual Traffic Lights'
+
+### TM 0.8.1, 19/04/2015
+
+- Added: Tool - Toggle Traffic Lights
+- Updated: No more deleting trains on 'clear traffic'
+- Fixed: Null exception timed traffic lights on node upgrade
+- Fixed: UI position on different resolutions
+- Meta: Released as version 0.8b
+
+### TM 0.8.0, 19/04/2015
+
+- Fixed: Can now load game from pause menu
+
+### TM 0.7.1, 18/04/2015
+
+- Improved: Tuned car wait on non-priority road
+
+### TM 0.7.0, 18/04/2015
+
+- Fixed: Lanes on save/load in Left Hand traffic maps
+- Fixed: Lane positions
+- Fixed: More save/load fixes
+- Fixed: Various timed lights problems
+- Fixed: Traffic Manager load on multiple game loads
+- Fixed: Multiple saves on one savegame
+- Updated: Moved UI button
+
+### TM 0.6.1, 18/04/2015
+
+- Fixed: Cars coming form wrong lane in Left Hand Traffic maps
+- Fixed: Not being able to save on new game
+- Fixed: Errors when pressing Esc before opening menu
+- Meta: Released as version 0.61b
+
+### TM 0.6.0, 18/04/2015
+
+- Added: Tool - Manual Control (traffic lights)
+- Added: Tool - Priority Signs
+- Added: Tool - Toggle Pedestrian Crossings
+- Added: Tool - Clear Traffic
+- Added: Tool - Vehicle Restrictions (Transport, Service, Cargo or Car)
+- Added: Option - No Despawn (thanks cope)
+- Added: Custom Pathfinder
+- Added: Settings stored in save game
+- Updated: Compatible with C:SL 1.0.7c
+- Updated: Custom traffic light steps can be timed
+- Updated: Custom Road AI
+- Updated: Better traffic light UI
+- Meta: Using cities-skylines-detour by cope (sschoener on github)
+
+### TPP 1.2.0, 14/04/2015
+
+- Added: Realistic driving speeds
+- Added: More roads
+- Fixed: Bug in mod options screen
+- Updated: Compatible with C:SL 1.0.7c
+
+### C:SL 1.0.7c, 07/04/2015
+
+- Fixed: Lots of stuff
+
+### TPP 1.2.1, 06/04/2015
+
+- Fixed: Bugs introduced by previous version
+
+### TPP 1.1.8, 05/04/2015
+
+- Added: Support for left-hand traffic maps
+- Improved: Compatibility with Fine Road Heights mod
+- Fixed: Various bugs
+- Updated: Compatible with C:SL 1.0.7
+
+### C:SL 1.0.7, 27/03/2015
+
+- Fixed: Lots of stuff
+
+### TPP 1.1.7, 26/03/2015
+
+- Fixed: Various bugs
+
+### TPP 1.1.6, 26/03/2015
+
+- Fixed: Various bugs
+
+### TPP 1.1.5, 26/03/2015
+
+- Fixed: Various bugs
+
+### TPP 1.1.4, 26/03/2015
+
+- Fixed: Various bugs
+
+### TPP 1.1.3, 26/03/2015
+
+- Fixed: Various bugs
+
+### TPP 1.1.2, 26/03/2015
+
+- Fixed: Various bugs
+
+### TPP 1.1.1, 25/03/2015
+
+- Added: Option to disable updates from previous version
+- Fixed: Bugs from previous version
+
+### TPP 1.1.0, 25/03/2015
+
+- Added: More roads & associated features
+- Added: Option to disable pedestrians in middle lane
+- Meta: Renamed to "Traffic++"
+
+### TPP 1.0.5, 24/03/2015
+
+- Fixed: Options panel bug
+
+### TPP 1.0.4, 23/03/2015
+
+- Fixed: Various bugs
+
+### TM 0.5.0, 22/03/2015
+
+- Added: Tool - Traffic Lights Editor
+- Added: Tool - Lane Changer
+- Added: Toolbar for mod features
+- Added: Log file
+- Updated: Compatible with C:SL 1.0.6
+
+### TPP 1.0.3, 20/03/2015
+
+- Added: Option to toggle which vehicles can use pedestrian roads
+- Added: Ghost mode to disable most of mod but still allow maps to load
+- Updated: Compatible with C:SL 1.0.6
+- Meta: First working implementation of vehicle restrictions
+
+### C:SL 1.0.6, 19/03/2015
+
+- Fixed: Lots of stuff
+
+### TPP 1.0.2, 18/03/2015
+
+- Improved: Some interface improvements
+
+### TPP 1.0.1, 17/03/2015
+
+- Fixed: Mod could only be used once per gaming session
+- Fixed: Various bugs
+
+### TPP 1.0.0, 16/03/2015
+
+- Added: Pedestrian zoneable road
+- Meta: Traffic++ project starts, but under its original name "CSL-Traffic"
+- Meta: GitHub Repository: [joaofarias/csl-traffic](https://github.com/joaofarias/csl-traffic)
+- Meta: Steam Workshop
+
+### TM 0.4.0, 14/03/2015
+
+- Meta: Traffic Manager project starts
+- Meta: Developer CBeTHaX (github user SvetlozarValchev)
+- Meta: GitHub Repository: [SvetlozarValchev/Skylines-Traffic-Manager](https://github.com/SvetlozarValchev/Skylines-Traffic-Manager)
+- Meta: Steam Workshop: Traffic Manager
+
+### C:SL 1.0, 10/03/2015
+
+- New: Cities: Skylines 1.0 official release
diff --git a/TLM/.editorconfig b/TLM/.editorconfig
new file mode 100644
index 000000000..9e11c451a
--- /dev/null
+++ b/TLM/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*.{cs,vb}]
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+
+[*.cs]
+# csharp_new_line_before_open_brace = methods
diff --git a/TLM/.vs/config/applicationhost.config b/TLM/.vs/config/applicationhost.config
deleted file mode 100644
index 5a8a09d3f..000000000
--- a/TLM/.vs/config/applicationhost.config
+++ /dev/null
@@ -1,1028 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/TLM/CSUtil.Commons/CSUtil.Commons.csproj b/TLM/CSUtil.Commons/CSUtil.Commons.csproj
index f51ceffd9..6e17d5df9 100644
--- a/TLM/CSUtil.Commons/CSUtil.Commons.csproj
+++ b/TLM/CSUtil.Commons/CSUtil.Commons.csproj
@@ -12,6 +12,7 @@
v3.5
512
+ latest
true
diff --git a/TLM/TLM/Custom/AI/CustomAmbulanceAI.cs b/TLM/TLM/Custom/AI/CustomAmbulanceAI.cs
index a71b85999..c1be31520 100644
--- a/TLM/TLM/Custom/AI/CustomAmbulanceAI.cs
+++ b/TLM/TLM/Custom/AI/CustomAmbulanceAI.cs
@@ -1,81 +1,75 @@
-using ColossalFramework;
-using TrafficManager.RedirectionFramework.Attributes;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using TrafficManager.Custom.PathFinding;
-using TrafficManager.Geometry;
-using TrafficManager.Manager;
-using TrafficManager.Manager.Impl;
-using TrafficManager.Traffic;
-using TrafficManager.Traffic.Data;
-using TrafficManager.Traffic.Enums;
-using UnityEngine;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
+namespace TrafficManager.Custom.AI {
+ using API.Traffic.Data;
+ using API.Traffic.Enums;
+ using ColossalFramework;
+ using Custom.PathFinding;
+ using Manager.Impl;
+ using RedirectionFramework.Attributes;
+ using Traffic.Data;
+ using UnityEngine;
-namespace TrafficManager.Custom.AI {
- [TargetType(typeof(AmbulanceAI))]
- public class CustomAmbulanceAI : CarAI {
- [RedirectMethod]
- public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
- ExtVehicleType vehicleType = ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, (vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0 ? ExtVehicleType.Emergency : ExtVehicleType.Service);
+ [TargetType(typeof(AmbulanceAI))]
+ public class CustomAmbulanceAI : CarAI {
+ [RedirectMethod]
+ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
+ ExtVehicleType vehicleType = ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, (vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0 ? ExtVehicleType.Emergency : ExtVehicleType.Service);
- VehicleInfo info = this.m_info;
- bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
- PathUnit.Position startPosA;
- PathUnit.Position startPosB;
- float startDistSqrA;
- float startDistSqrB;
- PathUnit.Position endPosA;
- PathUnit.Position endPosB;
- float endDistSqrA;
- float endDistSqrB;
- if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB) &&
- CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB)) {
- if (!startBothWays || startDistSqrA < 10f) {
- startPosB = default(PathUnit.Position);
- }
- if (!endBothWays || endDistSqrA < 10f) {
- endPosB = default(PathUnit.Position);
- }
- uint path;
- // NON-STOCK CODE START
- PathCreationArgs args;
- args.extPathType = ExtPathType.None;
- args.extVehicleType = vehicleType;
- args.vehicleId = vehicleID;
- args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- args.buildIndex = Singleton.instance.m_currentBuildIndex;
- args.startPosA = startPosA;
- args.startPosB = startPosB;
- args.endPosA = endPosA;
- args.endPosB = endPosB;
- args.vehiclePosition = default(PathUnit.Position);
- args.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;
- args.vehicleTypes = info.m_vehicleType;
- args.maxLength = 20000f;
- args.isHeavyVehicle = this.IsHeavyVehicle();
- args.hasCombustionEngine = this.CombustionEngine();
- args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
- args.ignoreFlooded = false;
- args.ignoreCosts = false;
- args.randomParking = false;
- args.stablePath = false;
- args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ VehicleInfo info = this.m_info;
+ bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
+ PathUnit.Position startPosA;
+ PathUnit.Position startPosB;
+ float startDistSqrA;
+ float startDistSqrB;
+ PathUnit.Position endPosA;
+ PathUnit.Position endPosB;
+ float endDistSqrA;
+ float endDistSqrB;
+ if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB) &&
+ CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB)) {
+ if (!startBothWays || startDistSqrA < 10f) {
+ startPosB = default(PathUnit.Position);
+ }
+ if (!endBothWays || endDistSqrA < 10f) {
+ endPosB = default(PathUnit.Position);
+ }
+ uint path;
+ // NON-STOCK CODE START
+ PathCreationArgs args;
+ args.extPathType = ExtPathType.None;
+ args.extVehicleType = vehicleType;
+ args.vehicleId = vehicleID;
+ args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ args.buildIndex = Singleton.instance.m_currentBuildIndex;
+ args.startPosA = startPosA;
+ args.startPosB = startPosB;
+ args.endPosA = endPosA;
+ args.endPosB = endPosB;
+ args.vehiclePosition = default(PathUnit.Position);
+ args.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;
+ args.vehicleTypes = info.m_vehicleType;
+ args.maxLength = 20000f;
+ args.isHeavyVehicle = this.IsHeavyVehicle();
+ args.hasCombustionEngine = this.CombustionEngine();
+ args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
+ args.ignoreFlooded = false;
+ args.ignoreCosts = false;
+ args.randomParking = false;
+ args.stablePath = false;
+ args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
- // NON-STOCK CODE END
- if (vehicleData.m_path != 0u) {
- Singleton.instance.ReleasePath(vehicleData.m_path);
- }
- vehicleData.m_path = path;
- vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
- return true;
- }
- } else {
- PathfindFailure(vehicleID, ref vehicleData);
- }
- return false;
- }
- }
-}
+ if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
+ // NON-STOCK CODE END
+ if (vehicleData.m_path != 0u) {
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ }
+ vehicleData.m_path = path;
+ vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
+ return true;
+ }
+ } else {
+ PathfindFailure(vehicleID, ref vehicleData);
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/AI/CustomBusAI.cs b/TLM/TLM/Custom/AI/CustomBusAI.cs
index 3bcc2ed49..ae897d40a 100644
--- a/TLM/TLM/Custom/AI/CustomBusAI.cs
+++ b/TLM/TLM/Custom/AI/CustomBusAI.cs
@@ -1,77 +1,71 @@
-using ColossalFramework;
-using TrafficManager.RedirectionFramework.Attributes;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using TrafficManager.Custom.PathFinding;
-using TrafficManager.Geometry;
-using TrafficManager.Manager;
-using TrafficManager.Traffic;
-using TrafficManager.Traffic.Data;
-using TrafficManager.Traffic.Enums;
-using UnityEngine;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
+namespace TrafficManager.Custom.AI {
+ using API.Traffic.Data;
+ using API.Traffic.Enums;
+ using ColossalFramework;
+ using Custom.PathFinding;
+ using RedirectionFramework.Attributes;
+ using Traffic.Data;
+ using UnityEngine;
-namespace TrafficManager.Custom.AI {
- [TargetType(typeof(BusAI))]
- public class CustomBusAI : CarAI {
- [RedirectMethod]
- public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
- VehicleInfo info = this.m_info;
- bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
- PathUnit.Position startPosA;
- PathUnit.Position startPosB;
- float startDistSqrA;
- float startDistSqrB;
- PathUnit.Position endPosA;
- PathUnit.Position endPosB;
- float endDistSqrA;
- float endDistSqrB;
- if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB) &&
- CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB)) {
- if (!startBothWays || startDistSqrA < 10f) {
- startPosB = default(PathUnit.Position);
- }
- if (!endBothWays || endDistSqrA < 10f) {
- endPosB = default(PathUnit.Position);
- }
- uint path;
- // NON-STOCK CODE START
- PathCreationArgs args;
- args.extPathType = ExtPathType.None;
- args.extVehicleType = ExtVehicleType.Bus;
- args.vehicleId = vehicleID;
- args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- args.buildIndex = Singleton.instance.m_currentBuildIndex;
- args.startPosA = startPosA;
- args.startPosB = startPosB;
- args.endPosA = endPosA;
- args.endPosB = endPosB;
- args.vehiclePosition = default(PathUnit.Position);
- args.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;
- args.vehicleTypes = info.m_vehicleType;
- args.maxLength = 20000f;
- args.isHeavyVehicle = this.IsHeavyVehicle();
- args.hasCombustionEngine = this.CombustionEngine();
- args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
- args.ignoreFlooded = false;
- args.randomParking = false;
- args.ignoreCosts = false;
- args.stablePath = true;
- args.skipQueue = true;
+ [TargetType(typeof(BusAI))]
+ public class CustomBusAI : CarAI {
+ [RedirectMethod]
+ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
+ VehicleInfo info = this.m_info;
+ bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
+ PathUnit.Position startPosA;
+ PathUnit.Position startPosB;
+ float startDistSqrA;
+ float startDistSqrB;
+ PathUnit.Position endPosA;
+ PathUnit.Position endPosB;
+ float endDistSqrA;
+ float endDistSqrB;
+ if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB) &&
+ CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB)) {
+ if (!startBothWays || startDistSqrA < 10f) {
+ startPosB = default(PathUnit.Position);
+ }
+ if (!endBothWays || endDistSqrA < 10f) {
+ endPosB = default(PathUnit.Position);
+ }
+ uint path;
+ // NON-STOCK CODE START
+ PathCreationArgs args;
+ args.extPathType = ExtPathType.None;
+ args.extVehicleType = ExtVehicleType.Bus;
+ args.vehicleId = vehicleID;
+ args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ args.buildIndex = Singleton.instance.m_currentBuildIndex;
+ args.startPosA = startPosA;
+ args.startPosB = startPosB;
+ args.endPosA = endPosA;
+ args.endPosB = endPosB;
+ args.vehiclePosition = default(PathUnit.Position);
+ args.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;
+ args.vehicleTypes = info.m_vehicleType;
+ args.maxLength = 20000f;
+ args.isHeavyVehicle = this.IsHeavyVehicle();
+ args.hasCombustionEngine = this.CombustionEngine();
+ args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
+ args.ignoreFlooded = false;
+ args.randomParking = false;
+ args.ignoreCosts = false;
+ args.stablePath = true;
+ args.skipQueue = true;
- if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
- // NON-STOCK CODE END
+ if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
+ // NON-STOCK CODE END
- if (vehicleData.m_path != 0u) {
- Singleton.instance.ReleasePath(vehicleData.m_path);
- }
- vehicleData.m_path = path;
- vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
- return true;
- }
- }
- return false;
- }
- }
-}
+ if (vehicleData.m_path != 0u) {
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ }
+ vehicleData.m_path = path;
+ vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/AI/CustomCarAI.cs b/TLM/TLM/Custom/AI/CustomCarAI.cs
index 984623987..4ce38a029 100644
--- a/TLM/TLM/Custom/AI/CustomCarAI.cs
+++ b/TLM/TLM/Custom/AI/CustomCarAI.cs
@@ -1,609 +1,610 @@
#define DEBUGVx
-using System;
-using System.Collections.Generic;
-using ColossalFramework;
-using ColossalFramework.Math;
-using TrafficManager.Geometry;
-using TrafficManager.TrafficLight;
-using UnityEngine;
-using Random = UnityEngine.Random;
-using TrafficManager.Custom.PathFinding;
-using TrafficManager.State;
-using TrafficManager.Manager;
-using TrafficManager.Traffic;
-using CSUtil.Commons;
-using TrafficManager.Manager.Impl;
-using System.Runtime.CompilerServices;
-using TrafficManager.Traffic.Data;
-using static TrafficManager.Traffic.Data.ExtCitizenInstance;
-using CSUtil.Commons.Benchmark;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
-using TrafficManager.Traffic.Enums;
-using TrafficManager.RedirectionFramework.Attributes;
-
namespace TrafficManager.Custom.AI {
- [TargetType(typeof(CarAI))]
- public class CustomCarAI : CarAI { // TODO inherit from VehicleAI (in order to keep the correct references to `base`)
- public void Awake() {
-
- }
-
- ///
- /// Lightweight simulation step method.
- /// This method is occasionally being called for different cars.
- ///
- ///
- ///
- ///
- [RedirectMethod]
- public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {
+ using System;
+ using System.Runtime.CompilerServices;
+ using API.Traffic.Data;
+ using API.Traffic.Enums;
+ using ColossalFramework;
+ using ColossalFramework.Math;
+ using CSUtil.Commons;
+ using Custom.PathFinding;
+ using Manager;
+ using Manager.Impl;
+ using RedirectionFramework.Attributes;
+ using State;
+ using Traffic.Data;
+ using UnityEngine;
+
+ [TargetType(typeof(CarAI))]
+ public class CustomCarAI : CarAI { // TODO inherit from VehicleAI (in order to keep the correct references to `base`)
+ public void Awake() {
+
+ }
+
+ ///
+ /// Lightweight simulation step method.
+ /// This method is occasionally being called for different cars.
+ ///
+ ///
+ ///
+ ///
+ [RedirectMethod]
+ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {
#if DEBUG
- bool vehDebug = (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleId);
- bool debug = GlobalConfig.Instance.Debug.Switches[2] && vehDebug;
- bool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && vehDebug;
+ bool vehDebug = (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleId);
+ bool debug = GlobalConfig.Instance.Debug.Switches[2] && vehDebug;
+ bool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && vehDebug;
#endif
- if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) {
- PathManager pathManager = Singleton.instance;
- byte pathFindFlags = pathManager.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;
+ if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) {
+ PathManager pathManager = Singleton.instance;
+ byte pathFindFlags = pathManager.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;
- // NON-STOCK CODE START
- ExtPathState mainPathState = ExtPathState.Calculating;
- if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) {
- mainPathState = ExtPathState.Failed;
- } else if ((pathFindFlags & PathUnit.FLAG_READY) != 0) {
- mainPathState = ExtPathState.Ready;
- }
+ // NON-STOCK CODE START
+ ExtPathState mainPathState = ExtPathState.Calculating;
+ if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) {
+ mainPathState = ExtPathState.Failed;
+ } else if ((pathFindFlags & PathUnit.FLAG_READY) != 0) {
+ mainPathState = ExtPathState.Ready;
+ }
#if DEBUG
- if (debug)
- Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Path: {vehicleData.m_path}, mainPathState={mainPathState}");
+ if (debug)
+ Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Path: {vehicleData.m_path}, mainPathState={mainPathState}");
#endif
- IExtVehicleManager extVehicleManager = Constants.ManagerFactory.ExtVehicleManager;
- ExtSoftPathState finalPathState = ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);
- if (Options.parkingAI && extVehicleManager.ExtVehicles[vehicleId].vehicleType == ExtVehicleType.PassengerCar) {
- ushort driverInstanceId = extVehicleManager.GetDriverInstanceId(vehicleId, ref vehicleData);
- finalPathState = AdvancedParkingManager.Instance.UpdateCarPathState(vehicleId, ref vehicleData, ref Singleton.instance.m_instances.m_buffer[driverInstanceId], ref ExtCitizenInstanceManager.Instance.ExtInstances[driverInstanceId], mainPathState);
+ IExtVehicleManager extVehicleManager = Constants.ManagerFactory.ExtVehicleManager;
+ ExtSoftPathState finalPathState = ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);
+ if (Options.parkingAI && extVehicleManager.ExtVehicles[vehicleId].vehicleType == ExtVehicleType.PassengerCar) {
+ ushort driverInstanceId = extVehicleManager.GetDriverInstanceId(vehicleId, ref vehicleData);
+ finalPathState = AdvancedParkingManager.Instance.UpdateCarPathState(vehicleId, ref vehicleData, ref Singleton.instance.m_instances.m_buffer[driverInstanceId], ref ExtCitizenInstanceManager.Instance.ExtInstances[driverInstanceId], mainPathState);
#if DEBUG
- if (debug)
- Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Applied Parking AI logic. Path: {vehicleData.m_path}, mainPathState={mainPathState}, finalPathState={finalPathState}");
+ if (debug)
+ Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Applied Parking AI logic. Path: {vehicleData.m_path}, mainPathState={mainPathState}, finalPathState={finalPathState}");
#endif
- }
+ }
- switch (finalPathState) {
- case ExtSoftPathState.Ready:
+ switch (finalPathState) {
+ case ExtSoftPathState.Ready:
#if DEBUG
- if (debug)
- Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding succeeded for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- calling CarAI.PathfindSuccess");
+ if (debug)
+ Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding succeeded for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- calling CarAI.PathfindSuccess");
#endif
- vehicleData.m_pathPositionIndex = 255;
- vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
- vehicleData.m_flags &= ~Vehicle.Flags.Arriving;
- this.PathfindSuccess(vehicleId, ref vehicleData);
- this.TrySpawn(vehicleId, ref vehicleData);
- break;
- case ExtSoftPathState.Ignore:
+ vehicleData.m_pathPositionIndex = 255;
+ vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
+ vehicleData.m_flags &= ~Vehicle.Flags.Arriving;
+ this.PathfindSuccess(vehicleId, ref vehicleData);
+ this.TrySpawn(vehicleId, ref vehicleData);
+ break;
+ case ExtSoftPathState.Ignore:
#if DEBUG
- if (debug)
- Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding result shall be ignored for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- ignoring");
+ if (debug)
+ Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding result shall be ignored for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- ignoring");
#endif
- return;
- case ExtSoftPathState.Calculating:
- default:
+ return;
+ case ExtSoftPathState.Calculating:
+ default:
#if DEBUG
- if (debug)
- Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding result undetermined for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- continue");
+ if (debug)
+ Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding result undetermined for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- continue");
#endif
- break;
- case ExtSoftPathState.FailedHard:
+ break;
+ case ExtSoftPathState.FailedHard:
#if DEBUG
- if (debug)
- Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): HARD path-finding failure for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- calling CarAI.PathfindFailure");
+ if (debug)
+ Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): HARD path-finding failure for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- calling CarAI.PathfindFailure");
#endif
- vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
- Singleton.instance.ReleasePath(vehicleData.m_path);
- vehicleData.m_path = 0u;
- this.PathfindFailure(vehicleId, ref vehicleData);
- return;
- case ExtSoftPathState.FailedSoft:
+ vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ vehicleData.m_path = 0u;
+ this.PathfindFailure(vehicleId, ref vehicleData);
+ return;
+ case ExtSoftPathState.FailedSoft:
#if DEBUG
- if (debug)
- Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): SOFT path-finding failure for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- calling CarAI.InvalidPath");
+ if (debug)
+ Log._Debug($"CustomCarAI.CustomSimulationStep({vehicleId}): SOFT path-finding failure for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- calling CarAI.InvalidPath");
#endif
- // path mode has been updated, repeat path-finding
- vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
- this.InvalidPath(vehicleId, ref vehicleData, vehicleId, ref vehicleData);
- break;
- }
- // NON-STOCK CODE END
- } else {
- if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) {
- this.TrySpawn(vehicleId, ref vehicleData);
- }
- }
-
- // NON-STOCK CODE START
- IExtVehicleManager extVehicleMan = Constants.ManagerFactory.ExtVehicleManager;
- extVehicleMan.UpdateVehiclePosition(vehicleId, ref vehicleData);
-
- if (!Options.isStockLaneChangerUsed() && (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0) {
- extVehicleMan.LogTraffic(vehicleId, ref vehicleData);
- }
- // NON-STOCK CODE END
-
- Vector3 lastFramePosition = vehicleData.GetLastFramePosition();
- int lodPhysics;
- if (Vector3.SqrMagnitude(physicsLodRefPos - lastFramePosition) >= 1210000f) {
- lodPhysics = 2;
- } else if (Vector3.SqrMagnitude(Singleton.instance.m_simulationView.m_position - lastFramePosition) >= 250000f) {
- lodPhysics = 1;
- } else {
- lodPhysics = 0;
- }
- this.SimulationStep(vehicleId, ref vehicleData, vehicleId, ref vehicleData, lodPhysics);
- if (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0) {
- VehicleManager vehManager = Singleton.instance;
- ushort trailerId = vehicleData.m_trailingVehicle;
- int numIters = 0;
- while (trailerId != 0) {
- ushort trailingVehicle = vehManager.m_vehicles.m_buffer[(int)trailerId].m_trailingVehicle;
- VehicleInfo info = vehManager.m_vehicles.m_buffer[(int)trailerId].Info;
- info.m_vehicleAI.SimulationStep(trailerId, ref vehManager.m_vehicles.m_buffer[(int)trailerId], vehicleId, ref vehicleData, lodPhysics);
- trailerId = trailingVehicle;
- if (++numIters > 16384) {
- CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
- break;
- }
- }
- }
-
- int privateServiceIndex = ItemClass.GetPrivateServiceIndex(this.m_info.m_class.m_service);
- int maxBlockCounter = (privateServiceIndex == -1) ? 150 : 100;
- if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace)) == 0 && vehicleData.m_cargoParent == 0) {
- Singleton.instance.ReleaseVehicle(vehicleId);
- } else if ((int)vehicleData.m_blockCounter >= maxBlockCounter) {
- // NON-STOCK CODE START
- if (VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData)) {
- // NON-STOCK CODE END
- Singleton.instance.ReleaseVehicle(vehicleId);
- } // NON-STOCK CODE
- }
- }
-
- [RedirectMethod]
- public bool CustomTrySpawn(ushort vehicleId, ref Vehicle vehicleData) {
- if ((vehicleData.m_flags & Vehicle.Flags.Spawned) != (Vehicle.Flags)0) {
- return true;
- }
- if (CustomCarAI.CheckOverlap(vehicleData.m_segment, 0, 1000f)) {
- vehicleData.m_flags |= Vehicle.Flags.WaitingSpace;
- return false;
- }
- vehicleData.Spawn(vehicleId);
- vehicleData.m_flags &= ~Vehicle.Flags.WaitingSpace;
- return true;
- }
-
- [RedirectMethod]
- public void CustomCalculateSegmentPosition(ushort vehicleId, ref Vehicle vehicleData, PathUnit.Position nextNextPosition,
- PathUnit.Position nextPosition, uint nextLaneId, byte nextOffset, PathUnit.Position curPosition, uint curLaneId,
- byte curOffset, int index, out Vector3 pos, out Vector3 dir, out float maxSpeed) {
- var netManager = Singleton.instance;
- ushort nextSourceNodeId;
- ushort nextTargetNodeId;
- if (nextOffset < nextPosition.m_offset) {
- nextSourceNodeId = netManager.m_segments.m_buffer[nextPosition.m_segment].m_startNode;
- nextTargetNodeId = netManager.m_segments.m_buffer[nextPosition.m_segment].m_endNode;
- } else {
- nextSourceNodeId = netManager.m_segments.m_buffer[nextPosition.m_segment].m_endNode;
- nextTargetNodeId = netManager.m_segments.m_buffer[nextPosition.m_segment].m_startNode;
- }
-
- ushort curTargetNodeId;
- if (curOffset == 0) {
- curTargetNodeId = netManager.m_segments.m_buffer[(int)curPosition.m_segment].m_startNode;
- } else {
- curTargetNodeId = netManager.m_segments.m_buffer[(int)curPosition.m_segment].m_endNode;
- }
+ // path mode has been updated, repeat path-finding
+ vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
+ this.InvalidPath(vehicleId, ref vehicleData, vehicleId, ref vehicleData);
+ break;
+ }
+ // NON-STOCK CODE END
+ } else {
+ if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) {
+ this.TrySpawn(vehicleId, ref vehicleData);
+ }
+ }
+
+ // NON-STOCK CODE START
+ IExtVehicleManager extVehicleMan = Constants.ManagerFactory.ExtVehicleManager;
+ extVehicleMan.UpdateVehiclePosition(vehicleId, ref vehicleData);
+
+ if (!Options.isStockLaneChangerUsed() && (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0) {
+ extVehicleMan.LogTraffic(vehicleId, ref vehicleData);
+ }
+ // NON-STOCK CODE END
+
+ Vector3 lastFramePosition = vehicleData.GetLastFramePosition();
+ int lodPhysics;
+ if (Vector3.SqrMagnitude(physicsLodRefPos - lastFramePosition) >= 1210000f) {
+ lodPhysics = 2;
+ } else if (Vector3.SqrMagnitude(Singleton.instance.m_simulationView.m_position - lastFramePosition) >= 250000f) {
+ lodPhysics = 1;
+ } else {
+ lodPhysics = 0;
+ }
+ this.SimulationStep(vehicleId, ref vehicleData, vehicleId, ref vehicleData, lodPhysics);
+ if (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0) {
+ VehicleManager vehManager = Singleton.instance;
+ ushort trailerId = vehicleData.m_trailingVehicle;
+ int numIters = 0;
+ while (trailerId != 0) {
+ ushort trailingVehicle = vehManager.m_vehicles.m_buffer[(int)trailerId].m_trailingVehicle;
+ VehicleInfo info = vehManager.m_vehicles.m_buffer[(int)trailerId].Info;
+ info.m_vehicleAI.SimulationStep(trailerId, ref vehManager.m_vehicles.m_buffer[(int)trailerId], vehicleId, ref vehicleData, lodPhysics);
+ trailerId = trailingVehicle;
+ if (++numIters > 16384) {
+ CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
+ break;
+ }
+ }
+ }
+
+ int privateServiceIndex = ItemClass.GetPrivateServiceIndex(this.m_info.m_class.m_service);
+ int maxBlockCounter = (privateServiceIndex == -1) ? 150 : 100;
+ if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace)) == 0 && vehicleData.m_cargoParent == 0) {
+ Singleton.instance.ReleaseVehicle(vehicleId);
+ } else if ((int)vehicleData.m_blockCounter >= maxBlockCounter) {
+ // NON-STOCK CODE START
+ if (VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData)) {
+ // NON-STOCK CODE END
+ Singleton.instance.ReleaseVehicle(vehicleId);
+ } // NON-STOCK CODE
+ }
+ }
+
+ [RedirectMethod]
+ public bool CustomTrySpawn(ushort vehicleId, ref Vehicle vehicleData) {
+ if ((vehicleData.m_flags & Vehicle.Flags.Spawned) != (Vehicle.Flags)0) {
+ return true;
+ }
+ if (CustomCarAI.CheckOverlap(vehicleData.m_segment, 0, 1000f)) {
+ vehicleData.m_flags |= Vehicle.Flags.WaitingSpace;
+ return false;
+ }
+ vehicleData.Spawn(vehicleId);
+ vehicleData.m_flags &= ~Vehicle.Flags.WaitingSpace;
+ return true;
+ }
+
+ [RedirectMethod]
+ public void CustomCalculateSegmentPosition(ushort vehicleId, ref Vehicle vehicleData, PathUnit.Position nextNextPosition,
+ PathUnit.Position nextPosition, uint nextLaneId, byte nextOffset, PathUnit.Position curPosition, uint curLaneId,
+ byte curOffset, int index, out Vector3 pos, out Vector3 dir, out float maxSpeed) {
+ var netManager = Singleton.instance;
+ ushort nextSourceNodeId;
+ ushort nextTargetNodeId;
+ if (nextOffset < nextPosition.m_offset) {
+ nextSourceNodeId = netManager.m_segments.m_buffer[nextPosition.m_segment].m_startNode;
+ nextTargetNodeId = netManager.m_segments.m_buffer[nextPosition.m_segment].m_endNode;
+ } else {
+ nextSourceNodeId = netManager.m_segments.m_buffer[nextPosition.m_segment].m_endNode;
+ nextTargetNodeId = netManager.m_segments.m_buffer[nextPosition.m_segment].m_startNode;
+ }
+
+ ushort curTargetNodeId;
+ if (curOffset == 0) {
+ curTargetNodeId = netManager.m_segments.m_buffer[(int)curPosition.m_segment].m_startNode;
+ } else {
+ curTargetNodeId = netManager.m_segments.m_buffer[(int)curPosition.m_segment].m_endNode;
+ }
#if DEBUG
- bool debug = GlobalConfig.Instance.Debug.Switches[21] && (GlobalConfig.Instance.Debug.NodeId <= 0 || curTargetNodeId == GlobalConfig.Instance.Debug.NodeId) && (GlobalConfig.Instance.Debug.ExtVehicleType == ExtVehicleType.None || GlobalConfig.Instance.Debug.ExtVehicleType == ExtVehicleType.RoadVehicle) && (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleId);
-
- if (debug) {
- Log._Debug($"CustomCarAI.CustomCalculateSegmentPosition({vehicleId}) called.\n" +
- $"\tcurPosition.m_segment={curPosition.m_segment}, curPosition.m_offset={curPosition.m_offset}\n" +
- $"\tnextPosition.m_segment={nextPosition.m_segment}, nextPosition.m_offset={nextPosition.m_offset}\n" +
- $"\tnextNextPosition.m_segment={nextNextPosition.m_segment}, nextNextPosition.m_offset={nextNextPosition.m_offset}\n" +
- $"\tcurLaneId={curLaneId}, curOffset={curOffset}\n" +
- $"\tnextLaneId={nextLaneId}, nextOffset={nextOffset}\n" +
- $"\tnextSourceNodeId={nextSourceNodeId}, nextTargetNodeId={nextTargetNodeId}\n" +
- $"\tcurTargetNodeId={curTargetNodeId}, curTargetNodeId={curTargetNodeId}\n" +
- $"\tindex={index}");
- }
+ bool debug = GlobalConfig.Instance.Debug.Switches[21]
+ && (GlobalConfig.Instance.Debug.NodeId <= 0
+ || curTargetNodeId == GlobalConfig.Instance.Debug.NodeId)
+ && (GlobalConfig.Instance.Debug.ApiExtVehicleType == ExtVehicleType.None
+ || GlobalConfig.Instance.Debug.ApiExtVehicleType == ExtVehicleType.RoadVehicle)
+ && (GlobalConfig.Instance.Debug.VehicleId == 0
+ || GlobalConfig.Instance.Debug.VehicleId == vehicleId);
+
+ if (debug) {
+ Log._Debug($"CustomCarAI.CustomCalculateSegmentPosition({vehicleId}) called.\n" +
+ $"\tcurPosition.m_segment={curPosition.m_segment}, curPosition.m_offset={curPosition.m_offset}\n" +
+ $"\tnextPosition.m_segment={nextPosition.m_segment}, nextPosition.m_offset={nextPosition.m_offset}\n" +
+ $"\tnextNextPosition.m_segment={nextNextPosition.m_segment}, nextNextPosition.m_offset={nextNextPosition.m_offset}\n" +
+ $"\tcurLaneId={curLaneId}, curOffset={curOffset}\n" +
+ $"\tnextLaneId={nextLaneId}, nextOffset={nextOffset}\n" +
+ $"\tnextSourceNodeId={nextSourceNodeId}, nextTargetNodeId={nextTargetNodeId}\n" +
+ $"\tcurTargetNodeId={curTargetNodeId}, curTargetNodeId={curTargetNodeId}\n" +
+ $"\tindex={index}");
+ }
#endif
- Vehicle.Frame lastFrameData = vehicleData.GetLastFrameData();
- Vector3 lastFrameVehiclePos = lastFrameData.m_position;
- float sqrVelocity = lastFrameData.m_velocity.sqrMagnitude;
- var prevSegmentInfo = netManager.m_segments.m_buffer[nextPosition.m_segment].Info;
- netManager.m_lanes.m_buffer[nextLaneId].CalculatePositionAndDirection(nextOffset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR, out pos, out dir);
-
- float braking = this.m_info.m_braking;
- if ((vehicleData.m_flags & Vehicle.Flags.Emergency2) != (Vehicle.Flags)0) {
- braking *= 2f;
- }
-
- // car position on the Bezier curve of the lane
- var refVehiclePosOnBezier = netManager.m_lanes.m_buffer[curLaneId].CalculatePosition(curOffset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
- //ushort currentSegmentId = netManager.m_lanes.m_buffer[prevLaneID].m_segment;
-
- // this seems to be like the required braking force in order to stop the vehicle within its half length.
- var crazyValue = 0.5f * sqrVelocity / braking + m_info.m_generatedInfo.m_size.z * 0.5f;
- bool withinBrakingDistance = Vector3.Distance(lastFrameVehiclePos, refVehiclePosOnBezier) >= crazyValue - 1f;
-
- if (
- nextSourceNodeId == curTargetNodeId &&
- withinBrakingDistance
- ) {
- // NON-STOCK CODE START (stock code replaced)
- if (
- !VehicleBehaviorManager.Instance.MayChangeSegment(vehicleId, ref vehicleData, sqrVelocity, ref curPosition, ref netManager.m_segments.m_buffer[curPosition.m_segment], curTargetNodeId, curLaneId, ref nextPosition, nextSourceNodeId, ref netManager.m_nodes.m_buffer[nextSourceNodeId], nextLaneId, ref nextNextPosition, nextTargetNodeId)
- ) { // NON-STOCK CODE
- maxSpeed = 0;
- return;
- } else {
- ExtVehicleManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData/*, lastFrameData.m_velocity.magnitude*/);
- }
- // NON-STOCK CODE END
- }
-
- if (prevSegmentInfo.m_lanes != null && prevSegmentInfo.m_lanes.Length > nextPosition.m_lane) {
- // NON-STOCK CODE START
- float laneSpeedLimit = 1f;
-
- if (!Options.customSpeedLimitsEnabled) {
- laneSpeedLimit = prevSegmentInfo.m_lanes[nextPosition.m_lane].m_speedLimit;
- } else {
- laneSpeedLimit = Constants.ManagerFactory.SpeedLimitManager.GetLockFreeGameSpeedLimit(nextPosition.m_segment, nextPosition.m_lane, nextLaneId, prevSegmentInfo.m_lanes[nextPosition.m_lane]);
- }
-
- // NON-STOCK CODE END
- maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, laneSpeedLimit, netManager.m_lanes.m_buffer[nextLaneId].m_curve);
- } else {
- maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f);
- }
-
- // NON-STOCK CODE START (stock code replaced)
- maxSpeed = Constants.ManagerFactory.VehicleBehaviorManager.CalcMaxSpeed(vehicleId, ref Constants.ManagerFactory.ExtVehicleManager.ExtVehicles[vehicleId], this.m_info, nextPosition, ref netManager.m_segments.m_buffer[nextPosition.m_segment], pos, maxSpeed, false);
- // NON-STOCK CODE END
- }
-
- [RedirectMethod]
- public void CustomCalculateSegmentPosition(ushort vehicleId, ref Vehicle vehicleData, PathUnit.Position position, uint laneId, byte offset, out Vector3 pos, out Vector3 dir, out float maxSpeed) {
- var netManager = Singleton.instance;
- var segmentInfo = netManager.m_segments.m_buffer[position.m_segment].Info;
- netManager.m_lanes.m_buffer[laneId].CalculatePositionAndDirection(offset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR, out pos, out dir);
-
- if (segmentInfo.m_lanes != null && segmentInfo.m_lanes.Length > position.m_lane) {
- // NON-STOCK CODE START
- float laneSpeedLimit = 1f;
- if (!Options.customSpeedLimitsEnabled) {
- laneSpeedLimit = segmentInfo.m_lanes[position.m_lane].m_speedLimit;
- } else {
- laneSpeedLimit = Constants.ManagerFactory.SpeedLimitManager.GetLockFreeGameSpeedLimit(position.m_segment, position.m_lane, laneId, segmentInfo.m_lanes[position.m_lane]);
- }
- // NON-STOCK CODE END
- maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, laneSpeedLimit, netManager.m_lanes.m_buffer[laneId].m_curve);
- } else {
- maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f);
- }
-
- // NON-STOCK CODE START
- maxSpeed = VehicleBehaviorManager.Instance.CalcMaxSpeed(vehicleId, ref Constants.ManagerFactory.ExtVehicleManager.ExtVehicles[vehicleId], this.m_info, position, ref netManager.m_segments.m_buffer[position.m_segment], pos, maxSpeed, false);
- // NON-STOCK CODE END
- }
-
- [RedirectMethod]
- public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
+ Vehicle.Frame lastFrameData = vehicleData.GetLastFrameData();
+ Vector3 lastFrameVehiclePos = lastFrameData.m_position;
+ float sqrVelocity = lastFrameData.m_velocity.sqrMagnitude;
+ var prevSegmentInfo = netManager.m_segments.m_buffer[nextPosition.m_segment].Info;
+ netManager.m_lanes.m_buffer[nextLaneId].CalculatePositionAndDirection(nextOffset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR, out pos, out dir);
+
+ float braking = this.m_info.m_braking;
+ if ((vehicleData.m_flags & Vehicle.Flags.Emergency2) != (Vehicle.Flags)0) {
+ braking *= 2f;
+ }
+
+ // car position on the Bezier curve of the lane
+ var refVehiclePosOnBezier = netManager.m_lanes.m_buffer[curLaneId].CalculatePosition(curOffset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+ //ushort currentSegmentId = netManager.m_lanes.m_buffer[prevLaneID].m_segment;
+
+ // this seems to be like the required braking force in order to stop the vehicle within its half length.
+ var crazyValue = 0.5f * sqrVelocity / braking + m_info.m_generatedInfo.m_size.z * 0.5f;
+ bool withinBrakingDistance = Vector3.Distance(lastFrameVehiclePos, refVehiclePosOnBezier) >= crazyValue - 1f;
+
+ if (
+ nextSourceNodeId == curTargetNodeId &&
+ withinBrakingDistance
+ ) {
+ // NON-STOCK CODE START (stock code replaced)
+ if (
+ !VehicleBehaviorManager.Instance.MayChangeSegment(vehicleId, ref vehicleData, sqrVelocity, ref curPosition, ref netManager.m_segments.m_buffer[curPosition.m_segment], curTargetNodeId, curLaneId, ref nextPosition, nextSourceNodeId, ref netManager.m_nodes.m_buffer[nextSourceNodeId], nextLaneId, ref nextNextPosition, nextTargetNodeId)
+ ) { // NON-STOCK CODE
+ maxSpeed = 0;
+ return;
+ } else {
+ ExtVehicleManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData/*, lastFrameData.m_velocity.magnitude*/);
+ }
+ // NON-STOCK CODE END
+ }
+
+ if (prevSegmentInfo.m_lanes != null && prevSegmentInfo.m_lanes.Length > nextPosition.m_lane) {
+ // NON-STOCK CODE START
+ float laneSpeedLimit = 1f;
+
+ if (!Options.customSpeedLimitsEnabled) {
+ laneSpeedLimit = prevSegmentInfo.m_lanes[nextPosition.m_lane].m_speedLimit;
+ } else {
+ laneSpeedLimit = Constants.ManagerFactory.SpeedLimitManager.GetLockFreeGameSpeedLimit(nextPosition.m_segment, nextPosition.m_lane, nextLaneId, prevSegmentInfo.m_lanes[nextPosition.m_lane]);
+ }
+
+ // NON-STOCK CODE END
+ maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, laneSpeedLimit, netManager.m_lanes.m_buffer[nextLaneId].m_curve);
+ } else {
+ maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f);
+ }
+
+ // NON-STOCK CODE START (stock code replaced)
+ maxSpeed = Constants.ManagerFactory.VehicleBehaviorManager.CalcMaxSpeed(vehicleId, ref Constants.ManagerFactory.ExtVehicleManager.ExtVehicles[vehicleId], this.m_info, nextPosition, ref netManager.m_segments.m_buffer[nextPosition.m_segment], pos, maxSpeed, false);
+ // NON-STOCK CODE END
+ }
+
+ [RedirectMethod]
+ public void CustomCalculateSegmentPosition(ushort vehicleId, ref Vehicle vehicleData, PathUnit.Position position, uint laneId, byte offset, out Vector3 pos, out Vector3 dir, out float maxSpeed) {
+ var netManager = Singleton.instance;
+ var segmentInfo = netManager.m_segments.m_buffer[position.m_segment].Info;
+ netManager.m_lanes.m_buffer[laneId].CalculatePositionAndDirection(offset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR, out pos, out dir);
+
+ if (segmentInfo.m_lanes != null && segmentInfo.m_lanes.Length > position.m_lane) {
+ // NON-STOCK CODE START
+ float laneSpeedLimit = 1f;
+ if (!Options.customSpeedLimitsEnabled) {
+ laneSpeedLimit = segmentInfo.m_lanes[position.m_lane].m_speedLimit;
+ } else {
+ laneSpeedLimit = Constants.ManagerFactory.SpeedLimitManager.GetLockFreeGameSpeedLimit(position.m_segment, position.m_lane, laneId, segmentInfo.m_lanes[position.m_lane]);
+ }
+ // NON-STOCK CODE END
+ maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, laneSpeedLimit, netManager.m_lanes.m_buffer[laneId].m_curve);
+ } else {
+ maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f);
+ }
+
+ // NON-STOCK CODE START
+ maxSpeed = VehicleBehaviorManager.Instance.CalcMaxSpeed(vehicleId, ref Constants.ManagerFactory.ExtVehicleManager.ExtVehicles[vehicleId], this.m_info, position, ref netManager.m_segments.m_buffer[position.m_segment], pos, maxSpeed, false);
+ // NON-STOCK CODE END
+ }
+
+ [RedirectMethod]
+ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
#if DEBUG
- bool vehDebug = GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleID;
- bool debug = GlobalConfig.Instance.Debug.Switches[2] && vehDebug;
- bool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && vehDebug;
+ bool vehDebug = GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleID;
+ bool debug = GlobalConfig.Instance.Debug.Switches[2] && vehDebug;
+ bool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && vehDebug;
- if (debug)
- Log.Warning($"CustomCarAI.CustomStartPathFind({vehicleID}): called for vehicle {vehicleID}, startPos={startPos}, endPos={endPos}, startBothWays={startBothWays}, endBothWays={endBothWays}, undergroundTarget={undergroundTarget}");
+ if (debug)
+ Log.Warning($"CustomCarAI.CustomStartPathFind({vehicleID}): called for vehicle {vehicleID}, startPos={startPos}, endPos={endPos}, startBothWays={startBothWays}, endBothWays={endBothWays}, undergroundTarget={undergroundTarget}");
#endif
- ExtVehicleType vehicleType = ExtVehicleType.None;
+ ExtVehicleType vehicleType = ExtVehicleType.None;
#if BENCHMARK
using (var bm = new Benchmark(null, "OnStartPathFind")) {
#endif
- vehicleType = ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, null);
- if (vehicleType == ExtVehicleType.None) {
+ vehicleType = ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, null);
+ if (vehicleType == ExtVehicleType.None) {
#if DEBUG
- Log.Warning($"CustomCarAI.CustomStartPathFind({vehicleID}): Vehicle {vehicleID} does not have a valid vehicle type!");
+ Log.Warning($"CustomCarAI.CustomStartPathFind({vehicleID}): Vehicle {vehicleID} does not have a valid vehicle type!");
#endif
- vehicleType = ExtVehicleType.RoadVehicle;
- }
+ vehicleType = ExtVehicleType.RoadVehicle;
+ }
#if BENCHMARK
}
#endif
- VehicleInfo info = this.m_info;
- bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
- PathUnit.Position startPosA;
- PathUnit.Position startPosB;
- float startDistSqrA;
- float startDistSqrB;
- PathUnit.Position endPosA;
- PathUnit.Position endPosB;
- float endDistSqrA;
- float endDistSqrB;
- if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB) &&
- CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB)) {
- if (!startBothWays || startDistSqrA < 10f) {
- startPosB = default(PathUnit.Position);
- }
- if (!endBothWays || endDistSqrA < 10f) {
- endPosB = default(PathUnit.Position);
- }
- uint path;
-
- // NON-STOCK CODE START
- PathCreationArgs args;
- args.extPathType = ExtPathType.None;
- args.extVehicleType = vehicleType;
- args.vehicleId = vehicleID;
- args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- args.buildIndex = Singleton.instance.m_currentBuildIndex;
- args.startPosA = startPosA;
- args.startPosB = startPosB;
- args.endPosA = endPosA;
- args.endPosB = endPosB;
- args.vehiclePosition = default(PathUnit.Position);
- args.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;
- args.vehicleTypes = info.m_vehicleType;
- args.maxLength = 20000f;
- args.isHeavyVehicle = this.IsHeavyVehicle();
- args.hasCombustionEngine = this.CombustionEngine();
- args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
- args.ignoreFlooded = false;
- args.ignoreCosts = false;
- args.randomParking = false;
- args.stablePath = false;
- args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
-
- if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
+ VehicleInfo info = this.m_info;
+ bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
+ PathUnit.Position startPosA;
+ PathUnit.Position startPosB;
+ float startDistSqrA;
+ float startDistSqrB;
+ PathUnit.Position endPosA;
+ PathUnit.Position endPosB;
+ float endDistSqrA;
+ float endDistSqrB;
+ if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB) &&
+ CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB)) {
+ if (!startBothWays || startDistSqrA < 10f) {
+ startPosB = default(PathUnit.Position);
+ }
+ if (!endBothWays || endDistSqrA < 10f) {
+ endPosB = default(PathUnit.Position);
+ }
+ uint path;
+
+ // NON-STOCK CODE START
+ PathCreationArgs args;
+ args.extPathType = ExtPathType.None;
+ args.extVehicleType = vehicleType;
+ args.vehicleId = vehicleID;
+ args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ args.buildIndex = Singleton.instance.m_currentBuildIndex;
+ args.startPosA = startPosA;
+ args.startPosB = startPosB;
+ args.endPosA = endPosA;
+ args.endPosB = endPosB;
+ args.vehiclePosition = default(PathUnit.Position);
+ args.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;
+ args.vehicleTypes = info.m_vehicleType;
+ args.maxLength = 20000f;
+ args.isHeavyVehicle = this.IsHeavyVehicle();
+ args.hasCombustionEngine = this.CombustionEngine();
+ args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
+ args.ignoreFlooded = false;
+ args.ignoreCosts = false;
+ args.randomParking = false;
+ args.stablePath = false;
+ args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+
+ if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
#if DEBUG
- if (debug)
- Log._Debug($"CustomCarAI.CustomStartPathFind({vehicleID}): Path-finding starts for vehicle {vehicleID}, path={path}, extVehicleType={vehicleType}, startPosA.segment={startPosA.m_segment}, startPosA.lane={startPosA.m_lane}, info.m_vehicleType={info.m_vehicleType}, endPosA.segment={endPosA.m_segment}, endPosA.lane={endPosA.m_lane}");
+ if (debug)
+ Log._Debug($"CustomCarAI.CustomStartPathFind({vehicleID}): Path-finding starts for vehicle {vehicleID}, path={path}, extVehicleType={vehicleType}, startPosA.segment={startPosA.m_segment}, startPosA.lane={startPosA.m_lane}, info.m_vehicleType={info.m_vehicleType}, endPosA.segment={endPosA.m_segment}, endPosA.lane={endPosA.m_lane}");
#endif
- // NON-STOCK CODE END
- if (vehicleData.m_path != 0u) {
- Singleton.instance.ReleasePath(vehicleData.m_path);
- }
- vehicleData.m_path = path;
- vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
- return true;
- }
- }
- return false;
- }
-
- [RedirectMethod]
- public static ushort CustomCheckOtherVehicle(ushort vehicleID, ref Vehicle vehicleData, ref Vehicle.Frame frameData, ref float maxSpeed, ref bool blocked, ref Vector3 collisionPush, float maxBraking, ushort otherID, ref Vehicle otherData, Vector3 min, Vector3 max, int lodPhysics) {
- if (otherID == vehicleID || vehicleData.m_leadingVehicle == otherID || vehicleData.m_trailingVehicle == otherID) {
- return otherData.m_nextGridVehicle;
- }
-
- VehicleInfo info = otherData.Info;
- if (info.m_vehicleType == VehicleInfo.VehicleType.Bicycle) {
- return otherData.m_nextGridVehicle;
- }
-
- if (((vehicleData.m_flags | otherData.m_flags) & Vehicle.Flags.Transition) == (Vehicle.Flags)0 && (vehicleData.m_flags & Vehicle.Flags.Underground) != (otherData.m_flags & Vehicle.Flags.Underground)) {
- return otherData.m_nextGridVehicle;
- }
+ // NON-STOCK CODE END
+ if (vehicleData.m_path != 0u) {
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ }
+ vehicleData.m_path = path;
+ vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ [RedirectMethod]
+ public static ushort CustomCheckOtherVehicle(ushort vehicleID, ref Vehicle vehicleData, ref Vehicle.Frame frameData, ref float maxSpeed, ref bool blocked, ref Vector3 collisionPush, float maxBraking, ushort otherID, ref Vehicle otherData, Vector3 min, Vector3 max, int lodPhysics) {
+ if (otherID == vehicleID || vehicleData.m_leadingVehicle == otherID || vehicleData.m_trailingVehicle == otherID) {
+ return otherData.m_nextGridVehicle;
+ }
+
+ VehicleInfo info = otherData.Info;
+ if (info.m_vehicleType == VehicleInfo.VehicleType.Bicycle) {
+ return otherData.m_nextGridVehicle;
+ }
+
+ if (((vehicleData.m_flags | otherData.m_flags) & Vehicle.Flags.Transition) == (Vehicle.Flags)0 && (vehicleData.m_flags & Vehicle.Flags.Underground) != (otherData.m_flags & Vehicle.Flags.Underground)) {
+ return otherData.m_nextGridVehicle;
+ }
#if DEBUG
- bool debug = GlobalConfig.Instance.Debug.Switches[24] &&
- (GlobalConfig.Instance.Debug.ExtVehicleType == ExtVehicleType.None || GlobalConfig.Instance.Debug.ExtVehicleType == ExtVehicleType.RoadVehicle) &&
- (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleID);
-
- if (debug) {
- Log._Debug($"CustomCarAI.CustomCheckOtherVehicle({vehicleID}, {otherID}) called.");
- }
+ bool debug = GlobalConfig.Instance.Debug.Switches[24] &&
+ (GlobalConfig.Instance.Debug.ApiExtVehicleType == ExtVehicleType.None
+ || GlobalConfig.Instance.Debug.ApiExtVehicleType == ExtVehicleType.RoadVehicle)
+ && (GlobalConfig.Instance.Debug.VehicleId == 0
+ || GlobalConfig.Instance.Debug.VehicleId == vehicleID);
+
+ if (debug) {
+ Log._Debug($"CustomCarAI.CustomCheckOtherVehicle({vehicleID}, {otherID}) called.");
+ }
#endif
- Vector3 otherSegMin;
- Vector3 otherSegMax;
- if (lodPhysics >= 2) {
- otherSegMin = otherData.m_segment.Min();
- otherSegMax = otherData.m_segment.Max();
- } else {
- otherSegMin = Vector3.Min(otherData.m_segment.Min(), otherData.m_targetPos3);
- otherSegMax = Vector3.Max(otherData.m_segment.Max(), otherData.m_targetPos3);
- }
-
- if (
- min.x >= otherSegMax.x + 2f &&
- min.y >= otherSegMax.y + 2f &&
- min.z >= otherSegMax.z + 2f &&
- otherSegMin.x >= max.x + 2f &&
- otherSegMin.y >= max.y + 2f &&
- otherSegMin.z >= max.z + 2f
- ) {
- return otherData.m_nextGridVehicle;
- }
-
- Vehicle.Frame otherFrameData = otherData.GetLastFrameData();
- if (lodPhysics < 2) {
- float u = default(float);
- float v = default(float);
- float segSqrDist = vehicleData.m_segment.DistanceSqr(otherData.m_segment, out u, out v);
- if (segSqrDist < 4f) {
- Vector3 vehPos = vehicleData.m_segment.Position(0.5f);
- Vector3 otherPos = otherData.m_segment.Position(0.5f);
- Vector3 vehBounds = vehicleData.m_segment.b - vehicleData.m_segment.a;
- if (Vector3.Dot(vehBounds, vehPos - otherPos) < 0f) {
- collisionPush -= vehBounds.normalized * (0.1f - segSqrDist * 0.025f);
- } else {
- collisionPush += vehBounds.normalized * (0.1f - segSqrDist * 0.025f);
- }
- blocked = true;
- }
- }
-
- float vehVelocity = frameData.m_velocity.magnitude + 0.01f;
- float otherVehVelocity = otherFrameData.m_velocity.magnitude;
- float otherBreakingDist = otherVehVelocity * (0.5f + 0.5f * otherVehVelocity / info.m_braking) + Mathf.Min(1f, otherVehVelocity);
- otherVehVelocity += 0.01f;
- float prevLength = 0f;
- Vector3 prevTargetPos = vehicleData.m_segment.b;
- Vector3 prevBounds = vehicleData.m_segment.b - vehicleData.m_segment.a;
- int startI = (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Tram) ? 1 : 0;
- for (int i = startI; i < 4; i++) {
- Vector3 targetPos = vehicleData.GetTargetPos(i);
- Vector3 targetPosDiff = targetPos - prevTargetPos;
- if (Vector3.Dot(prevBounds, targetPosDiff) > 0f) {
- float targetPosDiffLen = targetPosDiff.magnitude;
- Segment3 curSegment = new Segment3(prevTargetPos, targetPos);
- min = curSegment.Min();
- max = curSegment.Max();
- curSegment.a.y *= 0.5f;
- curSegment.b.y *= 0.5f;
-
- if (
- targetPosDiffLen > 0.01f &&
- min.x < otherSegMax.x + 2f &&
- min.y < otherSegMax.y + 2f &&
- min.z < otherSegMax.z + 2f &&
- otherSegMin.x < max.x + 2f &&
- otherSegMin.y < max.y + 2f &&
- otherSegMin.z < max.z + 2f
- ) {
- Vector3 otherVehFrontPos = otherData.m_segment.a;
- otherVehFrontPos.y *= 0.5f;
- float u = default(float);
- if (curSegment.DistanceSqr(otherVehFrontPos, out u) < 4f) {
- float otherCosAngleToTargetPosDiff = Vector3.Dot(otherFrameData.m_velocity, targetPosDiff) / targetPosDiffLen;
- float uDist = prevLength + targetPosDiffLen * u;
- if (uDist >= 0.01f) {
- uDist -= otherCosAngleToTargetPosDiff + 3f;
- float speed = Mathf.Max(0f, CustomCarAI.CalculateMaxSpeed(uDist, otherCosAngleToTargetPosDiff, maxBraking));
- if (speed < 0.01f) {
- blocked = true;
- }
-
- Vector3 normOtherDir = Vector3.Normalize((Vector3)otherData.m_targetPos0 - otherData.m_segment.a);
- float blockFactor = 1.2f - 1f / ((float)(int)vehicleData.m_blockCounter * 0.02f + 0.5f);
- if (Vector3.Dot(targetPosDiff, normOtherDir) > blockFactor * targetPosDiffLen) {
- maxSpeed = Mathf.Min(maxSpeed, speed);
- }
- }
- break;
- }
-
- if (lodPhysics < 2) {
- float totalDist = 0f;
- float otherBreakDist = otherBreakingDist;
- Vector3 otherFrontPos = otherData.m_segment.b;
- Vector3 otherBounds = otherData.m_segment.b - otherData.m_segment.a;
- int startOtherTargetPosIndex = (info.m_vehicleType == VehicleInfo.VehicleType.Tram) ? 1 : 0;
- bool exitTargetPosLoop = false;
- int otherTargetPosIndex = startOtherTargetPosIndex;
- while (otherTargetPosIndex < 4 && otherBreakDist > 0.1f) {
- Vector3 otherTargetPos;
- if (otherData.m_leadingVehicle == 0) {
- otherTargetPos = otherData.GetTargetPos(otherTargetPosIndex);
- } else {
- if (otherTargetPosIndex != startOtherTargetPosIndex) {
- break;
- }
- otherTargetPos = Singleton.instance.m_vehicles.m_buffer[otherData.m_leadingVehicle].m_segment.b;
- }
-
- Vector3 minBreakPos = Vector3.ClampMagnitude(otherTargetPos - otherFrontPos, otherBreakDist);
- if (Vector3.Dot(otherBounds, minBreakPos) > 0f) {
- otherTargetPos = otherFrontPos + minBreakPos;
- float breakPosDist = minBreakPos.magnitude;
- otherBreakDist -= breakPosDist;
- Segment3 otherVehNextSegment = new Segment3(otherFrontPos, otherTargetPos);
- otherVehNextSegment.a.y *= 0.5f;
- otherVehNextSegment.b.y *= 0.5f;
- if (breakPosDist > 0.01f) {
- float otherVehNextSegU = default(float);
- float otherVehNextSegV = default(float);
- float otherVehNextSegmentDistToCurSegment = (otherID >= vehicleID)
- ? curSegment.DistanceSqr(otherVehNextSegment, out otherVehNextSegU, out otherVehNextSegV)
- : otherVehNextSegment.DistanceSqr(curSegment, out otherVehNextSegV, out otherVehNextSegU);
- if (otherVehNextSegmentDistToCurSegment < 4f) {
- float uDist = prevLength + targetPosDiffLen * otherVehNextSegU;
- float vDist = totalDist + breakPosDist * otherVehNextSegV + 0.1f;
- if (uDist >= 0.01f && uDist * otherVehVelocity > vDist * vehVelocity) {
- float otherCosAngleToTargetPosDiff = Vector3.Dot(otherFrameData.m_velocity, targetPosDiff) / targetPosDiffLen;
- if (uDist >= 0.01f) {
- uDist -= otherCosAngleToTargetPosDiff + 1f + otherData.Info.m_generatedInfo.m_size.z;
- float speed = Mathf.Max(0f, CustomCarAI.CalculateMaxSpeed(uDist, otherCosAngleToTargetPosDiff, maxBraking));
- if (speed < 0.01f) {
- blocked = true;
- }
- maxSpeed = Mathf.Min(maxSpeed, speed);
- }
- }
- exitTargetPosLoop = true;
- break;
- }
- }
- otherBounds = minBreakPos;
- totalDist += breakPosDist;
- otherFrontPos = otherTargetPos;
- }
- otherTargetPosIndex++;
- }
- if (exitTargetPosLoop) {
- break;
- }
- }
- }
- prevBounds = targetPosDiff;
- prevLength += targetPosDiffLen;
- prevTargetPos = targetPos;
- }
- }
-
- return otherData.m_nextGridVehicle;
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)]
- [RedirectReverse]
- private static bool CheckOverlap(Segment3 segment, ushort ignoreVehicle, float maxVelocity) {
- Log.Error("CustomCarAI.CheckOverlap called");
- return false;
- }
-
- /*[MethodImpl(MethodImplOptions.NoInlining)]
- [RedirectReverse]
- private static ushort CheckOtherVehicle(ushort vehicleID, ref Vehicle vehicleData, ref Vehicle.Frame frameData, ref float maxSpeed, ref bool blocked, ref Vector3 collisionPush, float maxBraking, ushort otherID, ref Vehicle otherData, Vector3 min, Vector3 max, int lodPhysics) {
- Log.Error("CustomCarAI.CheckOtherVehicle called");
- return 0;
- }*/
-
- [MethodImpl(MethodImplOptions.NoInlining)]
- [RedirectReverse]
- private static ushort CheckCitizen(ushort vehicleID, ref Vehicle vehicleData, Segment3 segment, float lastLen, float nextLen, ref float maxSpeed, ref bool blocked, float maxBraking, ushort otherID, ref CitizenInstance otherData, Vector3 min, Vector3 max) {
- Log.Error("CustomCarAI.CheckCitizen called");
- return 0;
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)]
- [RedirectReverse]
- private static float CalculateMaxSpeed(float targetDistance, float targetSpeed, float maxBraking) {
- Log.Error("CustomCarAI.CalculateMaxSpeed called");
- return 0f;
- }
- }
+ Vector3 otherSegMin;
+ Vector3 otherSegMax;
+ if (lodPhysics >= 2) {
+ otherSegMin = otherData.m_segment.Min();
+ otherSegMax = otherData.m_segment.Max();
+ } else {
+ otherSegMin = Vector3.Min(otherData.m_segment.Min(), otherData.m_targetPos3);
+ otherSegMax = Vector3.Max(otherData.m_segment.Max(), otherData.m_targetPos3);
+ }
+
+ if (
+ min.x >= otherSegMax.x + 2f &&
+ min.y >= otherSegMax.y + 2f &&
+ min.z >= otherSegMax.z + 2f &&
+ otherSegMin.x >= max.x + 2f &&
+ otherSegMin.y >= max.y + 2f &&
+ otherSegMin.z >= max.z + 2f
+ ) {
+ return otherData.m_nextGridVehicle;
+ }
+
+ Vehicle.Frame otherFrameData = otherData.GetLastFrameData();
+ if (lodPhysics < 2) {
+ float u = default(float);
+ float v = default(float);
+ float segSqrDist = vehicleData.m_segment.DistanceSqr(otherData.m_segment, out u, out v);
+ if (segSqrDist < 4f) {
+ Vector3 vehPos = vehicleData.m_segment.Position(0.5f);
+ Vector3 otherPos = otherData.m_segment.Position(0.5f);
+ Vector3 vehBounds = vehicleData.m_segment.b - vehicleData.m_segment.a;
+ if (Vector3.Dot(vehBounds, vehPos - otherPos) < 0f) {
+ collisionPush -= vehBounds.normalized * (0.1f - segSqrDist * 0.025f);
+ } else {
+ collisionPush += vehBounds.normalized * (0.1f - segSqrDist * 0.025f);
+ }
+ blocked = true;
+ }
+ }
+
+ float vehVelocity = frameData.m_velocity.magnitude + 0.01f;
+ float otherVehVelocity = otherFrameData.m_velocity.magnitude;
+ float otherBreakingDist = otherVehVelocity * (0.5f + 0.5f * otherVehVelocity / info.m_braking) + Mathf.Min(1f, otherVehVelocity);
+ otherVehVelocity += 0.01f;
+ float prevLength = 0f;
+ Vector3 prevTargetPos = vehicleData.m_segment.b;
+ Vector3 prevBounds = vehicleData.m_segment.b - vehicleData.m_segment.a;
+ int startI = (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Tram) ? 1 : 0;
+ for (int i = startI; i < 4; i++) {
+ Vector3 targetPos = vehicleData.GetTargetPos(i);
+ Vector3 targetPosDiff = targetPos - prevTargetPos;
+ if (Vector3.Dot(prevBounds, targetPosDiff) > 0f) {
+ float targetPosDiffLen = targetPosDiff.magnitude;
+ Segment3 curSegment = new Segment3(prevTargetPos, targetPos);
+ min = curSegment.Min();
+ max = curSegment.Max();
+ curSegment.a.y *= 0.5f;
+ curSegment.b.y *= 0.5f;
+
+ if (
+ targetPosDiffLen > 0.01f &&
+ min.x < otherSegMax.x + 2f &&
+ min.y < otherSegMax.y + 2f &&
+ min.z < otherSegMax.z + 2f &&
+ otherSegMin.x < max.x + 2f &&
+ otherSegMin.y < max.y + 2f &&
+ otherSegMin.z < max.z + 2f
+ ) {
+ Vector3 otherVehFrontPos = otherData.m_segment.a;
+ otherVehFrontPos.y *= 0.5f;
+ float u = default(float);
+ if (curSegment.DistanceSqr(otherVehFrontPos, out u) < 4f) {
+ float otherCosAngleToTargetPosDiff = Vector3.Dot(otherFrameData.m_velocity, targetPosDiff) / targetPosDiffLen;
+ float uDist = prevLength + targetPosDiffLen * u;
+ if (uDist >= 0.01f) {
+ uDist -= otherCosAngleToTargetPosDiff + 3f;
+ float speed = Mathf.Max(0f, CustomCarAI.CalculateMaxSpeed(uDist, otherCosAngleToTargetPosDiff, maxBraking));
+ if (speed < 0.01f) {
+ blocked = true;
+ }
+
+ Vector3 normOtherDir = Vector3.Normalize((Vector3)otherData.m_targetPos0 - otherData.m_segment.a);
+ float blockFactor = 1.2f - 1f / ((float)(int)vehicleData.m_blockCounter * 0.02f + 0.5f);
+ if (Vector3.Dot(targetPosDiff, normOtherDir) > blockFactor * targetPosDiffLen) {
+ maxSpeed = Mathf.Min(maxSpeed, speed);
+ }
+ }
+ break;
+ }
+
+ if (lodPhysics < 2) {
+ float totalDist = 0f;
+ float otherBreakDist = otherBreakingDist;
+ Vector3 otherFrontPos = otherData.m_segment.b;
+ Vector3 otherBounds = otherData.m_segment.b - otherData.m_segment.a;
+ int startOtherTargetPosIndex = (info.m_vehicleType == VehicleInfo.VehicleType.Tram) ? 1 : 0;
+ bool exitTargetPosLoop = false;
+ int otherTargetPosIndex = startOtherTargetPosIndex;
+ while (otherTargetPosIndex < 4 && otherBreakDist > 0.1f) {
+ Vector3 otherTargetPos;
+ if (otherData.m_leadingVehicle == 0) {
+ otherTargetPos = otherData.GetTargetPos(otherTargetPosIndex);
+ } else {
+ if (otherTargetPosIndex != startOtherTargetPosIndex) {
+ break;
+ }
+ otherTargetPos = Singleton.instance.m_vehicles.m_buffer[otherData.m_leadingVehicle].m_segment.b;
+ }
+
+ Vector3 minBreakPos = Vector3.ClampMagnitude(otherTargetPos - otherFrontPos, otherBreakDist);
+ if (Vector3.Dot(otherBounds, minBreakPos) > 0f) {
+ otherTargetPos = otherFrontPos + minBreakPos;
+ float breakPosDist = minBreakPos.magnitude;
+ otherBreakDist -= breakPosDist;
+ Segment3 otherVehNextSegment = new Segment3(otherFrontPos, otherTargetPos);
+ otherVehNextSegment.a.y *= 0.5f;
+ otherVehNextSegment.b.y *= 0.5f;
+ if (breakPosDist > 0.01f) {
+ float otherVehNextSegU = default(float);
+ float otherVehNextSegV = default(float);
+ float otherVehNextSegmentDistToCurSegment = (otherID >= vehicleID)
+ ? curSegment.DistanceSqr(otherVehNextSegment, out otherVehNextSegU, out otherVehNextSegV)
+ : otherVehNextSegment.DistanceSqr(curSegment, out otherVehNextSegV, out otherVehNextSegU);
+ if (otherVehNextSegmentDistToCurSegment < 4f) {
+ float uDist = prevLength + targetPosDiffLen * otherVehNextSegU;
+ float vDist = totalDist + breakPosDist * otherVehNextSegV + 0.1f;
+ if (uDist >= 0.01f && uDist * otherVehVelocity > vDist * vehVelocity) {
+ float otherCosAngleToTargetPosDiff = Vector3.Dot(otherFrameData.m_velocity, targetPosDiff) / targetPosDiffLen;
+ if (uDist >= 0.01f) {
+ uDist -= otherCosAngleToTargetPosDiff + 1f + otherData.Info.m_generatedInfo.m_size.z;
+ float speed = Mathf.Max(0f, CustomCarAI.CalculateMaxSpeed(uDist, otherCosAngleToTargetPosDiff, maxBraking));
+ if (speed < 0.01f) {
+ blocked = true;
+ }
+ maxSpeed = Mathf.Min(maxSpeed, speed);
+ }
+ }
+ exitTargetPosLoop = true;
+ break;
+ }
+ }
+ otherBounds = minBreakPos;
+ totalDist += breakPosDist;
+ otherFrontPos = otherTargetPos;
+ }
+ otherTargetPosIndex++;
+ }
+ if (exitTargetPosLoop) {
+ break;
+ }
+ }
+ }
+ prevBounds = targetPosDiff;
+ prevLength += targetPosDiffLen;
+ prevTargetPos = targetPos;
+ }
+ }
+
+ return otherData.m_nextGridVehicle;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ [RedirectReverse]
+ private static bool CheckOverlap(Segment3 segment, ushort ignoreVehicle, float maxVelocity) {
+ Log.Error("CustomCarAI.CheckOverlap called");
+ return false;
+ }
+
+ /*[MethodImpl(MethodImplOptions.NoInlining)]
+ [RedirectReverse]
+ private static ushort CheckOtherVehicle(ushort vehicleID, ref Vehicle vehicleData, ref Vehicle.Frame frameData, ref float maxSpeed, ref bool blocked, ref Vector3 collisionPush, float maxBraking, ushort otherID, ref Vehicle otherData, Vector3 min, Vector3 max, int lodPhysics) {
+ Log.Error("CustomCarAI.CheckOtherVehicle called");
+ return 0;
+ }*/
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ [RedirectReverse]
+ private static ushort CheckCitizen(ushort vehicleID, ref Vehicle vehicleData, Segment3 segment, float lastLen, float nextLen, ref float maxSpeed, ref bool blocked, float maxBraking, ushort otherID, ref CitizenInstance otherData, Vector3 min, Vector3 max) {
+ Log.Error("CustomCarAI.CheckCitizen called");
+ return 0;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ [RedirectReverse]
+ private static float CalculateMaxSpeed(float targetDistance, float targetSpeed, float maxBraking) {
+ Log.Error("CustomCarAI.CalculateMaxSpeed called");
+ return 0f;
+ }
+ }
}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/AI/CustomCargoTruckAI.cs b/TLM/TLM/Custom/AI/CustomCargoTruckAI.cs
index 0e5fa8832..7f8864e07 100644
--- a/TLM/TLM/Custom/AI/CustomCargoTruckAI.cs
+++ b/TLM/TLM/Custom/AI/CustomCargoTruckAI.cs
@@ -1,149 +1,143 @@
-using System;
-using ColossalFramework;
-using UnityEngine;
-using TrafficManager.State;
-using TrafficManager.Geometry;
-using TrafficManager.Custom.PathFinding;
-using TrafficManager.Traffic;
-using TrafficManager.Manager;
-using CSUtil.Commons;
-using TrafficManager.Manager.Impl;
-using TrafficManager.Traffic.Data;
-using CSUtil.Commons.Benchmark;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
-using TrafficManager.Traffic.Enums;
-using TrafficManager.RedirectionFramework.Attributes;
-using System.Runtime.CompilerServices;
-
namespace TrafficManager.Custom.AI {
- [TargetType(typeof(CargoTruckAI))]
- public class CustomCargoTruckAI : CarAI {
- [RedirectMethod]
- public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {
- if ((vehicleData.m_flags & Vehicle.Flags.Congestion) != 0 && VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData)) {
- Singleton.instance.ReleaseVehicle(vehicleId);
- return;
- }
+ using System.Runtime.CompilerServices;
+ using API.Traffic.Data;
+ using API.Traffic.Enums;
+ using ColossalFramework;
+ using CSUtil.Commons;
+ using Custom.PathFinding;
+ using Manager.Impl;
+ using RedirectionFramework.Attributes;
+ using Traffic.Data;
+ using UnityEngine;
+
+ [TargetType(typeof(CargoTruckAI))]
+ public class CustomCargoTruckAI : CarAI {
+ [RedirectMethod]
+ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {
+ if ((vehicleData.m_flags & Vehicle.Flags.Congestion) != 0 && VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData)) {
+ Singleton.instance.ReleaseVehicle(vehicleId);
+ return;
+ }
- if ((vehicleData.m_flags & Vehicle.Flags.WaitingTarget) != 0 && (vehicleData.m_waitCounter += 1) > 20) {
- RemoveOffers(vehicleId, ref vehicleData);
- vehicleData.m_flags &= ~Vehicle.Flags.WaitingTarget;
- vehicleData.m_flags |= Vehicle.Flags.GoingBack;
- vehicleData.m_waitCounter = 0;
- if (!StartPathFind(vehicleId, ref vehicleData)) {
- vehicleData.Unspawn(vehicleId);
- }
- }
+ if ((vehicleData.m_flags & Vehicle.Flags.WaitingTarget) != 0 && (vehicleData.m_waitCounter += 1) > 20) {
+ RemoveOffers(vehicleId, ref vehicleData);
+ vehicleData.m_flags &= ~Vehicle.Flags.WaitingTarget;
+ vehicleData.m_flags |= Vehicle.Flags.GoingBack;
+ vehicleData.m_waitCounter = 0;
+ if (!StartPathFind(vehicleId, ref vehicleData)) {
+ vehicleData.Unspawn(vehicleId);
+ }
+ }
- base.SimulationStep(vehicleId, ref vehicleData, physicsLodRefPos);
- }
+ base.SimulationStep(vehicleId, ref vehicleData, physicsLodRefPos);
+ }
- [RedirectMethod]
- public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
+ [RedirectMethod]
+ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
#if DEBUG
- //Log._Debug($"CustomCargoTruckAI.CustomStartPathFind called for vehicle {vehicleID}");
+ //Log._Debug($"CustomCargoTruckAI.CustomStartPathFind called for vehicle {vehicleID}");
#endif
- ExtVehicleType vehicleType = ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, null);
- if (vehicleType == ExtVehicleType.None) {
+ ExtVehicleType vehicleType = ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, null);
+ if (vehicleType == ExtVehicleType.None) {
#if DEBUG
- Log.Warning($"CustomCargoTruck.CustomStartPathFind: Vehicle {vehicleID} does not have a valid vehicle type!");
+ Log.Warning($"CustomCargoTruck.CustomStartPathFind: Vehicle {vehicleID} does not have a valid vehicle type!");
#endif
- }
+ }
- if ((vehicleData.m_flags & (Vehicle.Flags.TransferToSource | Vehicle.Flags.GoingBack)) != 0) {
- return base.StartPathFind(vehicleID, ref vehicleData, startPos, endPos, startBothWays, endBothWays, undergroundTarget);
- }
+ if ((vehicleData.m_flags & (Vehicle.Flags.TransferToSource | Vehicle.Flags.GoingBack)) != 0) {
+ return base.StartPathFind(vehicleID, ref vehicleData, startPos, endPos, startBothWays, endBothWays, undergroundTarget);
+ }
- bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
- PathUnit.Position startPosA;
- PathUnit.Position startPosB;
- float startDistSqrA;
- float startDistSqrB;
- bool startPosFound = CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB);
- PathUnit.Position startAltPosA;
- PathUnit.Position startAltPosB;
- float startAltDistSqrA;
- float startAltDistSqrB;
- if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane, allowUnderground, false, 32f, out startAltPosA, out startAltPosB, out startAltDistSqrA, out startAltDistSqrB)) {
- if (!startPosFound || (startAltDistSqrA < startDistSqrA && (Mathf.Abs(endPos.x) > 8000f || Mathf.Abs(endPos.z) > 8000f))) {
- startPosA = startAltPosA;
- startPosB = startAltPosB;
- startDistSqrA = startAltDistSqrA;
- startDistSqrB = startAltDistSqrB;
- }
- startPosFound = true;
- }
- PathUnit.Position endPosA;
- PathUnit.Position endPosB;
- float endDistSqrA;
- float endDistSqrB;
- bool endPosFound = CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB);
- PathUnit.Position endAltPosA;
- PathUnit.Position endAltPosB;
- float endAltDistSqrA;
- float endAltDistSqrB;
- if (CustomPathManager.FindPathPosition(endPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane, undergroundTarget, false, 32f, out endAltPosA, out endAltPosB, out endAltDistSqrA, out endAltDistSqrB)) {
- if (!endPosFound || (endAltDistSqrA < endDistSqrA && (Mathf.Abs(endPos.x) > 8000f || Mathf.Abs(endPos.z) > 8000f))) {
- endPosA = endAltPosA;
- endPosB = endAltPosB;
- endDistSqrA = endAltDistSqrA;
- endDistSqrB = endAltDistSqrB;
- }
- endPosFound = true;
- }
- if (startPosFound && endPosFound) {
- CustomPathManager pathMan = CustomPathManager._instance;
- if (!startBothWays || startDistSqrA < 10f) {
- startPosB = default(PathUnit.Position);
- }
- if (!endBothWays || endDistSqrA < 10f) {
- endPosB = default(PathUnit.Position);
- }
- NetInfo.LaneType laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.CargoVehicle;
- VehicleInfo.VehicleType vehicleTypes = VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane;
- uint path;
- // NON-STOCK CODE START
- PathCreationArgs args;
- args.extPathType = ExtPathType.None;
- args.extVehicleType = ExtVehicleType.CargoVehicle;
- args.vehicleId = vehicleID;
- args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- args.buildIndex = Singleton.instance.m_currentBuildIndex;
- args.startPosA = startPosA;
- args.startPosB = startPosB;
- args.endPosA = endPosA;
- args.endPosB = endPosB;
- args.vehiclePosition = default(PathUnit.Position);
- args.laneTypes = laneTypes;
- args.vehicleTypes = vehicleTypes;
- args.maxLength = 20000f;
- args.isHeavyVehicle = this.IsHeavyVehicle();
- args.hasCombustionEngine = this.CombustionEngine();
- args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
- args.ignoreFlooded = false;
- args.ignoreCosts = false;
- args.randomParking = false;
- args.stablePath = false;
- args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
+ PathUnit.Position startPosA;
+ PathUnit.Position startPosB;
+ float startDistSqrA;
+ float startDistSqrB;
+ bool startPosFound = CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB);
+ PathUnit.Position startAltPosA;
+ PathUnit.Position startAltPosB;
+ float startAltDistSqrA;
+ float startAltDistSqrB;
+ if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane, allowUnderground, false, 32f, out startAltPosA, out startAltPosB, out startAltDistSqrA, out startAltDistSqrB)) {
+ if (!startPosFound || (startAltDistSqrA < startDistSqrA && (Mathf.Abs(endPos.x) > 8000f || Mathf.Abs(endPos.z) > 8000f))) {
+ startPosA = startAltPosA;
+ startPosB = startAltPosB;
+ startDistSqrA = startAltDistSqrA;
+ startDistSqrB = startAltDistSqrB;
+ }
+ startPosFound = true;
+ }
+ PathUnit.Position endPosA;
+ PathUnit.Position endPosB;
+ float endDistSqrA;
+ float endDistSqrB;
+ bool endPosFound = CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB);
+ PathUnit.Position endAltPosA;
+ PathUnit.Position endAltPosB;
+ float endAltDistSqrA;
+ float endAltDistSqrB;
+ if (CustomPathManager.FindPathPosition(endPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane, undergroundTarget, false, 32f, out endAltPosA, out endAltPosB, out endAltDistSqrA, out endAltDistSqrB)) {
+ if (!endPosFound || (endAltDistSqrA < endDistSqrA && (Mathf.Abs(endPos.x) > 8000f || Mathf.Abs(endPos.z) > 8000f))) {
+ endPosA = endAltPosA;
+ endPosB = endAltPosB;
+ endDistSqrA = endAltDistSqrA;
+ endDistSqrB = endAltDistSqrB;
+ }
+ endPosFound = true;
+ }
+ if (startPosFound && endPosFound) {
+ CustomPathManager pathMan = CustomPathManager._instance;
+ if (!startBothWays || startDistSqrA < 10f) {
+ startPosB = default(PathUnit.Position);
+ }
+ if (!endBothWays || endDistSqrA < 10f) {
+ endPosB = default(PathUnit.Position);
+ }
+ NetInfo.LaneType laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.CargoVehicle;
+ VehicleInfo.VehicleType vehicleTypes = VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane;
+ uint path;
+ // NON-STOCK CODE START
+ PathCreationArgs args;
+ args.extPathType = ExtPathType.None;
+ args.extVehicleType = ExtVehicleType.CargoVehicle;
+ args.vehicleId = vehicleID;
+ args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ args.buildIndex = Singleton.instance.m_currentBuildIndex;
+ args.startPosA = startPosA;
+ args.startPosB = startPosB;
+ args.endPosA = endPosA;
+ args.endPosB = endPosB;
+ args.vehiclePosition = default(PathUnit.Position);
+ args.laneTypes = laneTypes;
+ args.vehicleTypes = vehicleTypes;
+ args.maxLength = 20000f;
+ args.isHeavyVehicle = this.IsHeavyVehicle();
+ args.hasCombustionEngine = this.CombustionEngine();
+ args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
+ args.ignoreFlooded = false;
+ args.ignoreCosts = false;
+ args.randomParking = false;
+ args.stablePath = false;
+ args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- if (pathMan.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
- // NON-STOCK CODE END
- if (vehicleData.m_path != 0u) {
- pathMan.ReleasePath(vehicleData.m_path);
- }
- vehicleData.m_path = path;
- vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
- return true;
- }
- }
- return false;
- }
+ if (pathMan.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
+ // NON-STOCK CODE END
+ if (vehicleData.m_path != 0u) {
+ pathMan.ReleasePath(vehicleData.m_path);
+ }
+ vehicleData.m_path = path;
+ vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
+ return true;
+ }
+ }
+ return false;
+ }
- [MethodImpl(MethodImplOptions.NoInlining)]
- [RedirectReverse]
- private void RemoveOffers(ushort vehicleId, ref Vehicle data) {
- Log.Error("CustomCargoTruckAI.RemoveOffers called");
- }
- }
-}
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ [RedirectReverse]
+ private void RemoveOffers(ushort vehicleId, ref Vehicle data) {
+ Log.Error("CustomCargoTruckAI.RemoveOffers called");
+ }
+ }
+}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/AI/CustomFireTruckAI.cs b/TLM/TLM/Custom/AI/CustomFireTruckAI.cs
index c32d788f5..c3b444775 100644
--- a/TLM/TLM/Custom/AI/CustomFireTruckAI.cs
+++ b/TLM/TLM/Custom/AI/CustomFireTruckAI.cs
@@ -1,91 +1,84 @@
-using ColossalFramework;
-using CSUtil.Commons.Benchmark;
-using TrafficManager.RedirectionFramework.Attributes;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using TrafficManager.Custom.PathFinding;
-using TrafficManager.Geometry;
-using TrafficManager.Manager;
-using TrafficManager.Manager.Impl;
-using TrafficManager.Traffic;
-using TrafficManager.Traffic.Data;
-using TrafficManager.Traffic.Enums;
-using UnityEngine;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
+namespace TrafficManager.Custom.AI {
+ using API.Traffic.Data;
+ using API.Traffic.Enums;
+ using ColossalFramework;
+ using Custom.PathFinding;
+ using Manager.Impl;
+ using RedirectionFramework.Attributes;
+ using Traffic.Data;
+ using UnityEngine;
-namespace TrafficManager.Custom.AI {
- [TargetType(typeof(FireTruckAI))]
- public class CustomFireTruckAI : CarAI {
- [RedirectMethod]
- public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
+ [TargetType(typeof(FireTruckAI))]
+ public class CustomFireTruckAI : CarAI {
+ [RedirectMethod]
+ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
#if DEBUG
- //Log._Debug($"CustomFireTruckAI.CustomStartPathFind called for vehicle {vehicleID}");
+ //Log._Debug($"CustomFireTruckAI.CustomStartPathFind called for vehicle {vehicleID}");
#endif
- ExtVehicleType vehicleType = ExtVehicleType.None;
+ ExtVehicleType vehicleType = ExtVehicleType.None;
#if BENCHMARK
using (var bm = new Benchmark(null, "OnStartPathFind")) {
#endif
- vehicleType = ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, (vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0 ? ExtVehicleType.Emergency : ExtVehicleType.Service);
+ vehicleType = ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, (vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0 ? ExtVehicleType.Emergency : ExtVehicleType.Service);
#if BENCHMARK
}
#endif
- VehicleInfo info = this.m_info;
- bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
- PathUnit.Position startPosA;
- PathUnit.Position startPosB;
- float startDistSqrA;
- float startDistSqrB;
- PathUnit.Position endPosA;
- PathUnit.Position endPosB;
- float endDistSqrA;
- float endDistSqrB;
- if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB) &&
- CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB)) {
- if (!startBothWays || startDistSqrA < 10f) {
- startPosB = default(PathUnit.Position);
- }
- if (!endBothWays || endDistSqrA < 10f) {
- endPosB = default(PathUnit.Position);
- }
- uint path;
- // NON-STOCK CODE START
- PathCreationArgs args;
- args.extPathType = ExtPathType.None;
- args.extVehicleType = vehicleType;
- args.vehicleId = vehicleID;
- args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- args.buildIndex = Singleton.instance.m_currentBuildIndex;
- args.startPosA = startPosA;
- args.startPosB = startPosB;
- args.endPosA = endPosA;
- args.endPosB = endPosB;
- args.vehiclePosition = default(PathUnit.Position);
- args.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;
- args.vehicleTypes = info.m_vehicleType;
- args.maxLength = 20000f;
- args.isHeavyVehicle = this.IsHeavyVehicle();
- args.hasCombustionEngine = this.CombustionEngine();
- args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
- args.ignoreFlooded = false;
- args.ignoreCosts = false;
- args.randomParking = false;
- args.stablePath = false;
- args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ VehicleInfo info = this.m_info;
+ bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
+ PathUnit.Position startPosA;
+ PathUnit.Position startPosB;
+ float startDistSqrA;
+ float startDistSqrB;
+ PathUnit.Position endPosA;
+ PathUnit.Position endPosB;
+ float endDistSqrA;
+ float endDistSqrB;
+ if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB) &&
+ CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB)) {
+ if (!startBothWays || startDistSqrA < 10f) {
+ startPosB = default(PathUnit.Position);
+ }
+ if (!endBothWays || endDistSqrA < 10f) {
+ endPosB = default(PathUnit.Position);
+ }
+ uint path;
+ // NON-STOCK CODE START
+ PathCreationArgs args;
+ args.extPathType = ExtPathType.None;
+ args.extVehicleType = vehicleType;
+ args.vehicleId = vehicleID;
+ args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ args.buildIndex = Singleton.instance.m_currentBuildIndex;
+ args.startPosA = startPosA;
+ args.startPosB = startPosB;
+ args.endPosA = endPosA;
+ args.endPosB = endPosB;
+ args.vehiclePosition = default(PathUnit.Position);
+ args.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;
+ args.vehicleTypes = info.m_vehicleType;
+ args.maxLength = 20000f;
+ args.isHeavyVehicle = this.IsHeavyVehicle();
+ args.hasCombustionEngine = this.CombustionEngine();
+ args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
+ args.ignoreFlooded = false;
+ args.ignoreCosts = false;
+ args.randomParking = false;
+ args.stablePath = false;
+ args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
- // NON-STOCK CODE END
- if (vehicleData.m_path != 0u) {
- Singleton.instance.ReleasePath(vehicleData.m_path);
- }
- vehicleData.m_path = path;
- vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
- return true;
- }
- } else {
- PathfindFailure(vehicleID, ref vehicleData);
- }
- return false;
- }
- }
-}
+ if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
+ // NON-STOCK CODE END
+ if (vehicleData.m_path != 0u) {
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ }
+ vehicleData.m_path = path;
+ vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
+ return true;
+ }
+ } else {
+ PathfindFailure(vehicleID, ref vehicleData);
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/AI/CustomHumanAI.cs b/TLM/TLM/Custom/AI/CustomHumanAI.cs
index 5b318a197..833a36b2f 100644
--- a/TLM/TLM/Custom/AI/CustomHumanAI.cs
+++ b/TLM/TLM/Custom/AI/CustomHumanAI.cs
@@ -20,6 +20,9 @@
using TrafficManager.RedirectionFramework.Attributes;
namespace TrafficManager.Custom.AI {
+ using API.Traffic.Enums;
+ using API.TrafficLight;
+
[TargetType(typeof(HumanAI))]
public class CustomHumanAI : CitizenAI {
[RedirectMethod]
diff --git a/TLM/TLM/Custom/AI/CustomPoliceCarAI.cs b/TLM/TLM/Custom/AI/CustomPoliceCarAI.cs
index 0abcc1fd5..dd6a13dbd 100644
--- a/TLM/TLM/Custom/AI/CustomPoliceCarAI.cs
+++ b/TLM/TLM/Custom/AI/CustomPoliceCarAI.cs
@@ -1,82 +1,75 @@
-using ColossalFramework;
-using CSUtil.Commons.Benchmark;
-using TrafficManager.RedirectionFramework.Attributes;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using TrafficManager.Custom.PathFinding;
-using TrafficManager.Geometry;
-using TrafficManager.Manager;
-using TrafficManager.Manager.Impl;
-using TrafficManager.Traffic;
-using TrafficManager.Traffic.Data;
-using TrafficManager.Traffic.Enums;
-using UnityEngine;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
+namespace TrafficManager.Custom.AI {
+ using API.Traffic.Data;
+ using API.Traffic.Enums;
+ using ColossalFramework;
+ using Custom.PathFinding;
+ using Manager.Impl;
+ using RedirectionFramework.Attributes;
+ using Traffic.Data;
+ using UnityEngine;
-namespace TrafficManager.Custom.AI {
- [TargetType(typeof(PoliceCarAI))]
- public class CustomPoliceCarAI : CarAI {
- [RedirectMethod]
- public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
- ExtVehicleType vehicleType = ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, (vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0 ? ExtVehicleType.Emergency : ExtVehicleType.Service);
+ [TargetType(typeof(PoliceCarAI))]
+ public class CustomPoliceCarAI : CarAI {
+ [RedirectMethod]
+ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
+ ExtVehicleType vehicleType = ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, (vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0 ? ExtVehicleType.Emergency : ExtVehicleType.Service);
- VehicleInfo info = this.m_info;
- bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
- PathUnit.Position startPosA;
- PathUnit.Position startPosB;
- float startDistSqrA;
- float startDistSqrB;
- PathUnit.Position endPosA;
- PathUnit.Position endPosB;
- float endDistSqrA;
- float endDistSqrB;
- if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB) &&
- CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB)) {
- if (!startBothWays || startDistSqrA < 10f) {
- startPosB = default(PathUnit.Position);
- }
- if (!endBothWays || endDistSqrA < 10f) {
- endPosB = default(PathUnit.Position);
- }
- uint path;
- // NON-STOCK CODE START
- PathCreationArgs args;
- args.extPathType = ExtPathType.None;
- args.extVehicleType = vehicleType;
- args.vehicleId = vehicleID;
- args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- args.buildIndex = Singleton.instance.m_currentBuildIndex;
- args.startPosA = startPosA;
- args.startPosB = startPosB;
- args.endPosA = endPosA;
- args.endPosB = endPosB;
- args.vehiclePosition = default(PathUnit.Position);
- args.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;
- args.vehicleTypes = info.m_vehicleType;
- args.maxLength = 20000f;
- args.isHeavyVehicle = this.IsHeavyVehicle();
- args.hasCombustionEngine = this.CombustionEngine();
- args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
- args.ignoreFlooded = false;
- args.ignoreCosts = false;
- args.randomParking = false;
- args.stablePath = false;
- args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ VehicleInfo info = this.m_info;
+ bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
+ PathUnit.Position startPosA;
+ PathUnit.Position startPosB;
+ float startDistSqrA;
+ float startDistSqrB;
+ PathUnit.Position endPosA;
+ PathUnit.Position endPosB;
+ float endDistSqrA;
+ float endDistSqrB;
+ if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB) &&
+ CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB)) {
+ if (!startBothWays || startDistSqrA < 10f) {
+ startPosB = default(PathUnit.Position);
+ }
+ if (!endBothWays || endDistSqrA < 10f) {
+ endPosB = default(PathUnit.Position);
+ }
+ uint path;
+ // NON-STOCK CODE START
+ PathCreationArgs args;
+ args.extPathType = ExtPathType.None;
+ args.extVehicleType = vehicleType;
+ args.vehicleId = vehicleID;
+ args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ args.buildIndex = Singleton.instance.m_currentBuildIndex;
+ args.startPosA = startPosA;
+ args.startPosB = startPosB;
+ args.endPosA = endPosA;
+ args.endPosB = endPosB;
+ args.vehiclePosition = default(PathUnit.Position);
+ args.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;
+ args.vehicleTypes = info.m_vehicleType;
+ args.maxLength = 20000f;
+ args.isHeavyVehicle = this.IsHeavyVehicle();
+ args.hasCombustionEngine = this.CombustionEngine();
+ args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
+ args.ignoreFlooded = false;
+ args.ignoreCosts = false;
+ args.randomParking = false;
+ args.stablePath = false;
+ args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
- // NON-STOCK CODE END
- if (vehicleData.m_path != 0u) {
- Singleton.instance.ReleasePath(vehicleData.m_path);
- }
- vehicleData.m_path = path;
- vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
- return true;
- }
- }
- return false;
- }
+ if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
+ // NON-STOCK CODE END
+ if (vehicleData.m_path != 0u) {
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ }
+ vehicleData.m_path = path;
+ vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
+ return true;
+ }
+ }
+ return false;
+ }
- }
-}
+ }
+}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/AI/CustomPostVanAI.cs b/TLM/TLM/Custom/AI/CustomPostVanAI.cs
index 89f76c79f..5f7c073e6 100644
--- a/TLM/TLM/Custom/AI/CustomPostVanAI.cs
+++ b/TLM/TLM/Custom/AI/CustomPostVanAI.cs
@@ -1,119 +1,114 @@
-using ColossalFramework;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using TrafficManager.Custom.PathFinding;
-using TrafficManager.RedirectionFramework.Attributes;
-using TrafficManager.Traffic;
-using TrafficManager.Traffic.Data;
-using TrafficManager.Traffic.Enums;
-using UnityEngine;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
+namespace TrafficManager.Custom.AI {
+ using API.Traffic.Data;
+ using API.Traffic.Enums;
+ using ColossalFramework;
+ using Custom.PathFinding;
+ using RedirectionFramework.Attributes;
+ using Traffic.Data;
+ using UnityEngine;
-namespace TrafficManager.Custom.AI {
- [TargetType(typeof(PostVanAI))]
- public class CustomPostVanAI : CarAI {
- [RedirectMethod]
- public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
- if (vehicleData.m_transferType == (byte)TransferManager.TransferReason.Mail) {
- return base.StartPathFind(vehicleID, ref vehicleData, startPos, endPos, startBothWays, endBothWays, undergroundTarget);
- }
+ [TargetType(typeof(PostVanAI))]
+ public class CustomPostVanAI : CarAI {
+ [RedirectMethod]
+ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
+ if (vehicleData.m_transferType == (byte)TransferManager.TransferReason.Mail) {
+ return base.StartPathFind(vehicleID, ref vehicleData, startPos, endPos, startBothWays, endBothWays, undergroundTarget);
+ }
- if ((vehicleData.m_flags & (Vehicle.Flags.TransferToSource | Vehicle.Flags.GoingBack)) != 0) {
- return base.StartPathFind(vehicleID, ref vehicleData, startPos, endPos, startBothWays, endBothWays, undergroundTarget);
- }
+ if ((vehicleData.m_flags & (Vehicle.Flags.TransferToSource | Vehicle.Flags.GoingBack)) != 0) {
+ return base.StartPathFind(vehicleID, ref vehicleData, startPos, endPos, startBothWays, endBothWays, undergroundTarget);
+ }
- bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != (Vehicle.Flags)0;
- PathUnit.Position startPosA = default(PathUnit.Position);
- PathUnit.Position startPosB = default(PathUnit.Position);
- float startDistSqrA = default(float);
- float startDistSqrB = default(float);
+ bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != (Vehicle.Flags)0;
+ PathUnit.Position startPosA = default(PathUnit.Position);
+ PathUnit.Position startPosB = default(PathUnit.Position);
+ float startDistSqrA = default(float);
+ float startDistSqrB = default(float);
- // try to find road start position
- bool startPosFound = CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB);
+ // try to find road start position
+ bool startPosFound = CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, allowUnderground, false, 32f, out startPosA, out startPosB, out startDistSqrA, out startDistSqrB);
- // try to find other start position (plane, train, ship)
- PathUnit.Position altStartPosA = default(PathUnit.Position);
- PathUnit.Position altStartPosB = default(PathUnit.Position);
- float altStartDistSqrA = default(float);
- float altStartDistSqrB = default(float);
- if (PathManager.FindPathPosition(startPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane, allowUnderground, false, 32f, out altStartPosA, out altStartPosB, out altStartDistSqrA, out altStartDistSqrB)) {
- if (!startPosFound || (altStartDistSqrA < startDistSqrA && (Mathf.Abs(startPos.x) > 4800f || Mathf.Abs(startPos.z) > 4800f))) {
- startPosA = altStartPosA;
- startPosB = altStartPosB;
- startDistSqrA = altStartDistSqrA;
- startDistSqrB = altStartDistSqrB;
- }
- startPosFound = true;
- }
+ // try to find other start position (plane, train, ship)
+ PathUnit.Position altStartPosA = default(PathUnit.Position);
+ PathUnit.Position altStartPosB = default(PathUnit.Position);
+ float altStartDistSqrA = default(float);
+ float altStartDistSqrB = default(float);
+ if (PathManager.FindPathPosition(startPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane, allowUnderground, false, 32f, out altStartPosA, out altStartPosB, out altStartDistSqrA, out altStartDistSqrB)) {
+ if (!startPosFound || (altStartDistSqrA < startDistSqrA && (Mathf.Abs(startPos.x) > 4800f || Mathf.Abs(startPos.z) > 4800f))) {
+ startPosA = altStartPosA;
+ startPosB = altStartPosB;
+ startDistSqrA = altStartDistSqrA;
+ startDistSqrB = altStartDistSqrB;
+ }
+ startPosFound = true;
+ }
- PathUnit.Position endPosA = default(PathUnit.Position);
- PathUnit.Position endPosB = default(PathUnit.Position);
- float endDistSqrA = default(float);
- float endDistSqrB = default(float);
+ PathUnit.Position endPosA = default(PathUnit.Position);
+ PathUnit.Position endPosB = default(PathUnit.Position);
+ float endDistSqrA = default(float);
+ float endDistSqrB = default(float);
- // try to find road end position
- bool endPosFound = PathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB);
+ // try to find road end position
+ bool endPosFound = PathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, undergroundTarget, false, 32f, out endPosA, out endPosB, out endDistSqrA, out endDistSqrB);
- // try to find other end position (plane, train, ship)
- PathUnit.Position altEndPosA = default(PathUnit.Position);
- PathUnit.Position altEndPosB = default(PathUnit.Position);
- float altEndDistSqrA = default(float);
- float altEndDistSqrB = default(float);
- if (PathManager.FindPathPosition(endPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane, undergroundTarget, false, 32f, out altEndPosA, out altEndPosB, out altEndDistSqrA, out altEndDistSqrB)) {
- if (!endPosFound || (altEndDistSqrA < endDistSqrA && (Mathf.Abs(endPos.x) > 4800f || Mathf.Abs(endPos.z) > 4800f))) {
- endPosA = altEndPosA;
- endPosB = altEndPosB;
- endDistSqrA = altEndDistSqrA;
- endDistSqrB = altEndDistSqrB;
- }
- endPosFound = true;
- }
+ // try to find other end position (plane, train, ship)
+ PathUnit.Position altEndPosA = default(PathUnit.Position);
+ PathUnit.Position altEndPosB = default(PathUnit.Position);
+ float altEndDistSqrA = default(float);
+ float altEndDistSqrB = default(float);
+ if (PathManager.FindPathPosition(endPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane, undergroundTarget, false, 32f, out altEndPosA, out altEndPosB, out altEndDistSqrA, out altEndDistSqrB)) {
+ if (!endPosFound || (altEndDistSqrA < endDistSqrA && (Mathf.Abs(endPos.x) > 4800f || Mathf.Abs(endPos.z) > 4800f))) {
+ endPosA = altEndPosA;
+ endPosB = altEndPosB;
+ endDistSqrA = altEndDistSqrA;
+ endDistSqrB = altEndDistSqrB;
+ }
+ endPosFound = true;
+ }
- if (startPosFound && endPosFound) {
- CustomPathManager pathManager = CustomPathManager._instance;
- if (!startBothWays || startDistSqrA < 10f) {
- startPosB = default(PathUnit.Position);
- }
- if (!endBothWays || endDistSqrA < 10f) {
- endPosB = default(PathUnit.Position);
- }
- uint path;
+ if (startPosFound && endPosFound) {
+ CustomPathManager pathManager = CustomPathManager._instance;
+ if (!startBothWays || startDistSqrA < 10f) {
+ startPosB = default(PathUnit.Position);
+ }
+ if (!endBothWays || endDistSqrA < 10f) {
+ endPosB = default(PathUnit.Position);
+ }
+ uint path;
- PathCreationArgs args;
- args.extPathType = ExtPathType.None;
- args.extVehicleType = ExtVehicleType.Service;
- args.vehicleId = vehicleID;
- args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- args.buildIndex = Singleton.instance.m_currentBuildIndex;
- args.startPosA = startPosA;
- args.startPosB = startPosB;
- args.endPosA = endPosA;
- args.endPosB = endPosB;
- args.vehiclePosition = default(PathUnit.Position);
- args.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.CargoVehicle;
- args.vehicleTypes = VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane;
- args.maxLength = 20000f;
- args.isHeavyVehicle = this.IsHeavyVehicle();
- args.hasCombustionEngine = this.CombustionEngine();
- args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
- args.ignoreFlooded = false;
- args.ignoreCosts = false;
- args.randomParking = false;
- args.stablePath = false;
- args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ PathCreationArgs args;
+ args.extPathType = ExtPathType.None;
+ args.extVehicleType = ExtVehicleType.Service;
+ args.vehicleId = vehicleID;
+ args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ args.buildIndex = Singleton.instance.m_currentBuildIndex;
+ args.startPosA = startPosA;
+ args.startPosB = startPosB;
+ args.endPosA = endPosA;
+ args.endPosB = endPosB;
+ args.vehiclePosition = default(PathUnit.Position);
+ args.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.CargoVehicle;
+ args.vehicleTypes = VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane;
+ args.maxLength = 20000f;
+ args.isHeavyVehicle = this.IsHeavyVehicle();
+ args.hasCombustionEngine = this.CombustionEngine();
+ args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
+ args.ignoreFlooded = false;
+ args.ignoreCosts = false;
+ args.randomParking = false;
+ args.stablePath = false;
+ args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- if (pathManager.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
- if (vehicleData.m_path != 0) {
- pathManager.ReleasePath(vehicleData.m_path);
- }
- vehicleData.m_path = path;
- vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
- return true;
- }
- }
- return false;
- }
- }
-}
+ if (pathManager.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
+ if (vehicleData.m_path != 0) {
+ pathManager.ReleasePath(vehicleData.m_path);
+ }
+ vehicleData.m_path = path;
+ vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/AI/CustomShipAI.cs b/TLM/TLM/Custom/AI/CustomShipAI.cs
index ef1b0f0f6..a75397113 100644
--- a/TLM/TLM/Custom/AI/CustomShipAI.cs
+++ b/TLM/TLM/Custom/AI/CustomShipAI.cs
@@ -1,87 +1,79 @@
-using ColossalFramework;
-using CSUtil.Commons;
-using CSUtil.Commons.Benchmark;
-using TrafficManager.RedirectionFramework.Attributes;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using TrafficManager.Custom.PathFinding;
-using TrafficManager.Geometry;
-using TrafficManager.Manager;
-using TrafficManager.Traffic;
-using TrafficManager.Traffic.Data;
-using TrafficManager.Traffic.Enums;
-using UnityEngine;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
+namespace TrafficManager.Custom.AI {
+ using API.Traffic.Data;
+ using API.Traffic.Enums;
+ using ColossalFramework;
+ using Custom.PathFinding;
+ using RedirectionFramework.Attributes;
+ using Traffic.Data;
+ using UnityEngine;
-namespace TrafficManager.Custom.AI {
- [TargetType(typeof(ShipAI))]
- public class CustomShipAI : ShipAI {
- [RedirectMethod]
- public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) {
+ [TargetType(typeof(ShipAI))]
+ public class CustomShipAI : ShipAI {
+ [RedirectMethod]
+ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) {
#if DEBUG
- //Log._Debug($"CustomShipAI.CustomStartPathFind called for vehicle {vehicleID}");
+ //Log._Debug($"CustomShipAI.CustomStartPathFind called for vehicle {vehicleID}");
#endif
- /// NON-STOCK CODE START ///
- ExtVehicleType vehicleType = vehicleData.Info.m_vehicleAI is PassengerShipAI ? ExtVehicleType.PassengerShip : ExtVehicleType.CargoVehicle;
- /// NON-STOCK CODE END ///
+ /// NON-STOCK CODE START ///
+ ExtVehicleType vehicleType = vehicleData.Info.m_vehicleAI is PassengerShipAI ? ExtVehicleType.PassengerShip : ExtVehicleType.CargoVehicle;
+ /// NON-STOCK CODE END ///
- VehicleInfo info = this.m_info;
- PathUnit.Position startPosA;
- PathUnit.Position startPosB;
- float startSqrDistA;
- float startSqrDistB;
- PathUnit.Position endPosA;
- PathUnit.Position endPosB;
- float endSqrDistA;
- float endSqrDistB;
- if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, info.m_vehicleType, false, false, 64f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB) &&
- CustomPathManager.FindPathPosition(endPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, info.m_vehicleType, false, false, 64f, out endPosA, out endPosB, out endSqrDistA, out endSqrDistB)) {
- if (!startBothWays || startSqrDistA < 10f) {
- startPosB = default(PathUnit.Position);
- }
- if (!endBothWays || endSqrDistA < 10f) {
- endPosB = default(PathUnit.Position);
- }
- uint path;
- // NON-STOCK CODE START
- PathCreationArgs args;
- args.extPathType = ExtPathType.None;
- args.extVehicleType = vehicleType;
- args.vehicleId = vehicleID;
- args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- args.buildIndex = Singleton.instance.m_currentBuildIndex;
- args.startPosA = startPosA;
- args.startPosB = startPosB;
- args.endPosA = endPosA;
- args.endPosB = endPosB;
- args.vehiclePosition = default(PathUnit.Position);
- args.laneTypes = NetInfo.LaneType.Vehicle;
- args.vehicleTypes = info.m_vehicleType;
- args.maxLength = 20000f;
- args.isHeavyVehicle = false;
- args.hasCombustionEngine = false;
- args.ignoreBlocked = false;
- args.ignoreFlooded = false;
- args.ignoreCosts = false;
- args.randomParking = false;
- args.stablePath = false;
- args.skipQueue = false;
+ VehicleInfo info = this.m_info;
+ PathUnit.Position startPosA;
+ PathUnit.Position startPosB;
+ float startSqrDistA;
+ float startSqrDistB;
+ PathUnit.Position endPosA;
+ PathUnit.Position endPosB;
+ float endSqrDistA;
+ float endSqrDistB;
+ if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, info.m_vehicleType, false, false, 64f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB) &&
+ CustomPathManager.FindPathPosition(endPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, info.m_vehicleType, false, false, 64f, out endPosA, out endPosB, out endSqrDistA, out endSqrDistB)) {
+ if (!startBothWays || startSqrDistA < 10f) {
+ startPosB = default(PathUnit.Position);
+ }
+ if (!endBothWays || endSqrDistA < 10f) {
+ endPosB = default(PathUnit.Position);
+ }
+ uint path;
+ // NON-STOCK CODE START
+ PathCreationArgs args;
+ args.extPathType = ExtPathType.None;
+ args.extVehicleType = vehicleType;
+ args.vehicleId = vehicleID;
+ args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ args.buildIndex = Singleton.instance.m_currentBuildIndex;
+ args.startPosA = startPosA;
+ args.startPosB = startPosB;
+ args.endPosA = endPosA;
+ args.endPosB = endPosB;
+ args.vehiclePosition = default(PathUnit.Position);
+ args.laneTypes = NetInfo.LaneType.Vehicle;
+ args.vehicleTypes = info.m_vehicleType;
+ args.maxLength = 20000f;
+ args.isHeavyVehicle = false;
+ args.hasCombustionEngine = false;
+ args.ignoreBlocked = false;
+ args.ignoreFlooded = false;
+ args.ignoreCosts = false;
+ args.randomParking = false;
+ args.stablePath = false;
+ args.skipQueue = false;
- if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
- // NON-STOCK CODE END
+ if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
+ // NON-STOCK CODE END
- if (vehicleData.m_path != 0u) {
- Singleton.instance.ReleasePath(vehicleData.m_path);
- }
- vehicleData.m_path = path;
- vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
- return true;
- }
- }
- return false;
- }
+ if (vehicleData.m_path != 0u) {
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ }
+ vehicleData.m_path = path;
+ vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
+ return true;
+ }
+ }
+ return false;
+ }
- }
-}
+ }
+}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/AI/CustomTaxiAI.cs b/TLM/TLM/Custom/AI/CustomTaxiAI.cs
index 114c1b885..838e8d6a8 100644
--- a/TLM/TLM/Custom/AI/CustomTaxiAI.cs
+++ b/TLM/TLM/Custom/AI/CustomTaxiAI.cs
@@ -1,89 +1,83 @@
-using ColossalFramework;
-using TrafficManager.RedirectionFramework.Attributes;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using TrafficManager.Custom.PathFinding;
-using TrafficManager.Geometry;
-using TrafficManager.Manager;
-using TrafficManager.Traffic;
-using TrafficManager.Traffic.Data;
-using TrafficManager.Traffic.Enums;
-using UnityEngine;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
+namespace TrafficManager.Custom.AI {
+ using API.Traffic.Data;
+ using API.Traffic.Enums;
+ using ColossalFramework;
+ using Custom.PathFinding;
+ using RedirectionFramework.Attributes;
+ using Traffic.Data;
+ using UnityEngine;
-namespace TrafficManager.Custom.AI {
- [TargetType(typeof(TaxiAI))]
- public class CustomTaxiAI : CarAI {
- [RedirectMethod]
- public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
- CitizenManager instance = Singleton.instance;
- ushort passengerInstanceId = Constants.ManagerFactory.ExtVehicleManager.GetDriverInstanceId(vehicleID, ref vehicleData);
- if (passengerInstanceId == 0 || (instance.m_instances.m_buffer[(int)passengerInstanceId].m_flags & CitizenInstance.Flags.Character) != CitizenInstance.Flags.None) {
- return base.StartPathFind(vehicleID, ref vehicleData, startPos, endPos, startBothWays, endBothWays, undergroundTarget);
- }
- VehicleInfo info = this.m_info;
- CitizenInfo info2 = instance.m_instances.m_buffer[(int)passengerInstanceId].Info;
- NetInfo.LaneType laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.Pedestrian | NetInfo.LaneType.TransportVehicle;
- VehicleInfo.VehicleType vehicleTypes = this.m_info.m_vehicleType;
- bool allowUnderground = (vehicleData.m_flags & Vehicle.Flags.Underground) != 0;
- PathUnit.Position startPosA;
- PathUnit.Position startPosB;
- float startSqrDistA;
- float startSqrDistB;
- PathUnit.Position endPosA;
- if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB) &&
- Constants.ManagerFactory.ExtCitizenInstanceManager.FindPathPosition(passengerInstanceId, ref instance.m_instances.m_buffer[(int)passengerInstanceId], endPos, laneTypes, vehicleTypes, undergroundTarget, out endPosA)) {
- if ((instance.m_instances.m_buffer[(int)passengerInstanceId].m_flags & CitizenInstance.Flags.CannotUseTransport) == CitizenInstance.Flags.None) {
- laneTypes |= NetInfo.LaneType.PublicTransport;
+ [TargetType(typeof(TaxiAI))]
+ public class CustomTaxiAI : CarAI {
+ [RedirectMethod]
+ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {
+ CitizenManager instance = Singleton.instance;
+ ushort passengerInstanceId = Constants.ManagerFactory.ExtVehicleManager.GetDriverInstanceId(vehicleID, ref vehicleData);
+ if (passengerInstanceId == 0 || (instance.m_instances.m_buffer[(int)passengerInstanceId].m_flags & CitizenInstance.Flags.Character) != CitizenInstance.Flags.None) {
+ return base.StartPathFind(vehicleID, ref vehicleData, startPos, endPos, startBothWays, endBothWays, undergroundTarget);
+ }
+ VehicleInfo info = this.m_info;
+ CitizenInfo info2 = instance.m_instances.m_buffer[(int)passengerInstanceId].Info;
+ NetInfo.LaneType laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.Pedestrian | NetInfo.LaneType.TransportVehicle;
+ VehicleInfo.VehicleType vehicleTypes = this.m_info.m_vehicleType;
+ bool allowUnderground = (vehicleData.m_flags & Vehicle.Flags.Underground) != 0;
+ PathUnit.Position startPosA;
+ PathUnit.Position startPosB;
+ float startSqrDistA;
+ float startSqrDistB;
+ PathUnit.Position endPosA;
+ if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB) &&
+ Constants.ManagerFactory.ExtCitizenInstanceManager.FindPathPosition(passengerInstanceId, ref instance.m_instances.m_buffer[(int)passengerInstanceId], endPos, laneTypes, vehicleTypes, undergroundTarget, out endPosA)) {
+ if ((instance.m_instances.m_buffer[(int)passengerInstanceId].m_flags & CitizenInstance.Flags.CannotUseTransport) == CitizenInstance.Flags.None) {
+ laneTypes |= NetInfo.LaneType.PublicTransport;
- uint citizenId = instance.m_instances.m_buffer[passengerInstanceId].m_citizen;
- if (citizenId != 0u && (instance.m_citizens.m_buffer[citizenId].m_flags & Citizen.Flags.Evacuating) != Citizen.Flags.None) {
- laneTypes |= NetInfo.LaneType.EvacuationTransport;
- }
- }
- if (!startBothWays || startSqrDistA < 10f) {
- startPosB = default(PathUnit.Position);
- }
- PathUnit.Position endPosB = default(PathUnit.Position);
- SimulationManager simMan = Singleton.instance;
- uint path;
- // NON-STOCK CODE START
- PathCreationArgs args;
- args.extPathType = ExtPathType.None;
- args.extVehicleType = ExtVehicleType.Taxi;
- args.vehicleId = vehicleID;
- args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- args.buildIndex = simMan.m_currentBuildIndex;
- args.startPosA = startPosA;
- args.startPosB = startPosB;
- args.endPosA = endPosA;
- args.endPosB = endPosB;
- args.vehiclePosition = default(PathUnit.Position);
- args.laneTypes = laneTypes;
- args.vehicleTypes = vehicleTypes;
- args.maxLength = 20000f;
- args.isHeavyVehicle = this.IsHeavyVehicle();
- args.hasCombustionEngine = this.CombustionEngine();
- args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
- args.ignoreFlooded = false;
- args.ignoreCosts = false;
- args.randomParking = false;
- args.stablePath = false;
- args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ uint citizenId = instance.m_instances.m_buffer[passengerInstanceId].m_citizen;
+ if (citizenId != 0u && (instance.m_citizens.m_buffer[citizenId].m_flags & Citizen.Flags.Evacuating) != Citizen.Flags.None) {
+ laneTypes |= NetInfo.LaneType.EvacuationTransport;
+ }
+ }
+ if (!startBothWays || startSqrDistA < 10f) {
+ startPosB = default(PathUnit.Position);
+ }
+ PathUnit.Position endPosB = default(PathUnit.Position);
+ SimulationManager simMan = Singleton.instance;
+ uint path;
+ // NON-STOCK CODE START
+ PathCreationArgs args;
+ args.extPathType = ExtPathType.None;
+ args.extVehicleType = ExtVehicleType.Taxi;
+ args.vehicleId = vehicleID;
+ args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ args.buildIndex = simMan.m_currentBuildIndex;
+ args.startPosA = startPosA;
+ args.startPosB = startPosB;
+ args.endPosA = endPosA;
+ args.endPosB = endPosB;
+ args.vehiclePosition = default(PathUnit.Position);
+ args.laneTypes = laneTypes;
+ args.vehicleTypes = vehicleTypes;
+ args.maxLength = 20000f;
+ args.isHeavyVehicle = this.IsHeavyVehicle();
+ args.hasCombustionEngine = this.CombustionEngine();
+ args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
+ args.ignoreFlooded = false;
+ args.ignoreCosts = false;
+ args.randomParking = false;
+ args.stablePath = false;
+ args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- if (CustomPathManager._instance.CustomCreatePath(out path, ref simMan.m_randomizer, args)) {
- // NON-STOCK CODE END
- if (vehicleData.m_path != 0u) {
- Singleton.instance.ReleasePath(vehicleData.m_path);
- }
- vehicleData.m_path = path;
- vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
- return true;
- }
- }
- return false;
- }
+ if (CustomPathManager._instance.CustomCreatePath(out path, ref simMan.m_randomizer, args)) {
+ // NON-STOCK CODE END
+ if (vehicleData.m_path != 0u) {
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ }
+ vehicleData.m_path = path;
+ vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
+ return true;
+ }
+ }
+ return false;
+ }
- }
-}
+ }
+}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/AI/CustomTrainAI.cs b/TLM/TLM/Custom/AI/CustomTrainAI.cs
index 0a730751a..5ed5e0188 100644
--- a/TLM/TLM/Custom/AI/CustomTrainAI.cs
+++ b/TLM/TLM/Custom/AI/CustomTrainAI.cs
@@ -1,882 +1,883 @@
-using ColossalFramework;
-using ColossalFramework.Math;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using TrafficManager.Custom.PathFinding;
-using TrafficManager.State;
-using TrafficManager.Geometry;
-using UnityEngine;
-using TrafficManager.Traffic;
-using TrafficManager.Manager;
-using CSUtil.Commons;
-using TrafficManager.Manager.Impl;
-using System.Runtime.CompilerServices;
-using TrafficManager.Traffic.Data;
-using CSUtil.Commons.Benchmark;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
-using TrafficManager.Traffic.Enums;
-using TrafficManager.RedirectionFramework.Attributes;
-
-namespace TrafficManager.Custom.AI {
- [TargetType(typeof(TrainAI))]
- public class CustomTrainAI : TrainAI { // TODO inherit from VehicleAI (in order to keep the correct references to `base`)
- [RedirectMethod]
- public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {
- IExtVehicleManager extVehicleMan = Constants.ManagerFactory.ExtVehicleManager;
-
- if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) {
- byte pathFindFlags = Singleton.instance.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;
-
- if ((pathFindFlags & PathUnit.FLAG_READY) != 0) {
- try {
- this.PathFindReady(vehicleId, ref vehicleData);
- } catch (Exception e) {
- Log.Warning($"TrainAI.PathFindReady({vehicleId}) for vehicle {vehicleData.Info?.m_class?.name} threw an exception: {e.ToString()}");
- vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
- Singleton.instance.ReleasePath(vehicleData.m_path);
- vehicleData.m_path = 0u;
- vehicleData.Unspawn(vehicleId);
- return;
- }
- } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) {
- vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
- Singleton.instance.ReleasePath(vehicleData.m_path);
- vehicleData.m_path = 0u;
- vehicleData.Unspawn(vehicleId);
- return;
- }
- } else {
- if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) {
- this.TrySpawn(vehicleId, ref vehicleData);
- }
- }
-
- // NON-STOCK CODE START
- extVehicleMan.UpdateVehiclePosition(vehicleId, ref vehicleData);
-
- if (!Options.isStockLaneChangerUsed() && (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0) {
- // Advanced AI traffic measurement
- extVehicleMan.LogTraffic(vehicleId, ref vehicleData);
- }
- // NON-STOCK CODE END
-
- bool reversed = (vehicleData.m_flags & Vehicle.Flags.Reversed) != 0;
- ushort connectedVehicleId;
- if (reversed) {
- connectedVehicleId = vehicleData.GetLastVehicle(vehicleId);
- } else {
- connectedVehicleId = vehicleId;
- }
-
- VehicleManager instance = Singleton.instance;
- VehicleInfo info = instance.m_vehicles.m_buffer[(int)connectedVehicleId].Info;
- info.m_vehicleAI.SimulationStep(connectedVehicleId, ref instance.m_vehicles.m_buffer[(int)connectedVehicleId], vehicleId, ref vehicleData, 0);
- if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {
- return;
- }
- bool newReversed = (vehicleData.m_flags & Vehicle.Flags.Reversed) != 0;
- if (newReversed != reversed) {
- reversed = newReversed;
- if (reversed) {
- connectedVehicleId = vehicleData.GetLastVehicle(vehicleId);
- } else {
- connectedVehicleId = vehicleId;
- }
- info = instance.m_vehicles.m_buffer[(int)connectedVehicleId].Info;
- info.m_vehicleAI.SimulationStep(connectedVehicleId, ref instance.m_vehicles.m_buffer[(int)connectedVehicleId], vehicleId, ref vehicleData, 0);
- if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {
- return;
- }
- newReversed = ((vehicleData.m_flags & Vehicle.Flags.Reversed) != 0);
- if (newReversed != reversed) {
- Singleton.instance.ReleaseVehicle(vehicleId);
- return;
- }
- }
- if (reversed) {
- connectedVehicleId = instance.m_vehicles.m_buffer[(int)connectedVehicleId].m_leadingVehicle;
- int num2 = 0;
- while (connectedVehicleId != 0) {
- info = instance.m_vehicles.m_buffer[(int)connectedVehicleId].Info;
- info.m_vehicleAI.SimulationStep(connectedVehicleId, ref instance.m_vehicles.m_buffer[(int)connectedVehicleId], vehicleId, ref vehicleData, 0);
- if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {
- return;
- }
- connectedVehicleId = instance.m_vehicles.m_buffer[(int)connectedVehicleId].m_leadingVehicle;
- if (++num2 > 16384) {
- CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
- break;
- }
- }
- } else {
- connectedVehicleId = instance.m_vehicles.m_buffer[(int)connectedVehicleId].m_trailingVehicle;
- int num3 = 0;
- while (connectedVehicleId != 0) {
- info = instance.m_vehicles.m_buffer[(int)connectedVehicleId].Info;
- info.m_vehicleAI.SimulationStep(connectedVehicleId, ref instance.m_vehicles.m_buffer[(int)connectedVehicleId], vehicleId, ref vehicleData, 0);
- if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {
- return;
- }
- connectedVehicleId = instance.m_vehicles.m_buffer[(int)connectedVehicleId].m_trailingVehicle;
- if (++num3 > 16384) {
- CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
- break;
- }
- }
- }
- if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo)) == 0) {
- Singleton.instance.ReleaseVehicle(vehicleId);
- } else if (vehicleData.m_blockCounter == 255) {
- // NON-STOCK CODE START
- if (VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData)) {
- // NON-STOCK CODE END
- Singleton.instance.ReleaseVehicle(vehicleId);
- } // NON-STOCK CODE
- }
- }
-
- [RedirectMethod]
- public void CustomSimulationStep(ushort vehicleID, ref Vehicle vehicleData, ref Vehicle.Frame frameData, ushort leaderID, ref Vehicle leaderData, int lodPhysics) {
- bool reversed = (leaderData.m_flags & Vehicle.Flags.Reversed) != (Vehicle.Flags)0;
- ushort frontVehicleId = (!reversed) ? vehicleData.m_leadingVehicle : vehicleData.m_trailingVehicle;
- VehicleInfo vehicleInfo;
- if (leaderID != vehicleID) {
- vehicleInfo = leaderData.Info;
- } else {
- vehicleInfo = this.m_info;
- }
- TrainAI trainAI = vehicleInfo.m_vehicleAI as TrainAI;
- if (frontVehicleId != 0) {
- frameData.m_position += frameData.m_velocity * 0.4f;
- } else {
- frameData.m_position += frameData.m_velocity * 0.5f;
- }
- frameData.m_swayPosition += frameData.m_swayVelocity * 0.5f;
-
- Vector3 posBeforeWheelRot = frameData.m_position;
- Vector3 posAfterWheelRot = frameData.m_position;
- Vector3 wheelBaseRot = frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
- if (reversed) {
- posBeforeWheelRot -= wheelBaseRot;
- posAfterWheelRot += wheelBaseRot;
- } else {
- posBeforeWheelRot += wheelBaseRot;
- posAfterWheelRot -= wheelBaseRot;
- }
-
- float acceleration = this.m_info.m_acceleration;
- float braking = this.m_info.m_braking;
- float curSpeed = frameData.m_velocity.magnitude;
-
- Vector3 beforeRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posBeforeWheelRot;
- float beforeRotToTargetPos1DiffSqrMag = beforeRotToTargetPos1Diff.sqrMagnitude;
-
- Quaternion curInvRot = Quaternion.Inverse(frameData.m_rotation);
- Vector3 curveTangent = curInvRot * frameData.m_velocity;
-
- Vector3 forward = Vector3.forward;
- Vector3 targetMotion = Vector3.zero;
- float targetSpeed = 0f;
- float motionFactor = 0.5f;
-
- if (frontVehicleId != 0) {
- VehicleManager vehMan = Singleton.instance;
- Vehicle.Frame frontVehLastFrameData = vehMan.m_vehicles.m_buffer[(int)frontVehicleId].GetLastFrameData();
- VehicleInfo frontVehInfo = vehMan.m_vehicles.m_buffer[(int)frontVehicleId].Info;
-
- float attachOffset;
- if ((vehicleData.m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags)0 != reversed) {
- attachOffset = this.m_info.m_attachOffsetBack - this.m_info.m_generatedInfo.m_size.z * 0.5f;
- } else {
- attachOffset = this.m_info.m_attachOffsetFront - this.m_info.m_generatedInfo.m_size.z * 0.5f;
- }
-
- float frontAttachOffset;
- if ((vehMan.m_vehicles.m_buffer[(int)frontVehicleId].m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags)0 != reversed) {
- frontAttachOffset = frontVehInfo.m_attachOffsetFront - frontVehInfo.m_generatedInfo.m_size.z * 0.5f;
- } else {
- frontAttachOffset = frontVehInfo.m_attachOffsetBack - frontVehInfo.m_generatedInfo.m_size.z * 0.5f;
- }
-
- Vector3 posMinusAttachOffset = frameData.m_position;
- if (reversed) {
- posMinusAttachOffset += frameData.m_rotation * new Vector3(0f, 0f, attachOffset);
- } else {
- posMinusAttachOffset -= frameData.m_rotation * new Vector3(0f, 0f, attachOffset);
- }
-
- Vector3 frontPosPlusAttachOffset = frontVehLastFrameData.m_position;
- if (reversed) {
- frontPosPlusAttachOffset -= frontVehLastFrameData.m_rotation * new Vector3(0f, 0f, frontAttachOffset);
- } else {
- frontPosPlusAttachOffset += frontVehLastFrameData.m_rotation * new Vector3(0f, 0f, frontAttachOffset);
- }
-
- Vector3 frontPosMinusWheelBaseRot = frontVehLastFrameData.m_position;
- wheelBaseRot = frontVehLastFrameData.m_rotation * new Vector3(0f, 0f, frontVehInfo.m_generatedInfo.m_wheelBase * 0.5f);
- if (reversed) {
- frontPosMinusWheelBaseRot += wheelBaseRot;
- } else {
- frontPosMinusWheelBaseRot -= wheelBaseRot;
- }
-
- if (Vector3.Dot(vehicleData.m_targetPos1 - vehicleData.m_targetPos0, (Vector3)vehicleData.m_targetPos0 - posAfterWheelRot) < 0f && vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
- int someIndex = -1;
- UpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posAfterWheelRot, 0, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posAfterWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);
- beforeRotToTargetPos1DiffSqrMag = 0f;
- }
-
- float maxAttachDist = Mathf.Max(Vector3.Distance(posMinusAttachOffset, frontPosPlusAttachOffset), 2f);
- float one = 1f;
- float maxAttachSqrDist = maxAttachDist * maxAttachDist;
- float oneSqr = one * one;
- int i = 0;
- if (beforeRotToTargetPos1DiffSqrMag < maxAttachSqrDist) {
- if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
- UpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, posAfterWheelRot, posBeforeWheelRot, 0, ref leaderData, ref i, 1, 2, maxAttachSqrDist, oneSqr);
- }
- while (i < 4) {
- vehicleData.SetTargetPos(i, vehicleData.GetTargetPos(i - 1));
- i++;
- }
- beforeRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posBeforeWheelRot;
- beforeRotToTargetPos1DiffSqrMag = beforeRotToTargetPos1Diff.sqrMagnitude;
- }
-
- if (vehicleData.m_path != 0u) {
- NetManager netMan = Singleton.instance;
- byte pathPosIndex = vehicleData.m_pathPositionIndex;
- byte lastPathOffset = vehicleData.m_lastPathOffset;
- if (pathPosIndex == 255) {
- pathPosIndex = 0;
- }
-
- PathManager pathMan = Singleton.instance;
- PathUnit.Position curPathPos;
- if (pathMan.m_pathUnits.m_buffer[vehicleData.m_path].GetPosition(pathPosIndex >> 1, out curPathPos)) {
- netMan.m_segments.m_buffer[(int)curPathPos.m_segment].AddTraffic(Mathf.RoundToInt(this.m_info.m_generatedInfo.m_size.z * 3f), this.GetNoiseLevel());
- PathUnit.Position nextPathPos; // NON-STOCK CODE
- if ((pathPosIndex & 1) == 0 || lastPathOffset == 0 || (leaderData.m_flags & Vehicle.Flags.WaitingPath) != (Vehicle.Flags)0) {
- uint laneId = PathManager.GetLaneID(curPathPos);
- if (laneId != 0u) {
- netMan.m_lanes.m_buffer[laneId].ReserveSpace(this.m_info.m_generatedInfo.m_size.z);
- }
- } else if (pathMan.m_pathUnits.m_buffer[vehicleData.m_path].GetNextPosition(pathPosIndex >> 1, out nextPathPos)) {
- // NON-STOCK CODE START
- ushort transitNodeId;
- if (curPathPos.m_offset < 128) {
- transitNodeId = netMan.m_segments.m_buffer[curPathPos.m_segment].m_startNode;
- } else {
- transitNodeId = netMan.m_segments.m_buffer[curPathPos.m_segment].m_endNode;
- }
-
- if (VehicleBehaviorManager.Instance.IsSpaceReservationAllowed(transitNodeId, curPathPos, nextPathPos)) {
- // NON-STOCK CODE END
-
- uint nextLaneId = PathManager.GetLaneID(nextPathPos);
- if (nextLaneId != 0u) {
- netMan.m_lanes.m_buffer[nextLaneId].ReserveSpace(this.m_info.m_generatedInfo.m_size.z);
- }
- } // NON-STOCK CODE
- }
- }
- }
-
- beforeRotToTargetPos1Diff = curInvRot * beforeRotToTargetPos1Diff;
- float negTotalAttachLen = -((this.m_info.m_generatedInfo.m_wheelBase + frontVehInfo.m_generatedInfo.m_wheelBase) * 0.5f + attachOffset + frontAttachOffset);
- bool hasPath = false;
- if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
- float u1;
- float u2;
- if (Line3.Intersect(posBeforeWheelRot, vehicleData.m_targetPos1, frontPosMinusWheelBaseRot, negTotalAttachLen, out u1, out u2)) {
- targetMotion = beforeRotToTargetPos1Diff * Mathf.Clamp(Mathf.Min(u1, u2) / 0.6f, 0f, 2f);
- } else {
- Line3.DistanceSqr(posBeforeWheelRot, vehicleData.m_targetPos1, frontPosMinusWheelBaseRot, out u1);
- targetMotion = beforeRotToTargetPos1Diff * Mathf.Clamp(u1 / 0.6f, 0f, 2f);
- }
- hasPath = true;
- }
-
- if (hasPath) {
- if (Vector3.Dot(frontPosMinusWheelBaseRot - posBeforeWheelRot, posBeforeWheelRot - posAfterWheelRot) < 0f) {
- motionFactor = 0f;
- }
- } else {
- float frontPosBeforeToAfterWheelRotDist = Vector3.Distance(frontPosMinusWheelBaseRot, posBeforeWheelRot);
- motionFactor = 0f;
- targetMotion = curInvRot * ((frontPosMinusWheelBaseRot - posBeforeWheelRot) * (Mathf.Max(0f, frontPosBeforeToAfterWheelRotDist - negTotalAttachLen) / Mathf.Max(1f, frontPosBeforeToAfterWheelRotDist * 0.6f)));
- }
- } else {
- float estimatedFrameDist = (curSpeed + acceleration) * (0.5f + 0.5f * (curSpeed + acceleration) / braking);
- float maxSpeedAdd = Mathf.Max(curSpeed + acceleration, 2f);
- float meanSpeedAdd = Mathf.Max((estimatedFrameDist - maxSpeedAdd) / 2f, 1f);
- float maxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd;
- float meanSpeedAddSqr = meanSpeedAdd * meanSpeedAdd;
- if (Vector3.Dot(vehicleData.m_targetPos1 - vehicleData.m_targetPos0, (Vector3)vehicleData.m_targetPos0 - posAfterWheelRot) < 0f && vehicleData.m_path != 0u && (leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0) {
- int someIndex = -1;
- UpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posAfterWheelRot, leaderID, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posAfterWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);
- beforeRotToTargetPos1DiffSqrMag = 0f;
- }
-
- int posIndex = 0;
- bool flag3 = false;
- if ((beforeRotToTargetPos1DiffSqrMag < maxSpeedAddSqr || vehicleData.m_targetPos3.w < 0.01f) && (leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0) {
- if (vehicleData.m_path != 0u) {
- UpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, posAfterWheelRot, posBeforeWheelRot, leaderID, ref leaderData, ref posIndex, 1, 4, maxSpeedAddSqr, meanSpeedAddSqr);
- }
- if (posIndex < 4) {
- flag3 = true;
- while (posIndex < 4) {
- vehicleData.SetTargetPos(posIndex, vehicleData.GetTargetPos(posIndex - 1));
- posIndex++;
- }
- }
- beforeRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posBeforeWheelRot;
- beforeRotToTargetPos1DiffSqrMag = beforeRotToTargetPos1Diff.sqrMagnitude;
- }
-
- if ((leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0 && this.m_info.m_vehicleType != VehicleInfo.VehicleType.Monorail) {
- CustomForceTrafficLights(vehicleID, ref vehicleData, curSpeed > 0.1f); // NON-STOCK CODE
- }
-
- if (vehicleData.m_path != 0u) {
- NetManager netMan = Singleton.instance;
- byte pathPosIndex = vehicleData.m_pathPositionIndex;
- byte lastPathOffset = vehicleData.m_lastPathOffset;
- if (pathPosIndex == 255) {
- pathPosIndex = 0;
- }
-
- PathManager pathMan = Singleton.instance;
- PathUnit.Position curPathPos;
- if (pathMan.m_pathUnits.m_buffer[vehicleData.m_path].GetPosition(pathPosIndex >> 1, out curPathPos)) {
- netMan.m_segments.m_buffer[curPathPos.m_segment].AddTraffic(Mathf.RoundToInt(this.m_info.m_generatedInfo.m_size.z * 3f), this.GetNoiseLevel());
- PathUnit.Position nextPathPos;
- if ((pathPosIndex & 1) == 0 || lastPathOffset == 0 || (leaderData.m_flags & Vehicle.Flags.WaitingPath) != (Vehicle.Flags)0) {
- uint laneId = PathManager.GetLaneID(curPathPos);
- if (laneId != 0u) {
- netMan.m_lanes.m_buffer[laneId].ReserveSpace(this.m_info.m_generatedInfo.m_size.z, vehicleID);
- }
- } else if (pathMan.m_pathUnits.m_buffer[vehicleData.m_path].GetNextPosition(pathPosIndex >> 1, out nextPathPos)) {
- // NON-STOCK CODE START
- ushort transitNodeId;
- if (curPathPos.m_offset < 128) {
- transitNodeId = netMan.m_segments.m_buffer[curPathPos.m_segment].m_startNode;
- } else {
- transitNodeId = netMan.m_segments.m_buffer[curPathPos.m_segment].m_endNode;
- }
-
- if (VehicleBehaviorManager.Instance.IsSpaceReservationAllowed(transitNodeId, curPathPos, nextPathPos)) {
- // NON-STOCK CODE END
-
- uint nextLaneId = PathManager.GetLaneID(nextPathPos);
- if (nextLaneId != 0u) {
- netMan.m_lanes.m_buffer[nextLaneId].ReserveSpace(this.m_info.m_generatedInfo.m_size.z, vehicleID);
- }
- } // NON-STOCK CODE
- }
- }
- }
-
- float maxSpeed;
- if ((leaderData.m_flags & Vehicle.Flags.Stopped) != (Vehicle.Flags)0) {
- maxSpeed = 0f;
- } else {
- maxSpeed = Mathf.Min(vehicleData.m_targetPos1.w, GetMaxSpeed(leaderID, ref leaderData));
- }
-
- beforeRotToTargetPos1Diff = curInvRot * beforeRotToTargetPos1Diff;
- if (reversed) {
- beforeRotToTargetPos1Diff = -beforeRotToTargetPos1Diff;
- }
-
- bool blocked = false;
- float forwardLen = 0f;
- if (beforeRotToTargetPos1DiffSqrMag > 1f) {
- forward = VectorUtils.NormalizeXZ(beforeRotToTargetPos1Diff, out forwardLen);
- if (forwardLen > 1f) {
- Vector3 fwd = beforeRotToTargetPos1Diff;
- maxSpeedAdd = Mathf.Max(curSpeed, 2f);
- maxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd;
- if (beforeRotToTargetPos1DiffSqrMag > maxSpeedAddSqr) {
- float num20 = maxSpeedAdd / Mathf.Sqrt(beforeRotToTargetPos1DiffSqrMag);
- fwd.x *= num20;
- fwd.y *= num20;
- }
-
- if (fwd.z < -1f) {
- if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
- Vector3 targetPos0TargetPos1Diff = vehicleData.m_targetPos1 - vehicleData.m_targetPos0;
- targetPos0TargetPos1Diff = curInvRot * targetPos0TargetPos1Diff;
- if (reversed) {
- targetPos0TargetPos1Diff = -targetPos0TargetPos1Diff;
- }
-
- if (targetPos0TargetPos1Diff.z < -0.01f) {
- if (beforeRotToTargetPos1Diff.z < Mathf.Abs(beforeRotToTargetPos1Diff.x) * -10f) {
- if (curSpeed < 0.01f) {
- Reverse(leaderID, ref leaderData);
- return;
- }
-
- fwd.z = 0f;
- beforeRotToTargetPos1Diff = Vector3.zero;
- maxSpeed = 0f;
- } else {
- posBeforeWheelRot = posAfterWheelRot + Vector3.Normalize(vehicleData.m_targetPos1 - vehicleData.m_targetPos0) * this.m_info.m_generatedInfo.m_wheelBase;
- posIndex = -1;
- UpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, vehicleData.m_targetPos1, leaderID, ref leaderData, ref posIndex, 0, 0, Vector3.SqrMagnitude(vehicleData.m_targetPos1 - vehicleData.m_targetPos0) + 1f, 1f);
- }
- } else {
- posIndex = -1;
- UpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posAfterWheelRot, leaderID, ref leaderData, ref posIndex, 0, 0, Vector3.SqrMagnitude(posAfterWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);
- vehicleData.m_targetPos1 = posBeforeWheelRot;
- fwd.z = 0f;
- beforeRotToTargetPos1Diff = Vector3.zero;
- maxSpeed = 0f;
- }
- }
- motionFactor = 0f;
- }
-
- forward = VectorUtils.NormalizeXZ(fwd, out forwardLen);
- float curve = Mathf.PI / 2f /* 1.57079637f*/ * (1f - forward.z); // <- constant: a bit inaccurate PI/2
- if (forwardLen > 1f) {
- curve /= forwardLen;
- }
-
- maxSpeed = Mathf.Min(maxSpeed, this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1000f, curve));
- float targetDist = forwardLen;
- maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos2.w, braking));
- targetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos2 - vehicleData.m_targetPos1);
- maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos3.w, braking));
- targetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos3 - vehicleData.m_targetPos2);
- maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, 0f, braking));
- if (maxSpeed < curSpeed) {
- float brake = Mathf.Max(acceleration, Mathf.Min(braking, curSpeed));
- targetSpeed = Mathf.Max(maxSpeed, curSpeed - brake);
- } else {
- float accel = Mathf.Max(acceleration, Mathf.Min(braking, -curSpeed));
- targetSpeed = Mathf.Min(maxSpeed, curSpeed + accel);
- }
- }
- } else if (curSpeed < 0.1f && flag3 && vehicleInfo.m_vehicleAI.ArriveAtDestination(leaderID, ref leaderData)) {
- leaderData.Unspawn(leaderID);
- return;
- }
-
- if ((leaderData.m_flags & Vehicle.Flags.Stopped) == (Vehicle.Flags)0 && maxSpeed < 0.1f) {
- blocked = true;
- }
-
- if (blocked) {
- leaderData.m_blockCounter = (byte)Mathf.Min((int)(leaderData.m_blockCounter + 1), 255);
- } else {
- leaderData.m_blockCounter = 0;
- }
-
- if (forwardLen > 1f) {
- if (reversed) {
- forward = -forward;
- }
- targetMotion = forward * targetSpeed;
- } else {
- if (reversed) {
- beforeRotToTargetPos1Diff = -beforeRotToTargetPos1Diff;
- }
- Vector3 vel = Vector3.ClampMagnitude(beforeRotToTargetPos1Diff * 0.5f - curveTangent, braking);
- targetMotion = curveTangent + vel;
- }
- }
-
- Vector3 springs = targetMotion - curveTangent;
- Vector3 targetAfterWheelRotMotion = frameData.m_rotation * targetMotion;
- Vector3 posAfterWheelRotToTargetDiff = Vector3.Normalize((Vector3)vehicleData.m_targetPos0 - posAfterWheelRot) * (targetMotion.magnitude * motionFactor);
- posBeforeWheelRot += targetAfterWheelRotMotion;
- posAfterWheelRot += posAfterWheelRotToTargetDiff;
-
- Vector3 targetPos;
- if (reversed) {
- frameData.m_rotation = Quaternion.LookRotation(posAfterWheelRot - posBeforeWheelRot);
- targetPos = posBeforeWheelRot + frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
- } else {
- frameData.m_rotation = Quaternion.LookRotation(posBeforeWheelRot - posAfterWheelRot);
- targetPos = posBeforeWheelRot - frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
- }
- frameData.m_velocity = targetPos - frameData.m_position;
-
- if (frontVehicleId != 0) {
- frameData.m_position += frameData.m_velocity * 0.6f;
- } else {
- frameData.m_position += frameData.m_velocity * 0.5f;
- }
- frameData.m_swayVelocity = frameData.m_swayVelocity * (1f - this.m_info.m_dampers) - springs * (1f - this.m_info.m_springs) - frameData.m_swayPosition * this.m_info.m_springs;
- frameData.m_swayPosition += frameData.m_swayVelocity * 0.5f;
- frameData.m_steerAngle = 0f;
- frameData.m_travelDistance += targetMotion.z;
- frameData.m_lightIntensity.x = ((!reversed) ? 5f : 0f);
- frameData.m_lightIntensity.y = ((!reversed) ? 0f : 5f);
- frameData.m_lightIntensity.z = 0f;
- frameData.m_lightIntensity.w = 0f;
- frameData.m_underground = ((vehicleData.m_flags & Vehicle.Flags.Underground) != (Vehicle.Flags)0);
- frameData.m_transition = ((vehicleData.m_flags & Vehicle.Flags.Transition) != (Vehicle.Flags)0);
- //base.SimulationStep(vehicleID, ref vehicleData, ref frameData, leaderID, ref leaderData, lodPhysics);
- }
-
- [RedirectMethod]
- public void CustomCalculateSegmentPosition(ushort vehicleID, ref Vehicle vehicleData, PathUnit.Position position, uint laneID, byte offset, out Vector3 pos, out Vector3 dir, out float maxSpeed) {
- NetManager instance = Singleton.instance;
- instance.m_lanes.m_buffer[laneID].CalculatePositionAndDirection((float)offset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR, out pos, out dir);
- NetInfo info = instance.m_segments.m_buffer[(int)position.m_segment].Info;
- if (info.m_lanes != null && info.m_lanes.Length > (int)position.m_lane) {
- float laneSpeedLimit = Options.customSpeedLimitsEnabled ? SpeedLimitManager.Instance.GetLockFreeGameSpeedLimit(position.m_segment, position.m_lane, laneID, info.m_lanes[position.m_lane]) : info.m_lanes[position.m_lane].m_speedLimit;
- maxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, laneSpeedLimit, instance.m_lanes.m_buffer[laneID].m_curve);
- } else {
- maxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1f, 0f);
- }
- }
-
- [RedirectMethod]
- public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) {
- /// NON-STOCK CODE START ///
- ExtVehicleType vehicleType = ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, null);
- if (vehicleType == ExtVehicleType.None) {
+namespace TrafficManager.Custom.AI {
+ using System;
+ using System.Runtime.CompilerServices;
+ using API.Traffic.Data;
+ using API.Traffic.Enums;
+ using ColossalFramework;
+ using ColossalFramework.Math;
+ using CSUtil.Commons;
+ using Custom.PathFinding;
+ using Manager;
+ using Manager.Impl;
+ using RedirectionFramework.Attributes;
+ using State;
+ using Traffic.Data;
+ using UnityEngine;
+
+ [TargetType(typeof(TrainAI))]
+ public class CustomTrainAI : TrainAI { // TODO inherit from VehicleAI (in order to keep the correct references to `base`)
+ [RedirectMethod]
+ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {
+ IExtVehicleManager extVehicleMan = Constants.ManagerFactory.ExtVehicleManager;
+
+ if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) {
+ byte pathFindFlags = Singleton.instance.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;
+
+ if ((pathFindFlags & PathUnit.FLAG_READY) != 0) {
+ try {
+ this.PathFindReady(vehicleId, ref vehicleData);
+ } catch (Exception e) {
+ Log.Warning($"TrainAI.PathFindReady({vehicleId}) for vehicle {vehicleData.Info?.m_class?.name} threw an exception: {e.ToString()}");
+ vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ vehicleData.m_path = 0u;
+ vehicleData.Unspawn(vehicleId);
+ return;
+ }
+ } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) {
+ vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ vehicleData.m_path = 0u;
+ vehicleData.Unspawn(vehicleId);
+ return;
+ }
+ } else {
+ if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) {
+ this.TrySpawn(vehicleId, ref vehicleData);
+ }
+ }
+
+ // NON-STOCK CODE START
+ extVehicleMan.UpdateVehiclePosition(vehicleId, ref vehicleData);
+
+ if (!Options.isStockLaneChangerUsed() && (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0) {
+ // Advanced AI traffic measurement
+ extVehicleMan.LogTraffic(vehicleId, ref vehicleData);
+ }
+ // NON-STOCK CODE END
+
+ bool reversed = (vehicleData.m_flags & Vehicle.Flags.Reversed) != 0;
+ ushort connectedVehicleId;
+ if (reversed) {
+ connectedVehicleId = vehicleData.GetLastVehicle(vehicleId);
+ } else {
+ connectedVehicleId = vehicleId;
+ }
+
+ VehicleManager instance = Singleton.instance;
+ VehicleInfo info = instance.m_vehicles.m_buffer[(int)connectedVehicleId].Info;
+ info.m_vehicleAI.SimulationStep(connectedVehicleId, ref instance.m_vehicles.m_buffer[(int)connectedVehicleId], vehicleId, ref vehicleData, 0);
+ if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {
+ return;
+ }
+ bool newReversed = (vehicleData.m_flags & Vehicle.Flags.Reversed) != 0;
+ if (newReversed != reversed) {
+ reversed = newReversed;
+ if (reversed) {
+ connectedVehicleId = vehicleData.GetLastVehicle(vehicleId);
+ } else {
+ connectedVehicleId = vehicleId;
+ }
+ info = instance.m_vehicles.m_buffer[(int)connectedVehicleId].Info;
+ info.m_vehicleAI.SimulationStep(connectedVehicleId, ref instance.m_vehicles.m_buffer[(int)connectedVehicleId], vehicleId, ref vehicleData, 0);
+ if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {
+ return;
+ }
+ newReversed = ((vehicleData.m_flags & Vehicle.Flags.Reversed) != 0);
+ if (newReversed != reversed) {
+ Singleton.instance.ReleaseVehicle(vehicleId);
+ return;
+ }
+ }
+ if (reversed) {
+ connectedVehicleId = instance.m_vehicles.m_buffer[(int)connectedVehicleId].m_leadingVehicle;
+ int num2 = 0;
+ while (connectedVehicleId != 0) {
+ info = instance.m_vehicles.m_buffer[(int)connectedVehicleId].Info;
+ info.m_vehicleAI.SimulationStep(connectedVehicleId, ref instance.m_vehicles.m_buffer[(int)connectedVehicleId], vehicleId, ref vehicleData, 0);
+ if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {
+ return;
+ }
+ connectedVehicleId = instance.m_vehicles.m_buffer[(int)connectedVehicleId].m_leadingVehicle;
+ if (++num2 > 16384) {
+ CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
+ break;
+ }
+ }
+ } else {
+ connectedVehicleId = instance.m_vehicles.m_buffer[(int)connectedVehicleId].m_trailingVehicle;
+ int num3 = 0;
+ while (connectedVehicleId != 0) {
+ info = instance.m_vehicles.m_buffer[(int)connectedVehicleId].Info;
+ info.m_vehicleAI.SimulationStep(connectedVehicleId, ref instance.m_vehicles.m_buffer[(int)connectedVehicleId], vehicleId, ref vehicleData, 0);
+ if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {
+ return;
+ }
+ connectedVehicleId = instance.m_vehicles.m_buffer[(int)connectedVehicleId].m_trailingVehicle;
+ if (++num3 > 16384) {
+ CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
+ break;
+ }
+ }
+ }
+ if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo)) == 0) {
+ Singleton.instance.ReleaseVehicle(vehicleId);
+ } else if (vehicleData.m_blockCounter == 255) {
+ // NON-STOCK CODE START
+ if (VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData)) {
+ // NON-STOCK CODE END
+ Singleton.instance.ReleaseVehicle(vehicleId);
+ } // NON-STOCK CODE
+ }
+ }
+
+ [RedirectMethod]
+ public void CustomSimulationStep(ushort vehicleID, ref Vehicle vehicleData, ref Vehicle.Frame frameData, ushort leaderID, ref Vehicle leaderData, int lodPhysics) {
+ bool reversed = (leaderData.m_flags & Vehicle.Flags.Reversed) != (Vehicle.Flags)0;
+ ushort frontVehicleId = (!reversed) ? vehicleData.m_leadingVehicle : vehicleData.m_trailingVehicle;
+ VehicleInfo vehicleInfo;
+ if (leaderID != vehicleID) {
+ vehicleInfo = leaderData.Info;
+ } else {
+ vehicleInfo = this.m_info;
+ }
+ TrainAI trainAI = vehicleInfo.m_vehicleAI as TrainAI;
+ if (frontVehicleId != 0) {
+ frameData.m_position += frameData.m_velocity * 0.4f;
+ } else {
+ frameData.m_position += frameData.m_velocity * 0.5f;
+ }
+ frameData.m_swayPosition += frameData.m_swayVelocity * 0.5f;
+
+ Vector3 posBeforeWheelRot = frameData.m_position;
+ Vector3 posAfterWheelRot = frameData.m_position;
+ Vector3 wheelBaseRot = frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
+ if (reversed) {
+ posBeforeWheelRot -= wheelBaseRot;
+ posAfterWheelRot += wheelBaseRot;
+ } else {
+ posBeforeWheelRot += wheelBaseRot;
+ posAfterWheelRot -= wheelBaseRot;
+ }
+
+ float acceleration = this.m_info.m_acceleration;
+ float braking = this.m_info.m_braking;
+ float curSpeed = frameData.m_velocity.magnitude;
+
+ Vector3 beforeRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posBeforeWheelRot;
+ float beforeRotToTargetPos1DiffSqrMag = beforeRotToTargetPos1Diff.sqrMagnitude;
+
+ Quaternion curInvRot = Quaternion.Inverse(frameData.m_rotation);
+ Vector3 curveTangent = curInvRot * frameData.m_velocity;
+
+ Vector3 forward = Vector3.forward;
+ Vector3 targetMotion = Vector3.zero;
+ float targetSpeed = 0f;
+ float motionFactor = 0.5f;
+
+ if (frontVehicleId != 0) {
+ VehicleManager vehMan = Singleton.instance;
+ Vehicle.Frame frontVehLastFrameData = vehMan.m_vehicles.m_buffer[(int)frontVehicleId].GetLastFrameData();
+ VehicleInfo frontVehInfo = vehMan.m_vehicles.m_buffer[(int)frontVehicleId].Info;
+
+ float attachOffset;
+ if ((vehicleData.m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags)0 != reversed) {
+ attachOffset = this.m_info.m_attachOffsetBack - this.m_info.m_generatedInfo.m_size.z * 0.5f;
+ } else {
+ attachOffset = this.m_info.m_attachOffsetFront - this.m_info.m_generatedInfo.m_size.z * 0.5f;
+ }
+
+ float frontAttachOffset;
+ if ((vehMan.m_vehicles.m_buffer[(int)frontVehicleId].m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags)0 != reversed) {
+ frontAttachOffset = frontVehInfo.m_attachOffsetFront - frontVehInfo.m_generatedInfo.m_size.z * 0.5f;
+ } else {
+ frontAttachOffset = frontVehInfo.m_attachOffsetBack - frontVehInfo.m_generatedInfo.m_size.z * 0.5f;
+ }
+
+ Vector3 posMinusAttachOffset = frameData.m_position;
+ if (reversed) {
+ posMinusAttachOffset += frameData.m_rotation * new Vector3(0f, 0f, attachOffset);
+ } else {
+ posMinusAttachOffset -= frameData.m_rotation * new Vector3(0f, 0f, attachOffset);
+ }
+
+ Vector3 frontPosPlusAttachOffset = frontVehLastFrameData.m_position;
+ if (reversed) {
+ frontPosPlusAttachOffset -= frontVehLastFrameData.m_rotation * new Vector3(0f, 0f, frontAttachOffset);
+ } else {
+ frontPosPlusAttachOffset += frontVehLastFrameData.m_rotation * new Vector3(0f, 0f, frontAttachOffset);
+ }
+
+ Vector3 frontPosMinusWheelBaseRot = frontVehLastFrameData.m_position;
+ wheelBaseRot = frontVehLastFrameData.m_rotation * new Vector3(0f, 0f, frontVehInfo.m_generatedInfo.m_wheelBase * 0.5f);
+ if (reversed) {
+ frontPosMinusWheelBaseRot += wheelBaseRot;
+ } else {
+ frontPosMinusWheelBaseRot -= wheelBaseRot;
+ }
+
+ if (Vector3.Dot(vehicleData.m_targetPos1 - vehicleData.m_targetPos0, (Vector3)vehicleData.m_targetPos0 - posAfterWheelRot) < 0f && vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
+ int someIndex = -1;
+ UpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posAfterWheelRot, 0, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posAfterWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);
+ beforeRotToTargetPos1DiffSqrMag = 0f;
+ }
+
+ float maxAttachDist = Mathf.Max(Vector3.Distance(posMinusAttachOffset, frontPosPlusAttachOffset), 2f);
+ float one = 1f;
+ float maxAttachSqrDist = maxAttachDist * maxAttachDist;
+ float oneSqr = one * one;
+ int i = 0;
+ if (beforeRotToTargetPos1DiffSqrMag < maxAttachSqrDist) {
+ if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
+ UpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, posAfterWheelRot, posBeforeWheelRot, 0, ref leaderData, ref i, 1, 2, maxAttachSqrDist, oneSqr);
+ }
+ while (i < 4) {
+ vehicleData.SetTargetPos(i, vehicleData.GetTargetPos(i - 1));
+ i++;
+ }
+ beforeRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posBeforeWheelRot;
+ beforeRotToTargetPos1DiffSqrMag = beforeRotToTargetPos1Diff.sqrMagnitude;
+ }
+
+ if (vehicleData.m_path != 0u) {
+ NetManager netMan = Singleton.instance;
+ byte pathPosIndex = vehicleData.m_pathPositionIndex;
+ byte lastPathOffset = vehicleData.m_lastPathOffset;
+ if (pathPosIndex == 255) {
+ pathPosIndex = 0;
+ }
+
+ PathManager pathMan = Singleton.instance;
+ PathUnit.Position curPathPos;
+ if (pathMan.m_pathUnits.m_buffer[vehicleData.m_path].GetPosition(pathPosIndex >> 1, out curPathPos)) {
+ netMan.m_segments.m_buffer[(int)curPathPos.m_segment].AddTraffic(Mathf.RoundToInt(this.m_info.m_generatedInfo.m_size.z * 3f), this.GetNoiseLevel());
+ PathUnit.Position nextPathPos; // NON-STOCK CODE
+ if ((pathPosIndex & 1) == 0 || lastPathOffset == 0 || (leaderData.m_flags & Vehicle.Flags.WaitingPath) != (Vehicle.Flags)0) {
+ uint laneId = PathManager.GetLaneID(curPathPos);
+ if (laneId != 0u) {
+ netMan.m_lanes.m_buffer[laneId].ReserveSpace(this.m_info.m_generatedInfo.m_size.z);
+ }
+ } else if (pathMan.m_pathUnits.m_buffer[vehicleData.m_path].GetNextPosition(pathPosIndex >> 1, out nextPathPos)) {
+ // NON-STOCK CODE START
+ ushort transitNodeId;
+ if (curPathPos.m_offset < 128) {
+ transitNodeId = netMan.m_segments.m_buffer[curPathPos.m_segment].m_startNode;
+ } else {
+ transitNodeId = netMan.m_segments.m_buffer[curPathPos.m_segment].m_endNode;
+ }
+
+ if (VehicleBehaviorManager.Instance.IsSpaceReservationAllowed(transitNodeId, curPathPos, nextPathPos)) {
+ // NON-STOCK CODE END
+
+ uint nextLaneId = PathManager.GetLaneID(nextPathPos);
+ if (nextLaneId != 0u) {
+ netMan.m_lanes.m_buffer[nextLaneId].ReserveSpace(this.m_info.m_generatedInfo.m_size.z);
+ }
+ } // NON-STOCK CODE
+ }
+ }
+ }
+
+ beforeRotToTargetPos1Diff = curInvRot * beforeRotToTargetPos1Diff;
+ float negTotalAttachLen = -((this.m_info.m_generatedInfo.m_wheelBase + frontVehInfo.m_generatedInfo.m_wheelBase) * 0.5f + attachOffset + frontAttachOffset);
+ bool hasPath = false;
+ if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
+ float u1;
+ float u2;
+ if (Line3.Intersect(posBeforeWheelRot, vehicleData.m_targetPos1, frontPosMinusWheelBaseRot, negTotalAttachLen, out u1, out u2)) {
+ targetMotion = beforeRotToTargetPos1Diff * Mathf.Clamp(Mathf.Min(u1, u2) / 0.6f, 0f, 2f);
+ } else {
+ Line3.DistanceSqr(posBeforeWheelRot, vehicleData.m_targetPos1, frontPosMinusWheelBaseRot, out u1);
+ targetMotion = beforeRotToTargetPos1Diff * Mathf.Clamp(u1 / 0.6f, 0f, 2f);
+ }
+ hasPath = true;
+ }
+
+ if (hasPath) {
+ if (Vector3.Dot(frontPosMinusWheelBaseRot - posBeforeWheelRot, posBeforeWheelRot - posAfterWheelRot) < 0f) {
+ motionFactor = 0f;
+ }
+ } else {
+ float frontPosBeforeToAfterWheelRotDist = Vector3.Distance(frontPosMinusWheelBaseRot, posBeforeWheelRot);
+ motionFactor = 0f;
+ targetMotion = curInvRot * ((frontPosMinusWheelBaseRot - posBeforeWheelRot) * (Mathf.Max(0f, frontPosBeforeToAfterWheelRotDist - negTotalAttachLen) / Mathf.Max(1f, frontPosBeforeToAfterWheelRotDist * 0.6f)));
+ }
+ } else {
+ float estimatedFrameDist = (curSpeed + acceleration) * (0.5f + 0.5f * (curSpeed + acceleration) / braking);
+ float maxSpeedAdd = Mathf.Max(curSpeed + acceleration, 2f);
+ float meanSpeedAdd = Mathf.Max((estimatedFrameDist - maxSpeedAdd) / 2f, 1f);
+ float maxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd;
+ float meanSpeedAddSqr = meanSpeedAdd * meanSpeedAdd;
+ if (Vector3.Dot(vehicleData.m_targetPos1 - vehicleData.m_targetPos0, (Vector3)vehicleData.m_targetPos0 - posAfterWheelRot) < 0f && vehicleData.m_path != 0u && (leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0) {
+ int someIndex = -1;
+ UpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posAfterWheelRot, leaderID, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posAfterWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);
+ beforeRotToTargetPos1DiffSqrMag = 0f;
+ }
+
+ int posIndex = 0;
+ bool flag3 = false;
+ if ((beforeRotToTargetPos1DiffSqrMag < maxSpeedAddSqr || vehicleData.m_targetPos3.w < 0.01f) && (leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0) {
+ if (vehicleData.m_path != 0u) {
+ UpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, posAfterWheelRot, posBeforeWheelRot, leaderID, ref leaderData, ref posIndex, 1, 4, maxSpeedAddSqr, meanSpeedAddSqr);
+ }
+ if (posIndex < 4) {
+ flag3 = true;
+ while (posIndex < 4) {
+ vehicleData.SetTargetPos(posIndex, vehicleData.GetTargetPos(posIndex - 1));
+ posIndex++;
+ }
+ }
+ beforeRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posBeforeWheelRot;
+ beforeRotToTargetPos1DiffSqrMag = beforeRotToTargetPos1Diff.sqrMagnitude;
+ }
+
+ if ((leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0 && this.m_info.m_vehicleType != VehicleInfo.VehicleType.Monorail) {
+ CustomForceTrafficLights(vehicleID, ref vehicleData, curSpeed > 0.1f); // NON-STOCK CODE
+ }
+
+ if (vehicleData.m_path != 0u) {
+ NetManager netMan = Singleton.instance;
+ byte pathPosIndex = vehicleData.m_pathPositionIndex;
+ byte lastPathOffset = vehicleData.m_lastPathOffset;
+ if (pathPosIndex == 255) {
+ pathPosIndex = 0;
+ }
+
+ PathManager pathMan = Singleton.instance;
+ PathUnit.Position curPathPos;
+ if (pathMan.m_pathUnits.m_buffer[vehicleData.m_path].GetPosition(pathPosIndex >> 1, out curPathPos)) {
+ netMan.m_segments.m_buffer[curPathPos.m_segment].AddTraffic(Mathf.RoundToInt(this.m_info.m_generatedInfo.m_size.z * 3f), this.GetNoiseLevel());
+ PathUnit.Position nextPathPos;
+ if ((pathPosIndex & 1) == 0 || lastPathOffset == 0 || (leaderData.m_flags & Vehicle.Flags.WaitingPath) != (Vehicle.Flags)0) {
+ uint laneId = PathManager.GetLaneID(curPathPos);
+ if (laneId != 0u) {
+ netMan.m_lanes.m_buffer[laneId].ReserveSpace(this.m_info.m_generatedInfo.m_size.z, vehicleID);
+ }
+ } else if (pathMan.m_pathUnits.m_buffer[vehicleData.m_path].GetNextPosition(pathPosIndex >> 1, out nextPathPos)) {
+ // NON-STOCK CODE START
+ ushort transitNodeId;
+ if (curPathPos.m_offset < 128) {
+ transitNodeId = netMan.m_segments.m_buffer[curPathPos.m_segment].m_startNode;
+ } else {
+ transitNodeId = netMan.m_segments.m_buffer[curPathPos.m_segment].m_endNode;
+ }
+
+ if (VehicleBehaviorManager.Instance.IsSpaceReservationAllowed(transitNodeId, curPathPos, nextPathPos)) {
+ // NON-STOCK CODE END
+
+ uint nextLaneId = PathManager.GetLaneID(nextPathPos);
+ if (nextLaneId != 0u) {
+ netMan.m_lanes.m_buffer[nextLaneId].ReserveSpace(this.m_info.m_generatedInfo.m_size.z, vehicleID);
+ }
+ } // NON-STOCK CODE
+ }
+ }
+ }
+
+ float maxSpeed;
+ if ((leaderData.m_flags & Vehicle.Flags.Stopped) != (Vehicle.Flags)0) {
+ maxSpeed = 0f;
+ } else {
+ maxSpeed = Mathf.Min(vehicleData.m_targetPos1.w, GetMaxSpeed(leaderID, ref leaderData));
+ }
+
+ beforeRotToTargetPos1Diff = curInvRot * beforeRotToTargetPos1Diff;
+ if (reversed) {
+ beforeRotToTargetPos1Diff = -beforeRotToTargetPos1Diff;
+ }
+
+ bool blocked = false;
+ float forwardLen = 0f;
+ if (beforeRotToTargetPos1DiffSqrMag > 1f) {
+ forward = VectorUtils.NormalizeXZ(beforeRotToTargetPos1Diff, out forwardLen);
+ if (forwardLen > 1f) {
+ Vector3 fwd = beforeRotToTargetPos1Diff;
+ maxSpeedAdd = Mathf.Max(curSpeed, 2f);
+ maxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd;
+ if (beforeRotToTargetPos1DiffSqrMag > maxSpeedAddSqr) {
+ float num20 = maxSpeedAdd / Mathf.Sqrt(beforeRotToTargetPos1DiffSqrMag);
+ fwd.x *= num20;
+ fwd.y *= num20;
+ }
+
+ if (fwd.z < -1f) {
+ if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
+ Vector3 targetPos0TargetPos1Diff = vehicleData.m_targetPos1 - vehicleData.m_targetPos0;
+ targetPos0TargetPos1Diff = curInvRot * targetPos0TargetPos1Diff;
+ if (reversed) {
+ targetPos0TargetPos1Diff = -targetPos0TargetPos1Diff;
+ }
+
+ if (targetPos0TargetPos1Diff.z < -0.01f) {
+ if (beforeRotToTargetPos1Diff.z < Mathf.Abs(beforeRotToTargetPos1Diff.x) * -10f) {
+ if (curSpeed < 0.01f) {
+ Reverse(leaderID, ref leaderData);
+ return;
+ }
+
+ fwd.z = 0f;
+ beforeRotToTargetPos1Diff = Vector3.zero;
+ maxSpeed = 0f;
+ } else {
+ posBeforeWheelRot = posAfterWheelRot + Vector3.Normalize(vehicleData.m_targetPos1 - vehicleData.m_targetPos0) * this.m_info.m_generatedInfo.m_wheelBase;
+ posIndex = -1;
+ UpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, vehicleData.m_targetPos1, leaderID, ref leaderData, ref posIndex, 0, 0, Vector3.SqrMagnitude(vehicleData.m_targetPos1 - vehicleData.m_targetPos0) + 1f, 1f);
+ }
+ } else {
+ posIndex = -1;
+ UpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posAfterWheelRot, leaderID, ref leaderData, ref posIndex, 0, 0, Vector3.SqrMagnitude(posAfterWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);
+ vehicleData.m_targetPos1 = posBeforeWheelRot;
+ fwd.z = 0f;
+ beforeRotToTargetPos1Diff = Vector3.zero;
+ maxSpeed = 0f;
+ }
+ }
+ motionFactor = 0f;
+ }
+
+ forward = VectorUtils.NormalizeXZ(fwd, out forwardLen);
+ float curve = Mathf.PI / 2f /* 1.57079637f*/ * (1f - forward.z); // <- constant: a bit inaccurate PI/2
+ if (forwardLen > 1f) {
+ curve /= forwardLen;
+ }
+
+ maxSpeed = Mathf.Min(maxSpeed, this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1000f, curve));
+ float targetDist = forwardLen;
+ maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos2.w, braking));
+ targetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos2 - vehicleData.m_targetPos1);
+ maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos3.w, braking));
+ targetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos3 - vehicleData.m_targetPos2);
+ maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, 0f, braking));
+ if (maxSpeed < curSpeed) {
+ float brake = Mathf.Max(acceleration, Mathf.Min(braking, curSpeed));
+ targetSpeed = Mathf.Max(maxSpeed, curSpeed - brake);
+ } else {
+ float accel = Mathf.Max(acceleration, Mathf.Min(braking, -curSpeed));
+ targetSpeed = Mathf.Min(maxSpeed, curSpeed + accel);
+ }
+ }
+ } else if (curSpeed < 0.1f && flag3 && vehicleInfo.m_vehicleAI.ArriveAtDestination(leaderID, ref leaderData)) {
+ leaderData.Unspawn(leaderID);
+ return;
+ }
+
+ if ((leaderData.m_flags & Vehicle.Flags.Stopped) == (Vehicle.Flags)0 && maxSpeed < 0.1f) {
+ blocked = true;
+ }
+
+ if (blocked) {
+ leaderData.m_blockCounter = (byte)Mathf.Min((int)(leaderData.m_blockCounter + 1), 255);
+ } else {
+ leaderData.m_blockCounter = 0;
+ }
+
+ if (forwardLen > 1f) {
+ if (reversed) {
+ forward = -forward;
+ }
+ targetMotion = forward * targetSpeed;
+ } else {
+ if (reversed) {
+ beforeRotToTargetPos1Diff = -beforeRotToTargetPos1Diff;
+ }
+ Vector3 vel = Vector3.ClampMagnitude(beforeRotToTargetPos1Diff * 0.5f - curveTangent, braking);
+ targetMotion = curveTangent + vel;
+ }
+ }
+
+ Vector3 springs = targetMotion - curveTangent;
+ Vector3 targetAfterWheelRotMotion = frameData.m_rotation * targetMotion;
+ Vector3 posAfterWheelRotToTargetDiff = Vector3.Normalize((Vector3)vehicleData.m_targetPos0 - posAfterWheelRot) * (targetMotion.magnitude * motionFactor);
+ posBeforeWheelRot += targetAfterWheelRotMotion;
+ posAfterWheelRot += posAfterWheelRotToTargetDiff;
+
+ Vector3 targetPos;
+ if (reversed) {
+ frameData.m_rotation = Quaternion.LookRotation(posAfterWheelRot - posBeforeWheelRot);
+ targetPos = posBeforeWheelRot + frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
+ } else {
+ frameData.m_rotation = Quaternion.LookRotation(posBeforeWheelRot - posAfterWheelRot);
+ targetPos = posBeforeWheelRot - frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
+ }
+ frameData.m_velocity = targetPos - frameData.m_position;
+
+ if (frontVehicleId != 0) {
+ frameData.m_position += frameData.m_velocity * 0.6f;
+ } else {
+ frameData.m_position += frameData.m_velocity * 0.5f;
+ }
+ frameData.m_swayVelocity = frameData.m_swayVelocity * (1f - this.m_info.m_dampers) - springs * (1f - this.m_info.m_springs) - frameData.m_swayPosition * this.m_info.m_springs;
+ frameData.m_swayPosition += frameData.m_swayVelocity * 0.5f;
+ frameData.m_steerAngle = 0f;
+ frameData.m_travelDistance += targetMotion.z;
+ frameData.m_lightIntensity.x = ((!reversed) ? 5f : 0f);
+ frameData.m_lightIntensity.y = ((!reversed) ? 0f : 5f);
+ frameData.m_lightIntensity.z = 0f;
+ frameData.m_lightIntensity.w = 0f;
+ frameData.m_underground = ((vehicleData.m_flags & Vehicle.Flags.Underground) != (Vehicle.Flags)0);
+ frameData.m_transition = ((vehicleData.m_flags & Vehicle.Flags.Transition) != (Vehicle.Flags)0);
+ //base.SimulationStep(vehicleID, ref vehicleData, ref frameData, leaderID, ref leaderData, lodPhysics);
+ }
+
+ [RedirectMethod]
+ public void CustomCalculateSegmentPosition(ushort vehicleID, ref Vehicle vehicleData, PathUnit.Position position, uint laneID, byte offset, out Vector3 pos, out Vector3 dir, out float maxSpeed) {
+ NetManager instance = Singleton.instance;
+ instance.m_lanes.m_buffer[laneID].CalculatePositionAndDirection((float)offset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR, out pos, out dir);
+ NetInfo info = instance.m_segments.m_buffer[(int)position.m_segment].Info;
+ if (info.m_lanes != null && info.m_lanes.Length > (int)position.m_lane) {
+ float laneSpeedLimit = Options.customSpeedLimitsEnabled ? SpeedLimitManager.Instance.GetLockFreeGameSpeedLimit(position.m_segment, position.m_lane, laneID, info.m_lanes[position.m_lane]) : info.m_lanes[position.m_lane].m_speedLimit;
+ maxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, laneSpeedLimit, instance.m_lanes.m_buffer[laneID].m_curve);
+ } else {
+ maxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1f, 0f);
+ }
+ }
+
+ [RedirectMethod]
+ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) {
+ /// NON-STOCK CODE START ///
+ ExtVehicleType vehicleType = ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, null);
+ if (vehicleType == ExtVehicleType.None) {
#if DEBUG
- Log.Warning($"CustomTrainAI.CustomStartPathFind: Vehicle {vehicleID} does not have a valid vehicle type!");
+ Log.Warning($"CustomTrainAI.CustomStartPathFind: Vehicle {vehicleID} does not have a valid vehicle type!");
#endif
- vehicleType = ExtVehicleType.RailVehicle;
- } else if (vehicleType == ExtVehicleType.CargoTrain) {
- vehicleType = ExtVehicleType.CargoVehicle;
- }
- /// NON-STOCK CODE END ///
-
- VehicleInfo info = this.m_info;
- if ((vehicleData.m_flags & Vehicle.Flags.Spawned) == 0 && Vector3.Distance(startPos, endPos) < 100f) {
- startPos = endPos;
- }
- bool allowUnderground;
- bool allowUnderground2;
- if (info.m_vehicleType == VehicleInfo.VehicleType.Metro) {
- allowUnderground = true;
- allowUnderground2 = true;
- } else {
- allowUnderground = ((vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0);
- allowUnderground2 = false;
- }
- PathUnit.Position startPosA;
- PathUnit.Position startPosB;
- float startSqrDistA;
- float startSqrDistB;
- PathUnit.Position endPosA;
- PathUnit.Position endPosB;
- float endSqrDistA;
- float endSqrDistB;
- if (CustomPathManager.FindPathPosition(startPos, this.m_transportInfo.m_netService, this.m_transportInfo.m_secondaryNetService, NetInfo.LaneType.Vehicle, info.m_vehicleType, VehicleInfo.VehicleType.None, allowUnderground, false, 32f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB) &&
- CustomPathManager.FindPathPosition(endPos, this.m_transportInfo.m_netService, this.m_transportInfo.m_secondaryNetService, NetInfo.LaneType.Vehicle, info.m_vehicleType, VehicleInfo.VehicleType.None, allowUnderground2, false, 32f, out endPosA, out endPosB, out endSqrDistA, out endSqrDistB)) {
- if (!startBothWays || startSqrDistB > startSqrDistA * 1.2f) {
- startPosB = default(PathUnit.Position);
- }
- if (!endBothWays || endSqrDistB > endSqrDistA * 1.2f) {
- endPosB = default(PathUnit.Position);
- }
- uint path;
- // NON-STOCK CODE START
- PathCreationArgs args;
- args.extPathType = ExtPathType.None;
- args.extVehicleType = vehicleType;
- args.vehicleId = vehicleID;
- args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- args.buildIndex = Singleton.instance.m_currentBuildIndex;
- args.startPosA = startPosA;
- args.startPosB = startPosB;
- args.endPosA = endPosA;
- args.endPosB = endPosB;
- args.vehiclePosition = default(PathUnit.Position);
- args.laneTypes = NetInfo.LaneType.Vehicle;
- args.vehicleTypes = info.m_vehicleType;
- args.maxLength = 20000f;
- args.isHeavyVehicle = false;
- args.hasCombustionEngine = false;
- args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
- args.ignoreFlooded = false;
- args.ignoreCosts = false;
- args.randomParking = false;
- args.stablePath = true;
- args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
- // NON-STOCK CODE END
- if (vehicleData.m_path != 0u) {
- Singleton.instance.ReleasePath(vehicleData.m_path);
- }
- vehicleData.m_path = path;
- vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
- return true;
- }
- }
- return false;
- }
-
- [RedirectMethod]
- public void CustomCheckNextLane(ushort vehicleId, ref Vehicle vehicleData, ref float maxSpeed, PathUnit.Position nextPosition, uint nextLaneId, byte nextOffset, PathUnit.Position refPosition, uint refLaneId, byte refOffset, Bezier3 bezier) {
- NetManager netManager = Singleton.instance;
-
- ushort nextSourceNodeId;
- if (nextOffset < nextPosition.m_offset) {
- nextSourceNodeId = netManager.m_segments.m_buffer[(int)nextPosition.m_segment].m_startNode;
- } else {
- nextSourceNodeId = netManager.m_segments.m_buffer[(int)nextPosition.m_segment].m_endNode;
- }
-
- ushort refTargetNodeId;
- if (refOffset == 0) {
- refTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_startNode;
- } else {
- refTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_endNode;
- }
+ vehicleType = ExtVehicleType.RailVehicle;
+ } else if (vehicleType == ExtVehicleType.CargoTrain) {
+ vehicleType = ExtVehicleType.CargoVehicle;
+ }
+ /// NON-STOCK CODE END ///
+
+ VehicleInfo info = this.m_info;
+ if ((vehicleData.m_flags & Vehicle.Flags.Spawned) == 0 && Vector3.Distance(startPos, endPos) < 100f) {
+ startPos = endPos;
+ }
+ bool allowUnderground;
+ bool allowUnderground2;
+ if (info.m_vehicleType == VehicleInfo.VehicleType.Metro) {
+ allowUnderground = true;
+ allowUnderground2 = true;
+ } else {
+ allowUnderground = ((vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0);
+ allowUnderground2 = false;
+ }
+ PathUnit.Position startPosA;
+ PathUnit.Position startPosB;
+ float startSqrDistA;
+ float startSqrDistB;
+ PathUnit.Position endPosA;
+ PathUnit.Position endPosB;
+ float endSqrDistA;
+ float endSqrDistB;
+ if (CustomPathManager.FindPathPosition(startPos, this.m_transportInfo.m_netService, this.m_transportInfo.m_secondaryNetService, NetInfo.LaneType.Vehicle, info.m_vehicleType, VehicleInfo.VehicleType.None, allowUnderground, false, 32f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB) &&
+ CustomPathManager.FindPathPosition(endPos, this.m_transportInfo.m_netService, this.m_transportInfo.m_secondaryNetService, NetInfo.LaneType.Vehicle, info.m_vehicleType, VehicleInfo.VehicleType.None, allowUnderground2, false, 32f, out endPosA, out endPosB, out endSqrDistA, out endSqrDistB)) {
+ if (!startBothWays || startSqrDistB > startSqrDistA * 1.2f) {
+ startPosB = default(PathUnit.Position);
+ }
+ if (!endBothWays || endSqrDistB > endSqrDistA * 1.2f) {
+ endPosB = default(PathUnit.Position);
+ }
+ uint path;
+ // NON-STOCK CODE START
+ PathCreationArgs args;
+ args.extPathType = ExtPathType.None;
+ args.extVehicleType = vehicleType;
+ args.vehicleId = vehicleID;
+ args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ args.buildIndex = Singleton.instance.m_currentBuildIndex;
+ args.startPosA = startPosA;
+ args.startPosB = startPosB;
+ args.endPosA = endPosA;
+ args.endPosB = endPosB;
+ args.vehiclePosition = default(PathUnit.Position);
+ args.laneTypes = NetInfo.LaneType.Vehicle;
+ args.vehicleTypes = info.m_vehicleType;
+ args.maxLength = 20000f;
+ args.isHeavyVehicle = false;
+ args.hasCombustionEngine = false;
+ args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
+ args.ignoreFlooded = false;
+ args.ignoreCosts = false;
+ args.randomParking = false;
+ args.stablePath = true;
+ args.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
+ // NON-STOCK CODE END
+ if (vehicleData.m_path != 0u) {
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ }
+ vehicleData.m_path = path;
+ vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ [RedirectMethod]
+ public void CustomCheckNextLane(ushort vehicleId, ref Vehicle vehicleData, ref float maxSpeed, PathUnit.Position nextPosition, uint nextLaneId, byte nextOffset, PathUnit.Position refPosition, uint refLaneId, byte refOffset, Bezier3 bezier) {
+ NetManager netManager = Singleton.instance;
+
+ ushort nextSourceNodeId;
+ if (nextOffset < nextPosition.m_offset) {
+ nextSourceNodeId = netManager.m_segments.m_buffer[(int)nextPosition.m_segment].m_startNode;
+ } else {
+ nextSourceNodeId = netManager.m_segments.m_buffer[(int)nextPosition.m_segment].m_endNode;
+ }
+
+ ushort refTargetNodeId;
+ if (refOffset == 0) {
+ refTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_startNode;
+ } else {
+ refTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_endNode;
+ }
#if DEBUG
- bool debug = GlobalConfig.Instance.Debug.Switches[21] && (GlobalConfig.Instance.Debug.NodeId <= 0 || refTargetNodeId == GlobalConfig.Instance.Debug.NodeId) && (GlobalConfig.Instance.Debug.ExtVehicleType == ExtVehicleType.None || GlobalConfig.Instance.Debug.ExtVehicleType == ExtVehicleType.RailVehicle) && (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleId);
-
- if (debug) {
- Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}) called.\n" +
- $"\trefPosition.m_segment={refPosition.m_segment}, refPosition.m_offset={refPosition.m_offset}\n" +
- $"\tnextPosition.m_segment={nextPosition.m_segment}, nextPosition.m_offset={nextPosition.m_offset}\n" +
- $"\trefLaneId={refLaneId}, refOffset={refOffset}\n" +
- $"\tprevLaneId={nextLaneId}, prevOffset={nextOffset}\n" +
- $"\tnextSourceNodeId={nextSourceNodeId}\n" +
- $"\trefTargetNodeId={refTargetNodeId}, refTargetNodeId={refTargetNodeId}");
- }
+ bool debug = GlobalConfig.Instance.Debug.Switches[21]
+ && (GlobalConfig.Instance.Debug.NodeId <= 0
+ || refTargetNodeId == GlobalConfig.Instance.Debug.NodeId)
+ && (GlobalConfig.Instance.Debug.ApiExtVehicleType == ExtVehicleType.None
+ || GlobalConfig.Instance.Debug.ApiExtVehicleType == ExtVehicleType.RailVehicle)
+ && (GlobalConfig.Instance.Debug.VehicleId == 0
+ || GlobalConfig.Instance.Debug.VehicleId == vehicleId);
+
+ if (debug) {
+ Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}) called.\n" +
+ $"\trefPosition.m_segment={refPosition.m_segment}, refPosition.m_offset={refPosition.m_offset}\n" +
+ $"\tnextPosition.m_segment={nextPosition.m_segment}, nextPosition.m_offset={nextPosition.m_offset}\n" +
+ $"\trefLaneId={refLaneId}, refOffset={refOffset}\n" +
+ $"\tprevLaneId={nextLaneId}, prevOffset={nextOffset}\n" +
+ $"\tnextSourceNodeId={nextSourceNodeId}\n" +
+ $"\trefTargetNodeId={refTargetNodeId}, refTargetNodeId={refTargetNodeId}");
+ }
#endif
- Vehicle.Frame lastFrameData = vehicleData.GetLastFrameData();
- float sqrVelocity = lastFrameData.m_velocity.sqrMagnitude;
+ Vehicle.Frame lastFrameData = vehicleData.GetLastFrameData();
+ float sqrVelocity = lastFrameData.m_velocity.sqrMagnitude;
- Vector3 lastPosPlusRot = lastFrameData.m_position;
- Vector3 lastPosMinusRot = lastFrameData.m_position;
- Vector3 rotationAdd = lastFrameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
- lastPosPlusRot += rotationAdd;
- lastPosMinusRot -= rotationAdd;
- float breakingDist = 0.5f * sqrVelocity / this.m_info.m_braking;
- float distToTargetAfterRot = Vector3.Distance(lastPosPlusRot, bezier.a);
- float distToTargetBeforeRot = Vector3.Distance(lastPosMinusRot, bezier.a);
+ Vector3 lastPosPlusRot = lastFrameData.m_position;
+ Vector3 lastPosMinusRot = lastFrameData.m_position;
+ Vector3 rotationAdd = lastFrameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
+ lastPosPlusRot += rotationAdd;
+ lastPosMinusRot -= rotationAdd;
+ float breakingDist = 0.5f * sqrVelocity / this.m_info.m_braking;
+ float distToTargetAfterRot = Vector3.Distance(lastPosPlusRot, bezier.a);
+ float distToTargetBeforeRot = Vector3.Distance(lastPosMinusRot, bezier.a);
#if DEBUG
- if (debug)
- Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): lastPos={lastFrameData.m_position} lastPosMinusRot={lastPosMinusRot} lastPosPlusRot={lastPosPlusRot} rotationAdd={rotationAdd} breakingDist={breakingDist} distToTargetAfterRot={distToTargetAfterRot} distToTargetBeforeRot={distToTargetBeforeRot}");
+ if (debug)
+ Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): lastPos={lastFrameData.m_position} lastPosMinusRot={lastPosMinusRot} lastPosPlusRot={lastPosPlusRot} rotationAdd={rotationAdd} breakingDist={breakingDist} distToTargetAfterRot={distToTargetAfterRot} distToTargetBeforeRot={distToTargetBeforeRot}");
#endif
- if (Mathf.Min(distToTargetAfterRot, distToTargetBeforeRot) >= breakingDist - 5f) {
- /*VehicleManager vehMan = Singleton.instance;
- ushort firstVehicleId = vehicleData.GetFirstVehicle(vehicleId);
- if (VehicleBehaviorManager.Instance.MayDespawn(ref vehMan.m_vehicles.m_buffer[firstVehicleId]) || vehMan.m_vehicles.m_buffer[firstVehicleId].m_blockCounter < 100) {*/ // NON-STOCK CODE
+ if (Mathf.Min(distToTargetAfterRot, distToTargetBeforeRot) >= breakingDist - 5f) {
+ /*VehicleManager vehMan = Singleton.instance;
+ ushort firstVehicleId = vehicleData.GetFirstVehicle(vehicleId);
+ if (VehicleBehaviorManager.Instance.MayDespawn(ref vehMan.m_vehicles.m_buffer[firstVehicleId]) || vehMan.m_vehicles.m_buffer[firstVehicleId].m_blockCounter < 100) {*/ // NON-STOCK CODE
#if DEBUG
- if (debug)
- Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Checking for free space on lane {nextLaneId}.");
+ if (debug)
+ Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Checking for free space on lane {nextLaneId}.");
#endif
- if (!netManager.m_lanes.m_buffer[nextLaneId].CheckSpace(1000f, vehicleId)) {
+ if (!netManager.m_lanes.m_buffer[nextLaneId].CheckSpace(1000f, vehicleId)) {
#if DEBUG
- if (debug)
- Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): No space available on lane {nextLaneId}. ABORT.");
+ if (debug)
+ Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): No space available on lane {nextLaneId}. ABORT.");
#endif
- vehicleData.m_flags2 |= Vehicle.Flags2.Yielding;
- vehicleData.m_waitCounter = 0;
- maxSpeed = 0f;
- return;
- }
-
- Vector3 bezierMiddlePoint = bezier.Position(0.5f);
- Segment3 segment;
- if (Vector3.SqrMagnitude(vehicleData.m_segment.a - bezierMiddlePoint) < Vector3.SqrMagnitude(bezier.a - bezierMiddlePoint)) {
- segment = new Segment3(vehicleData.m_segment.a, bezierMiddlePoint);
- } else {
- segment = new Segment3(bezier.a, bezierMiddlePoint);
- }
+ vehicleData.m_flags2 |= Vehicle.Flags2.Yielding;
+ vehicleData.m_waitCounter = 0;
+ maxSpeed = 0f;
+ return;
+ }
+
+ Vector3 bezierMiddlePoint = bezier.Position(0.5f);
+ Segment3 segment;
+ if (Vector3.SqrMagnitude(vehicleData.m_segment.a - bezierMiddlePoint) < Vector3.SqrMagnitude(bezier.a - bezierMiddlePoint)) {
+ segment = new Segment3(vehicleData.m_segment.a, bezierMiddlePoint);
+ } else {
+ segment = new Segment3(bezier.a, bezierMiddlePoint);
+ }
#if DEBUG
- if (debug)
- Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Checking for overlap (1). segment.a={segment.a} segment.b={segment.b}");
+ if (debug)
+ Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Checking for overlap (1). segment.a={segment.a} segment.b={segment.b}");
#endif
- if (segment.LengthSqr() >= 3f) {
- segment.a += (segment.b - segment.a).normalized * 2.5f;
- if (CustomTrainAI.CheckOverlap(vehicleId, ref vehicleData, segment, vehicleId)) {
+ if (segment.LengthSqr() >= 3f) {
+ segment.a += (segment.b - segment.a).normalized * 2.5f;
+ if (CustomTrainAI.CheckOverlap(vehicleId, ref vehicleData, segment, vehicleId)) {
#if DEBUG
- if (debug)
- Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Overlap detected (1). segment.LengthSqr()={segment.LengthSqr()} segment.a={segment.a} ABORT.");
+ if (debug)
+ Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Overlap detected (1). segment.LengthSqr()={segment.LengthSqr()} segment.a={segment.a} ABORT.");
#endif
- vehicleData.m_flags2 |= Vehicle.Flags2.Yielding;
- vehicleData.m_waitCounter = 0;
- maxSpeed = 0f;
- return;
- }
- }
-
- segment = new Segment3(bezierMiddlePoint, bezier.d);
+ vehicleData.m_flags2 |= Vehicle.Flags2.Yielding;
+ vehicleData.m_waitCounter = 0;
+ maxSpeed = 0f;
+ return;
+ }
+ }
+
+ segment = new Segment3(bezierMiddlePoint, bezier.d);
#if DEBUG
- if (debug)
- Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Checking for overlap (2). segment.a={segment.a} segment.b={segment.b}");
+ if (debug)
+ Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Checking for overlap (2). segment.a={segment.a} segment.b={segment.b}");
#endif
- if (segment.LengthSqr() >= 1f && CustomTrainAI.CheckOverlap(vehicleId, ref vehicleData, segment, vehicleId)) {
+ if (segment.LengthSqr() >= 1f && CustomTrainAI.CheckOverlap(vehicleId, ref vehicleData, segment, vehicleId)) {
#if DEBUG
- if (debug)
- Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Overlap detected (2). ABORT.");
+ if (debug)
+ Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Overlap detected (2). ABORT.");
#endif
- vehicleData.m_flags2 |= Vehicle.Flags2.Yielding;
- vehicleData.m_waitCounter = 0;
- maxSpeed = 0f;
- return;
- }
- //} // NON-STOCK CODE
-
- //if (this.m_info.m_vehicleType != VehicleInfo.VehicleType.Monorail) { // NON-STOCK CODE
- if (nextSourceNodeId == refTargetNodeId) {
+ vehicleData.m_flags2 |= Vehicle.Flags2.Yielding;
+ vehicleData.m_waitCounter = 0;
+ maxSpeed = 0f;
+ return;
+ }
+ //} // NON-STOCK CODE
+
+ //if (this.m_info.m_vehicleType != VehicleInfo.VehicleType.Monorail) { // NON-STOCK CODE
+ if (nextSourceNodeId == refTargetNodeId) {
#if DEBUG
- if (debug)
- Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Checking if vehicle is allowed to change segment.");
+ if (debug)
+ Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Checking if vehicle is allowed to change segment.");
#endif
- float oldMaxSpeed = maxSpeed;
- if (! VehicleBehaviorManager.Instance.MayChangeSegment(vehicleId, ref vehicleData, sqrVelocity, ref refPosition, ref netManager.m_segments.m_buffer[refPosition.m_segment], refTargetNodeId, refLaneId, ref nextPosition, refTargetNodeId, ref netManager.m_nodes.m_buffer[refTargetNodeId], nextLaneId)) {
+ float oldMaxSpeed = maxSpeed;
+ if (! VehicleBehaviorManager.Instance.MayChangeSegment(vehicleId, ref vehicleData, sqrVelocity, ref refPosition, ref netManager.m_segments.m_buffer[refPosition.m_segment], refTargetNodeId, refLaneId, ref nextPosition, refTargetNodeId, ref netManager.m_nodes.m_buffer[refTargetNodeId], nextLaneId)) {
#if DEBUG
- if (debug)
- Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Vehicle is NOT allowed to change segment. ABORT.");
+ if (debug)
+ Log._Debug($"CustomTrainAI.CustomCheckNextLane({vehicleId}): Vehicle is NOT allowed to change segment. ABORT.");
#endif
- maxSpeed = 0;
- return;
- } else {
- ExtVehicleManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData/*, lastFrameData.m_velocity.magnitude*/);
- }
- maxSpeed = oldMaxSpeed;
- }
- //} // NON-STOCK CODE
- }
- }
-
- [RedirectMethod]
- private void CustomForceTrafficLights(ushort vehicleID, ref Vehicle vehicleData, bool reserveSpace) {
- uint pathUnitId = vehicleData.m_path;
- if (pathUnitId != 0u) {
- NetManager netMan = Singleton.instance;
- PathManager pathMan = Singleton.instance;
- byte pathPosIndex = vehicleData.m_pathPositionIndex;
- if (pathPosIndex == 255) {
- pathPosIndex = 0;
- }
- pathPosIndex = (byte)(pathPosIndex >> 1);
- bool stopLoop = false; // NON-STOCK CODE
- for (int i = 0; i < 6; i++) {
- PathUnit.Position position;
- if (!pathMan.m_pathUnits.m_buffer[pathUnitId].GetPosition(pathPosIndex, out position)) {
- return;
- }
-
- // NON-STOCK CODE START
- ushort transitNodeId;
- if (position.m_offset < 128) {
- transitNodeId = netMan.m_segments.m_buffer[position.m_segment].m_startNode;
- } else {
- transitNodeId = netMan.m_segments.m_buffer[position.m_segment].m_endNode;
- }
-
- if (Options.timedLightsEnabled) {
- // when a TTL is active only reserve space if it shows green
- PathUnit.Position nextPos;
- if (pathMan.m_pathUnits.m_buffer[pathUnitId].GetNextPosition(pathPosIndex, out nextPos)) {
- if (!VehicleBehaviorManager.Instance.IsSpaceReservationAllowed(transitNodeId, position, nextPos)) {
- stopLoop = true;
- }
- }
- }
- // NON-STOCK CODE END
-
- if (reserveSpace && i >= 1 && i <= 2) {
- uint laneID = PathManager.GetLaneID(position);
- if (laneID != 0u) {
- reserveSpace = netMan.m_lanes.m_buffer[laneID].ReserveSpace(this.m_info.m_generatedInfo.m_size.z, vehicleID);
- }
- }
- ForceTrafficLights(transitNodeId, position); // NON-STOCK CODE
-
- // NON-STOCK CODE START
- if (stopLoop) {
- return;
- }
- // NON-STOCK CODE END
-
- if ((pathPosIndex += 1) >= pathMan.m_pathUnits.m_buffer[pathUnitId].m_positionCount) {
- pathUnitId = pathMan.m_pathUnits.m_buffer[pathUnitId].m_nextPathUnit;
- pathPosIndex = 0;
- if (pathUnitId == 0u) {
- return;
- }
- }
- }
- }
- }
-
- // slightly modified version of TrainAI.ForceTrafficLights(PathUnit.Position)
- private static void ForceTrafficLights(ushort transitNodeId, PathUnit.Position position) {
- NetManager netMan = Singleton.instance;
- if ((netMan.m_nodes.m_buffer[(int)transitNodeId].m_flags & NetNode.Flags.TrafficLights) != NetNode.Flags.None) {
- uint frame = Singleton.instance.m_currentFrameIndex;
- uint simGroup = (uint)transitNodeId >> 7;
- uint rand = frame - simGroup & 255u;
- RoadBaseAI.TrafficLightState vehicleLightState;
- RoadBaseAI.TrafficLightState pedestrianLightState;
- bool vehicles;
- bool pedestrians;
- RoadBaseAI.GetTrafficLightState(transitNodeId, ref netMan.m_segments.m_buffer[(int)position.m_segment], frame - simGroup, out vehicleLightState, out pedestrianLightState, out vehicles, out pedestrians);
- if (!vehicles && rand >= 196u) {
- vehicles = true;
- RoadBaseAI.SetTrafficLightState(transitNodeId, ref netMan.m_segments.m_buffer[(int)position.m_segment], frame - simGroup, vehicleLightState, pedestrianLightState, vehicles, pedestrians);
- }
- }
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- protected static bool CheckOverlap(ushort vehicleID, ref Vehicle vehicleData, Segment3 segment, ushort ignoreVehicle) {
- Log.Error("CustomTrainAI.CheckOverlap (1) called.");
- return false;
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- protected static ushort CheckOverlap(ushort vehicleID, ref Vehicle vehicleData, Segment3 segment, ushort ignoreVehicle, ushort otherID, ref Vehicle otherData, ref bool overlap, Vector3 min, Vector3 max) {
- Log.Error("CustomTrainAI.CheckOverlap (2) called.");
- return 0;
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void InitializePath(ushort vehicleID, ref Vehicle vehicleData) {
- Log.Error("CustomTrainAI.InitializePath called");
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- public static void UpdatePathTargetPositions(TrainAI trainAI, ushort vehicleID, ref Vehicle vehicleData, Vector3 refPos1, Vector3 refPos2, ushort leaderID, ref Vehicle leaderData, ref int index, int max1, int max2, float minSqrDistanceA, float minSqrDistanceB) {
- Log.Error($"CustomTrainAI.InvokeUpdatePathTargetPositions called! trainAI={trainAI}");
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void Reverse(ushort leaderID, ref Vehicle leaderData) {
- Log.Error("CustomTrainAI.Reverse called");
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static float GetMaxSpeed(ushort leaderID, ref Vehicle leaderData) {
- Log.Error("CustomTrainAI.GetMaxSpeed called");
- return 0f;
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static float CalculateMaxSpeed(float targetDist, float targetSpeed, float maxBraking) {
- Log.Error("CustomTrainAI.CalculateMaxSpeed called");
- return 0f;
- }
- }
+ maxSpeed = 0;
+ return;
+ } else {
+ ExtVehicleManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData/*, lastFrameData.m_velocity.magnitude*/);
+ }
+ maxSpeed = oldMaxSpeed;
+ }
+ //} // NON-STOCK CODE
+ }
+ }
+
+ [RedirectMethod]
+ private void CustomForceTrafficLights(ushort vehicleID, ref Vehicle vehicleData, bool reserveSpace) {
+ uint pathUnitId = vehicleData.m_path;
+ if (pathUnitId != 0u) {
+ NetManager netMan = Singleton.instance;
+ PathManager pathMan = Singleton.instance;
+ byte pathPosIndex = vehicleData.m_pathPositionIndex;
+ if (pathPosIndex == 255) {
+ pathPosIndex = 0;
+ }
+ pathPosIndex = (byte)(pathPosIndex >> 1);
+ bool stopLoop = false; // NON-STOCK CODE
+ for (int i = 0; i < 6; i++) {
+ PathUnit.Position position;
+ if (!pathMan.m_pathUnits.m_buffer[pathUnitId].GetPosition(pathPosIndex, out position)) {
+ return;
+ }
+
+ // NON-STOCK CODE START
+ ushort transitNodeId;
+ if (position.m_offset < 128) {
+ transitNodeId = netMan.m_segments.m_buffer[position.m_segment].m_startNode;
+ } else {
+ transitNodeId = netMan.m_segments.m_buffer[position.m_segment].m_endNode;
+ }
+
+ if (Options.timedLightsEnabled) {
+ // when a TTL is active only reserve space if it shows green
+ PathUnit.Position nextPos;
+ if (pathMan.m_pathUnits.m_buffer[pathUnitId].GetNextPosition(pathPosIndex, out nextPos)) {
+ if (!VehicleBehaviorManager.Instance.IsSpaceReservationAllowed(transitNodeId, position, nextPos)) {
+ stopLoop = true;
+ }
+ }
+ }
+ // NON-STOCK CODE END
+
+ if (reserveSpace && i >= 1 && i <= 2) {
+ uint laneID = PathManager.GetLaneID(position);
+ if (laneID != 0u) {
+ reserveSpace = netMan.m_lanes.m_buffer[laneID].ReserveSpace(this.m_info.m_generatedInfo.m_size.z, vehicleID);
+ }
+ }
+ ForceTrafficLights(transitNodeId, position); // NON-STOCK CODE
+
+ // NON-STOCK CODE START
+ if (stopLoop) {
+ return;
+ }
+ // NON-STOCK CODE END
+
+ if ((pathPosIndex += 1) >= pathMan.m_pathUnits.m_buffer[pathUnitId].m_positionCount) {
+ pathUnitId = pathMan.m_pathUnits.m_buffer[pathUnitId].m_nextPathUnit;
+ pathPosIndex = 0;
+ if (pathUnitId == 0u) {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // slightly modified version of TrainAI.ForceTrafficLights(PathUnit.Position)
+ private static void ForceTrafficLights(ushort transitNodeId, PathUnit.Position position) {
+ NetManager netMan = Singleton.instance;
+ if ((netMan.m_nodes.m_buffer[(int)transitNodeId].m_flags & NetNode.Flags.TrafficLights) != NetNode.Flags.None) {
+ uint frame = Singleton.instance.m_currentFrameIndex;
+ uint simGroup = (uint)transitNodeId >> 7;
+ uint rand = frame - simGroup & 255u;
+ RoadBaseAI.TrafficLightState vehicleLightState;
+ RoadBaseAI.TrafficLightState pedestrianLightState;
+ bool vehicles;
+ bool pedestrians;
+ RoadBaseAI.GetTrafficLightState(transitNodeId, ref netMan.m_segments.m_buffer[(int)position.m_segment], frame - simGroup, out vehicleLightState, out pedestrianLightState, out vehicles, out pedestrians);
+ if (!vehicles && rand >= 196u) {
+ vehicles = true;
+ RoadBaseAI.SetTrafficLightState(transitNodeId, ref netMan.m_segments.m_buffer[(int)position.m_segment], frame - simGroup, vehicleLightState, pedestrianLightState, vehicles, pedestrians);
+ }
+ }
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ protected static bool CheckOverlap(ushort vehicleID, ref Vehicle vehicleData, Segment3 segment, ushort ignoreVehicle) {
+ Log.Error("CustomTrainAI.CheckOverlap (1) called.");
+ return false;
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ protected static ushort CheckOverlap(ushort vehicleID, ref Vehicle vehicleData, Segment3 segment, ushort ignoreVehicle, ushort otherID, ref Vehicle otherData, ref bool overlap, Vector3 min, Vector3 max) {
+ Log.Error("CustomTrainAI.CheckOverlap (2) called.");
+ return 0;
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void InitializePath(ushort vehicleID, ref Vehicle vehicleData) {
+ Log.Error("CustomTrainAI.InitializePath called");
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void UpdatePathTargetPositions(TrainAI trainAI, ushort vehicleID, ref Vehicle vehicleData, Vector3 refPos1, Vector3 refPos2, ushort leaderID, ref Vehicle leaderData, ref int index, int max1, int max2, float minSqrDistanceA, float minSqrDistanceB) {
+ Log.Error($"CustomTrainAI.InvokeUpdatePathTargetPositions called! trainAI={trainAI}");
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void Reverse(ushort leaderID, ref Vehicle leaderData) {
+ Log.Error("CustomTrainAI.Reverse called");
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static float GetMaxSpeed(ushort leaderID, ref Vehicle leaderData) {
+ Log.Error("CustomTrainAI.GetMaxSpeed called");
+ return 0f;
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static float CalculateMaxSpeed(float targetDist, float targetSpeed, float maxBraking) {
+ Log.Error("CustomTrainAI.CalculateMaxSpeed called");
+ return 0f;
+ }
+ }
}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/AI/CustomTramBaseAI.cs b/TLM/TLM/Custom/AI/CustomTramBaseAI.cs
index 030408db2..06be41a72 100644
--- a/TLM/TLM/Custom/AI/CustomTramBaseAI.cs
+++ b/TLM/TLM/Custom/AI/CustomTramBaseAI.cs
@@ -1,774 +1,774 @@
-using ColossalFramework;
-using ColossalFramework.Math;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using TrafficManager.Custom.PathFinding;
-using TrafficManager.State;
-using TrafficManager.Geometry;
-using UnityEngine;
-using TrafficManager.Manager;
-using TrafficManager.Traffic;
-using CSUtil.Commons;
-using System.Runtime.CompilerServices;
-using TrafficManager.Manager.Impl;
-using TrafficManager.Traffic.Data;
-using CSUtil.Commons.Benchmark;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
-using TrafficManager.Traffic.Enums;
-using TrafficManager.RedirectionFramework.Attributes;
-
-namespace TrafficManager.Custom.AI {
- [TargetType(typeof(TramBaseAI))]
- public class CustomTramBaseAI : TramBaseAI { // TODO inherit from VehicleAI (in order to keep the correct references to `base`)
- [RedirectMethod]
- public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {
- IExtVehicleManager extVehicleMan = Constants.ManagerFactory.ExtVehicleManager;
-
- if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) {
- byte pathFindFlags = Singleton.instance.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;
-
- if ((pathFindFlags & PathUnit.FLAG_READY) != 0) {
- try {
- this.PathfindSuccess(vehicleId, ref vehicleData);
- this.PathFindReady(vehicleId, ref vehicleData);
- } catch (Exception e) {
- Log.Warning($"TramBaseAI.PathFindSuccess/PathFindReady({vehicleId}) threw an exception: {e.ToString()}");
- vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
- Singleton.instance.ReleasePath(vehicleData.m_path);
- vehicleData.m_path = 0u;
- this.PathfindFailure(vehicleId, ref vehicleData);
- return;
- }
- } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) {
- vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
- Singleton.instance.ReleasePath(vehicleData.m_path);
- vehicleData.m_path = 0u;
- this.PathfindFailure(vehicleId, ref vehicleData);
- return;
- }
- } else {
- if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) {
- this.TrySpawn(vehicleId, ref vehicleData);
- }
- }
-
- // NON-STOCK CODE START
- extVehicleMan.UpdateVehiclePosition(vehicleId, ref vehicleData);
-
- if (!Options.isStockLaneChangerUsed() && (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0) {
- // Advanced AI traffic measurement
- extVehicleMan.LogTraffic(vehicleId, ref vehicleData);
- }
- // NON-STOCK CODE END
-
- VehicleManager instance = Singleton.instance;
- VehicleInfo info = instance.m_vehicles.m_buffer[(int)vehicleId].Info;
- info.m_vehicleAI.SimulationStep(vehicleId, ref instance.m_vehicles.m_buffer[(int)vehicleId], vehicleId, ref vehicleData, 0);
- if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {
- return;
- }
- ushort trailingVehicle = instance.m_vehicles.m_buffer[(int)vehicleId].m_trailingVehicle;
- int num = 0;
- while (trailingVehicle != 0) {
- info = instance.m_vehicles.m_buffer[(int)trailingVehicle].Info;
- info.m_vehicleAI.SimulationStep(trailingVehicle, ref instance.m_vehicles.m_buffer[(int)trailingVehicle], vehicleId, ref vehicleData, 0);
- if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {
- return;
- }
- trailingVehicle = instance.m_vehicles.m_buffer[(int)trailingVehicle].m_trailingVehicle;
- if (++num > 16384) {
- CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
- break;
- }
- }
- if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo)) == 0) {
- Singleton.instance.ReleaseVehicle(vehicleId);
- } else if (vehicleData.m_blockCounter == 255) {
- // NON-STOCK CODE START
- if (VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData)) {
- // NON-STOCK CODE END
- Singleton.instance.ReleaseVehicle(vehicleId);
- } // NON-STOCK CODE
- }
- }
-
- [RedirectMethod]
- public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) {
- ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, null); // NON-STOCK CODE
-
- VehicleInfo info = this.m_info;
- bool allowUnderground;
- bool allowUnderground2;
- if (info.m_vehicleType == VehicleInfo.VehicleType.Metro) {
- allowUnderground = true;
- allowUnderground2 = true;
- } else {
- allowUnderground = ((vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0);
- allowUnderground2 = false;
- }
- PathUnit.Position startPosA;
- PathUnit.Position startPosB;
- float startSqrDistA;
- float startSqrDistB;
- PathUnit.Position endPosA;
- PathUnit.Position endPosB;
- float endSqrDistA;
- float endSqrDistB;
- if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB) &&
- CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle, info.m_vehicleType, allowUnderground2, false, 32f, out endPosA, out endPosB, out endSqrDistA, out endSqrDistB)) {
- if (!startBothWays || startSqrDistB > startSqrDistA * 1.2f) {
- startPosB = default(PathUnit.Position);
- }
- if (!endBothWays || endSqrDistB > endSqrDistA * 1.2f) {
- endPosB = default(PathUnit.Position);
- }
- uint path;
- // NON-STOCK CODE START
- PathCreationArgs args;
- args.extPathType = ExtPathType.None;
- args.extVehicleType = ExtVehicleType.Tram;
- args.vehicleId = vehicleID;
- args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
- args.buildIndex = Singleton.instance.m_currentBuildIndex;
- args.startPosA = startPosA;
- args.startPosB = startPosB;
- args.endPosA = endPosA;
- args.endPosB = endPosB;
- args.vehiclePosition = default(PathUnit.Position);
- args.laneTypes = NetInfo.LaneType.Vehicle;
- args.vehicleTypes = info.m_vehicleType;
- args.maxLength = 20000f;
- args.isHeavyVehicle = false;
- args.hasCombustionEngine = false;
- args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
- args.ignoreFlooded = false;
- args.ignoreCosts = false;
- args.randomParking = false;
- args.stablePath = true;
- args.skipQueue = true;
-
- if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
- // NON-STOCK CODE END
- if (vehicleData.m_path != 0u) {
- Singleton.instance.ReleasePath(vehicleData.m_path);
- }
- vehicleData.m_path = path;
- vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
- return true;
- }
- }
- return false;
- }
-
- [RedirectMethod]
- public void CustomCalculateSegmentPosition(ushort vehicleId, ref Vehicle vehicleData, PathUnit.Position nextPosition, PathUnit.Position prevPosition, uint prevLaneId, byte prevOffset, PathUnit.Position refPosition, uint refLaneId, byte refOffset, int index, out Vector3 pos, out Vector3 dir, out float maxSpeed) {
- NetManager netManager = Singleton.instance;
- ushort prevSourceNodeId;
- ushort prevTargetNodeId;
- if (prevOffset < prevPosition.m_offset) {
- prevSourceNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_startNode;
- prevTargetNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_endNode;
- } else {
- prevSourceNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_endNode;
- prevTargetNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_startNode;
- }
-
- ushort refTargetNodeId;
- if (refOffset == 0) {
- refTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_startNode;
- } else {
- refTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_endNode;
- }
+namespace TrafficManager.Custom.AI {
+ using System;
+ using System.Runtime.CompilerServices;
+ using API.Traffic.Data;
+ using API.Traffic.Enums;
+ using ColossalFramework;
+ using ColossalFramework.Math;
+ using CSUtil.Commons;
+ using Custom.PathFinding;
+ using Manager;
+ using Manager.Impl;
+ using RedirectionFramework.Attributes;
+ using State;
+ using UnityEngine;
+
+ [TargetType(typeof(TramBaseAI))]
+ public class CustomTramBaseAI : TramBaseAI { // TODO inherit from VehicleAI (in order to keep the correct references to `base`)
+ [RedirectMethod]
+ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {
+ IExtVehicleManager extVehicleMan = Constants.ManagerFactory.ExtVehicleManager;
+
+ if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) {
+ byte pathFindFlags = Singleton.instance.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;
+
+ if ((pathFindFlags & PathUnit.FLAG_READY) != 0) {
+ try {
+ this.PathfindSuccess(vehicleId, ref vehicleData);
+ this.PathFindReady(vehicleId, ref vehicleData);
+ } catch (Exception e) {
+ Log.Warning($"TramBaseAI.PathFindSuccess/PathFindReady({vehicleId}) threw an exception: {e.ToString()}");
+ vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ vehicleData.m_path = 0u;
+ this.PathfindFailure(vehicleId, ref vehicleData);
+ return;
+ }
+ } else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) {
+ vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ vehicleData.m_path = 0u;
+ this.PathfindFailure(vehicleId, ref vehicleData);
+ return;
+ }
+ } else {
+ if ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) {
+ this.TrySpawn(vehicleId, ref vehicleData);
+ }
+ }
+
+ // NON-STOCK CODE START
+ extVehicleMan.UpdateVehiclePosition(vehicleId, ref vehicleData);
+
+ if (!Options.isStockLaneChangerUsed() && (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0) {
+ // Advanced AI traffic measurement
+ extVehicleMan.LogTraffic(vehicleId, ref vehicleData);
+ }
+ // NON-STOCK CODE END
+
+ VehicleManager instance = Singleton.instance;
+ VehicleInfo info = instance.m_vehicles.m_buffer[(int)vehicleId].Info;
+ info.m_vehicleAI.SimulationStep(vehicleId, ref instance.m_vehicles.m_buffer[(int)vehicleId], vehicleId, ref vehicleData, 0);
+ if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {
+ return;
+ }
+ ushort trailingVehicle = instance.m_vehicles.m_buffer[(int)vehicleId].m_trailingVehicle;
+ int num = 0;
+ while (trailingVehicle != 0) {
+ info = instance.m_vehicles.m_buffer[(int)trailingVehicle].Info;
+ info.m_vehicleAI.SimulationStep(trailingVehicle, ref instance.m_vehicles.m_buffer[(int)trailingVehicle], vehicleId, ref vehicleData, 0);
+ if ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {
+ return;
+ }
+ trailingVehicle = instance.m_vehicles.m_buffer[(int)trailingVehicle].m_trailingVehicle;
+ if (++num > 16384) {
+ CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
+ break;
+ }
+ }
+ if ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo)) == 0) {
+ Singleton.instance.ReleaseVehicle(vehicleId);
+ } else if (vehicleData.m_blockCounter == 255) {
+ // NON-STOCK CODE START
+ if (VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData)) {
+ // NON-STOCK CODE END
+ Singleton.instance.ReleaseVehicle(vehicleId);
+ } // NON-STOCK CODE
+ }
+ }
+
+ [RedirectMethod]
+ public bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) {
+ ExtVehicleManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, null); // NON-STOCK CODE
+
+ VehicleInfo info = this.m_info;
+ bool allowUnderground;
+ bool allowUnderground2;
+ if (info.m_vehicleType == VehicleInfo.VehicleType.Metro) {
+ allowUnderground = true;
+ allowUnderground2 = true;
+ } else {
+ allowUnderground = ((vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0);
+ allowUnderground2 = false;
+ }
+ PathUnit.Position startPosA;
+ PathUnit.Position startPosB;
+ float startSqrDistA;
+ float startSqrDistB;
+ PathUnit.Position endPosA;
+ PathUnit.Position endPosB;
+ float endSqrDistA;
+ float endSqrDistB;
+ if (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB) &&
+ CustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle, info.m_vehicleType, allowUnderground2, false, 32f, out endPosA, out endPosB, out endSqrDistA, out endSqrDistB)) {
+ if (!startBothWays || startSqrDistB > startSqrDistA * 1.2f) {
+ startPosB = default(PathUnit.Position);
+ }
+ if (!endBothWays || endSqrDistB > endSqrDistA * 1.2f) {
+ endPosB = default(PathUnit.Position);
+ }
+ uint path;
+ // NON-STOCK CODE START
+ PathCreationArgs args;
+ args.extPathType = ExtPathType.None;
+ args.extVehicleType = ExtVehicleType.Tram;
+ args.vehicleId = vehicleID;
+ args.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;
+ args.buildIndex = Singleton.instance.m_currentBuildIndex;
+ args.startPosA = startPosA;
+ args.startPosB = startPosB;
+ args.endPosA = endPosA;
+ args.endPosB = endPosB;
+ args.vehiclePosition = default(PathUnit.Position);
+ args.laneTypes = NetInfo.LaneType.Vehicle;
+ args.vehicleTypes = info.m_vehicleType;
+ args.maxLength = 20000f;
+ args.isHeavyVehicle = false;
+ args.hasCombustionEngine = false;
+ args.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);
+ args.ignoreFlooded = false;
+ args.ignoreCosts = false;
+ args.randomParking = false;
+ args.stablePath = true;
+ args.skipQueue = true;
+
+ if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
+ // NON-STOCK CODE END
+ if (vehicleData.m_path != 0u) {
+ Singleton.instance.ReleasePath(vehicleData.m_path);
+ }
+ vehicleData.m_path = path;
+ vehicleData.m_flags |= Vehicle.Flags.WaitingPath;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ [RedirectMethod]
+ public void CustomCalculateSegmentPosition(ushort vehicleId, ref Vehicle vehicleData, PathUnit.Position nextPosition, PathUnit.Position prevPosition, uint prevLaneId, byte prevOffset, PathUnit.Position refPosition, uint refLaneId, byte refOffset, int index, out Vector3 pos, out Vector3 dir, out float maxSpeed) {
+ NetManager netManager = Singleton.instance;
+ ushort prevSourceNodeId;
+ ushort prevTargetNodeId;
+ if (prevOffset < prevPosition.m_offset) {
+ prevSourceNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_startNode;
+ prevTargetNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_endNode;
+ } else {
+ prevSourceNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_endNode;
+ prevTargetNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_startNode;
+ }
+
+ ushort refTargetNodeId;
+ if (refOffset == 0) {
+ refTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_startNode;
+ } else {
+ refTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_endNode;
+ }
#if DEBUG
- bool debug = GlobalConfig.Instance.Debug.Switches[21] && (GlobalConfig.Instance.Debug.NodeId <= 0 || refTargetNodeId == GlobalConfig.Instance.Debug.NodeId) && (GlobalConfig.Instance.Debug.ExtVehicleType == ExtVehicleType.None || GlobalConfig.Instance.Debug.ExtVehicleType == ExtVehicleType.Tram) && (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleId);
-
- if (debug) {
- Log._Debug($"CustomTramBaseAI.CustomCalculateSegmentPosition({vehicleId}) called.\n" +
- $"\trefPosition.m_segment={refPosition.m_segment}, refPosition.m_offset={refPosition.m_offset}\n" +
- $"\tprevPosition.m_segment={prevPosition.m_segment}, prevPosition.m_offset={prevPosition.m_offset}\n" +
- $"\tnextPosition.m_segment={nextPosition.m_segment}, nextPosition.m_offset={nextPosition.m_offset}\n" +
- $"\trefLaneId={refLaneId}, refOffset={refOffset}\n" +
- $"\tprevLaneId={prevLaneId}, prevOffset={prevOffset}\n" +
- $"\tprevSourceNodeId={prevSourceNodeId}, prevTargetNodeId={prevTargetNodeId}\n" +
- $"\trefTargetNodeId={refTargetNodeId}, refTargetNodeId={refTargetNodeId}\n" +
- $"\tindex={index}");
- }
+ bool debug = GlobalConfig.Instance.Debug.Switches[21]
+ && (GlobalConfig.Instance.Debug.NodeId <= 0
+ || refTargetNodeId == GlobalConfig.Instance.Debug.NodeId)
+ && (GlobalConfig.Instance.Debug.ApiExtVehicleType == ExtVehicleType.None
+ || GlobalConfig.Instance.Debug.ApiExtVehicleType == ExtVehicleType.Tram)
+ && (GlobalConfig.Instance.Debug.VehicleId == 0
+ || GlobalConfig.Instance.Debug.VehicleId == vehicleId);
+
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.CustomCalculateSegmentPosition({vehicleId}) called.\n" +
+ $"\trefPosition.m_segment={refPosition.m_segment}, refPosition.m_offset={refPosition.m_offset}\n" +
+ $"\tprevPosition.m_segment={prevPosition.m_segment}, prevPosition.m_offset={prevPosition.m_offset}\n" +
+ $"\tnextPosition.m_segment={nextPosition.m_segment}, nextPosition.m_offset={nextPosition.m_offset}\n" +
+ $"\trefLaneId={refLaneId}, refOffset={refOffset}\n" +
+ $"\tprevLaneId={prevLaneId}, prevOffset={prevOffset}\n" +
+ $"\tprevSourceNodeId={prevSourceNodeId}, prevTargetNodeId={prevTargetNodeId}\n" +
+ $"\trefTargetNodeId={refTargetNodeId}, refTargetNodeId={refTargetNodeId}\n" +
+ $"\tindex={index}");
+ }
#endif
- Vehicle.Frame lastFrameData = vehicleData.GetLastFrameData();
- float sqrVelocity = lastFrameData.m_velocity.sqrMagnitude;
-
- netManager.m_lanes.m_buffer[prevLaneId].CalculatePositionAndDirection((float)prevOffset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR, out pos, out dir);
- Vector3 b = netManager.m_lanes.m_buffer[refLaneId].CalculatePosition((float)refOffset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
- Vector3 a = lastFrameData.m_position;
- Vector3 a2 = lastFrameData.m_position;
- Vector3 b2 = lastFrameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
- a += b2;
- a2 -= b2;
- float crazyValue = 0.5f * sqrVelocity / this.m_info.m_braking;
- float a3 = Vector3.Distance(a, b);
- float b3 = Vector3.Distance(a2, b);
- if (Mathf.Min(a3, b3) >= crazyValue - 1f) {
- // dead stock code
- /*Segment3 segment;
- segment.a = pos;
- if (prevOffset < prevPosition.m_offset) {
- segment.b = pos + dir.normalized * this.m_info.m_generatedInfo.m_size.z;
- } else {
- segment.b = pos - dir.normalized * this.m_info.m_generatedInfo.m_size.z;
- }*/
- if (prevSourceNodeId == refTargetNodeId) {
- if (!VehicleBehaviorManager.Instance.MayChangeSegment(vehicleId, ref vehicleData, sqrVelocity, ref refPosition, ref netManager.m_segments.m_buffer[refPosition.m_segment], refTargetNodeId, refLaneId, ref prevPosition, prevSourceNodeId, ref netManager.m_nodes.m_buffer[prevSourceNodeId], prevLaneId, ref nextPosition, prevTargetNodeId)) {
- maxSpeed = 0;
- return;
- } else {
- ExtVehicleManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData/*, lastFrameData.m_velocity.magnitude*/);
- }
- }
- }
-
- NetInfo info = netManager.m_segments.m_buffer[(int)prevPosition.m_segment].Info;
- if (info.m_lanes != null && info.m_lanes.Length > (int)prevPosition.m_lane) {
- float speedLimit = Options.customSpeedLimitsEnabled ? SpeedLimitManager.Instance.GetLockFreeGameSpeedLimit(prevPosition.m_segment, prevPosition.m_lane, prevLaneId, info.m_lanes[prevPosition.m_lane]) : info.m_lanes[prevPosition.m_lane].m_speedLimit; // NON-STOCK CODE
- maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, speedLimit, netManager.m_lanes.m_buffer[prevLaneId].m_curve);
- } else {
- maxSpeed = this.CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f);
- }
- }
-
- [RedirectMethod]
- public void CustomCalculateSegmentPosition(ushort vehicleID, ref Vehicle vehicleData, PathUnit.Position position, uint laneID, byte offset, out Vector3 pos, out Vector3 dir, out float maxSpeed) {
- NetManager instance = Singleton.instance;
- instance.m_lanes.m_buffer[laneID].CalculatePositionAndDirection((float)offset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR, out pos, out dir);
- NetInfo info = instance.m_segments.m_buffer[(int)position.m_segment].Info;
- if (info.m_lanes != null && info.m_lanes.Length > (int)position.m_lane) {
- float speedLimit = Options.customSpeedLimitsEnabled ? SpeedLimitManager.Instance.GetLockFreeGameSpeedLimit(position.m_segment, position.m_lane, laneID, info.m_lanes[position.m_lane]) : info.m_lanes[position.m_lane].m_speedLimit; // NON-STOCK CODE
- maxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, speedLimit, instance.m_lanes.m_buffer[laneID].m_curve);
- } else {
- maxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1f, 0f);
- }
- }
-
- [RedirectMethod]
- public void CustomSimulationStep(ushort vehicleID, ref Vehicle vehicleData, ref Vehicle.Frame frameData, ushort leaderID, ref Vehicle leaderData, int lodPhysics) {
+ Vehicle.Frame lastFrameData = vehicleData.GetLastFrameData();
+ float sqrVelocity = lastFrameData.m_velocity.sqrMagnitude;
+
+ netManager.m_lanes.m_buffer[prevLaneId].CalculatePositionAndDirection((float)prevOffset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR, out pos, out dir);
+ Vector3 b = netManager.m_lanes.m_buffer[refLaneId].CalculatePosition((float)refOffset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+ Vector3 a = lastFrameData.m_position;
+ Vector3 a2 = lastFrameData.m_position;
+ Vector3 b2 = lastFrameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
+ a += b2;
+ a2 -= b2;
+ float crazyValue = 0.5f * sqrVelocity / this.m_info.m_braking;
+ float a3 = Vector3.Distance(a, b);
+ float b3 = Vector3.Distance(a2, b);
+ if (Mathf.Min(a3, b3) >= crazyValue - 1f) {
+ // dead stock code
+ /*Segment3 segment;
+ segment.a = pos;
+ if (prevOffset < prevPosition.m_offset) {
+ segment.b = pos + dir.normalized * this.m_info.m_generatedInfo.m_size.z;
+ } else {
+ segment.b = pos - dir.normalized * this.m_info.m_generatedInfo.m_size.z;
+ }*/
+ if (prevSourceNodeId == refTargetNodeId) {
+ if (!VehicleBehaviorManager.Instance.MayChangeSegment(vehicleId, ref vehicleData, sqrVelocity, ref refPosition, ref netManager.m_segments.m_buffer[refPosition.m_segment], refTargetNodeId, refLaneId, ref prevPosition, prevSourceNodeId, ref netManager.m_nodes.m_buffer[prevSourceNodeId], prevLaneId, ref nextPosition, prevTargetNodeId)) {
+ maxSpeed = 0;
+ return;
+ } else {
+ ExtVehicleManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData/*, lastFrameData.m_velocity.magnitude*/);
+ }
+ }
+ }
+
+ NetInfo info = netManager.m_segments.m_buffer[(int)prevPosition.m_segment].Info;
+ if (info.m_lanes != null && info.m_lanes.Length > (int)prevPosition.m_lane) {
+ float speedLimit = Options.customSpeedLimitsEnabled ? SpeedLimitManager.Instance.GetLockFreeGameSpeedLimit(prevPosition.m_segment, prevPosition.m_lane, prevLaneId, info.m_lanes[prevPosition.m_lane]) : info.m_lanes[prevPosition.m_lane].m_speedLimit; // NON-STOCK CODE
+ maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, speedLimit, netManager.m_lanes.m_buffer[prevLaneId].m_curve);
+ } else {
+ maxSpeed = this.CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f);
+ }
+ }
+
+ [RedirectMethod]
+ public void CustomCalculateSegmentPosition(ushort vehicleID, ref Vehicle vehicleData, PathUnit.Position position, uint laneID, byte offset, out Vector3 pos, out Vector3 dir, out float maxSpeed) {
+ NetManager instance = Singleton.instance;
+ instance.m_lanes.m_buffer[laneID].CalculatePositionAndDirection((float)offset * Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR, out pos, out dir);
+ NetInfo info = instance.m_segments.m_buffer[(int)position.m_segment].Info;
+ if (info.m_lanes != null && info.m_lanes.Length > (int)position.m_lane) {
+ float speedLimit = Options.customSpeedLimitsEnabled ? SpeedLimitManager.Instance.GetLockFreeGameSpeedLimit(position.m_segment, position.m_lane, laneID, info.m_lanes[position.m_lane]) : info.m_lanes[position.m_lane].m_speedLimit; // NON-STOCK CODE
+ maxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, speedLimit, instance.m_lanes.m_buffer[laneID].m_curve);
+ } else {
+ maxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1f, 0f);
+ }
+ }
+
+ [RedirectMethod]
+ public void CustomSimulationStep(ushort vehicleID, ref Vehicle vehicleData, ref Vehicle.Frame frameData, ushort leaderID, ref Vehicle leaderData, int lodPhysics) {
#if DEBUG
- bool debug = GlobalConfig.Instance.Debug.Switches[16] && GlobalConfig.Instance.Debug.NodeId == vehicleID;
+ bool debug = GlobalConfig.Instance.Debug.Switches[16] && GlobalConfig.Instance.Debug.NodeId == vehicleID;
#endif
- ushort leadingVehicle = vehicleData.m_leadingVehicle;
- uint currentFrameIndex = Singleton.instance.m_currentFrameIndex;
- VehicleInfo leaderInfo;
- if (leaderID != vehicleID) {
- leaderInfo = leaderData.Info;
- } else {
- leaderInfo = this.m_info;
- }
- TramBaseAI tramBaseAI = leaderInfo.m_vehicleAI as TramBaseAI;
-
- if (leadingVehicle != 0) {
- frameData.m_position += frameData.m_velocity * 0.4f;
- } else {
- frameData.m_position += frameData.m_velocity * 0.5f;
- }
-
- frameData.m_swayPosition += frameData.m_swayVelocity * 0.5f;
- Vector3 wheelBaseRot = frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
- Vector3 posAfterWheelRot = frameData.m_position + wheelBaseRot;
- Vector3 posBeforeWheelRot = frameData.m_position - wheelBaseRot;
-
- float acceleration = this.m_info.m_acceleration;
- float braking = this.m_info.m_braking;
- float curSpeed = frameData.m_velocity.magnitude;
-
- Vector3 afterRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posAfterWheelRot;
- float afterRotToTargetPos1DiffSqrMag = afterRotToTargetPos1Diff.sqrMagnitude;
-
- Quaternion curInvRot = Quaternion.Inverse(frameData.m_rotation);
- Vector3 curveTangent = curInvRot * frameData.m_velocity;
+ ushort leadingVehicle = vehicleData.m_leadingVehicle;
+ uint currentFrameIndex = Singleton.instance.m_currentFrameIndex;
+ VehicleInfo leaderInfo;
+ if (leaderID != vehicleID) {
+ leaderInfo = leaderData.Info;
+ } else {
+ leaderInfo = this.m_info;
+ }
+ TramBaseAI tramBaseAI = leaderInfo.m_vehicleAI as TramBaseAI;
+
+ if (leadingVehicle != 0) {
+ frameData.m_position += frameData.m_velocity * 0.4f;
+ } else {
+ frameData.m_position += frameData.m_velocity * 0.5f;
+ }
+
+ frameData.m_swayPosition += frameData.m_swayVelocity * 0.5f;
+ Vector3 wheelBaseRot = frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
+ Vector3 posAfterWheelRot = frameData.m_position + wheelBaseRot;
+ Vector3 posBeforeWheelRot = frameData.m_position - wheelBaseRot;
+
+ float acceleration = this.m_info.m_acceleration;
+ float braking = this.m_info.m_braking;
+ float curSpeed = frameData.m_velocity.magnitude;
+
+ Vector3 afterRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posAfterWheelRot;
+ float afterRotToTargetPos1DiffSqrMag = afterRotToTargetPos1Diff.sqrMagnitude;
+
+ Quaternion curInvRot = Quaternion.Inverse(frameData.m_rotation);
+ Vector3 curveTangent = curInvRot * frameData.m_velocity;
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): ================================================");
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): leadingVehicle={leadingVehicle} frameData.m_position={frameData.m_position} frameData.m_swayPosition={frameData.m_swayPosition} wheelBaseRot={wheelBaseRot} posAfterWheelRot={posAfterWheelRot} posBeforeWheelRot={posBeforeWheelRot} acceleration={acceleration} braking={braking} curSpeed={curSpeed} afterRotToTargetPos1Diff={afterRotToTargetPos1Diff} afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag} curInvRot={curInvRot} curveTangent={curveTangent} this.m_info.m_generatedInfo.m_wheelBase={this.m_info.m_generatedInfo.m_wheelBase}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): ================================================");
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): leadingVehicle={leadingVehicle} frameData.m_position={frameData.m_position} frameData.m_swayPosition={frameData.m_swayPosition} wheelBaseRot={wheelBaseRot} posAfterWheelRot={posAfterWheelRot} posBeforeWheelRot={posBeforeWheelRot} acceleration={acceleration} braking={braking} curSpeed={curSpeed} afterRotToTargetPos1Diff={afterRotToTargetPos1Diff} afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag} curInvRot={curInvRot} curveTangent={curveTangent} this.m_info.m_generatedInfo.m_wheelBase={this.m_info.m_generatedInfo.m_wheelBase}");
+ }
#endif
- Vector3 forward = Vector3.forward;
- Vector3 targetMotion = Vector3.zero;
- float targetSpeed = 0f;
- float motionFactor = 0.5f;
- float turnAngle = 0f;
- if (leadingVehicle != 0) {
- VehicleManager vehMan = Singleton.instance;
- Vehicle.Frame leadingVehLastFrameData = vehMan.m_vehicles.m_buffer[(int)leadingVehicle].GetLastFrameData();
- VehicleInfo leadingVehInfo = vehMan.m_vehicles.m_buffer[(int)leadingVehicle].Info;
-
- float attachOffset;
- if ((vehicleData.m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags)0) {
- attachOffset = this.m_info.m_attachOffsetBack - this.m_info.m_generatedInfo.m_size.z * 0.5f;
- } else {
- attachOffset = this.m_info.m_attachOffsetFront - this.m_info.m_generatedInfo.m_size.z * 0.5f;
- }
-
- float leadingAttachOffset;
- if ((vehMan.m_vehicles.m_buffer[(int)leadingVehicle].m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags)0) {
- leadingAttachOffset = leadingVehInfo.m_attachOffsetFront - leadingVehInfo.m_generatedInfo.m_size.z * 0.5f;
- } else {
- leadingAttachOffset = leadingVehInfo.m_attachOffsetBack - leadingVehInfo.m_generatedInfo.m_size.z * 0.5f;
- }
-
- Vector3 curPosMinusRotAttachOffset = frameData.m_position - frameData.m_rotation * new Vector3(0f, 0f, attachOffset);
- Vector3 leadingPosPlusRotAttachOffset = leadingVehLastFrameData.m_position + leadingVehLastFrameData.m_rotation * new Vector3(0f, 0f, leadingAttachOffset);
-
- wheelBaseRot = leadingVehLastFrameData.m_rotation * new Vector3(0f, 0f, leadingVehInfo.m_generatedInfo.m_wheelBase * 0.5f);
- Vector3 leadingPosBeforeWheelRot = leadingVehLastFrameData.m_position - wheelBaseRot;
-
- if (Vector3.Dot(vehicleData.m_targetPos1 - vehicleData.m_targetPos0, (Vector3)vehicleData.m_targetPos0 - posBeforeWheelRot) < 0f && vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
- int someIndex = -1;
- UpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posBeforeWheelRot, 0, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posBeforeWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);
- afterRotToTargetPos1DiffSqrMag = 0f;
- }
-
- float attachRotDist = Mathf.Max(Vector3.Distance(curPosMinusRotAttachOffset, leadingPosPlusRotAttachOffset), 2f);
-
- float one = 1f;
- float attachRotSqrDist = attachRotDist * attachRotDist;
- float oneSqr = one * one;
- int i = 0;
- if (afterRotToTargetPos1DiffSqrMag < attachRotSqrDist) {
- if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
- UpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, posBeforeWheelRot, posAfterWheelRot, 0, ref leaderData, ref i, 1, 2, attachRotSqrDist, oneSqr);
- }
- while (i < 4) {
- vehicleData.SetTargetPos(i, vehicleData.GetTargetPos(i - 1));
- i++;
- }
- afterRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posAfterWheelRot;
- afterRotToTargetPos1DiffSqrMag = afterRotToTargetPos1Diff.sqrMagnitude;
- }
- afterRotToTargetPos1Diff = curInvRot * afterRotToTargetPos1Diff;
-
- float negTotalAttachLen = -((this.m_info.m_generatedInfo.m_wheelBase + leadingVehInfo.m_generatedInfo.m_wheelBase) * 0.5f + attachOffset + leadingAttachOffset);
- bool hasPath = false;
- if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
- float u1;
- float u2;
- if (Line3.Intersect(posAfterWheelRot, vehicleData.m_targetPos1, leadingPosBeforeWheelRot, negTotalAttachLen, out u1, out u2)) {
- targetMotion = afterRotToTargetPos1Diff * Mathf.Clamp(Mathf.Min(u1, u2) / 0.6f, 0f, 2f);
- } else {
- Line3.DistanceSqr(posAfterWheelRot, vehicleData.m_targetPos1, leadingPosBeforeWheelRot, out u1);
- targetMotion = afterRotToTargetPos1Diff * Mathf.Clamp(u1 / 0.6f, 0f, 2f);
- }
- hasPath = true;
- }
-
- if (hasPath) {
- if (Vector3.Dot(leadingPosBeforeWheelRot - posAfterWheelRot, posAfterWheelRot - posBeforeWheelRot) < 0f) {
- motionFactor = 0f;
- }
- } else {
- float leadingPosBeforeToAfterWheelRotDist = Vector3.Distance(leadingPosBeforeWheelRot, posAfterWheelRot);
- motionFactor = 0f;
- targetMotion = curInvRot * ((leadingPosBeforeWheelRot - posAfterWheelRot) * (Mathf.Max(0f, leadingPosBeforeToAfterWheelRotDist - negTotalAttachLen) / Mathf.Max(1f, leadingPosBeforeToAfterWheelRotDist * 0.6f)));
- }
- } else {
- float estimatedFrameDist = (curSpeed + acceleration) * (0.5f + 0.5f * (curSpeed + acceleration) / braking) + (this.m_info.m_generatedInfo.m_size.z - this.m_info.m_generatedInfo.m_wheelBase) * 0.5f;
- float maxSpeedAdd = Mathf.Max(curSpeed + acceleration, 2f);
- float meanSpeedAdd = Mathf.Max((estimatedFrameDist - maxSpeedAdd) / 2f, 2f);
- float maxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd;
- float meanSpeedAddSqr = meanSpeedAdd * meanSpeedAdd;
- if (Vector3.Dot(vehicleData.m_targetPos1 - vehicleData.m_targetPos0, (Vector3)vehicleData.m_targetPos0 - posBeforeWheelRot) < 0f && vehicleData.m_path != 0u && (leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0) {
- int someIndex = -1;
- UpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posBeforeWheelRot, leaderID, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posBeforeWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);
- afterRotToTargetPos1DiffSqrMag = 0f;
+ Vector3 forward = Vector3.forward;
+ Vector3 targetMotion = Vector3.zero;
+ float targetSpeed = 0f;
+ float motionFactor = 0.5f;
+ float turnAngle = 0f;
+ if (leadingVehicle != 0) {
+ VehicleManager vehMan = Singleton.instance;
+ Vehicle.Frame leadingVehLastFrameData = vehMan.m_vehicles.m_buffer[(int)leadingVehicle].GetLastFrameData();
+ VehicleInfo leadingVehInfo = vehMan.m_vehicles.m_buffer[(int)leadingVehicle].Info;
+
+ float attachOffset;
+ if ((vehicleData.m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags)0) {
+ attachOffset = this.m_info.m_attachOffsetBack - this.m_info.m_generatedInfo.m_size.z * 0.5f;
+ } else {
+ attachOffset = this.m_info.m_attachOffsetFront - this.m_info.m_generatedInfo.m_size.z * 0.5f;
+ }
+
+ float leadingAttachOffset;
+ if ((vehMan.m_vehicles.m_buffer[(int)leadingVehicle].m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags)0) {
+ leadingAttachOffset = leadingVehInfo.m_attachOffsetFront - leadingVehInfo.m_generatedInfo.m_size.z * 0.5f;
+ } else {
+ leadingAttachOffset = leadingVehInfo.m_attachOffsetBack - leadingVehInfo.m_generatedInfo.m_size.z * 0.5f;
+ }
+
+ Vector3 curPosMinusRotAttachOffset = frameData.m_position - frameData.m_rotation * new Vector3(0f, 0f, attachOffset);
+ Vector3 leadingPosPlusRotAttachOffset = leadingVehLastFrameData.m_position + leadingVehLastFrameData.m_rotation * new Vector3(0f, 0f, leadingAttachOffset);
+
+ wheelBaseRot = leadingVehLastFrameData.m_rotation * new Vector3(0f, 0f, leadingVehInfo.m_generatedInfo.m_wheelBase * 0.5f);
+ Vector3 leadingPosBeforeWheelRot = leadingVehLastFrameData.m_position - wheelBaseRot;
+
+ if (Vector3.Dot(vehicleData.m_targetPos1 - vehicleData.m_targetPos0, (Vector3)vehicleData.m_targetPos0 - posBeforeWheelRot) < 0f && vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
+ int someIndex = -1;
+ UpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posBeforeWheelRot, 0, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posBeforeWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);
+ afterRotToTargetPos1DiffSqrMag = 0f;
+ }
+
+ float attachRotDist = Mathf.Max(Vector3.Distance(curPosMinusRotAttachOffset, leadingPosPlusRotAttachOffset), 2f);
+
+ float one = 1f;
+ float attachRotSqrDist = attachRotDist * attachRotDist;
+ float oneSqr = one * one;
+ int i = 0;
+ if (afterRotToTargetPos1DiffSqrMag < attachRotSqrDist) {
+ if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
+ UpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, posBeforeWheelRot, posAfterWheelRot, 0, ref leaderData, ref i, 1, 2, attachRotSqrDist, oneSqr);
+ }
+ while (i < 4) {
+ vehicleData.SetTargetPos(i, vehicleData.GetTargetPos(i - 1));
+ i++;
+ }
+ afterRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posAfterWheelRot;
+ afterRotToTargetPos1DiffSqrMag = afterRotToTargetPos1Diff.sqrMagnitude;
+ }
+ afterRotToTargetPos1Diff = curInvRot * afterRotToTargetPos1Diff;
+
+ float negTotalAttachLen = -((this.m_info.m_generatedInfo.m_wheelBase + leadingVehInfo.m_generatedInfo.m_wheelBase) * 0.5f + attachOffset + leadingAttachOffset);
+ bool hasPath = false;
+ if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
+ float u1;
+ float u2;
+ if (Line3.Intersect(posAfterWheelRot, vehicleData.m_targetPos1, leadingPosBeforeWheelRot, negTotalAttachLen, out u1, out u2)) {
+ targetMotion = afterRotToTargetPos1Diff * Mathf.Clamp(Mathf.Min(u1, u2) / 0.6f, 0f, 2f);
+ } else {
+ Line3.DistanceSqr(posAfterWheelRot, vehicleData.m_targetPos1, leadingPosBeforeWheelRot, out u1);
+ targetMotion = afterRotToTargetPos1Diff * Mathf.Clamp(u1 / 0.6f, 0f, 2f);
+ }
+ hasPath = true;
+ }
+
+ if (hasPath) {
+ if (Vector3.Dot(leadingPosBeforeWheelRot - posAfterWheelRot, posAfterWheelRot - posBeforeWheelRot) < 0f) {
+ motionFactor = 0f;
+ }
+ } else {
+ float leadingPosBeforeToAfterWheelRotDist = Vector3.Distance(leadingPosBeforeWheelRot, posAfterWheelRot);
+ motionFactor = 0f;
+ targetMotion = curInvRot * ((leadingPosBeforeWheelRot - posAfterWheelRot) * (Mathf.Max(0f, leadingPosBeforeToAfterWheelRotDist - negTotalAttachLen) / Mathf.Max(1f, leadingPosBeforeToAfterWheelRotDist * 0.6f)));
+ }
+ } else {
+ float estimatedFrameDist = (curSpeed + acceleration) * (0.5f + 0.5f * (curSpeed + acceleration) / braking) + (this.m_info.m_generatedInfo.m_size.z - this.m_info.m_generatedInfo.m_wheelBase) * 0.5f;
+ float maxSpeedAdd = Mathf.Max(curSpeed + acceleration, 2f);
+ float meanSpeedAdd = Mathf.Max((estimatedFrameDist - maxSpeedAdd) / 2f, 2f);
+ float maxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd;
+ float meanSpeedAddSqr = meanSpeedAdd * meanSpeedAdd;
+ if (Vector3.Dot(vehicleData.m_targetPos1 - vehicleData.m_targetPos0, (Vector3)vehicleData.m_targetPos0 - posBeforeWheelRot) < 0f && vehicleData.m_path != 0u && (leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0) {
+ int someIndex = -1;
+ UpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posBeforeWheelRot, leaderID, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posBeforeWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);
+ afterRotToTargetPos1DiffSqrMag = 0f;
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): dot < 0");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): dot < 0");
+ }
#endif
- }
+ }
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Leading vehicle is 0. vehicleData.m_targetPos0={vehicleData.m_targetPos0} vehicleData.m_targetPos1={vehicleData.m_targetPos1} posBeforeWheelRot={posBeforeWheelRot} posBeforeWheelRot={posAfterWheelRot} estimatedFrameDist={estimatedFrameDist} maxSpeedAdd={maxSpeedAdd} meanSpeedAdd={meanSpeedAdd} maxSpeedAddSqr={maxSpeedAddSqr} meanSpeedAddSqr={meanSpeedAddSqr} afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Leading vehicle is 0. vehicleData.m_targetPos0={vehicleData.m_targetPos0} vehicleData.m_targetPos1={vehicleData.m_targetPos1} posBeforeWheelRot={posBeforeWheelRot} posBeforeWheelRot={posAfterWheelRot} estimatedFrameDist={estimatedFrameDist} maxSpeedAdd={maxSpeedAdd} meanSpeedAdd={meanSpeedAdd} maxSpeedAddSqr={maxSpeedAddSqr} meanSpeedAddSqr={meanSpeedAddSqr} afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag}");
+ }
#endif
- int posIndex = 0;
- bool hasValidPathTargetPos = false;
- if ((afterRotToTargetPos1DiffSqrMag < maxSpeedAddSqr || vehicleData.m_targetPos3.w < 0.01f) && (leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0) {
- if (vehicleData.m_path != 0u) {
- UpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, posBeforeWheelRot, posAfterWheelRot, leaderID, ref leaderData, ref posIndex, 1, 4, maxSpeedAddSqr, meanSpeedAddSqr);
- }
- if (posIndex < 4) {
- hasValidPathTargetPos = true;
- while (posIndex < 4) {
- vehicleData.SetTargetPos(posIndex, vehicleData.GetTargetPos(posIndex - 1));
- posIndex++;
- }
- }
- afterRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posAfterWheelRot;
- afterRotToTargetPos1DiffSqrMag = afterRotToTargetPos1Diff.sqrMagnitude;
- }
+ int posIndex = 0;
+ bool hasValidPathTargetPos = false;
+ if ((afterRotToTargetPos1DiffSqrMag < maxSpeedAddSqr || vehicleData.m_targetPos3.w < 0.01f) && (leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0) {
+ if (vehicleData.m_path != 0u) {
+ UpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, posBeforeWheelRot, posAfterWheelRot, leaderID, ref leaderData, ref posIndex, 1, 4, maxSpeedAddSqr, meanSpeedAddSqr);
+ }
+ if (posIndex < 4) {
+ hasValidPathTargetPos = true;
+ while (posIndex < 4) {
+ vehicleData.SetTargetPos(posIndex, vehicleData.GetTargetPos(posIndex - 1));
+ posIndex++;
+ }
+ }
+ afterRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posAfterWheelRot;
+ afterRotToTargetPos1DiffSqrMag = afterRotToTargetPos1Diff.sqrMagnitude;
+ }
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): posIndex={posIndex} hasValidPathTargetPos={hasValidPathTargetPos}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): posIndex={posIndex} hasValidPathTargetPos={hasValidPathTargetPos}");
+ }
#endif
- if (leaderData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
- NetManager netMan = Singleton.instance;
- byte leaderPathPosIndex = leaderData.m_pathPositionIndex;
- byte leaderLastPathOffset = leaderData.m_lastPathOffset;
- if (leaderPathPosIndex == 255) {
- leaderPathPosIndex = 0;
- }
- int noise;
- float leaderLen = 1f + leaderData.CalculateTotalLength(leaderID, out noise);
+ if (leaderData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
+ NetManager netMan = Singleton.instance;
+ byte leaderPathPosIndex = leaderData.m_pathPositionIndex;
+ byte leaderLastPathOffset = leaderData.m_lastPathOffset;
+ if (leaderPathPosIndex == 255) {
+ leaderPathPosIndex = 0;
+ }
+ int noise;
+ float leaderLen = 1f + leaderData.CalculateTotalLength(leaderID, out noise);
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): leaderPathPosIndex={leaderPathPosIndex} leaderLastPathOffset={leaderLastPathOffset} leaderPathPosIndex={leaderPathPosIndex} leaderLen={leaderLen}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): leaderPathPosIndex={leaderPathPosIndex} leaderLastPathOffset={leaderLastPathOffset} leaderPathPosIndex={leaderPathPosIndex} leaderLen={leaderLen}");
+ }
#endif
- // reserve space / add traffic
- PathManager pathMan = Singleton.instance;
- PathUnit.Position pathPos;
- if (pathMan.m_pathUnits.m_buffer[leaderData.m_path].GetPosition(leaderPathPosIndex >> 1, out pathPos)) {
- netMan.m_segments.m_buffer[(int)pathPos.m_segment].AddTraffic(Mathf.RoundToInt(leaderLen * 2.5f), noise);
- bool reservedSpaceOnCurrentLane = false;
- if ((leaderPathPosIndex & 1) == 0 || leaderLastPathOffset == 0) {
- uint laneId = PathManager.GetLaneID(pathPos);
- if (laneId != 0u) {
- Vector3 curPathOffsetPos = netMan.m_lanes.m_buffer[laneId].CalculatePosition((float)pathPos.m_offset * 0.003921569f);
- float speedAdd = 0.5f * curSpeed * curSpeed / this.m_info.m_braking;
- float afterWheelRotCurPathOffsetDist = Vector3.Distance(posAfterWheelRot, curPathOffsetPos);
- float beforeWheelRotCurPathOffsetDist = Vector3.Distance(posBeforeWheelRot, curPathOffsetPos);
- if (Mathf.Min(afterWheelRotCurPathOffsetDist, beforeWheelRotCurPathOffsetDist) >= speedAdd - 1f) {
- netMan.m_lanes.m_buffer[laneId].ReserveSpace(leaderLen);
- reservedSpaceOnCurrentLane = true;
- }
- }
- }
-
- if (!reservedSpaceOnCurrentLane && pathMan.m_pathUnits.m_buffer[leaderData.m_path].GetNextPosition(leaderPathPosIndex >> 1, out pathPos)) {
- uint nextLaneId = PathManager.GetLaneID(pathPos);
- if (nextLaneId != 0u) {
- netMan.m_lanes.m_buffer[nextLaneId].ReserveSpace(leaderLen);
- }
- }
- }
-
- if ((ulong)(currentFrameIndex >> 4 & 15u) == (ulong)((long)(leaderID & 15))) {
- // check if vehicle can proceed to next path position
-
- bool canProceeed = false;
- uint curLeaderPathId = leaderData.m_path;
- int curLeaderPathPosIndex = leaderPathPosIndex >> 1;
- int k = 0;
- while (k < 5) {
- bool invalidPos;
- if (PathUnit.GetNextPosition(ref curLeaderPathId, ref curLeaderPathPosIndex, out pathPos, out invalidPos)) {
- uint laneId = PathManager.GetLaneID(pathPos);
- if (laneId != 0u && !netMan.m_lanes.m_buffer[laneId].CheckSpace(leaderLen)) {
- k++;
- continue;
- }
- }
- if (invalidPos) {
- this.InvalidPath(vehicleID, ref vehicleData, leaderID, ref leaderData);
- }
- canProceeed = true;
- break;
- }
- if (!canProceeed) {
- leaderData.m_flags |= Vehicle.Flags.Congestion;
- }
- }
- }
-
- float maxSpeed;
- if ((leaderData.m_flags & Vehicle.Flags.Stopped) != (Vehicle.Flags)0) {
- maxSpeed = 0f;
+ // reserve space / add traffic
+ PathManager pathMan = Singleton.instance;
+ PathUnit.Position pathPos;
+ if (pathMan.m_pathUnits.m_buffer[leaderData.m_path].GetPosition(leaderPathPosIndex >> 1, out pathPos)) {
+ netMan.m_segments.m_buffer[(int)pathPos.m_segment].AddTraffic(Mathf.RoundToInt(leaderLen * 2.5f), noise);
+ bool reservedSpaceOnCurrentLane = false;
+ if ((leaderPathPosIndex & 1) == 0 || leaderLastPathOffset == 0) {
+ uint laneId = PathManager.GetLaneID(pathPos);
+ if (laneId != 0u) {
+ Vector3 curPathOffsetPos = netMan.m_lanes.m_buffer[laneId].CalculatePosition((float)pathPos.m_offset * 0.003921569f);
+ float speedAdd = 0.5f * curSpeed * curSpeed / this.m_info.m_braking;
+ float afterWheelRotCurPathOffsetDist = Vector3.Distance(posAfterWheelRot, curPathOffsetPos);
+ float beforeWheelRotCurPathOffsetDist = Vector3.Distance(posBeforeWheelRot, curPathOffsetPos);
+ if (Mathf.Min(afterWheelRotCurPathOffsetDist, beforeWheelRotCurPathOffsetDist) >= speedAdd - 1f) {
+ netMan.m_lanes.m_buffer[laneId].ReserveSpace(leaderLen);
+ reservedSpaceOnCurrentLane = true;
+ }
+ }
+ }
+
+ if (!reservedSpaceOnCurrentLane && pathMan.m_pathUnits.m_buffer[leaderData.m_path].GetNextPosition(leaderPathPosIndex >> 1, out pathPos)) {
+ uint nextLaneId = PathManager.GetLaneID(pathPos);
+ if (nextLaneId != 0u) {
+ netMan.m_lanes.m_buffer[nextLaneId].ReserveSpace(leaderLen);
+ }
+ }
+ }
+
+ if ((ulong)(currentFrameIndex >> 4 & 15u) == (ulong)((long)(leaderID & 15))) {
+ // check if vehicle can proceed to next path position
+
+ bool canProceeed = false;
+ uint curLeaderPathId = leaderData.m_path;
+ int curLeaderPathPosIndex = leaderPathPosIndex >> 1;
+ int k = 0;
+ while (k < 5) {
+ bool invalidPos;
+ if (PathUnit.GetNextPosition(ref curLeaderPathId, ref curLeaderPathPosIndex, out pathPos, out invalidPos)) {
+ uint laneId = PathManager.GetLaneID(pathPos);
+ if (laneId != 0u && !netMan.m_lanes.m_buffer[laneId].CheckSpace(leaderLen)) {
+ k++;
+ continue;
+ }
+ }
+ if (invalidPos) {
+ this.InvalidPath(vehicleID, ref vehicleData, leaderID, ref leaderData);
+ }
+ canProceeed = true;
+ break;
+ }
+ if (!canProceeed) {
+ leaderData.m_flags |= Vehicle.Flags.Congestion;
+ }
+ }
+ }
+
+ float maxSpeed;
+ if ((leaderData.m_flags & Vehicle.Flags.Stopped) != (Vehicle.Flags)0) {
+ maxSpeed = 0f;
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is stopped. maxSpeed={maxSpeed}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is stopped. maxSpeed={maxSpeed}");
+ }
#endif
- } else {
- maxSpeed = Mathf.Min(vehicleData.m_targetPos1.w, GetMaxSpeed(leaderID, ref leaderData));
+ } else {
+ maxSpeed = Mathf.Min(vehicleData.m_targetPos1.w, GetMaxSpeed(leaderID, ref leaderData));
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is not stopped. maxSpeed={maxSpeed}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is not stopped. maxSpeed={maxSpeed}");
+ }
#endif
- }
+ }
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Start of second part. curSpeed={curSpeed} curInvRot={curInvRot}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Start of second part. curSpeed={curSpeed} curInvRot={curInvRot}");
+ }
#endif
- afterRotToTargetPos1Diff = curInvRot * afterRotToTargetPos1Diff;
+ afterRotToTargetPos1Diff = curInvRot * afterRotToTargetPos1Diff;
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1Diff={afterRotToTargetPos1Diff} (old afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag})");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1Diff={afterRotToTargetPos1Diff} (old afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag})");
+ }
#endif
- Vector3 zero = Vector3.zero;
- bool blocked = false;
- float forwardLen = 0f;
- if (afterRotToTargetPos1DiffSqrMag > 1f) { // TODO why is this not recalculated?
- forward = VectorUtils.NormalizeXZ(afterRotToTargetPos1Diff, out forwardLen);
+ Vector3 zero = Vector3.zero;
+ bool blocked = false;
+ float forwardLen = 0f;
+ if (afterRotToTargetPos1DiffSqrMag > 1f) { // TODO why is this not recalculated?
+ forward = VectorUtils.NormalizeXZ(afterRotToTargetPos1Diff, out forwardLen);
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1DiffSqrMag > 1f. forward={forward} forwardLen={forwardLen}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1DiffSqrMag > 1f. forward={forward} forwardLen={forwardLen}");
+ }
#endif
- if (forwardLen > 1f) {
- Vector3 fwd = afterRotToTargetPos1Diff;
- maxSpeedAdd = Mathf.Max(curSpeed, 2f);
- maxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd;
+ if (forwardLen > 1f) {
+ Vector3 fwd = afterRotToTargetPos1Diff;
+ maxSpeedAdd = Mathf.Max(curSpeed, 2f);
+ maxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd;
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): forwardLen > 1f. fwd={fwd} maxSpeedAdd={maxSpeedAdd} maxSpeedAddSqr={maxSpeedAddSqr}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): forwardLen > 1f. fwd={fwd} maxSpeedAdd={maxSpeedAdd} maxSpeedAddSqr={maxSpeedAddSqr}");
+ }
#endif
- if (afterRotToTargetPos1DiffSqrMag > maxSpeedAddSqr) {
- float fwdLimiter = maxSpeedAdd / Mathf.Sqrt(afterRotToTargetPos1DiffSqrMag);
- fwd.x *= fwdLimiter;
- fwd.y *= fwdLimiter;
+ if (afterRotToTargetPos1DiffSqrMag > maxSpeedAddSqr) {
+ float fwdLimiter = maxSpeedAdd / Mathf.Sqrt(afterRotToTargetPos1DiffSqrMag);
+ fwd.x *= fwdLimiter;
+ fwd.y *= fwdLimiter;
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1DiffSqrMag > maxSpeedAddSqr. afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag} maxSpeedAddSqr={maxSpeedAddSqr} fwdLimiter={fwdLimiter} fwd={fwd}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1DiffSqrMag > maxSpeedAddSqr. afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag} maxSpeedAddSqr={maxSpeedAddSqr} fwdLimiter={fwdLimiter} fwd={fwd}");
+ }
#endif
- }
+ }
- if (fwd.z < -1f) { // !!!
+ if (fwd.z < -1f) { // !!!
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): fwd.z < -1f. fwd={fwd}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): fwd.z < -1f. fwd={fwd}");
+ }
#endif
- if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
- Vector3 targetPos0TargetPos1Diff = vehicleData.m_targetPos1 - vehicleData.m_targetPos0;
- if ((curInvRot * targetPos0TargetPos1Diff).z < -0.01f) { // !!!
+ if (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {
+ Vector3 targetPos0TargetPos1Diff = vehicleData.m_targetPos1 - vehicleData.m_targetPos0;
+ if ((curInvRot * targetPos0TargetPos1Diff).z < -0.01f) { // !!!
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): (curInvRot * targetPos0TargetPos1Diff).z < -0.01f. curInvRot={curInvRot} targetPos0TargetPos1Diff={targetPos0TargetPos1Diff}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): (curInvRot * targetPos0TargetPos1Diff).z < -0.01f. curInvRot={curInvRot} targetPos0TargetPos1Diff={targetPos0TargetPos1Diff}");
+ }
#endif
- if (afterRotToTargetPos1Diff.z < Mathf.Abs(afterRotToTargetPos1Diff.x) * -10f) { // !!!
+ if (afterRotToTargetPos1Diff.z < Mathf.Abs(afterRotToTargetPos1Diff.x) * -10f) { // !!!
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1Diff.z < Mathf.Abs(afterRotToTargetPos1Diff.x) * -10f. fwd={fwd} targetPos0TargetPos1Diff={targetPos0TargetPos1Diff} afterRotToTargetPos1Diff={afterRotToTargetPos1Diff}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1Diff.z < Mathf.Abs(afterRotToTargetPos1Diff.x) * -10f. fwd={fwd} targetPos0TargetPos1Diff={targetPos0TargetPos1Diff} afterRotToTargetPos1Diff={afterRotToTargetPos1Diff}");
+ }
#endif
- // Fix: Trams get stuck
- /*fwd.z = 0f;
- afterRotToTargetPos1Diff = Vector3.zero;*/
- maxSpeed = 0.5f; // NON-STOCK CODE
+ // Fix: Trams get stuck
+ /*fwd.z = 0f;
+ afterRotToTargetPos1Diff = Vector3.zero;*/
+ maxSpeed = 0.5f; // NON-STOCK CODE
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): (1) set maxSpeed={maxSpeed}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): (1) set maxSpeed={maxSpeed}");
+ }
#endif
- } else {
- posAfterWheelRot = posBeforeWheelRot + Vector3.Normalize(vehicleData.m_targetPos1 - vehicleData.m_targetPos0) * this.m_info.m_generatedInfo.m_wheelBase;
- posIndex = -1;
- UpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, vehicleData.m_targetPos1, leaderID, ref leaderData, ref posIndex, 0, 0, Vector3.SqrMagnitude(vehicleData.m_targetPos1 - vehicleData.m_targetPos0) + 1f, 1f);
+ } else {
+ posAfterWheelRot = posBeforeWheelRot + Vector3.Normalize(vehicleData.m_targetPos1 - vehicleData.m_targetPos0) * this.m_info.m_generatedInfo.m_wheelBase;
+ posIndex = -1;
+ UpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, vehicleData.m_targetPos1, leaderID, ref leaderData, ref posIndex, 0, 0, Vector3.SqrMagnitude(vehicleData.m_targetPos1 - vehicleData.m_targetPos0) + 1f, 1f);
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1Diff.z >= Mathf.Abs(afterRotToTargetPos1Diff.x) * -10f. Invoked UpdatePathTargetPositions. posAfterWheelRot={posAfterWheelRot} posBeforeWheelRot={posBeforeWheelRot} this.m_info.m_generatedInfo.m_wheelBase={this.m_info.m_generatedInfo.m_wheelBase}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1Diff.z >= Mathf.Abs(afterRotToTargetPos1Diff.x) * -10f. Invoked UpdatePathTargetPositions. posAfterWheelRot={posAfterWheelRot} posBeforeWheelRot={posBeforeWheelRot} this.m_info.m_generatedInfo.m_wheelBase={this.m_info.m_generatedInfo.m_wheelBase}");
+ }
#endif
- }
- } else {
- posIndex = -1;
- UpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posBeforeWheelRot, leaderID, ref leaderData, ref posIndex, 0, 0, Vector3.SqrMagnitude(posBeforeWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);
- vehicleData.m_targetPos1 = posAfterWheelRot;
- fwd.z = 0f;
- afterRotToTargetPos1Diff = Vector3.zero;
- maxSpeed = 0f;
+ }
+ } else {
+ posIndex = -1;
+ UpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posBeforeWheelRot, leaderID, ref leaderData, ref posIndex, 0, 0, Vector3.SqrMagnitude(posBeforeWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);
+ vehicleData.m_targetPos1 = posAfterWheelRot;
+ fwd.z = 0f;
+ afterRotToTargetPos1Diff = Vector3.zero;
+ maxSpeed = 0f;
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is waiting for a path. posIndex={posIndex} vehicleData.m_targetPos1={vehicleData.m_targetPos1} fwd={fwd} afterRotToTargetPos1Diff={afterRotToTargetPos1Diff} maxSpeed={maxSpeed}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is waiting for a path. posIndex={posIndex} vehicleData.m_targetPos1={vehicleData.m_targetPos1} fwd={fwd} afterRotToTargetPos1Diff={afterRotToTargetPos1Diff} maxSpeed={maxSpeed}");
+ }
#endif
- }
- }
- motionFactor = 0f;
+ }
+ }
+ motionFactor = 0f;
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Reset motion factor. motionFactor={motionFactor}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Reset motion factor. motionFactor={motionFactor}");
+ }
#endif
- }
-
- forward = VectorUtils.NormalizeXZ(fwd, out forwardLen);
- float curve = Mathf.PI/2f /* 1.57079637f*/ * (1f - forward.z); // <- constant: a bit inaccurate PI/2
- if (forwardLen > 1f) {
- curve /= forwardLen;
- }
- float targetDist = forwardLen;
+ }
+
+ forward = VectorUtils.NormalizeXZ(fwd, out forwardLen);
+ float curve = Mathf.PI/2f /* 1.57079637f*/ * (1f - forward.z); // <- constant: a bit inaccurate PI/2
+ if (forwardLen > 1f) {
+ curve /= forwardLen;
+ }
+ float targetDist = forwardLen;
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): targetDist={targetDist} fwd={fwd} curve={curve} maxSpeed={maxSpeed}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): targetDist={targetDist} fwd={fwd} curve={curve} maxSpeed={maxSpeed}");
+ }
#endif
- if (vehicleData.m_targetPos1.w < 0.1f) {
- maxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1000f, curve);
- maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos1.w, braking * 0.9f));
- } else {
- maxSpeed = Mathf.Min(maxSpeed, this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1000f, curve));
- }
+ if (vehicleData.m_targetPos1.w < 0.1f) {
+ maxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1000f, curve);
+ maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos1.w, braking * 0.9f));
+ } else {
+ maxSpeed = Mathf.Min(maxSpeed, this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1000f, curve));
+ }
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): [1] maxSpeed={maxSpeed}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): [1] maxSpeed={maxSpeed}");
+ }
#endif
- maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos2.w, braking * 0.9f));
+ maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos2.w, braking * 0.9f));
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): [2] maxSpeed={maxSpeed}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): [2] maxSpeed={maxSpeed}");
+ }
#endif
- targetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos2 - vehicleData.m_targetPos1);
- maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos3.w, braking * 0.9f));
+ targetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos2 - vehicleData.m_targetPos1);
+ maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos3.w, braking * 0.9f));
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): [3] maxSpeed={maxSpeed}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): [3] maxSpeed={maxSpeed}");
+ }
#endif
- targetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos3 - vehicleData.m_targetPos2);
- if (vehicleData.m_targetPos3.w < 0.01f) {
- targetDist = Mathf.Max(0f, targetDist + (this.m_info.m_generatedInfo.m_wheelBase - this.m_info.m_generatedInfo.m_size.z) * 0.5f);
- }
- maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, 0f, braking * 0.9f));
+ targetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos3 - vehicleData.m_targetPos2);
+ if (vehicleData.m_targetPos3.w < 0.01f) {
+ targetDist = Mathf.Max(0f, targetDist + (this.m_info.m_generatedInfo.m_wheelBase - this.m_info.m_generatedInfo.m_size.z) * 0.5f);
+ }
+ maxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, 0f, braking * 0.9f));
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): [4] maxSpeed={maxSpeed}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): [4] maxSpeed={maxSpeed}");
+ }
#endif
- CarAI.CheckOtherVehicles(vehicleID, ref vehicleData, ref frameData, ref maxSpeed, ref blocked, ref zero, estimatedFrameDist, braking * 0.9f, lodPhysics);
+ CarAI.CheckOtherVehicles(vehicleID, ref vehicleData, ref frameData, ref maxSpeed, ref blocked, ref zero, estimatedFrameDist, braking * 0.9f, lodPhysics);
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): CheckOtherVehicles finished. blocked={blocked}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): CheckOtherVehicles finished. blocked={blocked}");
+ }
#endif
- if (maxSpeed < curSpeed) {
- float brake = Mathf.Max(acceleration, Mathf.Min(braking, curSpeed));
- targetSpeed = Mathf.Max(maxSpeed, curSpeed - brake);
+ if (maxSpeed < curSpeed) {
+ float brake = Mathf.Max(acceleration, Mathf.Min(braking, curSpeed));
+ targetSpeed = Mathf.Max(maxSpeed, curSpeed - brake);
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): maxSpeed < curSpeed. maxSpeed={maxSpeed} curSpeed={curSpeed} brake={brake} targetSpeed={targetSpeed}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): maxSpeed < curSpeed. maxSpeed={maxSpeed} curSpeed={curSpeed} brake={brake} targetSpeed={targetSpeed}");
+ }
#endif
- } else {
- float accel = Mathf.Max(acceleration, Mathf.Min(braking, -curSpeed));
- targetSpeed = Mathf.Min(maxSpeed, curSpeed + accel);
+ } else {
+ float accel = Mathf.Max(acceleration, Mathf.Min(braking, -curSpeed));
+ targetSpeed = Mathf.Min(maxSpeed, curSpeed + accel);
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): maxSpeed >= curSpeed. maxSpeed={maxSpeed} curSpeed={curSpeed} accel={accel} targetSpeed={targetSpeed}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): maxSpeed >= curSpeed. maxSpeed={maxSpeed} curSpeed={curSpeed} accel={accel} targetSpeed={targetSpeed}");
+ }
#endif
- }
- }
- } else if (curSpeed < 0.1f && hasValidPathTargetPos && leaderInfo.m_vehicleAI.ArriveAtDestination(leaderID, ref leaderData)) {
- leaderData.Unspawn(leaderID);
- return;
- }
- if ((leaderData.m_flags & Vehicle.Flags.Stopped) == (Vehicle.Flags)0 && maxSpeed < 0.1f) {
+ }
+ }
+ } else if (curSpeed < 0.1f && hasValidPathTargetPos && leaderInfo.m_vehicleAI.ArriveAtDestination(leaderID, ref leaderData)) {
+ leaderData.Unspawn(leaderID);
+ return;
+ }
+ if ((leaderData.m_flags & Vehicle.Flags.Stopped) == (Vehicle.Flags)0 && maxSpeed < 0.1f) {
#if DEBUG
- if (debug) {
- Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is not stopped but maxSpeed < 0.1. maxSpeed={maxSpeed}");
- }
+ if (debug) {
+ Log._Debug($"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is not stopped but maxSpeed < 0.1. maxSpeed={maxSpeed}");
+ }
#endif
- blocked = true;
- }
- if (blocked) {
- leaderData.m_blockCounter = (byte)Mathf.Min((int)(leaderData.m_blockCounter + 1), 255);
- } else {
- leaderData.m_blockCounter = 0;
- }
- if (forwardLen > 1f) {
- turnAngle = Mathf.Asin(forward.x) * Mathf.Sign(targetSpeed);
- targetMotion = forward * targetSpeed;
- } else {
- Vector3 vel = Vector3.ClampMagnitude(afterRotToTargetPos1Diff * 0.5f - curveTangent, braking);
- targetMotion = curveTangent + vel;
- }
- }
- bool mayBlink = (currentFrameIndex + (uint)leaderID & 16u) != 0u;
- Vector3 springs = targetMotion - curveTangent;
- Vector3 targetAfterWheelRotMotion = frameData.m_rotation * targetMotion;
- Vector3 targetBeforeWheelRotMotion = Vector3.Normalize((Vector3)vehicleData.m_targetPos0 - posBeforeWheelRot) * (targetMotion.magnitude * motionFactor);
- targetBeforeWheelRotMotion -= targetAfterWheelRotMotion * (Vector3.Dot(targetAfterWheelRotMotion, targetBeforeWheelRotMotion) / Mathf.Max(1f, targetAfterWheelRotMotion.sqrMagnitude));
- posAfterWheelRot += targetAfterWheelRotMotion;
- posBeforeWheelRot += targetBeforeWheelRotMotion;
- frameData.m_rotation = Quaternion.LookRotation(posAfterWheelRot - posBeforeWheelRot);
- Vector3 targetPos = posAfterWheelRot - frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
- frameData.m_velocity = targetPos - frameData.m_position;
- if (leadingVehicle != 0) {
- frameData.m_position += frameData.m_velocity * 0.6f;
- } else {
- frameData.m_position += frameData.m_velocity * 0.5f;
- }
- frameData.m_swayVelocity = frameData.m_swayVelocity * (1f - this.m_info.m_dampers) - springs * (1f - this.m_info.m_springs) - frameData.m_swayPosition * this.m_info.m_springs;
- frameData.m_swayPosition += frameData.m_swayVelocity * 0.5f;
- frameData.m_steerAngle = 0f;
- frameData.m_travelDistance += targetMotion.z;
- if (leadingVehicle != 0) {
- frameData.m_lightIntensity = Singleton.instance.m_vehicles.m_buffer[(int)leaderID].GetLastFrameData().m_lightIntensity;
- } else {
- frameData.m_lightIntensity.x = 5f;
- frameData.m_lightIntensity.y = ((springs.z >= -0.1f) ? 0.5f : 5f);
- frameData.m_lightIntensity.z = ((turnAngle >= -0.1f || !mayBlink) ? 0f : 5f);
- frameData.m_lightIntensity.w = ((turnAngle <= 0.1f || !mayBlink) ? 0f : 5f);
- }
- frameData.m_underground = ((vehicleData.m_flags & Vehicle.Flags.Underground) != (Vehicle.Flags)0);
- frameData.m_transition = ((vehicleData.m_flags & Vehicle.Flags.Transition) != (Vehicle.Flags)0);
- //base.SimulationStep(vehicleID, ref vehicleData, ref frameData, leaderID, ref leaderData, lodPhysics);
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- public static void UpdatePathTargetPositions(TramBaseAI tramBaseAI, ushort vehicleID, ref Vehicle vehicleData, Vector3 refPos1, Vector3 refPos2, ushort leaderID, ref Vehicle leaderData, ref int index, int max1, int max2, float minSqrDistanceA, float minSqrDistanceB) {
- Log.Error($"CustomTramBaseAI.InvokeUpdatePathTargetPositions called! tramBaseAI={tramBaseAI}");
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static float GetMaxSpeed(ushort leaderID, ref Vehicle leaderData) {
- Log.Error("CustomTrainAI.GetMaxSpeed called");
- return 0f;
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static float CalculateMaxSpeed(float targetDist, float targetSpeed, float maxBraking) {
- Log.Error("CustomTrainAI.CalculateMaxSpeed called");
- return 0f;
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void InitializePath(ushort vehicleID, ref Vehicle vehicleData) {
- Log.Error("CustomTrainAI.InitializePath called");
- }
- }
-}
+ blocked = true;
+ }
+ if (blocked) {
+ leaderData.m_blockCounter = (byte)Mathf.Min((int)(leaderData.m_blockCounter + 1), 255);
+ } else {
+ leaderData.m_blockCounter = 0;
+ }
+ if (forwardLen > 1f) {
+ turnAngle = Mathf.Asin(forward.x) * Mathf.Sign(targetSpeed);
+ targetMotion = forward * targetSpeed;
+ } else {
+ Vector3 vel = Vector3.ClampMagnitude(afterRotToTargetPos1Diff * 0.5f - curveTangent, braking);
+ targetMotion = curveTangent + vel;
+ }
+ }
+ bool mayBlink = (currentFrameIndex + (uint)leaderID & 16u) != 0u;
+ Vector3 springs = targetMotion - curveTangent;
+ Vector3 targetAfterWheelRotMotion = frameData.m_rotation * targetMotion;
+ Vector3 targetBeforeWheelRotMotion = Vector3.Normalize((Vector3)vehicleData.m_targetPos0 - posBeforeWheelRot) * (targetMotion.magnitude * motionFactor);
+ targetBeforeWheelRotMotion -= targetAfterWheelRotMotion * (Vector3.Dot(targetAfterWheelRotMotion, targetBeforeWheelRotMotion) / Mathf.Max(1f, targetAfterWheelRotMotion.sqrMagnitude));
+ posAfterWheelRot += targetAfterWheelRotMotion;
+ posBeforeWheelRot += targetBeforeWheelRotMotion;
+ frameData.m_rotation = Quaternion.LookRotation(posAfterWheelRot - posBeforeWheelRot);
+ Vector3 targetPos = posAfterWheelRot - frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);
+ frameData.m_velocity = targetPos - frameData.m_position;
+ if (leadingVehicle != 0) {
+ frameData.m_position += frameData.m_velocity * 0.6f;
+ } else {
+ frameData.m_position += frameData.m_velocity * 0.5f;
+ }
+ frameData.m_swayVelocity = frameData.m_swayVelocity * (1f - this.m_info.m_dampers) - springs * (1f - this.m_info.m_springs) - frameData.m_swayPosition * this.m_info.m_springs;
+ frameData.m_swayPosition += frameData.m_swayVelocity * 0.5f;
+ frameData.m_steerAngle = 0f;
+ frameData.m_travelDistance += targetMotion.z;
+ if (leadingVehicle != 0) {
+ frameData.m_lightIntensity = Singleton.instance.m_vehicles.m_buffer[(int)leaderID].GetLastFrameData().m_lightIntensity;
+ } else {
+ frameData.m_lightIntensity.x = 5f;
+ frameData.m_lightIntensity.y = ((springs.z >= -0.1f) ? 0.5f : 5f);
+ frameData.m_lightIntensity.z = ((turnAngle >= -0.1f || !mayBlink) ? 0f : 5f);
+ frameData.m_lightIntensity.w = ((turnAngle <= 0.1f || !mayBlink) ? 0f : 5f);
+ }
+ frameData.m_underground = ((vehicleData.m_flags & Vehicle.Flags.Underground) != (Vehicle.Flags)0);
+ frameData.m_transition = ((vehicleData.m_flags & Vehicle.Flags.Transition) != (Vehicle.Flags)0);
+ //base.SimulationStep(vehicleID, ref vehicleData, ref frameData, leaderID, ref leaderData, lodPhysics);
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void UpdatePathTargetPositions(TramBaseAI tramBaseAI, ushort vehicleID, ref Vehicle vehicleData, Vector3 refPos1, Vector3 refPos2, ushort leaderID, ref Vehicle leaderData, ref int index, int max1, int max2, float minSqrDistanceA, float minSqrDistanceB) {
+ Log.Error($"CustomTramBaseAI.InvokeUpdatePathTargetPositions called! tramBaseAI={tramBaseAI}");
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static float GetMaxSpeed(ushort leaderID, ref Vehicle leaderData) {
+ Log.Error("CustomTrainAI.GetMaxSpeed called");
+ return 0f;
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static float CalculateMaxSpeed(float targetDist, float targetSpeed, float maxBraking) {
+ Log.Error("CustomTrainAI.CalculateMaxSpeed called");
+ return 0f;
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void InitializePath(ushort vehicleID, ref Vehicle vehicleData) {
+ Log.Error("CustomTrainAI.InitializePath called");
+ }
+ }
+}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/AI/CustomTransportLineAI.cs b/TLM/TLM/Custom/AI/CustomTransportLineAI.cs
index 44101a753..9fc7794b2 100644
--- a/TLM/TLM/Custom/AI/CustomTransportLineAI.cs
+++ b/TLM/TLM/Custom/AI/CustomTransportLineAI.cs
@@ -1,196 +1,189 @@
-using ColossalFramework;
-using CSUtil.Commons;
-using CSUtil.Commons.Benchmark;
-using TrafficManager.RedirectionFramework.Attributes;
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Text;
-using TrafficManager.Custom.PathFinding;
-using TrafficManager.Geometry;
-using TrafficManager.State;
-using TrafficManager.Traffic;
-using TrafficManager.Traffic.Data;
-using TrafficManager.Traffic.Enums;
-using UnityEngine;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
-
-namespace TrafficManager.Custom.AI {
- [TargetType(typeof(TransportLineAI))]
- public class CustomTransportLineAI : TransportLineAI { // TODO inherit from NetAI (in order to keep the correct references to `base`)
- [RedirectMethod]
- public static bool CustomStartPathFind(ushort segmentID, ref NetSegment data, ItemClass.Service netService, ItemClass.Service netService2, VehicleInfo.VehicleType vehicleType, bool skipQueue) {
- if (data.m_path != 0u) {
- Singleton.instance.ReleasePath(data.m_path);
- data.m_path = 0u;
- }
-
- NetManager netManager = Singleton.instance;
- if ((netManager.m_nodes.m_buffer[(int)data.m_startNode].m_flags & NetNode.Flags.Ambiguous) != NetNode.Flags.None) {
- for (int i = 0; i < 8; i++) {
- ushort segment = netManager.m_nodes.m_buffer[(int)data.m_startNode].GetSegment(i);
- if (segment != 0 && segment != segmentID && netManager.m_segments.m_buffer[(int)segment].m_path != 0u) {
- return true;
- }
- }
- }
- if ((netManager.m_nodes.m_buffer[(int)data.m_endNode].m_flags & NetNode.Flags.Ambiguous) != NetNode.Flags.None) {
- for (int j = 0; j < 8; j++) {
- ushort segment2 = netManager.m_nodes.m_buffer[(int)data.m_endNode].GetSegment(j);
- if (segment2 != 0 && segment2 != segmentID && netManager.m_segments.m_buffer[(int)segment2].m_path != 0u) {
- return true;
- }
- }
- }
-
- Vector3 position = netManager.m_nodes.m_buffer[(int)data.m_startNode].m_position;
- Vector3 position2 = netManager.m_nodes.m_buffer[(int)data.m_endNode].m_position;
+namespace TrafficManager.Custom.AI {
+ using System.Runtime.CompilerServices;
+ using API.Traffic.Data;
+ using API.Traffic.Enums;
+ using ColossalFramework;
+ using CSUtil.Commons;
+ using Custom.PathFinding;
+ using RedirectionFramework.Attributes;
+ using State;
+ using UnityEngine;
+
+ [TargetType(typeof(TransportLineAI))]
+ public class CustomTransportLineAI : TransportLineAI { // TODO inherit from NetAI (in order to keep the correct references to `base`)
+ [RedirectMethod]
+ public static bool CustomStartPathFind(ushort segmentID, ref NetSegment data, ItemClass.Service netService, ItemClass.Service netService2, VehicleInfo.VehicleType vehicleType, bool skipQueue) {
+ if (data.m_path != 0u) {
+ Singleton.instance.ReleasePath(data.m_path);
+ data.m_path = 0u;
+ }
+
+ NetManager netManager = Singleton.instance;
+ if ((netManager.m_nodes.m_buffer[(int)data.m_startNode].m_flags & NetNode.Flags.Ambiguous) != NetNode.Flags.None) {
+ for (int i = 0; i < 8; i++) {
+ ushort segment = netManager.m_nodes.m_buffer[(int)data.m_startNode].GetSegment(i);
+ if (segment != 0 && segment != segmentID && netManager.m_segments.m_buffer[(int)segment].m_path != 0u) {
+ return true;
+ }
+ }
+ }
+ if ((netManager.m_nodes.m_buffer[(int)data.m_endNode].m_flags & NetNode.Flags.Ambiguous) != NetNode.Flags.None) {
+ for (int j = 0; j < 8; j++) {
+ ushort segment2 = netManager.m_nodes.m_buffer[(int)data.m_endNode].GetSegment(j);
+ if (segment2 != 0 && segment2 != segmentID && netManager.m_segments.m_buffer[(int)segment2].m_path != 0u) {
+ return true;
+ }
+ }
+ }
+
+ Vector3 position = netManager.m_nodes.m_buffer[(int)data.m_startNode].m_position;
+ Vector3 position2 = netManager.m_nodes.m_buffer[(int)data.m_endNode].m_position;
#if DEBUG
- bool debug = GlobalConfig.Instance.Debug.Switches[18];
- if (debug)
- Log._Debug($"TransportLineAI.CustomStartPathFind({segmentID}, ..., {netService}, {netService2}, {vehicleType}, {skipQueue}): startNode={data.m_startNode} @ {position}, endNode={data.m_endNode} @ {position2} -- line: {netManager.m_nodes.m_buffer[(int)data.m_startNode].m_transportLine}/{netManager.m_nodes.m_buffer[(int)data.m_endNode].m_transportLine}");
+ bool debug = GlobalConfig.Instance.Debug.Switches[18];
+ if (debug)
+ Log._Debug($"TransportLineAI.CustomStartPathFind({segmentID}, ..., {netService}, {netService2}, {vehicleType}, {skipQueue}): startNode={data.m_startNode} @ {position}, endNode={data.m_endNode} @ {position2} -- line: {netManager.m_nodes.m_buffer[(int)data.m_startNode].m_transportLine}/{netManager.m_nodes.m_buffer[(int)data.m_endNode].m_transportLine}");
#endif
- PathUnit.Position startPosA;
- PathUnit.Position startPosB;
- float startSqrDistA;
- float startSqrDistB;
- if (!CustomPathManager.FindPathPosition(position, netService, netService2, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, vehicleType, true, false, 32f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB)) {
- CustomTransportLineAI.CheckSegmentProblems(segmentID, ref data);
- return true;
- }
-
- PathUnit.Position endPosA;
- PathUnit.Position endPosB;
- float endSqrDistA;
- float endSqrDistB;
- if (!CustomPathManager.FindPathPosition(position2, netService, netService2, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, vehicleType, true, false, 32f, out endPosA, out endPosB, out endSqrDistA, out endSqrDistB)) {
- CustomTransportLineAI.CheckSegmentProblems(segmentID, ref data);
- return true;
- }
-
- if ((netManager.m_nodes.m_buffer[(int)data.m_startNode].m_flags & NetNode.Flags.Fixed) != NetNode.Flags.None) {
- startPosB = default(PathUnit.Position);
- }
-
- if ((netManager.m_nodes.m_buffer[(int)data.m_endNode].m_flags & NetNode.Flags.Fixed) != NetNode.Flags.None) {
- endPosB = default(PathUnit.Position);
- }
-
- if (vehicleType != VehicleInfo.VehicleType.None) {
- startPosA.m_offset = 128;
- startPosB.m_offset = 128;
- endPosA.m_offset = 128;
- endPosB.m_offset = 128;
- } else {
- startPosA.m_offset = (byte)Mathf.Clamp(startPosA.m_offset, 1, 254);
- startPosB.m_offset = (byte)Mathf.Clamp(startPosB.m_offset, 1, 254);
- endPosA.m_offset = (byte)Mathf.Clamp(endPosA.m_offset, 1, 254);
- endPosB.m_offset = (byte)Mathf.Clamp(endPosB.m_offset, 1, 254);
- }
-
- bool stopLane = CustomTransportLineAI.GetStopLane(ref startPosA, vehicleType);
- bool stopLane2 = CustomTransportLineAI.GetStopLane(ref startPosB, vehicleType);
- bool stopLane3 = CustomTransportLineAI.GetStopLane(ref endPosA, vehicleType);
- bool stopLane4 = CustomTransportLineAI.GetStopLane(ref endPosB, vehicleType);
-
- if ((!stopLane && !stopLane2) || (!stopLane3 && !stopLane4)) {
- CustomTransportLineAI.CheckSegmentProblems(segmentID, ref data);
- return true;
- }
-
- ExtVehicleType extVehicleType = ExtVehicleType.None;
- if ((vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None)
- extVehicleType = ExtVehicleType.Bus;
- if ((vehicleType & (VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Monorail)) != VehicleInfo.VehicleType.None)
- extVehicleType = ExtVehicleType.PassengerTrain;
- if ((vehicleType & VehicleInfo.VehicleType.Tram) != VehicleInfo.VehicleType.None)
- extVehicleType = ExtVehicleType.Tram;
- if ((vehicleType & VehicleInfo.VehicleType.Ship) != VehicleInfo.VehicleType.None)
- extVehicleType = ExtVehicleType.PassengerShip;
- if ((vehicleType & VehicleInfo.VehicleType.Plane) != VehicleInfo.VehicleType.None)
- extVehicleType = ExtVehicleType.PassengerPlane;
- if ((vehicleType & VehicleInfo.VehicleType.Ferry) != VehicleInfo.VehicleType.None)
- extVehicleType = ExtVehicleType.Ferry;
- if ((vehicleType & VehicleInfo.VehicleType.Blimp) != VehicleInfo.VehicleType.None)
- extVehicleType = ExtVehicleType.Blimp;
- if ((vehicleType & VehicleInfo.VehicleType.CableCar) != VehicleInfo.VehicleType.None)
- extVehicleType = ExtVehicleType.CableCar;
-
- //Log._Debug($"Transport line. extVehicleType={extVehicleType}");
- uint path;
- // NON-STOCK CODE START
- PathCreationArgs args;
- args.extPathType = ExtPathType.None;
- args.extVehicleType = extVehicleType;
- args.vehicleId = 0;
- args.spawned = true;
- args.buildIndex = Singleton.instance.m_currentBuildIndex;
- args.startPosA = startPosA;
- args.startPosB = startPosB;
- args.endPosA = endPosA;
- args.endPosB = endPosB;
- args.vehiclePosition = default(PathUnit.Position);
- args.vehicleTypes = vehicleType;
- args.isHeavyVehicle = false;
- args.hasCombustionEngine = false;
- args.ignoreBlocked = true;
- args.ignoreFlooded = false;
- args.ignoreCosts = false;
- args.randomParking = false;
- args.stablePath = true;
- args.skipQueue = skipQueue;
-
- if (vehicleType == VehicleInfo.VehicleType.None) {
- args.laneTypes = NetInfo.LaneType.Pedestrian;
- args.maxLength = 160000f;
- } else {
- args.laneTypes = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
- args.maxLength = 20000f;
- }
-
- if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
- // NON-STOCK CODE END
- if (startPosA.m_segment != 0 && startPosB.m_segment != 0) {
- netManager.m_nodes.m_buffer[data.m_startNode].m_flags |= NetNode.Flags.Ambiguous;
- } else {
- netManager.m_nodes.m_buffer[data.m_startNode].m_flags &= ~NetNode.Flags.Ambiguous;
- }
- if (endPosA.m_segment != 0 && endPosB.m_segment != 0) {
- netManager.m_nodes.m_buffer[data.m_endNode].m_flags |= NetNode.Flags.Ambiguous;
- } else {
- netManager.m_nodes.m_buffer[data.m_endNode].m_flags &= ~NetNode.Flags.Ambiguous;
- }
- data.m_path = path;
- data.m_flags |= NetSegment.Flags.WaitingPath;
+ PathUnit.Position startPosA;
+ PathUnit.Position startPosB;
+ float startSqrDistA;
+ float startSqrDistB;
+ if (!CustomPathManager.FindPathPosition(position, netService, netService2, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, vehicleType, true, false, 32f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB)) {
+ CustomTransportLineAI.CheckSegmentProblems(segmentID, ref data);
+ return true;
+ }
+
+ PathUnit.Position endPosA;
+ PathUnit.Position endPosB;
+ float endSqrDistA;
+ float endSqrDistB;
+ if (!CustomPathManager.FindPathPosition(position2, netService, netService2, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, vehicleType, true, false, 32f, out endPosA, out endPosB, out endSqrDistA, out endSqrDistB)) {
+ CustomTransportLineAI.CheckSegmentProblems(segmentID, ref data);
+ return true;
+ }
+
+ if ((netManager.m_nodes.m_buffer[(int)data.m_startNode].m_flags & NetNode.Flags.Fixed) != NetNode.Flags.None) {
+ startPosB = default(PathUnit.Position);
+ }
+
+ if ((netManager.m_nodes.m_buffer[(int)data.m_endNode].m_flags & NetNode.Flags.Fixed) != NetNode.Flags.None) {
+ endPosB = default(PathUnit.Position);
+ }
+
+ if (vehicleType != VehicleInfo.VehicleType.None) {
+ startPosA.m_offset = 128;
+ startPosB.m_offset = 128;
+ endPosA.m_offset = 128;
+ endPosB.m_offset = 128;
+ } else {
+ startPosA.m_offset = (byte)Mathf.Clamp(startPosA.m_offset, 1, 254);
+ startPosB.m_offset = (byte)Mathf.Clamp(startPosB.m_offset, 1, 254);
+ endPosA.m_offset = (byte)Mathf.Clamp(endPosA.m_offset, 1, 254);
+ endPosB.m_offset = (byte)Mathf.Clamp(endPosB.m_offset, 1, 254);
+ }
+
+ bool stopLane = CustomTransportLineAI.GetStopLane(ref startPosA, vehicleType);
+ bool stopLane2 = CustomTransportLineAI.GetStopLane(ref startPosB, vehicleType);
+ bool stopLane3 = CustomTransportLineAI.GetStopLane(ref endPosA, vehicleType);
+ bool stopLane4 = CustomTransportLineAI.GetStopLane(ref endPosB, vehicleType);
+
+ if ((!stopLane && !stopLane2) || (!stopLane3 && !stopLane4)) {
+ CustomTransportLineAI.CheckSegmentProblems(segmentID, ref data);
+ return true;
+ }
+
+ ExtVehicleType extVehicleType = ExtVehicleType.None;
+ if ((vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None)
+ extVehicleType = ExtVehicleType.Bus;
+ if ((vehicleType & (VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Monorail)) != VehicleInfo.VehicleType.None)
+ extVehicleType = ExtVehicleType.PassengerTrain;
+ if ((vehicleType & VehicleInfo.VehicleType.Tram) != VehicleInfo.VehicleType.None)
+ extVehicleType = ExtVehicleType.Tram;
+ if ((vehicleType & VehicleInfo.VehicleType.Ship) != VehicleInfo.VehicleType.None)
+ extVehicleType = ExtVehicleType.PassengerShip;
+ if ((vehicleType & VehicleInfo.VehicleType.Plane) != VehicleInfo.VehicleType.None)
+ extVehicleType = ExtVehicleType.PassengerPlane;
+ if ((vehicleType & VehicleInfo.VehicleType.Ferry) != VehicleInfo.VehicleType.None)
+ extVehicleType = ExtVehicleType.Ferry;
+ if ((vehicleType & VehicleInfo.VehicleType.Blimp) != VehicleInfo.VehicleType.None)
+ extVehicleType = ExtVehicleType.Blimp;
+ if ((vehicleType & VehicleInfo.VehicleType.CableCar) != VehicleInfo.VehicleType.None)
+ extVehicleType = ExtVehicleType.CableCar;
+
+ //Log._Debug($"Transport line. extVehicleType={extVehicleType}");
+ uint path;
+ // NON-STOCK CODE START
+ PathCreationArgs args;
+ args.extPathType = ExtPathType.None;
+ args.extVehicleType = extVehicleType;
+ args.vehicleId = 0;
+ args.spawned = true;
+ args.buildIndex = Singleton.instance.m_currentBuildIndex;
+ args.startPosA = startPosA;
+ args.startPosB = startPosB;
+ args.endPosA = endPosA;
+ args.endPosB = endPosB;
+ args.vehiclePosition = default(PathUnit.Position);
+ args.vehicleTypes = vehicleType;
+ args.isHeavyVehicle = false;
+ args.hasCombustionEngine = false;
+ args.ignoreBlocked = true;
+ args.ignoreFlooded = false;
+ args.ignoreCosts = false;
+ args.randomParking = false;
+ args.stablePath = true;
+ args.skipQueue = skipQueue;
+
+ if (vehicleType == VehicleInfo.VehicleType.None) {
+ args.laneTypes = NetInfo.LaneType.Pedestrian;
+ args.maxLength = 160000f;
+ } else {
+ args.laneTypes = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
+ args.maxLength = 20000f;
+ }
+
+ if (CustomPathManager._instance.CustomCreatePath(out path, ref Singleton.instance.m_randomizer, args)) {
+ // NON-STOCK CODE END
+ if (startPosA.m_segment != 0 && startPosB.m_segment != 0) {
+ netManager.m_nodes.m_buffer[data.m_startNode].m_flags |= NetNode.Flags.Ambiguous;
+ } else {
+ netManager.m_nodes.m_buffer[data.m_startNode].m_flags &= ~NetNode.Flags.Ambiguous;
+ }
+ if (endPosA.m_segment != 0 && endPosB.m_segment != 0) {
+ netManager.m_nodes.m_buffer[data.m_endNode].m_flags |= NetNode.Flags.Ambiguous;
+ } else {
+ netManager.m_nodes.m_buffer[data.m_endNode].m_flags &= ~NetNode.Flags.Ambiguous;
+ }
+ data.m_path = path;
+ data.m_flags |= NetSegment.Flags.WaitingPath;
#if DEBUG
- if (debug)
- Log._Debug($"TransportLineAI.CustomStartPathFind({segmentID}, ..., {netService}, {netService2}, {vehicleType}, {skipQueue}): Started calculating path {path} for extVehicleType={extVehicleType}, startPosA=[seg={startPosA.m_segment}, lane={startPosA.m_lane}, off={startPosA.m_offset}], startPosB=[seg={startPosB.m_segment}, lane={startPosB.m_lane}, off={startPosB.m_offset}], endPosA=[seg={endPosA.m_segment}, lane={endPosA.m_lane}, off={endPosA.m_offset}], endPosB=[seg={endPosB.m_segment}, lane={endPosB.m_lane}, off={endPosB.m_offset}]");
+ if (debug)
+ Log._Debug($"TransportLineAI.CustomStartPathFind({segmentID}, ..., {netService}, {netService2}, {vehicleType}, {skipQueue}): Started calculating path {path} for extVehicleType={extVehicleType}, startPosA=[seg={startPosA.m_segment}, lane={startPosA.m_lane}, off={startPosA.m_offset}], startPosB=[seg={startPosB.m_segment}, lane={startPosB.m_lane}, off={startPosB.m_offset}], endPosA=[seg={endPosA.m_segment}, lane={endPosA.m_lane}, off={endPosA.m_offset}], endPosB=[seg={endPosB.m_segment}, lane={endPosB.m_lane}, off={endPosB.m_offset}]");
#endif
- return false;
- }
-
- CustomTransportLineAI.CheckSegmentProblems(segmentID, ref data);
- return true;
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static bool GetStopLane(ref PathUnit.Position pos, VehicleInfo.VehicleType vehicleType) {
- Log.Error($"CustomTransportLineAI.GetStopLane called.");
- return false;
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void CheckSegmentProblems(ushort segmentID, ref NetSegment data) {
- Log.Error($"CustomTransportLineAI.CheckSegmentProblems called.");
- }
-
- [RedirectReverse]
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static void CheckNodeProblems(ushort nodeID, ref NetNode data) {
- Log.Error($"CustomTransportLineAI.CheckNodeProblems called.");
- }
- }
-}
+ return false;
+ }
+
+ CustomTransportLineAI.CheckSegmentProblems(segmentID, ref data);
+ return true;
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static bool GetStopLane(ref PathUnit.Position pos, VehicleInfo.VehicleType vehicleType) {
+ Log.Error($"CustomTransportLineAI.GetStopLane called.");
+ return false;
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void CheckSegmentProblems(ushort segmentID, ref NetSegment data) {
+ Log.Error($"CustomTransportLineAI.CheckSegmentProblems called.");
+ }
+
+ [RedirectReverse]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void CheckNodeProblems(ushort nodeID, ref NetNode data) {
+ Log.Error($"CustomTransportLineAI.CheckNodeProblems called.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/AI/CustomVehicleAI.cs b/TLM/TLM/Custom/AI/CustomVehicleAI.cs
index be791f55e..ddae01a9f 100644
--- a/TLM/TLM/Custom/AI/CustomVehicleAI.cs
+++ b/TLM/TLM/Custom/AI/CustomVehicleAI.cs
@@ -57,7 +57,7 @@ protected void CustomUpdatePathTargetPositions(ushort vehicleID, ref Vehicle veh
$"\ttargetPosIndex={targetPosIndex}\n" +
$"\tmaxTargetPosIndex={maxTargetPosIndex}\n" +
$"\tminSqrDistanceA={minSqrDistanceA}\n" +
- $"\tminSqrDistanceB={minSqrDistanceB}\n" +
+ $"\tminSqrDistanceB={minSqrDistanceB}\n" +
$"\tvehicleData.m_path={vehicleData.m_path}\n" +
$"\tvehicleData.m_pathPositionIndex={vehicleData.m_pathPositionIndex}\n" +
$"\tvehicleData.m_lastPathOffset={vehicleData.m_lastPathOffset}"
@@ -381,14 +381,18 @@ protected void CustomUpdatePathTargetPositions(ushort vehicleID, ref Vehicle veh
Log._Debug($"CustomVehicle.CustomUpdatePathTargetPositions({vehicleID}): Finding best lane for emergency vehicles. Before: bestLaneIndex={bestLaneIndex}");
}
#endif
- bestLaneIndex = FindBestLane(vehicleID, ref vehicleData, nextPosition);
-
+#if ROUTING
+ bestLaneIndex = VehicleBehaviorManager.Instance.FindBestEmergencyLane(vehicleID, ref vehicleData, ref ExtVehicleManager.Instance.ExtVehicles[vehicleID], curLaneId, currentPosition, curSegmentInfo, nextPosition, nextSegmentInfo);
+#else
+ // stock procedure for emergency vehicles on duty
+ bestLaneIndex = FindBestLane(vehicleID, ref vehicleData, nextPathPos);
+#endif
#if DEBUG
if (debug) {
Log._Debug($"CustomVehicle.CustomUpdatePathTargetPositions({vehicleID}): Found best lane for emergency vehicles. After: bestLaneIndex={bestLaneIndex}");
}
#endif
- } else {
+ } else if (VehicleBehaviorManager.Instance.MayFindBestLane(vehicleID, ref vehicleData, ref ExtVehicleManager.Instance.ExtVehicles[vehicleID])) {
// NON-STOCK CODE START
if (firstIter &&
this.m_info.m_vehicleType == VehicleInfo.VehicleType.Car
diff --git a/TLM/TLM/Custom/Data/CustomVehicle.cs b/TLM/TLM/Custom/Data/CustomVehicle.cs
index 6047dac74..9e18a8147 100644
--- a/TLM/TLM/Custom/Data/CustomVehicle.cs
+++ b/TLM/TLM/Custom/Data/CustomVehicle.cs
@@ -29,7 +29,7 @@ public static void CustomSpawn(ref Vehicle vehicleData, ushort vehicleId) {
while (trailingVehicle != 0) {
vehManager.m_vehicles.m_buffer[trailingVehicle].Spawn(trailingVehicle);
trailingVehicle = vehManager.m_vehicles.m_buffer[trailingVehicle].m_trailingVehicle;
- if (++numIter > VehicleManager.MAX_VEHICLE_COUNT) {
+ if (++numIter > Constants.ServiceFactory.VehicleService.MaxVehicleCount) {
CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
break;
}
diff --git a/TLM/TLM/Custom/PathFinding/CustomPathFind.cs b/TLM/TLM/Custom/PathFinding/CustomPathFind.cs
index a615d4edc..ba86aafb0 100644
--- a/TLM/TLM/Custom/PathFinding/CustomPathFind.cs
+++ b/TLM/TLM/Custom/PathFinding/CustomPathFind.cs
@@ -1,670 +1,675 @@
#define DEBUGLOCKSx
#define COUNTSEGMENTSTONEXTJUNCTIONx
-using System;
-using System.Reflection;
-using System.Threading;
-using ColossalFramework;
-using ColossalFramework.Math;
-using ColossalFramework.UI;
-using TrafficManager.Geometry;
-using UnityEngine;
-using System.Collections.Generic;
-using TrafficManager.Custom.AI;
-using TrafficManager.TrafficLight;
-using TrafficManager.State;
-using TrafficManager.Manager;
-using TrafficManager.Traffic;
-using CSUtil.Commons;
-using TrafficManager.Manager.Impl;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
-using TrafficManager.Traffic.Data;
-using TrafficManager.Traffic.Enums;
-using TrafficManager.RedirectionFramework.Attributes;
-
namespace TrafficManager.Custom.PathFinding {
+ using System;
+ using System.Collections.Generic;
+ using System.Reflection;
+ using System.Threading;
+ using API.Traffic.Enums;
+ using API.TrafficLight;
+ using ColossalFramework;
+ using ColossalFramework.Math;
+ using CSUtil.Commons;
+ using Manager;
+ using Manager.Impl;
+ using State;
+ using Traffic.Data;
+ using Traffic.Enums;
+ using UnityEngine;
+
#if !PF2
[TargetType(typeof(PathFind))]
#endif
- public class CustomPathFind : PathFind {
- private struct BufferItem {
- public PathUnit.Position m_position;
- public float m_comparisonValue;
- public float m_methodDistance;
- public float m_duration;
- public uint m_laneID;
- public NetInfo.Direction m_direction;
- public NetInfo.LaneType m_lanesUsed;
- public VehicleInfo.VehicleType m_vehiclesUsed;
- public float m_trafficRand;
+ public class CustomPathFind : PathFind {
+ private struct BufferItem {
+ public PathUnit.Position m_position;
+ public float m_comparisonValue;
+ public float m_methodDistance;
+ public float m_duration;
+ public uint m_laneID;
+ public NetInfo.Direction m_direction;
+ public NetInfo.LaneType m_lanesUsed;
+ public VehicleInfo.VehicleType m_vehiclesUsed;
+ public float m_trafficRand;
#if COUNTSEGMENTSTONEXTJUNCTION
public uint m_numSegmentsToNextJunction;
#endif
- }
-
- private enum LaneChangingCostCalculationMode {
- None,
- ByLaneDistance,
- ByGivenDistance
- }
-
- private const float SEGMENT_MIN_AVERAGE_LENGTH = 30f;
- private const float LANE_DENSITY_DISCRETIZATION = 25f;
- private const float LANE_USAGE_DISCRETIZATION = 25f;
- private const float BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR = Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
-
- //Expose the private fields
- FieldInfo _fieldpathUnits;
- FieldInfo _fieldQueueFirst;
- FieldInfo _fieldQueueLast;
- FieldInfo _fieldQueueLock;
- FieldInfo _fieldCalculating;
- FieldInfo _fieldTerminated;
- FieldInfo _fieldPathFindThread;
-
- private Array32 PathUnits {
- get { return _fieldpathUnits.GetValue(this) as Array32; }
- set { _fieldpathUnits.SetValue(this, value); }
- }
-
- private uint QueueFirst {
- get { return (uint)_fieldQueueFirst.GetValue(this); }
- set { _fieldQueueFirst.SetValue(this, value); }
- }
-
- private uint QueueLast {
- get { return (uint)_fieldQueueLast.GetValue(this); }
- set { _fieldQueueLast.SetValue(this, value); }
- }
-
- private uint Calculating {
- get { return (uint)_fieldCalculating.GetValue(this); }
- set { _fieldCalculating.SetValue(this, value); }
- }
-
- private object QueueLock {
- get { return _fieldQueueLock.GetValue(this); }
- set { _fieldQueueLock.SetValue(this, value); }
- }
-
- private object _bufferLock;
- internal Thread CustomPathFindThread {
- get { return (Thread)_fieldPathFindThread.GetValue(this); }
- set { _fieldPathFindThread.SetValue(this, value); }
- }
-
- private bool Terminated {
- get { return (bool)_fieldTerminated.GetValue(this); }
- set { _fieldTerminated.SetValue(this, value); }
- }
- private int m_bufferMinPos;
- private int m_bufferMaxPos;
- private uint[] m_laneLocation;
- private PathUnit.Position[] m_laneTarget;
- private BufferItem[] m_buffer;
- private int[] m_bufferMin;
- private int[] m_bufferMax;
- private float m_maxLength;
- private uint m_startLaneA;
- private uint m_startLaneB;
- private ushort m_startSegmentA;
- private ushort m_startSegmentB;
- private uint m_endLaneA;
- private uint m_endLaneB;
- private uint m_vehicleLane;
- private byte m_startOffsetA;
- private byte m_startOffsetB;
- private byte m_vehicleOffset;
- private NetSegment.Flags m_carBanMask;
- private bool m_isHeavyVehicle;
- private bool m_ignoreBlocked;
- private bool m_stablePath;
- private bool m_randomParking;
- private bool m_transportVehicle;
- private bool m_ignoreCost;
- private PathUnitQueueItem queueItem;
- private NetSegment.Flags m_disableMask;
- /*private ExtVehicleType? _extVehicleType;
- private ushort? _vehicleId;
- private ExtCitizenInstance.ExtPathType? _extPathType;*/
- private bool m_isRoadVehicle;
- private bool m_isLaneArrowObeyingEntity;
- private bool m_isLaneConnectionObeyingEntity;
- private bool m_leftHandDrive;
- //private float _speedRand;
- //private bool _extPublicTransport;
- //private static ushort laneChangeRandCounter = 0;
+ }
+
+ private enum LaneChangingCostCalculationMode {
+ None,
+ ByLaneDistance,
+ ByGivenDistance
+ }
+
+ private const float SEGMENT_MIN_AVERAGE_LENGTH = 30f;
+ private const float LANE_DENSITY_DISCRETIZATION = 25f;
+ private const float LANE_USAGE_DISCRETIZATION = 25f;
+ private const float BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR = Constants.BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+
+ //Expose the private fields
+ FieldInfo _fieldpathUnits;
+ FieldInfo _fieldQueueFirst;
+ FieldInfo _fieldQueueLast;
+ FieldInfo _fieldQueueLock;
+ FieldInfo _fieldCalculating;
+ FieldInfo _fieldTerminated;
+ FieldInfo _fieldPathFindThread;
+
+ private Array32 PathUnits {
+ get { return _fieldpathUnits.GetValue(this) as Array32; }
+ set { _fieldpathUnits.SetValue(this, value); }
+ }
+
+ private uint QueueFirst {
+ get { return (uint)_fieldQueueFirst.GetValue(this); }
+ set { _fieldQueueFirst.SetValue(this, value); }
+ }
+
+ private uint QueueLast {
+ get { return (uint)_fieldQueueLast.GetValue(this); }
+ set { _fieldQueueLast.SetValue(this, value); }
+ }
+
+ private uint Calculating {
+ get { return (uint)_fieldCalculating.GetValue(this); }
+ set { _fieldCalculating.SetValue(this, value); }
+ }
+
+ private object QueueLock {
+ get { return _fieldQueueLock.GetValue(this); }
+ set { _fieldQueueLock.SetValue(this, value); }
+ }
+
+ private object _bufferLock;
+ internal Thread CustomPathFindThread {
+ get { return (Thread)_fieldPathFindThread.GetValue(this); }
+ set { _fieldPathFindThread.SetValue(this, value); }
+ }
+
+ private bool Terminated {
+ get { return (bool)_fieldTerminated.GetValue(this); }
+ set { _fieldTerminated.SetValue(this, value); }
+ }
+ private int m_bufferMinPos;
+ private int m_bufferMaxPos;
+ private uint[] m_laneLocation;
+ private PathUnit.Position[] m_laneTarget;
+ private BufferItem[] m_buffer;
+ private int[] m_bufferMin;
+ private int[] m_bufferMax;
+ private float m_maxLength;
+ private uint m_startLaneA;
+ private uint m_startLaneB;
+ private ushort m_startSegmentA;
+ private ushort m_startSegmentB;
+ private uint m_endLaneA;
+ private uint m_endLaneB;
+ private uint m_vehicleLane;
+ private byte m_startOffsetA;
+ private byte m_startOffsetB;
+ private byte m_vehicleOffset;
+ private NetSegment.Flags m_carBanMask;
+ private bool m_isHeavyVehicle;
+ private bool m_ignoreBlocked;
+ private bool m_stablePath;
+ private bool m_randomParking;
+ private bool m_transportVehicle;
+ private bool m_ignoreCost;
+ private PathUnitQueueItem queueItem;
+ private NetSegment.Flags m_disableMask;
+ /*private ExtVehicleType? _extVehicleType;
+ private ushort? _vehicleId;
+ private ExtCitizenInstance.ExtPathType? _extPathType;*/
+ private bool m_isRoadVehicle;
+ private bool m_isLaneArrowObeyingEntity;
+ private bool m_isLaneConnectionObeyingEntity;
+ private bool m_leftHandDrive;
+ //private float _speedRand;
+ //private bool _extPublicTransport;
+ //private static ushort laneChangeRandCounter = 0;
#if DEBUG
- public uint m_failedPathFinds = 0;
- public uint m_succeededPathFinds = 0;
- private bool m_debug = false;
- private IDictionary> m_debugPositions = null;
+ public uint m_failedPathFinds = 0;
+ public uint m_succeededPathFinds = 0;
+ private bool m_debug = false;
+ private IDictionary> m_debugPositions = null;
#endif
- public int pfId = 0;
- private Randomizer m_pathRandomizer;
- private uint m_pathFindIndex;
- private NetInfo.LaneType m_laneTypes;
- private VehicleInfo.VehicleType m_vehicleTypes;
+ public int pfId = 0;
+ private Randomizer m_pathRandomizer;
+ private uint m_pathFindIndex;
+ private NetInfo.LaneType m_laneTypes;
+ private VehicleInfo.VehicleType m_vehicleTypes;
- private GlobalConfig m_conf = null;
+ private GlobalConfig m_conf = null;
- private static readonly CustomSegmentLightsManager customTrafficLightsManager = CustomSegmentLightsManager.Instance;
- private static readonly JunctionRestrictionsManager junctionManager = JunctionRestrictionsManager.Instance;
- private static readonly VehicleRestrictionsManager vehicleRestrictionsManager = VehicleRestrictionsManager.Instance;
- private static readonly SpeedLimitManager speedLimitManager = SpeedLimitManager.Instance;
- private static readonly TrafficMeasurementManager trafficMeasurementManager = TrafficMeasurementManager.Instance;
- private static readonly RoutingManager routingManager = RoutingManager.Instance;
+ private static readonly CustomSegmentLightsManager customTrafficLightsManager = CustomSegmentLightsManager.Instance;
+ private static readonly JunctionRestrictionsManager junctionManager = JunctionRestrictionsManager.Instance;
+ private static readonly VehicleRestrictionsManager vehicleRestrictionsManager = VehicleRestrictionsManager.Instance;
+ private static readonly SpeedLimitManager speedLimitManager = SpeedLimitManager.Instance;
+ private static readonly TrafficMeasurementManager trafficMeasurementManager = TrafficMeasurementManager.Instance;
+ private static readonly RoutingManager routingManager = RoutingManager.Instance;
- public bool IsMasterPathFind = false;
+ public bool IsMasterPathFind = false;
- protected virtual void Awake() {
+ protected virtual void Awake() {
#if DEBUG
- Log._Debug($"CustomPathFind.Awake called.");
+ Log._Debug($"CustomPathFind.Awake called.");
#endif
- var stockPathFindType = typeof(PathFind);
- const BindingFlags fieldFlags = BindingFlags.NonPublic | BindingFlags.Instance;
+ var stockPathFindType = typeof(PathFind);
+ const BindingFlags fieldFlags = BindingFlags.NonPublic | BindingFlags.Instance;
- _fieldpathUnits = stockPathFindType.GetField("m_pathUnits", fieldFlags);
- _fieldQueueFirst = stockPathFindType.GetField("m_queueFirst", fieldFlags);
- _fieldQueueLast = stockPathFindType.GetField("m_queueLast", fieldFlags);
- _fieldQueueLock = stockPathFindType.GetField("m_queueLock", fieldFlags);
- _fieldTerminated = stockPathFindType.GetField("m_terminated", fieldFlags);
- _fieldCalculating = stockPathFindType.GetField("m_calculating", fieldFlags);
- _fieldPathFindThread = stockPathFindType.GetField("m_pathFindThread", fieldFlags);
+ _fieldpathUnits = stockPathFindType.GetField("m_pathUnits", fieldFlags);
+ _fieldQueueFirst = stockPathFindType.GetField("m_queueFirst", fieldFlags);
+ _fieldQueueLast = stockPathFindType.GetField("m_queueLast", fieldFlags);
+ _fieldQueueLock = stockPathFindType.GetField("m_queueLock", fieldFlags);
+ _fieldTerminated = stockPathFindType.GetField("m_terminated", fieldFlags);
+ _fieldCalculating = stockPathFindType.GetField("m_calculating", fieldFlags);
+ _fieldPathFindThread = stockPathFindType.GetField("m_pathFindThread", fieldFlags);
- m_buffer = new BufferItem[65536]; // 2^16
- _bufferLock = PathManager.instance.m_bufferLock;
- PathUnits = PathManager.instance.m_pathUnits;
+ m_buffer = new BufferItem[65536]; // 2^16
+ _bufferLock = PathManager.instance.m_bufferLock;
+ PathUnits = PathManager.instance.m_pathUnits;
#if DEBUG
- if (QueueLock == null) {
- Log._Debug($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.Awake: QueueLock is null. Creating.");
- QueueLock = new object();
- } else {
- Log._Debug($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.Awake: QueueLock is NOT null.");
- }
+ if (QueueLock == null) {
+ Log._Debug($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.Awake: QueueLock is null. Creating.");
+ QueueLock = new object();
+ } else {
+ Log._Debug($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.Awake: QueueLock is NOT null.");
+ }
#else
QueueLock = new object();
#endif
- m_laneLocation = new uint[262144]; // 2^18
- m_laneTarget = new PathUnit.Position[262144]; // 2^18
- m_bufferMin = new int[1024]; // 2^10
- m_bufferMax = new int[1024]; // 2^10
-
- m_pathfindProfiler = new ThreadProfiler();
- CustomPathFindThread = new Thread(PathFindThread) { Name = "Pathfind" };
- CustomPathFindThread.Priority = SimulationManager.SIMULATION_PRIORITY;
- CustomPathFindThread.Start();
- if (!CustomPathFindThread.IsAlive) {
- //CODebugBase.Error(LogChannel.Core, "Path find thread failed to start!");
- Log.Error($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) Path find thread failed to start!");
- }
+ m_laneLocation = new uint[262144]; // 2^18
+ m_laneTarget = new PathUnit.Position[262144]; // 2^18
+ m_bufferMin = new int[1024]; // 2^10
+ m_bufferMax = new int[1024]; // 2^10
+
+ m_pathfindProfiler = new ThreadProfiler();
+ CustomPathFindThread = new Thread(PathFindThread) { Name = "Pathfind" };
+ CustomPathFindThread.Priority = SimulationManager.SIMULATION_PRIORITY;
+ CustomPathFindThread.Start();
+ if (!CustomPathFindThread.IsAlive) {
+ //CODebugBase.Error(LogChannel.Core, "Path find thread failed to start!");
+ Log.Error($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) Path find thread failed to start!");
+ }
- }
+ }
- protected virtual void OnDestroy() {
+ protected virtual void OnDestroy() {
#if DEBUGLOCKS
uint lockIter = 0;
#endif
- try {
- Monitor.Enter(QueueLock);
- Terminated = true;
- Monitor.PulseAll(QueueLock);
- } catch (Exception e) {
- Log.Error("CustomPathFind.OnDestroy Error: " + e.ToString());
- } finally {
- Monitor.Exit(QueueLock);
- }
- }
+ try {
+ Monitor.Enter(QueueLock);
+ Terminated = true;
+ Monitor.PulseAll(QueueLock);
+ } catch (Exception e) {
+ Log.Error("CustomPathFind.OnDestroy Error: " + e.ToString());
+ } finally {
+ Monitor.Exit(QueueLock);
+ }
+ }
#if !PF2
[RedirectMethod]
#endif
- public new bool CalculatePath(uint unit, bool skipQueue) {
- return ExtCalculatePath(unit, skipQueue);
- }
-
- public bool ExtCalculatePath(uint unit, bool skipQueue) {
- if (CustomPathManager._instance.AddPathReference(unit)) {
- try {
- Monitor.Enter(QueueLock);
-
- if (skipQueue) {
-
- if (this.QueueLast == 0u) {
- this.QueueLast = unit;
- } else {
- CustomPathManager._instance.queueItems[unit].nextPathUnitId = QueueFirst;
- //this.PathUnits.m_buffer[unit].m_nextPathUnit = this.QueueFirst;
- }
- this.QueueFirst = unit;
- } else {
- if (this.QueueLast == 0u) {
- this.QueueFirst = unit;
- } else {
- CustomPathManager._instance.queueItems[QueueLast].nextPathUnitId = unit;
- //this.PathUnits.m_buffer[this.QueueLast].m_nextPathUnit = unit;
- }
- this.QueueLast = unit;
- }
- this.PathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_CREATED;
- ++this.m_queuedPathFindCount;
-
- Monitor.Pulse(this.QueueLock);
- } catch (Exception e) {
- Log.Error($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.CalculatePath({unit}, {skipQueue}): Error: {e.ToString()}");
- } finally {
- Monitor.Exit(this.QueueLock);
- }
- return true;
- }
- return false;
- }
-
- // PathFind
- protected void PathFindImplementation(uint unit, ref PathUnit data) {
- m_conf = GlobalConfig.Instance; // NON-STOCK CODE
-
- NetManager netManager = Singleton.instance;
- this.m_laneTypes = (NetInfo.LaneType)this.PathUnits.m_buffer[unit].m_laneTypes;
- this.m_vehicleTypes = (VehicleInfo.VehicleType)this.PathUnits.m_buffer[unit].m_vehicleTypes;
- this.m_maxLength = this.PathUnits.m_buffer[unit].m_length;
- this.m_pathFindIndex = (this.m_pathFindIndex + 1u & 32767u);
- this.m_pathRandomizer = new Randomizer(unit);
-
- this.m_carBanMask = NetSegment.Flags.CarBan;
- this.m_isHeavyVehicle = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 16) != 0);
- if (m_isHeavyVehicle) {
- this.m_carBanMask |= NetSegment.Flags.HeavyBan;
- }
- if ((this.PathUnits.m_buffer[unit].m_simulationFlags & 4) != 0) {
- this.m_carBanMask |= NetSegment.Flags.WaitingPath;
- }
- this.m_ignoreBlocked = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 32) != 0);
- this.m_stablePath = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 64) != 0);
- this.m_randomParking = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 128) != 0);
- this.m_transportVehicle = ((byte)(this.m_laneTypes & NetInfo.LaneType.TransportVehicle) != 0);
- this.m_ignoreCost = (this.m_stablePath || (this.PathUnits.m_buffer[unit].m_simulationFlags & 8) != 0);
- this.m_disableMask = (NetSegment.Flags.Collapsed | NetSegment.Flags.PathFailed);
- if ((this.PathUnits.m_buffer[unit].m_simulationFlags & 2) == 0) {
- this.m_disableMask |= NetSegment.Flags.Flooded;
- }
- //this._speedRand = 0;
- this.m_leftHandDrive = Constants.ServiceFactory.SimulationService.LeftHandDrive;
- this.m_isRoadVehicle = (queueItem.vehicleType & ExtVehicleType.RoadVehicle) != ExtVehicleType.None;
- this.m_isLaneArrowObeyingEntity = (m_vehicleTypes & LaneArrowManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None &&
- (queueItem.vehicleType & LaneArrowManager.EXT_VEHICLE_TYPES) != ExtVehicleType.None;
- this.m_isLaneConnectionObeyingEntity = (m_vehicleTypes & LaneConnectionManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None &&
- (queueItem.vehicleType & LaneConnectionManager.EXT_VEHICLE_TYPES) != ExtVehicleType.None;
+ public new bool CalculatePath(uint unit, bool skipQueue) {
+ return ExtCalculatePath(unit, skipQueue);
+ }
+
+ public bool ExtCalculatePath(uint unit, bool skipQueue) {
+ if (CustomPathManager._instance.AddPathReference(unit)) {
+ try {
+ Monitor.Enter(QueueLock);
+
+ if (skipQueue) {
+
+ if (this.QueueLast == 0u) {
+ this.QueueLast = unit;
+ } else {
+ CustomPathManager._instance.queueItems[unit].nextPathUnitId = QueueFirst;
+ //this.PathUnits.m_buffer[unit].m_nextPathUnit = this.QueueFirst;
+ }
+ this.QueueFirst = unit;
+ } else {
+ if (this.QueueLast == 0u) {
+ this.QueueFirst = unit;
+ } else {
+ CustomPathManager._instance.queueItems[QueueLast].nextPathUnitId = unit;
+ //this.PathUnits.m_buffer[this.QueueLast].m_nextPathUnit = unit;
+ }
+ this.QueueLast = unit;
+ }
+ this.PathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_CREATED;
+ ++this.m_queuedPathFindCount;
+
+ Monitor.Pulse(this.QueueLock);
+ } catch (Exception e) {
+ Log.Error($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.CalculatePath({unit}, {skipQueue}): Error: {e.ToString()}");
+ } finally {
+ Monitor.Exit(this.QueueLock);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ // PathFind
+ protected void PathFindImplementation(uint unit, ref PathUnit data) {
+ m_conf = GlobalConfig.Instance; // NON-STOCK CODE
+
+ NetManager netManager = Singleton.instance;
+ this.m_laneTypes = (NetInfo.LaneType)this.PathUnits.m_buffer[unit].m_laneTypes;
+ this.m_vehicleTypes = (VehicleInfo.VehicleType)this.PathUnits.m_buffer[unit].m_vehicleTypes;
+ this.m_maxLength = this.PathUnits.m_buffer[unit].m_length;
+ this.m_pathFindIndex = (this.m_pathFindIndex + 1u & 32767u);
+ this.m_pathRandomizer = new Randomizer(unit);
+
+ this.m_carBanMask = NetSegment.Flags.CarBan;
+ this.m_isHeavyVehicle = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 16) != 0);
+ if (m_isHeavyVehicle) {
+ this.m_carBanMask |= NetSegment.Flags.HeavyBan;
+ }
+ if ((this.PathUnits.m_buffer[unit].m_simulationFlags & 4) != 0) {
+ this.m_carBanMask |= NetSegment.Flags.WaitingPath;
+ }
+ this.m_ignoreBlocked = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 32) != 0);
+ this.m_stablePath = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 64) != 0);
+ this.m_randomParking = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 128) != 0);
+ this.m_transportVehicle = ((byte)(this.m_laneTypes & NetInfo.LaneType.TransportVehicle) != 0);
+ this.m_ignoreCost = (this.m_stablePath || (this.PathUnits.m_buffer[unit].m_simulationFlags & 8) != 0);
+ this.m_disableMask = (NetSegment.Flags.Collapsed | NetSegment.Flags.PathFailed);
+ if ((this.PathUnits.m_buffer[unit].m_simulationFlags & 2) == 0) {
+ this.m_disableMask |= NetSegment.Flags.Flooded;
+ }
+ //this._speedRand = 0;
+ this.m_leftHandDrive = Constants.ServiceFactory.SimulationService.LeftHandDrive;
+ this.m_isRoadVehicle = (queueItem.vehicleType & ExtVehicleType.RoadVehicle) != ExtVehicleType.None;
+ this.m_isLaneArrowObeyingEntity =
+ (m_vehicleTypes & LaneArrowManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None
+ && (queueItem.vehicleType & LaneArrowManager.EXT_VEHICLE_TYPES) != ExtVehicleType.None;
+ this.m_isLaneConnectionObeyingEntity =
+ (m_vehicleTypes & LaneConnectionManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None
+ && (queueItem.vehicleType & LaneConnectionManager.EXT_VEHICLE_TYPES) != ExtVehicleType.None;
#if DEBUGNEWPF && DEBUG
- bool debug = this.m_debug = m_conf.Debug.Switches[0] &&
- ((m_conf.Debug.ExtVehicleType == ExtVehicleType.None && queueItem.vehicleType == ExtVehicleType.None) || (queueItem.vehicleType & m_conf.Debug.ExtVehicleType) != ExtVehicleType.None) &&
- (m_conf.Debug.StartSegmentId == 0 || data.m_position00.m_segment == m_conf.Debug.StartSegmentId || data.m_position02.m_segment == m_conf.Debug.StartSegmentId) &&
- (m_conf.Debug.EndSegmentId == 0 || data.m_position01.m_segment == m_conf.Debug.EndSegmentId || data.m_position03.m_segment == m_conf.Debug.EndSegmentId) &&
- (m_conf.Debug.VehicleId == 0 || queueItem.vehicleId == m_conf.Debug.VehicleId)
- ;
- if (debug) {
- Log._Debug($"CustomPathFind.PathFindImplementation: START calculating path unit {unit}, type {queueItem.vehicleType}");
- m_debugPositions = new Dictionary>();
- }
-#endif
-
- if ((byte)(this.m_laneTypes & NetInfo.LaneType.Vehicle) != 0) {
- this.m_laneTypes |= NetInfo.LaneType.TransportVehicle;
- }
- int posCount = (int)(this.PathUnits.m_buffer[unit].m_positionCount & 15);
- int vehiclePosIndicator = this.PathUnits.m_buffer[unit].m_positionCount >> 4;
- BufferItem bufferItemStartA;
- if (data.m_position00.m_segment != 0 && posCount >= 1) {
- this.m_startLaneA = PathManager.GetLaneID(data.m_position00);
- this.m_startSegmentA = data.m_position00.m_segment; // NON-STOCK CODE
- this.m_startOffsetA = data.m_position00.m_offset;
- bufferItemStartA.m_laneID = this.m_startLaneA;
- bufferItemStartA.m_position = data.m_position00;
- this.GetLaneDirection(data.m_position00, out bufferItemStartA.m_direction, out bufferItemStartA.m_lanesUsed, out bufferItemStartA.m_vehiclesUsed);
- bufferItemStartA.m_comparisonValue = 0f;
- bufferItemStartA.m_duration = 0f;
+ bool debug =
+ this.m_debug =
+ m_conf.Debug.Switches[0] &&
+ ((m_conf.Debug.ApiExtVehicleType == ExtVehicleType.None
+ && queueItem.vehicleType == ExtVehicleType.None)
+ || (queueItem.vehicleType & m_conf.Debug.ApiExtVehicleType) != ExtVehicleType.None)
+ && (m_conf.Debug.StartSegmentId == 0
+ || data.m_position00.m_segment == m_conf.Debug.StartSegmentId
+ || data.m_position02.m_segment == m_conf.Debug.StartSegmentId)
+ && (m_conf.Debug.EndSegmentId == 0
+ || data.m_position01.m_segment == m_conf.Debug.EndSegmentId
+ || data.m_position03.m_segment == m_conf.Debug.EndSegmentId)
+ && (m_conf.Debug.VehicleId == 0
+ || queueItem.vehicleId == m_conf.Debug.VehicleId);
+ if (debug) {
+ Log._Debug($"CustomPathFind.PathFindImplementation: START calculating path unit {unit}, type {queueItem.vehicleType}");
+ m_debugPositions = new Dictionary>();
+ }
+#endif
+
+ if ((byte)(this.m_laneTypes & NetInfo.LaneType.Vehicle) != 0) {
+ this.m_laneTypes |= NetInfo.LaneType.TransportVehicle;
+ }
+ int posCount = (int)(this.PathUnits.m_buffer[unit].m_positionCount & 15);
+ int vehiclePosIndicator = this.PathUnits.m_buffer[unit].m_positionCount >> 4;
+ BufferItem bufferItemStartA;
+ if (data.m_position00.m_segment != 0 && posCount >= 1) {
+ this.m_startLaneA = PathManager.GetLaneID(data.m_position00);
+ this.m_startSegmentA = data.m_position00.m_segment; // NON-STOCK CODE
+ this.m_startOffsetA = data.m_position00.m_offset;
+ bufferItemStartA.m_laneID = this.m_startLaneA;
+ bufferItemStartA.m_position = data.m_position00;
+ this.GetLaneDirection(data.m_position00, out bufferItemStartA.m_direction, out bufferItemStartA.m_lanesUsed, out bufferItemStartA.m_vehiclesUsed);
+ bufferItemStartA.m_comparisonValue = 0f;
+ bufferItemStartA.m_duration = 0f;
#if COUNTSEGMENTSTONEXTJUNCTION
bufferItemStartA.m_numSegmentsToNextJunction = 0;
#endif
- } else {
- this.m_startLaneA = 0u;
- this.m_startSegmentA = 0; // NON-STOCK CODE
- this.m_startOffsetA = 0;
- bufferItemStartA = default(BufferItem);
- }
- BufferItem bufferItemStartB;
- if (data.m_position02.m_segment != 0 && posCount >= 3) {
- this.m_startLaneB = PathManager.GetLaneID(data.m_position02);
- this.m_startSegmentB = data.m_position02.m_segment; // NON-STOCK CODE
- this.m_startOffsetB = data.m_position02.m_offset;
- bufferItemStartB.m_laneID = this.m_startLaneB;
- bufferItemStartB.m_position = data.m_position02;
- this.GetLaneDirection(data.m_position02, out bufferItemStartB.m_direction, out bufferItemStartB.m_lanesUsed, out bufferItemStartB.m_vehiclesUsed);
- bufferItemStartB.m_comparisonValue = 0f;
- bufferItemStartB.m_duration = 0f;
+ } else {
+ this.m_startLaneA = 0u;
+ this.m_startSegmentA = 0; // NON-STOCK CODE
+ this.m_startOffsetA = 0;
+ bufferItemStartA = default(BufferItem);
+ }
+ BufferItem bufferItemStartB;
+ if (data.m_position02.m_segment != 0 && posCount >= 3) {
+ this.m_startLaneB = PathManager.GetLaneID(data.m_position02);
+ this.m_startSegmentB = data.m_position02.m_segment; // NON-STOCK CODE
+ this.m_startOffsetB = data.m_position02.m_offset;
+ bufferItemStartB.m_laneID = this.m_startLaneB;
+ bufferItemStartB.m_position = data.m_position02;
+ this.GetLaneDirection(data.m_position02, out bufferItemStartB.m_direction, out bufferItemStartB.m_lanesUsed, out bufferItemStartB.m_vehiclesUsed);
+ bufferItemStartB.m_comparisonValue = 0f;
+ bufferItemStartB.m_duration = 0f;
#if COUNTSEGMENTSTONEXTJUNCTION
bufferItemStartB.m_numSegmentsToNextJunction = 0;
#endif
- } else {
- this.m_startLaneB = 0u;
- this.m_startSegmentB = 0; // NON-STOCK CODE
- this.m_startOffsetB = 0;
- bufferItemStartB = default(BufferItem);
- }
- BufferItem bufferItemEndA;
- if (data.m_position01.m_segment != 0 && posCount >= 2) {
- this.m_endLaneA = PathManager.GetLaneID(data.m_position01);
- bufferItemEndA.m_laneID = this.m_endLaneA;
- bufferItemEndA.m_position = data.m_position01;
- this.GetLaneDirection(data.m_position01, out bufferItemEndA.m_direction, out bufferItemEndA.m_lanesUsed, out bufferItemEndA.m_vehiclesUsed);
- bufferItemEndA.m_methodDistance = 0.01f;
- bufferItemEndA.m_comparisonValue = 0f;
- bufferItemEndA.m_duration = 0f;
- bufferItemEndA.m_trafficRand = 0; // NON-STOCK CODE
+ } else {
+ this.m_startLaneB = 0u;
+ this.m_startSegmentB = 0; // NON-STOCK CODE
+ this.m_startOffsetB = 0;
+ bufferItemStartB = default(BufferItem);
+ }
+ BufferItem bufferItemEndA;
+ if (data.m_position01.m_segment != 0 && posCount >= 2) {
+ this.m_endLaneA = PathManager.GetLaneID(data.m_position01);
+ bufferItemEndA.m_laneID = this.m_endLaneA;
+ bufferItemEndA.m_position = data.m_position01;
+ this.GetLaneDirection(data.m_position01, out bufferItemEndA.m_direction, out bufferItemEndA.m_lanesUsed, out bufferItemEndA.m_vehiclesUsed);
+ bufferItemEndA.m_methodDistance = 0.01f;
+ bufferItemEndA.m_comparisonValue = 0f;
+ bufferItemEndA.m_duration = 0f;
+ bufferItemEndA.m_trafficRand = 0; // NON-STOCK CODE
#if COUNTSEGMENTSTONEXTJUNCTION
bufferItemEndA.m_numSegmentsToNextJunction = 0;
#endif
- } else {
- this.m_endLaneA = 0u;
- bufferItemEndA = default(BufferItem);
- }
- BufferItem bufferItemEndB;
- if (data.m_position03.m_segment != 0 && posCount >= 4) {
- this.m_endLaneB = PathManager.GetLaneID(data.m_position03);
- bufferItemEndB.m_laneID = this.m_endLaneB;
- bufferItemEndB.m_position = data.m_position03;
- this.GetLaneDirection(data.m_position03, out bufferItemEndB.m_direction, out bufferItemEndB.m_lanesUsed, out bufferItemEndB.m_vehiclesUsed);
- bufferItemEndB.m_methodDistance = 0.01f;
- bufferItemEndB.m_comparisonValue = 0f;
- bufferItemEndB.m_duration = 0f;
- bufferItemEndB.m_trafficRand = 0; // NON-STOCK CODE
+ } else {
+ this.m_endLaneA = 0u;
+ bufferItemEndA = default(BufferItem);
+ }
+ BufferItem bufferItemEndB;
+ if (data.m_position03.m_segment != 0 && posCount >= 4) {
+ this.m_endLaneB = PathManager.GetLaneID(data.m_position03);
+ bufferItemEndB.m_laneID = this.m_endLaneB;
+ bufferItemEndB.m_position = data.m_position03;
+ this.GetLaneDirection(data.m_position03, out bufferItemEndB.m_direction, out bufferItemEndB.m_lanesUsed, out bufferItemEndB.m_vehiclesUsed);
+ bufferItemEndB.m_methodDistance = 0.01f;
+ bufferItemEndB.m_comparisonValue = 0f;
+ bufferItemEndB.m_duration = 0f;
+ bufferItemEndB.m_trafficRand = 0; // NON-STOCK CODE
#if COUNTSEGMENTSTONEXTJUNCTION
bufferItemEndB.m_numSegmentsToNextJunction = 0;
#endif
- } else {
- this.m_endLaneB = 0u;
- bufferItemEndB = default(BufferItem);
- }
- if (data.m_position11.m_segment != 0 && vehiclePosIndicator >= 1) {
- this.m_vehicleLane = PathManager.GetLaneID(data.m_position11);
- this.m_vehicleOffset = data.m_position11.m_offset;
- } else {
- this.m_vehicleLane = 0u;
- this.m_vehicleOffset = 0;
- }
+ } else {
+ this.m_endLaneB = 0u;
+ bufferItemEndB = default(BufferItem);
+ }
+ if (data.m_position11.m_segment != 0 && vehiclePosIndicator >= 1) {
+ this.m_vehicleLane = PathManager.GetLaneID(data.m_position11);
+ this.m_vehicleOffset = data.m_position11.m_offset;
+ } else {
+ this.m_vehicleLane = 0u;
+ this.m_vehicleOffset = 0;
+ }
#if DEBUGNEWPF && DEBUG
- if (debug) {
- Log._Debug($"CustomPathFind.PathFindImplementation: Preparing calculating path unit {unit}, type {queueItem.vehicleType}:\n" +
- $"\tbufferItemStartA: segment={bufferItemStartA.m_position.m_segment} lane={bufferItemStartA.m_position.m_lane} off={bufferItemStartA.m_position.m_offset} laneId={bufferItemStartA.m_laneID}\n" +
- $"\tbufferItemStartB: segment={bufferItemStartB.m_position.m_segment} lane={bufferItemStartB.m_position.m_lane} off={bufferItemStartB.m_position.m_offset} laneId={bufferItemStartB.m_laneID}\n" +
- $"\tbufferItemEndA: segment={bufferItemEndA.m_position.m_segment} lane={bufferItemEndA.m_position.m_lane} off={bufferItemEndA.m_position.m_offset} laneId={bufferItemEndA.m_laneID}\n" +
- $"\tbufferItemEndB: segment={bufferItemEndB.m_position.m_segment} lane={bufferItemEndB.m_position.m_lane} off={bufferItemEndB.m_position.m_offset} laneId={bufferItemEndB.m_laneID}\n" +
- $"\tvehicleItem: segment={data.m_position11.m_segment} lane={data.m_position11.m_lane} off={data.m_position11.m_offset} laneId={m_vehicleLane} vehiclePosIndicator={vehiclePosIndicator}\n"
- );
- }
-#endif
- BufferItem finalBufferItem = default(BufferItem);
- byte startOffset = 0;
- this.m_bufferMinPos = 0;
- this.m_bufferMaxPos = -1;
- if (this.m_pathFindIndex == 0u) {
- uint maxUInt = 4294901760u;
- for (int i = 0; i < 262144; ++i) {
- this.m_laneLocation[i] = maxUInt;
- }
- }
- for (int j = 0; j < 1024; ++j) {
- this.m_bufferMin[j] = 0;
- this.m_bufferMax[j] = -1;
- }
- if (bufferItemEndA.m_position.m_segment != 0) {
- ++this.m_bufferMax[0];
- this.m_buffer[++this.m_bufferMaxPos] = bufferItemEndA;
- }
- if (bufferItemEndB.m_position.m_segment != 0) {
- ++this.m_bufferMax[0];
- this.m_buffer[++this.m_bufferMaxPos] = bufferItemEndB;
- }
- bool canFindPath = false;
-
- while (this.m_bufferMinPos <= this.m_bufferMaxPos) {
- int bufMin = this.m_bufferMin[this.m_bufferMinPos];
- int bufMax = this.m_bufferMax[this.m_bufferMinPos];
- if (bufMin > bufMax) {
- ++this.m_bufferMinPos;
- } else {
- this.m_bufferMin[this.m_bufferMinPos] = bufMin + 1;
- BufferItem candidateItem = this.m_buffer[(this.m_bufferMinPos << 6) + bufMin];
- if (candidateItem.m_position.m_segment == bufferItemStartA.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartA.m_position.m_lane) {
- // we reached startA
- if ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0 && candidateItem.m_position.m_offset >= this.m_startOffsetA) {
- finalBufferItem = candidateItem;
- startOffset = this.m_startOffsetA;
- canFindPath = true;
- break;
- }
- if ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0 && candidateItem.m_position.m_offset <= this.m_startOffsetA) {
- finalBufferItem = candidateItem;
- startOffset = this.m_startOffsetA;
- canFindPath = true;
- break;
- }
- }
- if (candidateItem.m_position.m_segment == bufferItemStartB.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartB.m_position.m_lane) {
- // we reached startB
- if ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0 && candidateItem.m_position.m_offset >= this.m_startOffsetB) {
- finalBufferItem = candidateItem;
- startOffset = this.m_startOffsetB;
- canFindPath = true;
- break;
- }
- if ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0 && candidateItem.m_position.m_offset <= this.m_startOffsetB) {
- finalBufferItem = candidateItem;
- startOffset = this.m_startOffsetB;
- canFindPath = true;
- break;
- }
- }
-
- // explore the path
- if ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0) {
- ushort startNode = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode;
- uint laneRoutingIndex = routingManager.GetLaneEndRoutingIndex(candidateItem.m_laneID, true);
- this.ProcessItemMain(unit, candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], routingManager.SegmentRoutings[candidateItem.m_position.m_segment], routingManager.LaneEndBackwardRoutings[laneRoutingIndex], startNode, true, ref netManager.m_nodes.m_buffer[startNode], 0, false);
- }
-
- if ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0) {
- ushort endNode = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode;
- uint laneRoutingIndex = routingManager.GetLaneEndRoutingIndex(candidateItem.m_laneID, false);
- this.ProcessItemMain(unit, candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], routingManager.SegmentRoutings[candidateItem.m_position.m_segment], routingManager.LaneEndBackwardRoutings[laneRoutingIndex], endNode, false, ref netManager.m_nodes.m_buffer[endNode], 255, false);
- }
-
- // handle special nodes (e.g. bus stops)
- int num6 = 0;
- ushort specialNodeId = netManager.m_lanes.m_buffer[candidateItem.m_laneID].m_nodes;
- if (specialNodeId != 0) {
- ushort startNode2 = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode;
- ushort endNode2 = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode;
- bool nodesDisabled = ((netManager.m_nodes.m_buffer[startNode2].m_flags | netManager.m_nodes.m_buffer[endNode2].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None;
- while (specialNodeId != 0) {
- NetInfo.Direction direction = NetInfo.Direction.None;
- byte laneOffset = netManager.m_nodes.m_buffer[specialNodeId].m_laneOffset;
- if (laneOffset <= candidateItem.m_position.m_offset) {
- direction |= NetInfo.Direction.Forward;
- }
- if (laneOffset >= candidateItem.m_position.m_offset) {
- direction |= NetInfo.Direction.Backward;
- }
- if ((byte)(candidateItem.m_direction & direction) != 0 && (!nodesDisabled || (netManager.m_nodes.m_buffer[specialNodeId].m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None)) {
+ if (debug) {
+ Log._Debug($"CustomPathFind.PathFindImplementation: Preparing calculating path unit {unit}, type {queueItem.vehicleType}:\n" +
+ $"\tbufferItemStartA: segment={bufferItemStartA.m_position.m_segment} lane={bufferItemStartA.m_position.m_lane} off={bufferItemStartA.m_position.m_offset} laneId={bufferItemStartA.m_laneID}\n" +
+ $"\tbufferItemStartB: segment={bufferItemStartB.m_position.m_segment} lane={bufferItemStartB.m_position.m_lane} off={bufferItemStartB.m_position.m_offset} laneId={bufferItemStartB.m_laneID}\n" +
+ $"\tbufferItemEndA: segment={bufferItemEndA.m_position.m_segment} lane={bufferItemEndA.m_position.m_lane} off={bufferItemEndA.m_position.m_offset} laneId={bufferItemEndA.m_laneID}\n" +
+ $"\tbufferItemEndB: segment={bufferItemEndB.m_position.m_segment} lane={bufferItemEndB.m_position.m_lane} off={bufferItemEndB.m_position.m_offset} laneId={bufferItemEndB.m_laneID}\n" +
+ $"\tvehicleItem: segment={data.m_position11.m_segment} lane={data.m_position11.m_lane} off={data.m_position11.m_offset} laneId={m_vehicleLane} vehiclePosIndicator={vehiclePosIndicator}\n"
+ );
+ }
+#endif
+ BufferItem finalBufferItem = default(BufferItem);
+ byte startOffset = 0;
+ this.m_bufferMinPos = 0;
+ this.m_bufferMaxPos = -1;
+ if (this.m_pathFindIndex == 0u) {
+ uint maxUInt = 4294901760u;
+ for (int i = 0; i < 262144; ++i) {
+ this.m_laneLocation[i] = maxUInt;
+ }
+ }
+ for (int j = 0; j < 1024; ++j) {
+ this.m_bufferMin[j] = 0;
+ this.m_bufferMax[j] = -1;
+ }
+ if (bufferItemEndA.m_position.m_segment != 0) {
+ ++this.m_bufferMax[0];
+ this.m_buffer[++this.m_bufferMaxPos] = bufferItemEndA;
+ }
+ if (bufferItemEndB.m_position.m_segment != 0) {
+ ++this.m_bufferMax[0];
+ this.m_buffer[++this.m_bufferMaxPos] = bufferItemEndB;
+ }
+ bool canFindPath = false;
+
+ while (this.m_bufferMinPos <= this.m_bufferMaxPos) {
+ int bufMin = this.m_bufferMin[this.m_bufferMinPos];
+ int bufMax = this.m_bufferMax[this.m_bufferMinPos];
+ if (bufMin > bufMax) {
+ ++this.m_bufferMinPos;
+ } else {
+ this.m_bufferMin[this.m_bufferMinPos] = bufMin + 1;
+ BufferItem candidateItem = this.m_buffer[(this.m_bufferMinPos << 6) + bufMin];
+ if (candidateItem.m_position.m_segment == bufferItemStartA.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartA.m_position.m_lane) {
+ // we reached startA
+ if ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0 && candidateItem.m_position.m_offset >= this.m_startOffsetA) {
+ finalBufferItem = candidateItem;
+ startOffset = this.m_startOffsetA;
+ canFindPath = true;
+ break;
+ }
+ if ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0 && candidateItem.m_position.m_offset <= this.m_startOffsetA) {
+ finalBufferItem = candidateItem;
+ startOffset = this.m_startOffsetA;
+ canFindPath = true;
+ break;
+ }
+ }
+ if (candidateItem.m_position.m_segment == bufferItemStartB.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartB.m_position.m_lane) {
+ // we reached startB
+ if ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0 && candidateItem.m_position.m_offset >= this.m_startOffsetB) {
+ finalBufferItem = candidateItem;
+ startOffset = this.m_startOffsetB;
+ canFindPath = true;
+ break;
+ }
+ if ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0 && candidateItem.m_position.m_offset <= this.m_startOffsetB) {
+ finalBufferItem = candidateItem;
+ startOffset = this.m_startOffsetB;
+ canFindPath = true;
+ break;
+ }
+ }
+
+ // explore the path
+ if ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0) {
+ ushort startNode = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode;
+ uint laneRoutingIndex = routingManager.GetLaneEndRoutingIndex(candidateItem.m_laneID, true);
+ this.ProcessItemMain(unit, candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], routingManager.SegmentRoutings[candidateItem.m_position.m_segment], routingManager.LaneEndBackwardRoutings[laneRoutingIndex], startNode, true, ref netManager.m_nodes.m_buffer[startNode], 0, false);
+ }
+
+ if ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0) {
+ ushort endNode = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode;
+ uint laneRoutingIndex = routingManager.GetLaneEndRoutingIndex(candidateItem.m_laneID, false);
+ this.ProcessItemMain(unit, candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], routingManager.SegmentRoutings[candidateItem.m_position.m_segment], routingManager.LaneEndBackwardRoutings[laneRoutingIndex], endNode, false, ref netManager.m_nodes.m_buffer[endNode], 255, false);
+ }
+
+ // handle special nodes (e.g. bus stops)
+ int num6 = 0;
+ ushort specialNodeId = netManager.m_lanes.m_buffer[candidateItem.m_laneID].m_nodes;
+ if (specialNodeId != 0) {
+ ushort startNode2 = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode;
+ ushort endNode2 = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode;
+ bool nodesDisabled = ((netManager.m_nodes.m_buffer[startNode2].m_flags | netManager.m_nodes.m_buffer[endNode2].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None;
+ while (specialNodeId != 0) {
+ NetInfo.Direction direction = NetInfo.Direction.None;
+ byte laneOffset = netManager.m_nodes.m_buffer[specialNodeId].m_laneOffset;
+ if (laneOffset <= candidateItem.m_position.m_offset) {
+ direction |= NetInfo.Direction.Forward;
+ }
+ if (laneOffset >= candidateItem.m_position.m_offset) {
+ direction |= NetInfo.Direction.Backward;
+ }
+ if ((byte)(candidateItem.m_direction & direction) != 0 && (!nodesDisabled || (netManager.m_nodes.m_buffer[specialNodeId].m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None)) {
#if DEBUGNEWPF && DEBUG
- if (debug && (m_conf.Debug.NodeId <= 0 || specialNodeId == m_conf.Debug.NodeId)) {
- Log._Debug($"CustomPathFind.PathFindImplementation: Handling special node for path unit {unit}, type {queueItem.vehicleType}:\n" +
- $"\tcandidateItem.m_position.m_segment={candidateItem.m_position.m_segment}\n" +
- $"\tcandidateItem.m_position.m_lane={candidateItem.m_position.m_lane}\n" +
- $"\tcandidateItem.m_laneID={candidateItem.m_laneID}\n" +
- $"\tspecialNodeId={specialNodeId}\n" +
- $"\tstartNode2={startNode2}\n" +
- $"\tendNode2={endNode2}\n"
- );
- }
-#endif
- this.ProcessItemMain(unit, candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], routingManager.SegmentRoutings[candidateItem.m_position.m_segment], routingManager.LaneEndBackwardRoutings[0], specialNodeId, false, ref netManager.m_nodes.m_buffer[specialNodeId], laneOffset, true);
- }
- specialNodeId = netManager.m_nodes.m_buffer[specialNodeId].m_nextLaneNode;
- if (++num6 >= NetManager.MAX_NODE_COUNT) {
- Log.Warning("Special loop: Too many iterations");
- break;
- }
- }
- }
- }
- }
-
- if (!canFindPath) {
- // we could not find a path
- PathUnits.m_buffer[(int)unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;
+ if (debug && (m_conf.Debug.NodeId <= 0 || specialNodeId == m_conf.Debug.NodeId)) {
+ Log._Debug($"CustomPathFind.PathFindImplementation: Handling special node for path unit {unit}, type {queueItem.vehicleType}:\n" +
+ $"\tcandidateItem.m_position.m_segment={candidateItem.m_position.m_segment}\n" +
+ $"\tcandidateItem.m_position.m_lane={candidateItem.m_position.m_lane}\n" +
+ $"\tcandidateItem.m_laneID={candidateItem.m_laneID}\n" +
+ $"\tspecialNodeId={specialNodeId}\n" +
+ $"\tstartNode2={startNode2}\n" +
+ $"\tendNode2={endNode2}\n"
+ );
+ }
+#endif
+ this.ProcessItemMain(unit, candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], routingManager.SegmentRoutings[candidateItem.m_position.m_segment], routingManager.LaneEndBackwardRoutings[0], specialNodeId, false, ref netManager.m_nodes.m_buffer[specialNodeId], laneOffset, true);
+ }
+ specialNodeId = netManager.m_nodes.m_buffer[specialNodeId].m_nextLaneNode;
+ if (++num6 >= NetManager.MAX_NODE_COUNT) {
+ Log.Warning("Special loop: Too many iterations");
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (!canFindPath) {
+ // we could not find a path
+ PathUnits.m_buffer[(int)unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;
#if DEBUG
- ++m_failedPathFinds;
-
-#if DEBUGNEWPF
- if (debug) {
- Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Could not find path for unit {unit} -- path-finding failed during process");
- string reachableBuf = "";
- string unreachableBuf = "";
- foreach (KeyValuePair> e in m_debugPositions) {
- string buf = $"{e.Key} -> {e.Value.CollectionToString()}\n";
- if (e.Value.Count <= 0) {
- unreachableBuf += buf;
- } else {
- reachableBuf += buf;
- }
- }
- Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Reachability graph for unit {unit}:\n== REACHABLE ==\n" + reachableBuf + "\n== UNREACHABLE ==\n" + unreachableBuf);
- }
-#endif
-#endif
- //CustomPathManager._instance.ResetQueueItem(unit);
-
- return;
- }
- // we could calculate a valid path
-
- float duration = (this.m_laneTypes != NetInfo.LaneType.Pedestrian) ? finalBufferItem.m_duration : finalBufferItem.m_methodDistance;
- this.PathUnits.m_buffer[unit].m_length = duration;
- this.PathUnits.m_buffer[unit].m_laneTypes = (byte)finalBufferItem.m_lanesUsed; // NON-STOCK CODE
- this.PathUnits.m_buffer[unit].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed; // NON-STOCK CODE
+ ++m_failedPathFinds;
+
+#if DEBUGNEWPF
+ if (debug) {
+ Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Could not find path for unit {unit} -- path-finding failed during process");
+ string reachableBuf = "";
+ string unreachableBuf = "";
+ foreach (KeyValuePair> e in m_debugPositions) {
+ string buf = $"{e.Key} -> {e.Value.CollectionToString()}\n";
+ if (e.Value.Count <= 0) {
+ unreachableBuf += buf;
+ } else {
+ reachableBuf += buf;
+ }
+ }
+ Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Reachability graph for unit {unit}:\n== REACHABLE ==\n" + reachableBuf + "\n== UNREACHABLE ==\n" + unreachableBuf);
+ }
+#endif
+#endif
+ //CustomPathManager._instance.ResetQueueItem(unit);
+
+ return;
+ }
+ // we could calculate a valid path
+
+ float duration = (this.m_laneTypes != NetInfo.LaneType.Pedestrian) ? finalBufferItem.m_duration : finalBufferItem.m_methodDistance;
+ this.PathUnits.m_buffer[unit].m_length = duration;
+ this.PathUnits.m_buffer[unit].m_laneTypes = (byte)finalBufferItem.m_lanesUsed; // NON-STOCK CODE
+ this.PathUnits.m_buffer[unit].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed; // NON-STOCK CODE
#if DEBUG
- /*if (_conf.Debug.Switches[4])
- Log._Debug($"Lane/Vehicle types of path unit {unit}: {finalBufferItem.m_lanesUsed} / {finalBufferItem.m_vehiclesUsed}");*/
-#endif
- uint currentPathUnitId = unit;
- int currentItemPositionCount = 0;
- int sumOfPositionCounts = 0;
- PathUnit.Position currentPosition = finalBufferItem.m_position;
- if ((currentPosition.m_segment != bufferItemEndA.m_position.m_segment || currentPosition.m_lane != bufferItemEndA.m_position.m_lane || currentPosition.m_offset != bufferItemEndA.m_position.m_offset) &&
- (currentPosition.m_segment != bufferItemEndB.m_position.m_segment || currentPosition.m_lane != bufferItemEndB.m_position.m_lane || currentPosition.m_offset != bufferItemEndB.m_position.m_offset)) {
- // the found starting position differs from the desired end position
- if (startOffset != currentPosition.m_offset) {
- // the offsets differ: copy the found starting position and modify the offset to fit the desired offset
- PathUnit.Position position2 = currentPosition;
- position2.m_offset = startOffset;
- this.PathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, position2);
- // now we have: [desired starting position]
- }
- // add the found starting position to the path unit
- this.PathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition);
- currentPosition = this.m_laneTarget[finalBufferItem.m_laneID]; // go to the next path position
-
- // now we have either [desired starting position, found starting position] or [found starting position], depending on if the found starting position matched the desired
- }
-
- // beginning with the starting position, going to the target position: assemble the path units
- for (int k = 0; k < 262144; ++k) {
- //pfCurrentState = 6;
- this.PathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition); // add the next path position to the current unit
-
- if ((currentPosition.m_segment == bufferItemEndA.m_position.m_segment && currentPosition.m_lane == bufferItemEndA.m_position.m_lane && currentPosition.m_offset == bufferItemEndA.m_position.m_offset) ||
- (currentPosition.m_segment == bufferItemEndB.m_position.m_segment && currentPosition.m_lane == bufferItemEndB.m_position.m_lane && currentPosition.m_offset == bufferItemEndB.m_position.m_offset)) {
- // we have reached the end position
-
- this.PathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;
- sumOfPositionCounts += currentItemPositionCount; // add position count of last unit to sum
- if (sumOfPositionCounts != 0) {
- // for each path unit from start to target: calculate length (distance) to target
- currentPathUnitId = this.PathUnits.m_buffer[unit].m_nextPathUnit; // (we do not need to calculate the length for the starting unit since this is done before; it's the total path length)
- currentItemPositionCount = (int)this.PathUnits.m_buffer[unit].m_positionCount;
- int totalIter = 0;
- while (currentPathUnitId != 0u) {
- this.PathUnits.m_buffer[currentPathUnitId].m_length = duration * (float)(sumOfPositionCounts - currentItemPositionCount) / (float)sumOfPositionCounts;
- currentItemPositionCount += (int)this.PathUnits.m_buffer[currentPathUnitId].m_positionCount;
- currentPathUnitId = this.PathUnits.m_buffer[currentPathUnitId].m_nextPathUnit;
- if (++totalIter >= 262144) {
+ /*if (_conf.Debug.Switches[4])
+ Log._Debug($"Lane/Vehicle types of path unit {unit}: {finalBufferItem.m_lanesUsed} / {finalBufferItem.m_vehiclesUsed}");*/
+#endif
+ uint currentPathUnitId = unit;
+ int currentItemPositionCount = 0;
+ int sumOfPositionCounts = 0;
+ PathUnit.Position currentPosition = finalBufferItem.m_position;
+ if ((currentPosition.m_segment != bufferItemEndA.m_position.m_segment || currentPosition.m_lane != bufferItemEndA.m_position.m_lane || currentPosition.m_offset != bufferItemEndA.m_position.m_offset) &&
+ (currentPosition.m_segment != bufferItemEndB.m_position.m_segment || currentPosition.m_lane != bufferItemEndB.m_position.m_lane || currentPosition.m_offset != bufferItemEndB.m_position.m_offset)) {
+ // the found starting position differs from the desired end position
+ if (startOffset != currentPosition.m_offset) {
+ // the offsets differ: copy the found starting position and modify the offset to fit the desired offset
+ PathUnit.Position position2 = currentPosition;
+ position2.m_offset = startOffset;
+ this.PathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, position2);
+ // now we have: [desired starting position]
+ }
+ // add the found starting position to the path unit
+ this.PathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition);
+ currentPosition = this.m_laneTarget[finalBufferItem.m_laneID]; // go to the next path position
+
+ // now we have either [desired starting position, found starting position] or [found starting position], depending on if the found starting position matched the desired
+ }
+
+ // beginning with the starting position, going to the target position: assemble the path units
+ for (int k = 0; k < 262144; ++k) {
+ //pfCurrentState = 6;
+ this.PathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition); // add the next path position to the current unit
+
+ if ((currentPosition.m_segment == bufferItemEndA.m_position.m_segment && currentPosition.m_lane == bufferItemEndA.m_position.m_lane && currentPosition.m_offset == bufferItemEndA.m_position.m_offset) ||
+ (currentPosition.m_segment == bufferItemEndB.m_position.m_segment && currentPosition.m_lane == bufferItemEndB.m_position.m_lane && currentPosition.m_offset == bufferItemEndB.m_position.m_offset)) {
+ // we have reached the end position
+
+ this.PathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;
+ sumOfPositionCounts += currentItemPositionCount; // add position count of last unit to sum
+ if (sumOfPositionCounts != 0) {
+ // for each path unit from start to target: calculate length (distance) to target
+ currentPathUnitId = this.PathUnits.m_buffer[unit].m_nextPathUnit; // (we do not need to calculate the length for the starting unit since this is done before; it's the total path length)
+ currentItemPositionCount = (int)this.PathUnits.m_buffer[unit].m_positionCount;
+ int totalIter = 0;
+ while (currentPathUnitId != 0u) {
+ this.PathUnits.m_buffer[currentPathUnitId].m_length = duration * (float)(sumOfPositionCounts - currentItemPositionCount) / (float)sumOfPositionCounts;
+ currentItemPositionCount += (int)this.PathUnits.m_buffer[currentPathUnitId].m_positionCount;
+ currentPathUnitId = this.PathUnits.m_buffer[currentPathUnitId].m_nextPathUnit;
+ if (++totalIter >= 262144) {
#if DEBUG
- Log.Error("THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: PathFindImplementation: Invalid list detected.");
+ Log.Error("THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: PathFindImplementation: Invalid list detected.");
#endif
- CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
- break;
- }
- }
- }
+ CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
+ break;
+ }
+ }
+ }
#if DEBUG
- //Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Path found (pfCurrentState={pfCurrentState}) for unit {unit}");
+ //Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Path found (pfCurrentState={pfCurrentState}) for unit {unit}");
#endif
- PathUnits.m_buffer[(int)unit].m_pathFindFlags |= PathUnit.FLAG_READY; // Path found
+ PathUnits.m_buffer[(int)unit].m_pathFindFlags |= PathUnit.FLAG_READY; // Path found
#if DEBUG
- ++m_succeededPathFinds;
+ ++m_succeededPathFinds;
#if DEBUGNEWPF
- if (debug)
- Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Path-find succeeded for unit {unit}");
+ if (debug)
+ Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Path-find succeeded for unit {unit}");
#endif
#endif
- //CustomPathManager._instance.ResetQueueItem(unit);
+ //CustomPathManager._instance.ResetQueueItem(unit);
- return;
- }
+ return;
+ }
- // We have not reached the target position yet
- if (currentItemPositionCount == 12) {
- // the current path unit is full, we need a new one
- uint createdPathUnitId;
- try {
- Monitor.Enter(_bufferLock);
- if (!this.PathUnits.CreateItem(out createdPathUnitId, ref this.m_pathRandomizer)) {
- // we failed to create a new path unit, thus the path-finding also failed
- PathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;
+ // We have not reached the target position yet
+ if (currentItemPositionCount == 12) {
+ // the current path unit is full, we need a new one
+ uint createdPathUnitId;
+ try {
+ Monitor.Enter(_bufferLock);
+ if (!this.PathUnits.CreateItem(out createdPathUnitId, ref this.m_pathRandomizer)) {
+ // we failed to create a new path unit, thus the path-finding also failed
+ PathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;
#if DEBUG
- ++m_failedPathFinds;
-
-#if DEBUGNEWPF
- if (debug)
- Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Could not find path for unit {unit} -- Could not create path unit");
-#endif
-#endif
- //CustomPathManager._instance.ResetQueueItem(unit);
- return;
- }
- this.PathUnits.m_buffer[createdPathUnitId] = this.PathUnits.m_buffer[(int)currentPathUnitId];
- this.PathUnits.m_buffer[createdPathUnitId].m_referenceCount = 1;
- this.PathUnits.m_buffer[createdPathUnitId].m_pathFindFlags = PathUnit.FLAG_READY;
- this.PathUnits.m_buffer[currentPathUnitId].m_nextPathUnit = createdPathUnitId;
- this.PathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;
- this.PathUnits.m_buffer[currentPathUnitId].m_laneTypes = (byte)finalBufferItem.m_lanesUsed; // NON-STOCK CODE (this is not accurate!)
- this.PathUnits.m_buffer[currentPathUnitId].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed; // NON-STOCK CODE (this is not accurate!)
- sumOfPositionCounts += currentItemPositionCount;
- Singleton.instance.m_pathUnitCount = (int)(this.PathUnits.ItemCount() - 1u);
- } catch (Exception e) {
- Log.Error($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindImplementation Error: {e.ToString()}");
- break;
- } finally {
- Monitor.Exit(this._bufferLock);
- }
- currentPathUnitId = createdPathUnitId;
- currentItemPositionCount = 0;
- }
-
- uint laneID = PathManager.GetLaneID(currentPosition);
+ ++m_failedPathFinds;
+
+#if DEBUGNEWPF
+ if (debug)
+ Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Could not find path for unit {unit} -- Could not create path unit");
+#endif
+#endif
+ //CustomPathManager._instance.ResetQueueItem(unit);
+ return;
+ }
+ this.PathUnits.m_buffer[createdPathUnitId] = this.PathUnits.m_buffer[(int)currentPathUnitId];
+ this.PathUnits.m_buffer[createdPathUnitId].m_referenceCount = 1;
+ this.PathUnits.m_buffer[createdPathUnitId].m_pathFindFlags = PathUnit.FLAG_READY;
+ this.PathUnits.m_buffer[currentPathUnitId].m_nextPathUnit = createdPathUnitId;
+ this.PathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;
+ this.PathUnits.m_buffer[currentPathUnitId].m_laneTypes = (byte)finalBufferItem.m_lanesUsed; // NON-STOCK CODE (this is not accurate!)
+ this.PathUnits.m_buffer[currentPathUnitId].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed; // NON-STOCK CODE (this is not accurate!)
+ sumOfPositionCounts += currentItemPositionCount;
+ Singleton.instance.m_pathUnitCount = (int)(this.PathUnits.ItemCount() - 1u);
+ } catch (Exception e) {
+ Log.Error($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindImplementation Error: {e.ToString()}");
+ break;
+ } finally {
+ Monitor.Exit(this._bufferLock);
+ }
+ currentPathUnitId = createdPathUnitId;
+ currentItemPositionCount = 0;
+ }
+
+ uint laneID = PathManager.GetLaneID(currentPosition);
#if PFTRAFFICSTATS
// NON-STOCK CODE START
#if MEASUREDENSITY
@@ -682,971 +687,971 @@ protected void PathFindImplementation(uint unit, ref PathUnit data) {
}
// NON-STOCK CODE END
#endif
- currentPosition = this.m_laneTarget[laneID];
- }
- PathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;
+ currentPosition = this.m_laneTarget[laneID];
+ }
+ PathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;
#if DEBUG
- ++m_failedPathFinds;
+ ++m_failedPathFinds;
#if DEBUGNEWPF
- if (debug)
- Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Could not find path for unit {unit} -- internal error: for loop break");
+ if (debug)
+ Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Could not find path for unit {unit} -- internal error: for loop break");
#endif
#endif
- //CustomPathManager._instance.ResetQueueItem(unit);
+ //CustomPathManager._instance.ResetQueueItem(unit);
#if DEBUG
- //Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Cannot find path (pfCurrentState={pfCurrentState}) for unit {unit}");
+ //Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Cannot find path (pfCurrentState={pfCurrentState}) for unit {unit}");
#endif
- }
+ }
- // be aware:
- // (1) path-finding works from target to start. the "next" segment is always the previous and the "previous" segment is always the next segment on the path!
- // (2) when I use the term "lane index from outer" this means outer right lane for right-hand traffic systems and outer-left lane for left-hand traffic systems.
+ // be aware:
+ // (1) path-finding works from target to start. the "next" segment is always the previous and the "previous" segment is always the next segment on the path!
+ // (2) when I use the term "lane index from outer" this means outer right lane for right-hand traffic systems and outer-left lane for left-hand traffic systems.
- // 1
- private void ProcessItemMain(uint unitId, BufferItem item, ref NetSegment prevSegment, SegmentRoutingData prevSegmentRouting, LaneEndRoutingData prevLaneEndRouting, ushort nextNodeId, bool nextIsStartNode, ref NetNode nextNode, byte connectOffset, bool isMiddle) {
+ // 1
+ private void ProcessItemMain(uint unitId, BufferItem item, ref NetSegment prevSegment, SegmentRoutingData prevSegmentRouting, LaneEndRoutingData prevLaneEndRouting, ushort nextNodeId, bool nextIsStartNode, ref NetNode nextNode, byte connectOffset, bool isMiddle) {
#if DEBUGNEWPF && DEBUG
- bool debug = this.m_debug && (m_conf.Debug.NodeId <= 0 || nextNodeId == m_conf.Debug.NodeId);
- bool debugPed = debug && m_conf.Debug.Switches[12];
- if (debug) {
- if (! m_debugPositions.ContainsKey(item.m_position.m_segment)) {
- m_debugPositions[item.m_position.m_segment] = new List();
- }
- }
+ bool debug = this.m_debug && (m_conf.Debug.NodeId <= 0 || nextNodeId == m_conf.Debug.NodeId);
+ bool debugPed = debug && m_conf.Debug.Switches[12];
+ if (debug) {
+ if (! m_debugPositions.ContainsKey(item.m_position.m_segment)) {
+ m_debugPositions[item.m_position.m_segment] = new List();
+ }
+ }
#else
bool debug = false;
bool debugPed = false;
#endif
- //Log.Message($"THREAD #{Thread.CurrentThread.ManagedThreadId} Path finder: " + this._pathFindIndex + " vehicle types: " + this._vehicleTypes);
+ //Log.Message($"THREAD #{Thread.CurrentThread.ManagedThreadId} Path finder: " + this._pathFindIndex + " vehicle types: " + this._vehicleTypes);
#if DEBUGNEWPF && DEBUG
- //bool debug = isTransportVehicle && isMiddle && item.m_position.m_segment == 13550;
- List logBuf = null;
- if (debug)
- logBuf = new List();
-#endif
-
- NetManager netManager = Singleton.instance;
- bool prevIsPedestrianLane = false;
- //bool prevIsBusLane = false; // non-stock
- bool prevIsBicycleLane = false;
- bool prevIsCenterPlatform = false;
- bool prevIsElevated = false;
- bool prevIsCarLane = false;
- int prevRelSimilarLaneIndex = 0; // inner/outer similar index
- //int prevInnerSimilarLaneIndex = 0; // similar index, starting with 0 at leftmost lane in right hand traffic
- int prevOuterSimilarLaneIndex = 0; // similar index, starting with 0 at rightmost lane in right hand traffic
- NetInfo prevSegmentInfo = prevSegment.Info;
- NetInfo.Lane prevLaneInfo = null;
- byte prevSimilarLaneCount = 0;
- if ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
- prevLaneInfo = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];
- prevIsPedestrianLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian);
- prevIsBicycleLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Vehicle && (prevLaneInfo.m_vehicleType & this.m_vehicleTypes) == VehicleInfo.VehicleType.Bicycle);
- prevIsCarLane = (prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None && (prevLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None;
- //prevIsBusLane = (prevLane.m_laneType == NetInfo.LaneType.TransportVehicle && (prevLane.m_vehicleType & this._vehicleTypes & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None);
- prevIsCenterPlatform = prevLaneInfo.m_centerPlatform;
- prevIsElevated = prevLaneInfo.m_elevated;
- prevSimilarLaneCount = (byte)prevLaneInfo.m_similarLaneCount;
- //prevInnerSimilarLaneIndex = RoutingManager.Instance.CalcInnerSimilarLaneIndex(prevLaneInfo);
- prevOuterSimilarLaneIndex = RoutingManager.Instance.CalcOuterSimilarLaneIndex(prevLaneInfo);
- if ((byte)(prevLaneInfo.m_finalDirection & NetInfo.Direction.Forward) != 0) {
- prevRelSimilarLaneIndex = prevLaneInfo.m_similarLaneIndex;
- } else {
- prevRelSimilarLaneIndex = prevLaneInfo.m_similarLaneCount - prevLaneInfo.m_similarLaneIndex - 1;
- }
- }
- int firstPrevSimilarLaneIndexFromInner = prevRelSimilarLaneIndex;
- ushort prevSegmentId = item.m_position.m_segment;
- if (isMiddle) {
- for (int i = 0; i < 8; ++i) {
- ushort nextSegmentId = nextNode.GetSegment(i);
- if (nextSegmentId <= 0)
- continue;
-
-#if DEBUGNEWPF
- if (debug) {
- FlushMainLog(logBuf, unitId);
- }
-#endif
-
- this.ProcessItemCosts(debug, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[(int)nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, !prevIsPedestrianLane, prevIsPedestrianLane, isMiddle);
- }
- } else if (prevIsPedestrianLane) {
- bool allowPedSwitch = (this.m_laneTypes & NetInfo.LaneType.Pedestrian) != 0;
- if (!prevIsElevated) {
- // explore pedestrian lanes
- int prevLaneIndex = (int)item.m_position.m_lane;
- if (nextNode.Info.m_class.m_service != ItemClass.Service.Beautification) {
- if (allowPedSwitch) { // NON-STOCK CODE
- bool canCrossStreet = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;
- bool isOnCenterPlatform = prevIsCenterPlatform && (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Junction)) == NetNode.Flags.None;
- ushort nextLeftSegment = prevSegmentId;
- ushort nextRightSegment = prevSegmentId;
- int leftLaneIndex;
- int rightLaneIndex;
- uint leftLaneId;
- uint rightLaneId;
- prevSegment.GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, prevLaneIndex, isOnCenterPlatform, out leftLaneIndex, out rightLaneIndex, out leftLaneId, out rightLaneId);
- if (leftLaneId == 0u || rightLaneId == 0u) {
- ushort leftSegment;
- ushort rightSegment;
- prevSegment.GetLeftAndRightSegments(nextNodeId, out leftSegment, out rightSegment);
- int numIter = 0;
- while (leftSegment != 0 && leftSegment != prevSegmentId && leftLaneId == 0u) {
- int someLeftLaneIndex;
- int someRightLaneIndex;
- uint someLeftLaneId;
- uint someRightLaneId;
- netManager.m_segments.m_buffer[(int)leftSegment].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);
- if (someRightLaneId != 0u) {
- nextLeftSegment = leftSegment;
- leftLaneIndex = someRightLaneIndex;
- leftLaneId = someRightLaneId;
- } else {
- leftSegment = netManager.m_segments.m_buffer[(int)leftSegment].GetLeftSegment(nextNodeId);
- }
- if (++numIter == 8) {
- break;
- }
- }
- numIter = 0;
- while (rightSegment != 0 && rightSegment != prevSegmentId && rightLaneId == 0u) {
- int someLeftLaneIndex;
- int someRightLaneIndex;
- uint someLeftLaneId;
- uint someRightLaneId;
- netManager.m_segments.m_buffer[(int)rightSegment].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);
- if (someLeftLaneId != 0u) {
- nextRightSegment = rightSegment;
- rightLaneIndex = someLeftLaneIndex;
- rightLaneId = someLeftLaneId;
- } else {
- rightSegment = netManager.m_segments.m_buffer[(int)rightSegment].GetRightSegment(nextNodeId);
- }
- if (++numIter == 8) {
- break;
- }
- }
- }
- if (leftLaneId != 0u && (nextLeftSegment != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {
-#if DEBUGNEWPF
- if (debugPed) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring left segment\n" +
- "\t" + $"_extPathType={queueItem.pathType}\n" +
- "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
- "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
- "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
- "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
- "\t" + $"_stablePath={m_stablePath}\n" +
- "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
- "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
- "\t" + $"nextLeftSegment={nextLeftSegment}\n" +
- "\t" + $"leftLaneId={leftLaneId}\n" +
- "\t" + $"mayCrossStreet={canCrossStreet}\n" +
- "\t" + $"isOnCenterPlatform={isOnCenterPlatform}\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
- this.ProcessItemPedBicycle(debugPed, item, nextNodeId, nextLeftSegment, ref prevSegment, ref netManager.m_segments.m_buffer[(int)nextLeftSegment], connectOffset, connectOffset, leftLaneIndex, leftLaneId); // ped
- }
- if (rightLaneId != 0u && rightLaneId != leftLaneId && (nextRightSegment != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {
-#if DEBUGNEWPF
- if (debugPed) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring right segment\n" +
- "\t" + $"_extPathType={queueItem.pathType}\n" +
- "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
- "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
- "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
- "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
- "\t" + $"_stablePath={m_stablePath}\n" +
- "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
- "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
- "\t" + $"nextRightSegment={nextRightSegment}\n" +
- "\t" + $"rightLaneId={rightLaneId}\n" +
- "\t" + $"mayCrossStreet={canCrossStreet}\n" +
- "\t" + $"isOnCenterPlatform={isOnCenterPlatform}\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
- this.ProcessItemPedBicycle(debugPed, item, nextNodeId, nextRightSegment, ref prevSegment, ref netManager.m_segments.m_buffer[(int)nextRightSegment], connectOffset, connectOffset, rightLaneIndex, rightLaneId); // ped
- }
- }
-
- // switch from bicycle lane to pedestrian lane
- int nextLaneIndex;
- uint nextLaneId;
- if ((this.m_vehicleTypes & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None &&
- prevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Bicycle, out nextLaneIndex, out nextLaneId)) {
-#if DEBUGNEWPF
- if (debugPed) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring bicycle switch\n" +
- "\t" + $"_extPathType={queueItem.pathType}\n" +
- "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
- "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
- "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
- "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
- "\t" + $"_stablePath={m_stablePath}\n" +
- "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
- "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
- "\t" + $"nextLaneIndex={nextLaneIndex}\n" +
- "\t" + $"nextLaneId={nextLaneId}\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
- this.ProcessItemPedBicycle(debugPed, item, nextNodeId, prevSegmentId, ref prevSegment, ref prevSegment, connectOffset, connectOffset, nextLaneIndex, nextLaneId); // bicycle
- }
- } else {
- // we are going from pedestrian lane to a beautification node
-
- for (int j = 0; j < 8; ++j) {
- ushort nextSegmentId = nextNode.GetSegment(j);
- if (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {
-#if DEBUGNEWPF
- if (debug) {
- FlushMainLog(logBuf, unitId);
- }
-#endif
-
- this.ProcessItemCosts(debug, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[(int)nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, false, true, isMiddle);
- }
- }
- }
-
- // NON-STOCK CODE START
- // switch from vehicle to pedestrian lane (parking)
- bool parkingAllowed = true;
- if (Options.parkingAI) {
- if (queueItem.vehicleType == ExtVehicleType.PassengerCar) {
- if ((item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
- // if pocket cars are prohibited, a citizen may only park their car once per path
- parkingAllowed = false;
- } else if ((item.m_lanesUsed & NetInfo.LaneType.PublicTransport) == NetInfo.LaneType.None) {
- // if the citizen is walking to their target (= no public transport used), the passenger car must be parked in the very last moment
- parkingAllowed = item.m_laneID == m_endLaneA || item.m_laneID == m_endLaneB;
- /*if (_conf.Debug.Switches[4]) {
- Log._Debug($"Path unit {unitId}: public transport has not been used. ");
- }*/
- }
- }
- }
-
- if (parkingAllowed) {
- // NON-STOCK CODE END
- NetInfo.LaneType laneType = this.m_laneTypes & ~NetInfo.LaneType.Pedestrian;
- VehicleInfo.VehicleType vehicleType = this.m_vehicleTypes & ~VehicleInfo.VehicleType.Bicycle;
- if ((byte)(item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {
- laneType &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
- }
- int nextLaneIndex2;
- uint nextlaneId2;
- if (laneType != NetInfo.LaneType.None &&
- vehicleType != VehicleInfo.VehicleType.None &&
- prevSegment.GetClosestLane(prevLaneIndex, laneType, vehicleType, out nextLaneIndex2, out nextlaneId2)) {
- NetInfo.Lane lane5 = prevSegmentInfo.m_lanes[nextLaneIndex2];
- byte connectOffset2;
- if ((prevSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None == ((byte)(lane5.m_finalDirection & NetInfo.Direction.Backward) != 0)) {
- connectOffset2 = 1;
- } else {
- connectOffset2 = 254;
- }
-
- CustomPathFind.BufferItem item2 = item;
- if (this.m_randomParking) {
- item2.m_comparisonValue += (float)this.m_pathRandomizer.Int32(300u) / this.m_maxLength;
- }
-#if DEBUGNEWPF
- if (debugPed) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring parking switch\n" +
- "\t" + $"_extPathType={queueItem.pathType}\n" +
- "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
- "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
- "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
- "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
- "\t" + $"_stablePath={m_stablePath}\n" +
- "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
- "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
- "\t" + $"nextLaneIndex2={nextLaneIndex2}\n" +
- "\t" + $"nextlaneId2={nextlaneId2}\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
- this.ProcessItemPedBicycle(debugPed, item2, nextNodeId, prevSegmentId, ref prevSegment, ref prevSegment, connectOffset2, 128, nextLaneIndex2, nextlaneId2); // ped
- }
- }
- }
- } else {
- // we are going to a non-pedestrian lane
-
- bool allowPedestrian = (byte)(this.m_laneTypes & NetInfo.LaneType.Pedestrian) != 0; // allow pedestrian switching to vehicle?
- bool nextIsBeautificationNode = nextNode.Info.m_class.m_service == ItemClass.Service.Beautification;
- bool allowBicycle = false; // is true if cim may switch from a pedestrian lane to a bike lane
- byte parkingConnectOffset = 0;
- if (allowPedestrian) {
- if (prevIsBicycleLane) {
- // we are going to a bicycle lane
- parkingConnectOffset = connectOffset;
- allowBicycle = nextIsBeautificationNode;
- } else if (this.m_vehicleLane != 0u) {
- // there is a parked vehicle position
- if (this.m_vehicleLane != item.m_laneID) {
- // we have not reached the parked vehicle yet
- allowPedestrian = false;
- } else {
- // pedestrian switches to parked vehicle
- parkingConnectOffset = this.m_vehicleOffset;
- }
- } else if (this.m_stablePath) {
- // enter a bus
- parkingConnectOffset = 128;
- } else {
- // pocket car spawning
- if (Options.parkingAI &&
- queueItem.vehicleType == ExtVehicleType.PassengerCar &&
- (queueItem.pathType == ExtPathType.WalkingOnly || (queueItem.pathType == ExtPathType.DrivingOnly && item.m_position.m_segment != m_startSegmentA && item.m_position.m_segment != m_startSegmentB))) {
- allowPedestrian = false;
- } else {
- parkingConnectOffset = (byte)this.m_pathRandomizer.UInt32(1u, 254u);
- }
- }
- }
-
- if ((this.m_vehicleTypes & (VehicleInfo.VehicleType.Ferry /* | VehicleInfo.VehicleType.Monorail*/)) != VehicleInfo.VehicleType.None) {
- // monorail / ferry
-
- for (int k = 0; k < 8; k++) {
- ushort nextSegmentId = nextNode.GetSegment(k);
- if (nextSegmentId == 0 || nextSegmentId == prevSegmentId) {
- continue;
- }
-
- this.ProcessItemCosts(debug, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, allowBicycle, isMiddle);
- }
-
- if ((nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None /*&&
+ //bool debug = isTransportVehicle && isMiddle && item.m_position.m_segment == 13550;
+ List logBuf = null;
+ if (debug)
+ logBuf = new List();
+#endif
+
+ NetManager netManager = Singleton.instance;
+ bool prevIsPedestrianLane = false;
+ //bool prevIsBusLane = false; // non-stock
+ bool prevIsBicycleLane = false;
+ bool prevIsCenterPlatform = false;
+ bool prevIsElevated = false;
+ bool prevIsCarLane = false;
+ int prevRelSimilarLaneIndex = 0; // inner/outer similar index
+ //int prevInnerSimilarLaneIndex = 0; // similar index, starting with 0 at leftmost lane in right hand traffic
+ int prevOuterSimilarLaneIndex = 0; // similar index, starting with 0 at rightmost lane in right hand traffic
+ NetInfo prevSegmentInfo = prevSegment.Info;
+ NetInfo.Lane prevLaneInfo = null;
+ byte prevSimilarLaneCount = 0;
+ if ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
+ prevLaneInfo = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];
+ prevIsPedestrianLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian);
+ prevIsBicycleLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Vehicle && (prevLaneInfo.m_vehicleType & this.m_vehicleTypes) == VehicleInfo.VehicleType.Bicycle);
+ prevIsCarLane = (prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None && (prevLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None;
+ //prevIsBusLane = (prevLane.m_laneType == NetInfo.LaneType.TransportVehicle && (prevLane.m_vehicleType & this._vehicleTypes & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None);
+ prevIsCenterPlatform = prevLaneInfo.m_centerPlatform;
+ prevIsElevated = prevLaneInfo.m_elevated;
+ prevSimilarLaneCount = (byte)prevLaneInfo.m_similarLaneCount;
+ //prevInnerSimilarLaneIndex = RoutingManager.Instance.CalcInnerSimilarLaneIndex(prevLaneInfo);
+ prevOuterSimilarLaneIndex = RoutingManager.Instance.CalcOuterSimilarLaneIndex(prevLaneInfo);
+ if ((byte)(prevLaneInfo.m_finalDirection & NetInfo.Direction.Forward) != 0) {
+ prevRelSimilarLaneIndex = prevLaneInfo.m_similarLaneIndex;
+ } else {
+ prevRelSimilarLaneIndex = prevLaneInfo.m_similarLaneCount - prevLaneInfo.m_similarLaneIndex - 1;
+ }
+ }
+ int firstPrevSimilarLaneIndexFromInner = prevRelSimilarLaneIndex;
+ ushort prevSegmentId = item.m_position.m_segment;
+ if (isMiddle) {
+ for (int i = 0; i < 8; ++i) {
+ ushort nextSegmentId = nextNode.GetSegment(i);
+ if (nextSegmentId <= 0)
+ continue;
+
+#if DEBUGNEWPF
+ if (debug) {
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+
+ this.ProcessItemCosts(debug, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[(int)nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, !prevIsPedestrianLane, prevIsPedestrianLane, isMiddle);
+ }
+ } else if (prevIsPedestrianLane) {
+ bool allowPedSwitch = (this.m_laneTypes & NetInfo.LaneType.Pedestrian) != 0;
+ if (!prevIsElevated) {
+ // explore pedestrian lanes
+ int prevLaneIndex = (int)item.m_position.m_lane;
+ if (nextNode.Info.m_class.m_service != ItemClass.Service.Beautification) {
+ if (allowPedSwitch) { // NON-STOCK CODE
+ bool canCrossStreet = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;
+ bool isOnCenterPlatform = prevIsCenterPlatform && (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Junction)) == NetNode.Flags.None;
+ ushort nextLeftSegment = prevSegmentId;
+ ushort nextRightSegment = prevSegmentId;
+ int leftLaneIndex;
+ int rightLaneIndex;
+ uint leftLaneId;
+ uint rightLaneId;
+ prevSegment.GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, prevLaneIndex, isOnCenterPlatform, out leftLaneIndex, out rightLaneIndex, out leftLaneId, out rightLaneId);
+ if (leftLaneId == 0u || rightLaneId == 0u) {
+ ushort leftSegment;
+ ushort rightSegment;
+ prevSegment.GetLeftAndRightSegments(nextNodeId, out leftSegment, out rightSegment);
+ int numIter = 0;
+ while (leftSegment != 0 && leftSegment != prevSegmentId && leftLaneId == 0u) {
+ int someLeftLaneIndex;
+ int someRightLaneIndex;
+ uint someLeftLaneId;
+ uint someRightLaneId;
+ netManager.m_segments.m_buffer[(int)leftSegment].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);
+ if (someRightLaneId != 0u) {
+ nextLeftSegment = leftSegment;
+ leftLaneIndex = someRightLaneIndex;
+ leftLaneId = someRightLaneId;
+ } else {
+ leftSegment = netManager.m_segments.m_buffer[(int)leftSegment].GetLeftSegment(nextNodeId);
+ }
+ if (++numIter == 8) {
+ break;
+ }
+ }
+ numIter = 0;
+ while (rightSegment != 0 && rightSegment != prevSegmentId && rightLaneId == 0u) {
+ int someLeftLaneIndex;
+ int someRightLaneIndex;
+ uint someLeftLaneId;
+ uint someRightLaneId;
+ netManager.m_segments.m_buffer[(int)rightSegment].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);
+ if (someLeftLaneId != 0u) {
+ nextRightSegment = rightSegment;
+ rightLaneIndex = someLeftLaneIndex;
+ rightLaneId = someLeftLaneId;
+ } else {
+ rightSegment = netManager.m_segments.m_buffer[(int)rightSegment].GetRightSegment(nextNodeId);
+ }
+ if (++numIter == 8) {
+ break;
+ }
+ }
+ }
+ if (leftLaneId != 0u && (nextLeftSegment != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {
+#if DEBUGNEWPF
+ if (debugPed) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring left segment\n" +
+ "\t" + $"_extPathType={queueItem.pathType}\n" +
+ "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
+ "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
+ "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
+ "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
+ "\t" + $"_stablePath={m_stablePath}\n" +
+ "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
+ "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
+ "\t" + $"nextLeftSegment={nextLeftSegment}\n" +
+ "\t" + $"leftLaneId={leftLaneId}\n" +
+ "\t" + $"mayCrossStreet={canCrossStreet}\n" +
+ "\t" + $"isOnCenterPlatform={isOnCenterPlatform}\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+ this.ProcessItemPedBicycle(debugPed, item, nextNodeId, nextLeftSegment, ref prevSegment, ref netManager.m_segments.m_buffer[(int)nextLeftSegment], connectOffset, connectOffset, leftLaneIndex, leftLaneId); // ped
+ }
+ if (rightLaneId != 0u && rightLaneId != leftLaneId && (nextRightSegment != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {
+#if DEBUGNEWPF
+ if (debugPed) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring right segment\n" +
+ "\t" + $"_extPathType={queueItem.pathType}\n" +
+ "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
+ "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
+ "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
+ "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
+ "\t" + $"_stablePath={m_stablePath}\n" +
+ "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
+ "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
+ "\t" + $"nextRightSegment={nextRightSegment}\n" +
+ "\t" + $"rightLaneId={rightLaneId}\n" +
+ "\t" + $"mayCrossStreet={canCrossStreet}\n" +
+ "\t" + $"isOnCenterPlatform={isOnCenterPlatform}\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+ this.ProcessItemPedBicycle(debugPed, item, nextNodeId, nextRightSegment, ref prevSegment, ref netManager.m_segments.m_buffer[(int)nextRightSegment], connectOffset, connectOffset, rightLaneIndex, rightLaneId); // ped
+ }
+ }
+
+ // switch from bicycle lane to pedestrian lane
+ int nextLaneIndex;
+ uint nextLaneId;
+ if ((this.m_vehicleTypes & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None &&
+ prevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Bicycle, out nextLaneIndex, out nextLaneId)) {
+#if DEBUGNEWPF
+ if (debugPed) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring bicycle switch\n" +
+ "\t" + $"_extPathType={queueItem.pathType}\n" +
+ "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
+ "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
+ "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
+ "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
+ "\t" + $"_stablePath={m_stablePath}\n" +
+ "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
+ "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
+ "\t" + $"nextLaneIndex={nextLaneIndex}\n" +
+ "\t" + $"nextLaneId={nextLaneId}\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+ this.ProcessItemPedBicycle(debugPed, item, nextNodeId, prevSegmentId, ref prevSegment, ref prevSegment, connectOffset, connectOffset, nextLaneIndex, nextLaneId); // bicycle
+ }
+ } else {
+ // we are going from pedestrian lane to a beautification node
+
+ for (int j = 0; j < 8; ++j) {
+ ushort nextSegmentId = nextNode.GetSegment(j);
+ if (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {
+#if DEBUGNEWPF
+ if (debug) {
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+
+ this.ProcessItemCosts(debug, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[(int)nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, false, true, isMiddle);
+ }
+ }
+ }
+
+ // NON-STOCK CODE START
+ // switch from vehicle to pedestrian lane (parking)
+ bool parkingAllowed = true;
+ if (Options.parkingAI) {
+ if (queueItem.vehicleType == ExtVehicleType.PassengerCar) {
+ if ((item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
+ // if pocket cars are prohibited, a citizen may only park their car once per path
+ parkingAllowed = false;
+ } else if ((item.m_lanesUsed & NetInfo.LaneType.PublicTransport) == NetInfo.LaneType.None) {
+ // if the citizen is walking to their target (= no public transport used), the passenger car must be parked in the very last moment
+ parkingAllowed = item.m_laneID == m_endLaneA || item.m_laneID == m_endLaneB;
+ /*if (_conf.Debug.Switches[4]) {
+ Log._Debug($"Path unit {unitId}: public transport has not been used. ");
+ }*/
+ }
+ }
+ }
+
+ if (parkingAllowed) {
+ // NON-STOCK CODE END
+ NetInfo.LaneType laneType = this.m_laneTypes & ~NetInfo.LaneType.Pedestrian;
+ VehicleInfo.VehicleType vehicleType = this.m_vehicleTypes & ~VehicleInfo.VehicleType.Bicycle;
+ if ((byte)(item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {
+ laneType &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
+ }
+ int nextLaneIndex2;
+ uint nextlaneId2;
+ if (laneType != NetInfo.LaneType.None &&
+ vehicleType != VehicleInfo.VehicleType.None &&
+ prevSegment.GetClosestLane(prevLaneIndex, laneType, vehicleType, out nextLaneIndex2, out nextlaneId2)) {
+ NetInfo.Lane lane5 = prevSegmentInfo.m_lanes[nextLaneIndex2];
+ byte connectOffset2;
+ if ((prevSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None == ((byte)(lane5.m_finalDirection & NetInfo.Direction.Backward) != 0)) {
+ connectOffset2 = 1;
+ } else {
+ connectOffset2 = 254;
+ }
+
+ CustomPathFind.BufferItem item2 = item;
+ if (this.m_randomParking) {
+ item2.m_comparisonValue += (float)this.m_pathRandomizer.Int32(300u) / this.m_maxLength;
+ }
+#if DEBUGNEWPF
+ if (debugPed) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring parking switch\n" +
+ "\t" + $"_extPathType={queueItem.pathType}\n" +
+ "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
+ "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
+ "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
+ "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
+ "\t" + $"_stablePath={m_stablePath}\n" +
+ "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
+ "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
+ "\t" + $"nextLaneIndex2={nextLaneIndex2}\n" +
+ "\t" + $"nextlaneId2={nextlaneId2}\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+ this.ProcessItemPedBicycle(debugPed, item2, nextNodeId, prevSegmentId, ref prevSegment, ref prevSegment, connectOffset2, 128, nextLaneIndex2, nextlaneId2); // ped
+ }
+ }
+ }
+ } else {
+ // we are going to a non-pedestrian lane
+
+ bool allowPedestrian = (byte)(this.m_laneTypes & NetInfo.LaneType.Pedestrian) != 0; // allow pedestrian switching to vehicle?
+ bool nextIsBeautificationNode = nextNode.Info.m_class.m_service == ItemClass.Service.Beautification;
+ bool allowBicycle = false; // is true if cim may switch from a pedestrian lane to a bike lane
+ byte parkingConnectOffset = 0;
+ if (allowPedestrian) {
+ if (prevIsBicycleLane) {
+ // we are going to a bicycle lane
+ parkingConnectOffset = connectOffset;
+ allowBicycle = nextIsBeautificationNode;
+ } else if (this.m_vehicleLane != 0u) {
+ // there is a parked vehicle position
+ if (this.m_vehicleLane != item.m_laneID) {
+ // we have not reached the parked vehicle yet
+ allowPedestrian = false;
+ } else {
+ // pedestrian switches to parked vehicle
+ parkingConnectOffset = this.m_vehicleOffset;
+ }
+ } else if (this.m_stablePath) {
+ // enter a bus
+ parkingConnectOffset = 128;
+ } else {
+ // pocket car spawning
+ if (Options.parkingAI &&
+ queueItem.vehicleType == ExtVehicleType.PassengerCar &&
+ (queueItem.pathType == ExtPathType.WalkingOnly || (queueItem.pathType == ExtPathType.DrivingOnly && item.m_position.m_segment != m_startSegmentA && item.m_position.m_segment != m_startSegmentB))) {
+ allowPedestrian = false;
+ } else {
+ parkingConnectOffset = (byte)this.m_pathRandomizer.UInt32(1u, 254u);
+ }
+ }
+ }
+
+ if ((this.m_vehicleTypes & (VehicleInfo.VehicleType.Ferry /* | VehicleInfo.VehicleType.Monorail*/)) != VehicleInfo.VehicleType.None) {
+ // monorail / ferry
+
+ for (int k = 0; k < 8; k++) {
+ ushort nextSegmentId = nextNode.GetSegment(k);
+ if (nextSegmentId == 0 || nextSegmentId == prevSegmentId) {
+ continue;
+ }
+
+ this.ProcessItemCosts(debug, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, allowBicycle, isMiddle);
+ }
+
+ if ((nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None /*&&
(this._vehicleTypes & VehicleInfo.VehicleType.Monorail) == VehicleInfo.VehicleType.None*/) {
- this.ProcessItemCosts(debug, item, nextNodeId, prevSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref prevSegment, ref prevRelSimilarLaneIndex, connectOffset, true, false, isMiddle);
- }
- } else {
- // road vehicles, trams, trains, metros, monorails, etc.
-
-
- // specifies if vehicles should follow lane arrows
- bool isStrictLaneChangePolicyEnabled = false;
- // specifies if the entity is allowed to u-turn (in general)
- bool isEntityAllowedToUturn = (this.m_vehicleTypes & (VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Monorail)) == VehicleInfo.VehicleType.None;
- // specifies if thes next node allows for u-turns
- bool isUturnAllowedHere = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;
- /*
- * specifies if u-turns are handled by custom code.
- * If not (performCustomVehicleUturns == false) AND the vanilla u-turn condition (stockUturn) evaluates to true, then u-turns are handled by the vanilla code
- */
- //bool performCustomVehicleUturns = false;
- bool prevIsRouted = prevLaneEndRouting.routed
+ this.ProcessItemCosts(debug, item, nextNodeId, prevSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref prevSegment, ref prevRelSimilarLaneIndex, connectOffset, true, false, isMiddle);
+ }
+ } else {
+ // road vehicles, trams, trains, metros, monorails, etc.
+
+
+ // specifies if vehicles should follow lane arrows
+ bool isStrictLaneChangePolicyEnabled = false;
+ // specifies if the entity is allowed to u-turn (in general)
+ bool isEntityAllowedToUturn = (this.m_vehicleTypes & (VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Monorail)) == VehicleInfo.VehicleType.None;
+ // specifies if thes next node allows for u-turns
+ bool isUturnAllowedHere = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;
+ /*
+ * specifies if u-turns are handled by custom code.
+ * If not (performCustomVehicleUturns == false) AND the vanilla u-turn condition (stockUturn) evaluates to true, then u-turns are handled by the vanilla code
+ */
+ //bool performCustomVehicleUturns = false;
+ bool prevIsRouted = prevLaneEndRouting.routed
#if DEBUG
- && !m_conf.Debug.Switches[11]
-#endif
- ;
-
- if (prevIsRouted) {
- bool prevIsOutgoingOneWay = nextIsStartNode ? prevSegmentRouting.startNodeOutgoingOneWay : prevSegmentRouting.endNodeOutgoingOneWay;
- bool nextIsUntouchable = (nextNode.m_flags & (NetNode.Flags.Untouchable)) != NetNode.Flags.None;
- bool nextIsTransitionOrJunction = (nextNode.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) != NetNode.Flags.None;
- bool nextIsBend = (nextNode.m_flags & (NetNode.Flags.Bend)) != NetNode.Flags.None;
-
- // determine if the vehicle may u-turn at the target node according to customization
- isUturnAllowedHere =
- isUturnAllowedHere || // stock u-turn points
- (Options.junctionRestrictionsEnabled &&
- m_isRoadVehicle && // only road vehicles may perform u-turns
- junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode) && // only do u-turns if allowed
- !nextIsBeautificationNode && // no u-turns at beautification nodes // TODO refactor to JunctionManager
- prevIsCarLane && // u-turns for road vehicles only
- !m_isHeavyVehicle && // only small vehicles may perform u-turns
- (nextIsTransitionOrJunction || nextIsBend) && // perform u-turns at transitions, junctions and bend nodes // TODO refactor to JunctionManager
- !prevIsOutgoingOneWay); // do not u-turn on one-ways // TODO refactor to JunctionManager
-
- isStrictLaneChangePolicyEnabled =
- !nextIsBeautificationNode && // do not obey lane arrows at beautification nodes
- !nextIsUntouchable &&
- m_isLaneArrowObeyingEntity &&
- //nextIsTransitionOrJunction && // follow lane arrows only at transitions and junctions
- !(
+ && !m_conf.Debug.Switches[11]
+#endif
+ ;
+
+ if (prevIsRouted) {
+ bool prevIsOutgoingOneWay = nextIsStartNode ? prevSegmentRouting.startNodeOutgoingOneWay : prevSegmentRouting.endNodeOutgoingOneWay;
+ bool nextIsUntouchable = (nextNode.m_flags & (NetNode.Flags.Untouchable)) != NetNode.Flags.None;
+ bool nextIsTransitionOrJunction = (nextNode.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) != NetNode.Flags.None;
+ bool nextIsBend = (nextNode.m_flags & (NetNode.Flags.Bend)) != NetNode.Flags.None;
+
+ // determine if the vehicle may u-turn at the target node according to customization
+ isUturnAllowedHere =
+ isUturnAllowedHere || // stock u-turn points
+ (Options.junctionRestrictionsEnabled &&
+ m_isRoadVehicle && // only road vehicles may perform u-turns
+ junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode) && // only do u-turns if allowed
+ !nextIsBeautificationNode && // no u-turns at beautification nodes // TODO refactor to JunctionManager
+ prevIsCarLane && // u-turns for road vehicles only
+ !m_isHeavyVehicle && // only small vehicles may perform u-turns
+ (nextIsTransitionOrJunction || nextIsBend) && // perform u-turns at transitions, junctions and bend nodes // TODO refactor to JunctionManager
+ !prevIsOutgoingOneWay); // do not u-turn on one-ways // TODO refactor to JunctionManager
+
+ isStrictLaneChangePolicyEnabled =
+ !nextIsBeautificationNode && // do not obey lane arrows at beautification nodes
+ !nextIsUntouchable &&
+ m_isLaneArrowObeyingEntity &&
+ //nextIsTransitionOrJunction && // follow lane arrows only at transitions and junctions
+ !(
#if DEBUG
- Options.allRelaxed || // debug option: all vehicle may ignore lane arrows
-#endif
- (Options.relaxedBusses && queueItem.vehicleType == ExtVehicleType.Bus)); // option: busses may ignore lane arrows
-
- /*if (! performCustomVehicleUturns) {
- isUturnAllowedHere = false;
- }*/
- //isEntityAllowedToUturn = isEntityAllowedToUturn && !performCustomVehicleUturns;
-
-
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane} (id {item.m_laneID}), node {nextNodeId} ({nextIsStartNode}):\n" +
- "\t" + $"_extPathType={queueItem.pathType}\n" +
- "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
- "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
- "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
- "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
- "\t" + $"_vehicleLane={m_vehicleLane}\n" +
- "\t" + $"_stablePath={m_stablePath}\n" +
- "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
- "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
- "\t" + $"prevIsOutgoingOneWay={prevIsOutgoingOneWay}\n" +
- "\t" + $"prevIsRouted={prevIsRouted}\n\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
- "\t" + $"isNextBeautificationNode={nextIsBeautificationNode}\n" +
- //"\t" + $"nextIsRealJunction={nextIsRealJunction}\n" +
- "\t" + $"nextIsTransitionOrJunction={nextIsTransitionOrJunction}\n" +
- "\t" + $"nextIsBend={nextIsBend}\n" +
- "\t" + $"nextIsUntouchable={nextIsUntouchable}\n" +
- "\t" + $"allowBicycle={allowBicycle}\n" +
- "\t" + $"isCustomUturnAllowed={junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode)}\n" +
- "\t" + $"isStrictLaneArrowPolicyEnabled={isStrictLaneChangePolicyEnabled}\n" +
- "\t" + $"isEntityAllowedToUturn={isEntityAllowedToUturn}\n" +
- "\t" + $"isUturnAllowedHere={isUturnAllowedHere}\n"
- //"\t" + $"performCustomVehicleUturns={performCustomVehicleUturns}\n"
- );
-#endif
- } else {
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}):\n" +
- "\t" + $"_extPathType={queueItem.pathType}\n" +
- "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
- "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
- "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
- "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
- "\t" + $"_stablePath={m_stablePath}\n" +
- "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
- "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
- "\t" + $"prevIsRouted={prevIsRouted}\n\n"
- );
-#endif
- }
-
- if (allowBicycle || !prevIsRouted) {
- /*
- * pedestrian to bicycle lane switch or no routing information available:
- * if pedestrian lanes should be explored (allowBicycle == true): do this here
- * if previous segment has custom routing (prevIsRouted == true): do NOT explore vehicle lanes here, else: vanilla exploration of vehicle lanes
- */
-
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add(
- $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"-> using DEFAULT exploration mode\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
-
- /*if (performCustomVehicleUturns) {
- isUturnAllowedHere = true;
- isEntityAllowedToUturn = true;
- }*/
-
- ushort nextSegmentId = prevSegment.GetRightSegment(nextNodeId);
- for (int k = 0; k < 8; ++k) {
- if (nextSegmentId == 0 || nextSegmentId == prevSegmentId) {
- break;
- }
-
- if (ProcessItemCosts(debug, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, !prevIsRouted, allowBicycle, isMiddle)) {
- // exceptional u-turns
- isUturnAllowedHere = true;
- }
-
- nextSegmentId = netManager.m_segments.m_buffer[nextSegmentId].GetRightSegment(nextNodeId);
- }
- }
-
- if (prevIsRouted) {
- /* routed vehicle paths */
-
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add(
- $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"-> using CUSTOM exploration mode\n"
- );
-#endif
- bool canUseLane = CanUseLane(debug, item.m_position.m_segment, prevSegmentInfo, item.m_position.m_lane, prevLaneInfo);
- LaneTransitionData[] laneTransitions = prevLaneEndRouting.transitions;
- if (laneTransitions != null && (canUseLane || Options.vehicleRestrictionsAggression != VehicleRestrictionsAggression.Strict)) {
-
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add(
- $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"CUSTOM exploration\n"
- );
-#endif
-
- LaneChangingCostCalculationMode laneChangingCostCalculationMode = LaneChangingCostCalculationMode.None; // lane changing cost calculation mode to use
- float? segmentSelectionCost = null; // cost for using that particular segment
- float? laneSelectionCost = null; // cost for using that particular lane
-
- /*
- * =======================================================================================================
- * (1) Apply vehicle restrictions
- * =======================================================================================================
- */
-
- if (! canUseLane) {
- laneSelectionCost = VehicleRestrictionsManager.PATHFIND_PENALTIES[(int)Options.vehicleRestrictionsAggression];
-
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"applied vehicle restrictions for vehicle {queueItem.vehicleId}, type {queueItem.vehicleType}:\n" +
- "\t" + $"=> laneSelectionCost={laneSelectionCost}\n"
- );
-#endif
- }
-
- if (m_isRoadVehicle &&
- prevLaneInfo != null &&
- prevIsCarLane) {
-
- if (Options.advancedAI) {
- laneChangingCostCalculationMode = LaneChangingCostCalculationMode.ByGivenDistance;
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"AI is active, prev is car lane and we are a car\n"
- );
-#endif
- }
-
- /*
- * =======================================================================================================
- * (2) Apply car ban district policies
- * =======================================================================================================
- */
-
- // Apply costs for traffic ban policies
- if ((prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&
- (prevLaneInfo.m_vehicleType & this.m_vehicleTypes) == VehicleInfo.VehicleType.Car &&
- (netManager.m_segments.m_buffer[item.m_position.m_segment].m_flags & this.m_carBanMask) != NetSegment.Flags.None) {
- // heavy vehicle ban / car ban ("Old Town" policy)
- if (laneSelectionCost == null) {
- laneSelectionCost = 1f;
- }
-#if DEBUGNEWPF
- float? oldLaneSelectionCost = laneSelectionCost;
-#endif
- laneSelectionCost *= 7.5f;
-
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"applied heavy vehicle ban / car ban ('Old Town' policy):\n" +
- "\t" + $"oldLaneSelectionCost={oldLaneSelectionCost}\n" +
- "\t" + $"=> laneSelectionCost={laneSelectionCost}\n"
- );
-#endif
- }
-
- /*
- * =======================================================================================================
- * (3) Apply costs for using/not using transport lanes
- * =======================================================================================================
- */
-
- /*
- * (1) busses should prefer transport lanes
- * (2) regular traffic should prefer regular lanes
- * (3) taxis, service vehicles and emergency vehicles may choose freely between regular and transport lanes
- */
- if ((prevLaneInfo.m_laneType & NetInfo.LaneType.TransportVehicle) != NetInfo.LaneType.None) {
- // previous lane is a public transport lane
- if ((queueItem.vehicleType & ExtVehicleType.Bus) != ExtVehicleType.None) {
- if (laneSelectionCost == null) {
- laneSelectionCost = 1f;
- }
-#if DEBUGNEWPF
- float? oldLaneSelectionCost = laneSelectionCost;
-#endif
- laneSelectionCost *= m_conf.PathFinding.PublicTransportLaneReward; // (1)
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"applied bus-on-transport lane reward:\n" +
- "\t" + $"oldLaneSelectionCost={oldLaneSelectionCost}\n" +
- "\t" + $"=> laneSelectionCost={laneSelectionCost}\n"
- );
-#endif
- } else if ((queueItem.vehicleType & (ExtVehicleType.RoadPublicTransport | ExtVehicleType.Service | ExtVehicleType.Emergency)) == ExtVehicleType.None) {
- if (laneSelectionCost == null) {
- laneSelectionCost = 1f;
- }
-#if DEBUGNEWPF
- float? oldLaneSelectionCost = laneSelectionCost;
-#endif
- laneSelectionCost *= m_conf.PathFinding.PublicTransportLanePenalty; // (2)
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"applied car-on-transport lane penalty:\n" +
- "\t" + $"oldLaneSelectionCost={oldLaneSelectionCost}\n" +
- "\t" + $"=> laneSelectionCost={laneSelectionCost}\n"
- );
-#endif
- } else {
- // (3), do nothing
- }
- }
-
- /*
- * =======================================================================================================
- * (4) Apply costs for large vehicles using inner lanes on highways
- * =======================================================================================================
- */
-
- bool nextIsJunction = (netManager.m_nodes.m_buffer[nextNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction;
- bool nextIsRealJunction = nextIsJunction && (netManager.m_nodes.m_buffer[nextNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);
- ushort prevNodeId = (nextNodeId == prevSegment.m_startNode) ? prevSegment.m_endNode : prevSegment.m_startNode;
- //bool prevIsRealJunction = (netManager.m_nodes.m_buffer[prevNodeId].m_flags & NetNode.Flags.Junction) != NetNode.Flags.None && (netManager.m_nodes.m_buffer[prevNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);
- if (prevLaneInfo.m_similarLaneCount > 1) {
- if (m_isHeavyVehicle &&
- Options.preferOuterLane &&
- prevSegmentRouting.highway &&
- m_pathRandomizer.Int32(m_conf.PathFinding.HeavyVehicleInnerLanePenaltySegmentSel) == 0
- /* && (netManager.m_nodes.m_buffer[prevNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) != NetNode.Flags.None */
- ) {
- // penalize large vehicles for using inner lanes
- if (laneSelectionCost == null) {
- laneSelectionCost = 1f;
- }
-#if DEBUGNEWPF
- float? oldLaneSelectionCost = laneSelectionCost;
-#endif
- float prevRelOuterLane = ((float)prevOuterSimilarLaneIndex / (float)(prevLaneInfo.m_similarLaneCount - 1));
- laneSelectionCost *= 1f + m_conf.PathFinding.HeavyVehicleMaxInnerLanePenalty * prevRelOuterLane;
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"applied inner lane penalty:\n" +
- "\t" + $"oldLaneSelectionCost={oldLaneSelectionCost}\n" +
- "\t" + $"=> laneSelectionCost={laneSelectionCost}\n"
- );
-#endif
- }
-
- /*
- * =======================================================================================================
- * (5) Apply costs for randomized lane selection in front of junctions and highway transitions
- * =======================================================================================================
- */
- if (Options.advancedAI &&
- !m_stablePath &&
- !m_isHeavyVehicle &&
- nextIsJunction &&
- m_pathRandomizer.Int32(m_conf.AdvancedVehicleAI.LaneRandomizationJunctionSel) == 0) {
- // randomized lane selection at junctions
- if (laneSelectionCost == null) {
- laneSelectionCost = 1f;
- }
-#if DEBUGNEWPF
- float? oldLaneSelectionCost = laneSelectionCost;
-#endif
- laneSelectionCost *= 1f + m_pathRandomizer.Int32(2) * m_conf.AdvancedVehicleAI.LaneRandomizationCostFactor;
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"applied lane randomizations at junctions:\n" +
- "\t" + $"oldLaneSelectionCost={oldLaneSelectionCost}\n" +
- "\t" + $"=> laneSelectionCost={laneSelectionCost}\n"
- );
-#endif
- }
- }
-
- /*
- * =======================================================================================================
- * (6) Apply junction costs
- * =======================================================================================================
- */
- if (Options.advancedAI && nextIsJunction && prevSegmentRouting.highway) {
- if (segmentSelectionCost == null) {
- segmentSelectionCost = 1f;
- }
-
- segmentSelectionCost *= 1f + m_conf.AdvancedVehicleAI.JunctionBaseCost;
- }
-
- /*
- * =======================================================================================================
- * (7) Apply traffic measurement costs for segment selection
- * =======================================================================================================
- */
- if (Options.advancedAI && (queueItem.vehicleType & (ExtVehicleType.RoadVehicle & ~ExtVehicleType.Bus)) != ExtVehicleType.None && !m_stablePath) {
- // segment selection based on segment traffic volume
- NetInfo.Direction prevFinalDir = nextIsStartNode ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;
- prevFinalDir = ((prevSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? prevFinalDir : NetInfo.InvertDirection(prevFinalDir);
- SegmentDirTrafficData prevDirTrafficData = trafficMeasurementManager.SegmentDirTrafficData[trafficMeasurementManager.GetDirIndex(item.m_position.m_segment, prevFinalDir)];
-
- float segmentTraffic = Mathf.Clamp(1f - (float)prevDirTrafficData.meanSpeed / (float)TrafficMeasurementManager.REF_REL_SPEED + item.m_trafficRand, 0, 1f);
-
- if (segmentSelectionCost == null) {
- segmentSelectionCost = 1f;
- }
-
- segmentSelectionCost *= 1f +
- m_conf.AdvancedVehicleAI.TrafficCostFactor *
- segmentTraffic;
-
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"applied traffic measurement costs for segment selection:\n" +
- "\t" + $"segmentTraffic={segmentTraffic}\n" +
- "\t" + $"=> segmentSelectionCost={segmentSelectionCost}\n"
- );
-#endif
-
- if (m_conf.AdvancedVehicleAI.LaneDensityRandInterval > 0 && nextIsRealJunction) {
- item.m_trafficRand = 0.01f * ((float)m_pathRandomizer.Int32((uint)m_conf.AdvancedVehicleAI.LaneDensityRandInterval + 1u) - m_conf.AdvancedVehicleAI.LaneDensityRandInterval / 2f);
-
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"updated item.m_trafficRand:\n" +
- "\t" + $"=> item.m_trafficRand={item.m_trafficRand}\n"
- );
-#endif
- }
- }
-
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"calculated traffic stats:\n" +
- "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
- "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
- "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
- "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
- "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
- "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
- "\t" + $"laneSelectionCost={laneSelectionCost}\n" +
- "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
- );
-#endif
- }
-
- for (int k = 0; k < laneTransitions.Length; ++k) {
- ushort nextSegmentId = laneTransitions[k].segmentId;
-
- if (nextSegmentId == 0) {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add(
- $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"CUSTOM exploration\n" +
- "\t" + $"transition iteration {k}:\n" +
- "\t" + $"{laneTransitions[k].ToString()}\n" +
- "\t" + $"*** SKIPPING *** (nextSegmentId=0)\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
- continue;
- }
-
- bool uturn = nextSegmentId == prevSegmentId;
- if (uturn) {
- // prevent double/forbidden exploration of previous segment by vanilla code during this method execution
- if (! isEntityAllowedToUturn || ! isUturnAllowedHere) {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add(
- $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"CUSTOM exploration\n" +
- "\t" + $"transition iteration {k}:\n" +
- "\t" + $"{laneTransitions[k].ToString()}\n" +
- "\t" + $"*** SKIPPING *** (u-turns prohibited)\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
- continue;
- }
- }
-
- if (laneTransitions[k].type == LaneEndTransitionType.Invalid) {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add(
- $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"CUSTOM exploration\n" +
- "\t" + $"transition iteration {k}:\n" +
- "\t" + $"{laneTransitions[k].ToString()}\n" +
- "\t" + $"*** SKIPPING *** (invalid transition)\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
- continue;
- }
-
- // allow vehicles to ignore strict lane routing when moving off
- bool relaxedLaneChanging =
- m_isRoadVehicle &&
- (queueItem.vehicleType & (ExtVehicleType.Service | ExtVehicleType.PublicTransport | ExtVehicleType.Emergency)) != ExtVehicleType.None &&
- queueItem.vehicleId == 0 &&
- (laneTransitions[k].laneId == m_startLaneA || laneTransitions[k].laneId == m_startLaneB);
-
- if (! relaxedLaneChanging &&
- (isStrictLaneChangePolicyEnabled && laneTransitions[k].type == LaneEndTransitionType.Relaxed)) {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add(
- $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"CUSTOM exploration\n" +
- "\t" + $"transition iteration {k}:\n" +
- "\t" + $"{laneTransitions[k].ToString()}\n" +
- "\t" + $"relaxedLaneChanging={relaxedLaneChanging}\n" +
- "\t" + $"isStrictLaneChangePolicyEnabled={relaxedLaneChanging}\n" +
- "\t" + $"*** SKIPPING *** (incompatible lane)\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
- continue;
- }
-
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add(
- $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"CUSTOM exploration\n" +
- "\t" + $"transition iteration {k}:\n" +
- "\t" + $"{laneTransitions[k].ToString()}\n" +
- "\t" + $"> PERFORMING EXPLORATION NOW <\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
-
- bool foundForced = false;
- int prevLaneIndexFromInner = prevRelSimilarLaneIndex;
- if (ProcessItemCosts(debug, false, laneChangingCostCalculationMode, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[nextSegmentId], /*routingManager.segmentRoutings[nextSegmentId],*/ ref prevLaneIndexFromInner, connectOffset, true, false, laneTransitions[k].laneIndex, laneTransitions[k].laneId, laneTransitions[k].distance, segmentSelectionCost, laneSelectionCost, isMiddle, out foundForced)) {
- // process exceptional u-turning in vanilla code
- isUturnAllowedHere = true;
- }
- }
- }
- }
-
- if (!prevIsRouted && isEntityAllowedToUturn && isUturnAllowedHere) {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"path unit {unitId}\n" +
- $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"-> exploring DEFAULT u-turn\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
-
- this.ProcessItemCosts(debug, item, nextNodeId, prevSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref prevSegment, ref prevRelSimilarLaneIndex, connectOffset, true, false, isMiddle);
- }
- }
-
- if (allowPedestrian) {
- // switch from walking to driving a car, bus, etc.
- int nextLaneIndex;
- uint nextLaneId;
- if (prevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Pedestrian, this.m_vehicleTypes, out nextLaneIndex, out nextLaneId)) {
-#if DEBUGNEWPF
- if (debugPed) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring vehicle switch\n" +
- "\t" + $"_extPathType={queueItem.pathType}\n" +
- "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
- "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
- "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
- "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
- "\t" + $"_stablePath={m_stablePath}\n" +
- "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
- "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
- "\t" + $"nextLaneIndex={nextLaneIndex}\n" +
- "\t" + $"nextLaneId={nextLaneId}\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
- this.ProcessItemPedBicycle(debugPed, item, nextNodeId, prevSegmentId, ref prevSegment, ref prevSegment, parkingConnectOffset, parkingConnectOffset, nextLaneIndex, nextLaneId); // ped
- }
- } // allowPedSwitch
- } // !prevIsPedestrianLane
-
- // [18/05/06] conditions commented out because cims could not go to an outside connection with path "walk -> public transport -> walk -> car"
- if (nextNode.m_lane != 0u /*&&
+ Options.allRelaxed || // debug option: all vehicle may ignore lane arrows
+#endif
+ (Options.relaxedBusses && queueItem.vehicleType == ExtVehicleType.Bus)); // option: busses may ignore lane arrows
+
+ /*if (! performCustomVehicleUturns) {
+ isUturnAllowedHere = false;
+ }*/
+ //isEntityAllowedToUturn = isEntityAllowedToUturn && !performCustomVehicleUturns;
+
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane} (id {item.m_laneID}), node {nextNodeId} ({nextIsStartNode}):\n" +
+ "\t" + $"_extPathType={queueItem.pathType}\n" +
+ "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
+ "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
+ "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
+ "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
+ "\t" + $"_vehicleLane={m_vehicleLane}\n" +
+ "\t" + $"_stablePath={m_stablePath}\n" +
+ "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
+ "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
+ "\t" + $"prevIsOutgoingOneWay={prevIsOutgoingOneWay}\n" +
+ "\t" + $"prevIsRouted={prevIsRouted}\n\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
+ "\t" + $"isNextBeautificationNode={nextIsBeautificationNode}\n" +
+ //"\t" + $"nextIsRealJunction={nextIsRealJunction}\n" +
+ "\t" + $"nextIsTransitionOrJunction={nextIsTransitionOrJunction}\n" +
+ "\t" + $"nextIsBend={nextIsBend}\n" +
+ "\t" + $"nextIsUntouchable={nextIsUntouchable}\n" +
+ "\t" + $"allowBicycle={allowBicycle}\n" +
+ "\t" + $"isCustomUturnAllowed={junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode)}\n" +
+ "\t" + $"isStrictLaneArrowPolicyEnabled={isStrictLaneChangePolicyEnabled}\n" +
+ "\t" + $"isEntityAllowedToUturn={isEntityAllowedToUturn}\n" +
+ "\t" + $"isUturnAllowedHere={isUturnAllowedHere}\n"
+ //"\t" + $"performCustomVehicleUturns={performCustomVehicleUturns}\n"
+ );
+#endif
+ } else {
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}):\n" +
+ "\t" + $"_extPathType={queueItem.pathType}\n" +
+ "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
+ "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
+ "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
+ "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
+ "\t" + $"_stablePath={m_stablePath}\n" +
+ "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
+ "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
+ "\t" + $"prevIsRouted={prevIsRouted}\n\n"
+ );
+#endif
+ }
+
+ if (allowBicycle || !prevIsRouted) {
+ /*
+ * pedestrian to bicycle lane switch or no routing information available:
+ * if pedestrian lanes should be explored (allowBicycle == true): do this here
+ * if previous segment has custom routing (prevIsRouted == true): do NOT explore vehicle lanes here, else: vanilla exploration of vehicle lanes
+ */
+
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add(
+ $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"-> using DEFAULT exploration mode\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+
+ /*if (performCustomVehicleUturns) {
+ isUturnAllowedHere = true;
+ isEntityAllowedToUturn = true;
+ }*/
+
+ ushort nextSegmentId = prevSegment.GetRightSegment(nextNodeId);
+ for (int k = 0; k < 8; ++k) {
+ if (nextSegmentId == 0 || nextSegmentId == prevSegmentId) {
+ break;
+ }
+
+ if (ProcessItemCosts(debug, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, !prevIsRouted, allowBicycle, isMiddle)) {
+ // exceptional u-turns
+ isUturnAllowedHere = true;
+ }
+
+ nextSegmentId = netManager.m_segments.m_buffer[nextSegmentId].GetRightSegment(nextNodeId);
+ }
+ }
+
+ if (prevIsRouted) {
+ /* routed vehicle paths */
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add(
+ $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"-> using CUSTOM exploration mode\n"
+ );
+#endif
+ bool canUseLane = CanUseLane(debug, item.m_position.m_segment, prevSegmentInfo, item.m_position.m_lane, prevLaneInfo);
+ LaneTransitionData[] laneTransitions = prevLaneEndRouting.transitions;
+ if (laneTransitions != null && (canUseLane || Options.vehicleRestrictionsAggression != VehicleRestrictionsAggression.Strict)) {
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add(
+ $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"CUSTOM exploration\n"
+ );
+#endif
+
+ LaneChangingCostCalculationMode laneChangingCostCalculationMode = LaneChangingCostCalculationMode.None; // lane changing cost calculation mode to use
+ float? segmentSelectionCost = null; // cost for using that particular segment
+ float? laneSelectionCost = null; // cost for using that particular lane
+
+ /*
+ * =======================================================================================================
+ * (1) Apply vehicle restrictions
+ * =======================================================================================================
+ */
+
+ if (! canUseLane) {
+ laneSelectionCost = VehicleRestrictionsManager.PATHFIND_PENALTIES[(int)Options.vehicleRestrictionsAggression];
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"applied vehicle restrictions for vehicle {queueItem.vehicleId}, type {queueItem.vehicleType}:\n" +
+ "\t" + $"=> laneSelectionCost={laneSelectionCost}\n"
+ );
+#endif
+ }
+
+ if (m_isRoadVehicle &&
+ prevLaneInfo != null &&
+ prevIsCarLane) {
+
+ if (Options.advancedAI) {
+ laneChangingCostCalculationMode = LaneChangingCostCalculationMode.ByGivenDistance;
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"AI is active, prev is car lane and we are a car\n"
+ );
+#endif
+ }
+
+ /*
+ * =======================================================================================================
+ * (2) Apply car ban district policies
+ * =======================================================================================================
+ */
+
+ // Apply costs for traffic ban policies
+ if ((prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&
+ (prevLaneInfo.m_vehicleType & this.m_vehicleTypes) == VehicleInfo.VehicleType.Car &&
+ (netManager.m_segments.m_buffer[item.m_position.m_segment].m_flags & this.m_carBanMask) != NetSegment.Flags.None) {
+ // heavy vehicle ban / car ban ("Old Town" policy)
+ if (laneSelectionCost == null) {
+ laneSelectionCost = 1f;
+ }
+#if DEBUGNEWPF
+ float? oldLaneSelectionCost = laneSelectionCost;
+#endif
+ laneSelectionCost *= 7.5f;
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"applied heavy vehicle ban / car ban ('Old Town' policy):\n" +
+ "\t" + $"oldLaneSelectionCost={oldLaneSelectionCost}\n" +
+ "\t" + $"=> laneSelectionCost={laneSelectionCost}\n"
+ );
+#endif
+ }
+
+ /*
+ * =======================================================================================================
+ * (3) Apply costs for using/not using transport lanes
+ * =======================================================================================================
+ */
+
+ /*
+ * (1) busses should prefer transport lanes
+ * (2) regular traffic should prefer regular lanes
+ * (3) taxis, service vehicles and emergency vehicles may choose freely between regular and transport lanes
+ */
+ if ((prevLaneInfo.m_laneType & NetInfo.LaneType.TransportVehicle) != NetInfo.LaneType.None) {
+ // previous lane is a public transport lane
+ if ((queueItem.vehicleType & ExtVehicleType.Bus) != ExtVehicleType.None) {
+ if (laneSelectionCost == null) {
+ laneSelectionCost = 1f;
+ }
+#if DEBUGNEWPF
+ float? oldLaneSelectionCost = laneSelectionCost;
+#endif
+ laneSelectionCost *= m_conf.PathFinding.PublicTransportLaneReward; // (1)
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"applied bus-on-transport lane reward:\n" +
+ "\t" + $"oldLaneSelectionCost={oldLaneSelectionCost}\n" +
+ "\t" + $"=> laneSelectionCost={laneSelectionCost}\n"
+ );
+#endif
+ } else if ((queueItem.vehicleType & (ExtVehicleType.RoadPublicTransport | ExtVehicleType.Service | ExtVehicleType.Emergency)) == ExtVehicleType.None) {
+ if (laneSelectionCost == null) {
+ laneSelectionCost = 1f;
+ }
+#if DEBUGNEWPF
+ float? oldLaneSelectionCost = laneSelectionCost;
+#endif
+ laneSelectionCost *= m_conf.PathFinding.PublicTransportLanePenalty; // (2)
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"applied car-on-transport lane penalty:\n" +
+ "\t" + $"oldLaneSelectionCost={oldLaneSelectionCost}\n" +
+ "\t" + $"=> laneSelectionCost={laneSelectionCost}\n"
+ );
+#endif
+ } else {
+ // (3), do nothing
+ }
+ }
+
+ /*
+ * =======================================================================================================
+ * (4) Apply costs for large vehicles using inner lanes on highways
+ * =======================================================================================================
+ */
+
+ bool nextIsJunction = (netManager.m_nodes.m_buffer[nextNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction;
+ bool nextIsRealJunction = nextIsJunction && (netManager.m_nodes.m_buffer[nextNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);
+ ushort prevNodeId = (nextNodeId == prevSegment.m_startNode) ? prevSegment.m_endNode : prevSegment.m_startNode;
+ //bool prevIsRealJunction = (netManager.m_nodes.m_buffer[prevNodeId].m_flags & NetNode.Flags.Junction) != NetNode.Flags.None && (netManager.m_nodes.m_buffer[prevNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);
+ if (prevLaneInfo.m_similarLaneCount > 1) {
+ if (m_isHeavyVehicle &&
+ Options.preferOuterLane &&
+ prevSegmentRouting.highway &&
+ m_pathRandomizer.Int32(m_conf.PathFinding.HeavyVehicleInnerLanePenaltySegmentSel) == 0
+ /* && (netManager.m_nodes.m_buffer[prevNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) != NetNode.Flags.None */
+ ) {
+ // penalize large vehicles for using inner lanes
+ if (laneSelectionCost == null) {
+ laneSelectionCost = 1f;
+ }
+#if DEBUGNEWPF
+ float? oldLaneSelectionCost = laneSelectionCost;
+#endif
+ float prevRelOuterLane = ((float)prevOuterSimilarLaneIndex / (float)(prevLaneInfo.m_similarLaneCount - 1));
+ laneSelectionCost *= 1f + m_conf.PathFinding.HeavyVehicleMaxInnerLanePenalty * prevRelOuterLane;
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"applied inner lane penalty:\n" +
+ "\t" + $"oldLaneSelectionCost={oldLaneSelectionCost}\n" +
+ "\t" + $"=> laneSelectionCost={laneSelectionCost}\n"
+ );
+#endif
+ }
+
+ /*
+ * =======================================================================================================
+ * (5) Apply costs for randomized lane selection in front of junctions and highway transitions
+ * =======================================================================================================
+ */
+ if (Options.advancedAI &&
+ !m_stablePath &&
+ !m_isHeavyVehicle &&
+ nextIsJunction &&
+ m_pathRandomizer.Int32(m_conf.AdvancedVehicleAI.LaneRandomizationJunctionSel) == 0) {
+ // randomized lane selection at junctions
+ if (laneSelectionCost == null) {
+ laneSelectionCost = 1f;
+ }
+#if DEBUGNEWPF
+ float? oldLaneSelectionCost = laneSelectionCost;
+#endif
+ laneSelectionCost *= 1f + m_pathRandomizer.Int32(2) * m_conf.AdvancedVehicleAI.LaneRandomizationCostFactor;
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"applied lane randomizations at junctions:\n" +
+ "\t" + $"oldLaneSelectionCost={oldLaneSelectionCost}\n" +
+ "\t" + $"=> laneSelectionCost={laneSelectionCost}\n"
+ );
+#endif
+ }
+ }
+
+ /*
+ * =======================================================================================================
+ * (6) Apply junction costs
+ * =======================================================================================================
+ */
+ if (Options.advancedAI && nextIsJunction && prevSegmentRouting.highway) {
+ if (segmentSelectionCost == null) {
+ segmentSelectionCost = 1f;
+ }
+
+ segmentSelectionCost *= 1f + m_conf.AdvancedVehicleAI.JunctionBaseCost;
+ }
+
+ /*
+ * =======================================================================================================
+ * (7) Apply traffic measurement costs for segment selection
+ * =======================================================================================================
+ */
+ if (Options.advancedAI && (queueItem.vehicleType & (ExtVehicleType.RoadVehicle & ~ExtVehicleType.Bus)) != ExtVehicleType.None && !m_stablePath) {
+ // segment selection based on segment traffic volume
+ NetInfo.Direction prevFinalDir = nextIsStartNode ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;
+ prevFinalDir = ((prevSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? prevFinalDir : NetInfo.InvertDirection(prevFinalDir);
+ SegmentDirTrafficData prevDirTrafficData = trafficMeasurementManager.SegmentDirTrafficData[trafficMeasurementManager.GetDirIndex(item.m_position.m_segment, prevFinalDir)];
+
+ float segmentTraffic = Mathf.Clamp(1f - (float)prevDirTrafficData.meanSpeed / (float)TrafficMeasurementManager.REF_REL_SPEED + item.m_trafficRand, 0, 1f);
+
+ if (segmentSelectionCost == null) {
+ segmentSelectionCost = 1f;
+ }
+
+ segmentSelectionCost *= 1f +
+ m_conf.AdvancedVehicleAI.TrafficCostFactor *
+ segmentTraffic;
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"applied traffic measurement costs for segment selection:\n" +
+ "\t" + $"segmentTraffic={segmentTraffic}\n" +
+ "\t" + $"=> segmentSelectionCost={segmentSelectionCost}\n"
+ );
+#endif
+
+ if (m_conf.AdvancedVehicleAI.LaneDensityRandInterval > 0 && nextIsRealJunction) {
+ item.m_trafficRand = 0.01f * ((float)m_pathRandomizer.Int32((uint)m_conf.AdvancedVehicleAI.LaneDensityRandInterval + 1u) - m_conf.AdvancedVehicleAI.LaneDensityRandInterval / 2f);
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"updated item.m_trafficRand:\n" +
+ "\t" + $"=> item.m_trafficRand={item.m_trafficRand}\n"
+ );
+#endif
+ }
+ }
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"calculated traffic stats:\n" +
+ "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
+ "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
+ "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
+ "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
+ "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
+ "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
+ "\t" + $"laneSelectionCost={laneSelectionCost}\n" +
+ "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
+ );
+#endif
+ }
+
+ for (int k = 0; k < laneTransitions.Length; ++k) {
+ ushort nextSegmentId = laneTransitions[k].segmentId;
+
+ if (nextSegmentId == 0) {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add(
+ $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"CUSTOM exploration\n" +
+ "\t" + $"transition iteration {k}:\n" +
+ "\t" + $"{laneTransitions[k].ToString()}\n" +
+ "\t" + $"*** SKIPPING *** (nextSegmentId=0)\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+ continue;
+ }
+
+ bool uturn = nextSegmentId == prevSegmentId;
+ if (uturn) {
+ // prevent double/forbidden exploration of previous segment by vanilla code during this method execution
+ if (! isEntityAllowedToUturn || ! isUturnAllowedHere) {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add(
+ $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"CUSTOM exploration\n" +
+ "\t" + $"transition iteration {k}:\n" +
+ "\t" + $"{laneTransitions[k].ToString()}\n" +
+ "\t" + $"*** SKIPPING *** (u-turns prohibited)\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+ continue;
+ }
+ }
+
+ if (laneTransitions[k].type == LaneEndTransitionType.Invalid) {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add(
+ $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"CUSTOM exploration\n" +
+ "\t" + $"transition iteration {k}:\n" +
+ "\t" + $"{laneTransitions[k].ToString()}\n" +
+ "\t" + $"*** SKIPPING *** (invalid transition)\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+ continue;
+ }
+
+ // allow vehicles to ignore strict lane routing when moving off
+ bool relaxedLaneChanging =
+ m_isRoadVehicle &&
+ (queueItem.vehicleType & (ExtVehicleType.Service | ExtVehicleType.PublicTransport | ExtVehicleType.Emergency)) != ExtVehicleType.None &&
+ queueItem.vehicleId == 0 &&
+ (laneTransitions[k].laneId == m_startLaneA || laneTransitions[k].laneId == m_startLaneB);
+
+ if (! relaxedLaneChanging &&
+ (isStrictLaneChangePolicyEnabled && laneTransitions[k].type == LaneEndTransitionType.Relaxed)) {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add(
+ $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"CUSTOM exploration\n" +
+ "\t" + $"transition iteration {k}:\n" +
+ "\t" + $"{laneTransitions[k].ToString()}\n" +
+ "\t" + $"relaxedLaneChanging={relaxedLaneChanging}\n" +
+ "\t" + $"isStrictLaneChangePolicyEnabled={relaxedLaneChanging}\n" +
+ "\t" + $"*** SKIPPING *** (incompatible lane)\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+ continue;
+ }
+
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add(
+ $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"CUSTOM exploration\n" +
+ "\t" + $"transition iteration {k}:\n" +
+ "\t" + $"{laneTransitions[k].ToString()}\n" +
+ "\t" + $"> PERFORMING EXPLORATION NOW <\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+
+ bool foundForced = false;
+ int prevLaneIndexFromInner = prevRelSimilarLaneIndex;
+ if (ProcessItemCosts(debug, false, laneChangingCostCalculationMode, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[nextSegmentId], /*routingManager.segmentRoutings[nextSegmentId],*/ ref prevLaneIndexFromInner, connectOffset, true, false, laneTransitions[k].laneIndex, laneTransitions[k].laneId, laneTransitions[k].distance, segmentSelectionCost, laneSelectionCost, isMiddle, out foundForced)) {
+ // process exceptional u-turning in vanilla code
+ isUturnAllowedHere = true;
+ }
+ }
+ }
+ }
+
+ if (!prevIsRouted && isEntityAllowedToUturn && isUturnAllowedHere) {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add($"path unit {unitId}\n" +
+ $"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"-> exploring DEFAULT u-turn\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+
+ this.ProcessItemCosts(debug, item, nextNodeId, prevSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref prevSegment, ref prevRelSimilarLaneIndex, connectOffset, true, false, isMiddle);
+ }
+ }
+
+ if (allowPedestrian) {
+ // switch from walking to driving a car, bus, etc.
+ int nextLaneIndex;
+ uint nextLaneId;
+ if (prevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Pedestrian, this.m_vehicleTypes, out nextLaneIndex, out nextLaneId)) {
+#if DEBUGNEWPF
+ if (debugPed) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring vehicle switch\n" +
+ "\t" + $"_extPathType={queueItem.pathType}\n" +
+ "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
+ "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
+ "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
+ "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
+ "\t" + $"_stablePath={m_stablePath}\n" +
+ "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
+ "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n" +
+ "\t" + $"nextLaneIndex={nextLaneIndex}\n" +
+ "\t" + $"nextLaneId={nextLaneId}\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+ this.ProcessItemPedBicycle(debugPed, item, nextNodeId, prevSegmentId, ref prevSegment, ref prevSegment, parkingConnectOffset, parkingConnectOffset, nextLaneIndex, nextLaneId); // ped
+ }
+ } // allowPedSwitch
+ } // !prevIsPedestrianLane
+
+ // [18/05/06] conditions commented out because cims could not go to an outside connection with path "walk -> public transport -> walk -> car"
+ if (nextNode.m_lane != 0u /*&&
(!Options.parkingAI ||
queueItem.vehicleType != ExtVehicleType.PassengerCar ||
(item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) == NetInfo.LaneType.None)*/) {
- // transport lines, cargo lines, etc.
-
- bool targetDisabled = (nextNode.m_flags & (NetNode.Flags.Disabled | NetNode.Flags.DisableOnlyMiddle)) == NetNode.Flags.Disabled;
- ushort nextSegmentId = netManager.m_lanes.m_buffer[nextNode.m_lane].m_segment;
- if (nextSegmentId != 0 && nextSegmentId != item.m_position.m_segment) {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring transport segment\n" +
- "\t" + $"_extPathType={queueItem.pathType}\n" +
- "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
- "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
- "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
- "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
- "\t" + $"_stablePath={m_stablePath}\n" +
- "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
- "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
- "\t" + $"nextNode.m_lane={nextNode.m_lane}\n" +
- "\t" + $"nextSegmentId={nextSegmentId}\n" +
- "\t" + $"nextIsStartNode={nextIsStartNode}\n"
- );
- FlushMainLog(logBuf, unitId);
- }
-#endif
- this.ProcessItemPublicTransport(debug, item, nextNodeId, targetDisabled, nextSegmentId, ref prevSegment, ref netManager.m_segments.m_buffer[nextSegmentId], nextNode.m_lane, nextNode.m_laneOffset, connectOffset);
- }
- }
-
-#if DEBUGNEWPF
- if (debug) {
- FlushMainLog(logBuf, unitId);
- }
-#endif
- }
-
- // 2
- private void ProcessItemPublicTransport(bool debug, BufferItem item, ushort nextNodeId, bool targetDisabled, ushort nextSegmentId, ref NetSegment prevSegment, ref NetSegment nextSegment, uint nextLaneId, byte offset, byte connectOffset) {
- if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {
- return;
- }
- NetManager netManager = Singleton.instance;
- if (targetDisabled && ((netManager.m_nodes.m_buffer[(int)nextSegment.m_startNode].m_flags | netManager.m_nodes.m_buffer[(int)nextSegment.m_endNode].m_flags) & NetNode.Flags.Disabled) == NetNode.Flags.None) {
- return;
- }
+ // transport lines, cargo lines, etc.
+
+ bool targetDisabled = (nextNode.m_flags & (NetNode.Flags.Disabled | NetNode.Flags.DisableOnlyMiddle)) == NetNode.Flags.Disabled;
+ ushort nextSegmentId = netManager.m_lanes.m_buffer[nextNode.m_lane].m_segment;
+ if (nextSegmentId != 0 && nextSegmentId != item.m_position.m_segment) {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring transport segment\n" +
+ "\t" + $"_extPathType={queueItem.pathType}\n" +
+ "\t" + $"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\n" +
+ "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
+ "\t" + $"_isRoadVehicle={m_isRoadVehicle}\n" +
+ "\t" + $"_isHeavyVehicle={m_isHeavyVehicle}\n" +
+ "\t" + $"_stablePath={m_stablePath}\n" +
+ "\t" + $"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\n" +
+ "\t" + $"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n\n" +
+ "\t" + $"nextNode.m_lane={nextNode.m_lane}\n" +
+ "\t" + $"nextSegmentId={nextSegmentId}\n" +
+ "\t" + $"nextIsStartNode={nextIsStartNode}\n"
+ );
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+ this.ProcessItemPublicTransport(debug, item, nextNodeId, targetDisabled, nextSegmentId, ref prevSegment, ref netManager.m_segments.m_buffer[nextSegmentId], nextNode.m_lane, nextNode.m_laneOffset, connectOffset);
+ }
+ }
+
+#if DEBUGNEWPF
+ if (debug) {
+ FlushMainLog(logBuf, unitId);
+ }
+#endif
+ }
+
+ // 2
+ private void ProcessItemPublicTransport(bool debug, BufferItem item, ushort nextNodeId, bool targetDisabled, ushort nextSegmentId, ref NetSegment prevSegment, ref NetSegment nextSegment, uint nextLaneId, byte offset, byte connectOffset) {
+ if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {
+ return;
+ }
+ NetManager netManager = Singleton.instance;
+ if (targetDisabled && ((netManager.m_nodes.m_buffer[(int)nextSegment.m_startNode].m_flags | netManager.m_nodes.m_buffer[(int)nextSegment.m_endNode].m_flags) & NetNode.Flags.Disabled) == NetNode.Flags.None) {
+ return;
+ }
#if COUNTSEGMENTSTONEXTJUNCTION
bool nextIsRegularNode = nextNodeId == prevSegment.m_startNode || nextNodeId == prevSegment.m_endNode;
@@ -1659,60 +1664,60 @@ private void ProcessItemPublicTransport(bool debug, BufferItem item, ushort next
}
#endif
- NetInfo nextSegmentInfo = nextSegment.Info;
- NetInfo prevSegmentInfo = prevSegment.Info;
- int nextNumLanes = nextSegmentInfo.m_lanes.Length;
- uint curLaneId = nextSegment.m_lanes;
- float prevMaxSpeed = 1f;
- float prevSpeed = 1f;
- NetInfo.LaneType prevLaneType = NetInfo.LaneType.None;
- if ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
- NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];
- prevMaxSpeed = GetLaneSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, prevLaneInfo); // NON-STOCK CODE
- prevLaneType = prevLaneInfo.m_laneType;
- if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
- prevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
- }
- prevSpeed = this.CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // NON-STOCK CODE
- }
- float segLength;
- if (prevLaneType == NetInfo.LaneType.PublicTransport) {
- segLength = netManager.m_lanes.m_buffer[item.m_laneID].m_length;
- } else {
- segLength = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, prevSegment.m_averageLength);
- }
- float offsetLength = (float)Mathf.Abs((int)(connectOffset - item.m_position.m_offset)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * segLength;
- float methodDistance = item.m_methodDistance + offsetLength;
- float comparisonValue = item.m_comparisonValue + offsetLength / (prevSpeed * this.m_maxLength);
- float duration = item.m_duration + offsetLength / prevMaxSpeed;
- Vector3 b = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
-
- if (! this.m_ignoreCost) {
- int ticketCost = netManager.m_lanes.m_buffer[item.m_laneID].m_ticketCost;
- if (ticketCost != 0) {
- comparisonValue += (float)(ticketCost * this.m_pathRandomizer.Int32(2000u)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;
- }
- }
-
- uint laneIndex = 0;
+ NetInfo nextSegmentInfo = nextSegment.Info;
+ NetInfo prevSegmentInfo = prevSegment.Info;
+ int nextNumLanes = nextSegmentInfo.m_lanes.Length;
+ uint curLaneId = nextSegment.m_lanes;
+ float prevMaxSpeed = 1f;
+ float prevSpeed = 1f;
+ NetInfo.LaneType prevLaneType = NetInfo.LaneType.None;
+ if ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
+ NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];
+ prevMaxSpeed = GetLaneSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, prevLaneInfo); // NON-STOCK CODE
+ prevLaneType = prevLaneInfo.m_laneType;
+ if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
+ prevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
+ }
+ prevSpeed = this.CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // NON-STOCK CODE
+ }
+ float segLength;
+ if (prevLaneType == NetInfo.LaneType.PublicTransport) {
+ segLength = netManager.m_lanes.m_buffer[item.m_laneID].m_length;
+ } else {
+ segLength = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, prevSegment.m_averageLength);
+ }
+ float offsetLength = (float)Mathf.Abs((int)(connectOffset - item.m_position.m_offset)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * segLength;
+ float methodDistance = item.m_methodDistance + offsetLength;
+ float comparisonValue = item.m_comparisonValue + offsetLength / (prevSpeed * this.m_maxLength);
+ float duration = item.m_duration + offsetLength / prevMaxSpeed;
+ Vector3 b = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+
+ if (! this.m_ignoreCost) {
+ int ticketCost = netManager.m_lanes.m_buffer[item.m_laneID].m_ticketCost;
+ if (ticketCost != 0) {
+ comparisonValue += (float)(ticketCost * this.m_pathRandomizer.Int32(2000u)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;
+ }
+ }
+
+ uint laneIndex = 0;
#if DEBUG
- int wIter = 0;
+ int wIter = 0;
#endif
- while (laneIndex < nextNumLanes && curLaneId != 0u) {
+ while (laneIndex < nextNumLanes && curLaneId != 0u) {
#if DEBUG
- ++wIter;
- if (wIter >= 20) {
- Log.Error("Too many iterations in ProcessItem2!");
- break;
- }
-#endif
-
- if (nextLaneId == curLaneId) {
- NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[laneIndex];
- if (nextLaneInfo.CheckType(this.m_laneTypes, this.m_vehicleTypes)) {
- Vector3 a = netManager.m_lanes.m_buffer[nextLaneId].CalculatePosition((float)offset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
- float distance = Vector3.Distance(a, b);
- BufferItem nextItem;
+ ++wIter;
+ if (wIter >= 20) {
+ Log.Error("Too many iterations in ProcessItem2!");
+ break;
+ }
+#endif
+
+ if (nextLaneId == curLaneId) {
+ NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[laneIndex];
+ if (nextLaneInfo.CheckType(this.m_laneTypes, this.m_vehicleTypes)) {
+ Vector3 a = netManager.m_lanes.m_buffer[nextLaneId].CalculatePosition((float)offset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+ float distance = Vector3.Distance(a, b);
+ BufferItem nextItem;
#if COUNTSEGMENTSTONEXTJUNCTION
// NON-STOCK CODE START //
if (prevIsRealJunction) {
@@ -1722,323 +1727,323 @@ private void ProcessItemPublicTransport(bool debug, BufferItem item, ushort next
}
// NON-STOCK CODE END //
#endif
- nextItem.m_position.m_segment = nextSegmentId;
- nextItem.m_position.m_lane = (byte)laneIndex;
- nextItem.m_position.m_offset = offset;
- if ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {
- nextItem.m_methodDistance = 0f;
- } else {
- nextItem.m_methodDistance = methodDistance + distance;
- }
-
- float nextMaxSpeed = GetLaneSpeedLimit(nextSegmentId, (byte)laneIndex, curLaneId, nextLaneInfo); // NON-STOCK CODE
- if (nextLaneInfo.m_laneType != NetInfo.LaneType.Pedestrian || nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance || m_stablePath) {
- nextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this.m_maxLength);
- nextItem.m_duration = duration + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);
- if ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {
- nextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);
- } else {
- nextItem.m_direction = nextLaneInfo.m_finalDirection;
- }
-
- if (nextLaneId == this.m_startLaneA) {
- if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < this.m_startOffsetA) &&
- ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > this.m_startOffsetA)) {
- return;
- }
- float nextSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE
- float nextOffsetDistance = (float)Mathf.Abs((int)nextItem.m_position.m_offset - (int)this.m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
- nextItem.m_comparisonValue += nextOffsetDistance * nextSegment.m_averageLength / (nextSpeed * this.m_maxLength);
- nextItem.m_duration += nextOffsetDistance * nextSegment.m_averageLength / nextSpeed;
- }
-
- if (nextLaneId == this.m_startLaneB) {
- if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < this.m_startOffsetB) &&
- ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > this.m_startOffsetB)) {
- return;
- }
- float nextSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE
- float nextOffsetDistance = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetB)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
- nextItem.m_comparisonValue += nextOffsetDistance * nextSegment.m_averageLength / (nextSpeed * this.m_maxLength);
- nextItem.m_duration += nextOffsetDistance * nextSegment.m_averageLength / nextSpeed;
- }
-
- nextItem.m_laneID = nextLaneId;
- nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);
- nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);
- nextItem.m_trafficRand = 0;
-#if DEBUGNEWPF
- if (debug) {
- m_debugPositions[item.m_position.m_segment].Add(nextItem.m_position.m_segment);
- }
-#endif
- this.AddBufferItem(nextItem, item.m_position);
- }
- }
- return;
- }
- curLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;
- ++laneIndex;
- }
- }
-
- private bool ProcessItemCosts(bool debug, BufferItem item, ushort nextNodeId, ushort nextSegmentId, ref NetSegment prevSegment, /*SegmentRoutingData prevSegmentRouting,*/ ref NetSegment nextSegment, ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian, bool isMiddle) {
- bool foundForced = false;
- return ProcessItemCosts(debug, true, LaneChangingCostCalculationMode.None, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref nextSegment, /*routingManager.segmentRoutings[nextSegmentId],*/ ref laneIndexFromInner, connectOffset, enableVehicle, enablePedestrian, null, null, null, null, null, isMiddle, out foundForced);
- }
-
- // 3
- private bool ProcessItemCosts(bool debug, bool obeyStockLaneArrows, LaneChangingCostCalculationMode laneChangingCostCalculationMode, BufferItem item, ushort nextNodeId, ushort nextSegmentId, ref NetSegment prevSegment, /* SegmentRoutingData prevSegmentRouting,*/ ref NetSegment nextSegment, /*SegmentRoutingData nextSegmentRouting,*/ ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian, int? forcedLaneIndex, uint? forcedLaneId, byte? forcedLaneDist, float? segmentSelectionCost, float? laneSelectionCost, bool isMiddle, out bool foundForced) {
+ nextItem.m_position.m_segment = nextSegmentId;
+ nextItem.m_position.m_lane = (byte)laneIndex;
+ nextItem.m_position.m_offset = offset;
+ if ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {
+ nextItem.m_methodDistance = 0f;
+ } else {
+ nextItem.m_methodDistance = methodDistance + distance;
+ }
+
+ float nextMaxSpeed = GetLaneSpeedLimit(nextSegmentId, (byte)laneIndex, curLaneId, nextLaneInfo); // NON-STOCK CODE
+ if (nextLaneInfo.m_laneType != NetInfo.LaneType.Pedestrian || nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance || m_stablePath) {
+ nextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this.m_maxLength);
+ nextItem.m_duration = duration + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);
+ if ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {
+ nextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);
+ } else {
+ nextItem.m_direction = nextLaneInfo.m_finalDirection;
+ }
+
+ if (nextLaneId == this.m_startLaneA) {
+ if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < this.m_startOffsetA) &&
+ ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > this.m_startOffsetA)) {
+ return;
+ }
+ float nextSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE
+ float nextOffsetDistance = (float)Mathf.Abs((int)nextItem.m_position.m_offset - (int)this.m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+ nextItem.m_comparisonValue += nextOffsetDistance * nextSegment.m_averageLength / (nextSpeed * this.m_maxLength);
+ nextItem.m_duration += nextOffsetDistance * nextSegment.m_averageLength / nextSpeed;
+ }
+
+ if (nextLaneId == this.m_startLaneB) {
+ if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < this.m_startOffsetB) &&
+ ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > this.m_startOffsetB)) {
+ return;
+ }
+ float nextSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE
+ float nextOffsetDistance = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetB)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+ nextItem.m_comparisonValue += nextOffsetDistance * nextSegment.m_averageLength / (nextSpeed * this.m_maxLength);
+ nextItem.m_duration += nextOffsetDistance * nextSegment.m_averageLength / nextSpeed;
+ }
+
+ nextItem.m_laneID = nextLaneId;
+ nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);
+ nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);
+ nextItem.m_trafficRand = 0;
+#if DEBUGNEWPF
+ if (debug) {
+ m_debugPositions[item.m_position.m_segment].Add(nextItem.m_position.m_segment);
+ }
+#endif
+ this.AddBufferItem(nextItem, item.m_position);
+ }
+ }
+ return;
+ }
+ curLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;
+ ++laneIndex;
+ }
+ }
+
+ private bool ProcessItemCosts(bool debug, BufferItem item, ushort nextNodeId, ushort nextSegmentId, ref NetSegment prevSegment, /*SegmentRoutingData prevSegmentRouting,*/ ref NetSegment nextSegment, ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian, bool isMiddle) {
+ bool foundForced = false;
+ return ProcessItemCosts(debug, true, LaneChangingCostCalculationMode.None, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref nextSegment, /*routingManager.segmentRoutings[nextSegmentId],*/ ref laneIndexFromInner, connectOffset, enableVehicle, enablePedestrian, null, null, null, null, null, isMiddle, out foundForced);
+ }
+
+ // 3
+ private bool ProcessItemCosts(bool debug, bool obeyStockLaneArrows, LaneChangingCostCalculationMode laneChangingCostCalculationMode, BufferItem item, ushort nextNodeId, ushort nextSegmentId, ref NetSegment prevSegment, /* SegmentRoutingData prevSegmentRouting,*/ ref NetSegment nextSegment, /*SegmentRoutingData nextSegmentRouting,*/ ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian, int? forcedLaneIndex, uint? forcedLaneId, byte? forcedLaneDist, float? segmentSelectionCost, float? laneSelectionCost, bool isMiddle, out bool foundForced) {
#if DEBUGNEWPF && DEBUG
- debug = debug && m_conf.Debug.Switches[1];
+ debug = debug && m_conf.Debug.Switches[1];
#else
debug = false;
#endif
#if DEBUGNEWPF && DEBUG
- List logBuf = null;
- if (debug)
- logBuf = new List();
+ List logBuf = null;
+ if (debug)
+ logBuf = new List();
#endif
- foundForced = false;
- bool blocked = false;
- if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {
+ foundForced = false;
+ bool blocked = false;
+ if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {
#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"Segment is PathFailed or flooded: {nextSegment.m_flags}");
- logBuf.Add("-- method returns --");
- FlushCostLog(logBuf);
- }
+ if (debug) {
+ logBuf.Add($"Segment is PathFailed or flooded: {nextSegment.m_flags}");
+ logBuf.Add("-- method returns --");
+ FlushCostLog(logBuf);
+ }
#endif
- return blocked;
- }
- NetManager netManager = Singleton.instance;
+ return blocked;
+ }
+ NetManager netManager = Singleton.instance;
- NetInfo nextSegmentInfo = nextSegment.Info;
- NetInfo prevSegmentInfo = prevSegment.Info;
- int nextNumLanes = nextSegmentInfo.m_lanes.Length;
- NetInfo.Direction nextDir = (nextNodeId != nextSegment.m_startNode) ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;
- NetInfo.Direction nextFinalDir = ((nextSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? nextDir : NetInfo.InvertDirection(nextDir);
- float turningAngle = 1f;
+ NetInfo nextSegmentInfo = nextSegment.Info;
+ NetInfo prevSegmentInfo = prevSegment.Info;
+ int nextNumLanes = nextSegmentInfo.m_lanes.Length;
+ NetInfo.Direction nextDir = (nextNodeId != nextSegment.m_startNode) ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;
+ NetInfo.Direction nextFinalDir = ((nextSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? nextDir : NetInfo.InvertDirection(nextDir);
+ float turningAngle = 1f;
#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"isStockLaneChangerUsed={Options.isStockLaneChangerUsed()}, _extVehicleType={queueItem.vehicleType}, nonBus={(queueItem.vehicleType & (ExtVehicleType.RoadVehicle & ~ExtVehicleType.Bus)) != ExtVehicleType.None}, _stablePath={m_stablePath}, enablePedestrian={enablePedestrian}, enableVehicle={enableVehicle}");
+ if (debug)
+ logBuf.Add($"isStockLaneChangerUsed={Options.isStockLaneChangerUsed()}, _extVehicleType={queueItem.vehicleType}, nonBus={(queueItem.vehicleType & (ExtVehicleType.RoadVehicle & ~ExtVehicleType.Bus)) != ExtVehicleType.None}, _stablePath={m_stablePath}, enablePedestrian={enablePedestrian}, enableVehicle={enableVehicle}");
#endif
- float prevMaxSpeed = 1f;
- float prevLaneSpeed = 1f;
- NetInfo.LaneType prevLaneType = NetInfo.LaneType.None;
- VehicleInfo.VehicleType prevVehicleType = VehicleInfo.VehicleType.None;
- // NON-STOCK CODE START //
- bool nextIsStartNodeOfPrevSegment = prevSegment.m_startNode == nextNodeId;
- ushort sourceNodeId = nextIsStartNodeOfPrevSegment ? prevSegment.m_endNode : prevSegment.m_startNode;
- bool prevIsJunction = (netManager.m_nodes.m_buffer[sourceNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction;
+ float prevMaxSpeed = 1f;
+ float prevLaneSpeed = 1f;
+ NetInfo.LaneType prevLaneType = NetInfo.LaneType.None;
+ VehicleInfo.VehicleType prevVehicleType = VehicleInfo.VehicleType.None;
+ // NON-STOCK CODE START //
+ bool nextIsStartNodeOfPrevSegment = prevSegment.m_startNode == nextNodeId;
+ ushort sourceNodeId = nextIsStartNodeOfPrevSegment ? prevSegment.m_endNode : prevSegment.m_startNode;
+ bool prevIsJunction = (netManager.m_nodes.m_buffer[sourceNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction;
#if COUNTSEGMENTSTONEXTJUNCTION
bool prevIsRealJunction = prevIsJunction &&
(netManager.m_nodes.m_buffer[sourceNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);
#endif
- /*bool nextIsRealJunction = (netManager.m_nodes.m_buffer[nextNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction &&
- (netManager.m_nodes.m_buffer[nextNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);*/
- int prevOuterSimilarLaneIndex = -1;
- NetInfo.Lane prevLaneInfo = null;
- // NON-STOCK CODE END //
- if ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
- prevLaneInfo = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];
- prevLaneType = prevLaneInfo.m_laneType;
- prevVehicleType = prevLaneInfo.m_vehicleType;
- prevMaxSpeed = GetLaneSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, prevLaneInfo); // NON-STOCK CODE
- prevLaneSpeed = this.CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // NON-STOCK CODE
-
- // NON-STOCK CODE START
- if ((byte)(prevLaneInfo.m_direction & NetInfo.Direction.Forward) != 0) {
- prevOuterSimilarLaneIndex = prevLaneInfo.m_similarLaneCount - prevLaneInfo.m_similarLaneIndex - 1;
- } else {
- prevOuterSimilarLaneIndex = prevLaneInfo.m_similarLaneIndex;
- }
- // NON-STOCK CODE END
- }
-
- if (prevLaneType == NetInfo.LaneType.Vehicle && (prevVehicleType & VehicleInfo.VehicleType.Car) == VehicleInfo.VehicleType.None) {
- // check turning angle
-
- turningAngle = 0.01f - Mathf.Min(nextSegmentInfo.m_maxTurnAngleCos, prevSegmentInfo.m_maxTurnAngleCos);
- if (turningAngle < 1f) {
- Vector3 prevDirection;
- if (nextNodeId == prevSegment.m_startNode) {
- prevDirection = prevSegment.m_startDirection;
- } else {
- prevDirection = prevSegment.m_endDirection;
- }
- Vector3 nextDirection;
- if ((nextDir & NetInfo.Direction.Forward) != NetInfo.Direction.None) {
- nextDirection = nextSegment.m_endDirection;
- } else {
- nextDirection = nextSegment.m_startDirection;
- }
- float dirDotProd = prevDirection.x * nextDirection.x + prevDirection.z * nextDirection.z;
- if (dirDotProd >= turningAngle) {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"turningAngle < 1f! dirDotProd={dirDotProd} >= turningAngle{turningAngle}!");
- logBuf.Add("-- method returns --");
- FlushCostLog(logBuf);
- }
-#endif
- return blocked;
- }
- }
- }
-
- float prevDist;
- if (prevLaneType == NetInfo.LaneType.PublicTransport) {
- prevDist = netManager.m_lanes.m_buffer[item.m_laneID].m_length;
- } else {
- prevDist = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, prevSegment.m_averageLength);
- }
-
- float prevCost = prevDist;
-
-
-#if DEBUGNEWPF
- float oldPrevCost = prevCost;
-#endif
-
- // NON-STOCK CODE START
- if (segmentSelectionCost != null) {
- prevCost *= (float)segmentSelectionCost;
- }
-
- if (laneSelectionCost != null) {
- prevCost *= (float)laneSelectionCost;
- }
- // NON-STOCK CODE END
-
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"applied traffic cost factors:\n" +
- "\t" + $"oldPrevCost={oldPrevCost}\n" +
- "\t" + $"=> prevCost={prevCost}\n"
- );
-#endif
-
- // stock code check for vehicle ban policies removed
- // stock code for transport lane usage control removed
-
- // calculate ticket costs
- float ticketCosts = 0f;
- if (! this.m_ignoreCost) {
- int ticketCost = netManager.m_lanes.m_buffer[item.m_laneID].m_ticketCost;
- if (ticketCost != 0) {
- ticketCosts += (float)(ticketCost * this.m_pathRandomizer.Int32(2000u)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;
- }
- }
-
- if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
- prevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
- }
- float prevOffsetCost = (float)Mathf.Abs((int)(connectOffset - item.m_position.m_offset)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevCost;
- float prevMethodDist = item.m_methodDistance + (float)Mathf.Abs((int)connectOffset - (int)item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevDist;
- float prevDuration = item.m_duration + prevOffsetCost / prevMaxSpeed;
- // NON-STOCK: vehicle restriction are applied to previous segment length in MainPathFind (not here, and not to prevOffsetCost)
- float prevComparisonPlusOffsetCostOverSpeed = item.m_comparisonValue + prevOffsetCost / (prevLaneSpeed * this.m_maxLength);
-
- if (!m_stablePath) {
- // CO randomization. Only randomizes over segments, not over lanes.
- if (segmentSelectionCost == null) { // NON-STOCK CODE
- Randomizer randomizer = new Randomizer(this.m_pathFindIndex << 16 | (uint)item.m_position.m_segment);
- prevOffsetCost *= (float)(randomizer.Int32(900, 1000 + (int)(prevSegment.m_trafficDensity * 10)) + this.m_pathRandomizer.Int32(20u)) * 0.001f;
- }
- }
-
- Vector3 prevLaneConnectPos = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
- int newLaneIndexFromInner = laneIndexFromInner;
- bool transitionNode = (netManager.m_nodes.m_buffer[nextNodeId].m_flags & NetNode.Flags.Transition) != NetNode.Flags.None;
- NetInfo.LaneType allowedLaneTypes = this.m_laneTypes;
- VehicleInfo.VehicleType allowedVehicleTypes = this.m_vehicleTypes;
+ /*bool nextIsRealJunction = (netManager.m_nodes.m_buffer[nextNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction &&
+ (netManager.m_nodes.m_buffer[nextNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);*/
+ int prevOuterSimilarLaneIndex = -1;
+ NetInfo.Lane prevLaneInfo = null;
+ // NON-STOCK CODE END //
+ if ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
+ prevLaneInfo = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];
+ prevLaneType = prevLaneInfo.m_laneType;
+ prevVehicleType = prevLaneInfo.m_vehicleType;
+ prevMaxSpeed = GetLaneSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, prevLaneInfo); // NON-STOCK CODE
+ prevLaneSpeed = this.CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // NON-STOCK CODE
+
+ // NON-STOCK CODE START
+ if ((byte)(prevLaneInfo.m_direction & NetInfo.Direction.Forward) != 0) {
+ prevOuterSimilarLaneIndex = prevLaneInfo.m_similarLaneCount - prevLaneInfo.m_similarLaneIndex - 1;
+ } else {
+ prevOuterSimilarLaneIndex = prevLaneInfo.m_similarLaneIndex;
+ }
+ // NON-STOCK CODE END
+ }
+
+ if (prevLaneType == NetInfo.LaneType.Vehicle && (prevVehicleType & VehicleInfo.VehicleType.Car) == VehicleInfo.VehicleType.None) {
+ // check turning angle
+
+ turningAngle = 0.01f - Mathf.Min(nextSegmentInfo.m_maxTurnAngleCos, prevSegmentInfo.m_maxTurnAngleCos);
+ if (turningAngle < 1f) {
+ Vector3 prevDirection;
+ if (nextNodeId == prevSegment.m_startNode) {
+ prevDirection = prevSegment.m_startDirection;
+ } else {
+ prevDirection = prevSegment.m_endDirection;
+ }
+ Vector3 nextDirection;
+ if ((nextDir & NetInfo.Direction.Forward) != NetInfo.Direction.None) {
+ nextDirection = nextSegment.m_endDirection;
+ } else {
+ nextDirection = nextSegment.m_startDirection;
+ }
+ float dirDotProd = prevDirection.x * nextDirection.x + prevDirection.z * nextDirection.z;
+ if (dirDotProd >= turningAngle) {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add($"turningAngle < 1f! dirDotProd={dirDotProd} >= turningAngle{turningAngle}!");
+ logBuf.Add("-- method returns --");
+ FlushCostLog(logBuf);
+ }
+#endif
+ return blocked;
+ }
+ }
+ }
+
+ float prevDist;
+ if (prevLaneType == NetInfo.LaneType.PublicTransport) {
+ prevDist = netManager.m_lanes.m_buffer[item.m_laneID].m_length;
+ } else {
+ prevDist = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, prevSegment.m_averageLength);
+ }
+
+ float prevCost = prevDist;
+
+
+#if DEBUGNEWPF
+ float oldPrevCost = prevCost;
+#endif
+
+ // NON-STOCK CODE START
+ if (segmentSelectionCost != null) {
+ prevCost *= (float)segmentSelectionCost;
+ }
+
+ if (laneSelectionCost != null) {
+ prevCost *= (float)laneSelectionCost;
+ }
+ // NON-STOCK CODE END
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"applied traffic cost factors:\n" +
+ "\t" + $"oldPrevCost={oldPrevCost}\n" +
+ "\t" + $"=> prevCost={prevCost}\n"
+ );
+#endif
+
+ // stock code check for vehicle ban policies removed
+ // stock code for transport lane usage control removed
+
+ // calculate ticket costs
+ float ticketCosts = 0f;
+ if (! this.m_ignoreCost) {
+ int ticketCost = netManager.m_lanes.m_buffer[item.m_laneID].m_ticketCost;
+ if (ticketCost != 0) {
+ ticketCosts += (float)(ticketCost * this.m_pathRandomizer.Int32(2000u)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;
+ }
+ }
+
+ if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
+ prevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
+ }
+ float prevOffsetCost = (float)Mathf.Abs((int)(connectOffset - item.m_position.m_offset)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevCost;
+ float prevMethodDist = item.m_methodDistance + (float)Mathf.Abs((int)connectOffset - (int)item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevDist;
+ float prevDuration = item.m_duration + prevOffsetCost / prevMaxSpeed;
+ // NON-STOCK: vehicle restriction are applied to previous segment length in MainPathFind (not here, and not to prevOffsetCost)
+ float prevComparisonPlusOffsetCostOverSpeed = item.m_comparisonValue + prevOffsetCost / (prevLaneSpeed * this.m_maxLength);
+
+ if (!m_stablePath) {
+ // CO randomization. Only randomizes over segments, not over lanes.
+ if (segmentSelectionCost == null) { // NON-STOCK CODE
+ Randomizer randomizer = new Randomizer(this.m_pathFindIndex << 16 | (uint)item.m_position.m_segment);
+ prevOffsetCost *= (float)(randomizer.Int32(900, 1000 + (int)(prevSegment.m_trafficDensity * 10)) + this.m_pathRandomizer.Int32(20u)) * 0.001f;
+ }
+ }
+
+ Vector3 prevLaneConnectPos = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+ int newLaneIndexFromInner = laneIndexFromInner;
+ bool transitionNode = (netManager.m_nodes.m_buffer[nextNodeId].m_flags & NetNode.Flags.Transition) != NetNode.Flags.None;
+ NetInfo.LaneType allowedLaneTypes = this.m_laneTypes;
+ VehicleInfo.VehicleType allowedVehicleTypes = this.m_vehicleTypes;
+
+ if (!enableVehicle) {
+ allowedVehicleTypes &= VehicleInfo.VehicleType.Bicycle;
+ if (allowedVehicleTypes == VehicleInfo.VehicleType.None) {
+ allowedLaneTypes &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
+ }
+ }
+ if (!enablePedestrian) {
+ allowedLaneTypes &= ~NetInfo.LaneType.Pedestrian;
+ }
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"allowedVehicleTypes={allowedVehicleTypes} allowedLaneTypes={allowedLaneTypes}");
+#endif
+
+ // NON-STOCK CODE START //
+ float laneChangeBaseCosts = 1f;
+ float junctionBaseCosts = 1f;
+ if (laneChangingCostCalculationMode != LaneChangingCostCalculationMode.None) {
+ float rand = (float)this.m_pathRandomizer.Int32(101u) / 100f;
+ laneChangeBaseCosts = m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost + rand * (m_conf.AdvancedVehicleAI.LaneChangingBaseMaxCost - m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost);
+ if (prevIsJunction) {
+ junctionBaseCosts = m_conf.AdvancedVehicleAI.LaneChangingJunctionBaseCost;
+ }
+ }
- if (!enableVehicle) {
- allowedVehicleTypes &= VehicleInfo.VehicleType.Bicycle;
- if (allowedVehicleTypes == VehicleInfo.VehicleType.None) {
- allowedLaneTypes &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
- }
- }
- if (!enablePedestrian) {
- allowedLaneTypes &= ~NetInfo.LaneType.Pedestrian;
- }
+ // NON-STOCK CODE END //
+ uint laneIndex = forcedLaneIndex != null ? (uint)forcedLaneIndex : 0u; // NON-STOCK CODE, forcedLaneIndex is not null if the next node is a (real) junction
+ uint curLaneId = (uint)(forcedLaneId != null ? forcedLaneId : nextSegment.m_lanes); // NON-STOCK CODE, forceLaneId is not null if the next node is a (real) junction
+ while (laneIndex < nextNumLanes && curLaneId != 0u) {
+ // NON-STOCK CODE START //
+ if (forcedLaneIndex != null && laneIndex != forcedLaneIndex) {
#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"allowedVehicleTypes={allowedVehicleTypes} allowedLaneTypes={allowedLaneTypes}");
+ if (debug)
+ logBuf.Add($"forceLaneIndex break! laneIndex={laneIndex}");
#endif
+ break;
+ }
+ // NON-STOCK CODE END //
+ NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[laneIndex];
- // NON-STOCK CODE START //
- float laneChangeBaseCosts = 1f;
- float junctionBaseCosts = 1f;
- if (laneChangingCostCalculationMode != LaneChangingCostCalculationMode.None) {
- float rand = (float)this.m_pathRandomizer.Int32(101u) / 100f;
- laneChangeBaseCosts = m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost + rand * (m_conf.AdvancedVehicleAI.LaneChangingBaseMaxCost - m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost);
- if (prevIsJunction) {
- junctionBaseCosts = m_conf.AdvancedVehicleAI.LaneChangingJunctionBaseCost;
- }
- }
-
- // NON-STOCK CODE END //
-
- uint laneIndex = forcedLaneIndex != null ? (uint)forcedLaneIndex : 0u; // NON-STOCK CODE, forcedLaneIndex is not null if the next node is a (real) junction
- uint curLaneId = (uint)(forcedLaneId != null ? forcedLaneId : nextSegment.m_lanes); // NON-STOCK CODE, forceLaneId is not null if the next node is a (real) junction
- while (laneIndex < nextNumLanes && curLaneId != 0u) {
- // NON-STOCK CODE START //
- if (forcedLaneIndex != null && laneIndex != forcedLaneIndex) {
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"forceLaneIndex break! laneIndex={laneIndex}");
-#endif
- break;
- }
- // NON-STOCK CODE END //
- NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[laneIndex];
-
- if ((byte)(nextLaneInfo.m_finalDirection & nextFinalDir) != 0) {
- // lane direction is compatible
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"Lane direction check passed: {nextLaneInfo.m_finalDirection}");
-#endif
- if (nextLaneInfo.CheckType(allowedLaneTypes, allowedVehicleTypes) &&
- (nextSegmentId != item.m_position.m_segment || laneIndex != (int)item.m_position.m_lane)) {
- // vehicle types match and no u-turn to the previous lane
-
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"vehicle type check passed: {nextLaneInfo.CheckType(allowedLaneTypes, allowedVehicleTypes)} && {(nextSegmentId != item.m_position.m_segment || laneIndex != (int)item.m_position.m_lane)}");
-#endif
-
- // NON-STOCK CODE START //
- float nextMaxSpeed = GetLaneSpeedLimit(nextSegmentId, (byte)laneIndex, curLaneId, nextLaneInfo);
- float customDeltaCost = 0f;
- // NON-STOCK CODE END //
-
- Vector3 nextLaneEndPointPos;
- if ((nextDir & NetInfo.Direction.Forward) != NetInfo.Direction.None) {
- nextLaneEndPointPos = netManager.m_lanes.m_buffer[curLaneId].m_bezier.d;
- } else {
- nextLaneEndPointPos = netManager.m_lanes.m_buffer[curLaneId].m_bezier.a;
- }
- float transitionCost = Vector3.Distance(nextLaneEndPointPos, prevLaneConnectPos); // This gives the distance of the previous to next lane endpoints.
-
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"costs from {nextSegmentId} (off {(byte)(((nextDir & NetInfo.Direction.Forward) == 0) ? 0 : 255)}) to {item.m_position.m_segment} (off {item.m_position.m_offset}), connectOffset={connectOffset}: transitionCost={transitionCost}");
-#endif
-
- if (transitionNode) {
- transitionCost *= 2f;
- }
-
- BufferItem nextItem;
+ if ((byte)(nextLaneInfo.m_finalDirection & nextFinalDir) != 0) {
+ // lane direction is compatible
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"Lane direction check passed: {nextLaneInfo.m_finalDirection}");
+#endif
+ if (nextLaneInfo.CheckType(allowedLaneTypes, allowedVehicleTypes) &&
+ (nextSegmentId != item.m_position.m_segment || laneIndex != (int)item.m_position.m_lane)) {
+ // vehicle types match and no u-turn to the previous lane
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"vehicle type check passed: {nextLaneInfo.CheckType(allowedLaneTypes, allowedVehicleTypes)} && {(nextSegmentId != item.m_position.m_segment || laneIndex != (int)item.m_position.m_lane)}");
+#endif
+
+ // NON-STOCK CODE START //
+ float nextMaxSpeed = GetLaneSpeedLimit(nextSegmentId, (byte)laneIndex, curLaneId, nextLaneInfo);
+ float customDeltaCost = 0f;
+ // NON-STOCK CODE END //
+
+ Vector3 nextLaneEndPointPos;
+ if ((nextDir & NetInfo.Direction.Forward) != NetInfo.Direction.None) {
+ nextLaneEndPointPos = netManager.m_lanes.m_buffer[curLaneId].m_bezier.d;
+ } else {
+ nextLaneEndPointPos = netManager.m_lanes.m_buffer[curLaneId].m_bezier.a;
+ }
+ float transitionCost = Vector3.Distance(nextLaneEndPointPos, prevLaneConnectPos); // This gives the distance of the previous to next lane endpoints.
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"costs from {nextSegmentId} (off {(byte)(((nextDir & NetInfo.Direction.Forward) == 0) ? 0 : 255)}) to {item.m_position.m_segment} (off {item.m_position.m_offset}), connectOffset={connectOffset}: transitionCost={transitionCost}");
+#endif
+
+ if (transitionNode) {
+ transitionCost *= 2f;
+ }
+
+ BufferItem nextItem;
#if COUNTSEGMENTSTONEXTJUNCTION
// NON-STOCK CODE START //
if (prevIsRealJunction) {
@@ -2048,200 +2053,200 @@ private bool ProcessItemCosts(bool debug, bool obeyStockLaneArrows, LaneChanging
}
// NON-STOCK CODE END //
#endif
- nextItem.m_comparisonValue = ticketCosts;
- nextItem.m_position.m_segment = nextSegmentId;
- nextItem.m_position.m_lane = (byte)laneIndex;
- nextItem.m_position.m_offset = (byte)(((nextDir & NetInfo.Direction.Forward) == 0) ? 0 : 255);
- if ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {
- nextItem.m_methodDistance = 0f;
- // NON-STOCK CODE START
- if (Options.realisticPublicTransport && isMiddle && nextLaneInfo.m_laneType == NetInfo.LaneType.PublicTransport && (item.m_lanesUsed & NetInfo.LaneType.PublicTransport) != NetInfo.LaneType.None) {
- // apply penalty when switching public transport vehicles
- float transportTransitionPenalty = (m_conf.PathFinding.PublicTransportTransitionMinPenalty + ((float)netManager.m_nodes.m_buffer[nextNodeId].m_maxWaitTime * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR) * (m_conf.PathFinding.PublicTransportTransitionMaxPenalty - m_conf.PathFinding.PublicTransportTransitionMinPenalty)) / (0.25f * this.m_maxLength);
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"applying public transport transition penalty: {transportTransitionPenalty}");
-#endif
- nextItem.m_comparisonValue += transportTransitionPenalty;
- }
- // NON-STOCK CODE END
- } else {
- nextItem.m_methodDistance = prevMethodDist + transitionCost;
- }
+ nextItem.m_comparisonValue = ticketCosts;
+ nextItem.m_position.m_segment = nextSegmentId;
+ nextItem.m_position.m_lane = (byte)laneIndex;
+ nextItem.m_position.m_offset = (byte)(((nextDir & NetInfo.Direction.Forward) == 0) ? 0 : 255);
+ if ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {
+ nextItem.m_methodDistance = 0f;
+ // NON-STOCK CODE START
+ if (Options.realisticPublicTransport && isMiddle && nextLaneInfo.m_laneType == NetInfo.LaneType.PublicTransport && (item.m_lanesUsed & NetInfo.LaneType.PublicTransport) != NetInfo.LaneType.None) {
+ // apply penalty when switching public transport vehicles
+ float transportTransitionPenalty = (m_conf.PathFinding.PublicTransportTransitionMinPenalty + ((float)netManager.m_nodes.m_buffer[nextNodeId].m_maxWaitTime * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR) * (m_conf.PathFinding.PublicTransportTransitionMaxPenalty - m_conf.PathFinding.PublicTransportTransitionMinPenalty)) / (0.25f * this.m_maxLength);
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"applying public transport transition penalty: {transportTransitionPenalty}");
+#endif
+ nextItem.m_comparisonValue += transportTransitionPenalty;
+ }
+ // NON-STOCK CODE END
+ } else {
+ nextItem.m_methodDistance = prevMethodDist + transitionCost;
+ }
#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"checking if methodDistance is in range: {nextLaneInfo.m_laneType != NetInfo.LaneType.Pedestrian} || {nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance} ({nextItem.m_methodDistance})");
+ if (debug)
+ logBuf.Add($"checking if methodDistance is in range: {nextLaneInfo.m_laneType != NetInfo.LaneType.Pedestrian} || {nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance} ({nextItem.m_methodDistance})");
#endif
- if (nextLaneInfo.m_laneType != NetInfo.LaneType.Pedestrian || nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance || m_stablePath) {
+ if (nextLaneInfo.m_laneType != NetInfo.LaneType.Pedestrian || nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance || m_stablePath) {
- // NON-STOCK CODE START //
- if (laneChangingCostCalculationMode == LaneChangingCostCalculationMode.None) {
- float transitionCostOverMeanMaxSpeed = transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this.m_maxLength);
- nextItem.m_comparisonValue += prevComparisonPlusOffsetCostOverSpeed + transitionCostOverMeanMaxSpeed; // stock code
- } else {
- nextItem.m_comparisonValue += item.m_comparisonValue;
- customDeltaCost = transitionCost + prevOffsetCost; // customDeltaCost now holds the costs for driving on the segment + costs for changing the segment
+ // NON-STOCK CODE START //
+ if (laneChangingCostCalculationMode == LaneChangingCostCalculationMode.None) {
+ float transitionCostOverMeanMaxSpeed = transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this.m_maxLength);
+ nextItem.m_comparisonValue += prevComparisonPlusOffsetCostOverSpeed + transitionCostOverMeanMaxSpeed; // stock code
+ } else {
+ nextItem.m_comparisonValue += item.m_comparisonValue;
+ customDeltaCost = transitionCost + prevOffsetCost; // customDeltaCost now holds the costs for driving on the segment + costs for changing the segment
#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"Path from {nextSegmentId} (idx {laneIndex}, id {curLaneId}) to {item.m_position.m_segment} (lane {prevOuterSimilarLaneIndex} from outer, idx {item.m_position.m_lane}): laneChangingCostCalculationMode={laneChangingCostCalculationMode}, transitionCost={transitionCost}");
- }
+ if (debug) {
+ logBuf.Add($"Path from {nextSegmentId} (idx {laneIndex}, id {curLaneId}) to {item.m_position.m_segment} (lane {prevOuterSimilarLaneIndex} from outer, idx {item.m_position.m_lane}): laneChangingCostCalculationMode={laneChangingCostCalculationMode}, transitionCost={transitionCost}");
+ }
#endif
- }
- nextItem.m_duration = prevDuration + transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);
+ }
+ nextItem.m_duration = prevDuration + transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);
- // account for public tranport transition costs on non-PT paths
- if (
+ // account for public tranport transition costs on non-PT paths
+ if (
#if DEBUG
- !m_conf.Debug.Switches[20] &&
+ !m_conf.Debug.Switches[20] &&
#endif
- Options.realisticPublicTransport &&
- (curLaneId == this.m_startLaneA || curLaneId == this.m_startLaneB) &&
- (item.m_lanesUsed & (NetInfo.LaneType.Pedestrian | NetInfo.LaneType.PublicTransport)) == NetInfo.LaneType.Pedestrian) {
- float transportTransitionPenalty = (2f * m_conf.PathFinding.PublicTransportTransitionMaxPenalty) / (0.25f * this.m_maxLength);
+ Options.realisticPublicTransport &&
+ (curLaneId == this.m_startLaneA || curLaneId == this.m_startLaneB) &&
+ (item.m_lanesUsed & (NetInfo.LaneType.Pedestrian | NetInfo.LaneType.PublicTransport)) == NetInfo.LaneType.Pedestrian) {
+ float transportTransitionPenalty = (2f * m_conf.PathFinding.PublicTransportTransitionMaxPenalty) / (0.25f * this.m_maxLength);
#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"applying public transport transition penalty on non-PT path: {transportTransitionPenalty}");
+ if (debug)
+ logBuf.Add($"applying public transport transition penalty on non-PT path: {transportTransitionPenalty}");
#endif
- nextItem.m_comparisonValue += transportTransitionPenalty;
- }
- // NON-STOCK CODE END //
+ nextItem.m_comparisonValue += transportTransitionPenalty;
+ }
+ // NON-STOCK CODE END //
- nextItem.m_direction = nextDir;
- if (curLaneId == this.m_startLaneA) {
- if (((byte)(nextItem.m_direction & NetInfo.Direction.Forward) == 0 || nextItem.m_position.m_offset < this.m_startOffsetA) && ((byte)(nextItem.m_direction & NetInfo.Direction.Backward) == 0 || nextItem.m_position.m_offset > this.m_startOffsetA)) {
+ nextItem.m_direction = nextDir;
+ if (curLaneId == this.m_startLaneA) {
+ if (((byte)(nextItem.m_direction & NetInfo.Direction.Forward) == 0 || nextItem.m_position.m_offset < this.m_startOffsetA) && ((byte)(nextItem.m_direction & NetInfo.Direction.Backward) == 0 || nextItem.m_position.m_offset > this.m_startOffsetA)) {
#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"Current lane is start lane A. goto next lane");
-#endif
- goto CONTINUE_LANE_LOOP;
- }
- float nextLaneSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE
- float nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetA)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
- float nextSegLength = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, nextSegment.m_averageLength);
- nextItem.m_comparisonValue += nextOffset * nextSegLength / (nextLaneSpeed * this.m_maxLength);
- nextItem.m_duration += nextOffset * nextSegLength / nextLaneSpeed;
- }
-
- if (curLaneId == this.m_startLaneB) {
- if (((byte)(nextItem.m_direction & NetInfo.Direction.Forward) == 0 || nextItem.m_position.m_offset < this.m_startOffsetB) && ((byte)(nextItem.m_direction & NetInfo.Direction.Backward) == 0 || nextItem.m_position.m_offset > this.m_startOffsetB)) {
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"Current lane is start lane B. goto next lane");
-#endif
- goto CONTINUE_LANE_LOOP;
- }
- float nextLaneSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE
- float nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetB)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
- float nextSegLength = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, nextSegment.m_averageLength);
- nextItem.m_comparisonValue += nextOffset * nextSegLength / (nextLaneSpeed * this.m_maxLength);
- nextItem.m_duration += nextOffset * nextSegLength / nextLaneSpeed;
- }
-
- if (!this.m_ignoreBlocked && (nextSegment.m_flags & NetSegment.Flags.Blocked) != NetSegment.Flags.None &&
- (byte)(nextLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {
- // NON-STOCK CODE START //
- if (laneChangingCostCalculationMode != LaneChangingCostCalculationMode.None) {
-#if DEBUGNEWPF
- float oldCustomDeltaCost = customDeltaCost;
-#endif
- // apply vanilla game restriction policies
- customDeltaCost *= 10f;
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- "\t" + $"applied blocked road cost factor on activated AI:\n" +
- "\t" + $"oldCustomDeltaCost={oldCustomDeltaCost}\n" +
- "\t" + $"=> customDeltaCost={customDeltaCost}\n"
- );
-#endif
- } else {
- // NON-STOCK CODE END //
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"Applying blocked road cost factor on disabled advanced AI");
-#endif
-
- nextItem.m_comparisonValue += 0.1f;
- }
- blocked = true;
- }
-
- if ((byte)(nextLaneInfo.m_laneType & prevLaneType) != 0 && nextLaneInfo.m_vehicleType == prevVehicleType) {
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"Applying stock lane changing costs. obeyStockLaneArrows={obeyStockLaneArrows}");
-#endif
-
-
- if (obeyStockLaneArrows) {
- // this is CO's way of matching lanes between segments
- int firstTarget = (int)netManager.m_lanes.m_buffer[curLaneId].m_firstTarget;
- int lastTarget = (int)netManager.m_lanes.m_buffer[curLaneId].m_lastTarget;
- if (laneIndexFromInner < firstTarget || laneIndexFromInner >= lastTarget) {
- nextItem.m_comparisonValue += Mathf.Max(1f, transitionCost * 3f - 3f) / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this.m_maxLength);
- }
- } // NON-STOCK CODE
-
- // stock code that prohibits cars to be on public transport lanes removed
- /*if (!this._transportVehicle && nextLaneInfo.m_laneType == NetInfo.LaneType.TransportVehicle) {
- nextItem.m_comparisonValue += 20f / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this._maxLength);
- }*/
-
- }
-
- // NON-STOCK CODE START //
- bool addItem = true; // should we add the next item to the buffer?
- if (laneChangingCostCalculationMode != LaneChangingCostCalculationMode.None) {
- // Advanced AI cost calculation
-
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"Calculating advanced AI lane changing costs");
-#endif
-
- int laneDist; // absolute lane distance
- if (laneChangingCostCalculationMode == LaneChangingCostCalculationMode.ByGivenDistance && forcedLaneDist != null) {
- laneDist = (byte)forcedLaneDist;
- } else {
- int nextOuterSimilarLaneIndex;
- if ((byte)(nextLaneInfo.m_direction & NetInfo.Direction.Forward) != 0) {
- nextOuterSimilarLaneIndex = nextLaneInfo.m_similarLaneCount - nextLaneInfo.m_similarLaneIndex - 1;
- } else {
- nextOuterSimilarLaneIndex = nextLaneInfo.m_similarLaneIndex;
- }
-
- int relLaneDist = nextOuterSimilarLaneIndex - prevOuterSimilarLaneIndex; // relative lane distance (positive: change to more outer lane, negative: change to more inner lane)
- laneDist = Math.Abs(relLaneDist);
- }
-
- // apply lane changing costs
- float laneMetric = 1f;
- bool relaxedLaneChanging = queueItem.vehicleId == 0 && (curLaneId == m_startLaneA || curLaneId == m_startLaneB);
- if (laneDist > 0 && !relaxedLaneChanging) {
- laneMetric = 1f + laneDist *
- junctionBaseCosts *
- laneChangeBaseCosts * // road type based lane changing cost factor
- (laneDist > 1 ? m_conf.AdvancedVehicleAI.MoreThanOneLaneChangingCostFactor : 1f); // additional costs for changing multiple lanes at once
- }
-
- // on highways: avoid lane changing before junctions: multiply with inverted distance to next junction
- /*float junctionMetric = 1f;
- if (prevSegmentRouting.highway && nextSegmentRouting.highway && // we are on a highway road
- !nextIsRealJunction && // next is not a junction
- laneDist > 0) {
- uint dist = _pathRandomizer.UInt32(_conf.MinHighwayInterchangeSegments, (_conf.MaxHighwayInterchangeSegments >= _conf.MinHighwayInterchangeSegments ? _conf.MaxHighwayInterchangeSegments : _conf.MinHighwayInterchangeSegments) + 1u);
- if (nextItem.m_numSegmentsToNextJunction < dist) {
- junctionMetric = _conf.HighwayInterchangeLaneChangingBaseCost * (float)(dist - nextItem.m_numSegmentsToNextJunction);
- }
- }*/
-
- // total metric value
- float metric = laneMetric/* * junctionMetric*/;
-
- //float oldTransitionDistanceOverMaxSpeed = transitionCostOverMeanMaxSpeed;
- float finalDeltaCost = (metric * customDeltaCost) / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this.m_maxLength);
+ if (debug)
+ logBuf.Add($"Current lane is start lane A. goto next lane");
+#endif
+ goto CONTINUE_LANE_LOOP;
+ }
+ float nextLaneSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE
+ float nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetA)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+ float nextSegLength = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, nextSegment.m_averageLength);
+ nextItem.m_comparisonValue += nextOffset * nextSegLength / (nextLaneSpeed * this.m_maxLength);
+ nextItem.m_duration += nextOffset * nextSegLength / nextLaneSpeed;
+ }
+
+ if (curLaneId == this.m_startLaneB) {
+ if (((byte)(nextItem.m_direction & NetInfo.Direction.Forward) == 0 || nextItem.m_position.m_offset < this.m_startOffsetB) && ((byte)(nextItem.m_direction & NetInfo.Direction.Backward) == 0 || nextItem.m_position.m_offset > this.m_startOffsetB)) {
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"Current lane is start lane B. goto next lane");
+#endif
+ goto CONTINUE_LANE_LOOP;
+ }
+ float nextLaneSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE
+ float nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetB)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+ float nextSegLength = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, nextSegment.m_averageLength);
+ nextItem.m_comparisonValue += nextOffset * nextSegLength / (nextLaneSpeed * this.m_maxLength);
+ nextItem.m_duration += nextOffset * nextSegLength / nextLaneSpeed;
+ }
+
+ if (!this.m_ignoreBlocked && (nextSegment.m_flags & NetSegment.Flags.Blocked) != NetSegment.Flags.None &&
+ (byte)(nextLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {
+ // NON-STOCK CODE START //
+ if (laneChangingCostCalculationMode != LaneChangingCostCalculationMode.None) {
+#if DEBUGNEWPF
+ float oldCustomDeltaCost = customDeltaCost;
+#endif
+ // apply vanilla game restriction policies
+ customDeltaCost *= 10f;
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ "\t" + $"applied blocked road cost factor on activated AI:\n" +
+ "\t" + $"oldCustomDeltaCost={oldCustomDeltaCost}\n" +
+ "\t" + $"=> customDeltaCost={customDeltaCost}\n"
+ );
+#endif
+ } else {
+ // NON-STOCK CODE END //
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"Applying blocked road cost factor on disabled advanced AI");
+#endif
+
+ nextItem.m_comparisonValue += 0.1f;
+ }
+ blocked = true;
+ }
+
+ if ((byte)(nextLaneInfo.m_laneType & prevLaneType) != 0 && nextLaneInfo.m_vehicleType == prevVehicleType) {
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"Applying stock lane changing costs. obeyStockLaneArrows={obeyStockLaneArrows}");
+#endif
+
+
+ if (obeyStockLaneArrows) {
+ // this is CO's way of matching lanes between segments
+ int firstTarget = (int)netManager.m_lanes.m_buffer[curLaneId].m_firstTarget;
+ int lastTarget = (int)netManager.m_lanes.m_buffer[curLaneId].m_lastTarget;
+ if (laneIndexFromInner < firstTarget || laneIndexFromInner >= lastTarget) {
+ nextItem.m_comparisonValue += Mathf.Max(1f, transitionCost * 3f - 3f) / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this.m_maxLength);
+ }
+ } // NON-STOCK CODE
+
+ // stock code that prohibits cars to be on public transport lanes removed
+ /*if (!this._transportVehicle && nextLaneInfo.m_laneType == NetInfo.LaneType.TransportVehicle) {
+ nextItem.m_comparisonValue += 20f / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this._maxLength);
+ }*/
+
+ }
+
+ // NON-STOCK CODE START //
+ bool addItem = true; // should we add the next item to the buffer?
+ if (laneChangingCostCalculationMode != LaneChangingCostCalculationMode.None) {
+ // Advanced AI cost calculation
+
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"Calculating advanced AI lane changing costs");
+#endif
+
+ int laneDist; // absolute lane distance
+ if (laneChangingCostCalculationMode == LaneChangingCostCalculationMode.ByGivenDistance && forcedLaneDist != null) {
+ laneDist = (byte)forcedLaneDist;
+ } else {
+ int nextOuterSimilarLaneIndex;
+ if ((byte)(nextLaneInfo.m_direction & NetInfo.Direction.Forward) != 0) {
+ nextOuterSimilarLaneIndex = nextLaneInfo.m_similarLaneCount - nextLaneInfo.m_similarLaneIndex - 1;
+ } else {
+ nextOuterSimilarLaneIndex = nextLaneInfo.m_similarLaneIndex;
+ }
+
+ int relLaneDist = nextOuterSimilarLaneIndex - prevOuterSimilarLaneIndex; // relative lane distance (positive: change to more outer lane, negative: change to more inner lane)
+ laneDist = Math.Abs(relLaneDist);
+ }
+
+ // apply lane changing costs
+ float laneMetric = 1f;
+ bool relaxedLaneChanging = queueItem.vehicleId == 0 && (curLaneId == m_startLaneA || curLaneId == m_startLaneB);
+ if (laneDist > 0 && !relaxedLaneChanging) {
+ laneMetric = 1f + laneDist *
+ junctionBaseCosts *
+ laneChangeBaseCosts * // road type based lane changing cost factor
+ (laneDist > 1 ? m_conf.AdvancedVehicleAI.MoreThanOneLaneChangingCostFactor : 1f); // additional costs for changing multiple lanes at once
+ }
+
+ // on highways: avoid lane changing before junctions: multiply with inverted distance to next junction
+ /*float junctionMetric = 1f;
+ if (prevSegmentRouting.highway && nextSegmentRouting.highway && // we are on a highway road
+ !nextIsRealJunction && // next is not a junction
+ laneDist > 0) {
+ uint dist = _pathRandomizer.UInt32(_conf.MinHighwayInterchangeSegments, (_conf.MaxHighwayInterchangeSegments >= _conf.MinHighwayInterchangeSegments ? _conf.MaxHighwayInterchangeSegments : _conf.MinHighwayInterchangeSegments) + 1u);
+ if (nextItem.m_numSegmentsToNextJunction < dist) {
+ junctionMetric = _conf.HighwayInterchangeLaneChangingBaseCost * (float)(dist - nextItem.m_numSegmentsToNextJunction);
+ }
+ }*/
+
+ // total metric value
+ float metric = laneMetric/* * junctionMetric*/;
+
+ //float oldTransitionDistanceOverMaxSpeed = transitionCostOverMeanMaxSpeed;
+ float finalDeltaCost = (metric * customDeltaCost) / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this.m_maxLength);
// if (finalDeltaCost < 0f) {
// // should never happen
@@ -2261,245 +2266,245 @@ private bool ProcessItemCosts(bool debug, bool obeyStockLaneArrows, LaneChanging
// finalDeltaCost = oldTransitionDistanceOverMaxSpeed;
// }
- nextItem.m_comparisonValue += finalDeltaCost;
-
- if (nextItem.m_comparisonValue > 1f) {
- // comparison value got too big. Do not add the lane to the buffer
- addItem = false;
- }
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- $"-> TRANSIT to seg. {nextSegmentId}, lane {laneIndex}\n" +
- "\t" + $"prevMaxSpeed={prevMaxSpeed}\n" +
- "\t" + $"nextMaxSpeed={nextMaxSpeed}\n\n" +
- "\t" + $"laneChangingCostCalculationMode={laneChangingCostCalculationMode}\n\n" +
- "\t" + $"laneDist={laneDist}\n\n" +
- "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
- "\t" + $"laneChangeRoadBaseCost={laneChangeBaseCosts}\n" +
- "\t" + $"moreThanOneLaneCost={(laneDist > 1 ? m_conf.AdvancedVehicleAI.MoreThanOneLaneChangingCostFactor : 1f)}\n" +
- "\t" + $"=> laneMetric={laneMetric}\n" +
- //"\t" + $"=> junctionMetric={junctionMetric}\n\n" +
- "\t" + $"=> metric={metric}\n" +
- "\t" + $"deltaCostOverMeanMaxSpeed={finalDeltaCost}\n" +
- "\t" + $"nextItem.m_comparisonValue={nextItem.m_comparisonValue}\n\n" +
- "\t" + $"=> addItem={addItem}\n"
- );
-#endif
- }
-
- if (forcedLaneIndex != null && laneIndex == forcedLaneIndex && addItem) {
- foundForced = true;
- }
-
- if (addItem) {
- // NON-STOCK CODE END //
-
- nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);
- nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);
- nextItem.m_laneID = curLaneId;
- nextItem.m_trafficRand = item.m_trafficRand;
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"adding item: seg {nextItem.m_position.m_segment}, lane {nextItem.m_position.m_lane} (idx {nextItem.m_laneID}), off {nextItem.m_position.m_offset} -> seg {item.m_position.m_segment}, lane {item.m_position.m_lane} (idx {item.m_laneID}), off {item.m_position.m_offset}, cost {nextItem.m_comparisonValue}, previous cost {item.m_comparisonValue}, methodDist {nextItem.m_methodDistance}");
- m_debugPositions[item.m_position.m_segment].Add(nextItem.m_position.m_segment);
- }
-#endif
-
- this.AddBufferItem(nextItem, item.m_position);
- // NON-STOCK CODE START //
- } else {
-#if DEBUGNEWPF
- if (debug)
- logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
- $"-> item seg. {nextSegmentId}, lane {laneIndex} NOT ADDED\n"
- );
-#endif
- }
- // NON-STOCK CODE END //
- }
- }
- goto CONTINUE_LANE_LOOP;
- }
-
- if ((byte)(nextLaneInfo.m_laneType & prevLaneType) != 0 && (nextLaneInfo.m_vehicleType & prevVehicleType) != VehicleInfo.VehicleType.None) {
- ++newLaneIndexFromInner;
- }
-
- CONTINUE_LANE_LOOP:
-
- curLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;
- ++laneIndex;
- continue;
- } // foreach lane
- laneIndexFromInner = newLaneIndexFromInner;
-
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add("-- method returns --");
- FlushCostLog(logBuf);
- }
-#endif
- return blocked;
- }
-
-#if DEBUGNEWPF
- private void FlushCostLog(List logBuf) {
- if (logBuf == null)
- return;
-
- foreach (String toLog in logBuf) {
- Log._Debug($"Pathfinder ({this.m_pathFindIndex}) for unit {Calculating} *COSTS*: " + toLog);
- }
- logBuf.Clear();
- }
-
- private void FlushMainLog(List logBuf, uint unitId) {
- if (logBuf == null)
- return;
-
- foreach (String toLog in logBuf) {
- Log._Debug($"Pathfinder ({this.m_pathFindIndex}) for unit {Calculating} *MAIN*: " + toLog);
- }
- logBuf.Clear();
- }
-#endif
-
- // 4
- private void ProcessItemPedBicycle(bool debug, BufferItem item, ushort nextNodeId, ushort nextSegmentId, ref NetSegment prevSegment, ref NetSegment nextSegment, byte connectOffset, byte laneSwitchOffset, int nextLaneIndex, uint nextLaneId) {
+ nextItem.m_comparisonValue += finalDeltaCost;
+
+ if (nextItem.m_comparisonValue > 1f) {
+ // comparison value got too big. Do not add the lane to the buffer
+ addItem = false;
+ }
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ $"-> TRANSIT to seg. {nextSegmentId}, lane {laneIndex}\n" +
+ "\t" + $"prevMaxSpeed={prevMaxSpeed}\n" +
+ "\t" + $"nextMaxSpeed={nextMaxSpeed}\n\n" +
+ "\t" + $"laneChangingCostCalculationMode={laneChangingCostCalculationMode}\n\n" +
+ "\t" + $"laneDist={laneDist}\n\n" +
+ "\t" + $"_extVehicleType={queueItem.vehicleType}\n" +
+ "\t" + $"laneChangeRoadBaseCost={laneChangeBaseCosts}\n" +
+ "\t" + $"moreThanOneLaneCost={(laneDist > 1 ? m_conf.AdvancedVehicleAI.MoreThanOneLaneChangingCostFactor : 1f)}\n" +
+ "\t" + $"=> laneMetric={laneMetric}\n" +
+ //"\t" + $"=> junctionMetric={junctionMetric}\n\n" +
+ "\t" + $"=> metric={metric}\n" +
+ "\t" + $"deltaCostOverMeanMaxSpeed={finalDeltaCost}\n" +
+ "\t" + $"nextItem.m_comparisonValue={nextItem.m_comparisonValue}\n\n" +
+ "\t" + $"=> addItem={addItem}\n"
+ );
+#endif
+ }
+
+ if (forcedLaneIndex != null && laneIndex == forcedLaneIndex && addItem) {
+ foundForced = true;
+ }
+
+ if (addItem) {
+ // NON-STOCK CODE END //
+
+ nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);
+ nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);
+ nextItem.m_laneID = curLaneId;
+ nextItem.m_trafficRand = item.m_trafficRand;
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add($"adding item: seg {nextItem.m_position.m_segment}, lane {nextItem.m_position.m_lane} (idx {nextItem.m_laneID}), off {nextItem.m_position.m_offset} -> seg {item.m_position.m_segment}, lane {item.m_position.m_lane} (idx {item.m_laneID}), off {item.m_position.m_offset}, cost {nextItem.m_comparisonValue}, previous cost {item.m_comparisonValue}, methodDist {nextItem.m_methodDistance}");
+ m_debugPositions[item.m_position.m_segment].Add(nextItem.m_position.m_segment);
+ }
+#endif
+
+ this.AddBufferItem(nextItem, item.m_position);
+ // NON-STOCK CODE START //
+ } else {
+#if DEBUGNEWPF
+ if (debug)
+ logBuf.Add($"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\n" +
+ $"-> item seg. {nextSegmentId}, lane {laneIndex} NOT ADDED\n"
+ );
+#endif
+ }
+ // NON-STOCK CODE END //
+ }
+ }
+ goto CONTINUE_LANE_LOOP;
+ }
+
+ if ((byte)(nextLaneInfo.m_laneType & prevLaneType) != 0 && (nextLaneInfo.m_vehicleType & prevVehicleType) != VehicleInfo.VehicleType.None) {
+ ++newLaneIndexFromInner;
+ }
+
+ CONTINUE_LANE_LOOP:
+
+ curLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;
+ ++laneIndex;
+ continue;
+ } // foreach lane
+ laneIndexFromInner = newLaneIndexFromInner;
+
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add("-- method returns --");
+ FlushCostLog(logBuf);
+ }
+#endif
+ return blocked;
+ }
+
+#if DEBUGNEWPF
+ private void FlushCostLog(List logBuf) {
+ if (logBuf == null)
+ return;
+
+ foreach (String toLog in logBuf) {
+ Log._Debug($"Pathfinder ({this.m_pathFindIndex}) for unit {Calculating} *COSTS*: " + toLog);
+ }
+ logBuf.Clear();
+ }
+
+ private void FlushMainLog(List logBuf, uint unitId) {
+ if (logBuf == null)
+ return;
+
+ foreach (String toLog in logBuf) {
+ Log._Debug($"Pathfinder ({this.m_pathFindIndex}) for unit {Calculating} *MAIN*: " + toLog);
+ }
+ logBuf.Clear();
+ }
+#endif
+
+ // 4
+ private void ProcessItemPedBicycle(bool debug, BufferItem item, ushort nextNodeId, ushort nextSegmentId, ref NetSegment prevSegment, ref NetSegment nextSegment, byte connectOffset, byte laneSwitchOffset, int nextLaneIndex, uint nextLaneId) {
#if DEBUGNEWPF && DEBUG
- List logBuf = null;
- if (debug) {
- logBuf = new List();
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: exploring\n" +
- "\t" + $"nextSegmentId={nextSegmentId}\n" +
- "\t" + $"connectOffset={connectOffset}\n" +
- "\t" + $"laneSwitchOffset={laneSwitchOffset}\n" +
- "\t" + $"nextLaneIndex={nextLaneIndex}\n" +
- "\t" + $"nextLaneId={nextLaneId}\n");
- }
-#endif
-
- if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- next segment disabled mask is incompatible!\n" +
- "\t" + $"nextSegment.m_flags={nextSegment.m_flags}\n" +
- "\t" + $"_disableMask={m_disableMask}\n");
- FlushCostLog(logBuf);
- }
-#endif
- return;
- }
-
- NetManager netManager = Singleton.instance;
-
- // NON-STOCK CODE START
- bool nextIsRegularNode = nextNodeId == nextSegment.m_startNode || nextNodeId == nextSegment.m_endNode;
+ List logBuf = null;
+ if (debug) {
+ logBuf = new List();
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: exploring\n" +
+ "\t" + $"nextSegmentId={nextSegmentId}\n" +
+ "\t" + $"connectOffset={connectOffset}\n" +
+ "\t" + $"laneSwitchOffset={laneSwitchOffset}\n" +
+ "\t" + $"nextLaneIndex={nextLaneIndex}\n" +
+ "\t" + $"nextLaneId={nextLaneId}\n");
+ }
+#endif
+
+ if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- next segment disabled mask is incompatible!\n" +
+ "\t" + $"nextSegment.m_flags={nextSegment.m_flags}\n" +
+ "\t" + $"_disableMask={m_disableMask}\n");
+ FlushCostLog(logBuf);
+ }
+#endif
+ return;
+ }
+
+ NetManager netManager = Singleton.instance;
+
+ // NON-STOCK CODE START
+ bool nextIsRegularNode = nextNodeId == nextSegment.m_startNode || nextNodeId == nextSegment.m_endNode;
#if COUNTSEGMENTSTONEXTJUNCTION
bool prevIsRealJunction = false;
#endif
- if (nextIsRegularNode) {
- bool nextIsStartNode = nextNodeId == nextSegment.m_startNode;
- ushort prevNodeId = (nextNodeId == prevSegment.m_startNode) ? prevSegment.m_endNode : prevSegment.m_startNode;
+ if (nextIsRegularNode) {
+ bool nextIsStartNode = nextNodeId == nextSegment.m_startNode;
+ ushort prevNodeId = (nextNodeId == prevSegment.m_startNode) ? prevSegment.m_endNode : prevSegment.m_startNode;
#if COUNTSEGMENTSTONEXTJUNCTION
prevIsRealJunction = (netManager.m_nodes.m_buffer[prevNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) != NetNode.Flags.Junction &&
(netManager.m_nodes.m_buffer[prevNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);
#endif
- // check if pedestrians are not allowed to cross here
- if (!junctionManager.IsPedestrianCrossingAllowed(nextSegmentId, nextIsStartNode)) {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- pedestrian crossing not allowed!\n");
- FlushCostLog(logBuf);
- }
-#endif
- return;
- }
-
- // check if pedestrian light won't change to green
- ICustomSegmentLights lights = customTrafficLightsManager.GetSegmentLights(nextSegmentId, nextIsStartNode, false);
- if (lights != null) {
- if (lights.InvalidPedestrianLight) {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- invalid pedestrian lights!\n");
- FlushCostLog(logBuf);
- }
-#endif
- return;
- }
- }
- }
- // NON-STOCK CODE END
-
- // NON-STOCK CODE START
- /*if (!_allowEscapeTransport) {
- ushort transportLineId = netManager.m_nodes.m_buffer[targetNodeId].m_transportLine;
- if (transportLineId != 0 && Singleton.instance.m_lines.m_buffer[transportLineId].Info.m_transportType == TransportInfo.TransportType.EvacuationBus)
- return;
- }*/
- // NON-STOCK CODE END
- NetInfo nextSegmentInfo = nextSegment.Info;
- NetInfo prevSegmentInfo = prevSegment.Info;
- int numLanes = nextSegmentInfo.m_lanes.Length;
- float distance;
- byte offset;
- Vector3 b = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)laneSwitchOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
- if (nextSegmentId == item.m_position.m_segment) {
- // next segment is previous segment
- Vector3 a = netManager.m_lanes.m_buffer[nextLaneId].CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
- distance = Vector3.Distance(a, b);
- offset = connectOffset;
- } else {
- // next segment differs from previous segment
- NetInfo.Direction direction = (nextNodeId != nextSegment.m_startNode) ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;
- Vector3 a;
- if ((byte)(direction & NetInfo.Direction.Forward) != 0) {
- a = netManager.m_lanes.m_buffer[nextLaneId].m_bezier.d;
- } else {
- a = netManager.m_lanes.m_buffer[nextLaneId].m_bezier.a;
- }
- distance = Vector3.Distance(a, b);
- offset = (byte)(((direction & NetInfo.Direction.Forward) == 0) ? 0 : 255);
- }
- float prevMaxSpeed = 1f;
- float prevSpeed = 1f;
- NetInfo.LaneType laneType = NetInfo.LaneType.None;
- VehicleInfo.VehicleType vehicleType = VehicleInfo.VehicleType.None; // NON-STOCK CODE
- if ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
- NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];
- prevMaxSpeed = GetLaneSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, prevLaneInfo); // SpeedLimitManager.GetLockFreeGameSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, ref lane2); // NON-STOCK CODE
- laneType = prevLaneInfo.m_laneType;
- vehicleType = prevLaneInfo.m_vehicleType; // NON-STOCK CODE
- if ((byte)(laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {
- laneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
- }
- prevSpeed = this.CalculateLaneSpeed(prevMaxSpeed, laneSwitchOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // NON-STOCK CODE
- }
-
- float segLength;
- if (laneType == NetInfo.LaneType.PublicTransport) {
- segLength = netManager.m_lanes.m_buffer[item.m_laneID].m_length;
- } else {
- segLength = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, prevSegment.m_averageLength);
- }
- float offsetLength = (float)Mathf.Abs((int)(laneSwitchOffset - item.m_position.m_offset)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * segLength;
- float methodDistance = item.m_methodDistance + offsetLength;
- float comparisonValue = item.m_comparisonValue + offsetLength / (prevSpeed * this.m_maxLength);
- float duration = item.m_duration + offsetLength / prevMaxSpeed;
-
- if (! this.m_ignoreCost) {
- int ticketCost = netManager.m_lanes.m_buffer[item.m_laneID].m_ticketCost;
- if (ticketCost != 0) {
- comparisonValue += (float)(ticketCost * this.m_pathRandomizer.Int32(2000u)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;
- }
- }
-
- if (nextLaneIndex < numLanes) {
- NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];
- BufferItem nextItem;
+ // check if pedestrians are not allowed to cross here
+ if (!junctionManager.IsPedestrianCrossingAllowed(nextSegmentId, nextIsStartNode)) {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- pedestrian crossing not allowed!\n");
+ FlushCostLog(logBuf);
+ }
+#endif
+ return;
+ }
+
+ // check if pedestrian light won't change to green
+ ICustomSegmentLights lights = customTrafficLightsManager.GetSegmentLights(nextSegmentId, nextIsStartNode, false);
+ if (lights != null) {
+ if (lights.InvalidPedestrianLight) {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- invalid pedestrian lights!\n");
+ FlushCostLog(logBuf);
+ }
+#endif
+ return;
+ }
+ }
+ }
+ // NON-STOCK CODE END
+
+ // NON-STOCK CODE START
+ /*if (!_allowEscapeTransport) {
+ ushort transportLineId = netManager.m_nodes.m_buffer[targetNodeId].m_transportLine;
+ if (transportLineId != 0 && Singleton.instance.m_lines.m_buffer[transportLineId].Info.m_transportType == TransportInfo.TransportType.EvacuationBus)
+ return;
+ }*/
+ // NON-STOCK CODE END
+ NetInfo nextSegmentInfo = nextSegment.Info;
+ NetInfo prevSegmentInfo = prevSegment.Info;
+ int numLanes = nextSegmentInfo.m_lanes.Length;
+ float distance;
+ byte offset;
+ Vector3 b = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)laneSwitchOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+ if (nextSegmentId == item.m_position.m_segment) {
+ // next segment is previous segment
+ Vector3 a = netManager.m_lanes.m_buffer[nextLaneId].CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+ distance = Vector3.Distance(a, b);
+ offset = connectOffset;
+ } else {
+ // next segment differs from previous segment
+ NetInfo.Direction direction = (nextNodeId != nextSegment.m_startNode) ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;
+ Vector3 a;
+ if ((byte)(direction & NetInfo.Direction.Forward) != 0) {
+ a = netManager.m_lanes.m_buffer[nextLaneId].m_bezier.d;
+ } else {
+ a = netManager.m_lanes.m_buffer[nextLaneId].m_bezier.a;
+ }
+ distance = Vector3.Distance(a, b);
+ offset = (byte)(((direction & NetInfo.Direction.Forward) == 0) ? 0 : 255);
+ }
+ float prevMaxSpeed = 1f;
+ float prevSpeed = 1f;
+ NetInfo.LaneType laneType = NetInfo.LaneType.None;
+ VehicleInfo.VehicleType vehicleType = VehicleInfo.VehicleType.None; // NON-STOCK CODE
+ if ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
+ NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];
+ prevMaxSpeed = GetLaneSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, prevLaneInfo); // SpeedLimitManager.GetLockFreeGameSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, ref lane2); // NON-STOCK CODE
+ laneType = prevLaneInfo.m_laneType;
+ vehicleType = prevLaneInfo.m_vehicleType; // NON-STOCK CODE
+ if ((byte)(laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {
+ laneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
+ }
+ prevSpeed = this.CalculateLaneSpeed(prevMaxSpeed, laneSwitchOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // NON-STOCK CODE
+ }
+
+ float segLength;
+ if (laneType == NetInfo.LaneType.PublicTransport) {
+ segLength = netManager.m_lanes.m_buffer[item.m_laneID].m_length;
+ } else {
+ segLength = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, prevSegment.m_averageLength);
+ }
+ float offsetLength = (float)Mathf.Abs((int)(laneSwitchOffset - item.m_position.m_offset)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * segLength;
+ float methodDistance = item.m_methodDistance + offsetLength;
+ float comparisonValue = item.m_comparisonValue + offsetLength / (prevSpeed * this.m_maxLength);
+ float duration = item.m_duration + offsetLength / prevMaxSpeed;
+
+ if (! this.m_ignoreCost) {
+ int ticketCost = netManager.m_lanes.m_buffer[item.m_laneID].m_ticketCost;
+ if (ticketCost != 0) {
+ comparisonValue += (float)(ticketCost * this.m_pathRandomizer.Int32(2000u)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;
+ }
+ }
+
+ if (nextLaneIndex < numLanes) {
+ NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];
+ BufferItem nextItem;
#if COUNTSEGMENTSTONEXTJUNCTION
// NON-STOCK CODE START //
if (prevIsRealJunction) {
@@ -2509,315 +2514,315 @@ private void ProcessItemPedBicycle(bool debug, BufferItem item, ushort nextNodeI
}
// NON-STOCK CODE END //
#endif
- nextItem.m_position.m_segment = nextSegmentId;
- nextItem.m_position.m_lane = (byte)nextLaneIndex;
- nextItem.m_position.m_offset = offset;
- if ((nextLaneInfo.m_laneType & laneType) == NetInfo.LaneType.None) {
- nextItem.m_methodDistance = 0f;
- } else {
- if (item.m_methodDistance == 0f) {
- comparisonValue += 100f / (0.25f * this.m_maxLength);
- }
- nextItem.m_methodDistance = methodDistance + distance;
- }
-
- float nextMaxSpeed = GetLaneSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo); // NON-STOCK CODE
- if (nextLaneInfo.m_laneType != NetInfo.LaneType.Pedestrian || nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance || m_stablePath) {
- nextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.25f * this.m_maxLength);
- nextItem.m_duration = duration + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);
- if ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {
- nextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);
- } else {
- nextItem.m_direction = nextLaneInfo.m_finalDirection;
- }
- if (nextLaneId == this.m_startLaneA) {
- if (((byte)(nextItem.m_direction & NetInfo.Direction.Forward) == 0 || nextItem.m_position.m_offset < this.m_startOffsetA) && ((byte)(nextItem.m_direction & NetInfo.Direction.Backward) == 0 || nextItem.m_position.m_offset > this.m_startOffsetA)) {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- start lane A reached in wrong direction!\n");
- FlushCostLog(logBuf);
- }
-#endif
- return;
- }
- float nextSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE
- float nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetA)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
- nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * this.m_maxLength);
- nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;
- }
- if (nextLaneId == this.m_startLaneB) {
- if (((byte)(nextItem.m_direction & NetInfo.Direction.Forward) == 0 || nextItem.m_position.m_offset < this.m_startOffsetB) && ((byte)(nextItem.m_direction & NetInfo.Direction.Backward) == 0 || nextItem.m_position.m_offset > this.m_startOffsetB)) {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- start lane B reached in wrong direction!\n");
- FlushCostLog(logBuf);
- }
-#endif
- return;
- }
- float nextSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE
- float nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetB)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
- nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * this.m_maxLength);
- nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;
- }
- nextItem.m_laneID = nextLaneId;
- nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);
- nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);
- nextItem.m_trafficRand = 0;
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: *ADDING*\n" +
- "\t" + $"nextItem.m_laneID={nextItem.m_laneID}\n" +
- "\t" + $"nextItem.m_lanesUsed={nextItem.m_lanesUsed}\n" +
- "\t" + $"nextItem.m_vehiclesUsed={nextItem.m_vehiclesUsed }\n" +
- "\t" + $"nextItem.m_comparisonValue={nextItem.m_comparisonValue}\n" +
- "\t" + $"nextItem.m_methodDistance={nextItem.m_methodDistance}\n"
- );
- FlushCostLog(logBuf);
-
- m_debugPositions[item.m_position.m_segment].Add(nextItem.m_position.m_segment);
- }
-#endif
- this.AddBufferItem(nextItem, item.m_position);
- } else {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- lane incompatible or method distance too large!\n" +
- "\t" + $"nextItem.m_methodDistance={nextItem.m_methodDistance}\n" +
- "\t" + $"nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}\n"
- );
- FlushCostLog(logBuf);
- }
-#endif
- }
- } else {
-#if DEBUGNEWPF
- if (debug) {
- logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- nextLaneIndex >= numLanes ({nextLaneIndex} >= {numLanes})!\n");
- FlushCostLog(logBuf);
- }
-#endif
- }
- }
-
- private float CalculateLaneSpeed(float speedLimit, byte startOffset, byte endOffset, ref NetSegment segment, NetInfo.Lane laneInfo) {
- /*if ((laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram)) != VehicleInfo.VehicleType.None)
- speedLimit = laneInfo.m_speedLimit;
- */
- NetInfo.Direction direction = ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? laneInfo.m_finalDirection : NetInfo.InvertDirection(laneInfo.m_finalDirection);
- if ((byte)(direction & NetInfo.Direction.Avoid) == 0) {
- return speedLimit;
- }
- if (endOffset > startOffset && direction == NetInfo.Direction.AvoidForward) {
- return speedLimit * 0.1f;
- }
- if (endOffset < startOffset && direction == NetInfo.Direction.AvoidBackward) {
- return speedLimit * 0.1f;
- }
- return speedLimit * 0.2f;
- }
-
- private void AddBufferItem(BufferItem item, PathUnit.Position target) {
- uint laneLocation = m_laneLocation[item.m_laneID];
- uint locPathFindIndex = laneLocation >> 16; // upper 16 bit, expected (?) path find index
- int bufferIndex = (int)(laneLocation & 65535u); // lower 16 bit
- int comparisonBufferPos;
- if (locPathFindIndex == m_pathFindIndex) {
- if (item.m_comparisonValue >= m_buffer[bufferIndex].m_comparisonValue) {
- return;
- }
-
- int bufferPosIndex = bufferIndex >> 6; // arithmetic shift (sign stays), upper 10 bit
- int bufferPos = bufferIndex & -64; // upper 10 bit (no shift)
- if (bufferPosIndex < m_bufferMinPos || (bufferPosIndex == m_bufferMinPos && bufferPos < m_bufferMin[bufferPosIndex])) {
- return;
- }
-
- comparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);
- if (comparisonBufferPos == bufferPosIndex) {
- m_buffer[bufferIndex] = item;
- m_laneTarget[item.m_laneID] = target;
- return;
- }
-
- int newBufferIndex = bufferPosIndex << 6 | m_bufferMax[bufferPosIndex]--;
- BufferItem bufferItem = m_buffer[newBufferIndex];
- m_laneLocation[bufferItem.m_laneID] = laneLocation;
- m_buffer[bufferIndex] = bufferItem;
- } else {
- comparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);
- }
-
- if (comparisonBufferPos >= 1024) {
- return;
- }
-
- if (comparisonBufferPos < 0) {
- return;
- }
-
- while (m_bufferMax[comparisonBufferPos] == 63) {
- ++comparisonBufferPos;
- if (comparisonBufferPos == 1024) {
- return;
- }
- }
-
- if (comparisonBufferPos > m_bufferMaxPos) {
- m_bufferMaxPos = comparisonBufferPos;
- }
-
- bufferIndex = (comparisonBufferPos << 6 | ++m_bufferMax[comparisonBufferPos]);
- m_buffer[bufferIndex] = item;
- m_laneLocation[item.m_laneID] = (m_pathFindIndex << 16 | (uint)bufferIndex);
- m_laneTarget[item.m_laneID] = target;
- }
-
- private void GetLaneDirection(PathUnit.Position pathPos, out NetInfo.Direction direction, out NetInfo.LaneType laneType, out VehicleInfo.VehicleType vehicleType) {
- NetManager instance = Singleton.instance;
- NetInfo info = instance.m_segments.m_buffer[pathPos.m_segment].Info;
- if (info.m_lanes.Length > pathPos.m_lane) {
- direction = info.m_lanes[pathPos.m_lane].m_finalDirection;
- laneType = info.m_lanes[pathPos.m_lane].m_laneType;
- vehicleType = info.m_lanes[pathPos.m_lane].m_vehicleType;
- if ((instance.m_segments.m_buffer[pathPos.m_segment].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {
- direction = NetInfo.InvertDirection(direction);
- }
- } else {
- direction = NetInfo.Direction.None;
- laneType = NetInfo.LaneType.None;
- vehicleType = VehicleInfo.VehicleType.None;
- }
- }
-
- private void PathFindThread() {
- while (true) {
- //Log.Message($"Pathfind Thread #{Thread.CurrentThread.ManagedThreadId} iteration!");
- try {
- Monitor.Enter(QueueLock);
-
- while (QueueFirst == 0u && !Terminated) {
- Monitor.Wait(QueueLock);
- }
-
- if (Terminated) {
- break;
- }
- Calculating = QueueFirst;
- QueueFirst = CustomPathManager._instance.queueItems[Calculating].nextPathUnitId;
- //QueueFirst = PathUnits.m_buffer[Calculating].m_nextPathUnit;
- if (QueueFirst == 0u) {
- QueueLast = 0u;
- m_queuedPathFindCount = 0;
- } else {
- --m_queuedPathFindCount;
- }
-
- CustomPathManager._instance.queueItems[Calculating].nextPathUnitId = 0u;
- //PathUnits.m_buffer[Calculating].m_nextPathUnit = 0u;
-
- // check if path unit is created
- /*if ((PathUnits.m_buffer[Calculating].m_pathFindFlags & PathUnit.FLAG_CREATED) == 0) {
- Log.Warning($"CustomPathFind: Refusing to calculate path unit {Calculating} which is not created!");
- continue;
- }*/
-
- PathUnits.m_buffer[Calculating].m_pathFindFlags = (byte)((PathUnits.m_buffer[Calculating].m_pathFindFlags & ~PathUnit.FLAG_CREATED) | PathUnit.FLAG_CALCULATING);
- this.queueItem = CustomPathManager._instance.queueItems[Calculating];
- } catch (Exception e) {
- Log.Error($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread Error for unit {Calculating}, flags={PathUnits.m_buffer[Calculating].m_pathFindFlags} (1): {e.ToString()}");
- } finally {
- Monitor.Exit(QueueLock);
- }
-
- // calculate path unit
- try {
- PathFindImplementation(Calculating, ref PathUnits.m_buffer[Calculating]);
- } catch (Exception ex) {
- Log.Error($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread Error for unit {Calculating}, flags={PathUnits.m_buffer[Calculating].m_pathFindFlags} (2): {ex.ToString()}");
- //UIView.ForwardException(ex);
+ nextItem.m_position.m_segment = nextSegmentId;
+ nextItem.m_position.m_lane = (byte)nextLaneIndex;
+ nextItem.m_position.m_offset = offset;
+ if ((nextLaneInfo.m_laneType & laneType) == NetInfo.LaneType.None) {
+ nextItem.m_methodDistance = 0f;
+ } else {
+ if (item.m_methodDistance == 0f) {
+ comparisonValue += 100f / (0.25f * this.m_maxLength);
+ }
+ nextItem.m_methodDistance = methodDistance + distance;
+ }
+
+ float nextMaxSpeed = GetLaneSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo); // NON-STOCK CODE
+ if (nextLaneInfo.m_laneType != NetInfo.LaneType.Pedestrian || nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance || m_stablePath) {
+ nextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.25f * this.m_maxLength);
+ nextItem.m_duration = duration + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);
+ if ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {
+ nextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);
+ } else {
+ nextItem.m_direction = nextLaneInfo.m_finalDirection;
+ }
+ if (nextLaneId == this.m_startLaneA) {
+ if (((byte)(nextItem.m_direction & NetInfo.Direction.Forward) == 0 || nextItem.m_position.m_offset < this.m_startOffsetA) && ((byte)(nextItem.m_direction & NetInfo.Direction.Backward) == 0 || nextItem.m_position.m_offset > this.m_startOffsetA)) {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- start lane A reached in wrong direction!\n");
+ FlushCostLog(logBuf);
+ }
+#endif
+ return;
+ }
+ float nextSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE
+ float nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetA)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+ nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * this.m_maxLength);
+ nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;
+ }
+ if (nextLaneId == this.m_startLaneB) {
+ if (((byte)(nextItem.m_direction & NetInfo.Direction.Forward) == 0 || nextItem.m_position.m_offset < this.m_startOffsetB) && ((byte)(nextItem.m_direction & NetInfo.Direction.Backward) == 0 || nextItem.m_position.m_offset > this.m_startOffsetB)) {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- start lane B reached in wrong direction!\n");
+ FlushCostLog(logBuf);
+ }
+#endif
+ return;
+ }
+ float nextSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE
+ float nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetB)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+ nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * this.m_maxLength);
+ nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;
+ }
+ nextItem.m_laneID = nextLaneId;
+ nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);
+ nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);
+ nextItem.m_trafficRand = 0;
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: *ADDING*\n" +
+ "\t" + $"nextItem.m_laneID={nextItem.m_laneID}\n" +
+ "\t" + $"nextItem.m_lanesUsed={nextItem.m_lanesUsed}\n" +
+ "\t" + $"nextItem.m_vehiclesUsed={nextItem.m_vehiclesUsed }\n" +
+ "\t" + $"nextItem.m_comparisonValue={nextItem.m_comparisonValue}\n" +
+ "\t" + $"nextItem.m_methodDistance={nextItem.m_methodDistance}\n"
+ );
+ FlushCostLog(logBuf);
+
+ m_debugPositions[item.m_position.m_segment].Add(nextItem.m_position.m_segment);
+ }
+#endif
+ this.AddBufferItem(nextItem, item.m_position);
+ } else {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- lane incompatible or method distance too large!\n" +
+ "\t" + $"nextItem.m_methodDistance={nextItem.m_methodDistance}\n" +
+ "\t" + $"nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}\n"
+ );
+ FlushCostLog(logBuf);
+ }
+#endif
+ }
+ } else {
+#if DEBUGNEWPF
+ if (debug) {
+ logBuf.Add($"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- nextLaneIndex >= numLanes ({nextLaneIndex} >= {numLanes})!\n");
+ FlushCostLog(logBuf);
+ }
+#endif
+ }
+ }
+
+ private float CalculateLaneSpeed(float speedLimit, byte startOffset, byte endOffset, ref NetSegment segment, NetInfo.Lane laneInfo) {
+ /*if ((laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram)) != VehicleInfo.VehicleType.None)
+ speedLimit = laneInfo.m_speedLimit;
+ */
+ NetInfo.Direction direction = ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? laneInfo.m_finalDirection : NetInfo.InvertDirection(laneInfo.m_finalDirection);
+ if ((byte)(direction & NetInfo.Direction.Avoid) == 0) {
+ return speedLimit;
+ }
+ if (endOffset > startOffset && direction == NetInfo.Direction.AvoidForward) {
+ return speedLimit * 0.1f;
+ }
+ if (endOffset < startOffset && direction == NetInfo.Direction.AvoidBackward) {
+ return speedLimit * 0.1f;
+ }
+ return speedLimit * 0.2f;
+ }
+
+ private void AddBufferItem(BufferItem item, PathUnit.Position target) {
+ uint laneLocation = m_laneLocation[item.m_laneID];
+ uint locPathFindIndex = laneLocation >> 16; // upper 16 bit, expected (?) path find index
+ int bufferIndex = (int)(laneLocation & 65535u); // lower 16 bit
+ int comparisonBufferPos;
+ if (locPathFindIndex == m_pathFindIndex) {
+ if (item.m_comparisonValue >= m_buffer[bufferIndex].m_comparisonValue) {
+ return;
+ }
+
+ int bufferPosIndex = bufferIndex >> 6; // arithmetic shift (sign stays), upper 10 bit
+ int bufferPos = bufferIndex & -64; // upper 10 bit (no shift)
+ if (bufferPosIndex < m_bufferMinPos || (bufferPosIndex == m_bufferMinPos && bufferPos < m_bufferMin[bufferPosIndex])) {
+ return;
+ }
+
+ comparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);
+ if (comparisonBufferPos == bufferPosIndex) {
+ m_buffer[bufferIndex] = item;
+ m_laneTarget[item.m_laneID] = target;
+ return;
+ }
+
+ int newBufferIndex = bufferPosIndex << 6 | m_bufferMax[bufferPosIndex]--;
+ BufferItem bufferItem = m_buffer[newBufferIndex];
+ m_laneLocation[bufferItem.m_laneID] = laneLocation;
+ m_buffer[bufferIndex] = bufferItem;
+ } else {
+ comparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);
+ }
+
+ if (comparisonBufferPos >= 1024) {
+ return;
+ }
+
+ if (comparisonBufferPos < 0) {
+ return;
+ }
+
+ while (m_bufferMax[comparisonBufferPos] == 63) {
+ ++comparisonBufferPos;
+ if (comparisonBufferPos == 1024) {
+ return;
+ }
+ }
+
+ if (comparisonBufferPos > m_bufferMaxPos) {
+ m_bufferMaxPos = comparisonBufferPos;
+ }
+
+ bufferIndex = (comparisonBufferPos << 6 | ++m_bufferMax[comparisonBufferPos]);
+ m_buffer[bufferIndex] = item;
+ m_laneLocation[item.m_laneID] = (m_pathFindIndex << 16 | (uint)bufferIndex);
+ m_laneTarget[item.m_laneID] = target;
+ }
+
+ private void GetLaneDirection(PathUnit.Position pathPos, out NetInfo.Direction direction, out NetInfo.LaneType laneType, out VehicleInfo.VehicleType vehicleType) {
+ NetManager instance = Singleton.instance;
+ NetInfo info = instance.m_segments.m_buffer[pathPos.m_segment].Info;
+ if (info.m_lanes.Length > pathPos.m_lane) {
+ direction = info.m_lanes[pathPos.m_lane].m_finalDirection;
+ laneType = info.m_lanes[pathPos.m_lane].m_laneType;
+ vehicleType = info.m_lanes[pathPos.m_lane].m_vehicleType;
+ if ((instance.m_segments.m_buffer[pathPos.m_segment].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {
+ direction = NetInfo.InvertDirection(direction);
+ }
+ } else {
+ direction = NetInfo.Direction.None;
+ laneType = NetInfo.LaneType.None;
+ vehicleType = VehicleInfo.VehicleType.None;
+ }
+ }
+
+ private void PathFindThread() {
+ while (true) {
+ //Log.Message($"Pathfind Thread #{Thread.CurrentThread.ManagedThreadId} iteration!");
+ try {
+ Monitor.Enter(QueueLock);
+
+ while (QueueFirst == 0u && !Terminated) {
+ Monitor.Wait(QueueLock);
+ }
+
+ if (Terminated) {
+ break;
+ }
+ Calculating = QueueFirst;
+ QueueFirst = CustomPathManager._instance.queueItems[Calculating].nextPathUnitId;
+ //QueueFirst = PathUnits.m_buffer[Calculating].m_nextPathUnit;
+ if (QueueFirst == 0u) {
+ QueueLast = 0u;
+ m_queuedPathFindCount = 0;
+ } else {
+ --m_queuedPathFindCount;
+ }
+
+ CustomPathManager._instance.queueItems[Calculating].nextPathUnitId = 0u;
+ //PathUnits.m_buffer[Calculating].m_nextPathUnit = 0u;
+
+ // check if path unit is created
+ /*if ((PathUnits.m_buffer[Calculating].m_pathFindFlags & PathUnit.FLAG_CREATED) == 0) {
+ Log.Warning($"CustomPathFind: Refusing to calculate path unit {Calculating} which is not created!");
+ continue;
+ }*/
+
+ PathUnits.m_buffer[Calculating].m_pathFindFlags = (byte)((PathUnits.m_buffer[Calculating].m_pathFindFlags & ~PathUnit.FLAG_CREATED) | PathUnit.FLAG_CALCULATING);
+ this.queueItem = CustomPathManager._instance.queueItems[Calculating];
+ } catch (Exception e) {
+ Log.Error($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread Error for unit {Calculating}, flags={PathUnits.m_buffer[Calculating].m_pathFindFlags} (1): {e.ToString()}");
+ } finally {
+ Monitor.Exit(QueueLock);
+ }
+
+ // calculate path unit
+ try {
+ PathFindImplementation(Calculating, ref PathUnits.m_buffer[Calculating]);
+ } catch (Exception ex) {
+ Log.Error($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread Error for unit {Calculating}, flags={PathUnits.m_buffer[Calculating].m_pathFindFlags} (2): {ex.ToString()}");
+ //UIView.ForwardException(ex);
#if DEBUG
- ++m_failedPathFinds;
+ ++m_failedPathFinds;
#if DEBUGNEWPF
- bool debug = this.m_debug;
- if (debug)
- Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Could not find path for unit {Calculating} -- exception occurred in PathFindImplementation");
+ bool debug = this.m_debug;
+ if (debug)
+ Log._Debug($"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Could not find path for unit {Calculating} -- exception occurred in PathFindImplementation");
#endif
#endif
- //CustomPathManager._instance.ResetQueueItem(Calculating);
+ //CustomPathManager._instance.ResetQueueItem(Calculating);
- PathUnits.m_buffer[Calculating].m_pathFindFlags |= PathUnit.FLAG_FAILED;
- } finally {
- }
- //tCurrentState = 10;
+ PathUnits.m_buffer[Calculating].m_pathFindFlags |= PathUnit.FLAG_FAILED;
+ } finally {
+ }
+ //tCurrentState = 10;
#if DEBUGLOCKS
lockIter = 0;
#endif
- try {
- Monitor.Enter(QueueLock);
-
- PathUnits.m_buffer[Calculating].m_pathFindFlags = (byte)(PathUnits.m_buffer[Calculating].m_pathFindFlags & ~PathUnit.FLAG_CALCULATING);
-
- // NON-STOCK CODE START
- try {
- Monitor.Enter(_bufferLock);
- CustomPathManager._instance.queueItems[Calculating].queued = false;
- CustomPathManager._instance.ReleasePath(Calculating);
- } finally {
- Monitor.Exit(this._bufferLock);
- }
- // NON-STOCK CODE END
-
- Calculating = 0u;
- Monitor.Pulse(QueueLock);
- } catch (Exception e) {
- Log.Error($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread Error for unit {Calculating}, flags={PathUnits.m_buffer[Calculating].m_pathFindFlags} (3): {e.ToString()}");
- } finally {
- Monitor.Exit(QueueLock);
- }
- }
- }
-
- ///
- /// Determines if a given lane may be used by the vehicle whose path is currently being calculated.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- protected virtual bool CanUseLane(bool debug, ushort segmentId, NetInfo segmentInfo, uint laneIndex, NetInfo.Lane laneInfo) {
- if (!Options.vehicleRestrictionsEnabled)
- return true;
-
- if (queueItem.vehicleType == ExtVehicleType.None || queueItem.vehicleType == ExtVehicleType.Tram)
- return true;
-
- /*if (laneInfo == null)
- laneInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info.m_lanes[laneIndex];*/
-
- if ((laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) == VehicleInfo.VehicleType.None)
- return true;
-
- ExtVehicleType allowedTypes = vehicleRestrictionsManager.GetAllowedVehicleTypes(segmentId, segmentInfo, laneIndex, laneInfo, VehicleRestrictionsMode.Configured);
-
- return ((allowedTypes & queueItem.vehicleType) != ExtVehicleType.None);
- }
-
- ///
- /// Determines the speed limit for the given lane.
- ///
- ///
- ///
- ///
- ///
- ///
- protected virtual float GetLaneSpeedLimit(ushort segmentId, byte laneIndex, uint laneId, NetInfo.Lane lane) {
- return Options.customSpeedLimitsEnabled ? speedLimitManager.GetLockFreeGameSpeedLimit(segmentId, laneIndex, laneId, lane) : lane.m_speedLimit;
- }
- }
-}
+ try {
+ Monitor.Enter(QueueLock);
+
+ PathUnits.m_buffer[Calculating].m_pathFindFlags = (byte)(PathUnits.m_buffer[Calculating].m_pathFindFlags & ~PathUnit.FLAG_CALCULATING);
+
+ // NON-STOCK CODE START
+ try {
+ Monitor.Enter(_bufferLock);
+ CustomPathManager._instance.queueItems[Calculating].queued = false;
+ CustomPathManager._instance.ReleasePath(Calculating);
+ } finally {
+ Monitor.Exit(this._bufferLock);
+ }
+ // NON-STOCK CODE END
+
+ Calculating = 0u;
+ Monitor.Pulse(QueueLock);
+ } catch (Exception e) {
+ Log.Error($"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindThread Error for unit {Calculating}, flags={PathUnits.m_buffer[Calculating].m_pathFindFlags} (3): {e.ToString()}");
+ } finally {
+ Monitor.Exit(QueueLock);
+ }
+ }
+ }
+
+ ///
+ /// Determines if a given lane may be used by the vehicle whose path is currently being calculated.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected virtual bool CanUseLane(bool debug, ushort segmentId, NetInfo segmentInfo, uint laneIndex, NetInfo.Lane laneInfo) {
+ if (!Options.vehicleRestrictionsEnabled)
+ return true;
+
+ if (queueItem.vehicleType == ExtVehicleType.None || queueItem.vehicleType == ExtVehicleType.Tram)
+ return true;
+
+ /*if (laneInfo == null)
+ laneInfo = Singleton.instance.m_segments.m_buffer[segmentId].Info.m_lanes[laneIndex];*/
+
+ if ((laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) == VehicleInfo.VehicleType.None)
+ return true;
+
+ ExtVehicleType allowedTypes = vehicleRestrictionsManager.GetAllowedVehicleTypes(segmentId, segmentInfo, laneIndex, laneInfo, VehicleRestrictionsMode.Configured);
+
+ return ((allowedTypes & queueItem.vehicleType) != ExtVehicleType.None);
+ }
+
+ ///
+ /// Determines the speed limit for the given lane.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected virtual float GetLaneSpeedLimit(ushort segmentId, byte laneIndex, uint laneId, NetInfo.Lane lane) {
+ return Options.customSpeedLimitsEnabled ? speedLimitManager.GetLockFreeGameSpeedLimit(segmentId, laneIndex, laneId, lane) : lane.m_speedLimit;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TLM/TLM/Custom/PathFinding/CustomPathFind2.cs b/TLM/TLM/Custom/PathFinding/CustomPathFind2.cs
index 04b8d486f..2b6d75223 100644
--- a/TLM/TLM/Custom/PathFinding/CustomPathFind2.cs
+++ b/TLM/TLM/Custom/PathFinding/CustomPathFind2.cs
@@ -1,3296 +1,3310 @@
-using ColossalFramework;
-using ColossalFramework.Math;
-using ColossalFramework.UI;
-using CSUtil.Commons;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading;
-using TrafficManager.Manager;
-using TrafficManager.Manager.Impl;
-using TrafficManager.RedirectionFramework.Attributes;
-using TrafficManager.State;
-using TrafficManager.Traffic;
-using TrafficManager.Traffic.Data;
-using TrafficManager.Traffic.Enums;
-using TrafficManager.TrafficLight;
-using UnityEngine;
-using static TrafficManager.Custom.PathFinding.CustomPathManager;
-
-namespace TrafficManager.Custom.PathFinding {
+namespace TrafficManager.Custom.PathFinding {
+ using System;
+ using System.Collections.Generic;
+ using System.Reflection;
+ using System.Threading;
+ using API.Traffic.Enums;
+ using API.TrafficLight;
+ using ColossalFramework;
+ using ColossalFramework.Math;
+ using ColossalFramework.UI;
+ using CSUtil.Commons;
+ using Manager;
+ using Manager.Impl;
+ using RedirectionFramework.Attributes;
+ using State;
+ using Traffic.Data;
+ using Traffic.Enums;
+ using UnityEngine;
+
#if PF2
- [TargetType(typeof(PathFind))]
+ [TargetType(typeof(PathFind))]
#endif
- public class CustomPathFind2 : PathFind {
- private const float BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR = 0.003921569f;
- private const float TICKET_COST_CONVERSION_FACTOR = BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;
+ public class CustomPathFind2 : PathFind {
+ private const float BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR = 0.003921569f;
+ private const float TICKET_COST_CONVERSION_FACTOR = BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;
#if ROUTING
- private readonly RoutingManager m_routingManager = RoutingManager.Instance;
+ private readonly RoutingManager m_routingManager = RoutingManager.Instance;
#endif
#if JUNCTIONRESTRICTIONS
- private readonly JunctionRestrictionsManager m_junctionManager = JunctionRestrictionsManager.Instance;
+ private readonly JunctionRestrictionsManager m_junctionManager = JunctionRestrictionsManager.Instance;
#endif
#if VEHICLERESTRICTIONS
- private readonly VehicleRestrictionsManager m_vehicleRestrictionsManager = VehicleRestrictionsManager.Instance;
+ private readonly VehicleRestrictionsManager m_vehicleRestrictionsManager = VehicleRestrictionsManager.Instance;
#endif
#if SPEEDLIMITS
- private readonly SpeedLimitManager m_speedLimitManager = SpeedLimitManager.Instance;
+ private readonly SpeedLimitManager m_speedLimitManager = SpeedLimitManager.Instance;
#endif
#if CUSTOMTRAFFICLIGHTS
- private readonly CustomSegmentLightsManager m_customTrafficLightsManager = CustomSegmentLightsManager.Instance;
+ private readonly CustomSegmentLightsManager m_customTrafficLightsManager = CustomSegmentLightsManager.Instance;
#endif
#if ADVANCEDAI && ROUTING
- private readonly TrafficMeasurementManager m_trafficMeasurementManager = TrafficMeasurementManager.Instance;
-#endif
- private GlobalConfig m_conf = null;
-
- private struct BufferItem {
- public PathUnit.Position m_position;
- public float m_comparisonValue;
- public float m_methodDistance;
- public float m_duration;
- public uint m_laneID;
- public NetInfo.Direction m_direction;
- public NetInfo.LaneType m_lanesUsed;
+ private readonly TrafficMeasurementManager m_trafficMeasurementManager = TrafficMeasurementManager.Instance;
+#endif
+ private GlobalConfig m_conf = null;
+
+ private struct BufferItem {
+ public PathUnit.Position m_position;
+ public float m_comparisonValue;
+ public float m_methodDistance;
+ public float m_duration;
+ public uint m_laneID;
+ public NetInfo.Direction m_direction;
+ public NetInfo.LaneType m_lanesUsed;
#if PARKINGAI
- public VehicleInfo.VehicleType m_vehiclesUsed;
+ public VehicleInfo.VehicleType m_vehiclesUsed;
#endif
#if ADVANCEDAI && ROUTING
- public float m_trafficRand;
-#endif
- public override string ToString() {
- return $"[BufferItem\n" +
- "\t" + $"m_position=(s#({m_position.m_segment}), l#({m_position.m_lane}), o#({m_position.m_offset}))\n" +
- "\t" + $"m_laneID={m_laneID}\n" +
- "\t" + $"m_comparisonValue={m_comparisonValue}\n" +
- "\t" + $"m_methodDistance={m_methodDistance}\n" +
- "\t" + $"m_duration={m_duration}\n" +
- "\t" + $"m_direction={m_direction}\n" +
- "\t" + $"m_lanesUsed={m_lanesUsed}\n" +
+ public float m_trafficRand;
+#endif
+ public override string ToString() {
+ return $"[BufferItem\n" +
+ "\t" + $"m_position=(s#({m_position.m_segment}), l#({m_position.m_lane}), o#({m_position.m_offset}))\n" +
+ "\t" + $"m_laneID={m_laneID}\n" +
+ "\t" + $"m_comparisonValue={m_comparisonValue}\n" +
+ "\t" + $"m_methodDistance={m_methodDistance}\n" +
+ "\t" + $"m_duration={m_duration}\n" +
+ "\t" + $"m_direction={m_direction}\n" +
+ "\t" + $"m_lanesUsed={m_lanesUsed}\n" +
#if PARKINGAI
- "\t" + $"m_vehiclesUsed={m_vehiclesUsed}\n" +
+ "\t" + $"m_vehiclesUsed={m_vehiclesUsed}\n" +
#endif
#if ADVANCEDAI && ROUTING
- "\t" + $"m_trafficRand={m_trafficRand}\n" +
-#endif
- "BufferItem]";
- }
- }
-
- private enum LaneChangingCostCalculationMode {
- None,
- ByLaneDistance,
- ByGivenDistance
- }
-
- // private stock fields
- FieldInfo pathUnitsField;
- FieldInfo queueFirstField;
- FieldInfo queueLastField;
- FieldInfo queueLockField;
- FieldInfo calculatingField;
- FieldInfo terminatedField;
- FieldInfo pathFindThreadField;
-
- private Array32 m_pathUnits {
- get { return pathUnitsField.GetValue(this) as Array32; }
- set { pathUnitsField.SetValue(this, value); }
- }
-
- private uint m_queueFirst {
- get { return (uint)queueFirstField.GetValue(this); }
- set { queueFirstField.SetValue(this, value); }
- }
-
- private uint m_queueLast {
- get { return (uint)queueLastField.GetValue(this); }
- set { queueLastField.SetValue(this, value); }
- }
-
- private uint m_calculating {
- get { return (uint)calculatingField.GetValue(this); }
- set { calculatingField.SetValue(this, value); }
- }
-
- private object m_queueLock {
- get { return queueLockField.GetValue(this); }
- set { queueLockField.SetValue(this, value); }
- }
-
- private Thread m_customPathFindThread {
- get { return (Thread)pathFindThreadField.GetValue(this); }
- set { pathFindThreadField.SetValue(this, value); }
- }
-
- private bool m_terminated {
- get { return (bool)terminatedField.GetValue(this); }
- set { terminatedField.SetValue(this, value); }
- }
-
- // stock fields
- public ThreadProfiler m_pathfindProfiler;
- private object m_bufferLock;
- private int m_bufferMinPos;
- private int m_bufferMaxPos;
- private uint[] m_laneLocation;
- private PathUnit.Position[] m_laneTarget;
- private BufferItem[] m_buffer;
- private int[] m_bufferMin;
- private int[] m_bufferMax;
- private float m_maxLength;
- private uint m_startLaneA;
- private uint m_startLaneB;
- private uint m_endLaneA;
- private uint m_endLaneB;
- private uint m_vehicleLane;
- private byte m_startOffsetA;
- private byte m_startOffsetB;
- private byte m_vehicleOffset;
- private NetSegment.Flags m_carBanMask;
- private bool m_ignoreBlocked;
- private bool m_stablePath;
- private bool m_randomParking;
- private bool m_transportVehicle;
- private bool m_ignoreCost;
- private NetSegment.Flags m_disableMask;
- private Randomizer m_pathRandomizer;
- private uint m_pathFindIndex;
- private NetInfo.LaneType m_laneTypes;
- private VehicleInfo.VehicleType m_vehicleTypes;
-
- // custom fields
- private PathUnitQueueItem m_queueItem;
- private bool m_isHeavyVehicle;
-#if DEBUG
- public uint m_failedPathFinds = 0;
- public uint m_succeededPathFinds = 0;
- private bool m_debug = false;
- private IDictionary> m_debugPositions = null;
+ "\t" + $"m_trafficRand={m_trafficRand}\n" +
+#endif
+ "BufferItem]";
+ }
+ }
+
+ private enum LaneChangingCostCalculationMode {
+ None,
+ ByLaneDistance,
+ ByGivenDistance
+ }
+
+ // private stock fields
+ FieldInfo pathUnitsField;
+ FieldInfo queueFirstField;
+ FieldInfo queueLastField;
+ FieldInfo queueLockField;
+ FieldInfo calculatingField;
+ FieldInfo terminatedField;
+ FieldInfo pathFindThreadField;
+
+ private Array32 m_pathUnits {
+ get { return pathUnitsField.GetValue(this) as Array32; }
+ set { pathUnitsField.SetValue(this, value); }
+ }
+
+ private uint m_queueFirst {
+ get { return (uint)queueFirstField.GetValue(this); }
+ set { queueFirstField.SetValue(this, value); }
+ }
+
+ private uint m_queueLast {
+ get { return (uint)queueLastField.GetValue(this); }
+ set { queueLastField.SetValue(this, value); }
+ }
+
+ private uint m_calculating {
+ get { return (uint)calculatingField.GetValue(this); }
+ set { calculatingField.SetValue(this, value); }
+ }
+
+ private object m_queueLock {
+ get { return queueLockField.GetValue(this); }
+ set { queueLockField.SetValue(this, value); }
+ }
+
+ private Thread m_customPathFindThread {
+ get { return (Thread)pathFindThreadField.GetValue(this); }
+ set { pathFindThreadField.SetValue(this, value); }
+ }
+
+ private bool m_terminated {
+ get { return (bool)terminatedField.GetValue(this); }
+ set { terminatedField.SetValue(this, value); }
+ }
+
+ // stock fields
+ public ThreadProfiler m_pathfindProfiler;
+ private object m_bufferLock;
+ private int m_bufferMinPos;
+ private int m_bufferMaxPos;
+ private uint[] m_laneLocation;
+ private PathUnit.Position[] m_laneTarget;
+ private BufferItem[] m_buffer;
+ private int[] m_bufferMin;
+ private int[] m_bufferMax;
+ private float m_maxLength;
+ private uint m_startLaneA;
+ private uint m_startLaneB;
+ private uint m_endLaneA;
+ private uint m_endLaneB;
+ private uint m_vehicleLane;
+ private byte m_startOffsetA;
+ private byte m_startOffsetB;
+ private byte m_vehicleOffset;
+ private NetSegment.Flags m_carBanMask;
+ private bool m_ignoreBlocked;
+ private bool m_stablePath;
+ private bool m_randomParking;
+ private bool m_transportVehicle;
+ private bool m_ignoreCost;
+ private NetSegment.Flags m_disableMask;
+ private Randomizer m_pathRandomizer;
+ private uint m_pathFindIndex;
+ private NetInfo.LaneType m_laneTypes;
+ private VehicleInfo.VehicleType m_vehicleTypes;
+
+ // custom fields
+ private PathUnitQueueItem m_queueItem;
+ private bool m_isHeavyVehicle;
+#if DEBUG
+ public uint m_failedPathFinds = 0;
+ public uint m_succeededPathFinds = 0;
+ private bool m_debug = false;
+ private IDictionary> m_debugPositions = null;
#endif
#if PARKINGAI || JUNCTIONRESTRICTIONS
- private ushort m_startSegmentA;
- private ushort m_startSegmentB;
+ private ushort m_startSegmentA;
+ private ushort m_startSegmentB;
#endif
#if ROUTING
- private bool m_isRoadVehicle;
- private bool m_isLaneArrowObeyingEntity;
- //private bool m_isLaneConnectionObeyingEntity;
-#endif
-
- private void Awake() {
- Type stockPathFindType = typeof(PathFind);
- const BindingFlags fieldFlags = BindingFlags.NonPublic | BindingFlags.Instance;
-
- pathUnitsField = stockPathFindType.GetField("m_pathUnits", fieldFlags);
- queueFirstField = stockPathFindType.GetField("m_queueFirst", fieldFlags);
- queueLastField = stockPathFindType.GetField("m_queueLast", fieldFlags);
- queueLockField = stockPathFindType.GetField("m_queueLock", fieldFlags);
- terminatedField = stockPathFindType.GetField("m_terminated", fieldFlags);
- calculatingField = stockPathFindType.GetField("m_calculating", fieldFlags);
- pathFindThreadField = stockPathFindType.GetField("m_pathFindThread", fieldFlags);
-
- m_pathfindProfiler = new ThreadProfiler();
- m_laneLocation = new uint[262144];
- m_laneTarget = new PathUnit.Position[262144];
- m_buffer = new BufferItem[65536];
- m_bufferMin = new int[1024];
- m_bufferMax = new int[1024];
- m_queueLock = new object();
- m_bufferLock = Singleton.instance.m_bufferLock;
- m_pathUnits = Singleton.instance.m_pathUnits;
- m_customPathFindThread = new Thread(PathFindThread);
- m_customPathFindThread.Name = "Pathfind";
- m_customPathFindThread.Priority = SimulationManager.SIMULATION_PRIORITY;
- m_customPathFindThread.Start();
-
- if (!m_customPathFindThread.IsAlive) {
- CODebugBase.Error(LogChannel.Core, "Path find thread failed to start!");
- }
- }
-
- private void OnDestroy() {
- try {
- Monitor.Enter(m_queueLock);
- m_terminated = true;
- Monitor.PulseAll(m_queueLock);
- } finally {
- Monitor.Exit(m_queueLock);
- }
- }
+ private bool m_isRoadVehicle;
+ private bool m_isLaneArrowObeyingEntity;
+ //private bool m_isLaneConnectionObeyingEntity;
+#endif
+
+ private void Awake() {
+ Type stockPathFindType = typeof(PathFind);
+ const BindingFlags fieldFlags = BindingFlags.NonPublic | BindingFlags.Instance;
+
+ pathUnitsField = stockPathFindType.GetField("m_pathUnits", fieldFlags);
+ queueFirstField = stockPathFindType.GetField("m_queueFirst", fieldFlags);
+ queueLastField = stockPathFindType.GetField("m_queueLast", fieldFlags);
+ queueLockField = stockPathFindType.GetField("m_queueLock", fieldFlags);
+ terminatedField = stockPathFindType.GetField("m_terminated", fieldFlags);
+ calculatingField = stockPathFindType.GetField("m_calculating", fieldFlags);
+ pathFindThreadField = stockPathFindType.GetField("m_pathFindThread", fieldFlags);
+
+ m_pathfindProfiler = new ThreadProfiler();
+ m_laneLocation = new uint[262144];
+ m_laneTarget = new PathUnit.Position[262144];
+ m_buffer = new BufferItem[65536];
+ m_bufferMin = new int[1024];
+ m_bufferMax = new int[1024];
+ m_queueLock = new object();
+ m_bufferLock = Singleton.instance.m_bufferLock;
+ m_pathUnits = Singleton.instance.m_pathUnits;
+ m_customPathFindThread = new Thread(PathFindThread);
+ m_customPathFindThread.Name = "Pathfind";
+ m_customPathFindThread.Priority = SimulationManager.SIMULATION_PRIORITY;
+ m_customPathFindThread.Start();
+
+ if (!m_customPathFindThread.IsAlive) {
+ CODebugBase.Error(LogChannel.Core, "Path find thread failed to start!");
+ }
+ }
+
+ private void OnDestroy() {
+ try {
+ Monitor.Enter(m_queueLock);
+ m_terminated = true;
+ Monitor.PulseAll(m_queueLock);
+ } finally {
+ Monitor.Exit(m_queueLock);
+ }
+ }
#if PF2
- [RedirectMethod]
-#endif
- public new bool CalculatePath(uint unit, bool skipQueue) {
- return ExtCalculatePath(unit, skipQueue);
- }
-
- public bool ExtCalculatePath(uint unit, bool skipQueue) {
- if (CustomPathManager._instance.AddPathReference(unit)) {
- try {
- Monitor.Enter(m_queueLock);
-
- if (skipQueue) {
- if (m_queueLast == 0) {
- m_queueLast = unit;
- } else {
- // NON-STOCK CODE START
- CustomPathManager._instance.queueItems[unit].nextPathUnitId = m_queueFirst;
- // NON-STOCK CODE END
- // PathUnits.m_buffer[unit].m_nextPathUnit = QueueFirst; // stock code commented
- }
- m_queueFirst = unit;
- } else {
- if (m_queueLast == 0) {
- m_queueFirst = unit;
- } else {
- // NON-STOCK CODE START
- CustomPathManager._instance.queueItems[m_queueLast].nextPathUnitId = unit;
- // NON-STOCK CODE END
- // PathUnits.m_buffer[QueueLast].m_nextPathUnit = unit; // stock code commented
- }
- m_queueLast = unit;
- }
-
- m_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_CREATED;
- m_queuedPathFindCount++;
-
- Monitor.Pulse(m_queueLock);
- } finally {
- Monitor.Exit(m_queueLock);
- }
- return true;
- }
- return false;
- }
-
- private void PathFindImplementation(uint unit, ref PathUnit data) {
- m_conf = GlobalConfig.Instance; // NON-STOCK CODE
-
- NetManager netManager = Singleton.instance;
-
- m_laneTypes = (NetInfo.LaneType)m_pathUnits.m_buffer[unit].m_laneTypes;
- m_vehicleTypes = (VehicleInfo.VehicleType)m_pathUnits.m_buffer[unit].m_vehicleTypes;
- m_maxLength = m_pathUnits.m_buffer[unit].m_length;
- m_pathFindIndex = (m_pathFindIndex + 1 & 0x7FFF);
- m_pathRandomizer = new Randomizer(unit);
- m_carBanMask = NetSegment.Flags.CarBan;
-
- m_isHeavyVehicle = (m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IS_HEAVY) != 0; // NON-STOCK CODE (refactored)
- if (m_isHeavyVehicle) {
- m_carBanMask |= NetSegment.Flags.HeavyBan;
- }
-
- if ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_READY) != 0) {
- m_carBanMask |= NetSegment.Flags.WaitingPath;
- }
-
- m_ignoreBlocked = ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IGNORE_BLOCKED) != 0);
- m_stablePath = ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_STABLE_PATH) != 0);
- m_randomParking = ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_RANDOM_PARKING) != 0);
- m_transportVehicle = ((m_laneTypes & NetInfo.LaneType.TransportVehicle) != NetInfo.LaneType.None);
- m_ignoreCost = (m_stablePath || (m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IGNORE_COST) != 0);
- m_disableMask = (NetSegment.Flags.Collapsed | NetSegment.Flags.PathFailed);
-
- if ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IGNORE_FLOODED) == 0) {
- m_disableMask |= NetSegment.Flags.Flooded;
- }
-
- if ((m_laneTypes & NetInfo.LaneType.Vehicle) != NetInfo.LaneType.None) {
- m_laneTypes |= NetInfo.LaneType.TransportVehicle;
- }
+ [RedirectMethod]
+#endif
+ public new bool CalculatePath(uint unit, bool skipQueue) {
+ return ExtCalculatePath(unit, skipQueue);
+ }
+
+ public bool ExtCalculatePath(uint unit, bool skipQueue) {
+ if (CustomPathManager._instance.AddPathReference(unit)) {
+ try {
+ Monitor.Enter(m_queueLock);
+
+ if (skipQueue) {
+ if (m_queueLast == 0) {
+ m_queueLast = unit;
+ } else {
+ // NON-STOCK CODE START
+ CustomPathManager._instance.queueItems[unit].nextPathUnitId = m_queueFirst;
+ // NON-STOCK CODE END
+ // PathUnits.m_buffer[unit].m_nextPathUnit = QueueFirst; // stock code commented
+ }
+ m_queueFirst = unit;
+ } else {
+ if (m_queueLast == 0) {
+ m_queueFirst = unit;
+ } else {
+ // NON-STOCK CODE START
+ CustomPathManager._instance.queueItems[m_queueLast].nextPathUnitId = unit;
+ // NON-STOCK CODE END
+ // PathUnits.m_buffer[QueueLast].m_nextPathUnit = unit; // stock code commented
+ }
+ m_queueLast = unit;
+ }
+
+ m_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_CREATED;
+ m_queuedPathFindCount++;
+
+ Monitor.Pulse(m_queueLock);
+ } finally {
+ Monitor.Exit(m_queueLock);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void PathFindImplementation(uint unit, ref PathUnit data) {
+ m_conf = GlobalConfig.Instance; // NON-STOCK CODE
+
+ NetManager netManager = Singleton.instance;
+
+ m_laneTypes = (NetInfo.LaneType)m_pathUnits.m_buffer[unit].m_laneTypes;
+ m_vehicleTypes = (VehicleInfo.VehicleType)m_pathUnits.m_buffer[unit].m_vehicleTypes;
+ m_maxLength = m_pathUnits.m_buffer[unit].m_length;
+ m_pathFindIndex = m_pathFindIndex + 1 & 0x7FFF;
+ m_pathRandomizer = new Randomizer(unit);
+ m_carBanMask = NetSegment.Flags.CarBan;
+
+ m_isHeavyVehicle = (m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IS_HEAVY) != 0; // NON-STOCK CODE (refactored)
+ if (m_isHeavyVehicle) {
+ m_carBanMask |= NetSegment.Flags.HeavyBan;
+ }
+
+ if ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_READY) != 0) {
+ m_carBanMask |= NetSegment.Flags.WaitingPath;
+ }
+
+ m_ignoreBlocked = (m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IGNORE_BLOCKED) != 0;
+ m_stablePath = (m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_STABLE_PATH) != 0;
+ m_randomParking = (m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_RANDOM_PARKING) != 0;
+ m_transportVehicle = (m_laneTypes & NetInfo.LaneType.TransportVehicle) != NetInfo.LaneType.None;
+ m_ignoreCost = m_stablePath || (m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IGNORE_COST) != 0;
+ m_disableMask = NetSegment.Flags.Collapsed | NetSegment.Flags.PathFailed;
+
+ if ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IGNORE_FLOODED) == 0) {
+ m_disableMask |= NetSegment.Flags.Flooded;
+ }
+
+ if ((m_laneTypes & NetInfo.LaneType.Vehicle) != NetInfo.LaneType.None) {
+ m_laneTypes |= NetInfo.LaneType.TransportVehicle;
+ }
#if ROUTING
- m_isRoadVehicle =
- (m_queueItem.vehicleType & ExtVehicleType.RoadVehicle) != ExtVehicleType.None;
+ m_isRoadVehicle =
+ (m_queueItem.vehicleType & ExtVehicleType.RoadVehicle) != ExtVehicleType.None;
- m_isLaneArrowObeyingEntity =
+ m_isLaneArrowObeyingEntity =
#if DEBUG
- ! Options.allRelaxed && // debug option: all vehicle may ignore lane arrows
+ ! Options.allRelaxed && // debug option: all vehicle may ignore lane arrows
#endif
- (! Options.relaxedBusses || m_queueItem.vehicleType != ExtVehicleType.Bus) && // option: busses may ignore lane arrows
- (m_vehicleTypes & LaneArrowManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None &&
- (m_queueItem.vehicleType & LaneArrowManager.EXT_VEHICLE_TYPES) != ExtVehicleType.None
- ;
+ (! Options.relaxedBusses || m_queueItem.vehicleType != ExtVehicleType.Bus) && // option: busses may ignore lane arrows
+ (m_vehicleTypes & LaneArrowManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None &&
+ (m_queueItem.vehicleType & LaneArrowManager.EXT_VEHICLE_TYPES) != ExtVehicleType.None
+ ;
#endif
#if DEBUG
- m_debug = m_conf.Debug.Switches[0] &&
- (m_conf.Debug.ExtVehicleType == ExtVehicleType.None || m_queueItem.vehicleType == m_conf.Debug.ExtVehicleType) &&
- (m_conf.Debug.StartSegmentId == 0 || data.m_position00.m_segment == m_conf.Debug.StartSegmentId || data.m_position02.m_segment == m_conf.Debug.StartSegmentId) &&
- (m_conf.Debug.EndSegmentId == 0 || data.m_position01.m_segment == m_conf.Debug.EndSegmentId || data.m_position03.m_segment == m_conf.Debug.EndSegmentId) &&
- (m_conf.Debug.VehicleId == 0 || m_queueItem.vehicleId == m_conf.Debug.VehicleId)
- ;
- if (m_debug) {
- m_debugPositions = new Dictionary>();
- }
+ m_debug = m_conf.Debug.Switches[0]
+ && (m_conf.Debug.ApiExtVehicleType == ExtVehicleType.None
+ || m_queueItem.vehicleType == m_conf.Debug.ApiExtVehicleType)
+ && (m_conf.Debug.StartSegmentId == 0
+ || data.m_position00.m_segment == m_conf.Debug.StartSegmentId
+ || data.m_position02.m_segment == m_conf.Debug.StartSegmentId)
+ && (m_conf.Debug.EndSegmentId == 0
+ || data.m_position01.m_segment == m_conf.Debug.EndSegmentId
+ || data.m_position03.m_segment == m_conf.Debug.EndSegmentId)
+ && (m_conf.Debug.VehicleId == 0
+ || m_queueItem.vehicleId == m_conf.Debug.VehicleId);
+ if (m_debug) {
+ m_debugPositions = new Dictionary>();
+ }
#endif
- int posCount = m_pathUnits.m_buffer[unit].m_positionCount & 0xF;
- int vehiclePosIndicator = m_pathUnits.m_buffer[unit].m_positionCount >> 4;
- BufferItem bufferItemStartA = default(BufferItem);
- if (data.m_position00.m_segment != 0 && posCount >= 1) {
+ int posCount = m_pathUnits.m_buffer[unit].m_positionCount & 0xF;
+ int vehiclePosIndicator = m_pathUnits.m_buffer[unit].m_positionCount >> 4;
+ BufferItem bufferItemStartA = default(BufferItem);
+ if (data.m_position00.m_segment != 0 && posCount >= 1) {
#if PARKINGAI || JUNCTIONRESTRICTIONS
- m_startSegmentA = data.m_position00.m_segment; // NON-STOCK CODE
+ m_startSegmentA = data.m_position00.m_segment; // NON-STOCK CODE
#endif
- m_startLaneA = PathManager.GetLaneID(data.m_position00);
- m_startOffsetA = data.m_position00.m_offset;
- bufferItemStartA.m_laneID = m_startLaneA;
- bufferItemStartA.m_position = data.m_position00;
- GetLaneDirection(data.m_position00, out bufferItemStartA.m_direction, out bufferItemStartA.m_lanesUsed
+ m_startLaneA = PathManager.GetLaneID(data.m_position00);
+ m_startOffsetA = data.m_position00.m_offset;
+ bufferItemStartA.m_laneID = m_startLaneA;
+ bufferItemStartA.m_position = data.m_position00;
+ GetLaneDirection(data.m_position00, out bufferItemStartA.m_direction, out bufferItemStartA.m_lanesUsed
#if PARKINGAI
- , out bufferItemStartA.m_vehiclesUsed
+ , out bufferItemStartA.m_vehiclesUsed
#endif
- );
- bufferItemStartA.m_comparisonValue = 0f;
- bufferItemStartA.m_duration = 0f;
- } else {
+ );
+ bufferItemStartA.m_comparisonValue = 0f;
+ bufferItemStartA.m_duration = 0f;
+ } else {
#if PARKINGAI || JUNCTIONRESTRICTIONS
- m_startSegmentA = 0; // NON-STOCK CODE
+ m_startSegmentA = 0; // NON-STOCK CODE
#endif
- m_startLaneA = 0u;
- m_startOffsetA = 0;
- }
+ m_startLaneA = 0u;
+ m_startOffsetA = 0;
+ }
- BufferItem bufferItemStartB = default(BufferItem);
- if (data.m_position02.m_segment != 0 && posCount >= 3) {
+ BufferItem bufferItemStartB = default(BufferItem);
+ if (data.m_position02.m_segment != 0 && posCount >= 3) {
#if PARKINGAI || JUNCTIONRESTRICTIONS
- m_startSegmentB = data.m_position02.m_segment; // NON-STOCK CODE
+ m_startSegmentB = data.m_position02.m_segment; // NON-STOCK CODE
#endif
- m_startLaneB = PathManager.GetLaneID(data.m_position02);
- m_startOffsetB = data.m_position02.m_offset;
- bufferItemStartB.m_laneID = m_startLaneB;
- bufferItemStartB.m_position = data.m_position02;
- GetLaneDirection(data.m_position02, out bufferItemStartB.m_direction, out bufferItemStartB.m_lanesUsed
+ m_startLaneB = PathManager.GetLaneID(data.m_position02);
+ m_startOffsetB = data.m_position02.m_offset;
+ bufferItemStartB.m_laneID = m_startLaneB;
+ bufferItemStartB.m_position = data.m_position02;
+ GetLaneDirection(data.m_position02, out bufferItemStartB.m_direction, out bufferItemStartB.m_lanesUsed
#if PARKINGAI
- , out bufferItemStartB.m_vehiclesUsed
+ , out bufferItemStartB.m_vehiclesUsed
#endif
- );
- bufferItemStartB.m_comparisonValue = 0f;
- bufferItemStartB.m_duration = 0f;
- } else {
+ );
+ bufferItemStartB.m_comparisonValue = 0f;
+ bufferItemStartB.m_duration = 0f;
+ } else {
#if PARKINGAI || JUNCTIONRESTRICTIONS
- m_startSegmentB = 0; // NON-STOCK CODE
-#endif
- m_startLaneB = 0u;
- m_startOffsetB = 0;
- }
-
- BufferItem bufferItemEndA = default(BufferItem);
- if (data.m_position01.m_segment != 0 && posCount >= 2) {
- m_endLaneA = PathManager.GetLaneID(data.m_position01);
- bufferItemEndA.m_laneID = m_endLaneA;
- bufferItemEndA.m_position = data.m_position01;
- GetLaneDirection(data.m_position01, out bufferItemEndA.m_direction, out bufferItemEndA.m_lanesUsed
+ m_startSegmentB = 0; // NON-STOCK CODE
+#endif
+ m_startLaneB = 0u;
+ m_startOffsetB = 0;
+ }
+
+ BufferItem bufferItemEndA = default(BufferItem);
+ if (data.m_position01.m_segment != 0 && posCount >= 2) {
+ m_endLaneA = PathManager.GetLaneID(data.m_position01);
+ bufferItemEndA.m_laneID = m_endLaneA;
+ bufferItemEndA.m_position = data.m_position01;
+ GetLaneDirection(data.m_position01, out bufferItemEndA.m_direction, out bufferItemEndA.m_lanesUsed
#if PARKINGAI
- , out bufferItemEndA.m_vehiclesUsed
-#endif
- );
- bufferItemEndA.m_methodDistance = 0.01f;
- bufferItemEndA.m_comparisonValue = 0f;
- bufferItemEndA.m_duration = 0f;
- } else {
- m_endLaneA = 0u;
- }
-
- BufferItem bufferItemEndB = default(BufferItem);
- if (data.m_position03.m_segment != 0 && posCount >= 4) {
- m_endLaneB = PathManager.GetLaneID(data.m_position03);
- bufferItemEndB.m_laneID = m_endLaneB;
- bufferItemEndB.m_position = data.m_position03;
- GetLaneDirection(data.m_position03, out bufferItemEndB.m_direction, out bufferItemEndB.m_lanesUsed
+ , out bufferItemEndA.m_vehiclesUsed
+#endif
+ );
+ bufferItemEndA.m_methodDistance = 0.01f;
+ bufferItemEndA.m_comparisonValue = 0f;
+ bufferItemEndA.m_duration = 0f;
+ } else {
+ m_endLaneA = 0u;
+ }
+
+ BufferItem bufferItemEndB = default(BufferItem);
+ if (data.m_position03.m_segment != 0 && posCount >= 4) {
+ m_endLaneB = PathManager.GetLaneID(data.m_position03);
+ bufferItemEndB.m_laneID = m_endLaneB;
+ bufferItemEndB.m_position = data.m_position03;
+ GetLaneDirection(data.m_position03, out bufferItemEndB.m_direction, out bufferItemEndB.m_lanesUsed
#if PARKINGAI
- , out bufferItemEndB.m_vehiclesUsed
-#endif
- );
- bufferItemEndB.m_methodDistance = 0.01f;
- bufferItemEndB.m_comparisonValue = 0f;
- bufferItemEndB.m_duration = 0f;
- } else {
- m_endLaneB = 0u;
- }
-
- if (data.m_position11.m_segment != 0 && vehiclePosIndicator >= 1) {
- m_vehicleLane = PathManager.GetLaneID(data.m_position11);
- m_vehicleOffset = data.m_position11.m_offset;
- } else {
- m_vehicleLane = 0u;
- m_vehicleOffset = 0;
- }
-
-#if DEBUG
- bool detourMissing = (m_vehicleTypes & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Monorail | VehicleInfo.VehicleType.Metro)) != VehicleInfo.VehicleType.None && !m_queueItem.queued;
- if (detourMissing) {
- Log.Warning($"Path-finding for unhandled vehicle requested!");
- }
-
- if (m_debug || detourMissing) {
- Debug(unit, $"PathFindImplementation: Preparing calculation:\n" +
- $"\tbufferItemStartA: segment={bufferItemStartA.m_position.m_segment} lane={bufferItemStartA.m_position.m_lane} off={bufferItemStartA.m_position.m_offset} laneId={bufferItemStartA.m_laneID}\n" +
- $"\tbufferItemStartB: segment={bufferItemStartB.m_position.m_segment} lane={bufferItemStartB.m_position.m_lane} off={bufferItemStartB.m_position.m_offset} laneId={bufferItemStartB.m_laneID}\n" +
- $"\tbufferItemEndA: segment={bufferItemEndA.m_position.m_segment} lane={bufferItemEndA.m_position.m_lane} off={bufferItemEndA.m_position.m_offset} laneId={bufferItemEndA.m_laneID}\n" +
- $"\tbufferItemEndB: segment={bufferItemEndB.m_position.m_segment} lane={bufferItemEndB.m_position.m_lane} off={bufferItemEndB.m_position.m_offset} laneId={bufferItemEndB.m_laneID}\n" +
- $"\tvehicleItem: segment={data.m_position11.m_segment} lane={data.m_position11.m_lane} off={data.m_position11.m_offset} laneId={m_vehicleLane} vehiclePosIndicator={vehiclePosIndicator}\n" +
- $"Properties:\n" +
- "\t" + $"m_maxLength={m_maxLength}\n" +
- "\t" + $"m_startLaneA={m_startLaneA}\n" +
- "\t" + $"m_startLaneB={m_startLaneB}\n" +
- "\t" + $"m_endLaneA={m_endLaneA}\n" +
- "\t" + $"m_endLaneB={m_endLaneB}\n" +
- "\t" + $"m_startOffsetA={m_startOffsetA}\n" +
- "\t" + $"m_startOffsetB={m_startOffsetB}\n" +
- "\t" + $"m_vehicleLane={m_vehicleLane}\n" +
- "\t" + $"m_vehicleOffset={m_vehicleOffset}\n" +
- "\t" + $"m_carBanMask={m_carBanMask}\n" +
- "\t" + $"m_disableMask={m_disableMask}\n" +
- "\t" + $"m_ignoreBlocked={m_ignoreBlocked}\n" +
- "\t" + $"m_stablePath={m_stablePath}\n" +
- "\t" + $"m_randomParking={m_randomParking}\n" +
- "\t" + $"m_transportVehicle={m_transportVehicle}\n" +
- "\t" + $"m_ignoreCost={m_ignoreCost}\n" +
- "\t" + $"m_pathFindIndex={m_pathFindIndex}\n" +
- "\t" + $"m_laneTypes={m_laneTypes}\n" +
- "\t" + $"m_vehicleTypes={m_vehicleTypes}\n" +
- "\t" + $"m_queueItem={m_queueItem}\n" +
- "\t" + $"m_isHeavyVehicle={m_isHeavyVehicle}\n" +
- "\t" + $"m_failedPathFinds={m_failedPathFinds}\n" +
- "\t" + $"m_succeededPathFinds={m_succeededPathFinds}\n" +
+ , out bufferItemEndB.m_vehiclesUsed
+#endif
+ );
+ bufferItemEndB.m_methodDistance = 0.01f;
+ bufferItemEndB.m_comparisonValue = 0f;
+ bufferItemEndB.m_duration = 0f;
+ } else {
+ m_endLaneB = 0u;
+ }
+
+ if (data.m_position11.m_segment != 0 && vehiclePosIndicator >= 1) {
+ m_vehicleLane = PathManager.GetLaneID(data.m_position11);
+ m_vehicleOffset = data.m_position11.m_offset;
+ } else {
+ m_vehicleLane = 0u;
+ m_vehicleOffset = 0;
+ }
+
+#if DEBUG
+ bool detourMissing = (m_vehicleTypes & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Monorail | VehicleInfo.VehicleType.Metro)) != VehicleInfo.VehicleType.None && !m_queueItem.queued;
+ if (detourMissing) {
+ Log.Warning($"Path-finding for unhandled vehicle requested!");
+ }
+
+ if (m_debug || detourMissing) {
+ Debug(unit, $"PathFindImplementation: Preparing calculation:\n" +
+ $"\tbufferItemStartA: segment={bufferItemStartA.m_position.m_segment} lane={bufferItemStartA.m_position.m_lane} off={bufferItemStartA.m_position.m_offset} laneId={bufferItemStartA.m_laneID}\n" +
+ $"\tbufferItemStartB: segment={bufferItemStartB.m_position.m_segment} lane={bufferItemStartB.m_position.m_lane} off={bufferItemStartB.m_position.m_offset} laneId={bufferItemStartB.m_laneID}\n" +
+ $"\tbufferItemEndA: segment={bufferItemEndA.m_position.m_segment} lane={bufferItemEndA.m_position.m_lane} off={bufferItemEndA.m_position.m_offset} laneId={bufferItemEndA.m_laneID}\n" +
+ $"\tbufferItemEndB: segment={bufferItemEndB.m_position.m_segment} lane={bufferItemEndB.m_position.m_lane} off={bufferItemEndB.m_position.m_offset} laneId={bufferItemEndB.m_laneID}\n" +
+ $"\tvehicleItem: segment={data.m_position11.m_segment} lane={data.m_position11.m_lane} off={data.m_position11.m_offset} laneId={m_vehicleLane} vehiclePosIndicator={vehiclePosIndicator}\n" +
+ $"Properties:\n" +
+ "\t" + $"m_maxLength={m_maxLength}\n" +
+ "\t" + $"m_startLaneA={m_startLaneA}\n" +
+ "\t" + $"m_startLaneB={m_startLaneB}\n" +
+ "\t" + $"m_endLaneA={m_endLaneA}\n" +
+ "\t" + $"m_endLaneB={m_endLaneB}\n" +
+ "\t" + $"m_startOffsetA={m_startOffsetA}\n" +
+ "\t" + $"m_startOffsetB={m_startOffsetB}\n" +
+ "\t" + $"m_vehicleLane={m_vehicleLane}\n" +
+ "\t" + $"m_vehicleOffset={m_vehicleOffset}\n" +
+ "\t" + $"m_carBanMask={m_carBanMask}\n" +
+ "\t" + $"m_disableMask={m_disableMask}\n" +
+ "\t" + $"m_ignoreBlocked={m_ignoreBlocked}\n" +
+ "\t" + $"m_stablePath={m_stablePath}\n" +
+ "\t" + $"m_randomParking={m_randomParking}\n" +
+ "\t" + $"m_transportVehicle={m_transportVehicle}\n" +
+ "\t" + $"m_ignoreCost={m_ignoreCost}\n" +
+ "\t" + $"m_pathFindIndex={m_pathFindIndex}\n" +
+ "\t" + $"m_laneTypes={m_laneTypes}\n" +
+ "\t" + $"m_vehicleTypes={m_vehicleTypes}\n" +
+ "\t" + $"m_queueItem={m_queueItem}\n" +
+ "\t" + $"m_isHeavyVehicle={m_isHeavyVehicle}\n" +
+ "\t" + $"m_failedPathFinds={m_failedPathFinds}\n" +
+ "\t" + $"m_succeededPathFinds={m_succeededPathFinds}\n" +
#if PARKINGAI || JUNCTIONRESTRICTIONS
- "\t" + $"m_startSegmentA={m_startSegmentA}\n" +
- "\t" + $"m_startSegmentB={m_startSegmentB}\n" +
+ "\t" + $"m_startSegmentA={m_startSegmentA}\n" +
+ "\t" + $"m_startSegmentB={m_startSegmentB}\n" +
#endif
#if ROUTING
- "\t" + $"m_isRoadVehicle={m_isRoadVehicle}\n" +
- "\t" + $"m_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}"
-#endif
- );
- }
-#endif
-
- BufferItem finalBufferItem = default(BufferItem);
- byte startOffset = 0;
- m_bufferMinPos = 0;
- m_bufferMaxPos = -1;
-
- if (m_pathFindIndex == 0) {
- uint num3 = 4294901760u;
- for (int i = 0; i < 262144; i++) {
- m_laneLocation[i] = num3;
- }
- }
-
- for (int j = 0; j < 1024; j++) {
- m_bufferMin[j] = 0;
- m_bufferMax[j] = -1;
- }
-
- if (bufferItemEndA.m_position.m_segment != 0) {
- m_bufferMax[0]++;
- m_buffer[++m_bufferMaxPos] = bufferItemEndA;
- }
-
- if (bufferItemEndB.m_position.m_segment != 0) {
- m_bufferMax[0]++;
- m_buffer[++m_bufferMaxPos] = bufferItemEndB;
- }
-
- bool canFindPath = false;
- while (m_bufferMinPos <= m_bufferMaxPos) {
- int bufMin = m_bufferMin[m_bufferMinPos];
- int bufMax = m_bufferMax[m_bufferMinPos];
-
- if (bufMin > bufMax) {
- m_bufferMinPos++;
- } else {
- m_bufferMin[m_bufferMinPos] = bufMin + 1;
- BufferItem candidateItem = m_buffer[(m_bufferMinPos << 6) + bufMin];
- if (candidateItem.m_position.m_segment == bufferItemStartA.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartA.m_position.m_lane) {
- if ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None && candidateItem.m_position.m_offset >= m_startOffsetA) {
- finalBufferItem = candidateItem;
- startOffset = m_startOffsetA;
- canFindPath = true;
- break;
- }
-
- if ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None && candidateItem.m_position.m_offset <= m_startOffsetA) {
- finalBufferItem = candidateItem;
- startOffset = m_startOffsetA;
- canFindPath = true;
- break;
- }
- }
-
- if (candidateItem.m_position.m_segment == bufferItemStartB.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartB.m_position.m_lane) {
- if ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None && candidateItem.m_position.m_offset >= m_startOffsetB) {
- finalBufferItem = candidateItem;
- startOffset = m_startOffsetB;
- canFindPath = true;
- break;
- }
-
- if ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None && candidateItem.m_position.m_offset <= m_startOffsetB) {
- finalBufferItem = candidateItem;
- startOffset = m_startOffsetB;
- canFindPath = true;
- break;
- }
- }
-
- ushort startNodeId = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode;
- ushort endNodeId = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode;
-
- if ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None) {
- ProcessItemMain(
-#if DEBUG
- unit,
-#endif
- candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], ref netManager.m_lanes.m_buffer[candidateItem.m_laneID], startNodeId, ref netManager.m_nodes.m_buffer[startNodeId], 0, false);
- }
-
- if ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None) {
- ProcessItemMain(
-#if DEBUG
- unit,
-#endif
- candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], ref netManager.m_lanes.m_buffer[candidateItem.m_laneID], endNodeId, ref netManager.m_nodes.m_buffer[endNodeId], 255, false);
- }
-
- int numIter = 0;
- ushort specialNodeId = netManager.m_lanes.m_buffer[candidateItem.m_laneID].m_nodes;
- if (specialNodeId != 0) {
- bool nodesDisabled = ((netManager.m_nodes.m_buffer[startNodeId].m_flags | netManager.m_nodes.m_buffer[endNodeId].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None;
-
- while (specialNodeId != 0) {
- NetInfo.Direction direction = NetInfo.Direction.None;
- byte laneOffset = netManager.m_nodes.m_buffer[specialNodeId].m_laneOffset;
-
- if (laneOffset <= candidateItem.m_position.m_offset) {
- direction |= NetInfo.Direction.Forward;
- }
-
- if (laneOffset >= candidateItem.m_position.m_offset) {
- direction |= NetInfo.Direction.Backward;
- }
-
- if ((candidateItem.m_direction & direction) != NetInfo.Direction.None && (!nodesDisabled || (netManager.m_nodes.m_buffer[specialNodeId].m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None)) {
-#if DEBUG
- if (m_debug && (m_conf.Debug.NodeId <= 0 || specialNodeId == m_conf.Debug.NodeId)) {
- Debug(unit, $"PathFindImplementation: Handling special node for path unit {unit}, type {m_queueItem.vehicleType}:\n" +
- $"\tcandidateItem.m_position.m_segment={candidateItem.m_position.m_segment}\n" +
- $"\tcandidateItem.m_position.m_lane={candidateItem.m_position.m_lane}\n" +
- $"\tcandidateItem.m_laneID={candidateItem.m_laneID}\n" +
- $"\tspecialNodeId={specialNodeId}\n" +
- $"\tstartNodeId={startNodeId}\n" +
- $"\tendNodeId={endNodeId}\n"
- );
- }
-#endif
- ProcessItemMain(
-#if DEBUG
- unit,
-#endif
- candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], ref netManager.m_lanes.m_buffer[candidateItem.m_laneID], specialNodeId, ref netManager.m_nodes.m_buffer[specialNodeId], laneOffset, true);
- }
-
- specialNodeId = netManager.m_nodes.m_buffer[specialNodeId].m_nextLaneNode;
-
- if (++numIter == 32768) {
- break;
- }
- }
- }
- }
- }
-
- if (!canFindPath) {
- m_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;
- // NON-STOCK CODE START
-#if DEBUG
- ++m_failedPathFinds;
-
- if (m_debug) {
- Debug(unit, $"PathFindImplementation: Path-find failed: Could not find path");
- string reachableBuf = "";
- string unreachableBuf = "";
- foreach (KeyValuePair> e in m_debugPositions) {
- string buf = $"{e.Key} -> {e.Value.CollectionToString()}\n";
- if (e.Value.Count <= 0) {
- unreachableBuf += buf;
- } else {
- reachableBuf += buf;
- }
- }
- Debug(unit, $"PathFindImplementation: Reachability graph:\n== REACHABLE ==\n" + reachableBuf + "\n== UNREACHABLE ==\n" + unreachableBuf);
- }
-#endif
- // NON-STOCK CODE END
- } else {
- float duration = (m_laneTypes != NetInfo.LaneType.Pedestrian && (m_laneTypes & NetInfo.LaneType.Pedestrian) != NetInfo.LaneType.None) ? finalBufferItem.m_duration : finalBufferItem.m_methodDistance;
- m_pathUnits.m_buffer[unit].m_length = duration;
- m_pathUnits.m_buffer[unit].m_speed = (byte)Mathf.Clamp(finalBufferItem.m_methodDistance * 100f / Mathf.Max(0.01f, finalBufferItem.m_duration), 0f, 255f);
+ "\t" + $"m_isRoadVehicle={m_isRoadVehicle}\n" +
+ "\t" + $"m_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}"
+#endif
+ );
+ }
+#endif
+
+ BufferItem finalBufferItem = default(BufferItem);
+ byte startOffset = 0;
+ m_bufferMinPos = 0;
+ m_bufferMaxPos = -1;
+
+ if (m_pathFindIndex == 0) {
+ uint num3 = 4294901760u;
+ for (int i = 0; i < 262144; i++) {
+ m_laneLocation[i] = num3;
+ }
+ }
+
+ for (int j = 0; j < 1024; j++) {
+ m_bufferMin[j] = 0;
+ m_bufferMax[j] = -1;
+ }
+
+ if (bufferItemEndA.m_position.m_segment != 0) {
+ m_bufferMax[0]++;
+ m_buffer[++m_bufferMaxPos] = bufferItemEndA;
+ }
+
+ if (bufferItemEndB.m_position.m_segment != 0) {
+ m_bufferMax[0]++;
+ m_buffer[++m_bufferMaxPos] = bufferItemEndB;
+ }
+
+ bool canFindPath = false;
+ while (m_bufferMinPos <= m_bufferMaxPos) {
+ int bufMin = m_bufferMin[m_bufferMinPos];
+ int bufMax = m_bufferMax[m_bufferMinPos];
+
+ if (bufMin > bufMax) {
+ m_bufferMinPos++;
+ } else {
+ m_bufferMin[m_bufferMinPos] = bufMin + 1;
+ BufferItem candidateItem = m_buffer[(m_bufferMinPos << 6) + bufMin];
+ if (candidateItem.m_position.m_segment == bufferItemStartA.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartA.m_position.m_lane) {
+ if ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None && candidateItem.m_position.m_offset >= m_startOffsetA) {
+ finalBufferItem = candidateItem;
+ startOffset = m_startOffsetA;
+ canFindPath = true;
+ break;
+ }
+
+ if ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None && candidateItem.m_position.m_offset <= m_startOffsetA) {
+ finalBufferItem = candidateItem;
+ startOffset = m_startOffsetA;
+ canFindPath = true;
+ break;
+ }
+ }
+
+ if (candidateItem.m_position.m_segment == bufferItemStartB.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartB.m_position.m_lane) {
+ if ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None && candidateItem.m_position.m_offset >= m_startOffsetB) {
+ finalBufferItem = candidateItem;
+ startOffset = m_startOffsetB;
+ canFindPath = true;
+ break;
+ }
+
+ if ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None && candidateItem.m_position.m_offset <= m_startOffsetB) {
+ finalBufferItem = candidateItem;
+ startOffset = m_startOffsetB;
+ canFindPath = true;
+ break;
+ }
+ }
+
+ ushort startNodeId = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode;
+ ushort endNodeId = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode;
+
+ if ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None) {
+ ProcessItemMain(
+#if DEBUG
+ unit,
+#endif
+ candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], ref netManager.m_lanes.m_buffer[candidateItem.m_laneID], startNodeId, ref netManager.m_nodes.m_buffer[startNodeId], 0, false);
+ }
+
+ if ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None) {
+ ProcessItemMain(
+#if DEBUG
+ unit,
+#endif
+ candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], ref netManager.m_lanes.m_buffer[candidateItem.m_laneID], endNodeId, ref netManager.m_nodes.m_buffer[endNodeId], 255, false);
+ }
+
+ int numIter = 0;
+ ushort specialNodeId = netManager.m_lanes.m_buffer[candidateItem.m_laneID].m_nodes;
+ if (specialNodeId != 0) {
+ bool nodesDisabled = ((netManager.m_nodes.m_buffer[startNodeId].m_flags | netManager.m_nodes.m_buffer[endNodeId].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None;
+
+ while (specialNodeId != 0) {
+ NetInfo.Direction direction = NetInfo.Direction.None;
+ byte laneOffset = netManager.m_nodes.m_buffer[specialNodeId].m_laneOffset;
+
+ if (laneOffset <= candidateItem.m_position.m_offset) {
+ direction |= NetInfo.Direction.Forward;
+ }
+
+ if (laneOffset >= candidateItem.m_position.m_offset) {
+ direction |= NetInfo.Direction.Backward;
+ }
+
+ if ((candidateItem.m_direction & direction) != NetInfo.Direction.None && (!nodesDisabled || (netManager.m_nodes.m_buffer[specialNodeId].m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None)) {
+#if DEBUG
+ if (m_debug && (m_conf.Debug.NodeId <= 0 || specialNodeId == m_conf.Debug.NodeId)) {
+ Debug(unit, $"PathFindImplementation: Handling special node for path unit {unit}, type {m_queueItem.vehicleType}:\n" +
+ $"\tcandidateItem.m_position.m_segment={candidateItem.m_position.m_segment}\n" +
+ $"\tcandidateItem.m_position.m_lane={candidateItem.m_position.m_lane}\n" +
+ $"\tcandidateItem.m_laneID={candidateItem.m_laneID}\n" +
+ $"\tspecialNodeId={specialNodeId}\n" +
+ $"\tstartNodeId={startNodeId}\n" +
+ $"\tendNodeId={endNodeId}\n"
+ );
+ }
+#endif
+ ProcessItemMain(
+#if DEBUG
+ unit,
+#endif
+ candidateItem, ref netManager.m_segments.m_buffer[candidateItem.m_position.m_segment], ref netManager.m_lanes.m_buffer[candidateItem.m_laneID], specialNodeId, ref netManager.m_nodes.m_buffer[specialNodeId], laneOffset, true);
+ }
+
+ specialNodeId = netManager.m_nodes.m_buffer[specialNodeId].m_nextLaneNode;
+
+ if (++numIter == 32768) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (!canFindPath) {
+ m_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;
+ // NON-STOCK CODE START
+#if DEBUG
+ ++m_failedPathFinds;
+
+ if (m_debug) {
+ Debug(unit, $"PathFindImplementation: Path-find failed: Could not find path");
+ string reachableBuf = "";
+ string unreachableBuf = "";
+ foreach (KeyValuePair> e in m_debugPositions) {
+ string buf = $"{e.Key} -> {e.Value.CollectionToString()}\n";
+ if (e.Value.Count <= 0) {
+ unreachableBuf += buf;
+ } else {
+ reachableBuf += buf;
+ }
+ }
+ Debug(unit, $"PathFindImplementation: Reachability graph:\n== REACHABLE ==\n" + reachableBuf + "\n== UNREACHABLE ==\n" + unreachableBuf);
+ }
+#endif
+ // NON-STOCK CODE END
+ } else {
+ float duration = (m_laneTypes != NetInfo.LaneType.Pedestrian && (m_laneTypes & NetInfo.LaneType.Pedestrian) != NetInfo.LaneType.None) ? finalBufferItem.m_duration : finalBufferItem.m_methodDistance;
+ m_pathUnits.m_buffer[unit].m_length = duration;
+ m_pathUnits.m_buffer[unit].m_speed = (byte)Mathf.Clamp(finalBufferItem.m_methodDistance * 100f / Mathf.Max(0.01f, finalBufferItem.m_duration), 0f, 255f);
#if PARKINGAI
- m_pathUnits.m_buffer[unit].m_laneTypes = (byte)finalBufferItem.m_lanesUsed;
- m_pathUnits.m_buffer[unit].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed;
-#endif
-
- uint currentPathUnitId = unit;
- int currentItemPositionCount = 0;
- int sumOfPositionCounts = 0;
- PathUnit.Position currentPosition = finalBufferItem.m_position;
-
- if ((currentPosition.m_segment != bufferItemEndA.m_position.m_segment || currentPosition.m_lane != bufferItemEndA.m_position.m_lane || currentPosition.m_offset != bufferItemEndA.m_position.m_offset) &&
- (currentPosition.m_segment != bufferItemEndB.m_position.m_segment || currentPosition.m_lane != bufferItemEndB.m_position.m_lane || currentPosition.m_offset != bufferItemEndB.m_position.m_offset)) {
- if (startOffset != currentPosition.m_offset) {
- PathUnit.Position position2 = currentPosition;
- position2.m_offset = startOffset;
- m_pathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, position2);
- }
-
- m_pathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition);
- currentPosition = m_laneTarget[finalBufferItem.m_laneID];
- }
-
- for (int k = 0; k < 262144; k++) {
- m_pathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition);
-
- if ((currentPosition.m_segment == bufferItemEndA.m_position.m_segment && currentPosition.m_lane == bufferItemEndA.m_position.m_lane && currentPosition.m_offset == bufferItemEndA.m_position.m_offset) ||
- (currentPosition.m_segment == bufferItemEndB.m_position.m_segment && currentPosition.m_lane == bufferItemEndB.m_position.m_lane && currentPosition.m_offset == bufferItemEndB.m_position.m_offset)) {
- m_pathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;
- sumOfPositionCounts += currentItemPositionCount;
- if (sumOfPositionCounts != 0) {
- currentPathUnitId = m_pathUnits.m_buffer[unit].m_nextPathUnit;
- currentItemPositionCount = m_pathUnits.m_buffer[unit].m_positionCount;
- int numIter = 0;
- while (currentPathUnitId != 0) {
- m_pathUnits.m_buffer[currentPathUnitId].m_length = duration * (float)(sumOfPositionCounts - currentItemPositionCount) / (float)sumOfPositionCounts;
- currentItemPositionCount += m_pathUnits.m_buffer[currentPathUnitId].m_positionCount;
- currentPathUnitId = m_pathUnits.m_buffer[currentPathUnitId].m_nextPathUnit;
- if (++numIter >= 262144) {
- CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
- break;
- }
- }
- }
- m_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_READY;
- // NON-STOCK CODE START
-#if DEBUG
- ++m_succeededPathFinds;
-
- if (m_debug) {
- Debug(unit, $"PathFindImplementation: Path-find succeeded");
- }
-#endif
- // NON-STOCK CODE END
- return;
- }
-
- if (currentItemPositionCount == 12) {
- uint createdPathUnitId;
- try {
- Monitor.Enter(m_bufferLock);
-
- if (!m_pathUnits.CreateItem(out createdPathUnitId, ref m_pathRandomizer)) {
- m_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;
- // NON-STOCK CODE START
-#if DEBUG
- ++m_failedPathFinds;
-
- if (m_debug) {
- Debug(unit, $"Path-finding failed: Could not create path unit");
- }
-#endif
- // NON-STOCK CODE END
- return;
- }
-
- m_pathUnits.m_buffer[createdPathUnitId] = m_pathUnits.m_buffer[currentPathUnitId];
- m_pathUnits.m_buffer[createdPathUnitId].m_referenceCount = 1;
- m_pathUnits.m_buffer[createdPathUnitId].m_pathFindFlags = PathUnit.FLAG_READY;
- m_pathUnits.m_buffer[currentPathUnitId].m_nextPathUnit = createdPathUnitId;
- m_pathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;
+ m_pathUnits.m_buffer[unit].m_laneTypes = (byte)finalBufferItem.m_lanesUsed;
+ m_pathUnits.m_buffer[unit].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed;
+#endif
+
+ uint currentPathUnitId = unit;
+ int currentItemPositionCount = 0;
+ int sumOfPositionCounts = 0;
+ PathUnit.Position currentPosition = finalBufferItem.m_position;
+
+ if ((currentPosition.m_segment != bufferItemEndA.m_position.m_segment || currentPosition.m_lane != bufferItemEndA.m_position.m_lane || currentPosition.m_offset != bufferItemEndA.m_position.m_offset) &&
+ (currentPosition.m_segment != bufferItemEndB.m_position.m_segment || currentPosition.m_lane != bufferItemEndB.m_position.m_lane || currentPosition.m_offset != bufferItemEndB.m_position.m_offset)) {
+ if (startOffset != currentPosition.m_offset) {
+ PathUnit.Position position2 = currentPosition;
+ position2.m_offset = startOffset;
+ m_pathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, position2);
+ }
+
+ m_pathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition);
+ currentPosition = m_laneTarget[finalBufferItem.m_laneID];
+ }
+
+ for (int k = 0; k < 262144; k++) {
+ m_pathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition);
+
+ if ((currentPosition.m_segment == bufferItemEndA.m_position.m_segment && currentPosition.m_lane == bufferItemEndA.m_position.m_lane && currentPosition.m_offset == bufferItemEndA.m_position.m_offset) ||
+ (currentPosition.m_segment == bufferItemEndB.m_position.m_segment && currentPosition.m_lane == bufferItemEndB.m_position.m_lane && currentPosition.m_offset == bufferItemEndB.m_position.m_offset)) {
+ m_pathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;
+ sumOfPositionCounts += currentItemPositionCount;
+ if (sumOfPositionCounts != 0) {
+ currentPathUnitId = m_pathUnits.m_buffer[unit].m_nextPathUnit;
+ currentItemPositionCount = m_pathUnits.m_buffer[unit].m_positionCount;
+ int numIter = 0;
+ while (currentPathUnitId != 0) {
+ m_pathUnits.m_buffer[currentPathUnitId].m_length = duration * (float)(sumOfPositionCounts - currentItemPositionCount) / (float)sumOfPositionCounts;
+ currentItemPositionCount += m_pathUnits.m_buffer[currentPathUnitId].m_positionCount;
+ currentPathUnitId = m_pathUnits.m_buffer[currentPathUnitId].m_nextPathUnit;
+ if (++numIter >= 262144) {
+ CODebugBase.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
+ break;
+ }
+ }
+ }
+ m_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_READY;
+ // NON-STOCK CODE START
+#if DEBUG
+ ++m_succeededPathFinds;
+
+ if (m_debug) {
+ Debug(unit, $"PathFindImplementation: Path-find succeeded");
+ }
+#endif
+ // NON-STOCK CODE END
+ return;
+ }
+
+ if (currentItemPositionCount == 12) {
+ uint createdPathUnitId;
+ try {
+ Monitor.Enter(m_bufferLock);
+
+ if (!m_pathUnits.CreateItem(out createdPathUnitId, ref m_pathRandomizer)) {
+ m_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;
+ // NON-STOCK CODE START
+#if DEBUG
+ ++m_failedPathFinds;
+
+ if (m_debug) {
+ Debug(unit, $"Path-finding failed: Could not create path unit");
+ }
+#endif
+ // NON-STOCK CODE END
+ return;
+ }
+
+ m_pathUnits.m_buffer[createdPathUnitId] = m_pathUnits.m_buffer[currentPathUnitId];
+ m_pathUnits.m_buffer[createdPathUnitId].m_referenceCount = 1;
+ m_pathUnits.m_buffer[createdPathUnitId].m_pathFindFlags = PathUnit.FLAG_READY;
+ m_pathUnits.m_buffer[currentPathUnitId].m_nextPathUnit = createdPathUnitId;
+ m_pathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;
#if PARKINGAI
- m_pathUnits.m_buffer[currentPathUnitId].m_laneTypes = (byte)finalBufferItem.m_lanesUsed; // (this is not accurate!)
- m_pathUnits.m_buffer[currentPathUnitId].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed; // (this is not accurate!)
-#endif
- sumOfPositionCounts += currentItemPositionCount;
- Singleton.instance.m_pathUnitCount = (int)(m_pathUnits.ItemCount() - 1);
- } finally {
- Monitor.Exit(m_bufferLock);
- }
-
- currentPathUnitId = createdPathUnitId;
- currentItemPositionCount = 0;
- }
-
- uint laneID = PathManager.GetLaneID(currentPosition);
- currentPosition = m_laneTarget[laneID];
- }
-
- m_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;
- // NON-STOCK CODE START
-#if DEBUG
- ++m_failedPathFinds;
-
- if (m_debug) {
- Debug(unit, $"Path-finding failed: Internal loop break error");
- }
-#endif
- // NON-STOCK CODE END
- }
- }
-
-#if DEBUG
- private void Debug(uint unit, string message) {
- Log._Debug(
- $"PF T#({Thread.CurrentThread.ManagedThreadId}) IDX#({m_pathFindIndex}):\n"
- + $"UNIT({unit})\n"
- + message
- );
- }
-
- private void Debug(uint unit, BufferItem item, string message) {
- Log._Debug(
- $"PF T#({Thread.CurrentThread.ManagedThreadId}) IDX#({m_pathFindIndex}):\n"
- + $"UNIT({unit}): s#({item.m_position.m_segment}), l#({item.m_position.m_lane})\n"
- + $"ITEM({item})\n"
- + message
- );
- }
-
- private void Debug(uint unit, BufferItem item, ushort nextSegmentId, string message) {
- Log._Debug(
- $"PF T#({Thread.CurrentThread.ManagedThreadId}) IDX#({m_pathFindIndex}):\n"
- + $"UNIT({unit}): s#({item.m_position.m_segment}), l#({item.m_position.m_lane}) -> s#({nextSegmentId})\n"
- + $"ITEM({item})\n"
- + message
- );
- }
-
- private void Debug(uint unit, BufferItem item, ushort nextSegmentId, int nextLaneIndex, uint nextLaneId, string message) {
- Log._Debug(
- $"PF T#({Thread.CurrentThread.ManagedThreadId}) IDX#({m_pathFindIndex}):\n"
- + $"UNIT({unit}): s#({item.m_position.m_segment}), l#({item.m_position.m_lane}) -> s#({nextSegmentId}), l#({nextLaneIndex}), lid#({nextLaneId})\n"
- + $"ITEM({item})\n"
- + message
- );
- }
-#endif
-
- // 1
- private void ProcessItemMain(
-#if DEBUG
- uint unitId,
-#endif
- BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, ushort nextNodeId, ref NetNode nextNode, byte connectOffset, bool isMiddle) {
-#if DEBUG
- bool debug = this.m_debug && (m_conf.Debug.NodeId <= 0 || nextNodeId == m_conf.Debug.NodeId);
- if (debug) {
- if (!m_debugPositions.ContainsKey(item.m_position.m_segment)) {
- m_debugPositions[item.m_position.m_segment] = new List();
- }
- }
-
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain called.\n"
- + "\t" + $"nextNodeId={nextNodeId}\n"
- + "\t" + $"connectOffset={connectOffset}\n"
- + "\t" + $"isMiddle={isMiddle}"
- );
- }
-#endif
-
- NetManager netManager = Singleton.instance;
-
- ushort prevSegmentId = item.m_position.m_segment;
- byte prevLaneIndex = item.m_position.m_lane;
-
- bool prevIsPedestrianLane = false;
- bool prevIsBicycleLane = false;
- bool prevIsCenterPlatform = false;
- bool prevIsElevated = false;
+ m_pathUnits.m_buffer[currentPathUnitId].m_laneTypes = (byte)finalBufferItem.m_lanesUsed; // (this is not accurate!)
+ m_pathUnits.m_buffer[currentPathUnitId].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed; // (this is not accurate!)
+#endif
+ sumOfPositionCounts += currentItemPositionCount;
+ Singleton.instance.m_pathUnitCount = (int)(m_pathUnits.ItemCount() - 1);
+ } finally {
+ Monitor.Exit(m_bufferLock);
+ }
+
+ currentPathUnitId = createdPathUnitId;
+ currentItemPositionCount = 0;
+ }
+
+ uint laneID = PathManager.GetLaneID(currentPosition);
+ currentPosition = m_laneTarget[laneID];
+ }
+
+ m_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;
+ // NON-STOCK CODE START
+#if DEBUG
+ ++m_failedPathFinds;
+
+ if (m_debug) {
+ Debug(unit, $"Path-finding failed: Internal loop break error");
+ }
+#endif
+ // NON-STOCK CODE END
+ }
+ }
+
+#if DEBUG
+ private void Debug(uint unit, string message) {
+ Log._Debug(
+ $"PF T#({Thread.CurrentThread.ManagedThreadId}) IDX#({m_pathFindIndex}):\n"
+ + $"UNIT({unit})\n"
+ + message
+ );
+ }
+
+ private void Debug(uint unit, BufferItem item, string message) {
+ Log._Debug(
+ $"PF T#({Thread.CurrentThread.ManagedThreadId}) IDX#({m_pathFindIndex}):\n"
+ + $"UNIT({unit}): s#({item.m_position.m_segment}), l#({item.m_position.m_lane})\n"
+ + $"ITEM({item})\n"
+ + message
+ );
+ }
+
+ private void Debug(uint unit, BufferItem item, ushort nextSegmentId, string message) {
+ Log._Debug(
+ $"PF T#({Thread.CurrentThread.ManagedThreadId}) IDX#({m_pathFindIndex}):\n"
+ + $"UNIT({unit}): s#({item.m_position.m_segment}), l#({item.m_position.m_lane}) -> s#({nextSegmentId})\n"
+ + $"ITEM({item})\n"
+ + message
+ );
+ }
+
+ private void Debug(uint unit, BufferItem item, ushort nextSegmentId, int nextLaneIndex, uint nextLaneId, string message) {
+ Log._Debug(
+ $"PF T#({Thread.CurrentThread.ManagedThreadId}) IDX#({m_pathFindIndex}):\n"
+ + $"UNIT({unit}): s#({item.m_position.m_segment}), l#({item.m_position.m_lane}) -> s#({nextSegmentId}), l#({nextLaneIndex}), lid#({nextLaneId})\n"
+ + $"ITEM({item})\n"
+ + message
+ );
+ }
+#endif
+
+ // 1
+ private void ProcessItemMain(
+#if DEBUG
+ uint unitId,
+#endif
+ BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, ushort nextNodeId, ref NetNode nextNode, byte connectOffset, bool isMiddle) {
+#if DEBUG
+ bool debug = this.m_debug && (m_conf.Debug.NodeId <= 0 || nextNodeId == m_conf.Debug.NodeId);
+ if (debug) {
+ if (!m_debugPositions.ContainsKey(item.m_position.m_segment)) {
+ m_debugPositions[item.m_position.m_segment] = new List();
+ }
+ }
+
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain called.\n"
+ + "\t" + $"nextNodeId={nextNodeId}\n"
+ + "\t" + $"connectOffset={connectOffset}\n"
+ + "\t" + $"isMiddle={isMiddle}"
+ );
+ }
+#endif
+
+ NetManager netManager = Singleton.instance;
+
+ ushort prevSegmentId = item.m_position.m_segment;
+ byte prevLaneIndex = item.m_position.m_lane;
+
+ bool prevIsPedestrianLane = false;
+ bool prevIsBicycleLane = false;
+ bool prevIsCenterPlatform = false;
+ bool prevIsElevated = false;
#if ADVANCEDAI && ROUTING
- // NON-STOCK CODE START
- bool prevIsCarLane = false;
- // NON-STOCK CODE END
-#endif
- int prevRelSimilarLaneIndex = 0;
- // NON-STOCK CODE START
- float prevMaxSpeed = 1f;
- float prevLaneSpeed = 1f;
- // NON-STOCK CODE END
-
- NetInfo prevSegmentInfo = prevSegment.Info;
- if (prevLaneIndex < prevSegmentInfo.m_lanes.Length) {
- NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];
- prevIsPedestrianLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian);
- prevIsBicycleLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Vehicle && (prevLaneInfo.m_vehicleType & m_vehicleTypes) == VehicleInfo.VehicleType.Bicycle);
- prevIsCenterPlatform = prevLaneInfo.m_centerPlatform;
- prevIsElevated = prevLaneInfo.m_elevated;
+ // NON-STOCK CODE START
+ bool prevIsCarLane = false;
+ // NON-STOCK CODE END
+#endif
+ int prevRelSimilarLaneIndex = 0;
+ // NON-STOCK CODE START
+ float prevMaxSpeed = 1f;
+ float prevLaneSpeed = 1f;
+ // NON-STOCK CODE END
+
+ NetInfo prevSegmentInfo = prevSegment.Info;
+ if (prevLaneIndex < prevSegmentInfo.m_lanes.Length) {
+ NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];
+ prevIsPedestrianLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian);
+ prevIsBicycleLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Vehicle && (prevLaneInfo.m_vehicleType & m_vehicleTypes) == VehicleInfo.VehicleType.Bicycle);
+ prevIsCenterPlatform = prevLaneInfo.m_centerPlatform;
+ prevIsElevated = prevLaneInfo.m_elevated;
#if (ADVANCEDAI || PARKINGAI) && ROUTING
- // NON-STOCK CODE START
- prevIsCarLane =
- (prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&
- (prevLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None
- ;
- // NON-STOCK CODE END
+ // NON-STOCK CODE START
+ prevIsCarLane =
+ (prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&
+ (prevLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None
+ ;
+ // NON-STOCK CODE END
#endif
- // NON-STOCK CODE START
+ // NON-STOCK CODE START
#if SPEEDLIMITS
- prevMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(prevSegmentId, prevLaneIndex, item.m_laneID, prevLaneInfo);
+ prevMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(prevSegmentId, prevLaneIndex, item.m_laneID, prevLaneInfo);
#else
prevMaxSpeed = prevLaneInfo.m_speedLimit;
#endif
- prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo);
- // NON-STOCK CODE END
-
- prevRelSimilarLaneIndex = (((prevLaneInfo.m_finalDirection & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? (prevLaneInfo.m_similarLaneCount - prevLaneInfo.m_similarLaneIndex - 1) : prevLaneInfo.m_similarLaneIndex);
- }
-
- if (isMiddle) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: middle: Exploring middle node\n" +
- "\t" + $"nextNodeId={nextNodeId}"
- );
- }
-#endif
- for (int i = 0; i < 8; i++) {
- ushort nextSegmentId = nextNode.GetSegment(i);
- if (nextSegmentId != 0) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: middle: Exploring next segment behind middle node\n" +
- "\t" + $"nextSegmentId={nextSegmentId}");
- }
-#endif
-
- ProcessItemCosts(
-#if DEBUG
- debug, unitId,
-#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, true, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, !prevIsPedestrianLane, prevIsPedestrianLane
- );
- }
- }
- } else if (prevIsPedestrianLane) {
- // we are going to a pedestrian lane
- if (!prevIsElevated) {
- if (nextNode.Info.m_class.m_service != ItemClass.Service.Beautification) {
- bool canCrossStreet = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;
- bool isOnCenterPlatform = prevIsCenterPlatform && (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Junction)) == NetNode.Flags.None;
- ushort nextLeftSegmentId = prevSegmentId;
- ushort nextRightSegmentId = prevSegmentId;
- int leftLaneIndex;
- int rightLaneIndex;
- uint leftLaneId;
- uint rightLaneId;
-
- prevSegment.GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, prevLaneIndex, isOnCenterPlatform, out leftLaneIndex, out rightLaneIndex, out leftLaneId, out rightLaneId);
-
- if (leftLaneId == 0 || rightLaneId == 0) {
- ushort leftSegmentId;
- ushort rightSegmentId;
- prevSegment.GetLeftAndRightSegments(nextNodeId, out leftSegmentId, out rightSegmentId);
-
- int numIter = 0;
- while (leftSegmentId != 0 && leftSegmentId != prevSegmentId && leftLaneId == 0) {
- int someLeftLaneIndex;
- int someRightLaneIndex;
- uint someLeftLaneId;
- uint someRightLaneId;
- netManager.m_segments.m_buffer[leftSegmentId].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);
-
- if (someRightLaneId != 0) {
- nextLeftSegmentId = leftSegmentId;
- leftLaneIndex = someRightLaneIndex;
- leftLaneId = someRightLaneId;
- break; // NON-STOCK CODE
- } else {
+ prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo);
+ // NON-STOCK CODE END
+
+ prevRelSimilarLaneIndex = (((prevLaneInfo.m_finalDirection & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? (prevLaneInfo.m_similarLaneCount - prevLaneInfo.m_similarLaneIndex - 1) : prevLaneInfo.m_similarLaneIndex);
+ }
+
+ if (isMiddle) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: middle: Exploring middle node\n" +
+ "\t" + $"nextNodeId={nextNodeId}"
+ );
+ }
+#endif
+ for (int i = 0; i < 8; i++) {
+ ushort nextSegmentId = nextNode.GetSegment(i);
+ if (nextSegmentId != 0) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: middle: Exploring next segment behind middle node\n" +
+ "\t" + $"nextSegmentId={nextSegmentId}");
+ }
+#endif
+
+ ProcessItemCosts(
+#if DEBUG
+ debug, unitId,
+#endif
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, true, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, !prevIsPedestrianLane, prevIsPedestrianLane
+ );
+ }
+ }
+ } else if (prevIsPedestrianLane) {
+ // we are going to a pedestrian lane
+ if (!prevIsElevated) {
+ if (nextNode.Info.m_class.m_service != ItemClass.Service.Beautification) {
+ bool canCrossStreet = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;
+ bool isOnCenterPlatform = prevIsCenterPlatform && (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Junction)) == NetNode.Flags.None;
+ ushort nextLeftSegmentId = prevSegmentId;
+ ushort nextRightSegmentId = prevSegmentId;
+ int leftLaneIndex;
+ int rightLaneIndex;
+ uint leftLaneId;
+ uint rightLaneId;
+
+ prevSegment.GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, prevLaneIndex, isOnCenterPlatform, out leftLaneIndex, out rightLaneIndex, out leftLaneId, out rightLaneId);
+
+ if (leftLaneId == 0 || rightLaneId == 0) {
+ ushort leftSegmentId;
+ ushort rightSegmentId;
+ prevSegment.GetLeftAndRightSegments(nextNodeId, out leftSegmentId, out rightSegmentId);
+
+ int numIter = 0;
+ while (leftSegmentId != 0 && leftSegmentId != prevSegmentId && leftLaneId == 0) {
+ int someLeftLaneIndex;
+ int someRightLaneIndex;
+ uint someLeftLaneId;
+ uint someRightLaneId;
+ netManager.m_segments.m_buffer[leftSegmentId].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);
+
+ if (someRightLaneId != 0) {
+ nextLeftSegmentId = leftSegmentId;
+ leftLaneIndex = someRightLaneIndex;
+ leftLaneId = someRightLaneId;
+ break; // NON-STOCK CODE
+ } else {
#if JUNCTIONRESTRICTIONS
- // next segment does not have pedestrian lanes but cims need to cross it to reach the next segment
- if (!m_junctionManager.IsPedestrianCrossingAllowed(leftSegmentId, netManager.m_segments.m_buffer[leftSegmentId].m_startNode == nextNodeId)) {
- break;
- }
-#endif
- leftSegmentId = netManager.m_segments.m_buffer[leftSegmentId].GetLeftSegment(nextNodeId);
- }
-
- if (++numIter == 8) {
- break;
- }
- }
-
- numIter = 0;
- while (rightSegmentId != 0 && rightSegmentId != prevSegmentId && rightLaneId == 0) {
- int someLeftLaneIndex;
- int someRightLaneIndex;
- uint someLeftLaneId;
- uint someRightLaneId;
- netManager.m_segments.m_buffer[rightSegmentId].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);
-
- if (someLeftLaneId != 0) {
- nextRightSegmentId = rightSegmentId;
- rightLaneIndex = someLeftLaneIndex;
- rightLaneId = someLeftLaneId;
- break; // NON-STOCK CODE
- } else {
+ // next segment does not have pedestrian lanes but cims need to cross it to reach the next segment
+ if (!m_junctionManager.IsPedestrianCrossingAllowed(leftSegmentId, netManager.m_segments.m_buffer[leftSegmentId].m_startNode == nextNodeId)) {
+ break;
+ }
+#endif
+ leftSegmentId = netManager.m_segments.m_buffer[leftSegmentId].GetLeftSegment(nextNodeId);
+ }
+
+ if (++numIter == 8) {
+ break;
+ }
+ }
+
+ numIter = 0;
+ while (rightSegmentId != 0 && rightSegmentId != prevSegmentId && rightLaneId == 0) {
+ int someLeftLaneIndex;
+ int someRightLaneIndex;
+ uint someLeftLaneId;
+ uint someRightLaneId;
+ netManager.m_segments.m_buffer[rightSegmentId].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);
+
+ if (someLeftLaneId != 0) {
+ nextRightSegmentId = rightSegmentId;
+ rightLaneIndex = someLeftLaneIndex;
+ rightLaneId = someLeftLaneId;
+ break; // NON-STOCK CODE
+ } else {
#if JUNCTIONRESTRICTIONS
- // next segment does not have pedestrian lanes but cims need to cross it to reach the next segment
- if (!m_junctionManager.IsPedestrianCrossingAllowed(rightSegmentId, netManager.m_segments.m_buffer[rightSegmentId].m_startNode == nextNodeId)) {
- break;
- }
+ // next segment does not have pedestrian lanes but cims need to cross it to reach the next segment
+ if (!m_junctionManager.IsPedestrianCrossingAllowed(rightSegmentId, netManager.m_segments.m_buffer[rightSegmentId].m_startNode == nextNodeId)) {
+ break;
+ }
#endif
- rightSegmentId = netManager.m_segments.m_buffer[rightSegmentId].GetRightSegment(nextNodeId);
- }
-
- if (++numIter == 8) {
- break;
- }
- }
- }
+ rightSegmentId = netManager.m_segments.m_buffer[rightSegmentId].GetRightSegment(nextNodeId);
+ }
+
+ if (++numIter == 8) {
+ break;
+ }
+ }
+ }
- if (leftLaneId != 0 && (nextLeftSegmentId != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: ped -> ped: Exploring left pedestrian lane\n" +
- "\t" + $"leftLaneId={leftLaneId}\n" +
- "\t" + $"nextLeftSegmentId={nextLeftSegmentId}\n" +
- "\t" + $"canCrossStreet={canCrossStreet}\n" +
- "\t" + $"isOnCenterPlatform={isOnCenterPlatform}"
- );
- }
+ if (leftLaneId != 0 && (nextLeftSegmentId != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: ped -> ped: Exploring left pedestrian lane\n" +
+ "\t" + $"leftLaneId={leftLaneId}\n" +
+ "\t" + $"nextLeftSegmentId={nextLeftSegmentId}\n" +
+ "\t" + $"canCrossStreet={canCrossStreet}\n" +
+ "\t" + $"isOnCenterPlatform={isOnCenterPlatform}"
+ );
+ }
#endif
- ProcessItemPedBicycle(
+ ProcessItemPedBicycle(
#if DEBUG
- debug, unitId,
+ debug, unitId,
#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextLeftSegmentId, ref netManager.m_segments.m_buffer[nextLeftSegmentId], nextNodeId, ref nextNode, leftLaneIndex, leftLaneId, ref netManager.m_lanes.m_buffer[leftLaneId], connectOffset, connectOffset);
- }
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextLeftSegmentId, ref netManager.m_segments.m_buffer[nextLeftSegmentId], nextNodeId, ref nextNode, leftLaneIndex, leftLaneId, ref netManager.m_lanes.m_buffer[leftLaneId], connectOffset, connectOffset);
+ }
- if (rightLaneId != 0 && rightLaneId != leftLaneId && (nextRightSegmentId != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {
+ if (rightLaneId != 0 && rightLaneId != leftLaneId && (nextRightSegmentId != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: ped -> ped: Exploring right pedestrian lane\n" +
- "\t" + $"leftLaneId={leftLaneId}\n" +
- "\t" + $"rightLaneId={rightLaneId}\n" +
- "\t" + $"nextRightSegmentId={nextRightSegmentId}\n" +
- "\t" + $"canCrossStreet={canCrossStreet}\n" +
- "\t" + $"isOnCenterPlatform={isOnCenterPlatform}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: ped -> ped: Exploring right pedestrian lane\n" +
+ "\t" + $"leftLaneId={leftLaneId}\n" +
+ "\t" + $"rightLaneId={rightLaneId}\n" +
+ "\t" + $"nextRightSegmentId={nextRightSegmentId}\n" +
+ "\t" + $"canCrossStreet={canCrossStreet}\n" +
+ "\t" + $"isOnCenterPlatform={isOnCenterPlatform}"
+ );
+ }
#endif
- ProcessItemPedBicycle(
+ ProcessItemPedBicycle(
#if DEBUG
- debug, unitId,
+ debug, unitId,
#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextRightSegmentId, ref netManager.m_segments.m_buffer[nextRightSegmentId], nextNodeId, ref nextNode, rightLaneIndex, rightLaneId, ref netManager.m_lanes.m_buffer[rightLaneId], connectOffset, connectOffset);
- }
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextRightSegmentId, ref netManager.m_segments.m_buffer[nextRightSegmentId], nextNodeId, ref nextNode, rightLaneIndex, rightLaneId, ref netManager.m_lanes.m_buffer[rightLaneId], connectOffset, connectOffset);
+ }
- // switch from bicycle lane to pedestrian lane
- int nextLaneIndex;
- uint nextLaneId;
- if ((m_vehicleTypes & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None &&
- prevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Bicycle, out nextLaneIndex, out nextLaneId)) {
+ // switch from bicycle lane to pedestrian lane
+ int nextLaneIndex;
+ uint nextLaneId;
+ if ((m_vehicleTypes & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None &&
+ prevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Bicycle, out nextLaneIndex, out nextLaneId)) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: bicycle -> ped: Exploring bicycle switch\n" +
- "\t" + $"leftLaneId={leftLaneId}\n" +
- "\t" + $"rightLaneId={rightLaneId}\n" +
- "\t" + $"nextRightSegmentId={nextRightSegmentId}\n" +
- "\t" + $"canCrossStreet={canCrossStreet}\n" +
- "\t" + $"isOnCenterPlatform={isOnCenterPlatform}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: bicycle -> ped: Exploring bicycle switch\n" +
+ "\t" + $"leftLaneId={leftLaneId}\n" +
+ "\t" + $"rightLaneId={rightLaneId}\n" +
+ "\t" + $"nextRightSegmentId={nextRightSegmentId}\n" +
+ "\t" + $"canCrossStreet={canCrossStreet}\n" +
+ "\t" + $"isOnCenterPlatform={isOnCenterPlatform}"
+ );
+ }
#endif
- ProcessItemPedBicycle(
+ ProcessItemPedBicycle(
#if DEBUG
- debug, unitId,
+ debug, unitId,
#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, nextLaneIndex, nextLaneId, ref netManager.m_lanes.m_buffer[nextLaneId], connectOffset, connectOffset);
- }
- } else {
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, nextLaneIndex, nextLaneId, ref netManager.m_lanes.m_buffer[nextLaneId], connectOffset, connectOffset);
+ }
+ } else {
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: beautification -> ped: Exploring pedestrian lane to beautficiation node\n" +
- "\t" + $"nextNodeId={nextNodeId}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: beautification -> ped: Exploring pedestrian lane to beautficiation node\n" +
+ "\t" + $"nextNodeId={nextNodeId}"
+ );
+ }
#endif
- // we are going from pedestrian lane to a beautification node
- for (int j = 0; j < 8; j++) {
- ushort nextSegmentId = nextNode.GetSegment(j);
- if (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {
+ // we are going from pedestrian lane to a beautification node
+ for (int j = 0; j < 8; j++) {
+ ushort nextSegmentId = nextNode.GetSegment(j);
+ if (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: beautification -> ped: Exploring next segment behind beautification node\n" +
- "\t" + $"nextSegmentId={nextSegmentId}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: beautification -> ped: Exploring next segment behind beautification node\n" +
+ "\t" + $"nextSegmentId={nextSegmentId}"
+ );
+ }
#endif
- ProcessItemCosts(
+ ProcessItemCosts(
#if DEBUG
- debug, unitId,
+ debug, unitId,
#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, false, true);
- }
- }
- }
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, false, true);
+ }
+ }
+ }
- // prepare switching from a vehicle to pedestrian lane
- NetInfo.LaneType nextLaneType = m_laneTypes & ~NetInfo.LaneType.Pedestrian;
- VehicleInfo.VehicleType nextVehicleType = m_vehicleTypes & ~VehicleInfo.VehicleType.Bicycle;
- if ((item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
- nextLaneType &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
- }
+ // prepare switching from a vehicle to pedestrian lane
+ NetInfo.LaneType nextLaneType = m_laneTypes & ~NetInfo.LaneType.Pedestrian;
+ VehicleInfo.VehicleType nextVehicleType = m_vehicleTypes & ~VehicleInfo.VehicleType.Bicycle;
+ if ((item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
+ nextLaneType &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
+ }
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> ped: Prepared parameters\n" +
- "\t" + $"m_queueItem.vehicleType={m_queueItem.vehicleType}\n" +
- "\t" + $"nextVehicleType={nextVehicleType}\n" +
- "\t" + $"nextLaneType={nextLaneType}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> ped: Prepared parameters\n" +
+ "\t" + $"m_queueItem.vehicleType={m_queueItem.vehicleType}\n" +
+ "\t" + $"nextVehicleType={nextVehicleType}\n" +
+ "\t" + $"nextLaneType={nextLaneType}"
+ );
+ }
#endif
- // NON-STOCK CODE START
- bool parkingAllowed = true;
+ // NON-STOCK CODE START
+ bool parkingAllowed = true;
#if PARKINGAI
- // Parking AI: Determine if parking is allowed
- if (Options.parkingAI) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> ped: Parking AI: Determining if parking is allowed here\n" +
- "\t" + $"m_queueItem.vehicleType={m_queueItem.vehicleType}\n" +
- "\t" + $"nextVehicleType={nextVehicleType}\n" +
- "\t" + $"nextLaneType={nextLaneType}\n" +
- "\t" + $"item.m_lanesUsed={item.m_lanesUsed}\n" +
- "\t" + $"m_endLaneA={m_endLaneA}\n" +
- "\t" + $"m_endLaneB={m_endLaneB}"
- );
- }
-#endif
-
- if (m_queueItem.vehicleType == ExtVehicleType.PassengerCar &&
- (nextVehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None &&
- ((nextLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None)) {
- if ((item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
- /* if pocket cars are prohibited, a citizen may only park their car once per path */
- parkingAllowed = false;
- } else if ((item.m_lanesUsed & NetInfo.LaneType.PublicTransport) == NetInfo.LaneType.None) {
- /* if the citizen is walking to their target (= no public transport used), the passenger car must be parked in the very last moment */
- parkingAllowed = item.m_laneID == m_endLaneA || item.m_laneID == m_endLaneB;
- }
- }
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> ped: Parking AI: Parking allowed here? {parkingAllowed}");
- }
-#endif
- }
-#endif
- // NON-STOCK CODE END
-
- int sameSegLaneIndex;
- uint sameSegLaneId;
- if (parkingAllowed && // NON-STOCK CODE
- nextLaneType != NetInfo.LaneType.None &&
- nextVehicleType != VehicleInfo.VehicleType.None &&
- prevSegment.GetClosestLane(prevLaneIndex, nextLaneType, nextVehicleType, out sameSegLaneIndex, out sameSegLaneId)
- ) {
- NetInfo.Lane sameSegLaneInfo = prevSegmentInfo.m_lanes[sameSegLaneIndex];
- byte sameSegConnectOffset = (byte)(((prevSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None == ((sameSegLaneInfo.m_finalDirection & NetInfo.Direction.Backward) != NetInfo.Direction.None)) ? 1 : 254);
- BufferItem nextItem = item;
- if (m_randomParking) {
- nextItem.m_comparisonValue += (float)m_pathRandomizer.Int32(300u) / m_maxLength;
- }
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> ped: Exploring parking\n" +
- "\t" + $"nextLaneType={nextLaneType}\n" +
- "\t" + $"nextVehicleType={nextVehicleType}\n" +
- "\t" + $"nextLaneType={nextLaneType}\n" +
- "\t" + $"sameSegConnectOffset={sameSegConnectOffset}\n" +
- "\t" + $"m_randomParking={m_randomParking}"
- );
- }
-#endif
-
- ProcessItemPedBicycle(
-#if DEBUG
- debug, unitId,
-#endif
- nextItem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, sameSegLaneIndex, sameSegLaneId, ref netManager.m_lanes.m_buffer[sameSegLaneId], sameSegConnectOffset, 128);
- }
- }
- } else {
- // We are going to a non-pedestrian lane
-
- bool nextIsBeautificationNode = nextNode.Info.m_class.m_service == ItemClass.Service.Beautification; // NON-STOCK CODE (refactored)
- bool allowPedestrian = (m_laneTypes & NetInfo.LaneType.Pedestrian) != NetInfo.LaneType.None; // allow switching from pedestrian lane to a non-pedestrian lane?
- bool allowBicycle = false; // allow switching from a pedestrian lane to a bike lane?
- byte switchConnectOffset = 0; // lane switching offset
- if (allowPedestrian) {
- if (prevIsBicycleLane) {
- // we are going to a bicycle lane
- switchConnectOffset = connectOffset;
- allowBicycle = nextIsBeautificationNode;
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Switching to a bicycle may be allowed here\n" +
- "\t" + $"switchConnectOffset={switchConnectOffset}\n" +
- "\t" + $"allowBicycle={allowBicycle}"
- );
- }
-#endif
- } else if (m_vehicleLane != 0) {
- // there is a parked vehicle position
- if (m_vehicleLane != item.m_laneID) {
- // we have not reached the parked vehicle yet
- allowPedestrian = false;
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Entering a parked vehicle is not allowed here");
- }
-#endif
- } else {
- // pedestrian switches to parked vehicle
- switchConnectOffset = m_vehicleOffset;
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Entering a parked vehicle is allowed here\n" +
- "\t" + $"switchConnectOffset={switchConnectOffset}"
- );
- }
-#endif
- }
- } else if (m_stablePath) {
- // enter a bus
- switchConnectOffset = 128;
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Entering a bus is allowed here\n" +
- "\t" + $"switchConnectOffset={switchConnectOffset}"
- );
- }
-#endif
- } else {
- // pocket car spawning
+ // Parking AI: Determine if parking is allowed
+ if (Options.parkingAI) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> ped: Parking AI: Determining if parking is allowed here\n" +
+ "\t" + $"m_queueItem.vehicleType={m_queueItem.vehicleType}\n" +
+ "\t" + $"nextVehicleType={nextVehicleType}\n" +
+ "\t" + $"nextLaneType={nextLaneType}\n" +
+ "\t" + $"item.m_lanesUsed={item.m_lanesUsed}\n" +
+ "\t" + $"m_endLaneA={m_endLaneA}\n" +
+ "\t" + $"m_endLaneB={m_endLaneB}"
+ );
+ }
+#endif
+
+ if (m_queueItem.vehicleType == ExtVehicleType.PassengerCar &&
+ (nextVehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None &&
+ ((nextLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None)) {
+ if ((item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
+ /* if pocket cars are prohibited, a citizen may only park their car once per path */
+ parkingAllowed = false;
+ } else if ((item.m_lanesUsed & NetInfo.LaneType.PublicTransport) == NetInfo.LaneType.None) {
+ /* if the citizen is walking to their target (= no public transport used), the passenger car must be parked in the very last moment */
+ parkingAllowed = item.m_laneID == m_endLaneA || item.m_laneID == m_endLaneB;
+ }
+ }
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> ped: Parking AI: Parking allowed here? {parkingAllowed}");
+ }
+#endif
+ }
+#endif
+ // NON-STOCK CODE END
+
+ int sameSegLaneIndex;
+ uint sameSegLaneId;
+ if (parkingAllowed && // NON-STOCK CODE
+ nextLaneType != NetInfo.LaneType.None &&
+ nextVehicleType != VehicleInfo.VehicleType.None &&
+ prevSegment.GetClosestLane(prevLaneIndex, nextLaneType, nextVehicleType, out sameSegLaneIndex, out sameSegLaneId)
+ ) {
+ NetInfo.Lane sameSegLaneInfo = prevSegmentInfo.m_lanes[sameSegLaneIndex];
+ byte sameSegConnectOffset = (byte)(((prevSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None == ((sameSegLaneInfo.m_finalDirection & NetInfo.Direction.Backward) != NetInfo.Direction.None)) ? 1 : 254);
+ BufferItem nextItem = item;
+ if (m_randomParking) {
+ nextItem.m_comparisonValue += (float)m_pathRandomizer.Int32(300u) / m_maxLength;
+ }
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> ped: Exploring parking\n" +
+ "\t" + $"nextLaneType={nextLaneType}\n" +
+ "\t" + $"nextVehicleType={nextVehicleType}\n" +
+ "\t" + $"nextLaneType={nextLaneType}\n" +
+ "\t" + $"sameSegConnectOffset={sameSegConnectOffset}\n" +
+ "\t" + $"m_randomParking={m_randomParking}"
+ );
+ }
+#endif
+
+ ProcessItemPedBicycle(
+#if DEBUG
+ debug, unitId,
+#endif
+ nextItem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, sameSegLaneIndex, sameSegLaneId, ref netManager.m_lanes.m_buffer[sameSegLaneId], sameSegConnectOffset, 128);
+ }
+ }
+ } else {
+ // We are going to a non-pedestrian lane
+
+ bool nextIsBeautificationNode = nextNode.Info.m_class.m_service == ItemClass.Service.Beautification; // NON-STOCK CODE (refactored)
+ bool allowPedestrian = (m_laneTypes & NetInfo.LaneType.Pedestrian) != NetInfo.LaneType.None; // allow switching from pedestrian lane to a non-pedestrian lane?
+ bool allowBicycle = false; // allow switching from a pedestrian lane to a bike lane?
+ byte switchConnectOffset = 0; // lane switching offset
+ if (allowPedestrian) {
+ if (prevIsBicycleLane) {
+ // we are going to a bicycle lane
+ switchConnectOffset = connectOffset;
+ allowBicycle = nextIsBeautificationNode;
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Switching to a bicycle may be allowed here\n" +
+ "\t" + $"switchConnectOffset={switchConnectOffset}\n" +
+ "\t" + $"allowBicycle={allowBicycle}"
+ );
+ }
+#endif
+ } else if (m_vehicleLane != 0) {
+ // there is a parked vehicle position
+ if (m_vehicleLane != item.m_laneID) {
+ // we have not reached the parked vehicle yet
+ allowPedestrian = false;
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Entering a parked vehicle is not allowed here");
+ }
+#endif
+ } else {
+ // pedestrian switches to parked vehicle
+ switchConnectOffset = m_vehicleOffset;
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Entering a parked vehicle is allowed here\n" +
+ "\t" + $"switchConnectOffset={switchConnectOffset}"
+ );
+ }
+#endif
+ }
+ } else if (m_stablePath) {
+ // enter a bus
+ switchConnectOffset = 128;
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Entering a bus is allowed here\n" +
+ "\t" + $"switchConnectOffset={switchConnectOffset}"
+ );
+ }
+#endif
+ } else {
+ // pocket car spawning
#if PARKINGAI
- if (
- Options.parkingAI
- ) {
+ if (
+ Options.parkingAI
+ ) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Parking AI: Determining if spawning pocket cars is allowed\n" +
- "\t" + $"m_queueItem.pathType={m_queueItem.pathType}\n" +
- "\t" + $"prevIsCarLane={prevIsCarLane}\n" +
- "\t" + $"m_queueItem.vehicleType={m_queueItem.vehicleType}\n" +
- "\t" + $"m_startSegmentA={m_startSegmentA}\n" +
- "\t" + $"m_startSegmentB={m_startSegmentB}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Parking AI: Determining if spawning pocket cars is allowed\n" +
+ "\t" + $"m_queueItem.pathType={m_queueItem.pathType}\n" +
+ "\t" + $"prevIsCarLane={prevIsCarLane}\n" +
+ "\t" + $"m_queueItem.vehicleType={m_queueItem.vehicleType}\n" +
+ "\t" + $"m_startSegmentA={m_startSegmentA}\n" +
+ "\t" + $"m_startSegmentB={m_startSegmentB}"
+ );
+ }
#endif
- if (
- (m_queueItem.pathType == ExtPathType.WalkingOnly && prevIsCarLane) ||
- (
- m_queueItem.pathType == ExtPathType.DrivingOnly &&
- m_queueItem.vehicleType == ExtVehicleType.PassengerCar &&
- ((item.m_position.m_segment != m_startSegmentA && item.m_position.m_segment != m_startSegmentB) || !prevIsCarLane)
- )
- ) {
- /* allow pocket cars only if an instant driving path is required and we are at the start segment */
- /* disallow pocket cars on walking paths */
- allowPedestrian = false;
+ if (
+ (m_queueItem.pathType == ExtPathType.WalkingOnly && prevIsCarLane) ||
+ (
+ m_queueItem.pathType == ExtPathType.DrivingOnly &&
+ m_queueItem.vehicleType == ExtVehicleType.PassengerCar &&
+ ((item.m_position.m_segment != m_startSegmentA && item.m_position.m_segment != m_startSegmentB) || !prevIsCarLane)
+ )
+ ) {
+ /* allow pocket cars only if an instant driving path is required and we are at the start segment */
+ /* disallow pocket cars on walking paths */
+ allowPedestrian = false;
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Parking AI: Spawning pocket cars is not allowed here");
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Parking AI: Spawning pocket cars is not allowed here");
+ }
#endif
- } else {
- switchConnectOffset = (byte)m_pathRandomizer.UInt32(1u, 254u);
+ } else {
+ switchConnectOffset = (byte)m_pathRandomizer.UInt32(1u, 254u);
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Parking AI: Spawning pocket cars is allowed here\n" +
- "\t" + $"switchConnectOffset={switchConnectOffset}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Parking AI: Spawning pocket cars is allowed here\n" +
+ "\t" + $"switchConnectOffset={switchConnectOffset}"
+ );
+ }
#endif
- }
- } else {
+ }
+ } else {
#endif
- switchConnectOffset = (byte)m_pathRandomizer.UInt32(1u, 254u);
+ switchConnectOffset = (byte)m_pathRandomizer.UInt32(1u, 254u);
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Spawning pocket cars is allowed here\n" +
- "\t" + $"switchConnectOffset={switchConnectOffset}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Spawning pocket cars is allowed here\n" +
+ "\t" + $"switchConnectOffset={switchConnectOffset}"
+ );
+ }
#endif
#if PARKINGAI
- }
+ }
#endif
- }
- }
+ }
+ }
- ushort nextSegmentId = 0;
- if ((m_vehicleTypes & (VehicleInfo.VehicleType.Ferry
+ ushort nextSegmentId = 0;
+ if ((m_vehicleTypes & (VehicleInfo.VehicleType.Ferry
#if !ROUTING
| VehicleInfo.VehicleType.Monorail
#endif
- )) != VehicleInfo.VehicleType.None) {
- // ferry (/ monorail)
+ )) != VehicleInfo.VehicleType.None) {
+ // ferry (/ monorail)
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Exploring ferry routes");
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Exploring ferry routes");
+ }
#endif
- bool isUturnAllowedHere = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;
- for (int k = 0; k < 8; k++) {
- nextSegmentId = nextNode.GetSegment(k);
- if (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {
+ bool isUturnAllowedHere = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;
+ for (int k = 0; k < 8; k++) {
+ nextSegmentId = nextNode.GetSegment(k);
+ if (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Exploring ferry route\n" +
- "\t" + $"nextSegmentId={nextSegmentId}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Exploring ferry route\n" +
+ "\t" + $"nextSegmentId={nextSegmentId}"
+ );
+ }
#endif
- ProcessItemCosts(
+ ProcessItemCosts(
#if DEBUG
- debug, unitId,
+ debug, unitId,
#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, allowBicycle);
- }
- }
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, allowBicycle);
+ }
+ }
- if (isUturnAllowedHere
+ if (isUturnAllowedHere
#if !ROUTING
&& (m_vehicleTypes & VehicleInfo.VehicleType.Monorail) == VehicleInfo.VehicleType.None
#endif
- ) {
+ ) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Exploring ferry u-turn");
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Exploring ferry u-turn");
+ }
#endif
- nextSegmentId = prevSegmentId;
- ProcessItemCosts(
+ nextSegmentId = prevSegmentId;
+ ProcessItemCosts(
#if DEBUG
- debug, unitId,
+ debug, unitId,
#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, false);
- }
- } else {
- // road vehicles / trams / trains / metros (/ monorails) / etc.
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, false);
+ }
+ } else {
+ // road vehicles / trams / trains / metros (/ monorails) / etc.
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Exploring vehicle routes");
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Exploring vehicle routes");
+ }
#endif
#if ROUTING
- bool exploreUturn = false;
+ bool exploreUturn = false;
#else
bool exploreUturn = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;
#endif
#if ROUTING
- bool prevIsRouted = false;
- uint laneRoutingIndex = 0;
- bool nextIsStartNode = nextNodeId == prevSegment.m_startNode;
- if (nextIsStartNode || nextNodeId == prevSegment.m_endNode) {
- laneRoutingIndex = m_routingManager.GetLaneEndRoutingIndex(item.m_laneID, nextIsStartNode);
- prevIsRouted = m_routingManager.LaneEndBackwardRoutings[laneRoutingIndex].routed;
+ bool prevIsRouted = false;
+ uint laneRoutingIndex = 0;
+ bool nextIsStartNode = nextNodeId == prevSegment.m_startNode;
+ if (nextIsStartNode || nextNodeId == prevSegment.m_endNode) {
+ laneRoutingIndex = m_routingManager.GetLaneEndRoutingIndex(item.m_laneID, nextIsStartNode);
+ prevIsRouted = m_routingManager.LaneEndBackwardRoutings[laneRoutingIndex].routed;
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Is previous segment routed? {prevIsRouted}");
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Is previous segment routed? {prevIsRouted}");
+ }
#endif
- }
+ }
- if (allowBicycle || !prevIsRouted) {
- /*
- * pedestrian to bicycle lane switch or no routing information available:
- * if pedestrian lanes should be explored (allowBicycle == true): do this here
- * if previous segment has custom routing (prevIsRouted == true): do NOT explore vehicle lanes here, else: vanilla exploration of vehicle lanes
- */
+ if (allowBicycle || !prevIsRouted) {
+ /*
+ * pedestrian to bicycle lane switch or no routing information available:
+ * if pedestrian lanes should be explored (allowBicycle == true): do this here
+ * if previous segment has custom routing (prevIsRouted == true): do NOT explore vehicle lanes here, else: vanilla exploration of vehicle lanes
+ */
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: bicycle -> vehicle / stock vehicle routing\n"
- + "\t" + $"prevIsRouted={prevIsRouted}\n"
- + "\t" + $"allowBicycle={allowBicycle}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: bicycle -> vehicle / stock vehicle routing\n"
+ + "\t" + $"prevIsRouted={prevIsRouted}\n"
+ + "\t" + $"allowBicycle={allowBicycle}"
+ );
+ }
#endif
- // NON-STOCK CODE END
+ // NON-STOCK CODE END
#endif
- nextSegmentId = prevSegment.GetRightSegment(nextNodeId);
- for (int l = 0; l < 8; l++) {
- if (nextSegmentId == 0) {
- break;
- }
+ nextSegmentId = prevSegment.GetRightSegment(nextNodeId);
+ for (int l = 0; l < 8; l++) {
+ if (nextSegmentId == 0) {
+ break;
+ }
- if (nextSegmentId == prevSegmentId) {
- break;
- }
+ if (nextSegmentId == prevSegmentId) {
+ break;
+ }
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: bicycle -> vehicle / stock vehicle routing: exploring next segment\n"
- + "\t" + $"nextSegmentId={nextSegmentId}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: bicycle -> vehicle / stock vehicle routing: exploring next segment\n"
+ + "\t" + $"nextSegmentId={nextSegmentId}"
+ );
+ }
#endif
- if (ProcessItemCosts(
+ if (ProcessItemCosts(
#if DEBUG
- debug, unitId,
+ debug, unitId,
#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset,
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset,
#if ROUTING
- !prevIsRouted // NON-STOCK CODE
+ !prevIsRouted // NON-STOCK CODE
#else
true
#endif
- , allowBicycle)
- ) {
- exploreUturn = true; // allow exceptional u-turns
+ , allowBicycle)
+ ) {
+ exploreUturn = true; // allow exceptional u-turns
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: bicycle -> vehicle / stock vehicle routing: exceptional u-turn allowed\n"
- + "\t" + $"nextSegmentId={nextSegmentId}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: bicycle -> vehicle / stock vehicle routing: exceptional u-turn allowed\n"
+ + "\t" + $"nextSegmentId={nextSegmentId}"
+ );
+ }
#endif
- }
+ }
- nextSegmentId = netManager.m_segments.m_buffer[nextSegmentId].GetRightSegment(nextNodeId);
- }
+ nextSegmentId = netManager.m_segments.m_buffer[nextSegmentId].GetRightSegment(nextNodeId);
+ }
#if ROUTING
- } // NON-STOCK CODE
+ } // NON-STOCK CODE
#endif
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Custom routing\n"
- + "\t" + $"Options.advancedAI={Options.advancedAI}\n"
- + "\t" + $"prevIsRouted={prevIsRouted}\n"
- + "\t" + $"m_isRoadVehicle={m_isRoadVehicle}\n"
- + "\t" + $"prevIsCarLane={prevIsCarLane}\n"
- + "\t" + $"m_stablePath={Options.advancedAI}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Custom routing\n"
+ + "\t" + $"Options.advancedAI={Options.advancedAI}\n"
+ + "\t" + $"prevIsRouted={prevIsRouted}\n"
+ + "\t" + $"m_isRoadVehicle={m_isRoadVehicle}\n"
+ + "\t" + $"prevIsCarLane={prevIsCarLane}\n"
+ + "\t" + $"m_stablePath={Options.advancedAI}"
+ );
+ }
#endif
- // NON-STOCK CODE START
- float segmentSelectionCost = 1f;
- float laneSelectionCost = 1f;
- float laneChangingCost = 1f;
- bool enableAdvancedAI = false;
- // NON-STOCK CODE END
+ // NON-STOCK CODE START
+ float segmentSelectionCost = 1f;
+ float laneSelectionCost = 1f;
+ float laneChangingCost = 1f;
+ bool enableAdvancedAI = false;
+ // NON-STOCK CODE END
#if ADVANCEDAI && ROUTING
- /*
- * =======================================================================================================
- * Calculate Advanced Vehicle AI cost factors
- * =======================================================================================================
- */
- if (
- Options.advancedAI &&
- prevIsRouted &&
- m_isRoadVehicle &&
- prevIsCarLane
- ) {
- enableAdvancedAI = true;
- if (!m_stablePath) {
- CalculateAdvancedAiCostFactors(
-#if DEBUG
- debug, unitId,
-#endif
- ref item, ref prevSegment, ref prevLane, nextNodeId, ref nextNode, ref segmentSelectionCost, ref laneSelectionCost, ref laneChangingCost
- );
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Custom routing with activated Advanced Vehicle AI: Calculated cost factors\n"
- + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
- + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
- + "\t" + $"laneChangingCost={laneChangingCost}"
- );
- }
-#endif
- } else {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Custom routing with activated Advanced Vehicle AI and stable path: Using default cost factors\n"
- + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
- + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
- + "\t" + $"laneChangingCost={laneChangingCost}"
- );
- }
-#endif
- }
- }
+ /*
+ * =======================================================================================================
+ * Calculate Advanced Vehicle AI cost factors
+ * =======================================================================================================
+ */
+ if (
+ Options.advancedAI &&
+ prevIsRouted &&
+ m_isRoadVehicle &&
+ prevIsCarLane
+ ) {
+ enableAdvancedAI = true;
+ if (!m_stablePath) {
+ CalculateAdvancedAiCostFactors(
+#if DEBUG
+ debug, unitId,
+#endif
+ ref item, ref prevSegment, ref prevLane, nextNodeId, ref nextNode, ref segmentSelectionCost, ref laneSelectionCost, ref laneChangingCost
+ );
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Custom routing with activated Advanced Vehicle AI: Calculated cost factors\n"
+ + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
+ + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
+ + "\t" + $"laneChangingCost={laneChangingCost}"
+ );
+ }
+#endif
+ } else {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Custom routing with activated Advanced Vehicle AI and stable path: Using default cost factors\n"
+ + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
+ + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
+ + "\t" + $"laneChangingCost={laneChangingCost}"
+ );
+ }
+#endif
+ }
+ }
#endif
#if ROUTING
- if (prevIsRouted) {
+ if (prevIsRouted) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Custom routing: Exploring custom routes");
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Custom routing: Exploring custom routes");
+ }
#endif
- exploreUturn = false; // custom routing processes regular u-turns
- if (ProcessItemRouted(
+ exploreUturn = false; // custom routing processes regular u-turns
+ if (ProcessItemRouted(
#if DEBUG
- debug, unitId,
+ debug, unitId,
#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed
#if ADVANCEDAI
- , enableAdvancedAI, laneChangingCost,
+ , enableAdvancedAI, laneChangingCost,
#endif
- segmentSelectionCost, laneSelectionCost, nextNodeId, ref nextNode, false, m_routingManager.SegmentRoutings[prevSegmentId], m_routingManager.LaneEndBackwardRoutings[laneRoutingIndex], connectOffset, prevRelSimilarLaneIndex
- )) {
- exploreUturn = true; // allow exceptional u-turns
- }
- } else {
+ segmentSelectionCost, laneSelectionCost, nextNodeId, ref nextNode, false, m_routingManager.SegmentRoutings[prevSegmentId], m_routingManager.LaneEndBackwardRoutings[laneRoutingIndex], connectOffset, prevRelSimilarLaneIndex
+ )) {
+ exploreUturn = true; // allow exceptional u-turns
+ }
+ } else {
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Custom routing: No custom routing present");
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Custom routing: No custom routing present");
+ }
#endif
- if (!exploreUturn) {
- // no exceptional u-turns allowed: allow regular u-turns
- exploreUturn = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;
+ if (!exploreUturn) {
+ // no exceptional u-turns allowed: allow regular u-turns
+ exploreUturn = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Custom routing: Allowing regular u-turns:\n"
- + "\t" + $"exploreUturn={exploreUturn}\n"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Custom routing: Allowing regular u-turns:\n"
+ + "\t" + $"exploreUturn={exploreUturn}\n"
+ );
+ }
#endif
- }
- }
+ }
+ }
#endif
- if (exploreUturn && (m_vehicleTypes & VehicleInfo.VehicleType.Tram) == VehicleInfo.VehicleType.None) {
+ if (exploreUturn && (m_vehicleTypes & VehicleInfo.VehicleType.Tram) == VehicleInfo.VehicleType.None) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Exploring stock u-turn\n"
- + "\t" + $"exploreUturn={exploreUturn}\n"
- );
- }
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: vehicle -> vehicle: Exploring stock u-turn\n"
+ + "\t" + $"exploreUturn={exploreUturn}\n"
+ );
+ }
#endif
- ProcessItemCosts(
+ ProcessItemCosts(
#if DEBUG
- debug, unitId,
+ debug, unitId,
#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed,
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed,
#if ADVANCEDAI && ROUTING
- false, 0f,
+ false, 0f,
#endif
- nextNodeId, ref nextNode, false, prevSegmentId, ref prevSegment,
+ nextNodeId, ref nextNode, false, prevSegmentId, ref prevSegment,
#if ROUTING
- segmentSelectionCost, laneSelectionCost, null,
+ segmentSelectionCost, laneSelectionCost, null,
#endif
- ref prevRelSimilarLaneIndex, connectOffset, true, false
- );
- }
- }
+ ref prevRelSimilarLaneIndex, connectOffset, true, false
+ );
+ }
+ }
- if (allowPedestrian) {
- int nextLaneIndex;
- uint nextLaneId;
- if (prevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Pedestrian, m_vehicleTypes, out nextLaneIndex, out nextLaneId)) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Exploring switch\n"
- + "\t" + $"nextLaneIndex={nextLaneIndex}\n"
- + "\t" + $"nextLaneId={nextLaneId}"
- );
- }
-#endif
-
- ProcessItemPedBicycle(
-#if DEBUG
- debug, unitId,
-#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, nextLaneIndex, nextLaneId, ref netManager.m_lanes.m_buffer[nextLaneId], switchConnectOffset, switchConnectOffset);
- }
- }
- }
-
- if (nextNode.m_lane != 0) {
- bool targetDisabled = (nextNode.m_flags & (NetNode.Flags.Disabled | NetNode.Flags.DisableOnlyMiddle)) == NetNode.Flags.Disabled;
- ushort nextSegmentId = netManager.m_lanes.m_buffer[nextNode.m_lane].m_segment;
- if (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemMain: transport -> *: Exploring special node\n"
- + "\t" + $"nextSegmentId={nextSegmentId}\n"
- + "\t" + $"nextNode.m_lane={nextNode.m_lane}\n"
- + "\t" + $"targetDisabled={targetDisabled}\n"
- + "\t" + $"nextNodeId={nextNodeId}"
- );
- }
-#endif
-
- ProcessItemPublicTransport(
-#if DEBUG
- debug, unitId,
-#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, targetDisabled, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], nextNode.m_lane, nextNode.m_laneOffset, connectOffset);
- }
- }
- }
-
- // 2
- private void ProcessItemPublicTransport(
-#if DEBUG
- bool debug, uint unitId,
-#endif
- BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed, ushort nextNodeId, bool targetDisabled, ushort nextSegmentId, ref NetSegment nextSegment, uint nextLaneId, byte offset, byte connectOffset) {
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemPublicTransport called.\n"
- + "\t" + $"prevMaxSpeed={prevMaxSpeed}\n"
- + "\t" + $"prevLaneSpeed={prevLaneSpeed}\n"
- + "\t" + $"nextNodeId={nextNodeId}\n"
- + "\t" + $"targetDisabled={targetDisabled}\n"
- + "\t" + $"nextLaneId={nextLaneId}\n"
- + "\t" + $"offset={offset}\n"
- + "\t" + $"connectOffset={connectOffset}"
- );
- }
-#endif
-
- if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemPublicTransport: Aborting: Disable mask\n"
- + "\t" + $"m_disableMask={m_disableMask}\n"
- + "\t" + $"nextSegment.m_flags={nextSegment.m_flags}\n");
- }
-#endif
- return;
- }
-
- NetManager netManager = Singleton.instance;
- if (targetDisabled && ((netManager.m_nodes.m_buffer[nextSegment.m_startNode].m_flags | netManager.m_nodes.m_buffer[nextSegment.m_endNode].m_flags) & NetNode.Flags.Disabled) == NetNode.Flags.None) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemPublicTransport: Aborting: Target disabled");
- }
-#endif
- return;
- }
-
- NetInfo nextSegmentInfo = nextSegment.Info;
- NetInfo prevSegmentInfo = prevSegment.Info;
- int nextNumLanes = nextSegmentInfo.m_lanes.Length;
- // float prevMaxSpeed = 1f; // stock code commented
- // float prevLaneSpeed = 1f; // stock code commented
- NetInfo.LaneType prevLaneType = NetInfo.LaneType.None;
- if (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
- NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];
- // prevMaxSpeed = prevLaneInfo.m_speedLimit; // stock code commented
- // prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // stock code commented
- prevLaneType = prevLaneInfo.m_laneType;
- if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
- prevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
- }
- }
-
- float prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? prevSegment.m_averageLength : prevLane.m_length;
- float offsetLength = (float)Mathf.Abs(connectOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength;
- float methodDistance = item.m_methodDistance + offsetLength;
- float comparisonValue = item.m_comparisonValue + offsetLength / (prevLaneSpeed * m_maxLength);
- float duration = item.m_duration + offsetLength / prevMaxSpeed;
- Vector3 b = prevLane.CalculatePosition((float)(int)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
-
- if (!m_ignoreCost) {
- int ticketCost = prevLane.m_ticketCost;
- if (ticketCost != 0) {
- comparisonValue += (float)(ticketCost * m_pathRandomizer.Int32(2000u)) * TICKET_COST_CONVERSION_FACTOR;
- }
- }
-
- int nextLaneIndex = 0;
- uint curLaneId = nextSegment.m_lanes;
- while (true) {
- if (nextLaneIndex < nextNumLanes && curLaneId != 0) {
- if (nextLaneId != curLaneId) {
- curLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;
- nextLaneIndex++;
- continue;
- }
- break;
- }
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemPublicTransport: Aborting: Next lane not found");
- }
-#endif
- return;
- }
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $"ProcessItemPublicTransport: Exploring next lane\n"
- + "\t" + $"nextLaneIndex={nextLaneIndex}\n"
- + "\t" + $"nextLaneId={nextLaneId}"
- );
- }
-#endif
-
- NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];
- if (nextLaneInfo.CheckType(m_laneTypes, m_vehicleTypes)) {
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $"ProcessItemPublicTransport: Next lane compatible\n"
- + "\t" + $"nextLaneInfo.m_vehicleType={nextLaneInfo.m_vehicleType}\n"
- + "\t" + $"nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}"
- );
- }
-#endif
-
- Vector3 a = netManager.m_lanes.m_buffer[nextLaneId].CalculatePosition((float)(int)offset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
- float distance = Vector3.Distance(a, b);
- BufferItem nextItem = default(BufferItem);
-
- nextItem.m_position.m_segment = nextSegmentId;
- nextItem.m_position.m_lane = (byte)nextLaneIndex;
- nextItem.m_position.m_offset = offset;
-
- if ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {
- nextItem.m_methodDistance = 0f;
- } else {
- nextItem.m_methodDistance = methodDistance + distance;
- }
-
- if (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance) && !m_stablePath) { // NON-STOCK CODE (custom walking distance)
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $"ProcessItemPublicTransport: Aborting: Max. walking distance exceeded\n"
- + "\t" + $"nextItem.m_methodDistance={nextItem.m_methodDistance}"
- );
- }
-#endif
- return;
- }
-
- float nextMaxSpeed;
+ if (allowPedestrian) {
+ int nextLaneIndex;
+ uint nextLaneId;
+ if (prevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Pedestrian, m_vehicleTypes, out nextLaneIndex, out nextLaneId)) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: ped -> vehicle: Exploring switch\n"
+ + "\t" + $"nextLaneIndex={nextLaneIndex}\n"
+ + "\t" + $"nextLaneId={nextLaneId}"
+ );
+ }
+#endif
+
+ ProcessItemPedBicycle(
+#if DEBUG
+ debug, unitId,
+#endif
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, nextLaneIndex, nextLaneId, ref netManager.m_lanes.m_buffer[nextLaneId], switchConnectOffset, switchConnectOffset);
+ }
+ }
+ }
+
+ if (nextNode.m_lane != 0) {
+ bool targetDisabled = (nextNode.m_flags & (NetNode.Flags.Disabled | NetNode.Flags.DisableOnlyMiddle)) == NetNode.Flags.Disabled;
+ ushort nextSegmentId = netManager.m_lanes.m_buffer[nextNode.m_lane].m_segment;
+ if (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemMain: transport -> *: Exploring special node\n"
+ + "\t" + $"nextSegmentId={nextSegmentId}\n"
+ + "\t" + $"nextNode.m_lane={nextNode.m_lane}\n"
+ + "\t" + $"targetDisabled={targetDisabled}\n"
+ + "\t" + $"nextNodeId={nextNodeId}"
+ );
+ }
+#endif
+
+ ProcessItemPublicTransport(
+#if DEBUG
+ debug, unitId,
+#endif
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, targetDisabled, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], nextNode.m_lane, nextNode.m_laneOffset, connectOffset);
+ }
+ }
+ }
+
+ // 2
+ private void ProcessItemPublicTransport(
+#if DEBUG
+ bool debug, uint unitId,
+#endif
+ BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed, ushort nextNodeId, bool targetDisabled, ushort nextSegmentId, ref NetSegment nextSegment, uint nextLaneId, byte offset, byte connectOffset) {
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemPublicTransport called.\n"
+ + "\t" + $"prevMaxSpeed={prevMaxSpeed}\n"
+ + "\t" + $"prevLaneSpeed={prevLaneSpeed}\n"
+ + "\t" + $"nextNodeId={nextNodeId}\n"
+ + "\t" + $"targetDisabled={targetDisabled}\n"
+ + "\t" + $"nextLaneId={nextLaneId}\n"
+ + "\t" + $"offset={offset}\n"
+ + "\t" + $"connectOffset={connectOffset}"
+ );
+ }
+#endif
+
+ if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemPublicTransport: Aborting: Disable mask\n"
+ + "\t" + $"m_disableMask={m_disableMask}\n"
+ + "\t" + $"nextSegment.m_flags={nextSegment.m_flags}\n");
+ }
+#endif
+ return;
+ }
+
+ NetManager netManager = Singleton.instance;
+ if (targetDisabled && ((netManager.m_nodes.m_buffer[nextSegment.m_startNode].m_flags | netManager.m_nodes.m_buffer[nextSegment.m_endNode].m_flags) & NetNode.Flags.Disabled) == NetNode.Flags.None) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemPublicTransport: Aborting: Target disabled");
+ }
+#endif
+ return;
+ }
+
+ NetInfo nextSegmentInfo = nextSegment.Info;
+ NetInfo prevSegmentInfo = prevSegment.Info;
+ int nextNumLanes = nextSegmentInfo.m_lanes.Length;
+ // float prevMaxSpeed = 1f; // stock code commented
+ // float prevLaneSpeed = 1f; // stock code commented
+ NetInfo.LaneType prevLaneType = NetInfo.LaneType.None;
+ if (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
+ NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];
+ // prevMaxSpeed = prevLaneInfo.m_speedLimit; // stock code commented
+ // prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // stock code commented
+ prevLaneType = prevLaneInfo.m_laneType;
+ if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
+ prevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
+ }
+ }
+
+ float prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? prevSegment.m_averageLength : prevLane.m_length;
+ float offsetLength = (float)Mathf.Abs(connectOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength;
+ float methodDistance = item.m_methodDistance + offsetLength;
+ float comparisonValue = item.m_comparisonValue + offsetLength / (prevLaneSpeed * m_maxLength);
+ float duration = item.m_duration + offsetLength / prevMaxSpeed;
+ Vector3 b = prevLane.CalculatePosition((float)(int)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+
+ if (!m_ignoreCost) {
+ int ticketCost = prevLane.m_ticketCost;
+ if (ticketCost != 0) {
+ comparisonValue += (float)(ticketCost * m_pathRandomizer.Int32(2000u)) * TICKET_COST_CONVERSION_FACTOR;
+ }
+ }
+
+ int nextLaneIndex = 0;
+ uint curLaneId = nextSegment.m_lanes;
+ while (true) {
+ if (nextLaneIndex < nextNumLanes && curLaneId != 0) {
+ if (nextLaneId != curLaneId) {
+ curLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;
+ nextLaneIndex++;
+ continue;
+ }
+ break;
+ }
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemPublicTransport: Aborting: Next lane not found");
+ }
+#endif
+ return;
+ }
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $"ProcessItemPublicTransport: Exploring next lane\n"
+ + "\t" + $"nextLaneIndex={nextLaneIndex}\n"
+ + "\t" + $"nextLaneId={nextLaneId}"
+ );
+ }
+#endif
+
+ NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];
+ if (nextLaneInfo.CheckType(m_laneTypes, m_vehicleTypes)) {
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $"ProcessItemPublicTransport: Next lane compatible\n"
+ + "\t" + $"nextLaneInfo.m_vehicleType={nextLaneInfo.m_vehicleType}\n"
+ + "\t" + $"nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}"
+ );
+ }
+#endif
+
+ Vector3 a = netManager.m_lanes.m_buffer[nextLaneId].CalculatePosition((float)(int)offset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+ float distance = Vector3.Distance(a, b);
+ BufferItem nextItem = default(BufferItem);
+
+ nextItem.m_position.m_segment = nextSegmentId;
+ nextItem.m_position.m_lane = (byte)nextLaneIndex;
+ nextItem.m_position.m_offset = offset;
+
+ if ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {
+ nextItem.m_methodDistance = 0f;
+ } else {
+ nextItem.m_methodDistance = methodDistance + distance;
+ }
+
+ if (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance) && !m_stablePath) { // NON-STOCK CODE (custom walking distance)
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $"ProcessItemPublicTransport: Aborting: Max. walking distance exceeded\n"
+ + "\t" + $"nextItem.m_methodDistance={nextItem.m_methodDistance}"
+ );
+ }
+#endif
+ return;
+ }
+
+ float nextMaxSpeed;
#if SPEEDLIMITS
- // NON-STOCK CODE START
- nextMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo);
- // NON-STOCK CODE END
+ // NON-STOCK CODE START
+ nextMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo);
+ // NON-STOCK CODE END
#else
nextMaxSpeed = nextLaneInfo.m_speedLimit;
#endif
- nextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength);
- nextItem.m_duration = duration + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);
+ nextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength);
+ nextItem.m_duration = duration + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);
- if ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {
- nextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);
- } else {
- nextItem.m_direction = nextLaneInfo.m_finalDirection;
- }
+ if ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {
+ nextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);
+ } else {
+ nextItem.m_direction = nextLaneInfo.m_finalDirection;
+ }
- if (nextLaneId == m_startLaneA) {
- if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetA) &&
- ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetA)) {
+ if (nextLaneId == m_startLaneA) {
+ if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetA) &&
+ ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetA)) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $"ProcessItemPublicTransport: Aborting: Invalid offset/direction on start lane A\n"
- + "\t" + $"nextItem.m_direction={nextItem.m_direction}\n"
- + "\t" + $"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\n"
- + "\t" + $"m_startOffsetA={m_startOffsetA}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $"ProcessItemPublicTransport: Aborting: Invalid offset/direction on start lane A\n"
+ + "\t" + $"nextItem.m_direction={nextItem.m_direction}\n"
+ + "\t" + $"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\n"
+ + "\t" + $"m_startOffsetA={m_startOffsetA}"
+ );
+ }
#endif
- return;
- }
+ return;
+ }
- float nextSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);
- float nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+ float nextSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);
+ float nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
- nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);
- nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;
- }
+ nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);
+ nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;
+ }
- if (nextLaneId == m_startLaneB) {
- if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetB) &&
- ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetB)) {
+ if (nextLaneId == m_startLaneB) {
+ if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetB) &&
+ ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetB)) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $"ProcessItemPublicTransport: Aborting: Invalid offset/direction on start lane B\n"
- + "\t" + $"nextItem.m_direction={nextItem.m_direction}\n"
- + "\t" + $"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\n"
- + "\t" + $"m_startOffsetB={m_startOffsetB}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $"ProcessItemPublicTransport: Aborting: Invalid offset/direction on start lane B\n"
+ + "\t" + $"nextItem.m_direction={nextItem.m_direction}\n"
+ + "\t" + $"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\n"
+ + "\t" + $"m_startOffsetB={m_startOffsetB}"
+ );
+ }
#endif
- return;
- }
+ return;
+ }
- float nextSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);
- float nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetB) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+ float nextSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);
+ float nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetB) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
- nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);
- nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;
- }
+ nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);
+ nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;
+ }
- nextItem.m_laneID = nextLaneId;
- nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);
+ nextItem.m_laneID = nextLaneId;
+ nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);
#if PARKINGAI
- nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);
+ nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);
#endif
#if ADVANCEDAI && ROUTING
- // NON-STOCK CODE START
- nextItem.m_trafficRand = item.m_trafficRand;
- // NON-STOCK CODE END
+ // NON-STOCK CODE START
+ nextItem.m_trafficRand = item.m_trafficRand;
+ // NON-STOCK CODE END
#endif
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $"ProcessItemPublicTransport: Adding next item\n"
- + "\t" + $"nextItem={nextItem}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $"ProcessItemPublicTransport: Adding next item\n"
+ + "\t" + $"nextItem={nextItem}"
+ );
+ }
#endif
- AddBufferItem(
+ AddBufferItem(
#if DEBUG
- debug,
+ debug,
#endif
- nextItem, item.m_position
- );
- }
- }
+ nextItem, item.m_position
+ );
+ }
+ }
#if ADVANCEDAI && ROUTING
- // 3a (non-routed, no adv. AI)
- private bool ProcessItemCosts(
+ // 3a (non-routed, no adv. AI)
+ private bool ProcessItemCosts(
#if DEBUG
- bool debug, uint unitId,
+ bool debug, uint unitId,
#endif
- BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed, ushort nextNodeId, ref NetNode nextNode, bool isMiddle, ushort nextSegmentId, ref NetSegment nextSegment, ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian) {
- return ProcessItemCosts(
+ BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed, ushort nextNodeId, ref NetNode nextNode, bool isMiddle, ushort nextSegmentId, ref NetSegment nextSegment, ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian) {
+ return ProcessItemCosts(
#if DEBUG
- debug, unitId,
+ debug, unitId,
#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed,
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed,
#if ADVANCEDAI && ROUTING
- false, 0f,
+ false, 0f,
#endif
- nextNodeId, ref nextNode, isMiddle, nextSegmentId, ref nextSegment,
+ nextNodeId, ref nextNode, isMiddle, nextSegmentId, ref nextSegment,
#if ROUTING
- 1f, 1f, null,
+ 1f, 1f, null,
#endif
- ref laneIndexFromInner, connectOffset, enableVehicle, enablePedestrian);
- }
+ ref laneIndexFromInner, connectOffset, enableVehicle, enablePedestrian);
+ }
#endif
- // 3b
- private bool ProcessItemCosts(
+ // 3b
+ private bool ProcessItemCosts(
#if DEBUG
- bool debug, uint unitId,
+ bool debug, uint unitId,
#endif
- BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed,
+ BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed,
#if ADVANCEDAI && ROUTING
- bool enableAdvancedAI, float laneChangingCost,
+ bool enableAdvancedAI, float laneChangingCost,
#endif
- ushort nextNodeId, ref NetNode nextNode, bool isMiddle, ushort nextSegmentId, ref NetSegment nextSegment,
+ ushort nextNodeId, ref NetNode nextNode, bool isMiddle, ushort nextSegmentId, ref NetSegment nextSegment,
#if ROUTING
- float segmentSelectionCost, float laneSelectionCost, LaneTransitionData? transition,
+ float segmentSelectionCost, float laneSelectionCost, LaneTransitionData? transition,
#endif
- ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian
- ) {
+ ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian
+ ) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemCosts called.\n"
- + "\t" + $"prevMaxSpeed={prevMaxSpeed}\n"
- + "\t" + $"prevLaneSpeed={prevLaneSpeed}\n"
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemCosts called.\n"
+ + "\t" + $"prevMaxSpeed={prevMaxSpeed}\n"
+ + "\t" + $"prevLaneSpeed={prevLaneSpeed}\n"
#if ADVANCEDAI && ROUTING
- + "\t" + $"enableAdvancedAI={enableAdvancedAI}\n"
- + "\t" + $"laneChangingCost={laneChangingCost}\n"
+ + "\t" + $"enableAdvancedAI={enableAdvancedAI}\n"
+ + "\t" + $"laneChangingCost={laneChangingCost}\n"
#endif
- + "\t" + $"nextNodeId={nextNodeId}\n"
- + "\t" + $"isMiddle={isMiddle}\n"
- + "\t" + $"nextSegmentId={nextSegmentId}\n"
+ + "\t" + $"nextNodeId={nextNodeId}\n"
+ + "\t" + $"isMiddle={isMiddle}\n"
+ + "\t" + $"nextSegmentId={nextSegmentId}\n"
#if ROUTING
- + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
- + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
- + "\t" + $"transition={transition}\n"
-#endif
- + "\t" + $"laneIndexFromInner={laneIndexFromInner}\n"
- + "\t" + $"connectOffset={connectOffset}\n"
- + "\t" + $"enableVehicle={enableVehicle}\n"
- + "\t" + $"enablePedestrian={enablePedestrian}"
- );
- }
-#endif
-
- bool blocked = false;
- if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Aborting: Disable mask\n"
- + "\t" + $"m_disableMask={m_disableMask}\n"
- + "\t" + $"nextSegment.m_flags={nextSegment.m_flags}\n");
- }
-#endif
- return blocked;
- }
-
- NetManager netManager = Singleton.instance;
- NetInfo nextSegmentInfo = nextSegment.Info;
- NetInfo prevSegmentInfo = prevSegment.Info;
- int nextNumLanes = nextSegmentInfo.m_lanes.Length;
- NetInfo.Direction nextDir = (nextNodeId != nextSegment.m_startNode) ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;
- NetInfo.Direction nextFinalDir = ((nextSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? nextDir : NetInfo.InvertDirection(nextDir);
- // float prevMaxSpeed = 1f; // stock code commented
- // float prevLaneSpeed = 1f; // stock code commented
- NetInfo.LaneType prevLaneType = NetInfo.LaneType.None;
- VehicleInfo.VehicleType prevVehicleType = VehicleInfo.VehicleType.None;
- if (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
- NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];
- prevLaneType = prevLaneInfo.m_laneType;
- prevVehicleType = prevLaneInfo.m_vehicleType;
- // prevMaxSpeed = prevLaneInfo.m_speedLimit; // stock code commented
- // prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // stock code commented
- }
-
- bool acuteTurningAngle = false;
- if (prevLaneType == NetInfo.LaneType.Vehicle && (prevVehicleType & VehicleInfo.VehicleType.Car) == VehicleInfo.VehicleType.None) {
- float turningAngle = 0.01f - Mathf.Min(nextSegmentInfo.m_maxTurnAngleCos, prevSegmentInfo.m_maxTurnAngleCos);
- if (turningAngle < 1f) {
- Vector3 vector = (nextNodeId != prevSegment.m_startNode) ? prevSegment.m_endDirection : prevSegment.m_startDirection;
- Vector3 vector2 = ((nextDir & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? nextSegment.m_startDirection : nextSegment.m_endDirection;
- float dirDotProd = vector.x * vector2.x + vector.z * vector2.z;
- if (dirDotProd >= turningAngle) {
- acuteTurningAngle = true;
- }
- }
- }
-
- float prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? prevSegment.m_averageLength : prevLane.m_length;
- float offsetLength = (float)Mathf.Abs(connectOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength;
- float methodDistance = item.m_methodDistance + offsetLength;
- float duration = item.m_duration + offsetLength / prevMaxSpeed;
-
- if (!m_stablePath) {
+ + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
+ + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
+ + "\t" + $"transition={transition}\n"
+#endif
+ + "\t" + $"laneIndexFromInner={laneIndexFromInner}\n"
+ + "\t" + $"connectOffset={connectOffset}\n"
+ + "\t" + $"enableVehicle={enableVehicle}\n"
+ + "\t" + $"enablePedestrian={enablePedestrian}"
+ );
+ }
+#endif
+
+ bool blocked = false;
+ if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Aborting: Disable mask\n"
+ + "\t" + $"m_disableMask={m_disableMask}\n"
+ + "\t" + $"nextSegment.m_flags={nextSegment.m_flags}\n");
+ }
+#endif
+ return blocked;
+ }
+
+ NetManager netManager = Singleton.instance;
+ NetInfo nextSegmentInfo = nextSegment.Info;
+ NetInfo prevSegmentInfo = prevSegment.Info;
+ int nextNumLanes = nextSegmentInfo.m_lanes.Length;
+ NetInfo.Direction nextDir = (nextNodeId != nextSegment.m_startNode) ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;
+ NetInfo.Direction nextFinalDir = ((nextSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? nextDir : NetInfo.InvertDirection(nextDir);
+ // float prevMaxSpeed = 1f; // stock code commented
+ // float prevLaneSpeed = 1f; // stock code commented
+ NetInfo.LaneType prevLaneType = NetInfo.LaneType.None;
+ VehicleInfo.VehicleType prevVehicleType = VehicleInfo.VehicleType.None;
+ if (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
+ NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];
+ prevLaneType = prevLaneInfo.m_laneType;
+ prevVehicleType = prevLaneInfo.m_vehicleType;
+ // prevMaxSpeed = prevLaneInfo.m_speedLimit; // stock code commented
+ // prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // stock code commented
+ }
+
+ bool acuteTurningAngle = false;
+ if (prevLaneType == NetInfo.LaneType.Vehicle && (prevVehicleType & VehicleInfo.VehicleType.Car) == VehicleInfo.VehicleType.None) {
+ float turningAngle = 0.01f - Mathf.Min(nextSegmentInfo.m_maxTurnAngleCos, prevSegmentInfo.m_maxTurnAngleCos);
+ if (turningAngle < 1f) {
+ Vector3 vector = (nextNodeId != prevSegment.m_startNode) ? prevSegment.m_endDirection : prevSegment.m_startDirection;
+ Vector3 vector2 = ((nextDir & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? nextSegment.m_startDirection : nextSegment.m_endDirection;
+ float dirDotProd = vector.x * vector2.x + vector.z * vector2.z;
+ if (dirDotProd >= turningAngle) {
+ acuteTurningAngle = true;
+ }
+ }
+ }
+
+ float prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? prevSegment.m_averageLength : prevLane.m_length;
+ float offsetLength = (float)Mathf.Abs(connectOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength;
+ float methodDistance = item.m_methodDistance + offsetLength;
+ float duration = item.m_duration + offsetLength / prevMaxSpeed;
+
+ if (!m_stablePath) {
#if ADVANCEDAI && ROUTING
- if (!enableAdvancedAI) {
+ if (!enableAdvancedAI) {
#endif
- offsetLength *= (float)(new Randomizer(m_pathFindIndex << 16 | item.m_position.m_segment).Int32(900, 1000 + prevSegment.m_trafficDensity * 10) + m_pathRandomizer.Int32(20u)) * 0.001f;
+ offsetLength *= (float)(new Randomizer(m_pathFindIndex << 16 | item.m_position.m_segment).Int32(900, 1000 + prevSegment.m_trafficDensity * 10) + m_pathRandomizer.Int32(20u)) * 0.001f;
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Applied stock segment randomization cost factor\n"
- + "\t" + $"offsetLength={offsetLength}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Applied stock segment randomization cost factor\n"
+ + "\t" + $"offsetLength={offsetLength}"
+ );
+ }
#endif
#if ADVANCEDAI && ROUTING
- }
+ }
#endif
- }
+ }
- if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None && (prevVehicleType & m_vehicleTypes) == VehicleInfo.VehicleType.Car && (prevSegment.m_flags & m_carBanMask) != NetSegment.Flags.None) {
- offsetLength *= 7.5f;
+ if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None && (prevVehicleType & m_vehicleTypes) == VehicleInfo.VehicleType.Car && (prevSegment.m_flags & m_carBanMask) != NetSegment.Flags.None) {
+ offsetLength *= 7.5f;
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Applied stock car ban cost factor\n"
- + "\t" + $"offsetLength={offsetLength}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Applied stock car ban cost factor\n"
+ + "\t" + $"offsetLength={offsetLength}"
+ );
+ }
#endif
- }
+ }
- if (m_transportVehicle && prevLaneType == NetInfo.LaneType.TransportVehicle) {
- offsetLength *= 0.95f;
+ if (m_transportVehicle && prevLaneType == NetInfo.LaneType.TransportVehicle) {
+ offsetLength *= 0.95f;
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Applied stock transport vehicle cost factor\n"
- + "\t" + $"offsetLength={offsetLength}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Applied stock transport vehicle cost factor\n"
+ + "\t" + $"offsetLength={offsetLength}"
+ );
+ }
#endif
- }
+ }
#if ROUTING
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Applying custom selection cost factors\n"
- + "\t" + $"offsetLength={offsetLength}\n"
- + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
- + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Applying custom selection cost factors\n"
+ + "\t" + $"offsetLength={offsetLength}\n"
+ + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
+ + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
+ );
+ }
#endif
- offsetLength *= segmentSelectionCost;
- offsetLength *= laneSelectionCost;
+ offsetLength *= segmentSelectionCost;
+ offsetLength *= laneSelectionCost;
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Applied custom selection cost factors\n"
- + "\t" + $"offsetLength={offsetLength}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Applied custom selection cost factors\n"
+ + "\t" + $"offsetLength={offsetLength}"
+ );
+ }
#endif
#endif
- float baseLength = offsetLength / (prevLaneSpeed * m_maxLength); // NON-STOCK CODE
- float comparisonValue = item.m_comparisonValue; // NON-STOCK CODE
+ float baseLength = offsetLength / (prevLaneSpeed * m_maxLength); // NON-STOCK CODE
+ float comparisonValue = item.m_comparisonValue; // NON-STOCK CODE
#if ROUTING
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Calculated base length\n"
- + "\t" + $"baseLength={baseLength}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Calculated base length\n"
+ + "\t" + $"baseLength={baseLength}"
+ );
+ }
#endif
- if (
+ if (
#if ADVANCEDAI
- !enableAdvancedAI &&
-#endif
- !m_stablePath) {
- comparisonValue += baseLength;
- }
-#endif
- int ticketCost = prevLane.m_ticketCost;
- if (!m_ignoreCost && ticketCost != 0) {
- comparisonValue += (float)(ticketCost * m_pathRandomizer.Int32(2000u)) * TICKET_COST_CONVERSION_FACTOR;
- }
-
- if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
- prevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
- }
-
- Vector3 b = prevLane.CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
- int newLaneIndexFromInner = laneIndexFromInner;
- bool isTransition = (nextNode.m_flags & NetNode.Flags.Transition) != NetNode.Flags.None;
-
- NetInfo.LaneType allowedLaneTypes = m_laneTypes;
- VehicleInfo.VehicleType allowedVehicleTypes = m_vehicleTypes;
- if (!enableVehicle) {
- allowedVehicleTypes &= VehicleInfo.VehicleType.Bicycle;
- if (allowedVehicleTypes == VehicleInfo.VehicleType.None) {
- allowedLaneTypes &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
- }
- }
- if (!enablePedestrian) {
- allowedLaneTypes &= ~NetInfo.LaneType.Pedestrian;
- }
-
- // NON-STOCK CODE START
- bool applyTransportTransferPenalty =
- Options.realisticPublicTransport &&
- !m_stablePath &&
- (allowedLaneTypes & (NetInfo.LaneType.PublicTransport | NetInfo.LaneType.Pedestrian)) == (NetInfo.LaneType.PublicTransport | NetInfo.LaneType.Pedestrian) &&
- m_conf.PathFinding.PublicTransportTransitionMinPenalty >= 0 &&
- m_conf.PathFinding.PublicTransportTransitionMaxPenalty > m_conf.PathFinding.PublicTransportTransitionMinPenalty
- ;
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Shall apply transport transfer penalty?\n"
- + "\t" + $"applyTransportTransferPenalty={applyTransportTransferPenalty}\n"
- + "\t" + $"Options.realisticPublicTransport={Options.realisticPublicTransport}\n"
- + "\t" + $"allowedLaneTypes={allowedLaneTypes}\n"
- + "\t" + $"allowedVehicleTypes={allowedVehicleTypes}\n"
- + "\t" + $"m_conf.PathFinding.PublicTransportTransitionMinPenalty={m_conf.PathFinding.PublicTransportTransitionMinPenalty}\n"
- + "\t" + $"m_conf.PathFinding.PublicTransportTransitionMaxPenalty={m_conf.PathFinding.PublicTransportTransitionMaxPenalty}"
- );
- }
-#endif
-
- int nextLaneIndex = 0;
- uint nextLaneId = nextSegment.m_lanes;
- int maxNextLaneIndex = nextNumLanes - 1;
+ !enableAdvancedAI &&
+#endif
+ !m_stablePath) {
+ comparisonValue += baseLength;
+ }
+#endif
+ int ticketCost = prevLane.m_ticketCost;
+ if (!m_ignoreCost && ticketCost != 0) {
+ comparisonValue += (float)(ticketCost * m_pathRandomizer.Int32(2000u)) * TICKET_COST_CONVERSION_FACTOR;
+ }
+
+ if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
+ prevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
+ }
+
+ Vector3 b = prevLane.CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+ int newLaneIndexFromInner = laneIndexFromInner;
+ bool isTransition = (nextNode.m_flags & NetNode.Flags.Transition) != NetNode.Flags.None;
+
+ NetInfo.LaneType allowedLaneTypes = m_laneTypes;
+ VehicleInfo.VehicleType allowedVehicleTypes = m_vehicleTypes;
+ if (!enableVehicle) {
+ allowedVehicleTypes &= VehicleInfo.VehicleType.Bicycle;
+ if (allowedVehicleTypes == VehicleInfo.VehicleType.None) {
+ allowedLaneTypes &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
+ }
+ }
+ if (!enablePedestrian) {
+ allowedLaneTypes &= ~NetInfo.LaneType.Pedestrian;
+ }
+
+ // NON-STOCK CODE START
+ bool applyTransportTransferPenalty =
+ Options.realisticPublicTransport &&
+ !m_stablePath &&
+ (allowedLaneTypes & (NetInfo.LaneType.PublicTransport | NetInfo.LaneType.Pedestrian)) == (NetInfo.LaneType.PublicTransport | NetInfo.LaneType.Pedestrian) &&
+ m_conf.PathFinding.PublicTransportTransitionMinPenalty >= 0 &&
+ m_conf.PathFinding.PublicTransportTransitionMaxPenalty > m_conf.PathFinding.PublicTransportTransitionMinPenalty
+ ;
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Shall apply transport transfer penalty?\n"
+ + "\t" + $"applyTransportTransferPenalty={applyTransportTransferPenalty}\n"
+ + "\t" + $"Options.realisticPublicTransport={Options.realisticPublicTransport}\n"
+ + "\t" + $"allowedLaneTypes={allowedLaneTypes}\n"
+ + "\t" + $"allowedVehicleTypes={allowedVehicleTypes}\n"
+ + "\t" + $"m_conf.PathFinding.PublicTransportTransitionMinPenalty={m_conf.PathFinding.PublicTransportTransitionMinPenalty}\n"
+ + "\t" + $"m_conf.PathFinding.PublicTransportTransitionMaxPenalty={m_conf.PathFinding.PublicTransportTransitionMaxPenalty}"
+ );
+ }
+#endif
+
+ int nextLaneIndex = 0;
+ uint nextLaneId = nextSegment.m_lanes;
+ int maxNextLaneIndex = nextNumLanes - 1;
#if ADVANCEDAI && ROUTING
- byte laneDist = 0;
+ byte laneDist = 0;
#endif
#if ROUTING
- if (transition != null) {
- LaneTransitionData trans = (LaneTransitionData)transition;
- if (trans.laneIndex >= 0 && trans.laneIndex <= maxNextLaneIndex) {
- nextLaneIndex = trans.laneIndex;
- nextLaneId = trans.laneId;
- maxNextLaneIndex = nextLaneIndex;
- } else {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Invalid transition detected. Skipping.");
- }
-#endif
- return blocked;
- }
-
- laneDist = trans.distance;
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Custom transition given\n"
- + "\t" + $"nextLaneIndex={nextLaneIndex}\n"
- + "\t" + $"nextLaneId={nextLaneId}\n"
- + "\t" + $"maxNextLaneIndex={maxNextLaneIndex}\n"
- + "\t" + $"laneDist={laneDist}"
- );
- }
-#endif
- } else {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: No custom transition given");
- }
-#endif
- }
-#endif
- // NON-STOCK CODE END
-
- for (; nextLaneIndex <= maxNextLaneIndex && nextLaneId != 0; nextLaneIndex++) {
- NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];
- if ((nextLaneInfo.m_finalDirection & nextFinalDir) != NetInfo.Direction.None) {
- if (nextLaneInfo.CheckType(allowedLaneTypes, allowedVehicleTypes) && (nextSegmentId != item.m_position.m_segment || nextLaneIndex != item.m_position.m_lane)) {
- if (acuteTurningAngle && nextLaneInfo.m_laneType == NetInfo.LaneType.Vehicle && (nextLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) == VehicleInfo.VehicleType.None) {
- continue;
- }
-
- BufferItem nextItem = default(BufferItem);
-
- Vector3 a = ((nextDir & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? netManager.m_lanes.m_buffer[nextLaneId].m_bezier.a : netManager.m_lanes.m_buffer[nextLaneId].m_bezier.d;
- float transitionCost = Vector3.Distance(a, b);
- if (isTransition) {
- transitionCost *= 2f;
- }
- if (ticketCost != 0 && netManager.m_lanes.m_buffer[nextLaneId].m_ticketCost != 0) {
- transitionCost *= 10f;
- }
-
- float nextMaxSpeed;
+ if (transition != null) {
+ LaneTransitionData trans = (LaneTransitionData)transition;
+ if (trans.laneIndex >= 0 && trans.laneIndex <= maxNextLaneIndex) {
+ nextLaneIndex = trans.laneIndex;
+ nextLaneId = trans.laneId;
+ maxNextLaneIndex = nextLaneIndex;
+ } else {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Invalid transition detected. Skipping.");
+ }
+#endif
+ return blocked;
+ }
+
+ laneDist = trans.distance;
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: Custom transition given\n"
+ + "\t" + $"nextLaneIndex={nextLaneIndex}\n"
+ + "\t" + $"nextLaneId={nextLaneId}\n"
+ + "\t" + $"maxNextLaneIndex={maxNextLaneIndex}\n"
+ + "\t" + $"laneDist={laneDist}"
+ );
+ }
+#endif
+ } else {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, $"ProcessItemCosts: No custom transition given");
+ }
+#endif
+ }
+#endif
+ // NON-STOCK CODE END
+
+ for (; nextLaneIndex <= maxNextLaneIndex && nextLaneId != 0; nextLaneIndex++) {
+ NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];
+ if ((nextLaneInfo.m_finalDirection & nextFinalDir) != NetInfo.Direction.None) {
+ if (nextLaneInfo.CheckType(allowedLaneTypes, allowedVehicleTypes) && (nextSegmentId != item.m_position.m_segment || nextLaneIndex != item.m_position.m_lane)) {
+ if (acuteTurningAngle && nextLaneInfo.m_laneType == NetInfo.LaneType.Vehicle && (nextLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) == VehicleInfo.VehicleType.None) {
+ continue;
+ }
+
+ BufferItem nextItem = default(BufferItem);
+
+ Vector3 a = ((nextDir & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? netManager.m_lanes.m_buffer[nextLaneId].m_bezier.a : netManager.m_lanes.m_buffer[nextLaneId].m_bezier.d;
+ float transitionCost = Vector3.Distance(a, b);
+ if (isTransition) {
+ transitionCost *= 2f;
+ }
+ if (ticketCost != 0 && netManager.m_lanes.m_buffer[nextLaneId].m_ticketCost != 0) {
+ transitionCost *= 10f;
+ }
+
+ float nextMaxSpeed;
#if SPEEDLIMITS
- // NON-STOCK CODE START
- nextMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo);
- // NON-STOCK CODE END
+ // NON-STOCK CODE START
+ nextMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo);
+ // NON-STOCK CODE END
#else
nextMaxSpeed = nextLaneInfo.m_speedLimit;
#endif
- float transitionCostOverMeanMaxSpeed = transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength);
+ float transitionCostOverMeanMaxSpeed = transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength);
#if ADVANCEDAI && ROUTING
- if (!enableAdvancedAI) {
+ if (!enableAdvancedAI) {
#endif
- if (!this.m_stablePath && (netManager.m_lanes.m_buffer[nextLaneId].m_flags & (ushort)NetLane.Flags.Merge) != 0) {
- int firstTarget = netManager.m_lanes.m_buffer[nextLaneId].m_firstTarget;
- int lastTarget = netManager.m_lanes.m_buffer[nextLaneId].m_lastTarget;
- transitionCostOverMeanMaxSpeed *= (float)new Randomizer(this.m_pathFindIndex ^ nextLaneId).Int32(1000, (lastTarget - firstTarget + 2) * 1000) * 0.001f;
- }
+ if (!this.m_stablePath && (netManager.m_lanes.m_buffer[nextLaneId].m_flags & (ushort)NetLane.Flags.Merge) != 0) {
+ int firstTarget = netManager.m_lanes.m_buffer[nextLaneId].m_firstTarget;
+ int lastTarget = netManager.m_lanes.m_buffer[nextLaneId].m_lastTarget;
+ transitionCostOverMeanMaxSpeed *= (float)new Randomizer(this.m_pathFindIndex ^ nextLaneId).Int32(1000, (lastTarget - firstTarget + 2) * 1000) * 0.001f;
+ }
#if ADVANCEDAI && ROUTING
- }
-#endif
- nextItem.m_position.m_segment = nextSegmentId;
- nextItem.m_position.m_lane = (byte)nextLaneIndex;
- nextItem.m_position.m_offset = (byte)(((nextDir & NetInfo.Direction.Forward) != NetInfo.Direction.None) ? 255 : 0);
- if ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {
- nextItem.m_methodDistance = 0f;
- } else {
- nextItem.m_methodDistance = methodDistance + transitionCost;
- }
-
- if (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance) && !m_stablePath) { // NON-STOCK CODE (custom walking distance)
- nextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane;
- continue;
- }
-
- // NON-STOCK CODE START
- if (applyTransportTransferPenalty) {
- if (
- isMiddle &&
- (nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None &&
- (item.m_lanesUsed & NetInfo.LaneType.PublicTransport) != NetInfo.LaneType.None &&
- nextLaneInfo.m_laneType == NetInfo.LaneType.PublicTransport
- ) {
- // apply penalty when switching between public transport lines
- float transportTransitionPenalty = (m_conf.PathFinding.PublicTransportTransitionMinPenalty + ((float)nextNode.m_maxWaitTime * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR) * (m_conf.PathFinding.PublicTransportTransitionMaxPenalty - m_conf.PathFinding.PublicTransportTransitionMinPenalty)) / (0.5f * this.m_maxLength);
- transitionCostOverMeanMaxSpeed += transportTransitionPenalty;
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Applied transport transfer penalty on PT change\n"
- + "\t" + $"transportTransitionPenalty={transportTransitionPenalty}\n"
- + "\t" + $"transitionCostOverMeanMaxSpeed={transitionCostOverMeanMaxSpeed}\n"
- + "\t" + $"isMiddle={isMiddle}\n"
- + "\t" + $"nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}\n"
- + "\t" + $"prevLaneType={prevLaneType}\n"
- + "\t" + $"item.m_lanesUsed={item.m_lanesUsed}\n"
- + "\t" + $"nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}"
- );
- }
-#endif
- } else if (
- (nextLaneId == m_startLaneA || nextLaneId == m_startLaneB) &&
- (item.m_lanesUsed & (NetInfo.LaneType.Pedestrian | NetInfo.LaneType.PublicTransport)) == NetInfo.LaneType.Pedestrian
- ) {
- // account for public tranport transition costs on non-PT paths
- float transportTransitionPenalty = (2f * m_conf.PathFinding.PublicTransportTransitionMaxPenalty) / (0.5f * this.m_maxLength);
- transitionCostOverMeanMaxSpeed += transportTransitionPenalty;
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Applied transport transfer penalty on non-PT path\n"
- + "\t" + $"transportTransitionPenalty={transportTransitionPenalty}\n"
- + "\t" + $"transitionCostOverMeanMaxSpeed={transitionCostOverMeanMaxSpeed}"
- );
- }
-#endif
- }
- }
- // NON-STOCK CODE END
-
- nextItem.m_comparisonValue = comparisonValue + transitionCostOverMeanMaxSpeed;
- nextItem.m_duration = duration + transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);
- nextItem.m_direction = nextDir;
-
- if (nextLaneId == m_startLaneA) {
- if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetA) &&
- ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetA)) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Skipping: Invalid offset/direction on start lane A\n"
- + "\t" + $"nextItem.m_direction={nextItem.m_direction}\n"
- + "\t" + $"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\n"
- + "\t" + $"m_startOffsetA={m_startOffsetA}"
- );
- }
-#endif
-
- nextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane;
- continue;
- }
-
- float nextLaneSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);
- float nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
-
- nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextLaneSpeed * m_maxLength);
- nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextLaneSpeed;
- }
-
- if (nextLaneId == m_startLaneB) {
- if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetB) &&
- ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetB)) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Skipping: Invalid offset/direction on start lane B\n"
- + "\t" + $"nextItem.m_direction={nextItem.m_direction}\n"
- + "\t" + $"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\n"
- + "\t" + $"m_startOffsetB={m_startOffsetB}"
- );
- }
-#endif
-
- nextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane;
- continue;
- }
-
- float nextLaneSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);
- float nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetB) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
-
- nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextLaneSpeed * m_maxLength);
- nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextLaneSpeed;
- }
-
- if (
- !m_ignoreBlocked && (nextSegment.m_flags & NetSegment.Flags.Blocked) != NetSegment.Flags.None &&
- (nextLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None
- ) {
- nextItem.m_comparisonValue += 0.1f;
- blocked = true;
- }
-
- nextItem.m_laneID = nextLaneId;
- nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);
+ }
+#endif
+ nextItem.m_position.m_segment = nextSegmentId;
+ nextItem.m_position.m_lane = (byte)nextLaneIndex;
+ nextItem.m_position.m_offset = (byte)(((nextDir & NetInfo.Direction.Forward) != NetInfo.Direction.None) ? 255 : 0);
+ if ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {
+ nextItem.m_methodDistance = 0f;
+ } else {
+ nextItem.m_methodDistance = methodDistance + transitionCost;
+ }
+
+ if (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance) && !m_stablePath) { // NON-STOCK CODE (custom walking distance)
+ nextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane;
+ continue;
+ }
+
+ // NON-STOCK CODE START
+ if (applyTransportTransferPenalty) {
+ if (
+ isMiddle &&
+ (nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None &&
+ (item.m_lanesUsed & NetInfo.LaneType.PublicTransport) != NetInfo.LaneType.None &&
+ nextLaneInfo.m_laneType == NetInfo.LaneType.PublicTransport
+ ) {
+ // apply penalty when switching between public transport lines
+ float transportTransitionPenalty = (m_conf.PathFinding.PublicTransportTransitionMinPenalty + ((float)nextNode.m_maxWaitTime * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR) * (m_conf.PathFinding.PublicTransportTransitionMaxPenalty - m_conf.PathFinding.PublicTransportTransitionMinPenalty)) / (0.5f * this.m_maxLength);
+ transitionCostOverMeanMaxSpeed += transportTransitionPenalty;
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Applied transport transfer penalty on PT change\n"
+ + "\t" + $"transportTransitionPenalty={transportTransitionPenalty}\n"
+ + "\t" + $"transitionCostOverMeanMaxSpeed={transitionCostOverMeanMaxSpeed}\n"
+ + "\t" + $"isMiddle={isMiddle}\n"
+ + "\t" + $"nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}\n"
+ + "\t" + $"prevLaneType={prevLaneType}\n"
+ + "\t" + $"item.m_lanesUsed={item.m_lanesUsed}\n"
+ + "\t" + $"nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}"
+ );
+ }
+#endif
+ } else if (
+ (nextLaneId == m_startLaneA || nextLaneId == m_startLaneB) &&
+ (item.m_lanesUsed & (NetInfo.LaneType.Pedestrian | NetInfo.LaneType.PublicTransport)) == NetInfo.LaneType.Pedestrian
+ ) {
+ // account for public tranport transition costs on non-PT paths
+ float transportTransitionPenalty = (2f * m_conf.PathFinding.PublicTransportTransitionMaxPenalty) / (0.5f * this.m_maxLength);
+ transitionCostOverMeanMaxSpeed += transportTransitionPenalty;
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Applied transport transfer penalty on non-PT path\n"
+ + "\t" + $"transportTransitionPenalty={transportTransitionPenalty}\n"
+ + "\t" + $"transitionCostOverMeanMaxSpeed={transitionCostOverMeanMaxSpeed}"
+ );
+ }
+#endif
+ }
+ }
+ // NON-STOCK CODE END
+
+ nextItem.m_comparisonValue = comparisonValue + transitionCostOverMeanMaxSpeed;
+ nextItem.m_duration = duration + transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);
+ nextItem.m_direction = nextDir;
+
+ if (nextLaneId == m_startLaneA) {
+ if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetA) &&
+ ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetA)) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Skipping: Invalid offset/direction on start lane A\n"
+ + "\t" + $"nextItem.m_direction={nextItem.m_direction}\n"
+ + "\t" + $"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\n"
+ + "\t" + $"m_startOffsetA={m_startOffsetA}"
+ );
+ }
+#endif
+
+ nextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane;
+ continue;
+ }
+
+ float nextLaneSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);
+ float nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+
+ nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextLaneSpeed * m_maxLength);
+ nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextLaneSpeed;
+ }
+
+ if (nextLaneId == m_startLaneB) {
+ if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetB) &&
+ ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetB)) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Skipping: Invalid offset/direction on start lane B\n"
+ + "\t" + $"nextItem.m_direction={nextItem.m_direction}\n"
+ + "\t" + $"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\n"
+ + "\t" + $"m_startOffsetB={m_startOffsetB}"
+ );
+ }
+#endif
+
+ nextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane;
+ continue;
+ }
+
+ float nextLaneSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);
+ float nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetB) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+
+ nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextLaneSpeed * m_maxLength);
+ nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextLaneSpeed;
+ }
+
+ if (
+ !m_ignoreBlocked && (nextSegment.m_flags & NetSegment.Flags.Blocked) != NetSegment.Flags.None &&
+ (nextLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None
+ ) {
+ nextItem.m_comparisonValue += 0.1f;
+ blocked = true;
+ }
+
+ nextItem.m_laneID = nextLaneId;
+ nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);
#if PARKINGAI
- nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);
+ nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);
#endif
#if ADVANCEDAI && ROUTING
- // NON-STOCK CODE START
- nextItem.m_trafficRand = item.m_trafficRand;
- // NON-STOCK CODE END
+ // NON-STOCK CODE START
+ nextItem.m_trafficRand = item.m_trafficRand;
+ // NON-STOCK CODE END
#endif
#if ROUTING
#if ADVANCEDAI
- if (enableAdvancedAI) {
- float adjustedBaseLength = baseLength;
- if (m_queueItem.spawned || (nextLaneId != m_startLaneA && nextLaneId != m_startLaneB)) {
- if (laneDist != 0) {
- // apply lane changing costs
- adjustedBaseLength *=
- 1f +
- laneDist *
- laneChangingCost *
- (laneDist > 1 ? m_conf.AdvancedVehicleAI.MoreThanOneLaneChangingCostFactor : 1f); // additional costs for changing multiple lanes at once
- }
- }
-
- nextItem.m_comparisonValue += adjustedBaseLength;
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Applied Advanced Vehicle AI\n"
- + "\t" + $"baseLength={baseLength}\n"
- + "\t" + $"adjustedBaseLength={adjustedBaseLength}\n"
- + "\t" + $"laneDist={laneDist}\n"
- + "\t" + $"laneChangingCost={laneChangingCost}"
- );
- }
-#endif
- } else
-#endif
- if (m_stablePath) {
- // all non-road vehicles with stable paths (trains, trams, etc.): apply lane distance factor
- float adjustedBaseLength = baseLength;
- adjustedBaseLength *= 1 + laneDist;
- nextItem.m_comparisonValue += adjustedBaseLength;
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Applied stable path lane distance costs\n"
- + "\t" + $"baseLength={baseLength}\n"
- + "\t" + $"adjustedBaseLength={adjustedBaseLength}\n"
- + "\t" + $"laneDist={laneDist}"
- );
- }
-#endif
- }
-#endif
-
- if (
- (nextLaneInfo.m_laneType & prevLaneType) != NetInfo.LaneType.None &&
- (nextLaneInfo.m_vehicleType & m_vehicleTypes) != VehicleInfo.VehicleType.None
- ) {
+ if (enableAdvancedAI) {
+ float adjustedBaseLength = baseLength;
+ if (m_queueItem.spawned || (nextLaneId != m_startLaneA && nextLaneId != m_startLaneB)) {
+ if (laneDist != 0) {
+ // apply lane changing costs
+ adjustedBaseLength *=
+ 1f +
+ laneDist *
+ laneChangingCost *
+ (laneDist > 1 ? m_conf.AdvancedVehicleAI.MoreThanOneLaneChangingCostFactor : 1f); // additional costs for changing multiple lanes at once
+ }
+ }
+
+ nextItem.m_comparisonValue += adjustedBaseLength;
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Applied Advanced Vehicle AI\n"
+ + "\t" + $"baseLength={baseLength}\n"
+ + "\t" + $"adjustedBaseLength={adjustedBaseLength}\n"
+ + "\t" + $"laneDist={laneDist}\n"
+ + "\t" + $"laneChangingCost={laneChangingCost}"
+ );
+ }
+#endif
+ } else
+#endif
+ if (m_stablePath) {
+ // all non-road vehicles with stable paths (trains, trams, etc.): apply lane distance factor
+ float adjustedBaseLength = baseLength;
+ adjustedBaseLength *= 1 + laneDist;
+ nextItem.m_comparisonValue += adjustedBaseLength;
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Applied stable path lane distance costs\n"
+ + "\t" + $"baseLength={baseLength}\n"
+ + "\t" + $"adjustedBaseLength={adjustedBaseLength}\n"
+ + "\t" + $"laneDist={laneDist}"
+ );
+ }
+#endif
+ }
+#endif
+
+ if (
+ (nextLaneInfo.m_laneType & prevLaneType) != NetInfo.LaneType.None &&
+ (nextLaneInfo.m_vehicleType & m_vehicleTypes) != VehicleInfo.VehicleType.None
+ ) {
#if ADVANCEDAI && ROUTING
- if (! enableAdvancedAI) {
+ if (! enableAdvancedAI) {
#endif
- int firstTarget = netManager.m_lanes.m_buffer[nextLaneId].m_firstTarget;
- int lastTarget = netManager.m_lanes.m_buffer[nextLaneId].m_lastTarget;
- if (laneIndexFromInner < firstTarget || laneIndexFromInner >= lastTarget) {
- nextItem.m_comparisonValue += Mathf.Max(1f, transitionCost * 3f - 3f) / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength);
- }
+ int firstTarget = netManager.m_lanes.m_buffer[nextLaneId].m_firstTarget;
+ int lastTarget = netManager.m_lanes.m_buffer[nextLaneId].m_lastTarget;
+ if (laneIndexFromInner < firstTarget || laneIndexFromInner >= lastTarget) {
+ nextItem.m_comparisonValue += Mathf.Max(1f, transitionCost * 3f - 3f) / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength);
+ }
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: stock lane change costs\n"
- + "\t" + $"firstTarget={firstTarget}\n"
- + "\t" + $"lastTarget={lastTarget}\n"
- + "\t" + $"laneIndexFromInner={laneIndexFromInner}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: stock lane change costs\n"
+ + "\t" + $"firstTarget={firstTarget}\n"
+ + "\t" + $"lastTarget={lastTarget}\n"
+ + "\t" + $"laneIndexFromInner={laneIndexFromInner}"
+ );
+ }
#endif
#if ADVANCEDAI && ROUTING
- }
+ }
#endif
- if (
- !m_transportVehicle &&
- nextLaneInfo.m_laneType == NetInfo.LaneType.TransportVehicle
- ) {
- nextItem.m_comparisonValue += 20f / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength);
- }
- }
+ if (
+ !m_transportVehicle &&
+ nextLaneInfo.m_laneType == NetInfo.LaneType.TransportVehicle
+ ) {
+ nextItem.m_comparisonValue += 20f / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength);
+ }
+ }
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Adding next item\n"
- + "\t" + $"nextItem={nextItem}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Adding next item\n"
+ + "\t" + $"nextItem={nextItem}"
+ );
+ }
#endif
- AddBufferItem(
+ AddBufferItem(
#if DEBUG
- debug,
+ debug,
#endif
- nextItem, item.m_position
- );
- } else {
+ nextItem, item.m_position
+ );
+ } else {
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Lane type and/or vehicle type mismatch or same segment/lane. Skipping."
- + "\t" + $"allowedLaneTypes={allowedLaneTypes}\n"
- + "\t" + $"allowedVehicleTypes={allowedVehicleTypes}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Lane type and/or vehicle type mismatch or same segment/lane. Skipping."
+ + "\t" + $"allowedLaneTypes={allowedLaneTypes}\n"
+ + "\t" + $"allowedVehicleTypes={allowedVehicleTypes}"
+ );
+ }
#endif
- }
- } else {
+ }
+ } else {
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Lane direction mismatch. Skipping."
- + "\t" + $"nextLaneInfo.m_finalDirection={nextLaneInfo.m_finalDirection}\n"
- + "\t" + $"nextFinalDir={nextFinalDir}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemCosts: Lane direction mismatch. Skipping."
+ + "\t" + $"nextLaneInfo.m_finalDirection={nextLaneInfo.m_finalDirection}\n"
+ + "\t" + $"nextFinalDir={nextFinalDir}"
+ );
+ }
#endif
- if ((nextLaneInfo.m_laneType & prevLaneType) != NetInfo.LaneType.None && (nextLaneInfo.m_vehicleType & prevVehicleType) != VehicleInfo.VehicleType.None) {
- newLaneIndexFromInner++;
- }
- }
+ if ((nextLaneInfo.m_laneType & prevLaneType) != NetInfo.LaneType.None && (nextLaneInfo.m_vehicleType & prevVehicleType) != VehicleInfo.VehicleType.None) {
+ newLaneIndexFromInner++;
+ }
+ }
- nextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane;
- continue;
- }
- laneIndexFromInner = newLaneIndexFromInner;
- return blocked;
- }
+ nextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane;
+ continue;
+ }
+ laneIndexFromInner = newLaneIndexFromInner;
+ return blocked;
+ }
- // 4
- private void ProcessItemPedBicycle(
+ // 4
+ private void ProcessItemPedBicycle(
#if DEBUG
- bool debug, uint unitId,
+ bool debug, uint unitId,
#endif
- BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed, ushort nextSegmentId, ref NetSegment nextSegment, ushort nextNodeId, ref NetNode nextNode, int nextLaneIndex, uint nextLaneId, ref NetLane nextLane, byte connectOffset, byte laneSwitchOffset) {
+ BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed, ushort nextSegmentId, ref NetSegment nextSegment, ushort nextNodeId, ref NetNode nextNode, int nextLaneIndex, uint nextLaneId, ref NetLane nextLane, byte connectOffset, byte laneSwitchOffset) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle called.\n"
- + "\t" + $"prevMaxSpeed={prevMaxSpeed}\n"
- + "\t" + $"prevLaneSpeed={prevLaneSpeed}\n"
- + "\t" + $"nextSegmentId={nextSegmentId}\n"
- + "\t" + $"nextNodeId={nextNodeId}\n"
- + "\t" + $"nextLaneIndex={nextLaneIndex}\n"
- + "\t" + $"nextLaneId={nextLaneId}\n"
- + "\t" + $"connectOffset={connectOffset}\n"
- + "\t" + $"laneSwitchOffset={laneSwitchOffset}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle called.\n"
+ + "\t" + $"prevMaxSpeed={prevMaxSpeed}\n"
+ + "\t" + $"prevLaneSpeed={prevLaneSpeed}\n"
+ + "\t" + $"nextSegmentId={nextSegmentId}\n"
+ + "\t" + $"nextNodeId={nextNodeId}\n"
+ + "\t" + $"nextLaneIndex={nextLaneIndex}\n"
+ + "\t" + $"nextLaneId={nextLaneId}\n"
+ + "\t" + $"connectOffset={connectOffset}\n"
+ + "\t" + $"laneSwitchOffset={laneSwitchOffset}"
+ );
+ }
#endif
- if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {
+ if ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Aborting: Disable mask\n"
- + "\t" + $"m_disableMask={m_disableMask}\n"
- + "\t" + $"nextSegment.m_flags={nextSegment.m_flags}\n");
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Aborting: Disable mask\n"
+ + "\t" + $"m_disableMask={m_disableMask}\n"
+ + "\t" + $"nextSegment.m_flags={nextSegment.m_flags}\n");
+ }
#endif
- return;
- }
+ return;
+ }
- // NON-STOCK CODE START
+ // NON-STOCK CODE START
+ bool mayCross = true;
#if JUNCTIONRESTRICTIONS || CUSTOMTRAFFICLIGHTS
- if (Options.junctionRestrictionsEnabled || Options.timedLightsEnabled) {
- bool nextIsStartNode = nextNodeId == nextSegment.m_startNode;
- if (nextIsStartNode || nextNodeId == nextSegment.m_endNode) {
+ if (Options.junctionRestrictionsEnabled || Options.timedLightsEnabled) {
+ bool nextIsStartNode = nextNodeId == nextSegment.m_startNode;
+ if (nextIsStartNode || nextNodeId == nextSegment.m_endNode) {
#if JUNCTIONRESTRICTIONS
- if (Options.junctionRestrictionsEnabled && item.m_position.m_segment == nextSegmentId) {
- // check if pedestrians are not allowed to cross here
- if (!m_junctionManager.IsPedestrianCrossingAllowed(nextSegmentId, nextIsStartNode)) {
+ if (Options.junctionRestrictionsEnabled && item.m_position.m_segment == nextSegmentId) {
+ // check if pedestrians are not allowed to cross here
+ if (!m_junctionManager.IsPedestrianCrossingAllowed(nextSegmentId, nextIsStartNode)) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Aborting: Pedestrian crossing prohibited");
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Pedestrian crossing prohibited");
+ }
#endif
- return;
- }
- }
+ mayCross = false;
+ }
+ }
#endif
#if CUSTOMTRAFFICLIGHTS
- if (Options.timedLightsEnabled) {
- // check if pedestrian light won't change to green
- ICustomSegmentLights lights = m_customTrafficLightsManager.GetSegmentLights(nextSegmentId, nextIsStartNode, false);
- if (lights != null && lights.InvalidPedestrianLight) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Aborting: Invalid pedestrian light");
- }
-#endif
- return;
- }
- }
-#endif
- }
- }
-#endif
- // NON-STOCK CODE END
-
- NetInfo nextSegmentInfo = nextSegment.Info;
- NetInfo prevSegmentInfo = prevSegment.Info;
- int nextNumLanes = nextSegmentInfo.m_lanes.Length;
- float distance;
- byte offset;
- if (nextSegmentId == item.m_position.m_segment) {
- Vector3 b = prevLane.CalculatePosition((float)(int)laneSwitchOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
- Vector3 a = nextLane.CalculatePosition((float)(int)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
- distance = Vector3.Distance(a, b);
- offset = connectOffset;
- } else {
- NetInfo.Direction direction = (NetInfo.Direction)((nextNodeId != nextSegment.m_startNode) ? 1 : 2);
- Vector3 b = prevLane.CalculatePosition((float)(int)laneSwitchOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
- Vector3 a = ((direction & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? nextLane.m_bezier.a : nextLane.m_bezier.d;
- distance = Vector3.Distance(a, b);
- offset = (byte)(((direction & NetInfo.Direction.Forward) != NetInfo.Direction.None) ? 255 : 0);
- }
-
- // float prevMaxSpeed = 1f; // stock code commented
- // float prevLaneSpeed = 1f; // stock code commented
- NetInfo.LaneType prevLaneType = NetInfo.LaneType.None;
- if (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
- NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];
- // prevMaxSpeed = prevLaneInfo.m_speedLimit; // stock code commented
- // prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, laneSwitchOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // stock code commented
- prevLaneType = prevLaneInfo.m_laneType;
- if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
- prevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
- }
- }
-
- float prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? prevSegment.m_averageLength : prevLane.m_length;
- float offsetLength = (float)Mathf.Abs(laneSwitchOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength;
- float methodDistance = item.m_methodDistance + offsetLength;
- float comparisonValue = item.m_comparisonValue + offsetLength / (prevLaneSpeed * m_maxLength);
- float duration = item.m_duration + offsetLength / prevMaxSpeed;
-
- if (!m_ignoreCost) {
- int ticketCost = prevLane.m_ticketCost;
- if (ticketCost != 0) {
- comparisonValue += (float)(ticketCost * m_pathRandomizer.Int32(2000u)) * TICKET_COST_CONVERSION_FACTOR;
- }
- }
-
- if (nextLaneIndex < nextNumLanes) {
- NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];
- BufferItem nextItem = default(BufferItem);
-
- nextItem.m_position.m_segment = nextSegmentId;
- nextItem.m_position.m_lane = (byte)nextLaneIndex;
- nextItem.m_position.m_offset = offset;
-
- if ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {
- nextItem.m_methodDistance = 0f;
- } else {
- if (item.m_methodDistance == 0f) {
- comparisonValue += 100f / (0.25f * m_maxLength);
- }
- nextItem.m_methodDistance = methodDistance + distance;
- }
-
- if (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance) && !m_stablePath) { // NON-STOCK CODE (custom walking distance)
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Aborting: Max. walking distance exceeded\n"
- + "\t" + $"nextItem.m_methodDistance={nextItem.m_methodDistance}"
- );
- }
-#endif
- return;
- }
-
- float nextMaxSpeed;
+ if (Options.timedLightsEnabled) {
+ // check if pedestrian light won't change to green
+ ICustomSegmentLights lights = m_customTrafficLightsManager.GetSegmentLights(nextSegmentId, nextIsStartNode, false);
+ if (lights != null && lights.InvalidPedestrianLight) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Aborting: Invalid pedestrian light");
+ }
+#endif
+ return;
+ }
+ }
+#endif
+ }
+ }
+#endif
+ // NON-STOCK CODE END
+
+ NetInfo nextSegmentInfo = nextSegment.Info;
+ NetInfo prevSegmentInfo = prevSegment.Info;
+ int nextNumLanes = nextSegmentInfo.m_lanes.Length;
+ float distance;
+ byte offset;
+ if (nextSegmentId == item.m_position.m_segment) {
+ Vector3 b = prevLane.CalculatePosition((float)(int)laneSwitchOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+ Vector3 a = nextLane.CalculatePosition((float)(int)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+ distance = Vector3.Distance(a, b);
+ offset = connectOffset;
+ } else {
+ NetInfo.Direction direction = (NetInfo.Direction)((nextNodeId != nextSegment.m_startNode) ? 1 : 2);
+ Vector3 b = prevLane.CalculatePosition((float)(int)laneSwitchOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);
+ Vector3 a = ((direction & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? nextLane.m_bezier.a : nextLane.m_bezier.d;
+ distance = Vector3.Distance(a, b);
+ offset = (byte)(((direction & NetInfo.Direction.Forward) != NetInfo.Direction.None) ? 255 : 0);
+ }
+
+ // float prevMaxSpeed = 1f; // stock code commented
+ // float prevLaneSpeed = 1f; // stock code commented
+ NetInfo.LaneType prevLaneType = NetInfo.LaneType.None;
+ if (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {
+ NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];
+ // prevMaxSpeed = prevLaneInfo.m_speedLimit; // stock code commented
+ // prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, laneSwitchOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // stock code commented
+ prevLaneType = prevLaneInfo.m_laneType;
+ if ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {
+ prevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);
+ }
+ }
+
+ float prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? prevSegment.m_averageLength : prevLane.m_length;
+ float offsetLength = (float)Mathf.Abs(laneSwitchOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength;
+ float methodDistance = item.m_methodDistance + offsetLength;
+ float comparisonValue = item.m_comparisonValue + offsetLength / (prevLaneSpeed * m_maxLength);
+ float duration = item.m_duration + offsetLength / prevMaxSpeed;
+
+ if (!m_ignoreCost) {
+ int ticketCost = prevLane.m_ticketCost;
+ if (ticketCost != 0) {
+ comparisonValue += (float)(ticketCost * m_pathRandomizer.Int32(2000u)) * TICKET_COST_CONVERSION_FACTOR;
+ }
+ }
+
+ if (nextLaneIndex < nextNumLanes) {
+ NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];
+ BufferItem nextItem = default(BufferItem);
+
+ nextItem.m_position.m_segment = nextSegmentId;
+ nextItem.m_position.m_lane = (byte)nextLaneIndex;
+ nextItem.m_position.m_offset = offset;
+
+ // NON-STOCK CODE START
+ if (!mayCross && nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Aborting: Crossing prohibited");
+ }
+#endif
+ return;
+ }
+ // NON-STOCK CODE END
+
+ if ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {
+ nextItem.m_methodDistance = 0f;
+ } else {
+ if (item.m_methodDistance == 0f) {
+ comparisonValue += 100f / (0.25f * m_maxLength);
+ }
+ nextItem.m_methodDistance = methodDistance + distance;
+ }
+
+ if (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance) && !m_stablePath) { // NON-STOCK CODE (custom walking distance)
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Aborting: Max. walking distance exceeded\n"
+ + "\t" + $"nextItem.m_methodDistance={nextItem.m_methodDistance}"
+ );
+ }
+#endif
+ return;
+ }
+
+ float nextMaxSpeed;
#if SPEEDLIMITS
- // NON-STOCK CODE START
- nextMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo);
- // NON-STOCK CODE END
+ // NON-STOCK CODE START
+ nextMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo);
+ // NON-STOCK CODE END
#else
nextMaxSpeed = nextLaneInfo.m_speedLimit;
#endif
- nextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.25f * m_maxLength);
- nextItem.m_duration = duration + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);
-
- if ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {
- nextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);
- } else {
- nextItem.m_direction = nextLaneInfo.m_finalDirection;
- }
-
- if (nextLaneId == m_startLaneA) {
- if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetA) &&
- ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetA)) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Aborting: Invalid offset/direction on start lane A\n"
- + "\t" + $"nextItem.m_direction={nextItem.m_direction}\n"
- + "\t" + $"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\n"
- + "\t" + $"m_startOffsetA={m_startOffsetA}"
- );
- }
-#endif
- return;
- }
-
- float nextSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);
- float nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
-
- nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);
- nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;
- }
-
- if (nextLaneId == m_startLaneB) {
- if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetB) &&
- ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetB)) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Aborting: Invalid offset/direction on start lane B\n"
- + "\t" + $"nextItem.m_direction={nextItem.m_direction}\n"
- + "\t" + $"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\n"
- + "\t" + $"m_startOffsetB={m_startOffsetB}"
- );
- }
-#endif
- return;
- }
-
- float nextSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);
- float nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetB) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
-
- nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);
- nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;
- }
-
- nextItem.m_laneID = nextLaneId;
- nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);
+ nextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.25f * m_maxLength);
+ nextItem.m_duration = duration + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);
+
+ if ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {
+ nextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);
+ } else {
+ nextItem.m_direction = nextLaneInfo.m_finalDirection;
+ }
+
+ if (nextLaneId == m_startLaneA) {
+ if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetA) &&
+ ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetA)) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Aborting: Invalid offset/direction on start lane A\n"
+ + "\t" + $"nextItem.m_direction={nextItem.m_direction}\n"
+ + "\t" + $"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\n"
+ + "\t" + $"m_startOffsetA={m_startOffsetA}"
+ );
+ }
+#endif
+ return;
+ }
+
+ float nextSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);
+ float nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+
+ nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);
+ nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;
+ }
+
+ if (nextLaneId == m_startLaneB) {
+ if (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetB) &&
+ ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetB)) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Aborting: Invalid offset/direction on start lane B\n"
+ + "\t" + $"nextItem.m_direction={nextItem.m_direction}\n"
+ + "\t" + $"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\n"
+ + "\t" + $"m_startOffsetB={m_startOffsetB}"
+ );
+ }
+#endif
+ return;
+ }
+
+ float nextSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);
+ float nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetB) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;
+
+ nextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);
+ nextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;
+ }
+
+ nextItem.m_laneID = nextLaneId;
+ nextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);
#if PARKINGAI
- nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);
+ nextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);
#endif
#if ADVANCEDAI && ROUTING
- // NON-STOCK CODE START
- nextItem.m_trafficRand = item.m_trafficRand;
- // NON-STOCK CODE END
+ // NON-STOCK CODE START
+ nextItem.m_trafficRand = item.m_trafficRand;
+ // NON-STOCK CODE END
#endif
#if DEBUG
- if (debug) {
- Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Adding next item\n"
- + "\t" + $"nextItem={nextItem}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $"ProcessItemPedBicycle: Adding next item\n"
+ + "\t" + $"nextItem={nextItem}"
+ );
+ }
#endif
- AddBufferItem(
+ AddBufferItem(
#if DEBUG
- debug,
+ debug,
#endif
- nextItem, item.m_position
- );
- }
- }
+ nextItem, item.m_position
+ );
+ }
+ }
#if ROUTING
- // 5 (custom: process routed vehicle paths)
- private bool ProcessItemRouted(
+ // 5 (custom: process routed vehicle paths)
+ private bool ProcessItemRouted(
#if DEBUG
- bool debug, uint unitId,
+ bool debug, uint unitId,
#endif
- BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed,
+ BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed,
#if ADVANCEDAI
- bool enableAdvancedAI, float laneChangingCost,
+ bool enableAdvancedAI, float laneChangingCost,
#endif
- float segmentSelectionCost, float laneSelectionCost, ushort nextNodeId, ref NetNode nextNode, bool isMiddle, SegmentRoutingData prevSegmentRouting, LaneEndRoutingData prevLaneEndRouting, byte connectOffset, int prevInnerSimilarLaneIndex
- ) {
+ float segmentSelectionCost, float laneSelectionCost, ushort nextNodeId, ref NetNode nextNode, bool isMiddle, SegmentRoutingData prevSegmentRouting, LaneEndRoutingData prevLaneEndRouting, byte connectOffset, int prevInnerSimilarLaneIndex
+ ) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemRouted called.\n"
- + "\t" + $"prevMaxSpeed={prevMaxSpeed}\n"
- + "\t" + $"prevLaneSpeed={prevLaneSpeed}\n"
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemRouted called.\n"
+ + "\t" + $"prevMaxSpeed={prevMaxSpeed}\n"
+ + "\t" + $"prevLaneSpeed={prevLaneSpeed}\n"
#if ADVANCEDAI
- + "\t" + $"enableAdvancedAI={enableAdvancedAI}\n"
- + "\t" + $"laneChangingCost={laneChangingCost}\n"
-#endif
- + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
- + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
- + "\t" + $"nextNodeId={nextNodeId}\n"
- + "\t" + $"isMiddle={isMiddle}\n"
- + "\t" + $"prevSegmentRouting={prevSegmentRouting}\n"
- + "\t" + $"prevLaneEndRouting={prevLaneEndRouting}\n"
- + "\t" + $"connectOffset={connectOffset}\n"
- + "\t" + $"prevInnerSimilarLaneIndex={prevInnerSimilarLaneIndex}\n"
- );
- }
-#endif
-
- /*
- * =======================================================================================================
- * Fetch lane end transitions, check if there are any present
- * =======================================================================================================
- */
- LaneTransitionData[] laneTransitions = prevLaneEndRouting.transitions;
- if (laneTransitions == null) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemRouted: Aborting: No lane transitions");
- }
-#endif
- return false;
- }
-
- ushort prevSegmentId = item.m_position.m_segment;
- int prevLaneIndex = item.m_position.m_lane;
- NetInfo prevSegmentInfo = prevSegment.Info;
- if (prevLaneIndex >= prevSegmentInfo.m_lanes.Length) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemRouted: Aborting: Invalid lane index");
- }
-#endif
- return false;
- }
- NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];
+ + "\t" + $"enableAdvancedAI={enableAdvancedAI}\n"
+ + "\t" + $"laneChangingCost={laneChangingCost}\n"
+#endif
+ + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
+ + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
+ + "\t" + $"nextNodeId={nextNodeId}\n"
+ + "\t" + $"isMiddle={isMiddle}\n"
+ + "\t" + $"prevSegmentRouting={prevSegmentRouting}\n"
+ + "\t" + $"prevLaneEndRouting={prevLaneEndRouting}\n"
+ + "\t" + $"connectOffset={connectOffset}\n"
+ + "\t" + $"prevInnerSimilarLaneIndex={prevInnerSimilarLaneIndex}\n"
+ );
+ }
+#endif
+
+ /*
+ * =======================================================================================================
+ * Fetch lane end transitions, check if there are any present
+ * =======================================================================================================
+ */
+ LaneTransitionData[] laneTransitions = prevLaneEndRouting.transitions;
+ if (laneTransitions == null) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemRouted: Aborting: No lane transitions");
+ }
+#endif
+ return false;
+ }
+
+ ushort prevSegmentId = item.m_position.m_segment;
+ int prevLaneIndex = item.m_position.m_lane;
+ NetInfo prevSegmentInfo = prevSegment.Info;
+ if (prevLaneIndex >= prevSegmentInfo.m_lanes.Length) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemRouted: Aborting: Invalid lane index");
+ }
+#endif
+ return false;
+ }
+ NetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];
#if VEHICLERESTRICTIONS
- /*
- * =======================================================================================================
- * Check vehicle restrictions, especially bans
- * =======================================================================================================
- */
- bool canUseLane = CanUseLane(prevSegmentId, prevSegmentInfo, prevLaneIndex, prevLaneInfo);
- if (! canUseLane && Options.vehicleRestrictionsAggression == VehicleRestrictionsAggression.Strict) {
- // vehicle is strictly prohibited to use this lane
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemRouted: Vehicle restrictions: Aborting: Strict vehicle restrictions active");
- }
-#endif
- return false;
- }
-#endif
-
- bool strictLaneRouting =
- m_isLaneArrowObeyingEntity &&
- nextNode.Info.m_class.m_service != ItemClass.Service.Beautification &&
- (nextNode.m_flags & NetNode.Flags.Untouchable) == NetNode.Flags.None
- ;
- bool prevIsCarLane =
- (prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&
- (prevLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None
- ;
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemRouted: Strict lane routing? {strictLaneRouting}\n"
- + "\t" + $"m_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n"
- + "\t" + $"nextNode.Info.m_class.m_service={nextNode.Info.m_class.m_service}\n"
- + "\t" + $"nextNode.m_flags={nextNode.m_flags}\n"
- + "\t" + $"prevIsCarLane={prevIsCarLane}"
- );
- }
-#endif
-
- /*
- * =======================================================================================================
- * Check if u-turns may be performed
- * =======================================================================================================
- */
- bool isUturnAllowedHere = false; // is u-turn allowed at this place?
- if ((this.m_vehicleTypes & (VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Monorail)) == VehicleInfo.VehicleType.None) { // is vehicle able to perform a u-turn?
- bool isStockUturnPoint = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;
+ /*
+ * =======================================================================================================
+ * Check vehicle restrictions, especially bans
+ * =======================================================================================================
+ */
+ bool canUseLane = CanUseLane(prevSegmentId, prevSegmentInfo, prevLaneIndex, prevLaneInfo);
+ if (! canUseLane && Options.vehicleRestrictionsAggression == VehicleRestrictionsAggression.Strict) {
+ // vehicle is strictly prohibited to use this lane
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemRouted: Vehicle restrictions: Aborting: Strict vehicle restrictions active");
+ }
+#endif
+ return false;
+ }
+#endif
+
+ bool strictLaneRouting =
+ m_isLaneArrowObeyingEntity &&
+ nextNode.Info.m_class.m_service != ItemClass.Service.Beautification &&
+ (nextNode.m_flags & NetNode.Flags.Untouchable) == NetNode.Flags.None
+ ;
+ bool prevIsCarLane =
+ (prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&
+ (prevLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None
+ ;
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemRouted: Strict lane routing? {strictLaneRouting}\n"
+ + "\t" + $"m_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\n"
+ + "\t" + $"nextNode.Info.m_class.m_service={nextNode.Info.m_class.m_service}\n"
+ + "\t" + $"nextNode.m_flags={nextNode.m_flags}\n"
+ + "\t" + $"prevIsCarLane={prevIsCarLane}"
+ );
+ }
+#endif
+
+ /*
+ * =======================================================================================================
+ * Check if u-turns may be performed
+ * =======================================================================================================
+ */
+ bool isUturnAllowedHere = false; // is u-turn allowed at this place?
+ if ((this.m_vehicleTypes & (VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Monorail)) == VehicleInfo.VehicleType.None) { // is vehicle able to perform a u-turn?
+ bool isStockUturnPoint = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;
#if JUNCTIONRESTRICTIONS
- if (Options.junctionRestrictionsEnabled) {
- bool nextIsStartNode = nextNodeId == prevSegment.m_startNode;
- bool prevIsOutgoingOneWay = nextIsStartNode ? prevSegmentRouting.startNodeOutgoingOneWay : prevSegmentRouting.endNodeOutgoingOneWay;
-
- // determine if the vehicle may u-turn at the target node, according to customization
- isUturnAllowedHere =
- m_isRoadVehicle && // only road vehicles may perform u-turns
- prevIsCarLane && // u-turns for road vehicles only
- (!m_isHeavyVehicle || isStockUturnPoint) && // only small vehicles may perform u-turns OR everyone at stock u-turn points
- !prevIsOutgoingOneWay && // do not u-turn on one-ways
- (
- m_junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode)
- /*|| // only do u-turns if allowed
- (!m_queueItem.spawned && // or a yet unspawned vehicle ...
- (prevSegmentId == m_startSegmentA || prevSegmentId == m_startSegmentB)) // ... starts at the current segment*/
- )
- ;
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemRouted: Junction restrictions: Is u-turn allowed here? {isUturnAllowedHere}\n"
- + "\t" + $"m_isRoadVehicle={m_isRoadVehicle}\n"
- + "\t" + $"prevIsCarLane={prevIsCarLane}\n"
- + "\t" + $"m_isHeavyVehicle={m_isHeavyVehicle}\n"
- + "\t" + $"isStockUturnPoint={isStockUturnPoint}\n"
- + "\t" + $"prevIsOutgoingOneWay={prevIsOutgoingOneWay}\n"
- + "\t" + $"m_junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode)={m_junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode)}\n"
- + "\t" + $"m_queueItem.vehicleId={m_queueItem.vehicleId}\n"
- + "\t" + $"m_queueItem.spawned={m_queueItem.spawned}\n"
- + "\t" + $"prevSegmentId={prevSegmentId}\n"
- + "\t" + $"m_startSegmentA={m_startSegmentA}\n"
- + "\t" + $"m_startSegmentB={m_startSegmentB}"
- );
- }
-#endif
- } else {
-#endif
- isUturnAllowedHere = isStockUturnPoint;
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemRouted: Junction restrictions disabled: Is u-turn allowed here? {isUturnAllowedHere}");
- }
+ if (Options.junctionRestrictionsEnabled) {
+ bool nextIsStartNode = nextNodeId == prevSegment.m_startNode;
+ bool prevIsOutgoingOneWay = nextIsStartNode ? prevSegmentRouting.startNodeOutgoingOneWay : prevSegmentRouting.endNodeOutgoingOneWay;
+
+ // determine if the vehicle may u-turn at the target node, according to customization
+ isUturnAllowedHere =
+ m_isRoadVehicle && // only road vehicles may perform u-turns
+ prevIsCarLane && // u-turns for road vehicles only
+ (!m_isHeavyVehicle || isStockUturnPoint) && // only small vehicles may perform u-turns OR everyone at stock u-turn points
+ !prevIsOutgoingOneWay && // do not u-turn on one-ways
+ (
+ m_junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode)
+ /*|| // only do u-turns if allowed
+ (!m_queueItem.spawned && // or a yet unspawned vehicle ...
+ (prevSegmentId == m_startSegmentA || prevSegmentId == m_startSegmentB)) // ... starts at the current segment*/
+ )
+ ;
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemRouted: Junction restrictions: Is u-turn allowed here? {isUturnAllowedHere}\n"
+ + "\t" + $"m_isRoadVehicle={m_isRoadVehicle}\n"
+ + "\t" + $"prevIsCarLane={prevIsCarLane}\n"
+ + "\t" + $"m_isHeavyVehicle={m_isHeavyVehicle}\n"
+ + "\t" + $"isStockUturnPoint={isStockUturnPoint}\n"
+ + "\t" + $"prevIsOutgoingOneWay={prevIsOutgoingOneWay}\n"
+ + "\t" + $"m_junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode)={m_junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode)}\n"
+ + "\t" + $"m_queueItem.vehicleId={m_queueItem.vehicleId}\n"
+ + "\t" + $"m_queueItem.spawned={m_queueItem.spawned}\n"
+ + "\t" + $"prevSegmentId={prevSegmentId}\n"
+ + "\t" + $"m_startSegmentA={m_startSegmentA}\n"
+ + "\t" + $"m_startSegmentB={m_startSegmentB}"
+ );
+ }
+#endif
+ } else {
+#endif
+ isUturnAllowedHere = isStockUturnPoint;
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemRouted: Junction restrictions disabled: Is u-turn allowed here? {isUturnAllowedHere}");
+ }
#endif
#if JUNCTIONRESTRICTIONS
- }
+ }
#endif
- }
+ }
#if VEHICLERESTRICTIONS
- /*
- * =======================================================================================================
- * Apply vehicle restriction costs
- * =======================================================================================================
- */
- if (!canUseLane) {
- laneSelectionCost *= VehicleRestrictionsManager.PATHFIND_PENALTIES[(int)Options.vehicleRestrictionsAggression];
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemRouted: Vehicle restrictions: Applied lane costs\n"
- + "\t" + $"laneSelectionCost={laneSelectionCost}"
- );
- }
-#endif
- }
-#endif
-
- /*
- * =======================================================================================================
- * Apply costs for large vehicles using inner lanes on highways
- * =======================================================================================================
- */
- if (Options.preferOuterLane &&
- m_isHeavyVehicle &&
- m_isRoadVehicle &&
- prevIsCarLane &&
- prevSegmentRouting.highway &&
- prevLaneInfo.m_similarLaneCount > 1 &&
- m_pathRandomizer.Int32(m_conf.PathFinding.HeavyVehicleInnerLanePenaltySegmentSel) == 0) {
-
- int prevOuterSimilarLaneIndex = m_routingManager.CalcOuterSimilarLaneIndex(prevLaneInfo);
- float prevRelOuterLane = ((float)prevOuterSimilarLaneIndex / (float)(prevLaneInfo.m_similarLaneCount - 1));
- laneSelectionCost *= 1f + m_conf.PathFinding.HeavyVehicleMaxInnerLanePenalty * prevRelOuterLane;
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemRouted: Heavy trucks prefer outer lanes on highways: Applied lane costs\n"
- + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
- + "\t" + $"Options.preferOuterLane={Options.preferOuterLane}\n"
- + "\t" + $"m_isHeavyVehicle={m_isHeavyVehicle}\n"
- + "\t" + $"m_isRoadVehicle={m_isRoadVehicle}\n"
- + "\t" + $"prevIsCarLane={prevIsCarLane}\n"
- + "\t" + $"prevSegmentRouting.highway={prevSegmentRouting.highway}\n"
- + "\t" + $"prevLaneInfo.m_similarLaneCount={prevLaneInfo.m_similarLaneCount}\n"
- + "\t" + $"prevOuterSimilarLaneIndex={prevOuterSimilarLaneIndex}\n"
- + "\t" + $"prevRelOuterLane={prevRelOuterLane}"
- );
- }
-#endif
- }
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, $"ProcessItemRouted: Final cost factors:\n"
- + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
- + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
- + "\t" + $"laneChangingCost={laneChangingCost}"
- );
- }
-#endif
-
- /*
- * =======================================================================================================
- * Explore available lane end routings
- * =======================================================================================================
- */
- NetManager netManager = Singleton.instance;
- bool blocked = false;
- bool uturnExplored = false;
- for (int k = 0; k < laneTransitions.Length; ++k) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Exploring lane transition #{k}: {laneTransitions[k]}");
- }
-#endif
-
- ushort nextSegmentId = laneTransitions[k].segmentId;
-
- if (nextSegmentId == 0) {
- continue;
- }
-
- if (laneTransitions[k].type == LaneEndTransitionType.Invalid) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Skipping transition: Transition is invalid");
- }
-#endif
- continue;
- }
-
- if (nextSegmentId == prevSegmentId) {
- if (!isUturnAllowedHere) {
-#if DEBUG
- if (debug) {
- Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Skipping transition: U-turn is not allowed here");
- }
-#endif
-
- // prevent double/forbidden exploration of previous segment by vanilla code during this method execution
- continue;
- }
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Processing transition: Exploring u-turn");
- }
-#endif
- // we are going to explore a regular u-turn
- uturnExplored = true;
- }
-
- // allow vehicles to ignore strict lane routing when moving off
- bool relaxedLaneRouting =
- m_isRoadVehicle &&
- ((!m_queueItem.spawned || (m_queueItem.vehicleType & (ExtVehicleType.PublicTransport | ExtVehicleType.Emergency)) != ExtVehicleType.None) &&
- (laneTransitions[k].laneId == m_startLaneA || laneTransitions[k].laneId == m_startLaneB));
-
-#if DEBUG
- if (debug) {
- Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Relaxed lane routing? {relaxedLaneRouting}\n"
- + "\t" + $"relaxedLaneRouting={relaxedLaneRouting}\n"
- + "\t" + $"m_isRoadVehicle={m_isRoadVehicle}\n"
- + "\t" + $"m_queueItem.spawned={m_queueItem.spawned}\n"
- + "\t" + $"m_queueItem.vehicleType={m_queueItem.vehicleType}\n"
- + "\t" + $"m_queueItem.vehicleId={m_queueItem.vehicleId}\n"
- + "\t" + $"m_startLaneA={m_startLaneA}\n"
- + "\t" + $"m_startLaneB={m_startLaneB}"
- );
- }
+ /*
+ * =======================================================================================================
+ * Apply vehicle restriction costs
+ * =======================================================================================================
+ */
+ if (!canUseLane) {
+ laneSelectionCost *= VehicleRestrictionsManager.PATHFIND_PENALTIES[(int)Options.vehicleRestrictionsAggression];
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemRouted: Vehicle restrictions: Applied lane costs\n"
+ + "\t" + $"laneSelectionCost={laneSelectionCost}"
+ );
+ }
+#endif
+ }
+#endif
+
+ /*
+ * =======================================================================================================
+ * Apply costs for large vehicles using inner lanes on highways
+ * =======================================================================================================
+ */
+ if (Options.preferOuterLane &&
+ m_isHeavyVehicle &&
+ m_isRoadVehicle &&
+ prevIsCarLane &&
+ prevSegmentRouting.highway &&
+ prevLaneInfo.m_similarLaneCount > 1 &&
+ m_pathRandomizer.Int32(m_conf.PathFinding.HeavyVehicleInnerLanePenaltySegmentSel) == 0) {
+
+ int prevOuterSimilarLaneIndex = m_routingManager.CalcOuterSimilarLaneIndex(prevLaneInfo);
+ float prevRelOuterLane = ((float)prevOuterSimilarLaneIndex / (float)(prevLaneInfo.m_similarLaneCount - 1));
+ laneSelectionCost *= 1f + m_conf.PathFinding.HeavyVehicleMaxInnerLanePenalty * prevRelOuterLane;
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemRouted: Heavy trucks prefer outer lanes on highways: Applied lane costs\n"
+ + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
+ + "\t" + $"Options.preferOuterLane={Options.preferOuterLane}\n"
+ + "\t" + $"m_isHeavyVehicle={m_isHeavyVehicle}\n"
+ + "\t" + $"m_isRoadVehicle={m_isRoadVehicle}\n"
+ + "\t" + $"prevIsCarLane={prevIsCarLane}\n"
+ + "\t" + $"prevSegmentRouting.highway={prevSegmentRouting.highway}\n"
+ + "\t" + $"prevLaneInfo.m_similarLaneCount={prevLaneInfo.m_similarLaneCount}\n"
+ + "\t" + $"prevOuterSimilarLaneIndex={prevOuterSimilarLaneIndex}\n"
+ + "\t" + $"prevRelOuterLane={prevRelOuterLane}"
+ );
+ }
+#endif
+ }
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, $"ProcessItemRouted: Final cost factors:\n"
+ + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
+ + "\t" + $"laneSelectionCost={laneSelectionCost}\n"
+ + "\t" + $"laneChangingCost={laneChangingCost}"
+ );
+ }
+#endif
+
+ /*
+ * =======================================================================================================
+ * Explore available lane end routings
+ * =======================================================================================================
+ */
+ NetManager netManager = Singleton.instance;
+ bool blocked = false;
+ bool uturnExplored = false;
+ for (int k = 0; k < laneTransitions.Length; ++k) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Exploring lane transition #{k}: {laneTransitions[k]}");
+ }
+#endif
+
+ ushort nextSegmentId = laneTransitions[k].segmentId;
+
+ if (nextSegmentId == 0) {
+ continue;
+ }
+
+ if (laneTransitions[k].type == LaneEndTransitionType.Invalid) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Skipping transition: Transition is invalid");
+ }
+#endif
+ continue;
+ }
+
+ if (nextSegmentId == prevSegmentId) {
+ if (!isUturnAllowedHere) {
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Skipping transition: U-turn is not allowed here");
+ }
+#endif
+
+ // prevent double/forbidden exploration of previous segment by vanilla code during this method execution
+ continue;
+ }
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Processing transition: Exploring u-turn");
+ }
+#endif
+ // we are going to explore a regular u-turn
+ uturnExplored = true;
+ }
+
+ // allow vehicles to ignore strict lane routing when moving off
+ bool relaxedLaneRouting =
+ m_isRoadVehicle &&
+ ((!m_queueItem.spawned || (m_queueItem.vehicleType & (ExtVehicleType.PublicTransport | ExtVehicleType.Emergency)) != ExtVehicleType.None) &&
+ (laneTransitions[k].laneId == m_startLaneA || laneTransitions[k].laneId == m_startLaneB));
+
+#if DEBUG
+ if (debug) {
+ Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Relaxed lane routing? {relaxedLaneRouting}\n"
+ + "\t" + $"relaxedLaneRouting={relaxedLaneRouting}\n"
+ + "\t" + $"m_isRoadVehicle={m_isRoadVehicle}\n"
+ + "\t" + $"m_queueItem.spawned={m_queueItem.spawned}\n"
+ + "\t" + $"m_queueItem.vehicleType={m_queueItem.vehicleType}\n"
+ + "\t" + $"m_queueItem.vehicleId={m_queueItem.vehicleId}\n"
+ + "\t" + $"m_startLaneA={m_startLaneA}\n"
+ + "\t" + $"m_startLaneB={m_startLaneB}"
+ );
+ }
#endif
- if (
- !relaxedLaneRouting &&
- (strictLaneRouting && laneTransitions[k].type == LaneEndTransitionType.Relaxed)
- ) {
+ if (
+ !relaxedLaneRouting &&
+ (strictLaneRouting && laneTransitions[k].type == LaneEndTransitionType.Relaxed)
+ ) {
#if DEBUG
- if (debug) {
- Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Aborting: Cannot explore relaxed lane\n"
- + "\t" + $"relaxedLaneRouting={relaxedLaneRouting}\n"
- + "\t" + $"strictLaneRouting={strictLaneRouting}\n"
- + "\t" + $"laneTransitions[k].type={laneTransitions[k].type}"
- );
- }
+ if (debug) {
+ Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Aborting: Cannot explore relaxed lane\n"
+ + "\t" + $"relaxedLaneRouting={relaxedLaneRouting}\n"
+ + "\t" + $"strictLaneRouting={strictLaneRouting}\n"
+ + "\t" + $"laneTransitions[k].type={laneTransitions[k].type}"
+ );
+ }
#endif
- continue;
- }
+ continue;
+ }
#if DEBUG
- if (debug) {
- Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Exploring lane transition now\n"
+ if (debug) {
+ Debug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $"ProcessItemRouted: Exploring lane transition now\n"
#if ADVANCEDAI
- + "\t" + $"enableAdvancedAI={enableAdvancedAI}\n"
- + "\t" + $"laneChangingCost={laneChangingCost}\n"
+ + "\t" + $"enableAdvancedAI={enableAdvancedAI}\n"
+ + "\t" + $"laneChangingCost={laneChangingCost}\n"
#endif
- + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
- + "\t" + $"laneSelectionCost={laneSelectionCost}"
- );
- }
+ + "\t" + $"segmentSelectionCost={segmentSelectionCost}\n"
+ + "\t" + $"laneSelectionCost={laneSelectionCost}"
+ );
+ }
#endif
- if (
- ProcessItemCosts(
+ if (
+ ProcessItemCosts(
#if DEBUG
- debug, unitId,
+ debug, unitId,
#endif
- item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed,
+ item, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed,
#if ADVANCEDAI
- enableAdvancedAI, laneChangingCost,
-#endif
- nextNodeId, ref nextNode, isMiddle, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], segmentSelectionCost, laneSelectionCost, laneTransitions[k], ref prevInnerSimilarLaneIndex, connectOffset, true, false
- )
- ) {
- blocked = true;
- }
- }
-
- return blocked && !uturnExplored;
- }
-#endif
-
- private void AddBufferItem(
-#if DEBUG
- bool debug,
-#endif
- BufferItem item, PathUnit.Position target
- ) {
-#if DEBUG
- if (debug) {
- m_debugPositions[target.m_segment].Add(item.m_position.m_segment);
- }
-#endif
-
- uint laneLocation = m_laneLocation[item.m_laneID];
- uint locPathFindIndex = laneLocation >> 16; // upper 16 bit, expected (?) path find index
- int bufferIndex = (int)(laneLocation & 65535u); // lower 16 bit
- int comparisonBufferPos;
-
- if (locPathFindIndex == m_pathFindIndex) {
- if (item.m_comparisonValue >= m_buffer[bufferIndex].m_comparisonValue) {
- return;
- }
-
- int bufferPosIndex = bufferIndex >> 6; // arithmetic shift (sign stays), upper 10 bit
- int bufferPos = bufferIndex & -64; // upper 10 bit (no shift)
- if (bufferPosIndex < m_bufferMinPos || (bufferPosIndex == m_bufferMinPos && bufferPos < m_bufferMin[bufferPosIndex])) {
- return;
- }
-
- comparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);
- if (comparisonBufferPos == bufferPosIndex) {
- m_buffer[bufferIndex] = item;
- m_laneTarget[item.m_laneID] = target;
- return;
- }
-
- int newBufferIndex = bufferPosIndex << 6 | m_bufferMax[bufferPosIndex]--;
- BufferItem bufferItem = m_buffer[newBufferIndex];
- m_laneLocation[bufferItem.m_laneID] = laneLocation;
- m_buffer[bufferIndex] = bufferItem;
- } else {
- comparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);
- }
-
- if (comparisonBufferPos >= 1024 || comparisonBufferPos < 0) {
- return;
- }
-
- while (m_bufferMax[comparisonBufferPos] == 63) {
- ++comparisonBufferPos;
- if (comparisonBufferPos == 1024) {
- return;
- }
- }
-
- if (comparisonBufferPos > m_bufferMaxPos) {
- m_bufferMaxPos = comparisonBufferPos;
- }
-
- bufferIndex = (comparisonBufferPos << 6 | ++m_bufferMax[comparisonBufferPos]);
- m_buffer[bufferIndex] = item;
- m_laneLocation[item.m_laneID] = (m_pathFindIndex << 16 | (uint)bufferIndex);
- m_laneTarget[item.m_laneID] = target;
- }
-
- private float CalculateLaneSpeed(float maxSpeed, byte startOffset, byte endOffset, ref NetSegment segment, NetInfo.Lane laneInfo) {
- NetInfo.Direction direction = ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? laneInfo.m_finalDirection : NetInfo.InvertDirection(laneInfo.m_finalDirection);
- if ((direction & NetInfo.Direction.Avoid) != NetInfo.Direction.None) {
- if (endOffset > startOffset && direction == NetInfo.Direction.AvoidForward) {
- return maxSpeed * 0.1f;
- }
- if (endOffset < startOffset && direction == NetInfo.Direction.AvoidBackward) {
- return maxSpeed * 0.1f;
- }
- return maxSpeed * 0.2f;
- }
- return maxSpeed;
- }
-
- private void GetLaneDirection(PathUnit.Position pathPos, out NetInfo.Direction direction, out NetInfo.LaneType laneType
+ enableAdvancedAI, laneChangingCost,
+#endif
+ nextNodeId, ref nextNode, isMiddle, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], segmentSelectionCost, laneSelectionCost, laneTransitions[k], ref prevInnerSimilarLaneIndex, connectOffset, true, false
+ )
+ ) {
+ blocked = true;
+ }
+ }
+
+ return blocked && !uturnExplored;
+ }
+#endif
+
+ private void AddBufferItem(
+#if DEBUG
+ bool debug,
+#endif
+ BufferItem item, PathUnit.Position target
+ ) {
+#if DEBUG
+ if (debug) {
+ m_debugPositions[target.m_segment].Add(item.m_position.m_segment);
+ }
+#endif
+
+ uint laneLocation = m_laneLocation[item.m_laneID];
+ uint locPathFindIndex = laneLocation >> 16; // upper 16 bit, expected (?) path find index
+ int bufferIndex = (int)(laneLocation & 65535u); // lower 16 bit
+ int comparisonBufferPos;
+
+ if (locPathFindIndex == m_pathFindIndex) {
+ if (item.m_comparisonValue >= m_buffer[bufferIndex].m_comparisonValue) {
+ return;
+ }
+
+ int bufferPosIndex = bufferIndex >> 6; // arithmetic shift (sign stays), upper 10 bit
+ int bufferPos = bufferIndex & -64; // upper 10 bit (no shift)
+ if (bufferPosIndex < m_bufferMinPos || (bufferPosIndex == m_bufferMinPos && bufferPos < m_bufferMin[bufferPosIndex])) {
+ return;
+ }
+
+ comparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);
+ if (comparisonBufferPos == bufferPosIndex) {
+ m_buffer[bufferIndex] = item;
+ m_laneTarget[item.m_laneID] = target;
+ return;
+ }
+
+ int newBufferIndex = bufferPosIndex << 6 | m_bufferMax[bufferPosIndex]--;
+ BufferItem bufferItem = m_buffer[newBufferIndex];
+ m_laneLocation[bufferItem.m_laneID] = laneLocation;
+ m_buffer[bufferIndex] = bufferItem;
+ } else {
+ comparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);
+ }
+
+ if (comparisonBufferPos >= 1024 || comparisonBufferPos < 0) {
+ return;
+ }
+
+ while (m_bufferMax[comparisonBufferPos] == 63) {
+ ++comparisonBufferPos;
+ if (comparisonBufferPos == 1024) {
+ return;
+ }
+ }
+
+ if (comparisonBufferPos > m_bufferMaxPos) {
+ m_bufferMaxPos = comparisonBufferPos;
+ }
+
+ bufferIndex = (comparisonBufferPos << 6 | ++m_bufferMax[comparisonBufferPos]);
+ m_buffer[bufferIndex] = item;
+ m_laneLocation[item.m_laneID] = (m_pathFindIndex << 16 | (uint)bufferIndex);
+ m_laneTarget[item.m_laneID] = target;
+ }
+
+ private float CalculateLaneSpeed(float maxSpeed, byte startOffset, byte endOffset, ref NetSegment segment, NetInfo.Lane laneInfo) {
+ NetInfo.Direction direction = ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? laneInfo.m_finalDirection : NetInfo.InvertDirection(laneInfo.m_finalDirection);
+ if ((direction & NetInfo.Direction.Avoid) != NetInfo.Direction.None) {
+ if (endOffset > startOffset && direction == NetInfo.Direction.AvoidForward) {
+ return maxSpeed * 0.1f;
+ }
+ if (endOffset < startOffset && direction == NetInfo.Direction.AvoidBackward) {
+ return maxSpeed * 0.1f;
+ }
+ return maxSpeed * 0.2f;
+ }
+ return maxSpeed;
+ }
+
+ private void GetLaneDirection(PathUnit.Position pathPos, out NetInfo.Direction direction, out NetInfo.LaneType laneType
#if PARKINGAI
- , out VehicleInfo.VehicleType vehicleType
-#endif
- ) {
- NetManager netManager = Singleton.instance;
- NetInfo info = netManager.m_segments.m_buffer[pathPos.m_segment].Info;
- if (info.m_lanes.Length > pathPos.m_lane) {
- direction = info.m_lanes[pathPos.m_lane].m_finalDirection;
- laneType = info.m_lanes[pathPos.m_lane].m_laneType;
+ , out VehicleInfo.VehicleType vehicleType
+#endif
+ ) {
+ NetManager netManager = Singleton.instance;
+ NetInfo info = netManager.m_segments.m_buffer[pathPos.m_segment].Info;
+ if (info.m_lanes.Length > pathPos.m_lane) {
+ direction = info.m_lanes[pathPos.m_lane].m_finalDirection;
+ laneType = info.m_lanes[pathPos.m_lane].m_laneType;
#if PARKINGAI
- vehicleType = info.m_lanes[pathPos.m_lane].m_vehicleType;
-#endif
- if ((netManager.m_segments.m_buffer[pathPos.m_segment].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {
- direction = NetInfo.InvertDirection(direction);
- }
- } else {
- direction = NetInfo.Direction.None;
- laneType = NetInfo.LaneType.None;
+ vehicleType = info.m_lanes[pathPos.m_lane].m_vehicleType;
+#endif
+ if ((netManager.m_segments.m_buffer[pathPos.m_segment].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {
+ direction = NetInfo.InvertDirection(direction);
+ }
+ } else {
+ direction = NetInfo.Direction.None;
+ laneType = NetInfo.LaneType.None;
#if PARKINGAI
- vehicleType = VehicleInfo.VehicleType.None;
+ vehicleType = VehicleInfo.VehicleType.None;
#endif
- }
- }
+ }
+ }
#if VEHICLERESTRICTIONS
- private bool CanUseLane(ushort segmentId, NetInfo segmentInfo, int laneIndex, NetInfo.Lane laneInfo) {
- if (!Options.vehicleRestrictionsEnabled ||
- m_queueItem.vehicleType == ExtVehicleType.None ||
- m_queueItem.vehicleType == ExtVehicleType.Tram ||
- (laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) == VehicleInfo.VehicleType.None) {
- return true;
- }
+ private bool CanUseLane(ushort segmentId, NetInfo segmentInfo, int laneIndex, NetInfo.Lane laneInfo) {
+ if (!Options.vehicleRestrictionsEnabled ||
+ m_queueItem.vehicleType == ExtVehicleType.None ||
+ m_queueItem.vehicleType == ExtVehicleType.Tram ||
+ (laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) == VehicleInfo.VehicleType.None) {
+ return true;
+ }
- ExtVehicleType allowedTypes = m_vehicleRestrictionsManager.GetAllowedVehicleTypes(segmentId, segmentInfo, (uint)laneIndex, laneInfo, VehicleRestrictionsMode.Configured);
+ ExtVehicleType allowedTypes = m_vehicleRestrictionsManager.GetAllowedVehicleTypes(segmentId, segmentInfo, (uint)laneIndex, laneInfo, VehicleRestrictionsMode.Configured);
- return ((allowedTypes & m_queueItem.vehicleType) != ExtVehicleType.None);
- }
+ return ((allowedTypes & m_queueItem.vehicleType) != ExtVehicleType.None);
+ }
#endif
#if ADVANCEDAI && ROUTING
- private void CalculateAdvancedAiCostFactors(
-#if DEBUG
- bool debug, uint unit,
-#endif
- ref BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, ushort nextNodeId, ref NetNode nextNode, ref float segmentSelectionCost, ref float laneSelectionCost, ref float laneChangingCost
- ) {
-#if DEBUG
- if (debug) {
- Debug(unit, item, $"CalculateAdvancedAiCostFactors called.\n" +
- "\t" + $"nextNodeId={nextNodeId}\n" +
- "\t" + $"segmentSelectionCost={segmentSelectionCost}\n" +
- "\t" + $"laneSelectionCost={laneSelectionCost}\n" +
- "\t" + $"laneChangingCost={laneChangingCost}"
- );
- }
-#endif
-
- NetInfo prevSegmentInfo = prevSegment.Info;
- bool nextIsJunction = (nextNode.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction;
-
- if (nextIsJunction) {
- /*
- * =======================================================================================================
- * Calculate costs for randomized lane selection behind junctions and highway transitions
- * =======================================================================================================
- */
- // TODO check if highway transitions are actually covered by this code
- if (
- !m_isHeavyVehicle &&
- m_conf.AdvancedVehicleAI.LaneRandomizationJunctionSel > 0 &&
- m_pathRandomizer.Int32(m_conf.AdvancedVehicleAI.LaneRandomizationJunctionSel) == 0 &&
- m_pathRandomizer.Int32((uint)prevSegmentInfo.m_lanes.Length) == 0
- ) {
- // randomized lane selection at junctions
- laneSelectionCost *= 1f + m_conf.AdvancedVehicleAI.LaneRandomizationCostFactor;
-
-#if DEBUG
- if (debug) {
- Debug(unit, item, $"CalculateAdvancedAiCostFactors: Calculated randomized lane selection costs\n" +
- "\t" + $"laneSelectionCost={laneSelectionCost}"
- );
- }
-#endif
- }
-
- /*
- * =======================================================================================================
- * Calculate junction costs
- * =======================================================================================================
- */
- // TODO if (prevSegmentRouting.highway) ?
- segmentSelectionCost *= 1f + m_conf.AdvancedVehicleAI.JunctionBaseCost;
-
-#if DEBUG
- if (debug) {
- Debug(unit, item, $"CalculateAdvancedAiCostFactors: Calculated junction costs\n" +
- "\t" + $"segmentSelectionCost={segmentSelectionCost}"
- );
- }
-#endif
- }
-
- bool nextIsStartNode = prevSegment.m_startNode == nextNodeId;
- bool nextIsEndNode = nextNodeId == prevSegment.m_endNode;
- if (nextIsStartNode || nextIsEndNode) { // next node is a regular node
- /*
- * =======================================================================================================
- * Calculate traffic measurement costs for segment selection
- * =======================================================================================================
- */
- NetInfo.Direction prevFinalDir = nextIsStartNode ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;
- prevFinalDir = ((prevSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? prevFinalDir : NetInfo.InvertDirection(prevFinalDir);
-
- float segmentTraffic = Mathf.Clamp(1f - (float)m_trafficMeasurementManager.SegmentDirTrafficData[m_trafficMeasurementManager.GetDirIndex(item.m_position.m_segment, prevFinalDir)].meanSpeed / (float)TrafficMeasurementManager.REF_REL_SPEED + item.m_trafficRand, 0, 1f);
-
- segmentSelectionCost *= 1f +
- m_conf.AdvancedVehicleAI.TrafficCostFactor *
- segmentTraffic;
-
- if (
- m_conf.AdvancedVehicleAI.LaneDensityRandInterval > 0 &&
- nextIsJunction &&
- (nextNode.m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)
- ) {
- item.m_trafficRand = 0.01f * ((float)m_pathRandomizer.Int32((uint)m_conf.AdvancedVehicleAI.LaneDensityRandInterval + 1u) - m_conf.AdvancedVehicleAI.LaneDensityRandInterval / 2f);
- }
-
- if (
- m_conf.AdvancedVehicleAI.LaneChangingJunctionBaseCost > 0 &&
- (Singleton.instance.m_nodes.m_buffer[nextIsStartNode ? prevSegment.m_endNode : prevSegment.m_startNode].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction // check previous node
- ) {
- /*
- * =======================================================================================================
- * Calculate lane changing base cost factor when in front of junctions
- * =======================================================================================================
- */
- laneChangingCost *= m_conf.AdvancedVehicleAI.LaneChangingJunctionBaseCost;
-
-#if DEBUG
- if (debug) {
- Debug(unit, item, $"CalculateAdvancedAiCostFactors: Calculated in-front-of-junction lane changing costs\n" +
- "\t" + $"laneChangingCost={laneChangingCost}"
- );
- }
-#endif
- }
-
- /*
- * =======================================================================================================
- * Calculate general lane changing base cost factor
- * =======================================================================================================
- */
- if (
- m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost > 0 &&
- m_conf.AdvancedVehicleAI.LaneChangingBaseMaxCost > m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost
- ) {
- float rand = (float)m_pathRandomizer.Int32(101u) / 100f;
- laneChangingCost *= m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost + rand * (m_conf.AdvancedVehicleAI.LaneChangingBaseMaxCost - m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost);
-
-#if DEBUG
- if (debug) {
- Debug(unit, item, $"CalculateAdvancedAiCostFactors: Calculated base lane changing costs\n" +
- "\t" + $"laneChangingCost={laneChangingCost}"
- );
- }
-#endif
- }
- }
-
-#if DEBUG
- if (debug) {
- Debug(unit, item, $"CalculateAdvancedAiCostFactors: Calculated cost factors\n" +
- "\t" + $"segmentSelectionCost={segmentSelectionCost}\n" +
- "\t" + $"laneSelectionCost={laneSelectionCost}\n" +
- "\t" + $"laneChangingCost={laneChangingCost}"
- );
- }
-#endif
- }
-#endif
-
- private void PathFindThread() {
- while (true) {
- try {
- Monitor.Enter(m_queueLock);
-
- while (m_queueFirst == 0 && !m_terminated) {
- Monitor.Wait(m_queueLock);
- }
-
- if (m_terminated) {
- break;
- }
-
- m_calculating = m_queueFirst;
- // NON-STOCK CODE START
- m_queueFirst = CustomPathManager._instance.queueItems[m_calculating].nextPathUnitId;
- // NON-STOCK CODE END
- // QueueFirst = PathUnits.m_buffer[Calculating].m_nextPathUnit; // stock code commented
-
- if (m_queueFirst == 0) {
- m_queueLast = 0u;
- m_queuedPathFindCount = 0;
- } else {
- m_queuedPathFindCount--;
- }
-
- // NON-STOCK CODE START
- CustomPathManager._instance.queueItems[m_calculating].nextPathUnitId = 0u;
- // NON-STOCK CODE END
- // PathUnits.m_buffer[Calculating].m_nextPathUnit = 0u; // stock code commented
-
- m_pathUnits.m_buffer[m_calculating].m_pathFindFlags = (byte)((m_pathUnits.m_buffer[m_calculating].m_pathFindFlags & ~PathUnit.FLAG_CREATED) | PathUnit.FLAG_CALCULATING);
-
- // NON-STOCK CODE START
- m_queueItem = CustomPathManager._instance.queueItems[m_calculating];
- // NON-STOCK CODE END
- } finally {
- Monitor.Exit(m_queueLock);
- }
-
- try {
- m_pathfindProfiler.BeginStep();
- try {
- PathFindImplementation(m_calculating, ref m_pathUnits.m_buffer[m_calculating]);
- } finally {
- m_pathfindProfiler.EndStep();
- }
- } catch (Exception ex) {
- UIView.ForwardException(ex);
- CODebugBase.Error(LogChannel.Core, "Path find error: " + ex.Message + "\n" + ex.StackTrace);
- m_pathUnits.m_buffer[m_calculating].m_pathFindFlags |= PathUnit.FLAG_FAILED;
- // NON-STOCK CODE START
-#if DEBUG
- ++m_failedPathFinds;
-#endif
- // NON-STOCK CODE END
- }
-
- try {
- Monitor.Enter(m_queueLock);
-
- m_pathUnits.m_buffer[m_calculating].m_pathFindFlags = (byte)(m_pathUnits.m_buffer[m_calculating].m_pathFindFlags & ~PathUnit.FLAG_CALCULATING);
-
- // NON-STOCK CODE START
- try {
- Monitor.Enter(m_bufferLock);
- CustomPathManager._instance.queueItems[m_calculating].queued = false;
- CustomPathManager._instance.ReleasePath(m_calculating);
- } finally {
- Monitor.Exit(m_bufferLock);
- }
- // NON-STOCK CODE END
- // Singleton.instance.ReleasePath(Calculating); // stock code commented
-
- m_calculating = 0u;
- Monitor.Pulse(m_queueLock);
- } finally {
- Monitor.Exit(m_queueLock);
- }
- }
- }
- }
-}
+ private void CalculateAdvancedAiCostFactors(
+#if DEBUG
+ bool debug, uint unit,
+#endif
+ ref BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, ushort nextNodeId, ref NetNode nextNode, ref float segmentSelectionCost, ref float laneSelectionCost, ref float laneChangingCost
+ ) {
+#if DEBUG
+ if (debug) {
+ Debug(unit, item, $"CalculateAdvancedAiCostFactors called.\n" +
+ "\t" + $"nextNodeId={nextNodeId}\n" +
+ "\t" + $"segmentSelectionCost={segmentSelectionCost}\n" +
+ "\t" + $"laneSelectionCost={laneSelectionCost}\n" +
+ "\t" + $"laneChangingCost={laneChangingCost}"
+ );
+ }
+#endif
+
+ NetInfo prevSegmentInfo = prevSegment.Info;
+ bool nextIsJunction = (nextNode.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction;
+
+ if (nextIsJunction) {
+ /*
+ * =======================================================================================================
+ * Calculate costs for randomized lane selection behind junctions and highway transitions
+ * =======================================================================================================
+ */
+ // TODO check if highway transitions are actually covered by this code
+ if (
+ !m_isHeavyVehicle &&
+ m_conf.AdvancedVehicleAI.LaneRandomizationJunctionSel > 0 &&
+ m_pathRandomizer.Int32(m_conf.AdvancedVehicleAI.LaneRandomizationJunctionSel) == 0 &&
+ m_pathRandomizer.Int32((uint)prevSegmentInfo.m_lanes.Length) == 0
+ ) {
+ // randomized lane selection at junctions
+ laneSelectionCost *= 1f + m_conf.AdvancedVehicleAI.LaneRandomizationCostFactor;
+
+#if DEBUG
+ if (debug) {
+ Debug(unit, item, $"CalculateAdvancedAiCostFactors: Calculated randomized lane selection costs\n" +
+ "\t" + $"laneSelectionCost={laneSelectionCost}"
+ );
+ }
+#endif
+ }
+
+ /*
+ * =======================================================================================================
+ * Calculate junction costs
+ * =======================================================================================================
+ */
+ // TODO if (prevSegmentRouting.highway) ?
+ segmentSelectionCost *= 1f + m_conf.AdvancedVehicleAI.JunctionBaseCost;
+
+#if DEBUG
+ if (debug) {
+ Debug(unit, item, $"CalculateAdvancedAiCostFactors: Calculated junction costs\n" +
+ "\t" + $"segmentSelectionCost={segmentSelectionCost}"
+ );
+ }
+#endif
+ }
+
+ bool nextIsStartNode = prevSegment.m_startNode == nextNodeId;
+ bool nextIsEndNode = nextNodeId == prevSegment.m_endNode;
+ if (nextIsStartNode || nextIsEndNode) { // next node is a regular node
+ /*
+ * =======================================================================================================
+ * Calculate traffic measurement costs for segment selection
+ * =======================================================================================================
+ */
+ NetInfo.Direction prevFinalDir = nextIsStartNode ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;
+ prevFinalDir = ((prevSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? prevFinalDir : NetInfo.InvertDirection(prevFinalDir);
+
+ float segmentTraffic = Mathf.Clamp(1f - (float)m_trafficMeasurementManager.SegmentDirTrafficData[m_trafficMeasurementManager.GetDirIndex(item.m_position.m_segment, prevFinalDir)].meanSpeed / (float)TrafficMeasurementManager.REF_REL_SPEED + item.m_trafficRand, 0, 1f);
+
+ segmentSelectionCost *= 1f +
+ m_conf.AdvancedVehicleAI.TrafficCostFactor *
+ segmentTraffic;
+
+ if (
+ m_conf.AdvancedVehicleAI.LaneDensityRandInterval > 0 &&
+ nextIsJunction &&
+ (nextNode.m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)
+ ) {
+ item.m_trafficRand = 0.01f * ((float)m_pathRandomizer.Int32((uint)m_conf.AdvancedVehicleAI.LaneDensityRandInterval + 1u) - m_conf.AdvancedVehicleAI.LaneDensityRandInterval / 2f);
+ }
+
+ if (
+ m_conf.AdvancedVehicleAI.LaneChangingJunctionBaseCost > 0 &&
+ (Singleton.instance.m_nodes.m_buffer[nextIsStartNode ? prevSegment.m_endNode : prevSegment.m_startNode].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction // check previous node
+ ) {
+ /*
+ * =======================================================================================================
+ * Calculate lane changing base cost factor when in front of junctions
+ * =======================================================================================================
+ */
+ laneChangingCost *= m_conf.AdvancedVehicleAI.LaneChangingJunctionBaseCost;
+
+#if DEBUG
+ if (debug) {
+ Debug(unit, item, $"CalculateAdvancedAiCostFactors: Calculated in-front-of-junction lane changing costs\n" +
+ "\t" + $"laneChangingCost={laneChangingCost}"
+ );
+ }
+#endif
+ }
+
+ /*
+ * =======================================================================================================
+ * Calculate general lane changing base cost factor
+ * =======================================================================================================
+ */
+ if (
+ m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost > 0 &&
+ m_conf.AdvancedVehicleAI.LaneChangingBaseMaxCost > m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost
+ ) {
+ float rand = (float)m_pathRandomizer.Int32(101u) / 100f;
+ laneChangingCost *= m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost + rand * (m_conf.AdvancedVehicleAI.LaneChangingBaseMaxCost - m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost);
+
+#if DEBUG
+ if (debug) {
+ Debug(unit, item, $"CalculateAdvancedAiCostFactors: Calculated base lane changing costs\n" +
+ "\t" + $"laneChangingCost={laneChangingCost}"
+ );
+ }
+#endif
+ }
+ }
+
+#if DEBUG
+ if (debug) {
+ Debug(unit, item, $"CalculateAdvancedAiCostFactors: Calculated cost factors\n" +
+ "\t" + $"segmentSelectionCost={segmentSelectionCost}\n" +
+ "\t" + $"laneSelectionCost={laneSelectionCost}\n" +
+ "\t" + $"laneChangingCost={laneChangingCost}"
+ );
+ }
+#endif
+ }
+#endif
+
+ private void PathFindThread() {
+ while (true) {
+ try {
+ Monitor.Enter(m_queueLock);
+
+ while (m_queueFirst == 0 && !m_terminated) {
+ Monitor.Wait(m_queueLock);
+ }
+
+ if (m_terminated) {
+ break;
+ }
+
+ m_calculating = m_queueFirst;
+ // NON-STOCK CODE START
+ m_queueFirst = CustomPathManager._instance.queueItems[m_calculating].nextPathUnitId;
+ // NON-STOCK CODE END
+ // QueueFirst = PathUnits.m_buffer[Calculating].m_nextPathUnit; // stock code commented
+
+ if (m_queueFirst == 0) {
+ m_queueLast = 0u;
+ m_queuedPathFindCount = 0;
+ } else {
+ m_queuedPathFindCount--;
+ }
+
+ // NON-STOCK CODE START
+ CustomPathManager._instance.queueItems[m_calculating].nextPathUnitId = 0u;
+ // NON-STOCK CODE END
+ // PathUnits.m_buffer[Calculating].m_nextPathUnit = 0u; // stock code commented
+
+ m_pathUnits.m_buffer[m_calculating].m_pathFindFlags = (byte)((m_pathUnits.m_buffer[m_calculating].m_pathFindFlags & ~PathUnit.FLAG_CREATED) | PathUnit.FLAG_CALCULATING);
+
+ // NON-STOCK CODE START
+ m_queueItem = CustomPathManager._instance.queueItems[m_calculating];
+ // NON-STOCK CODE END
+ } finally {
+ Monitor.Exit(m_queueLock);
+ }
+
+ try {
+ m_pathfindProfiler.BeginStep();
+ try {
+ PathFindImplementation(m_calculating, ref m_pathUnits.m_buffer[m_calculating]);
+ } finally {
+ m_pathfindProfiler.EndStep();
+ }
+ } catch (Exception ex) {
+ UIView.ForwardException(ex);
+ CODebugBase