[
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.sln.docstates\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbuild/\nbld/\n[Bb]in/\n[Oo]bj/\n\n# Roslyn cache directories\n*.ide/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n#NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n*.cachefile\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding addin-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# TODO: Comment the next line if you want to checkin your web deploy settings \n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# If using the old MSBuild-Integrated Package Restore, uncomment this:\n#!**/packages/repositories.config\n\n# Windows Azure Build Output\ncsx/\n*.build.csdef\n\n# Windows Store app package directory\nAppPackages/\n\n# Others\nsql/\n*.Cache\nClientBin/\n[Ss]tyle[Cc]op.*\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.pfx\n*.publishsettings\nnode_modules/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n/logo/\n\n# MSVS 2017 artifacts\n/.vs/slnx.sqlite\n/TLM/.vs/TMPE/*\n\n# Dependecies game dlls\n/TLM/dependencies"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"TLM/OptionsFramework\"]\n\tpath = TLM/OptionsFramework\n\turl = ../OptionsFramework.git\n[submodule \"TLM/CSUtil.CameraControl\"]\n\tpath = TLM/CSUtil.CameraControl\n\turl = ../CSUtil.CameraControl.git\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: csharp\nsolution: \"./TLM/TMPE.sln\"\nbranches:\n  only:\n    - master\n    - stable\nnotifications:\n  - email: false\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "﻿# Cities: Skylines - Traffic Manager: *President Edition* [![Discord](https://img.shields.io/discord/545065285862948894.svg)](https://discord.gg/faKUnST)\n\n# Changelog\n10.18, 29/03/2019\n- Bugfix: Parking AI: Cars do not spawn at outside connections (#245)\n- Bugfix: Trams perform turns on red (#248)\n- Update: Service Radius Adjuster mod by Egi removed from incompatible mods list (#255)\n\n10.17, 23/03/2019\n- Introduced new versioning scheme (10.17 instead of 1.10.17)\n- Synchronized code and version with stable version\n- Updated russian translation (thanks to @vitalii201 for translating) (#207)\n- Updated list of incompatible mods (#115)\n- Removed stable version from list of incompatible mods (#168)\n- Turn-on-red can now be toggled for unpreferred turns between one-ways\n- Improved train behavior at shunts: Trains now prefer to stay on their track (#230)\n- Fixed and optimized lane selection for u-turns and at dead ends (#101)\n- Parking AI: Improved public transport (PT) usage patterns, mixed car/PT paths are now possible  (#218)\n- Bugfix: Parking AI: Tourist cars despawn because they assume they are at an outside connection (#218)\n- Bugfix: Parking AI: Return path calculation did not accept beautification segments (#218)\n- Bugfix: Parking AI: Cars/Citizens waiting for a path might jump around (#218)\n- Bugfix: Vanilla lane randomization does not work as intended at highway transitions (#112)\n- Bugfix: Vehicles change lanes at tollbooths (#225)\n- Bugfix: Path-finding: Array index is out of range due to a race condition (#221)\n- Bugfix: Citizen not found errors when using walking tours (#219)\n- Bugfix: Timed light indicator only visible when any timed light node is selected (#222)\n\n1.10.16, 24/02/2019\n- Gameplay: Fixed problem with vehicle despawn after road upgrade/remove (thanks @pcfantasy for implementation suggestion)(#86, #101)\n- 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)\n- 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)\n- Gameplay: Added missing logic for noise density calculations (thanks to @pcfantasy for fix) (#66)\n- UI: New icons for empty and remove_priority_sign settings (thanks @aubergine10 for those icons) (#75, #77)\n- Other: Greatly improved incompatible mod scanner, added dialog to list and unsubscribe incompatible mods (#91)\n- Other: Changed mod name in Content Manager to __TM:PE__\n- Other: Discord server was set up by @FireController1847 - link in mod description\n- Other: Fixed 'silent error' inside log related with \"Esc key handler\" (#92)\n- Contribution: Added project building instructions and PR review\n\n1.10.15, 10/02/2019\n- Enhancement: Now you can use Escape key to close Traffic Manager without returning to Pause Menu (thanks to @aubergine10 for suggestion) (#16)\n- Gameplay: Updated pathfinding with missing vanilla logic\n- Gameplay: Tweaked values in CargoTruckAI path finding (thanks to @pcfantasy for improvement suggestion)\n- Gameplay: Tweaked speed multiplier of reckless drivers to get more realistic speed range (thanks to @aubergine10 for suggestion) (#23)\n- UI: New icons for cargo and passenger train restriction (thanks to @aubergine10) (#17)\n- Translations: Simplified Chinese translation updated (thanks to @Emphasia for translating)\n- Other: Added notification if user is still subscribed to old original TM:PE\n- [Experimental feature] Turn on red (thanks to @FireController1847 for implementation and to @pcfantasy for source code base)\n\n1.10.14, 27/01/2019\n- Bugfix: Added missing Car AI type (postVanAI) - now post vans and post trucks are assigned to service vehicles group\n- Bugfix: Vehicles doesn't stop when driving through toll booth - fixes toll booth income too\n- Bugfix: Cargo Airport doesn't work (Cargo planes not spawning and not arriving)\n- Updated Polish translation\n- Updated Korean translation (thanks to @Toothless FLY [ROK]LSh.st for translating)\n- Fixed Mod Options layout (text label overlaps slider control if too wide)\n\n1.10.13, 31/10/2018\n- Bugfix: Tollbooth fix\n\n1.10.12, 08/12/2018\n- Added the option to allow/disallow vehicles to enter a blocked junction at transition and pedestrian crossing nodes (#195)\n- Updated Russian translation (thanks to vitalii2011 for translating)\n- Bent nodes do not allow for u-turns by default (#170)\n- Bugfix: Emergency vehicles pass closed barriers at level crossings\n- Bugfix: Bus lines render u-turn where they should not (#207)\n- Bugfix: Parking AI: Cims leaving the city despawn their car at public transport stations (#214)\n- Bugfix: Crossing restrictions do not work at intersection between road and highway (#212)\n\n1.10.11, 21/07/2018\n- U-turn lane connections are represented by appropriate lane arrow (#201)\n- Bugfix: Heavy vehicles are unable to u-turn at dead ends (#194)\n- Bugfix: Routing & Priority rules do not work properly for acute (< 30°)/obtuse(> 150°) segment angles (#199)\n- Bugfix: Buses do not prefer lanes with correct lane arrow (#206)\n- Bugfix: Race condition in path-finding might cause paths to be assigned to wrong vehicle/citizen (#205)\n- Bugfix: Vehicles are unable to perform u-turns when setting off on multi-lane roads (#197)\n\n1.10.10, 14/07/2018\n- Parking AI: Improved park & ride behavior\n- Parking AI: Walking paths from parking position to destination building take public transportation into account\n- Bugfix: Parking AI causes unnecessary path-findings (#183, thanks to Sipke82 for reporting)\n- Bugfix: Prohibiting cims from crossing the road also affect paths where crossing is unnecessary (#168, thanks to aubergine10 for reporting)\n\n1.10.9, 13/07/2018\n- Updated for game version 1.10.1-f3\n- Re-implemented path-finding algorithm\n- Updated French translation (thanks to mjm92150 for translating!)\n\n1.10.8, 01/07/2018\n- Updated Korean translation (thanks to @Toothless FLY [ROK]LSh.st for translating)\n- Updated Polish translation (thanks to @Krzychu1245 for translating)\n- Added button to remove parked vehicles (in options dialog, see maintenance tab)\n- Parking AI: Removed check for distance between parked vehicle and target building\n- Bugfix: Parking AI: Cims spawn pocket cars when they originate from an outside connection\n- Bugfix: Incorrect speed limits returned for pedestrian lanes\n- Bugfix: Routing is not updated while the game is paused (thanks to @Oh My Lawwwd! for reporting)\n- Bugfix: Vanilla traffic lights are ignored when either the priority signs or timed traffic light features are disabled (thanks to @aubergine10 for reporting)\n- Bugfix: Park maintenance vehicles are not recognized as service vehicles\n- Bugfix: Cars leaving city state \"thinking of a good parking spot\" (thanks to @aubergine10 for reporting)\n\n1.10.7, 28/05/2018\n- Bugfix: U-turn routing is inconsistent on transport lines vs. bus paths (#137, thanks to @Zorgoth for reporting this issue)\n- Bugfix: Junction restrictions for pedestrian crossings are sometimes not preserved (#142, thanks to Anrew and @wizardrazer for reporting this issue)\n- Fixed: Geometry subscription feature may cause performance issues (#145)\n- 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)\n\n1.10.6, 24/05/2018\n- Updated for game version 1.10.0-f3\n- Accessibility: New option: Main menu size can be controlled\n- Accessibility: New option: GUI and overlay transparency can be controlled\n- New option: Penalties for switching between different public transport lines can be toggled\n- Cims can now be removed from the game\n- Improved window design\n- 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\n- Lane connector can be used on monorail tracks\n- Advanced Vehicle AI: Tuned parameters\n- Dynamic Lane Selection: Absolute speed measurements are used instead of relative measurements\n- Improved randomization for realistic speeds such that vehicles may change their target velocity over time\n- Improved vehicle position tracking\n- Improved mod compatibility checks\n- Parking AI: Improved behavior in situations where vehicles are parked near public transport hubs and road connections are partially unavailable\n- Bugfix: Parking AI: Not all possible paths are regarded during path-finding\n- 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)\n- Bugfix: Parking AI: Cims do not search for parking building when road-side parking spaces are found\n- Bugfix: Parking AI: Parked vehicles are spawned near the source building even when cims are already en route\n- Bugfix: Parking AI: Cims sometimes get stuck in an infinite loop while trying to enter their parked car\n- Bugfix: Lane connector does not work for roads with more than ten lanes\n- Bugfix: Allowing/Disallowing vehicles to enter a blocked junction does not work for certain junctions\n- Updated Korean translation (thanks to @Toothless FLY [ROK]LSh.st for translating)\n\n1.10.5, 06/01/2018\n- UI scaling removed\n- Simplified Chinese translation updated (thanks to Emphasia for translating)\n- Polish translation updated (thanks to @Krzychu1245 for translating)\n- Introduced randomization for lane changing costs\n- Introduced randomization for \"trucks prefer innermost lanes on highways\" costs\n- Removed unnecessary calculations in path-finding\n- Added path-finding costs for public transport transitions\n- Pedestrian traffic lights do not show up if crossing the street is prohibited\n- Busses are allowed to switch multiple lanes after leaving a bus stop\n- Bugfix: Main menu button might be out of view\n- Bugfix: Division by zero occurs for low speed roads\n- Bugfix: Automatic pedestrian lights at railroad do not work as expected\n- Bugfix: Timed traffic lights show up for bicycles (they should not)\n- Bugfix: Due to a multi-threading issue junction restrictions may cause the game state to become inconsistent\n- Bugfix: Routing rules prevents vehicles from spawning when starting building lies too close to an intersection/road end\n- Bugfix: Disabling tutorial message has no effect\n- Bugfix: \"Stay on lane\" feature does not work as intended for certain nodes\n\n1.10.4, 19/10/2017\n- Updated for game version 1.9.0-f5\n- Added possibility to add priority signs at multiple junctions at once (press Shift)\n- Added tutorials (can be disabled in the options window globally)\n\n1.10.3, 18/08/2017\n- Bugfix: Setting unlimited speed limit causes vehicles to crawl at low speed (thanks to @sethisuwan for reporting this issue)\n- Bugfix: Vehicle-separated traffic lights do not show up for trams & monorails (thanks to @thecitiesdork for reporting this issue)\n\n1.10.2, 17/08/2017\n- Updated for game version 1.8.0-f3\n- Improved performance\n- Bugfix: Pedestrians sometimes ignore red traffic light signals (thanks to @(c)RIKUPI™ for reporting this issue)\n- Bugfix: Timed traffic lights do not correctly recognize set vehicle restrictions (thanks to @alborzka for reporting this issue)\n\n1.10.1, 05/08/2017\n- Updated Polish, Korean, and Simplified Chinese translations\n- Bugfix: Default routing is disabled if the lane connector is used on a subset of all available lanes only\n- Bugfix: Parking AI cannot be enabled/disabled\n- Bugfix: Lane connection points can connected to themselves\n\n1.10.0, 30/07/2017\n- New feature: Dynamic Lane Selection\n- New feature: Adaptive step switching\n- New feature: Individual vehicles may be removed from the game\n- New option: Vehicle restrictions aggression\n- New option: Vehicles follow priority rules at junctions with timed traffic lights\n- Improved path-finding performance\n- Improved traffic measurement engine performance\n- Reorganized global configuration file (sorry, your main menu and main button positions are reset)\n- The option \"Road condition has a bigger impact on vehicle speed\" is only shown if the Snowfall DLC is owned\n- The flow/wait calculation mode to be used is now configurable via the global configuration file\n- Added path-find statistics label\n- Added confirmation dialog for \"Clear Traffic\" button\n- Currently active timed traffic light step is remembered\n- Trains do not wait for each other anymore near timed traffic lights\n- It is now possible to connect train station tracks and outside connections with the lane connector\n- Disabling the Parking AI triggers graceful clean up procedure\n- Relocated some options\n- Improved vehicle state tracking\n- Workaround for a base game issue that causes trams to get stuck\n- Trains do not longer stop in front of green timed traffic lights\n- Vehicles use queue skipping to prioritize path-finding runs that are caused by road modifications\n- Adding a vehicle separate light to a timed traffic lights applies the main light configuration\n- Parking AI: Vehicles can now find parking spaces at the opposite road side\n- Parking AI: Included an improved fallback logic for some edge cases\n- Parking AI: Citizens should now be more successful in returning their cars back home  \n- Parking AI: Tuned parking radius parameters\n- Parking AI: If the limit for parked vehicles is reached and parking fails due to it, no alternative parking space is queried\n- Vehicle AI: Busses prefer lanes with correct lane arrow over incorrect ones\n- Bugfix: Using the bulldozer tool might lead to inconsistent road geometry information\n- Bugfix: Citizens that fail to approach their parked car fly towards their target building\n- Bugfix: Parking AI: Path-finding fails if cars are parked too far away from a road\n- Bugfix: Parking AI: Citizens approaching a car start to float away\n- Bugfix: \"Heavy vehicles prefer outer lanes on highways\" does not work\n- Bugfix: The lane connector does not allow connecting all available lane end points at train stations and on bidirectional one-lane train tracks\n- Bugfix: Vehicles may get stuck in several situations\n- Upgrading to a road with bus lanes now copies an already existing traffic light state to the new traffic light\n\n1.9.6, 28/05/2017\n- Updated Simplified Chinese translation\n- Bugfix: Vehicles cannot perform u-turns at junctions with only one outgoing segment (thanks to @Sunbird for reporting this issue)\n- Bugfix: Path-finding costs for large distances exceed the maximum allowed value (thanks to @Huitsi for reporting this issue)\n- Bugfix: Under certain circumstances path-finding at railroad crossings allow switching from road to rail tracks.\n\n1.9.5, 24/05/2017\n- Updated for game version 1.7.1-f1\n- Updated Polish, Korean and Italian translation\n- Language can now be switched without requiring a game restart\n- 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)\n- 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)\n- Bugfix: In certain situations unnecessary vehicle-seperate traffic lights are being created\n- Bugfix: Upgrading a train track segment next to a timed traffic light causes trains to ignore the traffic light\n- Hotfix: Cable cars despawn at end-of-line stations\n\n1.9.4, 23/05/2017\n- New option: Ban private cars and trucks on bus lanes\n- Updated Spanish and French translation\n- Optimized path-finding\n- Increased path-finding cost for private cars driving on bus lanes\n- Increased path-finding cost for disregarding vehicle restrictions\n- Bugfix: Path-finding is unable to calculate certain paths after modifying the road network\n\n1.9.3, 22/05/2017\n- Disabled notification of route recalculating because some players report crashes\n- Removed default vehicle restrictions from bus lanes\n- Modified junction restrictions come into effect instantaneously\n- UI: Saving a timed step does not reset the timed traffic light to the first state\n- Bugfix: AI: Segment traffic data is not taken into account\n- Bugfix: Priority rules are not properly obeyed\n- Bugfix: Under certain circumstances priority signs cannot be removed\n- Bugfix: Path-finding is unable to calculate certain paths\n\n1.9.2, 20/05/2017\n- UI: Main menu & UI tools performance improved\n- Bugfix: Traffic lights can be removed from junctions that are controlled by a timed traffic light program\n\n1.9.1, 19/05/2017\n- Updated French, Dutch and Korean translation\n- Bugfix: Using the vanilla traffic light toggling feature crashes the game if TMPE's main menu has not been opened at least once\n- Bugfix: AI: More car traffic and less public transportation present than in vanilla\n\n1.9.0, 18/05/2017\n- Updated for game version 1.7.0-f5\n- New feature: Parking restrictions\n- New feature: Speed limits can be set up for individual lanes with the Control key\n- New feature: Added timed traffic light and speed limit support for monorails\n- New feature: Copy & paste for individual timed traffic lights\n- New feature: Rotate individual timed traffic lights\n- New feature: Lane customizations may come into effect instantaneously\n- Unified traffic light toggling feature with game code\n- Performance improvements\n- Reworked the way that traffic measurements are performed\n- Advanced Vehicle AI: Algorithm updated, performance improved - Possible routing decisions are now being precalculated\n- Path-finding cost multiplicator for vehicle restrictions is now configurable in TMPE_GlobalConfig.xml\n- UI: More compact, movable main menu UI\n- Added support for custom languages\n- Added Korean translation (thanks to @Toothless FLY [ROK]LSh.st for translating)\n- Updated translations: German, Polish, Russian, Portuguese, Traditional Chinese\n- Major code refactorings\n- AI: Tuned path-finding parameters\n- New option: Main button position can be locked\n- New option: Main menu position can be locked\n- New option: Added language selection in options dialog\n- New option: Customization of lane arrows, lane connections and vehicle restrictions can now come into effect instantaneously\n- Bugfix: Cars sometimes get stuck forever when the Advanced Parking AI is activated (thanks to @cmfcmf for reporting this issue)\n- Bugfix: Busses do not perform u-turns even if the transport line show u-turns (thanks to @dymanoid for reporting this issue)\n- Bugfix: Timed traffic lights do not work as expected on single-direction train tracks (thanks to @DaEgi01 for reporting this issue)\n- Bugfix: Vehicle restriction and speed limit signs overlay is displayed on the wrong side of inverted road segments\n- Bugfix: Influx statistics value is zero (thanks to @hjo for reporting this issue)\n\n1.8.16, 20/03/2017\n- Lane connections can now also be removed by pressing the backspace key\n- Improved lane selection for busses if the option \"Busses may ignore lane arrows\" is activated\n- Bugfix: The game sometimes freezes when using the timed traffic light tool\n- Bugfix: Lane connections are not correctly removed after modifying/removing a junction\n- Bugfix: Selecting a junction for setting up junction restrictions toggles the currently hovered junction restriction icon\n\n1.8.15, 27/01/2017\n- Updated for game version 1.6.3-f1\n\n1.8.14, 07/01/2017\n- Bugfix: Wait/flow ratio at timed traffic lights is sometimes not correctly calculated\n- Bugfix: A deadlock situation can arise at junctions with priority signs such that no vehicle enters the junction\n- Bugfix: When adding a junction to a timed traffic light, sometimes light states given by user input are not correctly stored\n- Bugfix: Joining two timed traffic lights sets the minimum time to \"1\" for steps with zero minimum time assigned\n- Bugfix: Modifications of timed traffic light states are sometimes not visible while editting the light (but they are applied nonetheless)\n- Bugfix: Button background is not always correctly changed after clicking on a button within the main menu\n- Tram lanes can now be customized by using the lane connector tool\n- Minor performance optimizations for priority sign simulation\n\n1.8.13, 05/01/2017\n- 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)\n- 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)\n- Bugfix: A division by zero exception can occur when calculating the average number of waiting/floating vehicles\n- Improved selection of overlay markers on underground roads (thanks to @Padi for reminding me of that issue)\n- Minor performance improvements\n\n1.8.12, 02/01/2017\n- Updated for game version 1.6.2-f1\n- Bugfix: After leaving the \"Manual traffic lights\" mode the traffic light simulation is not cleaned up correctly (thanks to @diezelunderwood for reporting this issue)\n- Bugfix: Insufficient access rights to log file causes the mod to crash\n\n1.8.11, 02/01/2017\n- 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)\n\n1.8.10, 31/12/2016\n- Improved path-finding performance (a bit)\n- Added a check for invalid road thumbnails in the \"custom default speed limits\" dialog\n\n1.8.9, 29/12/2016\n- It is now possible to set speed limits for metro tracks\n- Custom default speed limits may now be defined for train and metro tracks\n- Junction restrictions may now be controlled at bend road segments\n- Customizable junctions are now highlighted by the lane connector tool\n- Improved UI behavior\n- Performance improvements\n- Bugfix: Selecting a junction to set up priority signs sometimes does not work (thanks to @Artemis *Seven* for reporting this issue)\n- Bugfix: Automatic pedestrian lights do not work as expected at junctions with incoming one-ways and on left-hand traffic maps\n\n1.8.8, 25/12/2016\n- Bugfix: Taxis are not being used\n- Bugfix: Prohibiting u-turns with the junction restriction tool does not work (thanks to @Kisoe for reporting this issue)\n- Bugfix: Cars are sometimes floating across the map while trying to park (thanks to @[Delta ²k5] for reporting this issue)\n\n1.8.7, 24/12/2016\n- 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\n- Bugfix: Parking AI: Cims owning a parked car do not consider using other means of transportation\n- Bugfix: Parking AI: Residents are unable to leave the city through a highway outside connection\n- Bugfix: Trains/Trams are sometimes not detected at timed traffic lights\n- Advanced AI: Improved lane selection\n- The position of the main menu button is now forced inside screen bounds on startup\n- Improved overall user interface performance\n- Improved overlay behavior\n- Improved traffic measurement\n- Auto pedestrian lights at timed traffic lights behave more intelligently now\n- A timed traffic light step with zero minimum time assigned can now be skipped automatically\n- Using the lane connector to create a u-turn now automatically enables the \"u-turn allowed\" junction restriction\n- Updated French translation (thanks to @simon.royer007 for translating)\n- Added Italian translation (thanks to @Admix for translating)  \n\n1.8.6, 12/12/2016\n- Added Korean language (thanks to @Toothless FLY [ROK]LSh.st for translating)\n- Updated Chinese language code (zh-cn -> zh) in order to make it compatible with the game (thanks to @Lost丶青柠 for reporting this issue)\n\n1.8.5, 11/12/2016\n- Updated to game version 1.6.1-f2\n- Removed option \"Evacuation busses may only be used to reach a shelter\" (CO fixed this issue)\n- 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)\n\n1.8.4, 11/12/2016\n- 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.\n- U-turns are now only allowed to be performed from the innermost lane     \n- 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.\n- Bugfix: Bicycles cannot change from bicycle lanes to pedestrian lanes\n- Bugfix: Travel probabilities set in the \"Citizen Lifecycle Rebalance v2.1\" mod are not obeyed (thanks to @informmanuel, @shaundoddmusic for reporting this issue)\n- Bugfix: Number of tourists seems to drop when activating the mod (statistics were not updated, thanks to @hpp7117, @wjrohn for reporting this issue)\n- Bugfix: When loading a second savegame a second main menu button is displayed (thanks to @Cpt. Whitepaw for reporting this issue)\n- Bugfix: While path-finding is in progress vehicles do \"bungee-jumping\" on the current segment (thanks to @mxolsenx, @Howzitworld for reporting this issue)\n- Bugfix: Cims leaving the city search for parking spaces near the outside connection which is obviously not required   \n\n1.8.3, 4/12/2016\n- Bugfix: Despite having the Parking AI activated, cims sometimes still spawn pocket cars.\n- Bugfix: When the Parking AI is active, bicycle lanes are not used (thanks to @informmanuel for reporting this issue)\n- Tweaked u-turn behavior\n- Improved info views\n\n1.8.2, 3/12/2016\n- Bugfix: Taxis were not used (thanks to @[Delta ²k5] for reporting)\n- Bugfix: Minor UI fix in Default speed limits dialog\n\n1.8.1, 1/12/2016\n- Updated translations: Polish, Chinese (simplified)\n- Bugfix: Mod crashed when loading a second savegame\n\n1.8.0, 29/11/2016\n- Updated to game version 1.6.0-f4\n- New feature: Default speed limits\n- New feature: Parking AI (replaces \"Prohibit cims from spawning pocket cars\")\n- New option: Heavy vehicles prefer outer lanes on highways\n- New option: Realistic speeds\n- New option: Evacuation busses may ignore traffic rules (Natural Disasters DLC required)\n- New option: Evacuation busses may only be used to reach a shelter (Natural Disasters DLC required)\n- AI: Improved lane selection, especially on busy roads\n- AI: Improved mean lane speed measurement\n- Traffic info view shows parking space demand if Parking AI is activated\n- Public transport info view shows transport demand if Parking AI is activated\n- Added info texts for citizen and vehicle tool tips if Parking AI is activated\n- Extracted internal configuration to XML configuration file\n- Changed main menu button due to changes in the game's user interface\n- Main menu button is now moveable\n- 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)\n- Updated translations: German, Portuguese, Russian, Dutch, Chinese (traditional)\n\n1.7.15, 26/10/2016\n- 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)\n\n1.7.14, 18/10/2016\n- Updated for game version 1.5.2-f3\n\n1.7.13, 15/09/2016\n- Implemented a permanent fix to solve problems with stuck vehicles/cims caused by third party mods\n- Added a button to reset stuck vehicles/cims (see mod settings menu)\n- AI: Improved lane selection algorithm\n- Bugfix: AI: Lane merging was not working as expected\n- Bugfix: Pedestrian light states were sometimes not being stored correctly (thanks to Filip for pointing out this problem)\n\n1.7.12, 09/09/2016\n- AI: Lane changes are reduced on congested road segments\n- Timed traffic lights should now correctly detect trains and trams\n- Bugfix: GUI: Junction restriction icons sometimes disappear\n- Updated Chinese (simplified) translation\n\n1.7.11, 01/09/2016\n- Updated to game version 1.5.1-f3\n\n1.7.10, 31/08/2016\n- Players can now disable spawning of pocket cars\n- Updated Chinese (simplified) translation\n- Bugfix: Timed traffic lights were flickering\n- Bugfix: Pedestrian traffic lights were not working as expected\n- Bugfix: When upgrading/removing/adding a road segment, nearby junction restrictions were removed\n- Bugfix: Setting up vehicle restrictions affects trams (thanks to @chem for reporting)\n- Bugfix: Manual pedestrian traffic light states were not correctly handled\n- Bugfix: Junction restrictions overlay did not show all restricted junctions\n\n1.7.9, 22/08/2016\n- In-game traffic light states are now correctly rendered when showing \"yellow\"\n- Removed negative effects on public transport usage\n- GUI: Traffic light states do not flicker anymore\n- Performance improvements\n\n1.7.8, 18/08/2016:\n- Bugfix: Cims sometimes got stuck (thanks to all reports and especially to @Thilawyn for providing a savegame)\n- GUI: Improved traffic light arrow display\n- Improved performance while saving\n\n1.7.7, 16/08/2016:\n- AI: Instead of walking long distances, citizens now use a car\n- AI: Citizens will remember their last used mode of transport (e.g. they will not drive to work and come return by bus anymore)\n- AI: Increased path-finding costs for traversing over restricted road segments\n- Added \"110\" speed limit\n- GUI: Windows are draggable\n- GUI: Improved window scaling on lower resolutions\n- Improved performance while saving\n\n1.7.6, 14/08/2016:\n- New feature: Players may now prohibit cims from crossing the street\n- AI: Tuned randomization of lane changing behavior\n- AI: Introduced path-finding costs for leaving main highway (should reduce amount of detours taken)\n- UI: Clicking with the secondary mouse button now deselects the currently selected node/segment for all tools\n- Added the possibility to connect train track lanes with the lane connector (as requested by @pilot.patrick93)\n- Moved options from \"Change lane arrows\" to \"Vehicle restrictions\" tool\n- Updated Russian translation\n- Bugfix: AI: At specific junctions, vehicles were not obeying lane connections correctly (thanks to @Mariobro14 for pointing out this problem)\n- Bugfix: AI: Path-finding costs for u-turns were not correctly calculated (thanks to @Mariobro14 for pointing out this problem)\n- Bugfix: Vehicles were endlessly waiting for each other at junctions with certain priority sign configurations\n- Bugfix: AI: Lane changing costs corrected\n\n1.7.5, 07/08/2016:\n- Bugfix: AI: Cims were using pocket cars whenever possible\n- Bugfix: AI: Path-finding failures led to much less vehicles spawning\n- Bugfix: AI: Lane selection at junctions with custom lane connection was not always working properly (e.g. for Network Extensions roads with middle lane)\n- Bugfix: While editing a timed traffic light it could happen that the traffic light was deleted\n\n1.7.4, 31/07/2016:\n- AI: Switched from relative to absolute traffic density measurement\n- AI: Tuned new parameters\n- Bugfix: Activated/Disabled features were not loaded correctly\n- Bugfix: AI: At specific junctions the lane changer did not work as intended\n- Possible fix for OSX performance issues\n- Code improvements\n- Added French translations (thanks to @simon.royer007 for translating!)\n\n1.7.3, 29/07/2016:\n- Added the ability to enable/disable mod features (e.g. for performance reasons)\n- Bugfix: Vehicle type determination was incorrect (fixed u-turning trams/trains, stuck vehicles)\n- Bugfix: Clicking on a train/tram node with the lane connector tool led to an uncorrectable error (thanks to @noaccount for reporting this problem)\n- Further code improvements\n\n1.7.2, 26/07/2016:\n- Optimized UI overlay performance\n\n1.7.1, 24/07/2016:\n- Reverted \"Busses now may only ignore lane arrows if driving on a bus lane\" for now\n- Bugfix: Trains were not despawning if no path could be calculated\n- Workaround for third-party issue: TM:PE now detects if the calculation of total vehicle length fails    \n\n1.7.0, 23/07/2016:\n- New feature: Traffic++ lane connector\n- Busses now may only ignore lane arrows if driving on a bus lane\n- Rewritten and simplified vehicle position tracking near timed traffic lights and priority signs for performance reasons\n- Improved performance of priority sign rules\n- AI: Cims now ignore junctions where pedestrian lights never change to green\n- AI: Removed the need to define a lane changing probability\n- AI: Tweaked lane changing parameters\n- AI: Highway rules are automatically disabled at complex junctions (= more than 1 incoming and more than 1 outgoing roads)\n- Improved UI performance if overlays are deactivated\n- Simulation accuracy now also controls time intervals between traffic measurements\n- Added compatibility detection for the Rainfall mod\n- Improved fault-tolerance of the load/save system\n- Default wait-flow balance is set to 0.8\n- Bugfix: Taxis were allowed to ignore lane arrows\n- Bugfix: AI: Highway rules on left-hand traffic maps did not work the same as on right-hand traffic maps\n- 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)\n\n1.6.22, 29/06/2016:\n- AI: Taxis now may not ignore lane arrows and are using bus lanes whenever possible (thanks to @Cochy for pointing out this issue)\n- AI: Busses may only ignore lane arrows while driving on a bus lane\n- Bugfix: Traffic measurement at timed traffic lights was incorrect\n\n1.6.22, 21/06/2016:\n- Speed/vehicle restrictions may now be applied to all road segments between two junctions by holding the shift key\n- Reworked how changes in the road network are recognized\n- Advanced Vehicle AI: Improved lane selection at junctions where bus lanes end\n- Advanced Vehicle AI: Improved lane selection of busses\n- Improved automatic pedestrian lights\n- Improved separate traffic lights: Traffic lights now control traffic lane-wise\n- UI: Sensitivity slider is only available while adding/editing a step or while in test mode\n- Bugfix: Lane selection on maps with left-hand traffic was incorrect\n- Bugfix: While building in pause mode, changes in the road network were not always recognized causing vehicles to stop/despawn\n- Bugfix: Police cars off-duty were ignoring lane arrows\n- 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)\n- Bugfix: Trains/Trams were sometimes ignoring timed traffic lights (many thanks to Filip for identifying this problem)\n- Bugfix: Building roads with bus lanes caused garbage, bodies, etc. to pile up\n\n1.6.21, 14/06/2016:\n- Bugfix: Too few cargo trains were spawning (thanks to @Scratch, @toruk_makto1, @Mr.Miyagi, @mottoh and @Syparo for pointing out this problem)       \n- Bugfix: Vehicle restrictions did not work as expected (thanks to @nordlaser for pointing out this problem)\n\n1.6.20, 11/06/2016:\n- Bugfix: Priority signs were not working correctly (thanks to @mottoth, @Madgemade for pointing out this problem)\n\n1.6.19, 11/06/2016\n- Bugfix: Timed traffic lights UI not working as expected (thanks to @Madgemade for pointing out this problem)\n\n1.6.18, 09/06/2016\n- Updated for game patch 1.5.0-f4\n- Improved performance of priority signs and timed traffic lights\n- Players can now select elevated rail segments/nodes\n- Trams and trains now follow priority signs\n- Improved UI behavior when setting up priority signs\n\n1.6.17, 20/04/2016\n- Hotfix for reported path-finding problems\n\n1.6.16, 19/04/2016\n- Updated for game patch 1.4.1-f2\n\n1.6.15, 22/03/2016\n- Updated for game path 1.4.0-f3\n- Possible fix for crashes described by @cosminel1982\n- Added traditional Chinese translation\n\n1.6.14, 17/03/2016\n- Bugfix: Cargo trucks did not obey vehicle restrictions (thanks to @ad.vissers for pointing out this problem)\n- Bugfix: When Advanced AI was deactivated, u-turns did not have costs assigned\n\n1.6.13, 16/03/2016\n- Added Dutch translation\n- The pedestrian light mode of a traffic light can now be switched back to automatic\n- Vehicles approaching a different speed limit change their speed more gradually\n- The size of signs and symbols in the overlay is determined by screen resolution height, not by width\n- Path-finding: Performance improvements\n- Path-finding: Fine-tuned lane changing behaviour\n- Bugfix: After loading another savegame, timed traffic lights stopped working for a certain time\n- Bugfix: Lane speed calculation corrected\n\n1.6.12, 03/03/2016\n- Improved memory usage\n- Bugfix: Adding/removing junctions to/from existing timed traffic lights did not work (thanks to @nieksen for pointing out this problem)\n- Bugfix: Separate timed traffic lights were sometimes not saved (thanks to @nieksen for pointing out this problem)\n- Bugfix: Fixed an initialization error (thanks to @GordonDry for pointing out this problem)\n\n1.6.11, 03/03/2016\n- Added Chinese translation\n- By pressing \"Page up\"/\"Page down\" you can now switch between traffic and default map view\n- Size of information icons and signs is now based on your screen resolution\n- UI code refactored\n\n1.6.10, 02/03/2016\n- Additional controls for vehicle restrictions added\n- Bugfix: Clicking on a Traffic Manager overlay resulted in vanilla game components (e.g. houses, vehicles) being activated\n\n1.6.9, 02/03/2016\n- Updated for game patch 1.3.2-f1\n\n1.6.8, 01/03/2016\n- Path-finding: Major performance improvements\n- Updated Japanese translation (thanks to @Akira Ishizaki for translating!)\n- Added Spanish translation\n\n1.6.7, 27/02/2016\n- Tuned AI parameters\n- Improved traffic density measurements\n- Improved lane changing near junctions: Reintroduced costs for lane changing before junctions\n- Improved vehicle behavior near blocked roads (e.g. while a building is burning)\n- Bugfix: Automatic pedestrian lights for outgoing one-ways fixed\n- Bugfix: U-turns did not have appropriate costs assigned\n- Bugfix: The time span between AI traffic measurements was too high\n\n1.6.6, 27/02/2016\n- It should now be easier to select segment ends in order to change lane arrows.\n- Priority signs now cannot be setup at outgoing one-ways.\n- Updated French translation (thanks to @simon.royer007 for translating!)\n- Updated Polish translation (thanks to @Krzychu1245 for translating!)\n- Updated Portuguese translation (thanks to @igordeeoliveira for translating!)\n- Updated Russian translation (thanks to @FireGames for translating!)\n- Bugfix: U-turning vehicles were not obeying the correct directional traffic light (thanks to @t1a2l for pointing out this problem)\n\n1.6.5, 24/02/2016\n- Added despawning setting to options dialog\n- Improved detection of Traffic++ V2\n\n1.6.4, 23/02/2016\n- Minor performance improvements\n- Bugfix: Path-finding calculated erroneous traffic density values\n- Bugfix: Cims left the bus just to hop on a bus of the same line again (thanks to @kamzik911 for pointing out this problem)\n- Bugfix: Despawn control did not work (thanks to @xXHistoricalxDemoXx for pointing out this problem)\n- Bugfix: State of new settings was not displayed corretly (thanks to @Lord_Assaultーさま for pointing out this problem)\n- Bugfix: Default settings for vehicle restrictions on bus lanes corrected\n- Bugfix: Pedestrian lights at railway junctions fixed (they are still invisible but are derived from the car traffic light state automatically)\n\n1.6.3, 22/02/2016\n- Bugfix: Using the \"Old Town\" policy led to vehicles not spawning.\n- Bugfix: Planes, cargo trains and ship were sometimes not arriving\n- Bugfix: Trams are not doing u-turns anymore\n\n1.6.2, 20/02/2016\n- Trams are now obeying speed limits (thanks to @Clausewitz for pointing out the issue)\n- Bugfix: Clear traffic sometimes throwed an error\n- Bugfix: Vehicle restrctions did not work as expected (thanks to @[Delta ²k5] for pointing out this problem)\n- Bugfix: Transition of automatic pedestrian lights fixed\n\n1.6.1, 20/02/2016\n- Improved performance\n- Bugfix: Fixed UI issues\n- Modifying mod options through the main menu now gives an annoying warning message instead of a blank page.\n\n1.6.0, 18/02/2016\n- New feature: Separate traffic lights for different vehicle types\n- New feature: Vehicle restrictions\n- Snowfall compatibility\n- Better handling of vehicle bans\n- Improved the method for calculating lane traffic densities\n- Ambulances, fire trucks and police cars on duty are now ignoring lane arrows\n- Timed traffic lights may now be setup at arbitrary nodes on railway tracks\n- Reckless drivers now do not enter railroad crossings if the barrier is down\n- Option dialog is disabled if accessed through the main menu\n- Performance optimizations\n- Advanced Vehicle AI: Improved lane spreading\n- The option \"Vehicles may enter blocked junctions\" may now be defined for each junction separately\n- Vehicles going straight may now change lanes at junctions\n- Vehicles may now perform u-turns at junctions that have an appropriate lane arrow configuration\n- Road conditions (snow, maintenance state) may now have a higher impact on vehicle speed (see \"Options\" menu)\n- Emergency vehicles on duty now always aim for the the fastest route\n- Bugfix: Path-finding costs for crossing a junction fixed\n- Bugfix: Vehicle detection at timed traffic lights did not work as expected\n- Bugfix: Not all valid traffic light arrow modes were reachable\n\n1.5.2, 01/02/2016\n- Traffic lights may now be added to/removed from underground junctions\n- 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)\n- Display of priority signs, speed limits and timed traffic lights may now be toggled via the options dialog\n- Bugfix: Reckless driving does not apply for trains (thanks to @GordonDry for pointing out this problem)\n- Bugfix: Manual traffic lights were not working (thanks to @Mas71 for pointing out this problem)\n- Bugfix: Pedestrians were ignoring timed traffic lights (thanks to @Hannes8910 for pointing out this problem)\n- Bugfix: Sometimes speed limits were not saved (thanks to @cca_mikeman for pointing out this problem)\n\n1.5.1, 31/01/2016\n- Trains are now following speed limits\n\n1.5.0, 30/01/2016\n- New feature: Speed restrictions (as requested by @Gfurst)\n- AI: Parameters tuned\n- Code improvements\n- Lane arrow changer window is now positioned near the edited junction (as requested by @GordonDry)\n- Bugfix: Flowing/Waiting vehicles count corrected\n\n1.4.9, 27/01/2016\n- Junctions may now be added to/removed from timed traffic lights after they are created\n- When viewing/moving a timed step, the displayed/moved step is now highlighted (thanks to Joe for this idea)\n- Performance improvements\n- Bugfix (AI): Fixed a division by zero error (thanks to @GordonDry for pointing out this problem)\n- Bugfix (AI): Near highway exits vehicles tended to use the outermost lane (thanks to @Zake for pointing out this problem)\n- Bugfix: Some lane arrows disappeared on maps using left-hand traffic systems (thanks to @Mas71 for pointing out this problem)\n- Bugfix: In lane arrow edit mode, the order of arrows was sometimes incorrect (thanks to @Glowstrontium for pointing out this problem)\n- Bugfix: Lane merging in left-hand traffic systems fixed\n- Bugfix: Turning priority roads fixed (thanks to @GordonDry for pointing out this problem)\n\n1.4.8, 25/01/2016\n- AI: Parameters have been tuned\n- AI: Added traffic density measurements\n- Performance improvements\n- Added translation to Polish (thanks to @Krzychu1245 for working on this!)\n- Added translation to Russian (thanks to @FireGames for working on this!)\n- Bugfix: After removing a timed or manual light the traffic light was deleted (thanks to @Mas71 for pointing out this problem)\n- Bugfix: Segment geometries were not always calculated\n- Bugfix: In highway rule mode, lane arrows sometimes flickered\n- Bugfix: Some traffic light arrows were sometimes not selectable\n\n1.4.7, 22/01/2016\n- Added translation to Portuguese (thanks to @igordeeoliveira for working on this!)\n- Reduced mean size of files can become quite big (thanks to @GordonDry for reporting this problem)\n- Bugfix: Freight ships/trains were not coming in (thanks to @Mas71 and @clus for reporting this problem)\n- Bugfix: The toggle \"Vehicles may enter blocked junctions\" did not work properly (thanks for @exxonic for reporting this problem)\n- Bugfix: If a timed traffic light is being edited the segment geometry information is not updated (thanks to @GordonDry for reporting this problem)\n\n1.4.6, 22/01/2016\n- Running average lane speeds are measured now\n- Minor fixes\n\n1.4.5, 22/01/2016\n- The option \"Vehicles may enter blocked junctions\" may now be defined for each junction separately\n- Bugfix: A deadlock in the path-finding is fixed\n- Bugfix: Small timed light sensitivity values (< 0.1) were not saved correctly\n- Bugfix: Timed traffic lights were not working for some players\n- Refactored segment geometry calculation\n\n1.4.4, 21/01/2016\n- Added localization support\n\n1.4.3, 20/01/2016\n- Several performance improvements\n- Improved calculation of segment geometries\n- Improved load balancing\n- Police cars, ambulances, fire trucks and hearses are now also controlled by the AI\n- Bugfix: Vehicles did not always take the shortest path\n- Bugfix: Vehicles disappeared after deleting/upgrading a road segment\n- Bugfix: Fixed an error in path-finding cost calculation\n- Bugfix: Outgoing roads were treated as ingoing roads when highway rules were activated\n\n1.4.2, 16/01/2016\n- Several major performance improvements (thanks to @sci302 for pointing out those issues)\n- Improved the way traffic lights are saved/loaded\n- Lane-wise traffic density is only measured if Advanced AI is activated\n- Bugfix: AI did not consider speed limits/road types during path calculation (thanks to @bhanhart, @sa62039 for pointing out this problem)\n- 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.  \n- Bugfix: Vehicles were stopping in front of green traffic lights\n- Bugfix: Stop/Yield signs were not working properly (thanks to @GordonDry, @Glowstrontium for pointing out this problem)\n- Bugfix: Cargo trucks were ignoring the \"Heavy ban\" policy, they should do now (thanks to @Scratch for pointing out this problem)\n\n1.4.1, 15/01/2016\n- Bugfix: Path-finding near junctions fixed\n\n1.4.0, 15/01/2016\n- Introducing Advanced Vehicle AI (disabled by default! Go to \"Options\" and enable it if you want to use it.)\n- Bugfix: Traffic lights were popping up in the middle of roads\n- Bugfix: Fixed the lane changer for left-hand traffic systems (thanks to @Phishie for pointing out this problem)\n- Bugfix: Traffic lights on invalid nodes are not saved anymore\n\n1.3.24, 13/01/2016\n- Improved handling of priority signs\n- Priority signs: After adding two main road signs the next offered sign is a yield sign\n- Priority signs: Vehicles now should notice earlier that they can enter a junction\n- Removed the legacy XML file save system\n- Invalid (not created) lanes are not saved/loaded anymore\n- Added a configuration option that allows vehicles to enter blocked junctions\n- Bugfix: Some priority signs were not saved\n- Bugfix: Priority signs on deleted segments are now deleted too\n- Bugfix: Lane arrows on removed lanes are now removed too\n- 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)\n- Bugfix: If reckless driving was set to \"The Holy City (0 %)\", vehicles blocked intersections with traffic light.\n- Bugfix: Traffic light arrow modes were sometimes not correctly saved  \n\n1.3.23, 09/01/2016\n- 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)\n- Option added to forget all toggled traffic lights\n\n1.3.22, 08/01/2016\n- Added an option allowing busses to ignore lane arrows\n- Added an option to display nodes and segments\n\n1.3.21, 06/01/2016\n- New feature: Traffic Sensitivity Tuning\n- UI improvements: When adding a new step to a timed traffic light the lights are inverted.\n- Timed traffic light status symbols should now be less annoying\n- Bugfix: Deletion of junctions that were members of a traffic light group is now handled correctly\n\n1.3.20, 04/01/2016\n- Bugfix: Timed traffic lights are not saved correctly after upgrading a road nearby\n- UI improvements\n- New feature: Reckless driving\n\n1.3.19, 04/01/2016\n- Timed traffic lights: Absolute minimum time changed to 1\n- Timed traffic lights: Velocity of vehicles is being measured to detect traffic jams\n- Improved traffic flow measurement\n- Improved path finding: Cims may now choose their lanes more independently\n- Bugfix: Upgrading a road resets the traffic light arrow mode\n\n1.3.18, 03/01/2016\n- Provided a fix for unconnected junctions caused by other mods\n- Crosswalk feature removed. If you need to add/remove crosswalks please use the \"Crossings\" mod.\n- UI improvements: You can now switch between activated timed traffic lights without clicking on the menu button again\n\n1.3.17, 03/01/2016\n- 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)\n- Bugfix: After using the \"Manual traffic lights\" option, toggling lights does not work (thanks to @Timso113 for pointing out this problem)\n\n1.3.16, 03/01/2016\n- 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)\n- Improved save data management\n\n1.3.15, 02/01/2016\n- Simulation accuracy (and thus performance) is now controllable through the game options dialog\n- Bugfix: Vehicles on a priority road sometimes stop without an obvious reason\n\n1.3.14, 01/01/2016\n- Improved performance\n- UI: Non-timed traffic lights are now automatically removed when adding priority signs to a junction\n- Adjusted the adaptive traffic light decision formula (vehicle lengths are considered now)\n- Traffic two road segments in front of a timed traffic light is being measured now  \n\n1.3.13, 01/01/2016\n- Bugfix: Lane arrows are not correctly translated into path finding decisions (thanks to @bvoice360 for pointing out this problem)\n- Bugfix: Priority signs are sometimes undeletable (thank to @Blackwolf for pointing out this problem)\n- Bugfix: Errors occur when other mods without namespace definitions are loaded (thanks to @Arch Angel for pointing out this problem)\n- Connecting a new road segment to a junction that already has priority signs now allows modification of the new priority sign\n\n1.3.12, 30/12/2015\n- Bugfix: Priority signs are not editable (thanks to @ningcaohan for pointing out this problem)\n\n1.3.11, 30/12/2015\n- Road segments next to a timed traffic light may now be deleted/upgraded/added without leading to deletion of the light\n- Priority signs and Timed traffic light state symbols are now visible as soon as the menu is opened\n\n1.3.10, 29/12/2015\n- Fixed an issue where timed traffic light groups were not deleted after deleting an adjacent segment\n\n1.3.9, 29/12/2015\n- Introduced information icons for timed traffic lights\n- Mod is now compatible with \"Improved AI\" (Lane changer is deactivated if \"Improved AI\" is active)\n\n1.3.8, 29/12/2015\n- Articulated busses are now simulated correctly (thanks to @nieksen for pointing out this problem)\n- UI improvements\n\n1.3.7, 28/12/2015\n- When setting up a new timed traffic light, yellow lights from the real-world state are not taken over\n- When loading another save game via the escape menu, Traffic Manager does not crash\n- When loading another save game via the escape menu, Traffic++ detection works as intended\n- Lane arrows are saved correctly\n\n1.3.6, 28/12/2015\n- Bugfix: wrong flow value taken when comparing flowing vehicles\n- Forced node rendering after modifying a crosswalk\n\n1.3.5, 28/12/2015\n- Fixed pedestrian traffic Lights (thanks to @Glowstrontium for pointing out this problem)\n- Better fix for: Deleting a segment with a timed traffic light does not cause a NullReferenceException\n- Adjusted the comparison between flowing (green light) and waiting (red light) traffic\n\n1.3.4, 27/12/2015\n- Better traffic jam handling\n\n1.3.3, 27/12/2015\n- (Temporary) hotfix: Deleting a segment with a timed traffic light does not cause a NullReferenceException\n- If priority signs are located behind the camera they are not rendered anymore\n\n1.3.2, 27/12/2015\n- Priority signs are persistently visible when Traffic Manager is in \"Add priority sign\" mode\n- Synchronized traffic light rendering: In-game Traffic lights display the correct color (Thanks to @Fabrice for pointing out this problem)\n- Traffic lights switch between green, yellow and red. Not only between green and red.\n- UI tool tips are more explanatory and are shown longer.\n\n1.3.1, 26/12/2015\n- Minimum time units may be zero now\n- Timed traffic lights of deleted/modified junctions get properly disposed\n\n1.3.0, 25/12/2015\n- **Adaptive Timed Traffic Lights** (automatically adjusted based on traffic amount)\n\n1.2.0 (iMarbot)\n- Updated for 1.2.2-f2 game patch.\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Svetlozar\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "﻿# Traffic Manager: *President Edition* [![Discord](https://img.shields.io/discord/545065285862948894.svg)](https://discord.gg/faKUnST) [![Build status](https://ci.appveyor.com/api/projects/status/dehkvuxk8b3h66e7/branch/master?svg=true)](https://ci.appveyor.com/project/krzychu124/cities-skylines-traffic-manager-president-edition/branch/master)\n\nA mod for **Cities: Skylines** that gives you more control over road and rail traffic in your city.\n\n[Steam Workshop](https://steamcommunity.com/sharedfiles/filedetails/?id=1637663252) • [Installation](https://github.com/krzychu124/Cities-Skylines-Traffic-Manager-President-Edition/wiki/Installation) • [User Guide](http://www.viathinksoft.de/tmpe/wiki) • [Issue Tracker](https://github.com/krzychu124/Cities-Skylines-Traffic-Manager-President-Edition/issues) • [Report a Bug](https://github.com/krzychu124/Cities-Skylines-Traffic-Manager-President-Edition/wiki/Report-a-Bug)\n\n# Features\n\n* Timed traffic lights\n* Change lane arrows\n* Edit lane connections\n* Add priority signs\n* Junction restrictions\n    * Toggle u-turns\n    * Allow \"turn on red\"\n    * Enter blocked junctions\n    * Toggle pedestrian crossings\n* Vehicle restrictions\n    * For roads and trains!\n* Customise speed limits\n* Toggle despawn\n* Clear traffic, stuck cims, etc.\n\n# Changelog\n### [10.18](https://github.com/krzychu124/Cities-Skylines-Traffic-Manager-President-Edition/compare/10.17...10.18), 29/03/2019\n- Bugfix: Parking AI: Cars do not spawn at outside connections (#245)\n- Bugfix: Trams perform turns on red (#248)\n- Update: Service Radius Adjuster mod by Egi removed from incompatible mods list (#255)\n\nSee [Full Changelog](https://github.com/krzychu124/Cities-Skylines-Traffic-Manager-President-Edition/blob/master/CHANGELOG.md) for details of earlier releases.\n\n# Contributing\n\nWe welcome contributions from the community!\n\nContact us:\n\n* [Issue tracker](https://github.com/krzychu124/Cities-Skylines-Traffic-Manager-President-Edition/issues)\n* [Discord (chat)](https://discord.gg/faKUnST)\n* [Steam Workshop](https://steamcommunity.com/sharedfiles/filedetails/?id=1637663252)\n\n# License\n\n[MIT License](https://github.com/krzychu124/Cities-Skylines-Traffic-Manager-President-Edition/blob/master/LICENSE) (open source)\n"
  },
  {
    "path": "TLM/.vs/config/applicationhost.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n\n    IIS configuration sections.\n\n    For schema documentation, see\n    %IIS_BIN%\\config\\schema\\IIS_schema.xml.\n    \n    Please make a backup of this file before making any changes to it.\n\n    NOTE: The following environment variables are available to be used\n          within this file and are understood by the IIS Express.\n\n          %IIS_USER_HOME% - The IIS Express home directory for the user\n          %IIS_SITES_HOME% - The default home directory for sites\n          %IIS_BIN% - The location of the IIS Express binaries\n          %SYSTEMDRIVE% - The drive letter of %IIS_BIN%\n\n-->\n\n<configuration>\n\n    <!--\n\n        The <configSections> section controls the registration of sections.\n        Section is the basic unit of deployment, locking, searching and\n        containment for configuration settings.\n        \n        Every section belongs to one section group.\n        A section group is a container of logically-related sections.\n        \n        Sections cannot be nested.\n        Section groups may be nested.\n        \n        <section\n            name=\"\"  [Required, Collection Key] [XML name of the section]\n            allowDefinition=\"Everywhere\" [MachineOnly|MachineToApplication|AppHostOnly|Everywhere] [Level where it can be set]\n            overrideModeDefault=\"Allow\"  [Allow|Deny] [Default delegation mode]\n            allowLocation=\"true\"  [true|false] [Allowed in location tags]\n        />\n        \n        The recommended way to unlock sections is by using a location tag:\n        <location path=\"Default Web Site\" overrideMode=\"Allow\">\n            <system.webServer>\n                <asp />\n            </system.webServer>\n        </location>\n\n    -->\n    <configSections>\n        <sectionGroup name=\"system.applicationHost\">\n            <section name=\"applicationPools\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\n            <section name=\"configHistory\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\n            <section name=\"customMetadata\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\n            <section name=\"listenerAdapters\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\n            <section name=\"log\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\n            <section name=\"preloadProviders\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\n            <section name=\"sites\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\n            <section name=\"webLimits\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\n        </sectionGroup>\n\n        <sectionGroup name=\"system.webServer\">\n            <section name=\"asp\" overrideModeDefault=\"Deny\" />\n            <section name=\"caching\" overrideModeDefault=\"Allow\" />\n            <section name=\"cgi\" overrideModeDefault=\"Deny\" />\n            <section name=\"defaultDocument\" overrideModeDefault=\"Allow\" />\n            <section name=\"directoryBrowse\" overrideModeDefault=\"Allow\" />\n            <section name=\"fastCgi\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\n            <section name=\"globalModules\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\n            <section name=\"handlers\" overrideModeDefault=\"Deny\" />\n            <section name=\"httpCompression\" overrideModeDefault=\"Allow\" />\n            <section name=\"httpErrors\" overrideModeDefault=\"Allow\" />\n            <section name=\"httpLogging\" overrideModeDefault=\"Deny\" />\n            <section name=\"httpProtocol\" overrideModeDefault=\"Allow\" />\n            <section name=\"httpRedirect\" overrideModeDefault=\"Allow\" />\n            <section name=\"httpTracing\" overrideModeDefault=\"Deny\" />\n            <section name=\"isapiFilters\" allowDefinition=\"MachineToApplication\" overrideModeDefault=\"Deny\" />\n            <section name=\"modules\" allowDefinition=\"MachineToApplication\" overrideModeDefault=\"Deny\" />\n            <section name=\"odbcLogging\" overrideModeDefault=\"Deny\" />\n            <sectionGroup name=\"security\">\n                <section name=\"access\" overrideModeDefault=\"Deny\" />\n                <section name=\"applicationDependencies\" overrideModeDefault=\"Deny\" />\n                <sectionGroup name=\"authentication\">\n                    <section name=\"anonymousAuthentication\" overrideModeDefault=\"Deny\" />\n                    <section name=\"basicAuthentication\" overrideModeDefault=\"Deny\" />\n                    <section name=\"clientCertificateMappingAuthentication\" overrideModeDefault=\"Deny\" />\n                    <section name=\"digestAuthentication\" overrideModeDefault=\"Deny\" />\n                    <section name=\"iisClientCertificateMappingAuthentication\" overrideModeDefault=\"Deny\" />\n                    <section name=\"windowsAuthentication\" overrideModeDefault=\"Deny\" />\n                </sectionGroup>\n                <section name=\"authorization\" overrideModeDefault=\"Allow\" />\n                <section name=\"ipSecurity\" overrideModeDefault=\"Deny\" />\n                <section name=\"dynamicIpSecurity\" overrideModeDefault=\"Deny\" />\n                <section name=\"isapiCgiRestriction\" allowDefinition=\"AppHostOnly\" overrideModeDefault=\"Deny\" />\n                <section name=\"requestFiltering\" overrideModeDefault=\"Allow\" />\n            </sectionGroup>\n            <section name=\"serverRuntime\" overrideModeDefault=\"Deny\" />\n            <section name=\"serverSideInclude\" overrideModeDefault=\"Deny\" />\n            <section name=\"staticContent\" overrideModeDefault=\"Allow\" />\n            <sectionGroup name=\"tracing\">\n                <section name=\"traceFailedRequests\" overrideModeDefault=\"Allow\" />\n                <section name=\"traceProviderDefinitions\" overrideModeDefault=\"Deny\" />\n            </sectionGroup>\n            <section name=\"urlCompression\" overrideModeDefault=\"Allow\" />\n            <section name=\"validation\" overrideModeDefault=\"Allow\" />\n            <sectionGroup name=\"webdav\">\n                <section name=\"globalSettings\" overrideModeDefault=\"Deny\" />\n                <section name=\"authoring\" overrideModeDefault=\"Deny\" />\n                <section name=\"authoringRules\" overrideModeDefault=\"Deny\" />\n            </sectionGroup>\n            <sectionGroup name=\"rewrite\">\n                <section name=\"allowedServerVariables\" overrideModeDefault=\"Deny\" />\n                <section name=\"rules\" overrideModeDefault=\"Allow\" />\n                <section name=\"outboundRules\" overrideModeDefault=\"Allow\" />\n                <section name=\"globalRules\" overrideModeDefault=\"Deny\" allowDefinition=\"AppHostOnly\" />\n                <section name=\"providers\" overrideModeDefault=\"Allow\" />\n                <section name=\"rewriteMaps\" overrideModeDefault=\"Allow\" />\n            </sectionGroup>\n            <section name=\"applicationInitialization\" allowDefinition=\"MachineToApplication\" overrideModeDefault=\"Allow\" />\n            <section name=\"webSocket\" overrideModeDefault=\"Deny\" />\n        </sectionGroup>\n    </configSections>\n\n    <configProtectedData>\n        <providers>\n            <add name=\"IISWASOnlyRsaProvider\" type=\"\" description=\"Uses RsaCryptoServiceProvider to encrypt and decrypt\" keyContainerName=\"iisWasKey\" cspProviderName=\"\" useMachineContainer=\"true\" useOAEP=\"false\" />\n            <add name=\"AesProvider\" type=\"Microsoft.ApplicationHost.AesProtectedConfigurationProvider\" description=\"Uses an AES session key to encrypt and decrypt\" keyContainerName=\"iisConfigurationKey\" cspProviderName=\"\" useOAEP=\"false\" useMachineContainer=\"true\" sessionKey=\"AQIAAA5mAAAApAAAKmFQvWHDEETRz8l2bjZlRxIkwcqTFaCUnCLljn3Q1OkesrhEO9YyLyx4bUhsj1/DyShAv7OAFFhXlrlomaornnk5PLeyO4lIXxaiT33yOFUUgxDx4GSaygkqghVV0tO5yQ/XguUBp2juMfZyztnsNa4pLcz7ZNZQ6p4yn9hxwNs=\" />\n            <add name=\"IISWASOnlyAesProvider\" type=\"Microsoft.ApplicationHost.AesProtectedConfigurationProvider\" description=\"Uses an AES session key to encrypt and decrypt\" keyContainerName=\"iisWasKey\" cspProviderName=\"\" useOAEP=\"false\" useMachineContainer=\"true\" sessionKey=\"AQIAAA5mAAAApAAA4WoiRJ8KHwzAG8AgejPxEOO4/2Vhkolbwo/8gZeNdUDSD36m55hWv4uC9tr/MlKdnwRLL0NhT50Gccyftqz5xTZ0dg5FtvQhTw/he1NwexTKbV+I4Zrd+sZUqHZTsr7JiEr6OHGXL70qoISW5G2m9U8wKT3caPiDPNj2aAaYPLo=\" />\n        </providers>\n    </configProtectedData>\n\n    <system.applicationHost>\n\n        <applicationPools>\n            <add name=\"Clr4IntegratedAppPool\" managedRuntimeVersion=\"v4.0\" managedPipelineMode=\"Integrated\" CLRConfigFile=\"%IIS_USER_HOME%\\config\\aspnet.config\" autoStart=\"true\" />\n            <add name=\"Clr4ClassicAppPool\" managedRuntimeVersion=\"v4.0\" managedPipelineMode=\"Classic\" CLRConfigFile=\"%IIS_USER_HOME%\\config\\aspnet.config\" autoStart=\"true\" />\n            <add name=\"Clr2IntegratedAppPool\" managedRuntimeVersion=\"v2.0\" managedPipelineMode=\"Integrated\" CLRConfigFile=\"%IIS_USER_HOME%\\config\\aspnet.config\" autoStart=\"true\" />\n            <add name=\"Clr2ClassicAppPool\" managedRuntimeVersion=\"v2.0\" managedPipelineMode=\"Classic\" CLRConfigFile=\"%IIS_USER_HOME%\\config\\aspnet.config\" autoStart=\"true\" />\n            <add name=\"UnmanagedClassicAppPool\" managedRuntimeVersion=\"\" managedPipelineMode=\"Classic\" autoStart=\"true\" />\n            <applicationPoolDefaults managedRuntimeLoader=\"v4.0\" >\n                <processModel/>\n            </applicationPoolDefaults>\n        </applicationPools>\n\n        <!--\n\n          The <listenerAdapters> section defines the protocols with which the\n          Windows Process Activation Service (WAS) binds.\n\n        -->\n        <listenerAdapters>\n            <add name=\"http\" />\n        </listenerAdapters>\n\n        <sites>\n            <site name=\"WebSite1\" id=\"1\" serverAutoStart=\"true\">\n                <application path=\"/\">\n                    <virtualDirectory path=\"/\" physicalPath=\"%IIS_SITES_HOME%\\WebSite1\" />\n                </application>\n                <bindings>\n                    <binding protocol=\"http\" bindingInformation=\":8080:localhost\" />\n                </bindings>\n            </site>\n            <siteDefaults>\n                <logFile logFormat=\"W3C\" directory=\"%IIS_USER_HOME%\\Logs\" />\n                <traceFailedRequestsLogging directory=\"%IIS_USER_HOME%\\TraceLogFiles\" enabled=\"true\" maxLogFileSizeKB=\"1024\" />\n            </siteDefaults>\n            <applicationDefaults applicationPool=\"Clr4IntegratedAppPool\" />\n            <virtualDirectoryDefaults allowSubDirConfig=\"true\" />\n        </sites>\n\n        <webLimits />\n\n    </system.applicationHost>\n\n    <system.webServer>\n\n        <serverRuntime />\n\n        <asp scriptErrorSentToBrowser=\"true\">\n            <cache diskTemplateCacheDirectory=\"%TEMP%\\iisexpress\\ASP Compiled Templates\" />\n            <limits />\n        </asp>\n\n        <caching enabled=\"true\" enableKernelCache=\"true\">\n        </caching>\n\n        <cgi />\n\n        <defaultDocument enabled=\"true\">\n            <files>\n                <add value=\"Default.htm\" />\n                <add value=\"Default.asp\" />\n                <add value=\"index.htm\" />\n                <add value=\"index.html\" />\n                <add value=\"iisstart.htm\" />\n                <add value=\"default.aspx\" />\n            </files>\n        </defaultDocument>\n\n        <directoryBrowse enabled=\"false\" />\n\n        <fastCgi />\n\n        <!--\n\n          The <globalModules> section defines all native-code modules.\n          To enable a module, specify it in the <modules> section.\n\n        -->\n        <globalModules>\n            <add name=\"HttpLoggingModule\" image=\"%IIS_BIN%\\loghttp.dll\" />\n            <add name=\"UriCacheModule\" image=\"%IIS_BIN%\\cachuri.dll\" />\n<!--            <add name=\"FileCacheModule\" image=\"%IIS_BIN%\\cachfile.dll\" />  -->\n            <add name=\"TokenCacheModule\" image=\"%IIS_BIN%\\cachtokn.dll\" />\n<!--            <add name=\"HttpCacheModule\" image=\"%IIS_BIN%\\cachhttp.dll\" /> -->\n            <add name=\"DynamicCompressionModule\" image=\"%IIS_BIN%\\compdyn.dll\" />\n            <add name=\"StaticCompressionModule\" image=\"%IIS_BIN%\\compstat.dll\" />\n            <add name=\"DefaultDocumentModule\" image=\"%IIS_BIN%\\defdoc.dll\" />\n            <add name=\"DirectoryListingModule\" image=\"%IIS_BIN%\\dirlist.dll\" />\n            <add name=\"ProtocolSupportModule\" image=\"%IIS_BIN%\\protsup.dll\" />\n            <add name=\"HttpRedirectionModule\" image=\"%IIS_BIN%\\redirect.dll\" />\n            <add name=\"ServerSideIncludeModule\" image=\"%IIS_BIN%\\iis_ssi.dll\" />\n            <add name=\"StaticFileModule\" image=\"%IIS_BIN%\\static.dll\" />\n            <add name=\"AnonymousAuthenticationModule\" image=\"%IIS_BIN%\\authanon.dll\" />\n            <add name=\"CertificateMappingAuthenticationModule\" image=\"%IIS_BIN%\\authcert.dll\" />\n            <add name=\"UrlAuthorizationModule\" image=\"%IIS_BIN%\\urlauthz.dll\" />\n            <add name=\"BasicAuthenticationModule\" image=\"%IIS_BIN%\\authbas.dll\" />\n            <add name=\"WindowsAuthenticationModule\" image=\"%IIS_BIN%\\authsspi.dll\" />\n<!--            <add name=\"DigestAuthenticationModule\" image=\"%IIS_BIN%\\authmd5.dll\" /> -->\n            <add name=\"IISCertificateMappingAuthenticationModule\" image=\"%IIS_BIN%\\authmap.dll\" />\n            <add name=\"IpRestrictionModule\" image=\"%IIS_BIN%\\iprestr.dll\" />\n            <add name=\"DynamicIpRestrictionModule\" image=\"%IIS_BIN%\\diprestr.dll\" />\n            <add name=\"RequestFilteringModule\" image=\"%IIS_BIN%\\modrqflt.dll\" />\n            <add name=\"CustomLoggingModule\" image=\"%IIS_BIN%\\logcust.dll\" />\n            <add name=\"CustomErrorModule\" image=\"%IIS_BIN%\\custerr.dll\" />\n<!--            <add name=\"TracingModule\" image=\"%IIS_BIN%\\iisetw.dll\" /> -->\n            <add name=\"FailedRequestsTracingModule\" image=\"%IIS_BIN%\\iisfreb.dll\" />\n            <add name=\"RequestMonitorModule\" image=\"%IIS_BIN%\\iisreqs.dll\" />\n            <add name=\"IsapiModule\" image=\"%IIS_BIN%\\isapi.dll\" />\n            <add name=\"IsapiFilterModule\" image=\"%IIS_BIN%\\filter.dll\" />\n            <add name=\"CgiModule\" image=\"%IIS_BIN%\\cgi.dll\" />\n            <add name=\"FastCgiModule\" image=\"%IIS_BIN%\\iisfcgi.dll\" />\n<!--            <add name=\"WebDAVModule\" image=\"%IIS_BIN%\\webdav.dll\" /> -->\n            <add name=\"RewriteModule\" image=\"%IIS_BIN%\\rewrite.dll\" />\n            <add name=\"ConfigurationValidationModule\" image=\"%IIS_BIN%\\validcfg.dll\" />\n            <add name=\"WebSocketModule\" image=\"%IIS_BIN%\\iiswsock.dll\" />\n            <add name=\"WebMatrixSupportModule\" image=\"%IIS_BIN%\\webmatrixsup.dll\" />\n            <add name=\"ManagedEngine\" image=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\webengine.dll\" preCondition=\"integratedMode,runtimeVersionv2.0,bitness32\" />\n            <add name=\"ManagedEngine64\" image=\"%windir%\\Microsoft.NET\\Framework64\\v2.0.50727\\webengine.dll\" preCondition=\"integratedMode,runtimeVersionv2.0,bitness64\" />\n            <add name=\"ManagedEngineV4.0_32bit\" image=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\webengine4.dll\" preCondition=\"integratedMode,runtimeVersionv4.0,bitness32\" />\n            <add name=\"ManagedEngineV4.0_64bit\" image=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\webengine4.dll\" preCondition=\"integratedMode,runtimeVersionv4.0,bitness64\" />\n            <add name=\"ApplicationInitializationModule\" image=\"%IIS_BIN%\\warmup.dll\" />\n        </globalModules>\n\n        <httpCompression directory=\"%TEMP%\\iisexpress\\IIS Temporary Compressed Files\">\n            <scheme name=\"gzip\" dll=\"%IIS_BIN%\\gzip.dll\" />\n            <dynamicTypes>\n                <add mimeType=\"text/*\" enabled=\"true\" />\n                <add mimeType=\"message/*\" enabled=\"true\" />\n                <add mimeType=\"application/javascript\" enabled=\"true\" />\n                <add mimeType=\"application/atom+xml\" enabled=\"true\" />\n                <add mimeType=\"application/xaml+xml\" enabled=\"true\" />\n                <add mimeType=\"*/*\" enabled=\"false\" />\n            </dynamicTypes>\n            <staticTypes>\n                <add mimeType=\"text/*\" enabled=\"true\" />\n                <add mimeType=\"message/*\" enabled=\"true\" />\n                <add mimeType=\"application/javascript\" enabled=\"true\" />\n                <add mimeType=\"application/atom+xml\" enabled=\"true\" />\n                <add mimeType=\"application/xaml+xml\" enabled=\"true\" />\n                <add mimeType=\"*/*\" enabled=\"false\" />\n            </staticTypes>\n        </httpCompression>\n\n        <httpErrors lockAttributes=\"allowAbsolutePathsWhenDelegated,defaultPath\">\n            <error statusCode=\"401\" prefixLanguageFilePath=\"%IIS_BIN%\\custerr\" path=\"401.htm\" />\n            <error statusCode=\"403\" prefixLanguageFilePath=\"%IIS_BIN%\\custerr\" path=\"403.htm\" />\n            <error statusCode=\"404\" prefixLanguageFilePath=\"%IIS_BIN%\\custerr\" path=\"404.htm\" />\n            <error statusCode=\"405\" prefixLanguageFilePath=\"%IIS_BIN%\\custerr\" path=\"405.htm\" />\n            <error statusCode=\"406\" prefixLanguageFilePath=\"%IIS_BIN%\\custerr\" path=\"406.htm\" />\n            <error statusCode=\"412\" prefixLanguageFilePath=\"%IIS_BIN%\\custerr\" path=\"412.htm\" />\n            <error statusCode=\"500\" prefixLanguageFilePath=\"%IIS_BIN%\\custerr\" path=\"500.htm\" />\n            <error statusCode=\"501\" prefixLanguageFilePath=\"%IIS_BIN%\\custerr\" path=\"501.htm\" />\n            <error statusCode=\"502\" prefixLanguageFilePath=\"%IIS_BIN%\\custerr\" path=\"502.htm\" />\n        </httpErrors>\n\n        <httpLogging dontLog=\"false\" />\n\n        <httpProtocol>\n            <customHeaders>\n                <clear />\n                <add name=\"X-Powered-By\" value=\"ASP.NET\" />\n            </customHeaders>\n            <redirectHeaders>\n                <clear />\n            </redirectHeaders>\n        </httpProtocol>\n\n        <httpRedirect enabled=\"false\" />\n\n        <httpTracing>\n        </httpTracing>\n\n        <isapiFilters>\n            <filter name=\"ASP.Net_2.0.50727-64\" path=\"%windir%\\Microsoft.NET\\Framework64\\v2.0.50727\\aspnet_filter.dll\" enableCache=\"true\" preCondition=\"bitness64,runtimeVersionv2.0\" />\n            <filter name=\"ASP.Net_2.0.50727.0\" path=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\aspnet_filter.dll\" enableCache=\"true\" preCondition=\"bitness32,runtimeVersionv2.0\" />\n            <filter name=\"ASP.Net_2.0_for_v1.1\" path=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\aspnet_filter.dll\" enableCache=\"true\" preCondition=\"runtimeVersionv1.1\" />\n            <filter name=\"ASP.Net_4.0_32bit\" path=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_filter.dll\" enableCache=\"true\" preCondition=\"bitness32,runtimeVersionv4.0\" />\n            <filter name=\"ASP.Net_4.0_64bit\" path=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_filter.dll\" enableCache=\"true\" preCondition=\"bitness64,runtimeVersionv4.0\" />\n        </isapiFilters>\n\n        <odbcLogging />\n\n        <security>\n\n            <access sslFlags=\"None\" />\n\n            <applicationDependencies>\n                <application name=\"Active Server Pages\" groupId=\"ASP\" />\n            </applicationDependencies>\n\n            <authentication>\n\n                <anonymousAuthentication enabled=\"true\" userName=\"\" />\n\n                <basicAuthentication enabled=\"false\" />\n\n                <clientCertificateMappingAuthentication enabled=\"false\" />\n\n                <digestAuthentication enabled=\"false\" />\n\n                <iisClientCertificateMappingAuthentication enabled=\"false\">\n                </iisClientCertificateMappingAuthentication>\n\n                <windowsAuthentication enabled=\"false\">\n                    <providers>\n                        <add value=\"Negotiate\" />\n                        <add value=\"NTLM\" />\n                    </providers>\n                </windowsAuthentication>\n\n            </authentication>\n\n            <authorization>\n                <add accessType=\"Allow\" users=\"*\" />\n            </authorization>\n\n            <ipSecurity allowUnlisted=\"true\" />\n\n            <isapiCgiRestriction notListedIsapisAllowed=\"true\" notListedCgisAllowed=\"true\">\n                <add path=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\webengine4.dll\" allowed=\"true\" groupId=\"ASP.NET_v4.0\" description=\"ASP.NET_v4.0\" />\n                <add path=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\webengine4.dll\" allowed=\"true\" groupId=\"ASP.NET_v4.0\" description=\"ASP.NET_v4.0\" />\n                <add path=\"%windir%\\Microsoft.NET\\Framework64\\v2.0.50727\\aspnet_isapi.dll\" allowed=\"true\" groupId=\"ASP.NET v2.0.50727\" description=\"ASP.NET v2.0.50727\" />\n                <add path=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\aspnet_isapi.dll\" allowed=\"true\" groupId=\"ASP.NET v2.0.50727\" description=\"ASP.NET v2.0.50727\" />\n            </isapiCgiRestriction>\n\n            <requestFiltering>\n                <fileExtensions allowUnlisted=\"true\" applyToWebDAV=\"true\">\n                    <add fileExtension=\".asax\" allowed=\"false\" />\n                    <add fileExtension=\".ascx\" allowed=\"false\" />\n                    <add fileExtension=\".master\" allowed=\"false\" />\n                    <add fileExtension=\".skin\" allowed=\"false\" />\n                    <add fileExtension=\".browser\" allowed=\"false\" />\n                    <add fileExtension=\".sitemap\" allowed=\"false\" />\n                    <add fileExtension=\".config\" allowed=\"false\" />\n                    <add fileExtension=\".cs\" allowed=\"false\" />\n                    <add fileExtension=\".csproj\" allowed=\"false\" />\n                    <add fileExtension=\".vb\" allowed=\"false\" />\n                    <add fileExtension=\".vbproj\" allowed=\"false\" />\n                    <add fileExtension=\".webinfo\" allowed=\"false\" />\n                    <add fileExtension=\".licx\" allowed=\"false\" />\n                    <add fileExtension=\".resx\" allowed=\"false\" />\n                    <add fileExtension=\".resources\" allowed=\"false\" />\n                    <add fileExtension=\".mdb\" allowed=\"false\" />\n                    <add fileExtension=\".vjsproj\" allowed=\"false\" />\n                    <add fileExtension=\".java\" allowed=\"false\" />\n                    <add fileExtension=\".jsl\" allowed=\"false\" />\n                    <add fileExtension=\".ldb\" allowed=\"false\" />\n                    <add fileExtension=\".dsdgm\" allowed=\"false\" />\n                    <add fileExtension=\".ssdgm\" allowed=\"false\" />\n                    <add fileExtension=\".lsad\" allowed=\"false\" />\n                    <add fileExtension=\".ssmap\" allowed=\"false\" />\n                    <add fileExtension=\".cd\" allowed=\"false\" />\n                    <add fileExtension=\".dsprototype\" allowed=\"false\" />\n                    <add fileExtension=\".lsaprototype\" allowed=\"false\" />\n                    <add fileExtension=\".sdm\" allowed=\"false\" />\n                    <add fileExtension=\".sdmDocument\" allowed=\"false\" />\n                    <add fileExtension=\".mdf\" allowed=\"false\" />\n                    <add fileExtension=\".ldf\" allowed=\"false\" />\n                    <add fileExtension=\".ad\" allowed=\"false\" />\n                    <add fileExtension=\".dd\" allowed=\"false\" />\n                    <add fileExtension=\".ldd\" allowed=\"false\" />\n                    <add fileExtension=\".sd\" allowed=\"false\" />\n                    <add fileExtension=\".adprototype\" allowed=\"false\" />\n                    <add fileExtension=\".lddprototype\" allowed=\"false\" />\n                    <add fileExtension=\".exclude\" allowed=\"false\" />\n                    <add fileExtension=\".refresh\" allowed=\"false\" />\n                    <add fileExtension=\".compiled\" allowed=\"false\" />\n                    <add fileExtension=\".msgx\" allowed=\"false\" />\n                    <add fileExtension=\".vsdisco\" allowed=\"false\" />\n                    <add fileExtension=\".rules\" allowed=\"false\" />\n                </fileExtensions>\n                <verbs allowUnlisted=\"true\" applyToWebDAV=\"true\" />\n                <hiddenSegments applyToWebDAV=\"true\">\n                    <add segment=\"web.config\" />\n                    <add segment=\"bin\" />\n                    <add segment=\"App_code\" />\n                    <add segment=\"App_GlobalResources\" />\n                    <add segment=\"App_LocalResources\" />\n                    <add segment=\"App_WebReferences\" />\n                    <add segment=\"App_Data\" />\n                    <add segment=\"App_Browsers\" />\n                </hiddenSegments>\n            </requestFiltering>\n\n        </security>\n\n        <serverSideInclude ssiExecDisable=\"false\" />\n\n        <staticContent lockAttributes=\"isDocFooterFileName\">\n            <mimeMap fileExtension=\".323\" mimeType=\"text/h323\" />\n            <mimeMap fileExtension=\".3g2\" mimeType=\"video/3gpp2\" />\n            <mimeMap fileExtension=\".3gp2\" mimeType=\"video/3gpp2\" />\n            <mimeMap fileExtension=\".3gp\" mimeType=\"video/3gpp\" />\n            <mimeMap fileExtension=\".3gpp\" mimeType=\"video/3gpp\" />\n            <mimeMap fileExtension=\".aac\" mimeType=\"audio/aac\" />\n            <mimeMap fileExtension=\".aaf\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".aca\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".accdb\" mimeType=\"application/msaccess\" />\n            <mimeMap fileExtension=\".accde\" mimeType=\"application/msaccess\" />\n            <mimeMap fileExtension=\".accdt\" mimeType=\"application/msaccess\" />\n            <mimeMap fileExtension=\".acx\" mimeType=\"application/internet-property-stream\" />\n            <mimeMap fileExtension=\".adt\" mimeType=\"audio/vnd.dlna.adts\" />\n            <mimeMap fileExtension=\".adts\" mimeType=\"audio/vnd.dlna.adts\" />\n            <mimeMap fileExtension=\".afm\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".ai\" mimeType=\"application/postscript\" />\n            <mimeMap fileExtension=\".aif\" mimeType=\"audio/x-aiff\" />\n            <mimeMap fileExtension=\".aifc\" mimeType=\"audio/aiff\" />\n            <mimeMap fileExtension=\".aiff\" mimeType=\"audio/aiff\" />\n            <mimeMap fileExtension=\".appcache\" mimeType=\"text/cache-manifest\" />\n            <mimeMap fileExtension=\".application\" mimeType=\"application/x-ms-application\" />\n            <mimeMap fileExtension=\".art\" mimeType=\"image/x-jg\" />\n            <mimeMap fileExtension=\".asd\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".asf\" mimeType=\"video/x-ms-asf\" />\n            <mimeMap fileExtension=\".asi\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".asm\" mimeType=\"text/plain\" />\n            <mimeMap fileExtension=\".asr\" mimeType=\"video/x-ms-asf\" />\n            <mimeMap fileExtension=\".asx\" mimeType=\"video/x-ms-asf\" />\n            <mimeMap fileExtension=\".atom\" mimeType=\"application/atom+xml\" />\n            <mimeMap fileExtension=\".au\" mimeType=\"audio/basic\" />\n            <mimeMap fileExtension=\".avi\" mimeType=\"video/msvideo\" />\n            <mimeMap fileExtension=\".axs\" mimeType=\"application/olescript\" />\n            <mimeMap fileExtension=\".bas\" mimeType=\"text/plain\" />\n            <mimeMap fileExtension=\".bcpio\" mimeType=\"application/x-bcpio\" />\n            <mimeMap fileExtension=\".bin\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".bmp\" mimeType=\"image/bmp\" />\n            <mimeMap fileExtension=\".c\" mimeType=\"text/plain\" />\n            <mimeMap fileExtension=\".cab\" mimeType=\"application/vnd.ms-cab-compressed\" />\n            <mimeMap fileExtension=\".calx\" mimeType=\"application/vnd.ms-office.calx\" />\n            <mimeMap fileExtension=\".cat\" mimeType=\"application/vnd.ms-pki.seccat\" />\n            <mimeMap fileExtension=\".cdf\" mimeType=\"application/x-cdf\" />\n            <mimeMap fileExtension=\".chm\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".class\" mimeType=\"application/x-java-applet\" />\n            <mimeMap fileExtension=\".clp\" mimeType=\"application/x-msclip\" />\n            <mimeMap fileExtension=\".cmx\" mimeType=\"image/x-cmx\" />\n            <mimeMap fileExtension=\".cnf\" mimeType=\"text/plain\" />\n            <mimeMap fileExtension=\".cod\" mimeType=\"image/cis-cod\" />\n            <mimeMap fileExtension=\".cpio\" mimeType=\"application/x-cpio\" />\n            <mimeMap fileExtension=\".cpp\" mimeType=\"text/plain\" />\n            <mimeMap fileExtension=\".crd\" mimeType=\"application/x-mscardfile\" />\n            <mimeMap fileExtension=\".crl\" mimeType=\"application/pkix-crl\" />\n            <mimeMap fileExtension=\".crt\" mimeType=\"application/x-x509-ca-cert\" />\n            <mimeMap fileExtension=\".csh\" mimeType=\"application/x-csh\" />\n            <mimeMap fileExtension=\".css\" mimeType=\"text/css\" />\n            <mimeMap fileExtension=\".csv\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".cur\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".dcr\" mimeType=\"application/x-director\" />\n            <mimeMap fileExtension=\".deploy\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".der\" mimeType=\"application/x-x509-ca-cert\" />\n            <mimeMap fileExtension=\".dib\" mimeType=\"image/bmp\" />\n            <mimeMap fileExtension=\".dir\" mimeType=\"application/x-director\" />\n            <mimeMap fileExtension=\".disco\" mimeType=\"text/xml\" />\n            <mimeMap fileExtension=\".dll\" mimeType=\"application/x-msdownload\" />\n            <mimeMap fileExtension=\".dll.config\" mimeType=\"text/xml\" />\n            <mimeMap fileExtension=\".dlm\" mimeType=\"text/dlm\" />\n            <mimeMap fileExtension=\".doc\" mimeType=\"application/msword\" />\n            <mimeMap fileExtension=\".docm\" mimeType=\"application/vnd.ms-word.document.macroEnabled.12\" />\n            <mimeMap fileExtension=\".docx\" mimeType=\"application/vnd.openxmlformats-officedocument.wordprocessingml.document\" />\n            <mimeMap fileExtension=\".dot\" mimeType=\"application/msword\" />\n            <mimeMap fileExtension=\".dotm\" mimeType=\"application/vnd.ms-word.template.macroEnabled.12\" />\n            <mimeMap fileExtension=\".dotx\" mimeType=\"application/vnd.openxmlformats-officedocument.wordprocessingml.template\" />\n            <mimeMap fileExtension=\".dsp\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".dtd\" mimeType=\"text/xml\" />\n            <mimeMap fileExtension=\".dvi\" mimeType=\"application/x-dvi\" />\n            <mimeMap fileExtension=\".dvr-ms\" mimeType=\"video/x-ms-dvr\" />\n            <mimeMap fileExtension=\".dwf\" mimeType=\"drawing/x-dwf\" />\n            <mimeMap fileExtension=\".dwp\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".dxr\" mimeType=\"application/x-director\" />\n            <mimeMap fileExtension=\".eml\" mimeType=\"message/rfc822\" />\n            <mimeMap fileExtension=\".emz\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".eot\" mimeType=\"application/vnd.ms-fontobject\" />\n            <mimeMap fileExtension=\".eps\" mimeType=\"application/postscript\" />\n            <mimeMap fileExtension=\".etx\" mimeType=\"text/x-setext\" />\n            <mimeMap fileExtension=\".evy\" mimeType=\"application/envoy\" />\n            <mimeMap fileExtension=\".exe\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".exe.config\" mimeType=\"text/xml\" />\n            <mimeMap fileExtension=\".fdf\" mimeType=\"application/vnd.fdf\" />\n            <mimeMap fileExtension=\".fif\" mimeType=\"application/fractals\" />\n            <mimeMap fileExtension=\".fla\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".flr\" mimeType=\"x-world/x-vrml\" />\n            <mimeMap fileExtension=\".flv\" mimeType=\"video/x-flv\" />\n            <mimeMap fileExtension=\".gif\" mimeType=\"image/gif\" />\n            <mimeMap fileExtension=\".gtar\" mimeType=\"application/x-gtar\" />\n            <mimeMap fileExtension=\".gz\" mimeType=\"application/x-gzip\" />\n            <mimeMap fileExtension=\".h\" mimeType=\"text/plain\" />\n            <mimeMap fileExtension=\".hdf\" mimeType=\"application/x-hdf\" />\n            <mimeMap fileExtension=\".hdml\" mimeType=\"text/x-hdml\" />\n            <mimeMap fileExtension=\".hhc\" mimeType=\"application/x-oleobject\" />\n            <mimeMap fileExtension=\".hhk\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".hhp\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".hlp\" mimeType=\"application/winhlp\" />\n            <mimeMap fileExtension=\".hqx\" mimeType=\"application/mac-binhex40\" />\n            <mimeMap fileExtension=\".hta\" mimeType=\"application/hta\" />\n            <mimeMap fileExtension=\".htc\" mimeType=\"text/x-component\" />\n            <mimeMap fileExtension=\".htm\" mimeType=\"text/html\" />\n            <mimeMap fileExtension=\".html\" mimeType=\"text/html\" />\n            <mimeMap fileExtension=\".htt\" mimeType=\"text/webviewhtml\" />\n            <mimeMap fileExtension=\".hxt\" mimeType=\"text/html\" />\n            <mimeMap fileExtension=\".ico\" mimeType=\"image/x-icon\" />\n            <mimeMap fileExtension=\".ics\" mimeType=\"text/calendar\" />\n            <mimeMap fileExtension=\".ief\" mimeType=\"image/ief\" />\n            <mimeMap fileExtension=\".iii\" mimeType=\"application/x-iphone\" />\n            <mimeMap fileExtension=\".inf\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".ins\" mimeType=\"application/x-internet-signup\" />\n            <mimeMap fileExtension=\".isp\" mimeType=\"application/x-internet-signup\" />\n            <mimeMap fileExtension=\".IVF\" mimeType=\"video/x-ivf\" />\n            <mimeMap fileExtension=\".jar\" mimeType=\"application/java-archive\" />\n            <mimeMap fileExtension=\".java\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".jck\" mimeType=\"application/liquidmotion\" />\n            <mimeMap fileExtension=\".jcz\" mimeType=\"application/liquidmotion\" />\n            <mimeMap fileExtension=\".jfif\" mimeType=\"image/pjpeg\" />\n            <mimeMap fileExtension=\".jpb\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".jpe\" mimeType=\"image/jpeg\" />\n            <mimeMap fileExtension=\".jpeg\" mimeType=\"image/jpeg\" />\n            <mimeMap fileExtension=\".jpg\" mimeType=\"image/jpeg\" />\n            <mimeMap fileExtension=\".js\" mimeType=\"application/javascript\" />\n            <mimeMap fileExtension=\".json\" mimeType=\"application/json\" />\n            <mimeMap fileExtension=\".jsonld\" mimeType=\"application/ld+json\" />\n            <mimeMap fileExtension=\".jsx\" mimeType=\"text/jscript\" />\n            <mimeMap fileExtension=\".latex\" mimeType=\"application/x-latex\" />\n            <mimeMap fileExtension=\".less\" mimeType=\"text/css\" />\n            <mimeMap fileExtension=\".lit\" mimeType=\"application/x-ms-reader\" />\n            <mimeMap fileExtension=\".lpk\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".lsf\" mimeType=\"video/x-la-asf\" />\n            <mimeMap fileExtension=\".lsx\" mimeType=\"video/x-la-asf\" />\n            <mimeMap fileExtension=\".lzh\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".m13\" mimeType=\"application/x-msmediaview\" />\n            <mimeMap fileExtension=\".m14\" mimeType=\"application/x-msmediaview\" />\n            <mimeMap fileExtension=\".m1v\" mimeType=\"video/mpeg\" />\n            <mimeMap fileExtension=\".m2ts\" mimeType=\"video/vnd.dlna.mpeg-tts\" />\n            <mimeMap fileExtension=\".m3u\" mimeType=\"audio/x-mpegurl\" />\n            <mimeMap fileExtension=\".m4a\" mimeType=\"audio/mp4\" />\n            <mimeMap fileExtension=\".m4v\" mimeType=\"video/mp4\" />\n            <mimeMap fileExtension=\".man\" mimeType=\"application/x-troff-man\" />\n            <mimeMap fileExtension=\".manifest\" mimeType=\"application/x-ms-manifest\" />\n            <mimeMap fileExtension=\".map\" mimeType=\"text/plain\" />\n            <mimeMap fileExtension=\".mdb\" mimeType=\"application/x-msaccess\" />\n            <mimeMap fileExtension=\".mdp\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".me\" mimeType=\"application/x-troff-me\" />\n            <mimeMap fileExtension=\".mht\" mimeType=\"message/rfc822\" />\n            <mimeMap fileExtension=\".mhtml\" mimeType=\"message/rfc822\" />\n            <mimeMap fileExtension=\".mid\" mimeType=\"audio/mid\" />\n            <mimeMap fileExtension=\".midi\" mimeType=\"audio/mid\" />\n            <mimeMap fileExtension=\".mix\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".mmf\" mimeType=\"application/x-smaf\" />\n            <mimeMap fileExtension=\".mno\" mimeType=\"text/xml\" />\n            <mimeMap fileExtension=\".mny\" mimeType=\"application/x-msmoney\" />\n            <mimeMap fileExtension=\".mov\" mimeType=\"video/quicktime\" />\n            <mimeMap fileExtension=\".movie\" mimeType=\"video/x-sgi-movie\" />\n            <mimeMap fileExtension=\".mp2\" mimeType=\"video/mpeg\" />\n            <mimeMap fileExtension=\".mp3\" mimeType=\"audio/mpeg\" />\n            <mimeMap fileExtension=\".mp4\" mimeType=\"video/mp4\" />\n            <mimeMap fileExtension=\".mp4v\" mimeType=\"video/mp4\" />\n            <mimeMap fileExtension=\".mpa\" mimeType=\"video/mpeg\" />\n            <mimeMap fileExtension=\".mpe\" mimeType=\"video/mpeg\" />\n            <mimeMap fileExtension=\".mpeg\" mimeType=\"video/mpeg\" />\n            <mimeMap fileExtension=\".mpg\" mimeType=\"video/mpeg\" />\n            <mimeMap fileExtension=\".mpp\" mimeType=\"application/vnd.ms-project\" />\n            <mimeMap fileExtension=\".mpv2\" mimeType=\"video/mpeg\" />\n            <mimeMap fileExtension=\".ms\" mimeType=\"application/x-troff-ms\" />\n            <mimeMap fileExtension=\".msi\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".mso\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".mvb\" mimeType=\"application/x-msmediaview\" />\n            <mimeMap fileExtension=\".mvc\" mimeType=\"application/x-miva-compiled\" />\n            <mimeMap fileExtension=\".nc\" mimeType=\"application/x-netcdf\" />\n            <mimeMap fileExtension=\".nsc\" mimeType=\"video/x-ms-asf\" />\n            <mimeMap fileExtension=\".nws\" mimeType=\"message/rfc822\" />\n            <mimeMap fileExtension=\".ocx\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".oda\" mimeType=\"application/oda\" />\n            <mimeMap fileExtension=\".odc\" mimeType=\"text/x-ms-odc\" />\n            <mimeMap fileExtension=\".ods\" mimeType=\"application/oleobject\" />\n            <mimeMap fileExtension=\".oga\" mimeType=\"audio/ogg\" />\n            <mimeMap fileExtension=\".ogg\" mimeType=\"video/ogg\" />\n            <mimeMap fileExtension=\".ogv\" mimeType=\"video/ogg\" />\n            <mimeMap fileExtension=\".one\" mimeType=\"application/onenote\" />\n            <mimeMap fileExtension=\".onea\" mimeType=\"application/onenote\" />\n            <mimeMap fileExtension=\".onetoc\" mimeType=\"application/onenote\" />\n            <mimeMap fileExtension=\".onetoc2\" mimeType=\"application/onenote\" />\n            <mimeMap fileExtension=\".onetmp\" mimeType=\"application/onenote\" />\n            <mimeMap fileExtension=\".onepkg\" mimeType=\"application/onenote\" />\n            <mimeMap fileExtension=\".osdx\" mimeType=\"application/opensearchdescription+xml\" />\n            <mimeMap fileExtension=\".otf\" mimeType=\"font/otf\" />\n            <mimeMap fileExtension=\".p10\" mimeType=\"application/pkcs10\" />\n            <mimeMap fileExtension=\".p12\" mimeType=\"application/x-pkcs12\" />\n            <mimeMap fileExtension=\".p7b\" mimeType=\"application/x-pkcs7-certificates\" />\n            <mimeMap fileExtension=\".p7c\" mimeType=\"application/pkcs7-mime\" />\n            <mimeMap fileExtension=\".p7m\" mimeType=\"application/pkcs7-mime\" />\n            <mimeMap fileExtension=\".p7r\" mimeType=\"application/x-pkcs7-certreqresp\" />\n            <mimeMap fileExtension=\".p7s\" mimeType=\"application/pkcs7-signature\" />\n            <mimeMap fileExtension=\".pbm\" mimeType=\"image/x-portable-bitmap\" />\n            <mimeMap fileExtension=\".pcx\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".pcz\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".pdf\" mimeType=\"application/pdf\" />\n            <mimeMap fileExtension=\".pfb\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".pfm\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".pfx\" mimeType=\"application/x-pkcs12\" />\n            <mimeMap fileExtension=\".pgm\" mimeType=\"image/x-portable-graymap\" />\n            <mimeMap fileExtension=\".pko\" mimeType=\"application/vnd.ms-pki.pko\" />\n            <mimeMap fileExtension=\".pma\" mimeType=\"application/x-perfmon\" />\n            <mimeMap fileExtension=\".pmc\" mimeType=\"application/x-perfmon\" />\n            <mimeMap fileExtension=\".pml\" mimeType=\"application/x-perfmon\" />\n            <mimeMap fileExtension=\".pmr\" mimeType=\"application/x-perfmon\" />\n            <mimeMap fileExtension=\".pmw\" mimeType=\"application/x-perfmon\" />\n            <mimeMap fileExtension=\".png\" mimeType=\"image/png\" />\n            <mimeMap fileExtension=\".pnm\" mimeType=\"image/x-portable-anymap\" />\n            <mimeMap fileExtension=\".pnz\" mimeType=\"image/png\" />\n            <mimeMap fileExtension=\".pot\" mimeType=\"application/vnd.ms-powerpoint\" />\n            <mimeMap fileExtension=\".potm\" mimeType=\"application/vnd.ms-powerpoint.template.macroEnabled.12\" />\n            <mimeMap fileExtension=\".potx\" mimeType=\"application/vnd.openxmlformats-officedocument.presentationml.template\" />\n            <mimeMap fileExtension=\".ppam\" mimeType=\"application/vnd.ms-powerpoint.addin.macroEnabled.12\" />\n            <mimeMap fileExtension=\".ppm\" mimeType=\"image/x-portable-pixmap\" />\n            <mimeMap fileExtension=\".pps\" mimeType=\"application/vnd.ms-powerpoint\" />\n            <mimeMap fileExtension=\".ppsm\" mimeType=\"application/vnd.ms-powerpoint.slideshow.macroEnabled.12\" />\n            <mimeMap fileExtension=\".ppsx\" mimeType=\"application/vnd.openxmlformats-officedocument.presentationml.slideshow\" />\n            <mimeMap fileExtension=\".ppt\" mimeType=\"application/vnd.ms-powerpoint\" />\n            <mimeMap fileExtension=\".pptm\" mimeType=\"application/vnd.ms-powerpoint.presentation.macroEnabled.12\" />\n            <mimeMap fileExtension=\".pptx\" mimeType=\"application/vnd.openxmlformats-officedocument.presentationml.presentation\" />\n            <mimeMap fileExtension=\".prf\" mimeType=\"application/pics-rules\" />\n            <mimeMap fileExtension=\".prm\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".prx\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".ps\" mimeType=\"application/postscript\" />\n            <mimeMap fileExtension=\".psd\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".psm\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".psp\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".pub\" mimeType=\"application/x-mspublisher\" />\n            <mimeMap fileExtension=\".qt\" mimeType=\"video/quicktime\" />\n            <mimeMap fileExtension=\".qtl\" mimeType=\"application/x-quicktimeplayer\" />\n            <mimeMap fileExtension=\".qxd\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".ra\" mimeType=\"audio/x-pn-realaudio\" />\n            <mimeMap fileExtension=\".ram\" mimeType=\"audio/x-pn-realaudio\" />\n            <mimeMap fileExtension=\".rar\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".ras\" mimeType=\"image/x-cmu-raster\" />\n            <mimeMap fileExtension=\".rf\" mimeType=\"image/vnd.rn-realflash\" />\n            <mimeMap fileExtension=\".rgb\" mimeType=\"image/x-rgb\" />\n            <mimeMap fileExtension=\".rm\" mimeType=\"application/vnd.rn-realmedia\" />\n            <mimeMap fileExtension=\".rmi\" mimeType=\"audio/mid\" />\n            <mimeMap fileExtension=\".roff\" mimeType=\"application/x-troff\" />\n            <mimeMap fileExtension=\".rpm\" mimeType=\"audio/x-pn-realaudio-plugin\" />\n            <mimeMap fileExtension=\".rtf\" mimeType=\"application/rtf\" />\n            <mimeMap fileExtension=\".rtx\" mimeType=\"text/richtext\" />\n            <mimeMap fileExtension=\".scd\" mimeType=\"application/x-msschedule\" />\n            <mimeMap fileExtension=\".sct\" mimeType=\"text/scriptlet\" />\n            <mimeMap fileExtension=\".sea\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".setpay\" mimeType=\"application/set-payment-initiation\" />\n            <mimeMap fileExtension=\".setreg\" mimeType=\"application/set-registration-initiation\" />\n            <mimeMap fileExtension=\".sgml\" mimeType=\"text/sgml\" />\n            <mimeMap fileExtension=\".sh\" mimeType=\"application/x-sh\" />\n            <mimeMap fileExtension=\".shar\" mimeType=\"application/x-shar\" />\n            <mimeMap fileExtension=\".sit\" mimeType=\"application/x-stuffit\" />\n            <mimeMap fileExtension=\".sldm\" mimeType=\"application/vnd.ms-powerpoint.slide.macroEnabled.12\" />\n            <mimeMap fileExtension=\".sldx\" mimeType=\"application/vnd.openxmlformats-officedocument.presentationml.slide\" />\n            <mimeMap fileExtension=\".smd\" mimeType=\"audio/x-smd\" />\n            <mimeMap fileExtension=\".smi\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".smx\" mimeType=\"audio/x-smd\" />\n            <mimeMap fileExtension=\".smz\" mimeType=\"audio/x-smd\" />\n            <mimeMap fileExtension=\".snd\" mimeType=\"audio/basic\" />\n            <mimeMap fileExtension=\".snp\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".spc\" mimeType=\"application/x-pkcs7-certificates\" />\n            <mimeMap fileExtension=\".spl\" mimeType=\"application/futuresplash\" />\n            <mimeMap fileExtension=\".spx\" mimeType=\"audio/ogg\" />\n            <mimeMap fileExtension=\".src\" mimeType=\"application/x-wais-source\" />\n            <mimeMap fileExtension=\".ssm\" mimeType=\"application/streamingmedia\" />\n            <mimeMap fileExtension=\".sst\" mimeType=\"application/vnd.ms-pki.certstore\" />\n            <mimeMap fileExtension=\".stl\" mimeType=\"application/vnd.ms-pki.stl\" />\n            <mimeMap fileExtension=\".sv4cpio\" mimeType=\"application/x-sv4cpio\" />\n            <mimeMap fileExtension=\".sv4crc\" mimeType=\"application/x-sv4crc\" />\n            <mimeMap fileExtension=\".svg\" mimeType=\"image/svg+xml\" />\n            <mimeMap fileExtension=\".svgz\" mimeType=\"image/svg+xml\" />\n            <mimeMap fileExtension=\".swf\" mimeType=\"application/x-shockwave-flash\" />\n            <mimeMap fileExtension=\".t\" mimeType=\"application/x-troff\" />\n            <mimeMap fileExtension=\".tar\" mimeType=\"application/x-tar\" />\n            <mimeMap fileExtension=\".tcl\" mimeType=\"application/x-tcl\" />\n            <mimeMap fileExtension=\".tex\" mimeType=\"application/x-tex\" />\n            <mimeMap fileExtension=\".texi\" mimeType=\"application/x-texinfo\" />\n            <mimeMap fileExtension=\".texinfo\" mimeType=\"application/x-texinfo\" />\n            <mimeMap fileExtension=\".tgz\" mimeType=\"application/x-compressed\" />\n            <mimeMap fileExtension=\".thmx\" mimeType=\"application/vnd.ms-officetheme\" />\n            <mimeMap fileExtension=\".thn\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".tif\" mimeType=\"image/tiff\" />\n            <mimeMap fileExtension=\".tiff\" mimeType=\"image/tiff\" />\n            <mimeMap fileExtension=\".toc\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".tr\" mimeType=\"application/x-troff\" />\n            <mimeMap fileExtension=\".trm\" mimeType=\"application/x-msterminal\" />\n            <mimeMap fileExtension=\".ts\" mimeType=\"video/vnd.dlna.mpeg-tts\" />\n            <mimeMap fileExtension=\".tsv\" mimeType=\"text/tab-separated-values\" />\n            <mimeMap fileExtension=\".ttf\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".tts\" mimeType=\"video/vnd.dlna.mpeg-tts\" />\n            <mimeMap fileExtension=\".txt\" mimeType=\"text/plain\" />\n            <mimeMap fileExtension=\".u32\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".uls\" mimeType=\"text/iuls\" />\n            <mimeMap fileExtension=\".ustar\" mimeType=\"application/x-ustar\" />\n            <mimeMap fileExtension=\".vbs\" mimeType=\"text/vbscript\" />\n            <mimeMap fileExtension=\".vcf\" mimeType=\"text/x-vcard\" />\n            <mimeMap fileExtension=\".vcs\" mimeType=\"text/plain\" />\n            <mimeMap fileExtension=\".vdx\" mimeType=\"application/vnd.ms-visio.viewer\" />\n            <mimeMap fileExtension=\".vml\" mimeType=\"text/xml\" />\n            <mimeMap fileExtension=\".vsd\" mimeType=\"application/vnd.visio\" />\n            <mimeMap fileExtension=\".vss\" mimeType=\"application/vnd.visio\" />\n            <mimeMap fileExtension=\".vst\" mimeType=\"application/vnd.visio\" />\n            <mimeMap fileExtension=\".vsto\" mimeType=\"application/x-ms-vsto\" />\n            <mimeMap fileExtension=\".vsw\" mimeType=\"application/vnd.visio\" />\n            <mimeMap fileExtension=\".vsx\" mimeType=\"application/vnd.visio\" />\n            <mimeMap fileExtension=\".vtx\" mimeType=\"application/vnd.visio\" />\n            <mimeMap fileExtension=\".wav\" mimeType=\"audio/wav\" />\n            <mimeMap fileExtension=\".wax\" mimeType=\"audio/x-ms-wax\" />\n            <mimeMap fileExtension=\".wbmp\" mimeType=\"image/vnd.wap.wbmp\" />\n            <mimeMap fileExtension=\".wcm\" mimeType=\"application/vnd.ms-works\" />\n            <mimeMap fileExtension=\".wdb\" mimeType=\"application/vnd.ms-works\" />\n            <mimeMap fileExtension=\".webm\" mimeType=\"video/webm\" />\n            <mimeMap fileExtension=\".wks\" mimeType=\"application/vnd.ms-works\" />\n            <mimeMap fileExtension=\".wm\" mimeType=\"video/x-ms-wm\" />\n            <mimeMap fileExtension=\".wma\" mimeType=\"audio/x-ms-wma\" />\n            <mimeMap fileExtension=\".wmd\" mimeType=\"application/x-ms-wmd\" />\n            <mimeMap fileExtension=\".wmf\" mimeType=\"application/x-msmetafile\" />\n            <mimeMap fileExtension=\".wml\" mimeType=\"text/vnd.wap.wml\" />\n            <mimeMap fileExtension=\".wmlc\" mimeType=\"application/vnd.wap.wmlc\" />\n            <mimeMap fileExtension=\".wmls\" mimeType=\"text/vnd.wap.wmlscript\" />\n            <mimeMap fileExtension=\".wmlsc\" mimeType=\"application/vnd.wap.wmlscriptc\" />\n            <mimeMap fileExtension=\".wmp\" mimeType=\"video/x-ms-wmp\" />\n            <mimeMap fileExtension=\".wmv\" mimeType=\"video/x-ms-wmv\" />\n            <mimeMap fileExtension=\".wmx\" mimeType=\"video/x-ms-wmx\" />\n            <mimeMap fileExtension=\".wmz\" mimeType=\"application/x-ms-wmz\" />\n            <mimeMap fileExtension=\".woff\" mimeType=\"font/x-woff\" />\n            <mimeMap fileExtension=\".woff2\" mimeType=\"application/font-woff2\" />\n            <mimeMap fileExtension=\".wps\" mimeType=\"application/vnd.ms-works\" />\n            <mimeMap fileExtension=\".wri\" mimeType=\"application/x-mswrite\" />\n            <mimeMap fileExtension=\".wrl\" mimeType=\"x-world/x-vrml\" />\n            <mimeMap fileExtension=\".wrz\" mimeType=\"x-world/x-vrml\" />\n            <mimeMap fileExtension=\".wsdl\" mimeType=\"text/xml\" />\n            <mimeMap fileExtension=\".wtv\" mimeType=\"video/x-ms-wtv\" />\n            <mimeMap fileExtension=\".wvx\" mimeType=\"video/x-ms-wvx\" />\n            <mimeMap fileExtension=\".x\" mimeType=\"application/directx\" />\n            <mimeMap fileExtension=\".xaf\" mimeType=\"x-world/x-vrml\" />\n            <mimeMap fileExtension=\".xaml\" mimeType=\"application/xaml+xml\" />\n            <mimeMap fileExtension=\".xap\" mimeType=\"application/x-silverlight-app\" />\n            <mimeMap fileExtension=\".xbap\" mimeType=\"application/x-ms-xbap\" />\n            <mimeMap fileExtension=\".xbm\" mimeType=\"image/x-xbitmap\" />\n            <mimeMap fileExtension=\".xdr\" mimeType=\"text/plain\" />\n            <mimeMap fileExtension=\".xht\" mimeType=\"application/xhtml+xml\" />\n            <mimeMap fileExtension=\".xhtml\" mimeType=\"application/xhtml+xml\" />\n            <mimeMap fileExtension=\".xla\" mimeType=\"application/vnd.ms-excel\" />\n            <mimeMap fileExtension=\".xlam\" mimeType=\"application/vnd.ms-excel.addin.macroEnabled.12\" />\n            <mimeMap fileExtension=\".xlc\" mimeType=\"application/vnd.ms-excel\" />\n            <mimeMap fileExtension=\".xlm\" mimeType=\"application/vnd.ms-excel\" />\n            <mimeMap fileExtension=\".xls\" mimeType=\"application/vnd.ms-excel\" />\n            <mimeMap fileExtension=\".xlsb\" mimeType=\"application/vnd.ms-excel.sheet.binary.macroEnabled.12\" />\n            <mimeMap fileExtension=\".xlsm\" mimeType=\"application/vnd.ms-excel.sheet.macroEnabled.12\" />\n            <mimeMap fileExtension=\".xlsx\" mimeType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\" />\n            <mimeMap fileExtension=\".xlt\" mimeType=\"application/vnd.ms-excel\" />\n            <mimeMap fileExtension=\".xltm\" mimeType=\"application/vnd.ms-excel.template.macroEnabled.12\" />\n            <mimeMap fileExtension=\".xltx\" mimeType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.template\" />\n            <mimeMap fileExtension=\".xlw\" mimeType=\"application/vnd.ms-excel\" />\n            <mimeMap fileExtension=\".xml\" mimeType=\"text/xml\" />\n            <mimeMap fileExtension=\".xof\" mimeType=\"x-world/x-vrml\" />\n            <mimeMap fileExtension=\".xpm\" mimeType=\"image/x-xpixmap\" />\n            <mimeMap fileExtension=\".xps\" mimeType=\"application/vnd.ms-xpsdocument\" />\n            <mimeMap fileExtension=\".xsd\" mimeType=\"text/xml\" />\n            <mimeMap fileExtension=\".xsf\" mimeType=\"text/xml\" />\n            <mimeMap fileExtension=\".xsl\" mimeType=\"text/xml\" />\n            <mimeMap fileExtension=\".xslt\" mimeType=\"text/xml\" />\n            <mimeMap fileExtension=\".xsn\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".xtp\" mimeType=\"application/octet-stream\" />\n            <mimeMap fileExtension=\".xwd\" mimeType=\"image/x-xwindowdump\" />\n            <mimeMap fileExtension=\".z\" mimeType=\"application/x-compress\" />\n            <mimeMap fileExtension=\".zip\" mimeType=\"application/x-zip-compressed\" />\n        </staticContent>\n\n        <tracing>\n\n             <traceProviderDefinitions>\n                <add name=\"WWW Server\" guid=\"{3a2a4e84-4c21-4981-ae10-3fda0d9b0f83}\">\n                    <areas>\n                        <clear />\n                        <add name=\"Authentication\" value=\"2\" />\n                        <add name=\"Security\" value=\"4\" />\n                        <add name=\"Filter\" value=\"8\" />\n                        <add name=\"StaticFile\" value=\"16\" />\n                        <add name=\"CGI\" value=\"32\" />\n                        <add name=\"Compression\" value=\"64\" />\n                        <add name=\"Cache\" value=\"128\" />\n                        <add name=\"RequestNotifications\" value=\"256\" />\n                        <add name=\"Module\" value=\"512\" />\n                        <add name=\"Rewrite\" value=\"1024\" />\n                        <add name=\"FastCGI\" value=\"4096\" />\n                        <add name=\"WebSocket\" value=\"16384\" />\n                    </areas>\n                </add>\n                <add name=\"ASP\" guid=\"{06b94d9a-b15e-456e-a4ef-37c984a2cb4b}\">\n                    <areas>\n                        <clear />\n                    </areas>\n                </add>\n                <add name=\"ISAPI Extension\" guid=\"{a1c2040e-8840-4c31-ba11-9871031a19ea}\">\n                    <areas>\n                        <clear />\n                    </areas>\n                </add>\n                <add name=\"ASPNET\" guid=\"{AFF081FE-0247-4275-9C4E-021F3DC1DA35}\">\n                    <areas>\n                        <add name=\"Infrastructure\" value=\"1\" />\n                        <add name=\"Module\" value=\"2\" />\n                        <add name=\"Page\" value=\"4\" />\n                        <add name=\"AppServices\" value=\"8\" />\n                    </areas>\n                </add>\n            </traceProviderDefinitions>\n\n            <traceFailedRequests>\n                <add path=\"*\">\n                    <traceAreas>\n                        <add provider=\"ASP\" verbosity=\"Verbose\" />\n                        <add provider=\"ASPNET\" areas=\"Infrastructure,Module,Page,AppServices\" verbosity=\"Verbose\" />\n                        <add provider=\"ISAPI Extension\" verbosity=\"Verbose\" />\n                        <add provider=\"WWW Server\" areas=\"Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module,Rewrite,WebSocket\" verbosity=\"Verbose\" />\n                    </traceAreas>\n                    <failureDefinitions statusCodes=\"200-999\" />\n                </add>\n            </traceFailedRequests>\n\n        </tracing>\n\n        <urlCompression />\n\n        <validation />\n        <webdav>\n            <globalSettings>\n                <propertyStores>\n                    <add name=\"webdav_simple_prop\" image=\"%IIS_BIN%\\webdav_simple_prop.dll\" image32=\"%IIS_BIN%\\webdav_simple_prop.dll\" />\n                </propertyStores>\n                <lockStores>\n                    <add name=\"webdav_simple_lock\" image=\"%IIS_BIN%\\webdav_simple_lock.dll\" image32=\"%IIS_BIN%\\webdav_simple_lock.dll\" />\n                </lockStores>\n\n            </globalSettings>\n            <authoring>\n                <locks enabled=\"true\" lockStore=\"webdav_simple_lock\" />\n            </authoring>\n            <authoringRules />\n        </webdav>\n        <webSocket />\n        <applicationInitialization />\n\n    </system.webServer>\n    <location path=\"\" overrideMode=\"Allow\">\n        <system.webServer>\n            <modules>\n                <add name=\"IsapiFilterModule\" lockItem=\"true\" />\n                <add name=\"BasicAuthenticationModule\" lockItem=\"true\" />\n                <add name=\"IsapiModule\" lockItem=\"true\" />\n                <add name=\"HttpLoggingModule\" lockItem=\"true\" />\n                <!--\n                <add name=\"HttpCacheModule\" lockItem=\"true\" />\n-->\n                <add name=\"DynamicCompressionModule\" lockItem=\"true\" />\n                <add name=\"StaticCompressionModule\" lockItem=\"true\" />\n                <add name=\"DefaultDocumentModule\" lockItem=\"true\" />\n                <add name=\"DirectoryListingModule\" lockItem=\"true\" />\n\n                <add name=\"ProtocolSupportModule\" lockItem=\"true\" />\n                <add name=\"HttpRedirectionModule\" lockItem=\"true\" />\n                <add name=\"ServerSideIncludeModule\" lockItem=\"true\" />\n                <add name=\"StaticFileModule\" lockItem=\"true\" />\n                <add name=\"AnonymousAuthenticationModule\" lockItem=\"true\" />\n                <add name=\"CertificateMappingAuthenticationModule\" lockItem=\"true\" />\n                <add name=\"UrlAuthorizationModule\" lockItem=\"true\" />\n                <add name=\"WindowsAuthenticationModule\" lockItem=\"true\" />\n                <!--\n                <add name=\"DigestAuthenticationModule\" lockItem=\"true\" />\n-->\n                <add name=\"IISCertificateMappingAuthenticationModule\" lockItem=\"true\" />\n                <add name=\"WebMatrixSupportModule\" lockItem=\"true\" />\n                <add name=\"IpRestrictionModule\" lockItem=\"true\" />\n                <add name=\"DynamicIpRestrictionModule\" lockItem=\"true\" />\n                <add name=\"RequestFilteringModule\" lockItem=\"true\" />\n                <add name=\"CustomLoggingModule\" lockItem=\"true\" />\n                <add name=\"CustomErrorModule\" lockItem=\"true\" />\n                <add name=\"FailedRequestsTracingModule\" lockItem=\"true\" />\n                <add name=\"CgiModule\" lockItem=\"true\" />\n                <add name=\"FastCgiModule\" lockItem=\"true\" />\n                <!--                <add name=\"WebDAVModule\" /> -->\n                <add name=\"RewriteModule\" />\n                <add name=\"OutputCache\" type=\"System.Web.Caching.OutputCacheModule\" preCondition=\"managedHandler\" />\n                <add name=\"Session\" type=\"System.Web.SessionState.SessionStateModule\" preCondition=\"managedHandler\" />\n                <add name=\"WindowsAuthentication\" type=\"System.Web.Security.WindowsAuthenticationModule\" preCondition=\"managedHandler\" />\n                <add name=\"FormsAuthentication\" type=\"System.Web.Security.FormsAuthenticationModule\" preCondition=\"managedHandler\" />\n                <add name=\"DefaultAuthentication\" type=\"System.Web.Security.DefaultAuthenticationModule\" preCondition=\"managedHandler\" />\n                <add name=\"RoleManager\" type=\"System.Web.Security.RoleManagerModule\" preCondition=\"managedHandler\" />\n                <add name=\"UrlAuthorization\" type=\"System.Web.Security.UrlAuthorizationModule\" preCondition=\"managedHandler\" />\n                <add name=\"FileAuthorization\" type=\"System.Web.Security.FileAuthorizationModule\" preCondition=\"managedHandler\" />\n                <add name=\"AnonymousIdentification\" type=\"System.Web.Security.AnonymousIdentificationModule\" preCondition=\"managedHandler\" />\n                <add name=\"Profile\" type=\"System.Web.Profile.ProfileModule\" preCondition=\"managedHandler\" />\n                <add name=\"UrlMappingsModule\" type=\"System.Web.UrlMappingsModule\" preCondition=\"managedHandler\" />\n                <add name=\"ConfigurationValidationModule\" lockItem=\"true\" />\n                <add name=\"WebSocketModule\" lockItem=\"true\" />\n                <add name=\"ServiceModel-4.0\" type=\"System.ServiceModel.Activation.ServiceHttpModule,System.ServiceModel.Activation,Version=4.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35\" preCondition=\"managedHandler,runtimeVersionv4.0\" />\n                <add name=\"UrlRoutingModule-4.0\" type=\"System.Web.Routing.UrlRoutingModule\" preCondition=\"managedHandler,runtimeVersionv4.0\" />\n                <add name=\"ScriptModule-4.0\" type=\"System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" preCondition=\"managedHandler,runtimeVersionv4.0\" />\n                <add name=\"ServiceModel\" type=\"System.ServiceModel.Activation.HttpModule, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" preCondition=\"managedHandler,runtimeVersionv2.0\" />\n                <add name=\"ApplicationInitializationModule\" lockItem=\"true\" />\n            </modules>\n            <handlers accessPolicy=\"Read, Script\">\n                <!--                <add name=\"WebDAV\" path=\"*\" verb=\"PROPFIND,PROPPATCH,MKCOL,PUT,COPY,DELETE,MOVE,LOCK,UNLOCK\" modules=\"WebDAVModule\" resourceType=\"Unspecified\" requireAccess=\"None\" /> -->\n                <add name=\"AXD-ISAPI-4.0_64bit\" path=\"*.axd\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"PageHandlerFactory-ISAPI-4.0_64bit\" path=\"*.aspx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"SimpleHandlerFactory-ISAPI-4.0_64bit\" path=\"*.ashx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"WebServiceHandlerFactory-ISAPI-4.0_64bit\" path=\"*.asmx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"HttpRemotingHandlerFactory-rem-ISAPI-4.0_64bit\" path=\"*.rem\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"HttpRemotingHandlerFactory-soap-ISAPI-4.0_64bit\" path=\"*.soap\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"svc-ISAPI-4.0_64bit\" path=\"*.svc\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" />\n                <add name=\"rules-ISAPI-4.0_64bit\" path=\"*.rules\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" />\n                <add name=\"xoml-ISAPI-4.0_64bit\" path=\"*.xoml\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" />\n                <add name=\"xamlx-ISAPI-4.0_64bit\" path=\"*.xamlx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" />\n                <add name=\"aspq-ISAPI-4.0_64bit\" path=\"*.aspq\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"cshtm-ISAPI-4.0_64bit\" path=\"*.cshtm\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"cshtml-ISAPI-4.0_64bit\" path=\"*.cshtml\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"vbhtm-ISAPI-4.0_64bit\" path=\"*.vbhtm\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"vbhtml-ISAPI-4.0_64bit\" path=\"*.vbhtml\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"svc-Integrated\" path=\"*.svc\" verb=\"*\" type=\"System.ServiceModel.Activation.HttpHandler, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" preCondition=\"integratedMode,runtimeVersionv2.0\" />\n                <add name=\"svc-ISAPI-2.0\" path=\"*.svc\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness32\" />\n                <add name=\"xoml-Integrated\" path=\"*.xoml\" verb=\"*\" type=\"System.ServiceModel.Activation.HttpHandler, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" preCondition=\"integratedMode,runtimeVersionv2.0\" />\n                <add name=\"xoml-ISAPI-2.0\" path=\"*.xoml\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness32\" />\n                <add name=\"rules-Integrated\" path=\"*.rules\" verb=\"*\" type=\"System.ServiceModel.Activation.HttpHandler, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" preCondition=\"integratedMode,runtimeVersionv2.0\" />\n                <add name=\"rules-ISAPI-2.0\" path=\"*.rules\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness32\" />\n                <add name=\"AXD-ISAPI-4.0_32bit\" path=\"*.axd\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"PageHandlerFactory-ISAPI-4.0_32bit\" path=\"*.aspx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"SimpleHandlerFactory-ISAPI-4.0_32bit\" path=\"*.ashx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"WebServiceHandlerFactory-ISAPI-4.0_32bit\" path=\"*.asmx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"HttpRemotingHandlerFactory-rem-ISAPI-4.0_32bit\" path=\"*.rem\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"HttpRemotingHandlerFactory-soap-ISAPI-4.0_32bit\" path=\"*.soap\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"svc-ISAPI-4.0_32bit\" path=\"*.svc\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" />\n                <add name=\"rules-ISAPI-4.0_32bit\" path=\"*.rules\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" />\n                <add name=\"xoml-ISAPI-4.0_32bit\" path=\"*.xoml\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" />\n                <add name=\"xamlx-ISAPI-4.0_32bit\" path=\"*.xamlx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" />\n                <add name=\"aspq-ISAPI-4.0_32bit\" path=\"*.aspq\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"cshtm-ISAPI-4.0_32bit\" path=\"*.cshtm\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"cshtml-ISAPI-4.0_32bit\" path=\"*.cshtml\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"vbhtm-ISAPI-4.0_32bit\" path=\"*.vbhtm\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"vbhtml-ISAPI-4.0_32bit\" path=\"*.vbhtml\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"TraceHandler-Integrated-4.0\" path=\"trace.axd\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.Handlers.TraceHandler\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"WebAdminHandler-Integrated-4.0\" path=\"WebAdmin.axd\" verb=\"GET,DEBUG\" type=\"System.Web.Handlers.WebAdminHandler\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"AssemblyResourceLoader-Integrated-4.0\" path=\"WebResource.axd\" verb=\"GET,DEBUG\" type=\"System.Web.Handlers.AssemblyResourceLoader\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"PageHandlerFactory-Integrated-4.0\" path=\"*.aspx\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.UI.PageHandlerFactory\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"SimpleHandlerFactory-Integrated-4.0\" path=\"*.ashx\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.UI.SimpleHandlerFactory\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"WebServiceHandlerFactory-Integrated-4.0\" path=\"*.asmx\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"HttpRemotingHandlerFactory-rem-Integrated-4.0\" path=\"*.rem\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"HttpRemotingHandlerFactory-soap-Integrated-4.0\" path=\"*.soap\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"svc-Integrated-4.0\" path=\"*.svc\" verb=\"*\" type=\"System.ServiceModel.Activation.ServiceHttpHandlerFactory, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"rules-Integrated-4.0\" path=\"*.rules\" verb=\"*\" type=\"System.ServiceModel.Activation.ServiceHttpHandlerFactory, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"xoml-Integrated-4.0\" path=\"*.xoml\" verb=\"*\" type=\"System.ServiceModel.Activation.ServiceHttpHandlerFactory, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"xamlx-Integrated-4.0\" path=\"*.xamlx\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Xaml.Hosting.XamlHttpHandlerFactory, System.Xaml.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"aspq-Integrated-4.0\" path=\"*.aspq\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.HttpForbiddenHandler\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"cshtm-Integrated-4.0\" path=\"*.cshtm\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.HttpForbiddenHandler\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"cshtml-Integrated-4.0\" path=\"*.cshtml\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.HttpForbiddenHandler\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"vbhtm-Integrated-4.0\" path=\"*.vbhtm\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.HttpForbiddenHandler\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"vbhtml-Integrated-4.0\" path=\"*.vbhtml\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.HttpForbiddenHandler\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"ScriptHandlerFactoryAppServices-Integrated-4.0\" path=\"*_AppService.axd\" verb=\"*\" type=\"System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"ScriptResourceIntegrated-4.0\" path=\"*ScriptResource.axd\" verb=\"GET,HEAD\" type=\"System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35\" preCondition=\"integratedMode,runtimeVersionv4.0\" />\n                <add name=\"ASPClassic\" path=\"*.asp\" verb=\"GET,HEAD,POST\" modules=\"IsapiModule\" scriptProcessor=\"%IIS_BIN%\\asp.dll\" resourceType=\"File\" />\n                <add name=\"SecurityCertificate\" path=\"*.cer\" verb=\"GET,HEAD,POST\" modules=\"IsapiModule\" scriptProcessor=\"%IIS_BIN%\\asp.dll\" resourceType=\"File\" />\n                <add name=\"ISAPI-dll\" path=\"*.dll\" verb=\"*\" modules=\"IsapiModule\" resourceType=\"File\" requireAccess=\"Execute\" allowPathInfo=\"true\" />\n                <add name=\"TraceHandler-Integrated\" path=\"trace.axd\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.Handlers.TraceHandler\" preCondition=\"integratedMode,runtimeVersionv2.0\" />\n                <add name=\"WebAdminHandler-Integrated\" path=\"WebAdmin.axd\" verb=\"GET,DEBUG\" type=\"System.Web.Handlers.WebAdminHandler\" preCondition=\"integratedMode,runtimeVersionv2.0\" />\n                <add name=\"AssemblyResourceLoader-Integrated\" path=\"WebResource.axd\" verb=\"GET,DEBUG\" type=\"System.Web.Handlers.AssemblyResourceLoader\" preCondition=\"integratedMode,runtimeVersionv2.0\" />\n                <add name=\"PageHandlerFactory-Integrated\" path=\"*.aspx\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.UI.PageHandlerFactory\" preCondition=\"integratedMode,runtimeVersionv2.0\" />\n                <add name=\"SimpleHandlerFactory-Integrated\" path=\"*.ashx\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.UI.SimpleHandlerFactory\" preCondition=\"integratedMode,runtimeVersionv2.0\" />\n                <add name=\"WebServiceHandlerFactory-Integrated\" path=\"*.asmx\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.Services.Protocols.WebServiceHandlerFactory,System.Web.Services,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a\" preCondition=\"integratedMode,runtimeVersionv2.0\" />\n                <add name=\"HttpRemotingHandlerFactory-rem-Integrated\" path=\"*.rem\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory,System.Runtime.Remoting,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089\" preCondition=\"integratedMode,runtimeVersionv2.0\" />\n                <add name=\"HttpRemotingHandlerFactory-soap-Integrated\" path=\"*.soap\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory,System.Runtime.Remoting,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089\" preCondition=\"integratedMode,runtimeVersionv2.0\" />\n                <add name=\"AXD-ISAPI-2.0\" path=\"*.axd\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"PageHandlerFactory-ISAPI-2.0\" path=\"*.aspx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"SimpleHandlerFactory-ISAPI-2.0\" path=\"*.ashx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"WebServiceHandlerFactory-ISAPI-2.0\" path=\"*.asmx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"HttpRemotingHandlerFactory-rem-ISAPI-2.0\" path=\"*.rem\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"HttpRemotingHandlerFactory-soap-ISAPI-2.0\" path=\"*.soap\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"svc-ISAPI-2.0-64\" path=\"*.svc\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness64\" />\n                <add name=\"AXD-ISAPI-2.0-64\" path=\"*.axd\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"PageHandlerFactory-ISAPI-2.0-64\" path=\"*.aspx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"SimpleHandlerFactory-ISAPI-2.0-64\" path=\"*.ashx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"WebServiceHandlerFactory-ISAPI-2.0-64\" path=\"*.asmx\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"HttpRemotingHandlerFactory-rem-ISAPI-2.0-64\" path=\"*.rem\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"HttpRemotingHandlerFactory-soap-ISAPI-2.0-64\" path=\"*.soap\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"rules-64-ISAPI-2.0\" path=\"*.rules\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness64\" />\n                <add name=\"xoml-64-ISAPI-2.0\" path=\"*.xoml\" verb=\"*\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v2.0.50727\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv2.0,bitness64\" />\n                <add name=\"CGI-exe\" path=\"*.exe\" verb=\"*\" modules=\"CgiModule\" resourceType=\"File\" requireAccess=\"Execute\" allowPathInfo=\"true\" />\n                <add name=\"SSINC-stm\" path=\"*.stm\" verb=\"GET,HEAD,POST\" modules=\"ServerSideIncludeModule\" resourceType=\"File\" />\n                <add name=\"SSINC-shtm\" path=\"*.shtm\" verb=\"GET,HEAD,POST\" modules=\"ServerSideIncludeModule\" resourceType=\"File\" />\n                <add name=\"SSINC-shtml\" path=\"*.shtml\" verb=\"GET,HEAD,POST\" modules=\"ServerSideIncludeModule\" resourceType=\"File\" />\n                <add name=\"TRACEVerbHandler\" path=\"*\" verb=\"TRACE\" modules=\"ProtocolSupportModule\" requireAccess=\"None\" />\n                <add name=\"OPTIONSVerbHandler\" path=\"*\" verb=\"OPTIONS\" modules=\"ProtocolSupportModule\" requireAccess=\"None\" />\n                <add name=\"ExtensionlessUrl-ISAPI-4.0_32bit\" path=\"*.\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness32\" responseBufferLimit=\"0\" />\n                <add name=\"ExtensionlessUrlHandler-ISAPI-4.0_64bit\" path=\"*.\" verb=\"GET,HEAD,POST,DEBUG\" modules=\"IsapiModule\" scriptProcessor=\"%windir%\\Microsoft.NET\\Framework64\\v4.0.30319\\aspnet_isapi.dll\" preCondition=\"classicMode,runtimeVersionv4.0,bitness64\" responseBufferLimit=\"0\" />\n                <add name=\"ExtensionlessUrl-Integrated-4.0\" path=\"*.\" verb=\"GET,HEAD,POST,DEBUG\" type=\"System.Web.Handlers.TransferRequestHandler\" preCondition=\"integratedMode,runtimeVersionv4.0\" responseBufferLimit=\"0\" />\n                <add name=\"StaticFile\" path=\"*\" verb=\"*\" modules=\"StaticFileModule,DefaultDocumentModule,DirectoryListingModule\" resourceType=\"Either\" requireAccess=\"Read\" />\n            </handlers>\n        </system.webServer>\n    </location>\n</configuration>\n"
  },
  {
    "path": "TLM/ATTACHING_DEBUGGER.md",
    "content": "# Attaching Debugger to Cities.exe\n\nUse this guide to attach a debugger to Cities: Skylines.\n\n> **Notes:**\n> * Attaching a debugger can significantly reduce frame rate and cause lots of lag.\n> * This has only been tested on Windows.\n\n### Setup\n\n> You only need to follow these steps once to set up your debug environment.\n\nFirst, let's backup your current ```mono.dll```:\n\n* Navigate to ```<%STEAM%>\\steamapps\\common\\Cities_Skylines\\Cities_Data\\Mono\\```\n* Make a backup of ```mono.dll``` (you could just rename it ```mono-backup.dll```)\n\n> The location of ```<%STEAM%>``` is usually ```C:\\Program Files (x86)\\Steam```\n\nNext, download the following files from [```https://github.com/0xd4d/dnSpy/releases```](https://github.com/0xd4d/dnSpy/releases):\n\n* ```dnSpy-net472.zip```\n* ```Unity-debugging-5.x.zip```\n\nNow we apply a new ```mono.dll``` and test the game is working:\n\n* Make sure the game is **not** running\n* Open ```Unity-debugging-5.x.zip```:\n    * Navigate to ```Unity-debugging\\unity-5.6.6\\win64\\``` (note: **```unity-5.6.6```**)\n    * Copy ```mono.dll``` to ```<%STEAM%>\\steamapps\\common\\Cities_Skylines\\Cities_Data\\Mono\\```\n* Run the game to check if it's working:\n    * If not, delete the downloaded ```mono.dll``` then restore the original version\n    * You'll have to scour the internet to work out what went wrong, sorry.\n* Close the game\n\nNext, add two environment variables:\n\n* Press **Win+R** (_Run dialog appears_):\n    * Enter ```sysdm.cpl```\n    * Choose **OK**\n* On the **Advanced** tab, choose **Environment Variables...**\n* The variables to add are shown below:\n\n1. > **key:** ```DNSPY_UNITY_DBG```  \n   > **value:** ```--debugger-agent=transport=dt_socket,server=y,address=127.0.0.1:55555,defer=y,no-hide-debugger```\n2. > **key:** ```DNSPY_UNITY_DBG2```  \n   > **value:** ```--debugger-agent=transport=dt_socket,server=y,address=127.0.0.1:55555,suspend=n,no-hide-debugger```\n\nFinally, unarchive **dnSpy**:\n\n* Extract the downloaded ```dnSpy-net472.zip``` to a folder\n    * It can be anywhere, eg. ```dnSpy/``` on your desktop\n\n### Debugging\n\n> Do this each time you want to debug the game.\n\nFirst, launch **dnSpy**:\n\n* Run ```dnSpy.exe```\n* On the left, in **Assembly Explorer**, remove any ```.dll``` files that are listed\n    * Tip: Select one, press **Ctrl+A** then **Delete**\n\nNow run the game and attach **dnSpy**:\n\n* Start ```Cities.exe```\n* **Alt+Tab** to **dnSpy** app\n* Press **F5** (or choose **Start**) and select:\n    * **Debug Engine:** ```Unity (Connect)```\n    * **Port:** ```55555```\n* Click **OK**:\n    * You should see an **orange status bar** at the bottom of application with text: ```Running...```\n* From the **Debug** menu, choose **Windows -> Modules** _(Ctrl+Shift+A)_\n* You should see lots of ```.dll``` files and some ```data-00...``` entries\n* **Right-click** on any of them, select **Open All Modules**, then * Click **OK**\n    * The game may hang for few seconds\n* On the left, in **Assembly Explorer**, you should see all ```.dll``` files loaded in-game\n    * There will be some duplicates\n* **Right-click** on any of them, then **Sort Assemblies** to make the list easier to work with\n\nThat's it, you are debugging. Now your mods are sure to be bugless :P \n\n### Reverting\n\nIf you want to return the game back to normal:\n\n* Exit the game\n* Replace the downloaded ```mono.dll`` with your backup of the original ```mono.dll```\n* Start the game\n\nI'm sure you can work out how to simplify or automate toggling between the two ```mono.dll``` files :)\n\n### Tips\n\n* Use **Search** tab _(Ctrl+Shift+K)_ for to find class, property, field, method, etc...\n* You can right-click a method definition then select **Analyze** to see where it's used\n\n### Notes\n\n* I have no idea why there are duplicated libraries (some sort of protection?)\n* Only one copy of each library will have working breakpoints\n    * After sorting assemblies, it's usually the first instance of a listed file\n    * Once you know which one it is, you can safely remove the other from Assembly Explorer\n* Don't rebuild your mod library with game running, otherwise you'll have to clear Assembly Explorer and open the modules again, which means the duplicates come back\n"
  },
  {
    "path": "TLM/BUILDING_INSTRUCTIONS.md",
    "content": "\n\n# Project building instructions\n\n\n#### Prerequisites:\n* [Git for Windows](https://gitforwindows.org/) / [GitHub Desktop](https://desktop.github.com/)\n* one prefered __IDE__ _(Integrated Development Environment)_ to build project:\n  * Visual Studio 2017 Community (free)\n  * JetBrains Rider (paid)\n  * or other similar...\n* sources of this repository: \n  * Git for Windows console:\n    * use ```git clone https://github.com/krzychu124/Cities-Skylines-Traffic-Manager-President-Edition``` to download sources of this repository\n    * after successful cloning type ```cd Cities-Skylines-Traffic-Manager-President-Edition```\n    * then ```git submodule update --init --recursive``` to fetch dependencies\n  * Github desktop:\n    * clone repository using this link ```https://github.com/krzychu124/Cities-Skylines-Traffic-Manager-President-Edition.git```\n    * dependencies should be installed automatically\n    \n## To build project follow actions:\n\n\nOpen __TMPL.sln__ located at ``` <Your cloned source code folder>\\TLM\\``` using preferred __IDE__.\n\n##### Visual Studio:\n\n * use dropdown from __actions bar__ _(dropdown located under Team menu)_ to select _desired_ solution configuration\n * fix missing libraries locations - inside __Solution Explorer__ right click on every project and select __Properties__\n then __Reference Paths__ and add __Managed__ folder from game directory located under ```<game_dir>\\Cities_Data\\Managed```\n * to build project with selected configuration choose one of actions:\n   * right click on __TLM__ project from __Solution Explorer__\n   * use _Build_ menu -> _Build Solution_ __F6__\n\n##### JetBrains Rider:\n * currently there is no GUI for adding Reference Paths so you have to create config for every project inside from scratch: \n \n   1. Create file with extension ```*.csproj.user```named as project name e.g. ```TLM.csproj.user```\n   2. Paste below code inside newly created file and replace ```<full_url_to_game_location>``` with correct path\n   ```\n    <?xml version=\"1.0\" encoding=\"utf-8\"?>\n    <Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n      <PropertyGroup>\n        <ReferencePath><full_url_to_game_location>\\Cities_Data\\Managed\\</ReferencePath>\n      </PropertyGroup>\n    </Project> \n   ```\n \n * select configuration using dropdown located on _actions bar_\n * use __Ctrl+F9__ to build solution, or __right click__ on solution inside __File Explorer (Alt+1)__\n"
  },
  {
    "path": "TLM/CSUtil.Commons/ArrowDirection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace CSUtil.Commons {\n\tpublic enum ArrowDirection {\n\t\tNone = 0,\n\t\tLeft = 1,\n\t\tForward = 2,\n\t\tRight = 3,\n\t\tTurn = 4\n\t}\n}\n"
  },
  {
    "path": "TLM/CSUtil.Commons/ArrowDirectionUtil.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace CSUtil.Commons {\n\tpublic class ArrowDirectionUtil {\n\t\tpublic static ArrowDirection InvertLeftRight(ArrowDirection dir) {\n\t\t\tif (dir == ArrowDirection.Left)\n\t\t\t\tdir = ArrowDirection.Right;\n\t\t\telse if (dir == ArrowDirection.Right)\n\t\t\t\tdir = ArrowDirection.Left;\n\t\t\treturn dir;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calculates the direction of <paramref name=\"toRelDir\"/> in relation to ArrowDirection.TURN.\n\t\t/// </summary>\n\t\t/// <param name=\"fromDir\">source direction</param>\n\t\t/// <param name=\"toRelDir\">target direction, relative to <paramref name=\"fromDir\"/></param>\n\t\t/// <returns></returns>\n\t\tpublic static ArrowDirection MakeAbsolute(ArrowDirection fromDir, ArrowDirection toRelDir) {\n\t\t\tif (fromDir == ArrowDirection.None) {\n\t\t\t\t// invalid direction\n\t\t\t\treturn ArrowDirection.None;\n\t\t\t}\n\n\t\t\tif (toRelDir == ArrowDirection.None) {\n\t\t\t\t// invalid direction\n\t\t\t\treturn ArrowDirection.None;\n\t\t\t}\n\n\t\t\tif (fromDir == ArrowDirection.Turn) {\n\t\t\t\t// toRelDir is already relative to TURN\n\t\t\t\treturn toRelDir;\n\t\t\t}\n\n\t\t\tif (toRelDir == ArrowDirection.Turn) {\n\t\t\t\t// toRelDir is fromDir\n\t\t\t\treturn fromDir;\n\t\t\t}\n\n\t\t\tint fromDirBase0 = (int)fromDir - 1;\n\t\t\tint toRelDirBase1 = (int)toRelDir;\n\t\t\t/*\n\t\t\t * Direction | Base 0 | Base 1\n\t\t\t * ==========+========+=======\n\t\t\t * Left      | 0      | 1\n\t\t\t * Forward   | 1      | 2\n\t\t\t * Right     | 2      | 3\n\t\t\t * \n\t\t\t *\n\t\t\t * Direction 1 | Direction 2 | Dir. 1 B0 | Dir. 2 B1 | Sum | (Sum + 1) % 4 | Desired dir.\n\t\t\t * ============+=============+===========+===========+=====|===============+=============\n\t\t\t * Left        | Left        | 0         | 1         | 1   | 2             | (Forward, 2)\n\t\t\t * Left        | Forward     | 0         | 2         | 2   | 3             | (Right, 3)\n\t\t\t * Left        | Right       | 0         | 3         | 3   | 0             | (Turn, 4)\n\t\t\t * Forward     | Left        | 1         | 1         | 2   | 3             | (Right, 3)\n\t\t\t * Forward     | Forward     | 1         | 2         | 3   | 0             | (Turn, 4)\n\t\t\t * Forward     | Right       | 1         | 3         | 4   | 1             | (Left, 1)\n\t\t\t * Right       | Left        | 2         | 1         | 3   | 0             | (Turn, 4)\n\t\t\t * Right       | Forward     | 2         | 2         | 4   | 1             | (Left, 1)\n\t\t\t * Right       | Right       | 2         | 3         | 5   | 2             | (Forward, 2)\n\t\t\t */\n\n\t\t\tint ret = (fromDirBase0 + toRelDirBase1 + 1) % 4;\n\t\t\tif (ret == 0) {\n\t\t\t\tret = 4;\n\t\t\t}\n\t\t\treturn (ArrowDirection)ret;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/CSUtil.Commons/Benchmark/Benchmark.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\n\nnamespace CSUtil.Commons.Benchmark {\n\tpublic class Benchmark : IDisposable {\n\t\tprivate BenchmarkProfile profile;\n\n\t\tpublic Benchmark(string id = null, string postfix = null) {\n\t\t\tif (id == null) {\n\t\t\t\tStackFrame frame = new StackFrame(1);\n\t\t\t\tMethodBase method = frame.GetMethod();\n\t\t\t\tid = method.DeclaringType.Name + \"#\" + method.Name;\n\t\t\t}\n\n\t\t\tif (postfix != null) {\n\t\t\t\tid += \"#\" + postfix;\n\t\t\t}\n\n\t\t\tprofile = BenchmarkProfileProvider.Instance.GetProfile(id);\n\t\t\tprofile.Start();\n\t\t}\n\n\t\tpublic void Dispose() {\n\t\t\tprofile.Stop();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/CSUtil.Commons/Benchmark/BenchmarkProfile.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\n\nnamespace CSUtil.Commons.Benchmark {\n\tpublic class BenchmarkProfile {\n\t\tpublic string Id { get; private set; }\n\t\tprivate Stopwatch timer;\n\t\tpublic int NumBenchmarks { get; private set; } = 0;\n\n\t\tpublic BenchmarkProfile(string id) {\n\t\t\tId = id;\n\t\t\ttimer = new Stopwatch();\n\t\t}\n\n\t\tpublic void Start() {\n\t\t\ttimer.Start();\n\t\t}\n\n\t\tpublic void Stop() {\n\t\t\tif (timer.IsRunning) {\n\t\t\t\ttimer.Stop();\n\t\t\t\t++NumBenchmarks;\n\t\t\t}\n\t\t}\n\n\t\tpublic TimeSpan GetElapsedTime() {\n\t\t\treturn timer.Elapsed;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/CSUtil.Commons/Benchmark/BenchmarkProfileProvider.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace CSUtil.Commons.Benchmark {\n\tpublic class BenchmarkProfileProvider {\n\t\tpublic static readonly BenchmarkProfileProvider Instance = new BenchmarkProfileProvider();\n\n\t\tprivate IDictionary<string, BenchmarkProfile> Profiles = new Dictionary<string, BenchmarkProfile>();\n\n\t\tpublic BenchmarkProfile GetProfile(string id) {\n\t\t\tBenchmarkProfile profile = null;\n\t\t\tProfiles.TryGetValue(id, out profile);\n\t\t\tif (profile == null) {\n\t\t\t\tprofile = new BenchmarkProfile(id);\n\t\t\t\tProfiles.Add(id, profile);\n\t\t\t}\n\t\t\treturn profile;\n\t\t}\n\n\t\tpublic void ClearProfiles() {\n\t\t\tProfiles.Clear();\n\t\t}\n\n\t\tpublic string CreateReport() {\n\t\t\tstring ret = \"=== BENCHMARK REPORT ===\\n\";\n\n\t\t\tret += \"=== ORDERED BY TOTAL TIME ===\\n\";\n\t\t\tList<string> orderedKeys = new List<string>(Profiles.Keys);\n\t\t\torderedKeys.Sort(delegate (string x, string y) {\n\t\t\t\tlong xTicks = Profiles[x].GetElapsedTime().Ticks;\n\t\t\t\tlong yTicks = Profiles[y].GetElapsedTime().Ticks;\n\t\t\t\treturn yTicks.CompareTo(xTicks);\n\t\t\t});\n\t\t\tret = CreateReport(ret, orderedKeys);\n\n\t\t\tret += \"\\n=== ORDERED BY AVG. TIME ===\\n\";\n\t\t\torderedKeys = new List<string>(Profiles.Keys);\n\t\t\torderedKeys.Sort(delegate (string x, string y) {\n\t\t\t\tBenchmarkProfile xProfile = Profiles[x];\n\t\t\t\tBenchmarkProfile yProfile = Profiles[y];\n\t\t\t\tif (xProfile.NumBenchmarks <= 0 && yProfile.NumBenchmarks <= 0) {\n\t\t\t\t\treturn 0;\n\t\t\t\t} else if (xProfile.NumBenchmarks > 0 && yProfile.NumBenchmarks <= 0) {\n\t\t\t\t\treturn -1;\n\t\t\t\t} else if (xProfile.NumBenchmarks <= 0 && yProfile.NumBenchmarks > 0) {\n\t\t\t\t\treturn 1;\n\t\t\t\t} else {\n\t\t\t\t\tfloat xAvg = (float)xProfile.GetElapsedTime().TotalMilliseconds / (float)xProfile.NumBenchmarks;\n\t\t\t\t\tfloat yAvg = (float)yProfile.GetElapsedTime().TotalMilliseconds / (float)yProfile.NumBenchmarks;\n\t\t\t\t\treturn yAvg.CompareTo(xAvg);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tret = CreateReport(ret, orderedKeys);\n\t\t\treturn ret;\n\t\t}\n\n\t\tprivate string CreateReport(string ret, List<string> orderedKeys) {\n\t\t\tforeach (string key in orderedKeys) {\n\t\t\t\tBenchmarkProfile profile = Profiles[key];\n\t\t\t\tret += $\"\\t{key}: {profile.GetElapsedTime()} ({profile.NumBenchmarks} benchmarks, avg. {(profile.NumBenchmarks <= 0 ? 0f : (float)profile.GetElapsedTime().TotalMilliseconds / (float)profile.NumBenchmarks)})\\n\";\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/CSUtil.Commons/CSUtil.Commons.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"14.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{D3ADE06E-F493-4819-865A-3BB44FEEDF01}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>CSUtil.Commons</RootNamespace>\n    <AssemblyName>CSUtil.Commons</AssemblyName>\n    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Benchmark|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\Benchmark\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'PF2_Debug|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\PF2_Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"UnityEngine\">\n      <HintPath>..\\dependencies\\UnityEngine.dll</HintPath>\n    </Reference>\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"ArrowDirection.cs\" />\n    <Compile Include=\"ArrowDirectionUtil.cs\" />\n    <Compile Include=\"Benchmark\\BenchmarkProfileProvider.cs\" />\n    <Compile Include=\"EnumUtil.cs\" />\n    <Compile Include=\"Log.cs\" />\n    <Compile Include=\"LogicUtil.cs\" />\n    <Compile Include=\"Benchmark\\Benchmark.cs\" />\n    <Compile Include=\"Benchmark\\BenchmarkProfile.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"TernaryBool.cs\" />\n    <Compile Include=\"TernaryBoolUtil.cs\" />\n    <Compile Include=\"ToStringExt.cs\" />\n    <Compile Include=\"VectorUtil.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.CodeFixes.dll\" />\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.dll\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "TLM/CSUtil.Commons/EnumUtil.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace CSUtil.Commons {\n\tpublic static class EnumUtil {\n\t\tpublic static IEnumerable<T> GetValues<T>() {\n\t\t\treturn Enum.GetValues(typeof(T)).Cast<T>();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/CSUtil.Commons/Log.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Threading;\nusing UnityEngine;\n\nnamespace CSUtil.Commons {\n\n\tpublic static class Log {\n\t\tprivate enum LogLevel {\n\t\t\tDebug,\n\t\t\tInfo,\n\t\t\tWarning,\n\t\t\tError\n\t\t}\n\n\t\tprivate static object logLock = new object();\n\n\t\tprivate static string logFilename = Path.Combine(Application.dataPath, \"TMPE.log\"); // TODO refactor log filename to configuration\n\t\tprivate static Stopwatch sw = Stopwatch.StartNew();\n\n\t\tstatic Log() {\n\t\t\ttry {\n\t\t\t\tif (File.Exists(logFilename)) {\n\t\t\t\t\tFile.Delete(logFilename);\n\t\t\t\t}\n\t\t\t} catch (Exception) {\n\t\t\t\t\n\t\t\t}\n\t\t}\n\n\t\t[Conditional(\"DEBUG\")]\n\t\tpublic static void _Debug(string s) {\n\t\t\tLogToFile(s, LogLevel.Debug);\n\t\t}\n\n\t\tpublic static void Info(string s) {\n\t\t\tLogToFile(s, LogLevel.Info);\n\t\t}\n\n\t\tpublic static void Warning(string s) {\n\t\t\tLogToFile(s, LogLevel.Warning);\n\t\t}\n\n\t\tpublic static void Error(string s) {\n\t\t\tLogToFile(s, LogLevel.Error);\n\t\t}\n\n\t\tprivate static void LogToFile(string log, LogLevel level) {\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(logLock);\n\t\t\t\t\n\t\t\t\tusing (StreamWriter w = File.AppendText(logFilename)) {\n\t\t\t\t\tw.WriteLine($\"[{level.ToString()}] @ {sw.ElapsedTicks} {log}\");\n\t\t\t\t\tif (level == LogLevel.Warning || level == LogLevel.Error) {\n\t\t\t\t\t\tw.WriteLine((new System.Diagnostics.StackTrace()).ToString());\n\t\t\t\t\t\tw.WriteLine();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(logLock);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "TLM/CSUtil.Commons/LogicUtil.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace CSUtil.Commons {\n\tpublic static class LogicUtil {\n\t\tpublic static bool CheckFlags(uint flags, uint flagMask, uint? expectedResult=null) {\n\t\t\tuint res = flags & flagMask;\n\t\t\tif (expectedResult == null) {\n\t\t\t\treturn res != 0;\n\t\t\t} else {\n\t\t\t\treturn res == expectedResult;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/CSUtil.Commons/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following \n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"Util\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"Util\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2017\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible \n// to COM components.  If you need to access a type in this assembly from \n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"d3ade06e-f493-4819-865a-3bb44feedf01\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version \n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers \n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.*\")]\n"
  },
  {
    "path": "TLM/CSUtil.Commons/TernaryBool.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace CSUtil.Commons {\n\tpublic enum TernaryBool {\n\t\tUndefined = 0,\n\t\tFalse = 1,\n\t\tTrue = 2\n\t}\n}\n"
  },
  {
    "path": "TLM/CSUtil.Commons/TernaryBoolUtil.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace CSUtil.Commons {\n\tpublic static class TernaryBoolUtil {\n\t\tpublic static bool ToBool(TernaryBool tb) {\n\t\t\tif (tb == TernaryBool.Undefined) {\n\t\t\t\tthrow new ArgumentException(\"Cannot determine boolean value for undefined ternary bool\");\n\t\t\t}\n\n\t\t\treturn tb == TernaryBool.True;\n\t\t}\n\n\t\tpublic static bool? ToOptBool(TernaryBool tb) {\n\t\t\tif (tb == TernaryBool.Undefined) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn tb == TernaryBool.True;\n\t\t}\n\n\t\tpublic static TernaryBool ToTernaryBool(bool? b) {\n\t\t\tswitch (b) {\n\t\t\t\tcase null:\n\t\t\t\tdefault:\n\t\t\t\t\treturn TernaryBool.Undefined;\n\t\t\t\tcase false:\n\t\t\t\t\treturn TernaryBool.False;\n\t\t\t\tcase true:\n\t\t\t\t\treturn TernaryBool.True;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/CSUtil.Commons/ToStringExt.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace CSUtil.Commons {\n\tpublic static class ToStringExt {\n\t\tpublic static string DictionaryToString<K, V>(this IDictionary<K, V> element) {\n\t\t\treturn string.Join(\", \", element.Keys.Select(x => $\"{ToString(x)}={ToString(element[x])}\").ToArray());\n\t\t}\n\n\t\tpublic static string CollectionToString<T>(this ICollection<T> elements) {\n\t\t\treturn string.Join(\", \", elements.Select(x => ToString(x)).ToArray());\n\t\t}\n\n\t\tpublic static string ArrayToString<T>(this T[] elements) {\n\t\t\treturn string.Join(\", \", elements.Select(x => ToString(x)).ToArray());\n\t\t}\n\n\t\tpublic static string ToString(object obj) {\n\t\t\treturn obj == null ? \"<null>\" : obj.ToString();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/CSUtil.Commons/VectorUtil.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing UnityEngine;\n\nnamespace CSUtil.Commons {\n\tpublic static class VectorUtil {\n\t\tpublic static void ClampRectToScreen(ref Rect rect, Vector2 resolution) {\n\t\t\tLog._Debug($\"ClampPosToScreen([{rect.x}, {rect.y}, {rect.xMax}, {rect.yMax}], [{resolution.x}, {resolution.y}]) called\");\n\t\t\tif (rect.x < 0)\n\t\t\t\trect.x = 0;\n\t\t\tif (rect.y < 0)\n\t\t\t\trect.y = 0;\n\t\t\tif (rect.xMax >= resolution.x)\n\t\t\t\trect.x = resolution.x - rect.width;\n\t\t\tif (rect.yMax >= resolution.y)\n\t\t\t\trect.y = resolution.y - rect.height;\n\t\t\tLog._Debug($\"ClampPosToScreen() -> [{rect.x}, {rect.y}, {rect.xMax}, {rect.yMax}]\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/CSUtil.Commons/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"StyleCop.Analyzers\" version=\"1.0.2\" targetFramework=\"net35\" developmentDependency=\"true\" />\n</packages>"
  },
  {
    "path": "TLM/CSUtil.Redirection/CSUtil.Redirection.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProductVersion>8.0.30703</ProductVersion>\n    <SchemaVersion>2.0</SchemaVersion>\n    <ProjectGuid>{7DCC08EF-DC85-47A4-BD6F-79FC52C7EF13}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>CSUtil.Redirection</RootNamespace>\n    <AssemblyName>CSUtil.Redirection</AssemblyName>\n    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Benchmark|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\Benchmark\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"RedirectionHelper.cs\" />\n    <Compile Include=\"Redirector.cs\" />\n    <Compile Include=\"MethodInfoExt.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\CSUtil.Commons\\CSUtil.Commons.csproj\">\n      <Project>{D3ADE06E-F493-4819-865A-3BB44FEEDF01}</Project>\n      <Name>CSUtil.Commons</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "TLM/CSUtil.Redirection/MethodInfoExt.cs",
    "content": "﻿using System;\nusing System.Reflection;\n\nnamespace CSUtil.Redirection {\n    public static class MethodInfoExt\n    {\n        internal static Redirector.MethodRedirection RedirectTo(this MethodInfo originalMethod, MethodInfo newMethod, Assembly redirectionSource)\n        {\n            return new Redirector.MethodRedirection(originalMethod, newMethod, redirectionSource);\n        }\n\n        public static bool IsCompatibleWith(this MethodInfo thisMethod, MethodInfo otherMethod)\n        {\n            if (thisMethod.ReturnType != otherMethod.ReturnType)\n                return false;\n\n            ParameterInfo[] thisParameters = thisMethod.GetParameters();\n            ParameterInfo[] otherParameters = otherMethod.GetParameters();\n\n            if (thisParameters.Length != otherParameters.Length)\n                return false;\n\n            for (int i = 0; i < thisParameters.Length; i++)\n            {\n                if (!otherParameters[i].ParameterType.IsAssignableFrom(thisParameters[i].ParameterType))\n                {\n                    return false;\n                }\n            }\n\n            return true;\n        }\n    }\n}"
  },
  {
    "path": "TLM/CSUtil.Redirection/NetworkExtensions.Framework.Unsafe.csproj.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=_005FExtensions/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "TLM/CSUtil.Redirection/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following \n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"Transit.Framework.Redirection\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"Transit.Framework.Redirection\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2015\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible \n// to COM components.  If you need to access a type in this assembly from \n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"4250c75b-3c80-494f-a5c9-7e49c18ef1bc\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version \n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers \n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.*\")]\n"
  },
  {
    "path": "TLM/CSUtil.Redirection/RedirectionHelper.cs",
    "content": "﻿/*\nThe MIT License (MIT)\nCopyright (c) 2015 Sebastian Schöner\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\nusing System;\nusing System.Reflection;\n\nnamespace CSUtil.Redirection {\n\n    public struct RedirectCallsState\n    {\n        public byte a, b, c, d, e;\n        public ulong f;\n    }\n\n    /// <summary>\n    /// Helper class to deal with detours. This version is for Unity 5 x64 on Windows.\n    /// We provide three different methods of detouring.\n    /// </summary>\n    public static class RedirectionHelper\n    {\n        /// <summary>\n        /// Redirects all calls from method 'from' to method 'to'.\n        /// </summary>\n        /// <param name=\"from\"></param>\n        /// <param name=\"to\"></param>\n        public static RedirectCallsState RedirectCalls(MethodInfo from, MethodInfo to)\n        {\n            // GetFunctionPointer enforces compilation of the method.\n            var fptr1 = from.MethodHandle.GetFunctionPointer();\n            var fptr2 = to.MethodHandle.GetFunctionPointer();\n            return PatchJumpTo(fptr1, fptr2);\n        }\n\n        public static RedirectCallsState RedirectCalls(RuntimeMethodHandle from, RuntimeMethodHandle to)\n        {\n            // GetFunctionPointer enforces compilation of the method.\n            var fptr1 = from.GetFunctionPointer();\n            var fptr2 = to.GetFunctionPointer();\n            return PatchJumpTo(fptr1, fptr2);\n        }\n\n        public static void RevertRedirect(MethodInfo from, RedirectCallsState state)\n        {\n            try\n            {\n                var fptr1 = from.MethodHandle.GetFunctionPointer();\n                RevertJumpTo(fptr1, state);\n            }\n            catch\n            {\n                // ignored\n            }\n        }\n\n        /// <summary>\n        /// Primitive patching. Inserts a jump to 'target' at 'site'. Works even if both methods'\n        /// callers have already been compiled.\n        /// </summary>\n        /// <param name=\"site\"></param>\n        /// <param name=\"target\"></param>\n        public static RedirectCallsState PatchJumpTo(IntPtr site, IntPtr target)\n        {\n            RedirectCallsState state = new RedirectCallsState();\n\n            // R11 is volatile.\n            unsafe\n            {\n                byte* sitePtr = (byte*)site.ToPointer();\n                state.a = *sitePtr;\n                state.b = *(sitePtr + 1);\n                state.c = *(sitePtr + 10);\n                state.d = *(sitePtr + 11);\n                state.e = *(sitePtr + 12);\n                state.f = *((ulong*)(sitePtr + 2));\n\n                *sitePtr = 0x49; // mov r11, target\n                *(sitePtr + 1) = 0xBB;\n                *((ulong*)(sitePtr + 2)) = (ulong)target.ToInt64();\n                *(sitePtr + 10) = 0x41; // jmp r11\n                *(sitePtr + 11) = 0xFF;\n                *(sitePtr + 12) = 0xE3;\n            }\n\n            return state;\n        }\n\n        public static void RevertJumpTo(IntPtr site, RedirectCallsState state)\n        {\n            unsafe\n            {\n                byte* sitePtr = (byte*)site.ToPointer();\n                *sitePtr = state.a; // mov r11, target\n                *(sitePtr + 1) = state.b;\n                *((ulong*)(sitePtr + 2)) = state.f;\n                *(sitePtr + 10) = state.c; // jmp r11\n                *(sitePtr + 11) = state.d;\n                *(sitePtr + 12) = state.e;\n            }\n        }\n\n    }\n}"
  },
  {
    "path": "TLM/CSUtil.Redirection/Redirector.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\n\nnamespace CSUtil.Redirection {\n    public abstract class RedirectAttribute : Attribute\n    {\n        public RedirectAttribute(Type classType, string methodName, ulong bitSetOption = 0)\n        {\n            ClassType = classType;\n            MethodName = methodName;\n            BitSetRequiredOption = bitSetOption;\n        }\n\n        public RedirectAttribute(Type classType, ulong bitSetOption = 0)\n            : this(classType, null, bitSetOption)\n        { }\n\n        public Type ClassType { get; set; }\n        public string MethodName { get; set; }\n        public ulong BitSetRequiredOption { get; set; }\n    }\n\n    /// <summary>\n    /// Marks a method for redirection. All marked methods are redirected by calling\n    /// <see cref=\"Redirector.PerformRedirections\"/> and reverted by <see cref=\"Redirector.RevertRedirections\"/>\n    /// <para>NOTE: only the methods belonging to the same assembly that calls Perform/RevertRedirections are redirected.</para>\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]\n    public class RedirectFromAttribute : RedirectAttribute\n    {\n        /// <param name=\"classType\">The class of the method that will be redirected</param>\n        /// <param name=\"methodName\">The name of the method that will be redirected. If null,\n        /// the name of the attribute's target method will be used.</param>\n        public RedirectFromAttribute(Type classType, string methodName, ulong bitSetOption = 0)\n            : base(classType, methodName, bitSetOption)\n        { }\n\n        public RedirectFromAttribute(Type classType, ulong bitSetOption = 0)\n            : base(classType, bitSetOption)\n        { }\n    }\n\n    /// <summary>\n    /// Marks a method for redirection. All marked methods are redirected by calling\n    /// <see cref=\"Redirector.PerformRedirections\"/> and reverted by <see cref=\"Redirector.RevertRedirections\"/>\n    /// <para>NOTE: only the methods belonging to the same assembly that calls Perform/RevertRedirections are redirected.</para>\n    /// </summary>\n    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]\n    public class RedirectToAttribute : RedirectAttribute\n    {\n        /// <param name=\"classType\">The class of the target method</param>\n        /// <param name=\"methodName\">The name of the target method. If null,\n        /// the name of the attribute's target method will be used.</param>\n        public RedirectToAttribute(Type classType, string methodName, ulong bitSetOption = 0)\n            : base(classType, methodName, bitSetOption)\n        { }\n\n        public RedirectToAttribute(Type classType, ulong bitSetOption = 0)\n            : base(classType, bitSetOption)\n        { }\n    }\n\n    public static class Redirector\n    {\n        internal class MethodRedirection : IDisposable\n        {\n            private bool _isDisposed = false;\n\n            private MethodInfo _originalMethod;\n            private readonly RedirectCallsState _callsState;\n            public Assembly RedirectionSource { get; set; }\n\n            public MethodRedirection(MethodInfo originalMethod, MethodInfo newMethod, Assembly redirectionSource)\n            {\n                _originalMethod = originalMethod;\n                _callsState = RedirectionHelper.RedirectCalls(_originalMethod, newMethod);\n                RedirectionSource = redirectionSource;\n            }\n\n            public void Dispose()\n            {\n                if (!_isDisposed)\n                {\n                    RedirectionHelper.RevertRedirect(_originalMethod, _callsState);\n                    _originalMethod = null;\n                    _isDisposed = true;\n                }\n            }\n\n            public MethodInfo OriginalMethod\n            {\n                get\n                {\n                    return _originalMethod;\n                }\n            }\n        }\n\n        private static List<MethodRedirection> s_redirections = new List<MethodRedirection>();\n\n        public static void PerformRedirections(ulong bitMask = 0)\n        {\n            Assembly callingAssembly = Assembly.GetCallingAssembly();\n\n            IEnumerable<MethodInfo> methods = from type in callingAssembly.GetTypes()\n                                              from method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)\n                                              where method.GetCustomAttributes(typeof(RedirectAttribute), false).Length > 0\n                                              select method;\n\n            foreach (MethodInfo method in methods)\n            {\n                foreach (RedirectAttribute redirectAttr in method.GetCustomAttributes(typeof(RedirectAttribute), false))\n                {\n                    if (redirectAttr.BitSetRequiredOption != 0 && (bitMask & redirectAttr.BitSetRequiredOption) == 0)\n                        continue;\n\n                    string originalName = String.IsNullOrEmpty(redirectAttr.MethodName) ? method.Name : redirectAttr.MethodName;\n\n                    MethodInfo originalMethod = null;\n                    foreach (MethodInfo m in redirectAttr.ClassType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))\n                    {\n                        if (m.Name != originalName)\n                            continue;\n\n                        if (method.IsCompatibleWith(m))\n                        {\n                            originalMethod = m;\n                            break;\n                        }\n                    }\n\n                    if (originalMethod == null)\n                    {\n                        throw new Exception(string.Format(\"Redirector: Original method {0} has not been found for redirection\", originalName));\n                    }\n\n                    if (redirectAttr is RedirectFromAttribute)\n                    {\n                        if (!s_redirections.Any(r => r.OriginalMethod == originalMethod))\n                        {\n                            Log.Info(string.Format(\"Redirector: Detouring method calls from {0}.{1} to {2}.{3} via RedirectFrom\",\n                                originalMethod.DeclaringType,\n                                originalMethod.Name,\n                                method.DeclaringType,\n                                method.Name));\n                            s_redirections.Add(originalMethod.RedirectTo(method, callingAssembly));\n                        }\n                    }\n\n                    if (redirectAttr is RedirectToAttribute)\n                    {\n                        if (!s_redirections.Any(r => r.OriginalMethod == method))\n                        {\n\t\t\t\t\t\t\tLog.Info(string.Format(\"Redirector: Detouring method calls from {0}.{1} to {2}.{3} via RedirectTo\",\n                                method.DeclaringType,\n                                method.Name,\n                                originalMethod.DeclaringType,\n                                originalMethod.Name));\n                            s_redirections.Add(method.RedirectTo(originalMethod, callingAssembly));\n                        }\n                    }\n                }\n            }\n        }\n\n        public static void RevertRedirections()\n        {\n            Assembly callingAssembly = Assembly.GetCallingAssembly();\n\n            for (int i = s_redirections.Count - 1; i >= 0; --i)\n            {\n                var redirection = s_redirections[i];\n\n                if (Equals(redirection.RedirectionSource, callingAssembly))\n                {\n\t\t\t\t\tLog.Info(string.Format(\"Redirector: Removing redirection {0}\", s_redirections[i].OriginalMethod));\n                    s_redirections[i].Dispose();\n                    s_redirections.RemoveAt(i);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "TLM/CSUtil.Redirection/Transit.Framework.Redirection.csproj.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=_005FExtensions/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "TLM/CSUtil.Redirection/Transit.Framework.Unsafe.csproj.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=_005FExtensions/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "TLM/PR_REVIEW_INSTRUCTIONS.md",
    "content": "\n## Pull Request Review\n\n\n### Clone PR as new branch inside repository folder:\n\n#### Github Desktop(Atom-version):\n* the PR can be selected from the 'Current Branch' drop-down.\n\n\n#### Git for windows console\n* get __PR__ index - this number with __#__ after __PR__ name\n* inside project folder ``` \\<Folder_of_cloned_repository>\\TLM\\ ```\n  * type ```git fetch origin pull/<pr_number_skip_#>/head:<your_new_branch_name>``` _(skip <>)_ e.g. ```git fetch origin pull/123/head:PR_123```\n* to switch to newly created branch:\n  * type ```git checkout <your_new_branch_name>``` _(skip <>)_ e.g. ```git checkout PR_123```\n  * or use branch switch menu _(usually bottom right corner of preferred __IDE__)_\n  \n  If you don't see new branch to select try to refresh available branches:\n    * __VS 2017:__ _TeamExplorer -> Refresh_\n    * __JB Rider:__ _VCS -> Git -> Fetch_\n\n### Now newly created branch should be accessible from branch switch menu\n"
  },
  {
    "path": "TLM/TLM/CodeProfiler.cs",
    "content": "﻿using ColossalFramework;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\n\nnamespace TrafficManager {\n#if TRACE\n\tpublic class CodeProfiler : Singleton<CodeProfiler> {\n\t\tprivate Dictionary<string, Stopwatch> watches = new Dictionary<string, Stopwatch>();\n\t\tprivate Dictionary<string, ulong> intervals = new Dictionary<string, ulong>();\n\n\t\tinternal void Start(string name) {\n\t\t\tValidate(name);\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(watches);\n\t\t\t\twatches[name].Start();\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(watches);\n\t\t\t}\n\t\t}\n\n\t\tinternal void Stop(string name) {\n\t\t\tValidate(name);\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(watches);\n\t\t\t\twatches[name].Stop();\n\t\t\t\t++intervals[name];\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(watches);\n\t\t\t}\n\t\t}\n\n\t\tinternal void Reset(string name) {\n\t\t\tValidate(name);\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(watches);\n\t\t\t\twatches[name].Reset();\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(watches);\n\t\t\t}\n\t\t}\n\n\t\tinternal ulong ElapsedNano(string name) {\n\t\t\tValidate(name);\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(watches);\n\t\t\t\treturn (ulong)(watches[name].Elapsed.TotalMilliseconds * 1000d * 1000d);\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(watches);\n\t\t\t}\n\t\t}\n\n\t\tprivate void Validate(string name) {\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(watches);\n\t\t\t\tif (!watches.ContainsKey(name)) {\n\t\t\t\t\twatches[name] = new Stopwatch();\n\t\t\t\t\tintervals[name] = 0;\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(watches);\n\t\t\t}\n\t\t}\n\n\t\tinternal void OnLevelUnloading() {\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(watches);\n\n\t\t\t\tforeach (KeyValuePair<string, Stopwatch> we in watches) {\n\t\t\t\t\tLog._Debug($\"Stopwatch {we.Key}: Total elapsed ns: {ElapsedNano(we.Key)} ms: {ElapsedNano(we.Key) / 1000u / 1000u} s: {ElapsedNano(we.Key) / 1000u / 1000u / 1000u} min: {ElapsedNano(we.Key) / 1000u / 1000u / 1000u / 60u} h: {ElapsedNano(we.Key) / 1000u / 1000u / 1000u / 60u / 60u} intervals: {intervals[we.Key]} avg. ns: {(intervals[we.Key] > 0 ? (\"\" + (ElapsedNano(we.Key) / intervals[we.Key])) : \"n/a\")}\");\n                }\n\n\t\t\t\twatches.Clear();\n\t\t\t\tintervals.Clear();\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(watches);\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n"
  },
  {
    "path": "TLM/TLM/Constants.cs",
    "content": "﻿using GenericGameBridge.Factory;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Manager;\n\nnamespace TrafficManager {\n\tpublic static class Constants {\n\t\tpublic static readonly bool[] ALL_BOOL = new bool[] { false, true };\n\n\t\tpublic static IServiceFactory ServiceFactory {\n\t\t\tget {\n#if UNITTEST\n\t\t\t\treturn TestGameBridge.Factory.ServiceFactory.Instance;\n#else\n\t\t\t\treturn CitiesGameBridge.Factory.ServiceFactory.Instance;\n#endif\n\t\t\t}\n\t\t}\n\n\t\tpublic static IManagerFactory ManagerFactory {\n\t\t\tget {\n\t\t\t\treturn Manager.Impl.ManagerFactory.Instance;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomAmbulanceAI.cs",
    "content": "﻿using ColossalFramework;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing UnityEngine;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\tclass CustomAmbulanceAI : CarAI {\n\t\tpublic bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {\n#if DEBUG\n\t\t\t//Log._Debug($\"CustomAmbulanceAI.CustomStartPathFind called for vehicle {vehicleID}\");\n#endif\n\n\t\t\tExtVehicleType vehicleType = VehicleStateManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, (vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0 ? ExtVehicleType.Emergency : ExtVehicleType.Service);\n\n\t\t\tVehicleInfo info = this.m_info;\n\t\t\tbool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;\n\t\t\tPathUnit.Position startPosA;\n\t\t\tPathUnit.Position startPosB;\n\t\t\tfloat startDistSqrA;\n\t\t\tfloat startDistSqrB;\n\t\t\tPathUnit.Position endPosA;\n\t\t\tPathUnit.Position endPosB;\n\t\t\tfloat endDistSqrA;\n\t\t\tfloat endDistSqrB;\n\t\t\tif (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) &&\n\t\t\t\tCustomPathManager.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)) {\n\t\t\t\tif (!startBothWays || startDistSqrA < 10f) {\n\t\t\t\t\tstartPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tif (!endBothWays || endDistSqrA < 10f) {\n\t\t\t\t\tendPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tuint path;\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = ExtCitizenInstance.ExtPathType.None;\n\t\t\t\targs.extVehicleType = vehicleType;\n\t\t\t\targs.vehicleId = vehicleID;\n\t\t\t\targs.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\t\targs.buildIndex = Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\t\targs.startPosA = startPosA;\n\t\t\t\targs.startPosB = startPosB;\n\t\t\t\targs.endPosA = endPosA;\n\t\t\t\targs.endPosB = endPosB;\n\t\t\t\targs.vehiclePosition = default(PathUnit.Position);\n\t\t\t\targs.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;\n\t\t\t\targs.vehicleTypes = info.m_vehicleType;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = this.IsHeavyVehicle();\n\t\t\t\targs.hasCombustionEngine = this.CombustionEngine();\n\t\t\t\targs.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.ignoreCosts = false;\n\t\t\t\targs.randomParking = false;\n\t\t\t\targs.stablePath = false;\n\t\t\t\targs.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\n\t\t\t\tif (CustomPathManager._instance.CreatePath(out path, ref Singleton<SimulationManager>.instance.m_randomizer, args)) {\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tvehicleData.m_path = path;\n\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.WaitingPath;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tPathfindFailure(vehicleID, ref vehicleData);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomBuildingAI.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing CSUtil.Commons.Benchmark;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.UI;\nusing UnityEngine;\n\nnamespace TrafficManager.Custom.AI {\n\tpublic class CustomBuildingAI : BuildingAI {\n\t\tpublic Color CustomGetColor(ushort buildingID, ref Building data, InfoManager.InfoMode infoMode) {\n\t\t\tif (infoMode != InfoManager.InfoMode.None) {\n\t\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark()) {\n#endif\n\t\t\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\t\t\tColor? color;\n\t\t\t\t\t\tif (AdvancedParkingManager.Instance.GetBuildingInfoViewColor(buildingID, ref data, ref ExtBuildingManager.Instance.ExtBuildings[buildingID], infoMode, out color)) {\n\t\t\t\t\t\t\treturn (Color)color;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\treturn Singleton<InfoManager>.instance.m_properties.m_neutralColor;\n\t\t\t}\n\t\t\tif (!this.m_info.m_useColorVariations) {\n\t\t\t\treturn this.m_info.m_color0;\n\t\t\t}\n\t\t\tRandomizer randomizer = new Randomizer((int)buildingID);\n\t\t\tswitch (randomizer.Int32(4u)) {\n\t\t\t\tcase 0:\n\t\t\t\t\treturn this.m_info.m_color0;\n\t\t\t\tcase 1:\n\t\t\t\t\treturn this.m_info.m_color1;\n\t\t\t\tcase 2:\n\t\t\t\t\treturn this.m_info.m_color2;\n\t\t\t\tcase 3:\n\t\t\t\t\treturn this.m_info.m_color3;\n\t\t\t\tdefault:\n\t\t\t\t\treturn this.m_info.m_color0;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomBusAI.cs",
    "content": "﻿using ColossalFramework;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing UnityEngine;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\tclass CustomBusAI : CarAI {\n\t\tpublic bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {\n#if DEBUG\n\t\t\t//Log._Debug($\"CustomBusAI.CustomStartPathFind called for vehicle {vehicleID}\");\n#endif\n\n\t\t\tVehicleInfo info = this.m_info;\n\t\t\tbool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;\n\t\t\tPathUnit.Position startPosA;\n\t\t\tPathUnit.Position startPosB;\n\t\t\tfloat startDistSqrA;\n\t\t\tfloat startDistSqrB;\n\t\t\tPathUnit.Position endPosA;\n\t\t\tPathUnit.Position endPosB;\n\t\t\tfloat endDistSqrA;\n\t\t\tfloat endDistSqrB;\n\t\t\tif (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) &&\n\t\t\t\tCustomPathManager.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)) {\n\t\t\t\tif (!startBothWays || startDistSqrA < 10f) {\n\t\t\t\t\tstartPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tif (!endBothWays || endDistSqrA < 10f) {\n\t\t\t\t\tendPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tuint path;\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = ExtCitizenInstance.ExtPathType.None;\n\t\t\t\targs.extVehicleType = ExtVehicleType.Bus;\n\t\t\t\targs.vehicleId = vehicleID;\n\t\t\t\targs.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\t\targs.buildIndex = Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\t\targs.startPosA = startPosA;\n\t\t\t\targs.startPosB = startPosB;\n\t\t\t\targs.endPosA = endPosA;\n\t\t\t\targs.endPosB = endPosB;\n\t\t\t\targs.vehiclePosition = default(PathUnit.Position);\n\t\t\t\targs.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;\n\t\t\t\targs.vehicleTypes = info.m_vehicleType;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = this.IsHeavyVehicle();\n\t\t\t\targs.hasCombustionEngine = this.CombustionEngine();\n\t\t\t\targs.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.randomParking = false;\n\t\t\t\targs.ignoreCosts = false;\n\t\t\t\targs.stablePath = true;\n\t\t\t\targs.skipQueue = true;\n\n\t\t\t\tif (CustomPathManager._instance.CreatePath(out path, ref Singleton<SimulationManager>.instance.m_randomizer, args)) {\n\t\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tvehicleData.m_path = path;\n\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.WaitingPath;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomCarAI.cs",
    "content": "#define DEBUGVx\n\nusing System;\nusing System.Collections.Generic;\nusing ColossalFramework;\nusing ColossalFramework.Math;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing UnityEngine;\nusing Random = UnityEngine.Random;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.State;\nusing TrafficManager.Manager;\nusing TrafficManager.Traffic;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing System.Runtime.CompilerServices;\nusing TrafficManager.Traffic.Data;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\nusing CSUtil.Commons.Benchmark;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\tpublic class CustomCarAI : CarAI { // TODO inherit from VehicleAI (in order to keep the correct references to `base`)\n\t\tpublic void Awake() {\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Lightweight simulation step method.\n\t\t/// This method is occasionally being called for different cars.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleId\"></param>\n\t\t/// <param name=\"vehicleData\"></param>\n\t\t/// <param name=\"physicsLodRefPos\"></param>\n\t\tpublic void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {\n#if DEBUG\n\t\t\tbool vehDebug = (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleId);\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && vehDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && vehDebug;\n#endif\n\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) {\n\t\t\t\tPathManager pathManager = Singleton<PathManager>.instance;\n\t\t\t\tbyte pathFindFlags = pathManager.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;\n\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tExtPathState mainPathState = ExtPathState.Calculating;\n\t\t\t\tif ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) {\n\t\t\t\t\tmainPathState = ExtPathState.Failed;\n\t\t\t\t} else if ((pathFindFlags & PathUnit.FLAG_READY) != 0) {\n\t\t\t\t\tmainPathState = ExtPathState.Ready;\n\t\t\t\t}\n\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomCarAI.CustomSimulationStep({vehicleId}): Path: {vehicleData.m_path}, mainPathState={mainPathState}\");\n#endif\n\t\t\t\tExtSoftPathState finalPathState = ExtSoftPathState.None;\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"UpdateCarPathState\")) {\n#endif\n\t\t\t\tfinalPathState = ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);\n\t\t\t\tif (Options.prohibitPocketCars && VehicleStateManager.Instance.VehicleStates[vehicleId].vehicleType == ExtVehicleType.PassengerCar) {\n\t\t\t\t\tushort driverInstanceId = CustomPassengerCarAI.GetDriverInstanceId(vehicleId, ref vehicleData);\n\t\t\t\t\tfinalPathState = AdvancedParkingManager.Instance.UpdateCarPathState(vehicleId, ref vehicleData, ref Singleton<CitizenManager>.instance.m_instances.m_buffer[driverInstanceId], ref ExtCitizenInstanceManager.Instance.ExtInstances[driverInstanceId], mainPathState);\n\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomCarAI.CustomSimulationStep({vehicleId}): Applied Parking AI logic. Path: {vehicleData.m_path}, mainPathState={mainPathState}, finalPathState={finalPathState}\");\n#endif\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\n\t\t\t\tswitch (finalPathState) {\n\t\t\t\t\tcase ExtSoftPathState.Ready:\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding succeeded for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- calling CarAI.PathfindSuccess\");\n#endif\n\n\t\t\t\t\t\tvehicleData.m_pathPositionIndex = 255;\n\t\t\t\t\t\tvehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;\n\t\t\t\t\t\tvehicleData.m_flags &= ~Vehicle.Flags.Arriving;\n\t\t\t\t\t\tthis.PathfindSuccess(vehicleId, ref vehicleData);\n\t\t\t\t\t\tthis.TrySpawn(vehicleId, ref vehicleData);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtSoftPathState.Ignore:\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding result shall be ignored for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- ignoring\");\n#endif\n\t\t\t\t\t\treturn;\n\t\t\t\t\tcase ExtSoftPathState.Calculating:\n\t\t\t\t\tdefault:\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomCarAI.CustomSimulationStep({vehicleId}): Path-finding result undetermined for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- continue\");\n#endif\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtSoftPathState.FailedHard:\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomCarAI.CustomSimulationStep({vehicleId}): HARD path-finding failure for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- calling CarAI.PathfindFailure\");\n#endif\n\t\t\t\t\t\tvehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t\tvehicleData.m_path = 0u;\n\t\t\t\t\t\tthis.PathfindFailure(vehicleId, ref vehicleData);\n\t\t\t\t\t\treturn;\n\t\t\t\t\tcase ExtSoftPathState.FailedSoft:\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomCarAI.CustomSimulationStep({vehicleId}): SOFT path-finding failure for vehicle {vehicleId} (finalPathState={finalPathState}). Path: {vehicleData.m_path} -- calling CarAI.InvalidPath\");\n#endif\n\t\t\t\t\t\t// path mode has been updated, repeat path-finding\n\t\t\t\t\t\tvehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;\n\t\t\t\t\t\tthis.InvalidPath(vehicleId, ref vehicleData, vehicleId, ref vehicleData);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t} else {\n\t\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) {\n\t\t\t\t\tthis.TrySpawn(vehicleId, ref vehicleData);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"UpdateVehiclePosition\")) {\n#endif\n\t\t\t\tVehicleStateManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\tif (!Options.isStockLaneChangerUsed() && (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0) {\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"LogTraffic\")) {\n#endif\n\t\t\t\t\t// Advanced AI traffic measurement\n\t\t\t\t\tVehicleStateManager.Instance.LogTraffic(vehicleId);\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tVector3 lastFramePosition = vehicleData.GetLastFramePosition();\n\t\t\tint lodPhysics;\n\t\t\tif (Vector3.SqrMagnitude(physicsLodRefPos - lastFramePosition) >= 1210000f) {\n\t\t\t\tlodPhysics = 2;\n\t\t\t} else if (Vector3.SqrMagnitude(Singleton<SimulationManager>.instance.m_simulationView.m_position - lastFramePosition) >= 250000f) {\n\t\t\t\tlodPhysics = 1;\n\t\t\t} else {\n\t\t\t\tlodPhysics = 0;\n\t\t\t}\n\t\t\tthis.SimulationStep(vehicleId, ref vehicleData, vehicleId, ref vehicleData, lodPhysics);\n\t\t\tif (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0) {\n\t\t\t\tVehicleManager vehManager = Singleton<VehicleManager>.instance;\n\t\t\t\tushort trailerId = vehicleData.m_trailingVehicle;\n\t\t\t\tint numIters = 0;\n\t\t\t\twhile (trailerId != 0) {\n\t\t\t\t\tushort trailingVehicle = vehManager.m_vehicles.m_buffer[(int)trailerId].m_trailingVehicle;\n\t\t\t\t\tVehicleInfo info = vehManager.m_vehicles.m_buffer[(int)trailerId].Info;\n\t\t\t\t\tinfo.m_vehicleAI.SimulationStep(trailerId, ref vehManager.m_vehicles.m_buffer[(int)trailerId], vehicleId, ref vehicleData, lodPhysics);\n\t\t\t\t\ttrailerId = trailingVehicle;\n\t\t\t\t\tif (++numIters > 16384) {\n\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tint privateServiceIndex = ItemClass.GetPrivateServiceIndex(this.m_info.m_class.m_service);\n\t\t\tint maxBlockCounter = (privateServiceIndex == -1) ? 150 : 100;\n\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace)) == 0 && vehicleData.m_cargoParent == 0) {\n\t\t\t\tSingleton<VehicleManager>.instance.ReleaseVehicle(vehicleId);\n\t\t\t} else if ((int)vehicleData.m_blockCounter >= maxBlockCounter) {\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tbool mayDespawn = true;\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"MayDespawn\")) {\n#endif\n\t\t\t\t\tmayDespawn = VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData);\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (mayDespawn) {\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\tSingleton<VehicleManager>.instance.ReleaseVehicle(vehicleId);\n\t\t\t\t} // NON-STOCK CODE\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool TrySpawn(ushort vehicleId, ref Vehicle vehicleData) {\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Spawned) != (Vehicle.Flags)0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (CustomCarAI.CheckOverlap(vehicleData.m_segment, 0, 1000f)) {\n\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.WaitingSpace;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tvehicleData.Spawn(vehicleId);\n\t\t\tvehicleData.m_flags &= ~Vehicle.Flags.WaitingSpace;\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic void CustomCalculateSegmentPosition(ushort vehicleId, ref Vehicle vehicleData, PathUnit.Position nextPosition,\n\t\t\t\tPathUnit.Position prevPosition, uint prevLaneId, byte prevOffset, PathUnit.Position refPosition, uint refLaneId,\n\t\t\t\tbyte refOffset, int index, out Vector3 pos, out Vector3 dir, out float maxSpeed) {\n\t\t\tvar netManager = Singleton<NetManager>.instance;\n\t\t\tushort prevSourceNodeId;\n\t\t\tushort prevTargetNodeId;\n\t\t\tif (prevOffset < prevPosition.m_offset) {\n\t\t\t\tprevSourceNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_startNode;\n\t\t\t\tprevTargetNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_endNode;\n\t\t\t} else {\n\t\t\t\tprevSourceNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_endNode;\n\t\t\t\tprevTargetNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_startNode;\n\t\t\t}\n\n\t\t\tushort refTargetNodeId;\n\t\t\tif (refOffset == 0) {\n\t\t\t\trefTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_startNode;\n\t\t\t} else {\n\t\t\t\trefTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_endNode;\n\t\t\t}\n\n#if DEBUG\n\t\t\tbool 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.RoadVehicle) && (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleId);\n\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"CustomCarAI.CustomCalculateSegmentPosition({vehicleId}) called.\\n\" +\n\t\t\t\t\t$\"\\trefPosition.m_segment={refPosition.m_segment}, refPosition.m_offset={refPosition.m_offset}\\n\" +\n\t\t\t\t\t$\"\\tprevPosition.m_segment={prevPosition.m_segment}, prevPosition.m_offset={prevPosition.m_offset}\\n\" +\n\t\t\t\t\t$\"\\tnextPosition.m_segment={nextPosition.m_segment}, nextPosition.m_offset={nextPosition.m_offset}\\n\" +\n\t\t\t\t\t$\"\\trefLaneId={refLaneId}, refOffset={refOffset}\\n\" +\n\t\t\t\t\t$\"\\tprevLaneId={prevLaneId}, prevOffset={prevOffset}\\n\" +\n\t\t\t\t\t$\"\\tprevSourceNodeId={prevSourceNodeId}, prevTargetNodeId={prevTargetNodeId}\\n\" +\n\t\t\t\t\t$\"\\trefTargetNodeId={refTargetNodeId}, refTargetNodeId={refTargetNodeId}\\n\" +\n\t\t\t\t\t$\"\\tindex={index}\");\n\t\t\t}\n#endif\n\n\t\t\tVehicle.Frame lastFrameData = vehicleData.GetLastFrameData();\n\t\t\tVector3 lastFrameVehiclePos = lastFrameData.m_position;\n\t\t\tfloat sqrVelocity = lastFrameData.m_velocity.sqrMagnitude;\n\n\t\t\tnetManager.m_lanes.m_buffer[prevLaneId].CalculatePositionAndDirection(prevOffset * 0.003921569f, out pos, out dir);\n\n\t\t\tfloat braking = this.m_info.m_braking;\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Emergency2) != (Vehicle.Flags)0) {\n\t\t\t\tbraking *= 2f;\n\t\t\t}\n\n\t\t\t// car position on the Bezier curve of the lane\n\t\t\tvar refVehiclePosOnBezier = netManager.m_lanes.m_buffer[refLaneId].CalculatePosition(refOffset * 0.003921569f);\n\t\t\t//ushort currentSegmentId = netManager.m_lanes.m_buffer[prevLaneID].m_segment;\n\n\t\t\t// this seems to be like the required braking force in order to stop the vehicle within its half length.\n\t\t\tvar crazyValue = 0.5f * sqrVelocity / braking + m_info.m_generatedInfo.m_size.z * 0.5f;\n\t\t\tbool withinBrakingDistance = Vector3.Distance(lastFrameVehiclePos, refVehiclePosOnBezier) >= crazyValue - 1f;\n\n\t\t\tif (prevSourceNodeId == refTargetNodeId && withinBrakingDistance) {\n\t\t\t\t// NON-STOCK CODE START (stock code replaced)\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"MayChangeSegment\")) {\n#endif\n\t\t\t\t//bool isRecklessDriver = VehicleStateManager.Instance.IsRecklessDriver(vehicleId, ref vehicleData); // NON-STOCK CODE\n\n\t\t\t\tif (!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, out maxSpeed)) { // NON-STOCK CODE\n\t\t\t\t\treturn;\n\t\t\t\t} else {\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"UpdateVehiclePosition\")) {\n#endif\n\t\t\t\t\tVehicleStateManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData/*, lastFrameData.m_velocity.magnitude*/);\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t}\n\n\t\t\tvar segmentInfo = netManager.m_segments.m_buffer[prevPosition.m_segment].Info;\n\t\t\tif (segmentInfo.m_lanes != null && segmentInfo.m_lanes.Length > prevPosition.m_lane) {\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tfloat laneSpeedLimit = 1f;\n\n\t\t\t\tif (!Options.customSpeedLimitsEnabled) {\n\t\t\t\t\tlaneSpeedLimit = segmentInfo.m_lanes[prevPosition.m_lane].m_speedLimit;\n\t\t\t\t} else {\n\t\t\t\t\tlaneSpeedLimit = Constants.ManagerFactory.SpeedLimitManager.GetLockFreeGameSpeedLimit(prevPosition.m_segment, prevPosition.m_lane, prevLaneId, segmentInfo.m_lanes[prevPosition.m_lane]);\n\t\t\t\t}\n\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t\tmaxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, laneSpeedLimit, netManager.m_lanes.m_buffer[prevLaneId].m_curve);\n\t\t\t} else {\n\t\t\t\tmaxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f);\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START (stock code replaced)\n\t\t\tmaxSpeed = Constants.ManagerFactory.VehicleBehaviorManager.CalcMaxSpeed(vehicleId, ref VehicleStateManager.Instance.VehicleStates[vehicleId], this.m_info, prevPosition, ref netManager.m_segments.m_buffer[prevPosition.m_segment], pos, maxSpeed);\n\t\t\t// NON-STOCK CODE END\n\t\t}\n\n\t\tpublic void CustomCalculateSegmentPositionPathFinder(ushort vehicleId, ref Vehicle vehicleData, PathUnit.Position position, uint laneId, byte offset, out Vector3 pos, out Vector3 dir, out float maxSpeed) {\n\t\t\tvar netManager = Singleton<NetManager>.instance;\n\t\t\tnetManager.m_lanes.m_buffer[laneId].CalculatePositionAndDirection(offset * 0.003921569f, out pos, out dir);\n\t\t\tvar segmentInfo = netManager.m_segments.m_buffer[position.m_segment].Info;\n\t\t\tif (segmentInfo.m_lanes != null && segmentInfo.m_lanes.Length > position.m_lane) {\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tfloat laneSpeedLimit = 1f;\n\t\t\t\tif (!Options.customSpeedLimitsEnabled) {\n\t\t\t\t\tlaneSpeedLimit = segmentInfo.m_lanes[position.m_lane].m_speedLimit;\n\t\t\t\t} else {\n\t\t\t\t\tlaneSpeedLimit = Constants.ManagerFactory.SpeedLimitManager.GetLockFreeGameSpeedLimit(position.m_segment, position.m_lane, laneId, segmentInfo.m_lanes[position.m_lane]);\n\t\t\t\t}\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t\tmaxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, laneSpeedLimit, netManager.m_lanes.m_buffer[laneId].m_curve);\n\t\t\t} else {\n\t\t\t\tmaxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f);\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n\t\t\tmaxSpeed = VehicleBehaviorManager.Instance.CalcMaxSpeed(vehicleId, ref VehicleStateManager.Instance.VehicleStates[vehicleId], this.m_info, position, ref netManager.m_segments.m_buffer[position.m_segment], pos, maxSpeed);\n\t\t\t// NON-STOCK CODE END\n\t\t}\n\n\t\tpublic bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {\n#if DEBUG\n\t\t\tbool vehDebug = GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleID;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && vehDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && vehDebug;\n\n\t\t\tif (debug)\n\t\t\t\tLog.Warning($\"CustomCarAI.CustomStartPathFind({vehicleID}): called for vehicle {vehicleID}, startPos={startPos}, endPos={endPos}, startBothWays={startBothWays}, endBothWays={endBothWays}, undergroundTarget={undergroundTarget}\");\n#endif\n\n\t\t\tExtVehicleType vehicleType = ExtVehicleType.None;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"OnStartPathFind\")) {\n#endif\n\t\t\t\tvehicleType = VehicleStateManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, null);\n\t\t\t\tif (vehicleType == ExtVehicleType.None) {\n#if DEBUG\n\t\t\t\t\tLog.Warning($\"CustomCarAI.CustomStartPathFind({vehicleID}): Vehicle {vehicleID} does not have a valid vehicle type!\");\n#endif\n\t\t\t\t\tvehicleType = ExtVehicleType.RoadVehicle;\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\tVehicleInfo info = this.m_info;\n\t\t\tbool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;\n\t\t\tPathUnit.Position startPosA;\n\t\t\tPathUnit.Position startPosB;\n\t\t\tfloat startDistSqrA;\n\t\t\tfloat startDistSqrB;\n\t\t\tPathUnit.Position endPosA;\n\t\t\tPathUnit.Position endPosB;\n\t\t\tfloat endDistSqrA;\n\t\t\tfloat endDistSqrB;\n\t\t\tif (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) &&\n\t\t\t\tCustomPathManager.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)) {\n\t\t\t\tif (!startBothWays || startDistSqrA < 10f) {\n\t\t\t\t\tstartPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tif (!endBothWays || endDistSqrA < 10f) {\n\t\t\t\t\tendPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tuint path;\n\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = ExtCitizenInstance.ExtPathType.None;\n\t\t\t\targs.extVehicleType = vehicleType;\n\t\t\t\targs.vehicleId = vehicleID;\n\t\t\t\targs.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\t\targs.buildIndex = Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\t\targs.startPosA = startPosA;\n\t\t\t\targs.startPosB = startPosB;\n\t\t\t\targs.endPosA = endPosA;\n\t\t\t\targs.endPosB = endPosB;\n\t\t\t\targs.vehiclePosition = default(PathUnit.Position);\n\t\t\t\targs.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;\n\t\t\t\targs.vehicleTypes = info.m_vehicleType;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = this.IsHeavyVehicle();\n\t\t\t\targs.hasCombustionEngine = this.CombustionEngine();\n\t\t\t\targs.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.ignoreCosts = false;\n\t\t\t\targs.randomParking = false;\n\t\t\t\targs.stablePath = false;\n\t\t\t\targs.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\n\t\t\t\tif (CustomPathManager._instance.CreatePath(out path, ref Singleton<SimulationManager>.instance.m_randomizer, args)) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._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}\");\n#endif\n\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tvehicleData.m_path = path;\n\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.WaitingPath;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate static bool CheckOverlap(Segment3 segment, ushort ignoreVehicle, float maxVelocity) {\n\t\t\tLog.Error(\"CustomCarAI.CheckOverlap called\");\n\t\t\treturn false;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate 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) {\n\t\t\tLog.Error(\"CustomCarAI.CheckOtherVehicle called\");\n\t\t\treturn 0;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate 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) {\n\t\t\tLog.Error(\"CustomCarAI.CheckCitizen called\");\n\t\t\treturn 0;\n\t\t}\n\t}\n}"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomCargoTruckAI.cs",
    "content": "using System;\nusing ColossalFramework;\nusing UnityEngine;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Traffic.Data;\nusing CSUtil.Commons.Benchmark;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\tpublic class CustomCargoTruckAI : CarAI {\n\t\tpublic void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Congestion) != 0 && VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData)) {\n\t\t\t\tSingleton<VehicleManager>.instance.ReleaseVehicle(vehicleId);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.WaitingTarget) != 0 && (vehicleData.m_waitCounter += 1) > 20) {\n\t\t\t\tRemoveOffers(vehicleId, ref vehicleData);\n\t\t\t\tvehicleData.m_flags &= ~Vehicle.Flags.WaitingTarget;\n\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.GoingBack;\n\t\t\t\tvehicleData.m_waitCounter = 0;\n\t\t\t\tif (!StartPathFind(vehicleId, ref vehicleData)) {\n\t\t\t\t\tvehicleData.Unspawn(vehicleId);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbase.SimulationStep(vehicleId, ref vehicleData, physicsLodRefPos);\n\t\t}\n\n\t\t// stock code\n\t\tprivate static void RemoveOffers(ushort vehicleId, ref Vehicle data) {\n\t\t\tif ((data.m_flags & Vehicle.Flags.WaitingTarget) != (Vehicle.Flags)0) {\n\t\t\t\tvar offer = default(TransferManager.TransferOffer);\n\t\t\t\toffer.Vehicle = vehicleId;\n\t\t\t\tif ((data.m_flags & Vehicle.Flags.TransferToSource) != (Vehicle.Flags)0) {\n\t\t\t\t\tSingleton<TransferManager>.instance.RemoveIncomingOffer((TransferManager.TransferReason)data.m_transferType, offer);\n\t\t\t\t} else if ((data.m_flags & Vehicle.Flags.TransferToTarget) != (Vehicle.Flags)0) {\n\t\t\t\t\tSingleton<TransferManager>.instance.RemoveOutgoingOffer((TransferManager.TransferReason)data.m_transferType, offer);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {\n#if DEBUG\n\t\t\t//Log._Debug($\"CustomCargoTruckAI.CustomStartPathFind called for vehicle {vehicleID}\");\n#endif\n\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"OnStartPathFind\")) {\n#endif\n\t\t\t\tExtVehicleType vehicleType = VehicleStateManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, null);\n\t\t\t\tif (vehicleType == ExtVehicleType.None) {\n#if DEBUG\n\t\t\t\t\tLog.Warning($\"CustomCargoTruck.CustomStartPathFind: Vehicle {vehicleID} does not have a valid vehicle type!\");\n#endif\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\n\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.TransferToSource | Vehicle.Flags.GoingBack)) != 0) {\n\t\t\t\treturn base.StartPathFind(vehicleID, ref vehicleData, startPos, endPos, startBothWays, endBothWays, undergroundTarget);\n\t\t\t}\n\n\t\t\tbool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;\n\t\t\tPathUnit.Position startPosA;\n\t\t\tPathUnit.Position startPosB;\n\t\t\tfloat startDistSqrA;\n\t\t\tfloat startDistSqrB;\n\t\t\tbool 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);\n\t\t\tPathUnit.Position startAltPosA;\n\t\t\tPathUnit.Position startAltPosB;\n\t\t\tfloat startAltDistSqrA;\n\t\t\tfloat startAltDistSqrB;\n\t\t\tif (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)) {\n\t\t\t\tif (!startPosFound || (startAltDistSqrA < startDistSqrA && (Mathf.Abs(endPos.x) > 8000f || Mathf.Abs(endPos.z) > 8000f))) {\n\t\t\t\t\tstartPosA = startAltPosA;\n\t\t\t\t\tstartPosB = startAltPosB;\n\t\t\t\t\tstartDistSqrA = startAltDistSqrA;\n\t\t\t\t\tstartDistSqrB = startAltDistSqrB;\n\t\t\t\t}\n\t\t\t\tstartPosFound = true;\n\t\t\t}\n\t\t\tPathUnit.Position endPosA;\n\t\t\tPathUnit.Position endPosB;\n\t\t\tfloat endDistSqrA;\n\t\t\tfloat endDistSqrB;\n\t\t\tbool 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);\n\t\t\tPathUnit.Position endAltPosA;\n\t\t\tPathUnit.Position endAltPosB;\n\t\t\tfloat endAltDistSqrA;\n\t\t\tfloat endAltDistSqrB;\n\t\t\tif (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)) {\n\t\t\t\tif (!endPosFound || (endAltDistSqrA < endDistSqrA && (Mathf.Abs(endPos.x) > 8000f || Mathf.Abs(endPos.z) > 8000f))) {\n\t\t\t\t\tendPosA = endAltPosA;\n\t\t\t\t\tendPosB = endAltPosB;\n\t\t\t\t\tendDistSqrA = endAltDistSqrA;\n\t\t\t\t\tendDistSqrB = endAltDistSqrB;\n\t\t\t\t}\n\t\t\t\tendPosFound = true;\n\t\t\t}\n\t\t\tif (startPosFound && endPosFound) {\n\t\t\t\tCustomPathManager pathMan = CustomPathManager._instance;\n\t\t\t\tif (!startBothWays || startDistSqrA < 10f) {\n\t\t\t\t\tstartPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tif (!endBothWays || endDistSqrA < 10f) {\n\t\t\t\t\tendPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tNetInfo.LaneType laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.CargoVehicle;\n\t\t\t\tVehicleInfo.VehicleType vehicleTypes =  VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Ship | VehicleInfo.VehicleType.Plane;\n\t\t\t\tuint path;\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = ExtCitizenInstance.ExtPathType.None;\n\t\t\t\targs.extVehicleType = ExtVehicleType.CargoVehicle;\n\t\t\t\targs.vehicleId = vehicleID;\n\t\t\t\targs.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\t\targs.buildIndex = Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\t\targs.startPosA = startPosA;\n\t\t\t\targs.startPosB = startPosB;\n\t\t\t\targs.endPosA = endPosA;\n\t\t\t\targs.endPosB = endPosB;\n\t\t\t\targs.vehiclePosition = default(PathUnit.Position);\n\t\t\t\targs.laneTypes = laneTypes;\n\t\t\t\targs.vehicleTypes = vehicleTypes;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = this.IsHeavyVehicle();\n\t\t\t\targs.hasCombustionEngine = this.CombustionEngine();\n\t\t\t\targs.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.ignoreCosts = false;\n\t\t\t\targs.randomParking = false;\n\t\t\t\targs.stablePath = false;\n\t\t\t\targs.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\n\t\t\t\tif (pathMan.CreatePath(out path, ref Singleton<SimulationManager>.instance.m_randomizer, args)) {\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tpathMan.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tvehicleData.m_path = path;\n\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.WaitingPath;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomCitizenAI.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing UnityEngine;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Traffic.Data;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\nusing CSUtil.Commons.Benchmark;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\t// TODO move Parking AI features from here to a distinct manager\n\tpublic class CustomCitizenAI : CitizenAI {\n\n\t\tpublic bool CustomStartPathFind(ushort instanceID, ref CitizenInstance citizenData, Vector3 startPos, Vector3 endPos, VehicleInfo vehicleInfo, bool enableTransport, bool ignoreCost) {\n\t\t\treturn ExtStartPathFind(instanceID, ref citizenData, ref ExtCitizenInstanceManager.Instance.ExtInstances[instanceID], ref ExtCitizenManager.Instance.ExtCitizens[Singleton<CitizenManager>.instance.m_instances.m_buffer[instanceID].m_citizen], startPos, endPos, vehicleInfo, enableTransport, ignoreCost);\n\t\t}\n\n\t\tpublic bool ExtStartPathFind(ushort instanceID, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance, ref ExtCitizen extCitizen, Vector3 startPos, Vector3 endPos, VehicleInfo vehicleInfo, bool enableTransport, bool ignoreCost) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceID) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == instanceData.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == instanceData.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == instanceData.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (debug)\n\t\t\t\tLog.Warning($\"CustomCitizenAI.ExtStartPathFind({instanceID}): called for citizen {instanceData.m_citizen}, startPos={startPos}, endPos={endPos}, sourceBuilding={instanceData.m_sourceBuilding}, targetBuilding={instanceData.m_targetBuilding}, pathMode={extInstance.pathMode}, enableTransport={enableTransport}, ignoreCost={ignoreCost}\");\n#endif\n\n\t\t\t// NON-STOCK CODE START\n\t\t\tCitizenManager citizenManager = Singleton<CitizenManager>.instance;\n\t\t\tushort parkedVehicleId = citizenManager.m_citizens.m_buffer[instanceData.m_citizen].m_parkedVehicle;\n\t\t\tushort homeId = citizenManager.m_citizens.m_buffer[instanceData.m_citizen].m_homeBuilding;\n\t\t\tCarUsagePolicy carUsageMode = CarUsagePolicy.Allowed;\n\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"ParkingAI.Preparation\")) {\n#endif\n\t\t\tbool startsAtOutsideConnection = false;\n\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\tswitch (extInstance.pathMode) {\n\t\t\t\t\tcase ExtPathMode.RequiresWalkingPathToParkedCar:\n\t\t\t\t\tcase ExtPathMode.CalculatingWalkingPathToParkedCar:\n\t\t\t\t\tcase ExtPathMode.WalkingToParkedCar:\n\t\t\t\t\tcase ExtPathMode.ApproachingParkedCar:\n\t\t\t\t\t\tif (parkedVehicleId == 0) {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * Parked vehicle not present but citizen wants to reach it\n\t\t\t\t\t\t\t * -> Reset path mode\n\t\t\t\t\t\t\t */\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode} but no parked vehicle present. Change to 'None'.\");\n#endif\n\n\t\t\t\t\t\t\textInstance.Reset();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * Parked vehicle is present and citizen wants to reach it\n\t\t\t\t\t\t\t * -> Prohibit car usage\n\t\t\t\t\t\t\t */\n#if DEBUG\n\t\t\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}.  Change to 'CalculatingWalkingPathToParkedCar'.\");\n#endif\n\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.CalculatingWalkingPathToParkedCar;\n\t\t\t\t\t\t\tcarUsageMode = CarUsagePolicy.Forbidden;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtPathMode.RequiresWalkingPathToTarget:\n\t\t\t\t\tcase ExtPathMode.CalculatingWalkingPathToTarget:\n\t\t\t\t\tcase ExtPathMode.WalkingToTarget:\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Citizen walks to target\n\t\t\t\t\t\t * -> Reset path mode\n\t\t\t\t\t\t */\n#if DEBUG\n\t\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}. Change to 'CalculatingWalkingPathToTarget'.\");\n#endif\n\t\t\t\t\t\textInstance.pathMode = ExtPathMode.CalculatingWalkingPathToTarget;\n\t\t\t\t\t\tcarUsageMode = CarUsagePolicy.Forbidden;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtPathMode.RequiresCarPath:\n\t\t\t\t\tcase ExtPathMode.RequiresMixedCarPathToTarget:\n\t\t\t\t\tcase ExtPathMode.DrivingToTarget:\n\t\t\t\t\tcase ExtPathMode.DrivingToKnownParkPos:\n\t\t\t\t\tcase ExtPathMode.DrivingToAltParkPos:\n\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToAltParkPos:\n\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToKnownParkPos:\n\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToTarget:\n\t\t\t\t\t\tif (parkedVehicleId == 0) {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * Citizen wants to drive to target but parked vehicle is not present\n\t\t\t\t\t\t\t * -> Reset path mode\n\t\t\t\t\t\t\t */\n\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode} but no parked vehicle present. Change to 'None'.\");\n#endif\n\n\t\t\t\t\t\t\textInstance.Reset();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * Citizen wants to drive to target and parked vehicle is present\n\t\t\t\t\t\t\t * -> Force parked car usage\n\t\t\t\t\t\t\t */\n\n#if DEBUG\n\t\t\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}.  Change to 'RequiresCarPath'.\");\n#endif\n\n\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.RequiresCarPath;\n\t\t\t\t\t\t\tcarUsageMode = CarUsagePolicy.ForcedParked;\n\t\t\t\t\t\t\tstartPos = Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position; // force to start from the parked car\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen has CurrentPathMode={extInstance.pathMode}. Change to 'None'.\");\n#endif\n\t\t\t\t\t\textInstance.Reset();\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tstartsAtOutsideConnection = Constants.ManagerFactory.ExtCitizenInstanceManager.IsAtOutsideConnection(instanceID, ref instanceData, ref extInstance, startPos);\n\t\t\t\tif (extInstance.pathMode == ExtPathMode.None) {\n\t\t\t\t\tif ((instanceData.m_flags & CitizenInstance.Flags.OnTour) != CitizenInstance.Flags.None || ignoreCost) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Citizen is on a walking tour or is a mascot\n\t\t\t\t\t\t * -> Prohibit car usage\n\t\t\t\t\t\t */\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen ignores cost ({ignoreCost}) or is on a walking tour ({(instanceData.m_flags & CitizenInstance.Flags.OnTour) != CitizenInstance.Flags.None}): Setting path mode to 'CalculatingWalkingPathToTarget'\");\n#endif\n\t\t\t\t\t\tcarUsageMode = CarUsagePolicy.Forbidden;\n\t\t\t\t\t\textInstance.pathMode = ExtPathMode.CalculatingWalkingPathToTarget;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Citizen is not on a walking tour and is not a mascot\n\t\t\t\t\t\t * -> Check if citizen is located at an outside connection and make them obey Parking AI restrictions\n\t\t\t\t\t\t */\n\n\t\t\t\t\t\tif (instanceData.m_sourceBuilding != 0) {\n\t\t\t\t\t\t\tItemClass.Service sourceBuildingService = Singleton<BuildingManager>.instance.m_buildings.m_buffer[instanceData.m_sourceBuilding].Info.m_class.m_service;\n\n\t\t\t\t\t\t\tif (startsAtOutsideConnection) {\n\t\t\t\t\t\t\t\tif (sourceBuildingService == ItemClass.Service.Road) {\n\t\t\t\t\t\t\t\t\tif (vehicleInfo != null) {\n\t\t\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\t\t * Citizen is located at a road outside connection and can spawn a car\n\t\t\t\t\t\t\t\t\t\t * -> Force car usage\n\t\t\t\t\t\t\t\t\t\t */\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is located at a road outside connection: Setting path mode to 'RequiresCarPath' and carUsageMode to 'ForcedPocket'\");\n#endif\n\t\t\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.RequiresCarPath;\n\t\t\t\t\t\t\t\t\t\tcarUsageMode = CarUsagePolicy.ForcedPocket;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\t\t * Citizen is located at a non-road outside connection and cannot spawn a car\n\t\t\t\t\t\t\t\t\t\t * -> Path-finding fails\n\t\t\t\t\t\t\t\t\t\t */\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is located at a road outside connection but does not have a car template: ABORTING PATH-FINDING\");\n#endif\n\t\t\t\t\t\t\t\t\t\textInstance.Reset();\n\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\t * Citizen is located at a non-road outside connection\n\t\t\t\t\t\t\t\t\t * -> Prohibit car usage\n\t\t\t\t\t\t\t\t\t */\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is located at a non-road outside connection: Setting path mode to 'CalculatingWalkingPathToTarget'\");\n#endif\n\t\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.CalculatingWalkingPathToTarget;\n\t\t\t\t\t\t\t\t\tcarUsageMode = CarUsagePolicy.Forbidden;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ((carUsageMode == CarUsagePolicy.Allowed || carUsageMode == CarUsagePolicy.ForcedParked) && parkedVehicleId != 0) {\n\t\t\t\t\t/*\n\t\t\t\t\t* Reuse parked vehicle info\n\t\t\t\t\t*/\n\t\t\t\t\tvehicleInfo = Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Check if the citizen should return their car back home\n\t\t\t\t\t */\n\t\t\t\t\tif (extInstance.pathMode == ExtPathMode.None && // initiating a new path\n\t\t\t\t\t\thomeId != 0 && // home building present\n\t\t\t\t\t\tinstanceData.m_targetBuilding == homeId // current target is home\n\t\t\t\t\t) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * citizen travels back home\n\t\t\t\t\t\t * -> check if their car should be returned\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif ((extCitizen.lastTransportMode & ExtCitizen.ExtTransportMode.Car) != ExtCitizen.ExtTransportMode.None) {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * citizen travelled by car\n\t\t\t\t\t\t\t * -> return car back home\n\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\textInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToParkedCar;\n\t\t\t\t\t\t\tcarUsageMode = CarUsagePolicy.Forbidden;\n\n#if DEBUG\n\t\t\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen used their car before and is not at home. Forcing to walk to parked car.\");\n#endif\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * citizen travelled by other means of transport\n\t\t\t\t\t\t\t * -> check distance between home and parked car. if too far away: force to take the car back home\n\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\tfloat distHomeToParked = (Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position - Singleton<BuildingManager>.instance.m_buildings.m_buffer[homeId].m_position).magnitude;\n\n\t\t\t\t\t\t\tif (distHomeToParked > GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToHome) {\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t * force to take car back home\n\t\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\t\textInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToParkedCar;\n\t\t\t\t\t\t\t\tcarUsageMode = CarUsagePolicy.Forbidden;\n\n#if DEBUG\n\t\t\t\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen wants to go home and parked car is too far away ({distHomeToParked}). Forcing walking to parked car.\");\n#endif\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * The following holds:\n\t\t\t\t * - pathMode is now either CalculatingWalkingPathToParkedCar, CalculatingWalkingPathToTarget, RequiresCarPath or None.\n\t\t\t\t * - if pathMode is CalculatingWalkingPathToParkedCar or RequiresCarPath: parked car is present and citizen is not on a walking tour\n\t\t\t\t * - carUsageMode is valid\n\t\t\t\t * - if pathMode is RequiresCarPath: carUsageMode is either ForcedParked or ForcedPocket\n\t\t\t\t */\n\n\t\t\t\t/*\n\t\t\t\t * modify path-finding constraints (vehicleInfo, endPos) if citizen is forced to walk\n\t\t\t\t */\n\t\t\t\tif (extInstance.pathMode == ExtPathMode.CalculatingWalkingPathToParkedCar || extInstance.pathMode == ExtPathMode.CalculatingWalkingPathToTarget) {\n\t\t\t\t\t/*\n\t\t\t\t\t * vehicle must not be used since we need a walking path to either\n\t\t\t\t\t * 1. a parked car or\n\t\t\t\t\t * 2. the target building\n\t\t\t\t\t */\n\t\t\t\t\t\n\t\t\t\t\tif (extInstance.pathMode == ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToParkedCar) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * walk to parked car\n\t\t\t\t\t\t * -> end position is parked car\n\t\t\t\t\t\t */\n\t\t\t\t\t\tendPos = Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position;\n#if DEBUG\n\t\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen shall go to parked vehicle @ {endPos}\");\n#endif\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n#if DEBUG\n\t\t\tif (fineDebug)\n\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen is allowed to drive their car? {carUsageMode}\");\n#endif\n\t\t\t// NON-STOCK CODE END\n\n\t\t\t/*\n\t\t\t * semi-stock code: determine path-finding parameters (laneTypes, vehicleTypes, extVehicleType, etc.)\n\t\t\t */\n\t\t\tNetInfo.LaneType laneTypes = NetInfo.LaneType.Pedestrian;\n\t\t\tVehicleInfo.VehicleType vehicleTypes = VehicleInfo.VehicleType.None;\n\t\t\tbool randomParking = false;\n\t\t\tbool combustionEngine = false;\n\t\t\tExtVehicleType extVehicleType = ExtVehicleType.None;\n\t\t\tif (vehicleInfo != null) {\n\t\t\t\tif (vehicleInfo.m_class.m_subService == ItemClass.SubService.PublicTransportTaxi) {\n\t\t\t\t\tif ((instanceData.m_flags & CitizenInstance.Flags.CannotUseTaxi) == CitizenInstance.Flags.None && Singleton<DistrictManager>.instance.m_districts.m_buffer[0].m_productionData.m_finalTaxiCapacity != 0u) {\n\t\t\t\t\t\tSimulationManager instance = Singleton<SimulationManager>.instance;\n\t\t\t\t\t\tif (instance.m_isNightTime || instance.m_randomizer.Int32(2u) == 0) {\n\t\t\t\t\t\t\tlaneTypes |= (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t\t\t\tvehicleTypes |= vehicleInfo.m_vehicleType;\n\t\t\t\t\t\t\textVehicleType = ExtVehicleType.Taxi; // NON-STOCK CODE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  // NON-STOCK CODE START\n\t\t\t\t\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.TaxiToTarget;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tif (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Car) {\n\t\t\t\t\tif (carUsageMode != CarUsagePolicy.Forbidden) {\n\t\t\t\t\t\textVehicleType = ExtVehicleType.PassengerCar;\n\t\t\t\t\t\tlaneTypes |= NetInfo.LaneType.Vehicle;\n\t\t\t\t\t\tvehicleTypes |= vehicleInfo.m_vehicleType;\n\t\t\t\t\t\tcombustionEngine = vehicleInfo.m_class.m_subService == ItemClass.SubService.ResidentialLow;\n\t\t\t\t\t}\n\t\t\t\t} else if (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Bicycle) {\n\t\t\t\t\textVehicleType = ExtVehicleType.Bicycle;\n\t\t\t\t\tlaneTypes |= NetInfo.LaneType.Vehicle;\n\t\t\t\t\tvehicleTypes |= vehicleInfo.m_vehicleType;\n\t\t\t\t}\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n\t\t\tExtPathType extPathType = ExtPathType.None;\n\t\t\tPathUnit.Position endPosA = default(PathUnit.Position);\n\t\t\tbool calculateEndPos = true;\n\t\t\tbool allowRandomParking = true;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"ParkingAI.Main\")) {\n#endif\n\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\t// Parking AI\n\n\t\t\t\tif (extInstance.pathMode == ExtCitizenInstance.ExtPathMode.RequiresCarPath) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Setting startPos={startPos} for citizen instance {instanceID}. CurrentDepartureMode={extInstance.pathMode}\");\n#endif\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tinstanceData.m_targetBuilding == 0 ||\n\t\t\t\t\t\t(Singleton<BuildingManager>.instance.m_buildings.m_buffer[instanceData.m_targetBuilding].m_flags & Building.Flags.IncomingOutgoing) == Building.Flags.None\n\t\t\t\t\t) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * the citizen is starting their journey and the target is not an outside connection\n\t\t\t\t\t\t * -> find a suitable parking space near the target\n\t\t\t\t\t\t */\n\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Finding parking space at target for citizen instance {instanceID}. CurrentDepartureMode={extInstance.pathMode} parkedVehicleId={parkedVehicleId}\");\n#endif\n\n\t\t\t\t\t\t// find a parking space in the vicinity of the target\n\t\t\t\t\t\tbool calcEndPos;\n\t\t\t\t\t\tVector3 parkPos;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tAdvancedParkingManager.Instance.FindParkingSpaceForCitizen(endPos, vehicleInfo, ref extInstance, homeId, instanceData.m_targetBuilding == homeId, 0, false, out parkPos, ref endPosA, out calcEndPos) &&\n\t\t\t\t\t\t\textInstance.CalculateReturnPath(parkPos, endPos)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// success\n\t\t\t\t\t\t\textInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingCarPathToKnownParkPos;\n\t\t\t\t\t\t\tcalculateEndPos = calcEndPos; // if true, the end path position still needs to be calculated\n\t\t\t\t\t\t\tallowRandomParking = false; // find a direct path to the calculated parking position\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Finding known parking space for citizen instance {instanceID}, parked vehicle {parkedVehicleId} succeeded and return path {extInstance.returnPathId} ({extInstance.returnPathState}) is calculating. PathMode={extInstance.pathMode}\");\n#endif\n\t\t\t\t\t\t\t/*if (! extInstance.CalculateReturnPath(parkPos, endPos)) {\n\t\t\t\t\t\t\t\t// TODO retry?\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.CustomStartPathFind: [PFFAIL] Could not calculate return path for citizen instance {instanceID}, parked vehicle {parkedVehicleId}. Calling OnPathFindFailed.\");\n\t\t\t\t\t\t\t\tCustomHumanAI.OnPathFindFailure(extInstance);\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}*/\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (extInstance.pathMode == ExtPathMode.RequiresCarPath) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * no known parking space found (pathMode has not been updated in the block above)\n\t\t\t\t\t\t * -> calculate direct path to target\n\t\t\t\t\t\t */\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} is still at CurrentPathMode={extInstance.pathMode} (no parking space found?). Setting it to CalculatingCarPath. parkedVehicleId={parkedVehicleId}\");\n#endif\n\t\t\t\t\t\textInstance.pathMode = ExtCitizenInstance.ExtPathMode.CalculatingCarPathToTarget;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * determine path type from path mode\n\t\t\t\t */\n\t\t\t\textPathType = extInstance.GetPathType();\n\t\t\t\textInstance.atOutsideConnection = startsAtOutsideConnection;\n\t\t\t\t/*\n\t\t\t\t * the following holds:\n\t\t\t\t * - pathMode is now either CalculatingWalkingPathToParkedCar, CalculatingWalkingPathToTarget, CalculatingCarPathToTarget, CalculatingCarPathToKnownParkPos or None.\n\t\t\t\t */\n\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\n\t\t\t/*\n\t\t\t * enable random parking if exact parking space was not calculated yet\n\t\t\t */\n\t\t\tif (extVehicleType == ExtVehicleType.PassengerCar || extVehicleType == ExtVehicleType.Bicycle) {\n\t\t\t\tif (allowRandomParking &&\n\t\t\t\t\tinstanceData.m_targetBuilding != 0 &&\n\t\t\t\t\t(\n\t\t\t\t\t\tSingleton<BuildingManager>.instance.m_buildings.m_buffer[instanceData.m_targetBuilding].Info.m_class.m_service > ItemClass.Service.Office ||\n\t\t\t\t\t\t(instanceData.m_flags & CitizenInstance.Flags.TargetIsNode) != 0\n\t\t\t\t\t)) {\n\t\t\t\t\trandomParking = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// NON-STOCK CODE END\n\n\t\t\t/*\n\t\t\t * determine the path position of the parked vehicle\n\t\t\t */\n\t\t\tPathUnit.Position parkedVehiclePathPos = default(PathUnit.Position);\n\t\t\tif (parkedVehicleId != 0 && extVehicleType == ExtVehicleType.PassengerCar) {\n\t\t\t\tVector3 position = Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position;\n\t\t\t\tCustomPathManager.FindPathPositionWithSpiralLoop(position, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, false, false, GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance, out parkedVehiclePathPos);\n\t\t\t}\n\t\t\tbool allowUnderground = (instanceData.m_flags & (CitizenInstance.Flags.Underground | CitizenInstance.Flags.Transition)) != CitizenInstance.Flags.None;\n\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Requesting path-finding for citizen instance {instanceID}, citizen {instanceData.m_citizen}, extVehicleType={extVehicleType}, extPathType={extPathType}, startPos={startPos}, endPos={endPos}, sourceBuilding={instanceData.m_sourceBuilding}, targetBuilding={instanceData.m_targetBuilding} pathMode={extInstance.pathMode}\");\n#endif\n\n\t\t\t/*\n\t\t\t * determine start & end path positions\n\t\t\t */\n\t\t\tbool foundEndPos = !calculateEndPos || FindPathPosition(instanceID, ref instanceData, endPos, Options.prohibitPocketCars && (instanceData.m_targetBuilding == 0 || (Singleton<BuildingManager>.instance.m_buildings.m_buffer[instanceData.m_targetBuilding].m_flags & Building.Flags.IncomingOutgoing) == Building.Flags.None) ? NetInfo.LaneType.Pedestrian : laneTypes, vehicleTypes, false, out endPosA); // NON-STOCK CODE: with Parking AI enabled, the end position must be a pedestrian position\n\t\t\tbool foundStartPos = false;\n\t\t\tPathUnit.Position startPosA;\n\n\t\t\tif (Options.prohibitPocketCars && (extInstance.pathMode == ExtPathMode.CalculatingCarPathToTarget || extInstance.pathMode == ExtPathMode.CalculatingCarPathToKnownParkPos)) {\n\t\t\t\t/*\n\t\t\t\t * citizen will enter their car now\n\t\t\t\t * -> find a road start position\n\t\t\t\t */\n\t\t\t\tfoundStartPos = CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, laneTypes & ~NetInfo.LaneType.Pedestrian, vehicleTypes, allowUnderground, false, GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance, out startPosA);\n\t\t\t} else {\n\t\t\t\tfoundStartPos = FindPathPosition(instanceID, ref instanceData, startPos, laneTypes, vehicleTypes, allowUnderground, out startPosA);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * start path-finding\n\t\t\t */\n\t\t\tif (foundStartPos && // TODO probably fails if vehicle is parked too far away from road\n\t\t\t\tfoundEndPos // NON-STOCK CODE\n\t\t\t\t) {\n\n\t\t\t\tif (enableTransport) {\n\t\t\t\t\t/*\n\t\t\t\t\t * public transport usage is allowed for this path\n\t\t\t\t\t */\n\t\t\t\t\tif ((instanceData.m_flags & CitizenInstance.Flags.CannotUseTransport) == CitizenInstance.Flags.None) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t* citizen may use public transport\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tlaneTypes |= NetInfo.LaneType.PublicTransport;\n\n\t\t\t\t\t\tuint citizenId = instanceData.m_citizen;\n\t\t\t\t\t\tif (citizenId != 0u && (citizenManager.m_citizens.m_buffer[citizenId].m_flags & Citizen.Flags.Evacuating) != Citizen.Flags.None) {\n\t\t\t\t\t\t\tlaneTypes |= NetInfo.LaneType.EvacuationTransport;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (Options.prohibitPocketCars) { // TODO check for incoming connection\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t* citizen tried to use public transport but waiting time was too long\n\t\t\t\t\t\t* -> add public transport demand for source building\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tif (instanceData.m_sourceBuilding != 0) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Citizen instance {instanceID} cannot uses public transport from building {instanceData.m_sourceBuilding} to {instanceData.m_targetBuilding}. Incrementing public transport demand.\");\n#endif\n\t\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[instanceData.m_sourceBuilding].AddPublicTransportDemand((uint)GlobalConfig.Instance.ParkingAI.PublicTransportDemandWaitingIncrement, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tPathUnit.Position dummyPathPos = default(PathUnit.Position);\n\t\t\t\tuint path;\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = extPathType;\n\t\t\t\targs.extVehicleType = extVehicleType;\n\t\t\t\targs.vehicleId = 0;\n\t\t\t\targs.spawned = (instanceData.m_flags & CitizenInstance.Flags.Character) != CitizenInstance.Flags.None;\n\t\t\t\targs.buildIndex = Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\t\targs.startPosA = startPosA;\n\t\t\t\targs.startPosB = dummyPathPos;\n\t\t\t\targs.endPosA = endPosA;\n\t\t\t\targs.endPosB = dummyPathPos;\n\t\t\t\targs.vehiclePosition = parkedVehiclePathPos;\n\t\t\t\targs.laneTypes = laneTypes;\n\t\t\t\targs.vehicleTypes = vehicleTypes;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = false;\n\t\t\t\targs.hasCombustionEngine = combustionEngine;\n\t\t\t\targs.ignoreBlocked = false;\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.ignoreCosts = ignoreCost;\n\t\t\t\targs.randomParking = randomParking;\n\t\t\t\targs.stablePath = false;\n\t\t\t\targs.skipQueue = false;\n\n\t\t\t\tif ((instanceData.m_flags & CitizenInstance.Flags.OnTour) != 0) {\n\t\t\t\t\targs.stablePath = true;\n\t\t\t\t\targs.maxLength = 160000f;\n\t\t\t\t\t//args.laneTypes &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t} else {\n\t\t\t\t\targs.stablePath = false;\n\t\t\t\t\targs.maxLength = 20000f;\n\t\t\t\t}\n\n\t\t\t\tbool res = CustomPathManager._instance.CreatePath(out path, ref Singleton<SimulationManager>.instance.m_randomizer, args);\n\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\tif (res) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): Path-finding starts for citizen instance {instanceID}, path={path}, extVehicleType={extVehicleType}, extPathType={extPathType}, startPosA.segment={startPosA.m_segment}, startPosA.lane={startPosA.m_lane}, laneType={laneTypes}, vehicleType={vehicleTypes}, endPosA.segment={endPosA.m_segment}, endPosA.lane={endPosA.m_lane}, vehiclePos.m_segment={parkedVehiclePathPos.m_segment}, vehiclePos.m_lane={parkedVehiclePathPos.m_lane}, vehiclePos.m_offset={parkedVehiclePathPos.m_offset}\");\n#endif\n\n\t\t\t\t\tif (instanceData.m_path != 0u) {\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(instanceData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tinstanceData.m_path = path;\n\t\t\t\t\tinstanceData.m_flags |= CitizenInstance.Flags.WaitingPath;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomCitizenAI.ExtStartPathFind({instanceID}): CustomCitizenAI.CustomStartPathFind: [PFFAIL] failed for citizen instance {instanceID} (CurrentPathMode={extInstance.pathMode}). startPosA.segment={startPosA.m_segment}, startPosA.lane={startPosA.m_lane}, startPosA.offset={startPosA.m_offset}, endPosA.segment={endPosA.m_segment}, endPosA.lane={endPosA.m_lane}, endPosA.offset={endPosA.m_offset}, foundStartPos={foundStartPos}, foundEndPos={foundEndPos}\");\n\t\t\t\textInstance.Reset();\n\t\t\t}\n#endif\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic bool CustomFindPathPosition(ushort instanceID, ref CitizenInstance citizenData, Vector3 pos, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, bool allowUnderground, out PathUnit.Position position) {\n\t\t\treturn CustomPathManager.FindCitizenPathPosition(pos, laneTypes, vehicleTypes, (citizenData.m_flags & CitizenInstance.Flags.CannotUseTransport) == CitizenInstance.Flags.None, allowUnderground, out position);\n\t\t}\n\n\t\t// stock code\n\t\tinternal static Citizen.AgeGroup GetAgeGroup(Citizen.AgePhase agePhase) {\n\t\t\tswitch (agePhase) {\n\t\t\t\tcase Citizen.AgePhase.Child:\n\t\t\t\t\treturn Citizen.AgeGroup.Child;\n\t\t\t\tcase Citizen.AgePhase.Teen0:\n\t\t\t\tcase Citizen.AgePhase.Teen1:\n\t\t\t\t\treturn Citizen.AgeGroup.Teen;\n\t\t\t\tcase Citizen.AgePhase.Young0:\n\t\t\t\tcase Citizen.AgePhase.Young1:\n\t\t\t\tcase Citizen.AgePhase.Young2:\n\t\t\t\t\treturn Citizen.AgeGroup.Young;\n\t\t\t\tcase Citizen.AgePhase.Adult0:\n\t\t\t\tcase Citizen.AgePhase.Adult1:\n\t\t\t\tcase Citizen.AgePhase.Adult2:\n\t\t\t\tcase Citizen.AgePhase.Adult3:\n\t\t\t\t\treturn Citizen.AgeGroup.Adult;\n\t\t\t\tcase Citizen.AgePhase.Senior0:\n\t\t\t\tcase Citizen.AgePhase.Senior1:\n\t\t\t\tcase Citizen.AgePhase.Senior2:\n\t\t\t\tcase Citizen.AgePhase.Senior3:\n\t\t\t\t\treturn Citizen.AgeGroup.Senior;\n\t\t\t\tdefault:\n\t\t\t\t\treturn Citizen.AgeGroup.Adult;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomCommonBuildingAI.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons.Benchmark;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\n\nnamespace TrafficManager.Custom.AI {\n\tpublic class CustomCommonBuildingAI : BuildingAI {\n\t\tpublic void CustomSimulationStep(ushort buildingID, ref Building data) {\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"ExtSimulationStep\")) {\n#endif\n\t\t\t\t// slowly decrease parking space demand / public transport demand\n\t\t\t\tuint frameIndex = Singleton<SimulationManager>.instance.m_currentFrameIndex >> 8;\n\t\t\t\tif ((frameIndex & 1u) == 0u) {\n\t\t\t\t\tExtSimulationStep(buildingID, ref data, ref ExtBuildingManager.Instance.ExtBuildings[buildingID]);\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tbase.SimulationStep(buildingID, ref data);\n\t\t\tif ((data.m_flags & Building.Flags.Demolishing) != Building.Flags.None) {\n\t\t\t\tuint rand = (uint)(((int)buildingID << 8) / 49152);\n\t\t\t\tuint frameIndexRand = Singleton<SimulationManager>.instance.m_currentFrameIndex - rand;\n\t\t\t\tif ((data.m_flags & Building.Flags.Collapsed) == Building.Flags.None || data.GetFrameData(frameIndexRand - 256u).m_constructState == 0) {\n\t\t\t\t\tSingleton<BuildingManager>.instance.ReleaseBuilding(buildingID);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tinternal void ExtSimulationStep(ushort buildingID, ref Building data, ref ExtBuilding extBuilding) {\n\t\t\textBuilding.RemoveParkingSpaceDemand(GlobalConfig.Instance.ParkingAI.ParkingSpaceDemandDecrement);\n\t\t\textBuilding.RemovePublicTransportDemand(GlobalConfig.Instance.ParkingAI.PublicTransportDemandDecrement, true);\n\t\t\textBuilding.RemovePublicTransportDemand(GlobalConfig.Instance.ParkingAI.PublicTransportDemandDecrement, false);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomFireTruckAI.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons.Benchmark;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing UnityEngine;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\tclass CustomFireTruckAI : CarAI {\n\t\tpublic bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {\n#if DEBUG\n\t\t\t//Log._Debug($\"CustomFireTruckAI.CustomStartPathFind called for vehicle {vehicleID}\");\n#endif\n\t\t\tExtVehicleType vehicleType = ExtVehicleType.None;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"OnStartPathFind\")) {\n#endif\n\t\t\t\tvehicleType = VehicleStateManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, (vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0 ? ExtVehicleType.Emergency : ExtVehicleType.Service);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\tVehicleInfo info = this.m_info;\n\t\t\tbool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;\n\t\t\tPathUnit.Position startPosA;\n\t\t\tPathUnit.Position startPosB;\n\t\t\tfloat startDistSqrA;\n\t\t\tfloat startDistSqrB;\n\t\t\tPathUnit.Position endPosA;\n\t\t\tPathUnit.Position endPosB;\n\t\t\tfloat endDistSqrA;\n\t\t\tfloat endDistSqrB;\n\t\t\tif (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) &&\n\t\t\t\tCustomPathManager.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)) {\n\t\t\t\tif (!startBothWays || startDistSqrA < 10f) {\n\t\t\t\t\tstartPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tif (!endBothWays || endDistSqrA < 10f) {\n\t\t\t\t\tendPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tuint path;\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = ExtCitizenInstance.ExtPathType.None;\n\t\t\t\targs.extVehicleType = vehicleType;\n\t\t\t\targs.vehicleId = vehicleID;\n\t\t\t\targs.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\t\targs.buildIndex = Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\t\targs.startPosA = startPosA;\n\t\t\t\targs.startPosB = startPosB;\n\t\t\t\targs.endPosA = endPosA;\n\t\t\t\targs.endPosB = endPosB;\n\t\t\t\targs.vehiclePosition = default(PathUnit.Position);\n\t\t\t\targs.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;\n\t\t\t\targs.vehicleTypes = info.m_vehicleType;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = this.IsHeavyVehicle();\n\t\t\t\targs.hasCombustionEngine = this.CombustionEngine();\n\t\t\t\targs.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.ignoreCosts = false;\n\t\t\t\targs.randomParking = false;\n\t\t\t\targs.stablePath = false;\n\t\t\t\targs.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\n\t\t\t\tif (CustomPathManager._instance.CreatePath(out path, ref Singleton<SimulationManager>.instance.m_randomizer, args)) {\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tvehicleData.m_path = path;\n\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.WaitingPath;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tPathfindFailure(vehicleID, ref vehicleData);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomHumanAI.cs",
    "content": "using ColossalFramework;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Manager;\nusing UnityEngine;\nusing TrafficManager.Traffic;\nusing TrafficManager.Custom.PathFinding;\nusing System;\nusing TrafficManager.Util;\nusing ColossalFramework.Math;\nusing TrafficManager.UI;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing System.Runtime.CompilerServices;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\nusing TrafficManager.Traffic.Data;\nusing CSUtil.Commons.Benchmark;\n\nnamespace TrafficManager.Custom.AI {\n\tclass CustomHumanAI : CitizenAI {\n\t\tpublic void CustomSimulationStep(ushort instanceID, ref CitizenInstance instanceData, Vector3 physicsLodRefPos) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceID) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == instanceData.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == instanceData.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == instanceData.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n#endif\n\n\t\t\tCitizenManager citizenManager = Singleton<CitizenManager>.instance;\n\n\t\t\tuint citizenId = instanceData.m_citizen;\n\t\t\tif ((instanceData.m_flags & (CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating)) != CitizenInstance.Flags.None && (instanceData.m_flags & CitizenInstance.Flags.Character) == CitizenInstance.Flags.None) {\n\t\t\t\tcitizenManager.ReleaseCitizenInstance(instanceID);\n\t\t\t\tif (citizenId != 0u) {\n\t\t\t\t\tcitizenManager.ReleaseCitizen(citizenId);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ((instanceData.m_flags & CitizenInstance.Flags.WaitingPath) != CitizenInstance.Flags.None) {\n\t\t\t\tPathManager pathManager = Singleton<PathManager>.instance;\n\t\t\t\tbyte pathFindFlags = pathManager.m_pathUnits.m_buffer[instanceData.m_path].m_pathFindFlags;\n\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tExtPathState mainPathState = ExtPathState.Calculating;\n\t\t\t\tif ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || instanceData.m_path == 0) {\n\t\t\t\t\tmainPathState = ExtPathState.Failed;\n\t\t\t\t} else if ((pathFindFlags & PathUnit.FLAG_READY) != 0) {\n\t\t\t\t\tmainPathState = ExtPathState.Ready;\n\t\t\t\t}\n\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomHumanAI.CustomSimulationStep({instanceID}): Path: {instanceData.m_path}, mainPathState={mainPathState}\");\n#endif\n\n\t\t\t\tExtSoftPathState finalPathState = ExtSoftPathState.None;\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"ConvertPathStateToSoftPathState+UpdateCitizenPathState\")) {\n#endif\n\t\t\t\t\tfinalPathState = ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);\n\t\t\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\t\t\tfinalPathState = AdvancedParkingManager.Instance.UpdateCitizenPathState(instanceID, ref instanceData, ref ExtCitizenInstanceManager.Instance.ExtInstances[instanceID], ref ExtCitizenManager.Instance.ExtCitizens[citizenId], ref citizenManager.m_citizens.m_buffer[instanceData.m_citizen], mainPathState);\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomHumanAI.CustomSimulationStep({instanceID}): Applied Parking AI logic. Path: {instanceData.m_path}, mainPathState={mainPathState}, finalPathState={finalPathState}, extCitizenInstance={ExtCitizenInstanceManager.Instance.ExtInstances[instanceID]}\");\n#endif\n\t\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\n\t\t\t\tswitch (finalPathState) {\n\t\t\t\t\tcase ExtSoftPathState.Ready:\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomHumanAI.CustomSimulationStep({instanceID}): Path-finding succeeded for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- calling HumanAI.PathfindSuccess\");\n#endif\n\t\t\t\t\t\tif (citizenId == 0 || citizenManager.m_citizens.m_buffer[instanceData.m_citizen].m_vehicle == 0) {\n\t\t\t\t\t\t\tthis.Spawn(instanceID, ref instanceData);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tinstanceData.m_pathPositionIndex = 255;\n\t\t\t\t\t\tinstanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;\n\t\t\t\t\t\tinstanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering);\n\t\t\t\t\t\t// NON-STOCK CODE START (transferred from ResidentAI.PathfindSuccess)\n\t\t\t\t\t\tif (citizenId != 0 && (citizenManager.m_citizens.m_buffer[citizenId].m_flags & (Citizen.Flags.Tourist | Citizen.Flags.MovingIn | Citizen.Flags.DummyTraffic)) == Citizen.Flags.MovingIn) {\n\t\t\t\t\t\t\tStatisticBase statisticBase = Singleton<StatisticsManager>.instance.Acquire<StatisticInt32>(StatisticType.MoveRate);\n\t\t\t\t\t\t\tstatisticBase.Add(1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t\tthis.PathfindSuccess(instanceID, ref instanceData);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtSoftPathState.Ignore:\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomHumanAI.CustomSimulationStep({instanceID}): Path-finding result shall be ignored for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- ignoring\");\n#endif\n\t\t\t\t\t\treturn;\n\t\t\t\t\tcase ExtSoftPathState.Calculating:\n\t\t\t\t\tdefault:\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomHumanAI.CustomSimulationStep({instanceID}): Path-finding result undetermined for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- continue\");\n#endif\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtSoftPathState.FailedHard:\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomHumanAI.CustomSimulationStep({instanceID}): HARD path-finding failure for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- calling HumanAI.PathfindFailure\");\n#endif\n\t\t\t\t\t\tinstanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;\n\t\t\t\t\t\tinstanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering);\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(instanceData.m_path);\n\t\t\t\t\t\tinstanceData.m_path = 0u;\n\t\t\t\t\t\tthis.PathfindFailure(instanceID, ref instanceData);\n\t\t\t\t\t\treturn;\n\t\t\t\t\tcase ExtSoftPathState.FailedSoft:\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomHumanAI.CustomSimulationStep({instanceID}): SOFT path-finding failure for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- calling HumanAI.InvalidPath\");\n#endif\n\t\t\t\t\t\t// path mode has been updated, repeat path-finding\n\t\t\t\t\t\tinstanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;\n\t\t\t\t\t\tinstanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering);\n\t\t\t\t\t\tthis.InvalidPath(instanceID, ref instanceData);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"ExtSimulationStep\")) {\n#endif\n\t\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\t\tif (ExtSimulationStep(instanceID, ref instanceData, ref ExtCitizenInstanceManager.Instance.ExtInstances[instanceID], physicsLodRefPos)) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tbase.SimulationStep(instanceID, ref instanceData, physicsLodRefPos);\n\n\t\t\tVehicleManager vehicleManager = Singleton<VehicleManager>.instance;\n\t\t\tushort vehicleId = 0;\n\t\t\tif (instanceData.m_citizen != 0u) {\n\t\t\t\tvehicleId = citizenManager.m_citizens.m_buffer[instanceData.m_citizen].m_vehicle;\n\t\t\t}\n\t\t\tif (vehicleId != 0) {\n\t\t\t\tVehicleInfo vehicleInfo = vehicleManager.m_vehicles.m_buffer[(int)vehicleId].Info;\n\t\t\t\tif (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Bicycle) {\n\t\t\t\t\tvehicleInfo.m_vehicleAI.SimulationStep(vehicleId, ref vehicleManager.m_vehicles.m_buffer[(int)vehicleId], vehicleId, ref vehicleManager.m_vehicles.m_buffer[(int)vehicleId], 0);\n\t\t\t\t\tvehicleId = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (vehicleId == 0 && (instanceData.m_flags & (CitizenInstance.Flags.Character | CitizenInstance.Flags.WaitingPath | CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating)) == CitizenInstance.Flags.None) {\n\t\t\t\tinstanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown);\n\t\t\t\tCustomArriveAtDestination(instanceID, ref instanceData, false);\n\t\t\t\tcitizenManager.ReleaseCitizenInstance(instanceID);\n\t\t\t}\n\t\t}\n\n\t\tinternal bool ExtSimulationStep(ushort instanceID, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance, Vector3 physicsLodRefPos) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceID) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == instanceData.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == instanceData.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == instanceData.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n#endif\n\n\t\t\t// check if the citizen has reached a parked car or target\n\t\t\tif (extInstance.pathMode == ExtPathMode.WalkingToParkedCar || extInstance.pathMode == ExtPathMode.ApproachingParkedCar) {\n\t\t\t\tushort parkedVehicleId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[instanceData.m_citizen].m_parkedVehicle;\n\t\t\t\tif (parkedVehicleId == 0) {\n\t\t\t\t\t// citizen is reaching their parked car but does not own a parked car\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog.Warning($\"CustomHumanAI.ExtSimulationStep({instanceID}): Citizen instance {instanceID} was walking to / reaching their parked car ({extInstance.pathMode}) but parked car has disappeared. RESET.\");\n#endif\n\n\t\t\t\t\textInstance.Reset();\n\n\t\t\t\t\tinstanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;\n\t\t\t\t\tinstanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering);\n\t\t\t\t\tthis.InvalidPath(instanceID, ref instanceData);\n\t\t\t\t\treturn true;\n\t\t\t\t} else {\n\t\t\t\t\tParkedCarApproachState approachState = AdvancedParkingManager.Instance.CitizenApproachingParkedCarSimulationStep(instanceID, ref instanceData, ref extInstance, physicsLodRefPos, ref Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId]);\n\t\t\t\t\tswitch (approachState) {\n\t\t\t\t\t\tcase ParkedCarApproachState.None:\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase ParkedCarApproachState.Approaching:\n\t\t\t\t\t\t\t// citizen approaches their parked car\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\tcase ParkedCarApproachState.Approached:\n\t\t\t\t\t\t\t// citizen reached their parked car\n#if DEBUG\n\t\t\t\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomHumanAI.CustomSimulationStep({instanceID}): Citizen instance {instanceID} arrived at parked car. PathMode={extInstance.pathMode}\");\n#endif\n\t\t\t\t\t\t\tif (instanceData.m_path != 0) {\n\t\t\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(instanceData.m_path);\n\t\t\t\t\t\t\t\tinstanceData.m_path = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tinstanceData.m_flags = instanceData.m_flags & (CitizenInstance.Flags.Created | CitizenInstance.Flags.Cheering | CitizenInstance.Flags.Deleted | CitizenInstance.Flags.Underground | CitizenInstance.Flags.CustomName | CitizenInstance.Flags.Character | CitizenInstance.Flags.BorrowCar | CitizenInstance.Flags.HangAround | CitizenInstance.Flags.InsideBuilding | CitizenInstance.Flags.WaitingPath | CitizenInstance.Flags.TryingSpawnVehicle | CitizenInstance.Flags.CannotUseTransport | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.OnPath | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.AtTarget | CitizenInstance.Flags.RequireSlowStart | CitizenInstance.Flags.Transition | CitizenInstance.Flags.RidingBicycle | CitizenInstance.Flags.OnBikeLane | CitizenInstance.Flags.CannotUseTaxi | CitizenInstance.Flags.CustomColor | CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating | CitizenInstance.Flags.TargetFlags);\n\t\t\t\t\t\t\tif (!this.StartPathFind(instanceID, ref instanceData)) {\n\t\t\t\t\t\t\t\tinstanceData.Unspawn(instanceID);\n\t\t\t\t\t\t\t\textInstance.Reset();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\tcase ParkedCarApproachState.Failure:\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomHumanAI.ExtSimulationStep({instanceID}): Citizen instance {instanceID} failed to arrive at parked car. PathMode={extInstance.pathMode}\");\n#endif\n\t\t\t\t\t\t\t// repeat path-finding\n\t\t\t\t\t\t\tinstanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;\n\t\t\t\t\t\t\tinstanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering);\n\t\t\t\t\t\t\tthis.InvalidPath(instanceID, ref instanceData);\n\t\t\t\t\t\t\treturn true;\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (extInstance.pathMode == ExtCitizenInstance.ExtPathMode.WalkingToTarget ||\n\t\t\t\t\textInstance.pathMode == ExtCitizenInstance.ExtPathMode.TaxiToTarget\n\t\t\t) {\n\t\t\t\tAdvancedParkingManager.Instance.CitizenApproachingTargetSimulationStep(instanceID, ref instanceData, ref extInstance);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Makes the given citizen instance enter their parked car.\n\t\t/// </summary>\n\t\t/// <param name=\"instanceID\">Citizen instance id</param>\n\t\t/// <param name=\"instanceData\">Citizen instance data</param>\n\t\t/// <param name=\"parkedVehicleId\">Parked vehicle id</param>\n\t\t/// <param name=\"vehicleId\">Vehicle id</param>\n\t\t/// <returns>true if entering the car succeeded, false otherwise</returns>\n\t\tpublic static bool EnterParkedCar(ushort instanceID, ref CitizenInstance instanceData, ushort parkedVehicleId, out ushort vehicleId) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceID) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == instanceData.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == instanceData.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == instanceData.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"CustomHumanAI.EnterParkedCar({instanceID}, ..., {parkedVehicleId}) called.\");\n#endif\n\t\t\tVehicleManager vehManager = Singleton<VehicleManager>.instance;\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tCitizenManager citManager = Singleton<CitizenManager>.instance;\n\n\t\t\tVector3 parkedVehPos = vehManager.m_parkedVehicles.m_buffer[parkedVehicleId].m_position;\n\t\t\tQuaternion parkedVehRot = vehManager.m_parkedVehicles.m_buffer[parkedVehicleId].m_rotation;\n\t\t\tVehicleInfo vehicleInfo = vehManager.m_parkedVehicles.m_buffer[parkedVehicleId].Info;\n\n\t\t\tPathUnit.Position vehLanePathPos;\n\t\t\tif (! CustomPathManager._instance.m_pathUnits.m_buffer[instanceData.m_path].GetPosition(0, out vehLanePathPos)) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomHumanAI.EnterParkedCar({instanceID}): Could not get first car path position of citizen instance {instanceID}!\");\n#endif\n\n\t\t\t\tvehicleId = 0;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tuint vehLaneId = PathManager.GetLaneID(vehLanePathPos);\n#if DEBUG\n\t\t\tif (fineDebug)\n\t\t\t\tLog._Debug($\"CustomHumanAI.EnterParkedCar({instanceID}): Determined vehicle position for citizen instance {instanceID}: seg. {vehLanePathPos.m_segment}, lane {vehLanePathPos.m_lane}, off {vehLanePathPos.m_offset} (lane id {vehLaneId})\");\n#endif\n\n\t\t\tVector3 vehLanePos;\n\t\t\tfloat vehLaneOff;\n\t\t\tnetManager.m_lanes.m_buffer[vehLaneId].GetClosestPosition(parkedVehPos, out vehLanePos, out vehLaneOff);\n\t\t\tbyte vehLaneOffset = (byte)Mathf.Clamp(Mathf.RoundToInt(vehLaneOff * 255f), 0, 255);\n\n\t\t\t// movement vector from parked vehicle position to road position\n\t\t\tVector3 forwardVector = parkedVehPos + Vector3.ClampMagnitude(vehLanePos - parkedVehPos, 5f);\n\t\t\tif (vehManager.CreateVehicle(out vehicleId, ref Singleton<SimulationManager>.instance.m_randomizer, vehicleInfo, parkedVehPos, TransferManager.TransferReason.None, false, false)) {\n\t\t\t\t// update frame data\n\t\t\t\tVehicle.Frame frame = vehManager.m_vehicles.m_buffer[(int)vehicleId].m_frame0;\n\t\t\t\tframe.m_rotation = parkedVehRot;\n\n\t\t\t\tvehManager.m_vehicles.m_buffer[vehicleId].m_frame0 = frame;\n\t\t\t\tvehManager.m_vehicles.m_buffer[vehicleId].m_frame1 = frame;\n\t\t\t\tvehManager.m_vehicles.m_buffer[vehicleId].m_frame2 = frame;\n\t\t\t\tvehManager.m_vehicles.m_buffer[vehicleId].m_frame3 = frame;\n\t\t\t\tvehicleInfo.m_vehicleAI.FrameDataUpdated(vehicleId, ref vehManager.m_vehicles.m_buffer[vehicleId], ref frame);\n\n\t\t\t\t// update vehicle target position\n\t\t\t\tvehManager.m_vehicles.m_buffer[vehicleId].m_targetPos0 = new Vector4(vehLanePos.x, vehLanePos.y, vehLanePos.z, 2f);\n\n\t\t\t\t// update other fields\n\t\t\t\tvehManager.m_vehicles.m_buffer[vehicleId].m_flags = (vehManager.m_vehicles.m_buffer[vehicleId].m_flags | Vehicle.Flags.Stopped);\n\t\t\t\tvehManager.m_vehicles.m_buffer[vehicleId].m_path = instanceData.m_path;\n\t\t\t\tvehManager.m_vehicles.m_buffer[vehicleId].m_pathPositionIndex = 0;\n\t\t\t\tvehManager.m_vehicles.m_buffer[vehicleId].m_lastPathOffset = vehLaneOffset;\n\t\t\t\tvehManager.m_vehicles.m_buffer[vehicleId].m_transferSize = (ushort)(instanceData.m_citizen & 65535u);\n\n\t\t\t\tif (! vehicleInfo.m_vehicleAI.TrySpawn(vehicleId, ref vehManager.m_vehicles.m_buffer[vehicleId])) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomHumanAI.EnterParkedCar({instanceID}): Could not spawn a {vehicleInfo.m_vehicleType} for citizen instance {instanceID}!\");\n#endif\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// change instances\n\t\t\t\tInstanceID parkedVehInstance = InstanceID.Empty;\n\t\t\t\tparkedVehInstance.ParkedVehicle = parkedVehicleId;\n\t\t\t\tInstanceID vehInstance = InstanceID.Empty;\n\t\t\t\tvehInstance.Vehicle = vehicleId;\n\t\t\t\tSingleton<InstanceManager>.instance.ChangeInstance(parkedVehInstance, vehInstance);\n\n\t\t\t\t// set vehicle id for citizen instance\n\t\t\t\tinstanceData.m_path = 0u;\n\t\t\t\tcitManager.m_citizens.m_buffer[instanceData.m_citizen].SetParkedVehicle(instanceData.m_citizen, 0);\n\t\t\t\tcitManager.m_citizens.m_buffer[instanceData.m_citizen].SetVehicle(instanceData.m_citizen, vehicleId, 0u);\n\n\t\t\t\t// update citizen instance flags\n\t\t\t\tinstanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath;\n\t\t\t\tinstanceData.m_flags &= ~CitizenInstance.Flags.EnteringVehicle;\n\t\t\t\tinstanceData.m_flags &= ~CitizenInstance.Flags.TryingSpawnVehicle;\n\t\t\t\tinstanceData.m_flags &= ~CitizenInstance.Flags.BoredOfWaiting;\n\t\t\t\tinstanceData.m_waitCounter = 0;\n\n\t\t\t\t// unspawn citizen instance\n\t\t\t\tinstanceData.Unspawn(instanceID);\n\n#if DEBUG\n\t\t\t\tif (fineDebug)\n\t\t\t\t\tLog._Debug($\"CustomHumanAI.EnterParkedCar({instanceID}): Citizen instance {instanceID} is now entering vehicle {vehicleId}. Set vehicle target position to {vehLanePos} (segment={vehLanePathPos.m_segment}, lane={vehLanePathPos.m_lane}, offset={vehLanePathPos.m_offset})\");\n#endif\n\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\t// failed to find a road position\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomHumanAI.EnterParkedCar({instanceID}): Could not find a road position for citizen instance {instanceID} near parked vehicle {parkedVehicleId}!\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tpublic bool CustomCheckTrafficLights(ushort nodeId, ushort segmentId) {\n#if DEBUGTTL\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == nodeId;\n#endif\n\n\t\t\tvar netManager = Singleton<NetManager>.instance;\n\n\t\t\tvar currentFrameIndex = Singleton<SimulationManager>.instance.m_currentFrameIndex;\n\t\t\tvar num = (uint)(((int)nodeId << 8) / 32768);\n\t\t\tvar stepWaitTime = currentFrameIndex - num & 255u;\n\n\t\t\t// NON-STOCK CODE START //\n\n\t\t\tbool customSim = false;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"GetNodeSimulation\")) {\n#endif\n\t\t\t\tcustomSim = Options.timedLightsEnabled && TrafficLightSimulationManager.Instance.HasActiveSimulation(nodeId);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\tRoadBaseAI.TrafficLightState pedestrianLightState;\n\t\t\tbool startNode = netManager.m_segments.m_buffer[segmentId].m_startNode == nodeId;\n\n\t\t\tICustomSegmentLights lights = null;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"GetSegmentLights\")) {\n#endif\n\t\t\t\tif (customSim) {\n\t\t\t\t\tlights = CustomSegmentLightsManager.Instance.GetSegmentLights(segmentId, startNode, false);\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\n\t\t\tif (lights == null) {\n\t\t\t\t// NON-STOCK CODE END //\n\t\t\t\tRoadBaseAI.TrafficLightState vehicleLightState;\n\t\t\t\tbool vehicles;\n\t\t\t\tbool pedestrians;\n\n#if DEBUGTTL\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"CustomHumanAI.CustomCheckTrafficLights({nodeId}, {segmentId}): No custom simulation!\");\n\t\t\t\t}\n#endif\n\n\t\t\t\tRoadBaseAI.GetTrafficLightState(nodeId, ref netManager.m_segments.m_buffer[segmentId], currentFrameIndex - num, out vehicleLightState, out pedestrianLightState, out vehicles, out pedestrians);\n\t\t\t\tif (pedestrianLightState == RoadBaseAI.TrafficLightState.GreenToRed || pedestrianLightState ==  RoadBaseAI.TrafficLightState.Red) {\n\t\t\t\t\tif (!pedestrians && stepWaitTime >= 196u) {\n\t\t\t\t\t\tRoadBaseAI.SetTrafficLightState(nodeId, ref netManager.m_segments.m_buffer[segmentId], currentFrameIndex - num, vehicleLightState, pedestrianLightState, vehicles, true);\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t// NON-STOCK CODE START //\n\t\t\t} else {\n\n\n\t\t\t\tif (lights.InvalidPedestrianLight) {\n\t\t\t\t\tpedestrianLightState = RoadBaseAI.TrafficLightState.Green;\n\t\t\t\t} else {\n\t\t\t\t\tpedestrianLightState = (RoadBaseAI.TrafficLightState)lights.PedestrianLightState;\n\t\t\t\t}\n\n#if DEBUGTTL\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"CustomHumanAI.CustomCheckTrafficLights({nodeId}, {segmentId}): Custom simulation! pedestrianLightState={pedestrianLightState}, lights.InvalidPedestrianLight={lights.InvalidPedestrianLight}\");\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t\t// NON-STOCK CODE END //\n\n\t\t\tswitch (pedestrianLightState) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.RedToGreen:\n\t\t\t\t\tif (stepWaitTime < 60u) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\tcase RoadBaseAI.TrafficLightState.GreenToRed:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tprotected void CustomArriveAtDestination(ushort instanceID, ref CitizenInstance citizenData, bool success) {\n\t\t\tuint citizenId = citizenData.m_citizen;\n\t\t\tif (citizenId != 0) {\n\t\t\t\tCitizenManager citizenMan = Singleton<CitizenManager>.instance;\n\t\t\t\tcitizenMan.m_citizens.m_buffer[citizenId].SetVehicle(citizenId, 0, 0u);\n\n\t\t\t\tif ((citizenData.m_flags & CitizenInstance.Flags.TargetIsNode) != CitizenInstance.Flags.None) {\n\t\t\t\t\tif (success) {\n\t\t\t\t\t\tushort targetBuildingId = citizenData.m_targetBuilding;\n\t\t\t\t\t\tif (targetBuildingId != 0) {\n\t\t\t\t\t\t\tushort transportLineId = Singleton<NetManager>.instance.m_nodes.m_buffer[targetBuildingId].m_transportLine;\n\t\t\t\t\t\t\tif (transportLineId != 0) {\n\t\t\t\t\t\t\t\tTransportInfo info = Singleton<TransportManager>.instance.m_lines.m_buffer[transportLineId].Info;\n\t\t\t\t\t\t\t\tif (info.m_vehicleType == VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\t\t\t\ttargetBuildingId = (((instanceID & 1) != 0) ? TransportLine.GetPrevStop(targetBuildingId) : TransportLine.GetNextStop(targetBuildingId));\n\t\t\t\t\t\t\t\t\tif (targetBuildingId != 0) {\n\t\t\t\t\t\t\t\t\t\tcitizenData.m_flags |= CitizenInstance.Flags.OnTour;\n\t\t\t\t\t\t\t\t\t\t((CitizenAI)this).SetTarget(instanceID, ref citizenData, targetBuildingId, true);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// Unrolled goto statement\n\t\t\t\t\t\t\t\t\t\tif ((citizenData.m_flags & CitizenInstance.Flags.HangAround) != 0 && success) {\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t((CitizenAI)this).SetSource(instanceID, ref citizenData, (ushort)0);\n\t\t\t\t\t\t\t\t\t\t((CitizenAI)this).SetTarget(instanceID, ref citizenData, (ushort)0);\n\t\t\t\t\t\t\t\t\t\tcitizenData.Unspawn(instanceID);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcitizenData.m_flags |= CitizenInstance.Flags.OnTour;\n\t\t\t\t\t\t\t\tthis.WaitTouristVehicle(instanceID, ref citizenData, targetBuildingId);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (success) {\n\t\t\t\t\t\tcitizenMan.m_citizens.m_buffer[citizenId].SetLocationByBuilding(citizenId, citizenData.m_targetBuilding);\n\t\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\t\tConstants.ManagerFactory.ExtCitizenManager.OnArriveAtDestination(citizenId, ref citizenMan.m_citizens.m_buffer[citizenId]);\n\t\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t}\n\n\t\t\t\t\tif (citizenData.m_targetBuilding != 0 && citizenMan.m_citizens.m_buffer[citizenId].CurrentLocation == Citizen.Location.Visit) {\n\t\t\t\t\t\tBuildingManager buildingMan = Singleton<BuildingManager>.instance;\n\t\t\t\t\t\tBuildingInfo info = buildingMan.m_buildings.m_buffer[citizenData.m_targetBuilding].Info;\n\t\t\t\t\t\tinfo.m_buildingAI.VisitorEnter(citizenData.m_targetBuilding, ref buildingMan.m_buildings.m_buffer[citizenData.m_targetBuilding], citizenId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((citizenData.m_flags & CitizenInstance.Flags.HangAround) != 0 && success) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t((CitizenAI)this).SetSource(instanceID, ref citizenData, (ushort)0);\n\t\t\t((CitizenAI)this).SetTarget(instanceID, ref citizenData, (ushort)0);\n\t\t\tcitizenData.Unspawn(instanceID);\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate void PathfindFailure(ushort instanceID, ref CitizenInstance data) {\n\t\t\tLog.Error($\"HumanAI.PathfindFailure is not overriden!\");\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate void PathfindSuccess(ushort instanceID, ref CitizenInstance data) {\n\t\t\tLog.Error($\"HumanAI.PathfindSuccess is not overriden!\");\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate void Spawn(ushort instanceID, ref CitizenInstance data) {\n\t\t\tLog.Error($\"HumanAI.Spawn is not overriden!\");\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate void GetBuildingTargetPosition(ushort instanceID, ref CitizenInstance data, float minSqrDistance) {\n\t\t\tLog.Error($\"HumanAI.GetBuildingTargetPosition is not overriden!\");\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate void WaitTouristVehicle(ushort instanceID, ref CitizenInstance data, ushort targetBuildingId) {\n\t\t\tLog.Error($\"HumanAI.InvokeWaitTouristVehicle is not overriden!\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomPassengerCarAI.cs",
    "content": "using System;\nusing ColossalFramework;\nusing UnityEngine;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.Geometry;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager;\nusing ColossalFramework.Math;\nusing TrafficManager.Util;\nusing System.Reflection;\nusing ColossalFramework.Globalization;\nusing TrafficManager.UI;\nusing System.Xml;\nusing System.IO;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Traffic.Data;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\nusing CSUtil.Commons.Benchmark;\nusing System.Runtime.CompilerServices;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\t// TODO move Parking AI features from here to a distinct manager\n\tpublic class CustomPassengerCarAI : CarAI {\n\t\tpublic void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Congestion) != 0 && VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData)) {\n\t\t\t\tSingleton<VehicleManager>.instance.ReleaseVehicle(vehicleId);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbase.SimulationStep(vehicleId, ref vehicleData, physicsLodRefPos);\n\t\t}\n\n\t\tpublic string CustomGetLocalizedStatus(ushort vehicleID, ref Vehicle data, out InstanceID target) {\n\t\t\tCitizenManager citizenManager = Singleton<CitizenManager>.instance;\n\t\t\tushort driverInstanceId = GetDriverInstanceId(vehicleID, ref data);\n\t\t\tushort targetBuildingId = 0;\n\t\t\tbool targetIsNode = false;\n\t\t\tif (driverInstanceId != 0) {\n\t\t\t\tif ((data.m_flags & Vehicle.Flags.Parking) != (Vehicle.Flags)0) {\n\t\t\t\t\tuint citizen = citizenManager.m_instances.m_buffer[(int)driverInstanceId].m_citizen;\n\t\t\t\t\tif (citizen != 0u && citizenManager.m_citizens.m_buffer[citizen].m_parkedVehicle != 0) {\n\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\treturn Locale.Get(\"VEHICLE_STATUS_PARKING\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttargetBuildingId = citizenManager.m_instances.m_buffer[(int)driverInstanceId].m_targetBuilding;\n\t\t\t\ttargetIsNode = ((citizenManager.m_instances.m_buffer[driverInstanceId].m_flags & CitizenInstance.Flags.TargetIsNode) != CitizenInstance.Flags.None);\n\t\t\t}\n\t\t\tif (targetBuildingId == 0) {\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\treturn Locale.Get(\"VEHICLE_STATUS_CONFUSED\");\n\t\t\t}\n\t\t\tstring ret;\n\t\t\tbool leavingCity = (Singleton<BuildingManager>.instance.m_buildings.m_buffer[(int)targetBuildingId].m_flags & Building.Flags.IncomingOutgoing) != Building.Flags.None;\n\t\t\tif (leavingCity) {\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\tret = Locale.Get(\"VEHICLE_STATUS_LEAVING\");\n\t\t\t} else {\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\tif (targetIsNode) {\n\t\t\t\t\ttarget.NetNode = targetBuildingId;\n\t\t\t\t} else {\n\t\t\t\t\ttarget.Building = targetBuildingId;\n\t\t\t\t}\n\n\t\t\t\tret = Locale.Get(\"VEHICLE_STATUS_GOINGTO\");\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"EnrichLocalizedCarStatus\")) {\n#endif\n\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\tret = AdvancedParkingManager.Instance.EnrichLocalizedCarStatus(ret, ref ExtCitizenInstanceManager.Instance.ExtInstances[driverInstanceId]);\n\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic static ushort GetDriverInstanceId(ushort vehicleID, ref Vehicle data) { // TODO reverse-redirect\n\t\t\tCitizenManager instance = Singleton<CitizenManager>.instance;\n\t\t\tuint curCitUnitId = data.m_citizenUnits;\n\t\t\tint numIters = 0;\n\t\t\twhile (curCitUnitId != 0u) {\n\t\t\t\tuint nextUnit = instance.m_units.m_buffer[curCitUnitId].m_nextUnit;\n\t\t\t\tfor (int i = 0; i < 5; i++) {\n\t\t\t\t\tuint citizenId = instance.m_units.m_buffer[curCitUnitId].GetCitizen(i);\n\t\t\t\t\tif (citizenId != 0u) {\n\t\t\t\t\t\tushort citInstanceId = instance.m_citizens.m_buffer[citizenId].m_instance;\n\t\t\t\t\t\tif (citInstanceId != 0) {\n\t\t\t\t\t\t\treturn citInstanceId;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcurCitUnitId = nextUnit;\n\t\t\t\tif (++numIters > 524288) {\n\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tpublic bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {\n\t\t\tushort driverInstanceId = CustomPassengerCarAI.GetDriverInstanceId(vehicleID, ref vehicleData);\n\t\t\tif (driverInstanceId == 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"ExtStartPathFind\")) {\n#endif\n\t\t\treturn ExtStartPathFind(vehicleID, ref vehicleData, driverInstanceId, ref Singleton<CitizenManager>.instance.m_instances.m_buffer[(int)driverInstanceId], ref ExtCitizenInstanceManager.Instance.ExtInstances[driverInstanceId], startPos, endPos, startBothWays, endBothWays, undergroundTarget);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t}\n\n\t\tpublic bool ExtStartPathFind(ushort vehicleID, ref Vehicle vehicleData, ushort driverInstanceId, ref CitizenInstance driverInstance, ref ExtCitizenInstance driverExtInstance, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleID) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == driverExtInstance.instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == driverInstance.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == driverInstance.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == driverInstance.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (debug)\n\t\t\t\tLog.Warning($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): called for vehicle {vehicleID}, driverInstanceId={driverInstanceId}, startPos={startPos}, endPos={endPos}, sourceBuilding={vehicleData.m_sourceBuilding}, targetBuilding={vehicleData.m_targetBuilding} pathMode={driverExtInstance.pathMode}\");\n#endif\n\n\t\t\tPathUnit.Position startPosA = default(PathUnit.Position);\n\t\t\tPathUnit.Position startPosB = default(PathUnit.Position);\n\t\t\tPathUnit.Position endPosA = default(PathUnit.Position);\n\t\t\tfloat sqrDistA = 0f;\n\t\t\tfloat sqrDistB;\n\n\t\t\tushort targetBuildingId = driverInstance.m_targetBuilding;\n\t\t\tuint driverCitizenId = driverInstance.m_citizen;\n\n\t\t\t// NON-STOCK CODE START\n\t\t\tbool calculateEndPos = true;\n\t\t\tbool allowRandomParking = true;\n\t\t\tbool movingToParkingPos = false;\n\t\t\tbool foundStartingPos = false;\n\t\t\tbool skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\tExtPathType extPathType = ExtPathType.None;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"ParkingAI\")) {\n#endif\n\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\t//if (driverExtInstance != null) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog.Warning($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): PathMode={driverExtInstance.pathMode} for vehicle {vehicleID}, driver citizen instance {driverExtInstance.instanceId}!\");\n#endif\n\n\t\t\t\tif (driverExtInstance.pathMode == ExtPathMode.RequiresMixedCarPathToTarget) {\n\t\t\t\t\tdriverExtInstance.pathMode = ExtPathMode.CalculatingCarPathToTarget;\n\t\t\t\t\tstartBothWays = false;\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): PathMode was RequiresDirectCarPathToTarget: Parking spaces will NOT be searched beforehand. Setting pathMode={driverExtInstance.pathMode}\");\n#endif\n\t\t\t\t} else if (\n\t\t\t\t\tdriverExtInstance.pathMode != ExtPathMode.ParkingFailed &&\n\t\t\t\t\ttargetBuildingId != 0 &&\n\t\t\t\t\t(Singleton<BuildingManager>.instance.m_buildings.m_buffer[targetBuildingId].m_flags & Building.Flags.IncomingOutgoing) != Building.Flags.None\n\t\t\t\t) {\n\t\t\t\t\t// target is outside connection\n\t\t\t\t\tdriverExtInstance.pathMode = ExtPathMode.CalculatingCarPathToTarget;\n\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): PathMode was not ParkingFailed and target is outside connection: Setting pathMode={driverExtInstance.pathMode}\");\n#endif\n\t\t\t\t} else {\n\t\t\t\t\tif (driverExtInstance.pathMode == ExtPathMode.DrivingToTarget || driverExtInstance.pathMode == ExtPathMode.DrivingToKnownParkPos || driverExtInstance.pathMode == ExtPathMode.ParkingFailed) {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Skipping queue. pathMode={driverExtInstance.pathMode}\");\n#endif\n\t\t\t\t\t\tskipQueue = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tbool allowTourists = false;\n\t\t\t\t\tbool searchAtCurrentPos = false;\n\t\t\t\t\tif (driverExtInstance.pathMode == ExtPathMode.ParkingFailed) {\n\t\t\t\t\t\t// previous parking attempt failed\n\t\t\t\t\t\tdriverExtInstance.pathMode = ExtPathMode.CalculatingCarPathToAltParkPos;\n\t\t\t\t\t\tallowTourists = true;\n\t\t\t\t\t\tsearchAtCurrentPos = true;\n\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Vehicle {vehicleID} shall move to an alternative parking position! CurrentPathMode={driverExtInstance.pathMode} FailedParkingAttempts={driverExtInstance.failedParkingAttempts}\");\n#endif\n\n\t\t\t\t\t\tif (driverExtInstance.parkingPathStartPosition != null) {\n\t\t\t\t\t\t\tstartPosA = (PathUnit.Position)driverExtInstance.parkingPathStartPosition;\n\t\t\t\t\t\t\tfoundStartingPos = true;\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Setting starting pos for {vehicleID} to segment={startPosA.m_segment}, laneIndex={startPosA.m_lane}, offset={startPosA.m_offset}\");\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstartBothWays = false;\n\n\t\t\t\t\t\tif (driverExtInstance.failedParkingAttempts > GlobalConfig.Instance.ParkingAI.MaxParkingAttempts) {\n\t\t\t\t\t\t\t// maximum number of parking attempts reached\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Reached maximum number of parking attempts for vehicle {vehicleID}! GIVING UP.\");\n#endif\n\t\t\t\t\t\t\tdriverExtInstance.Reset();\n\n\t\t\t\t\t\t\t// pocket car fallback\n\t\t\t\t\t\t\t//vehicleData.m_flags |= Vehicle.Flags.Parking;\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Increased number of parking attempts for vehicle {vehicleID}: {driverExtInstance.failedParkingAttempts}/{GlobalConfig.Instance.ParkingAI.MaxParkingAttempts}\");\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdriverExtInstance.pathMode = ExtPathMode.CalculatingCarPathToKnownParkPos;\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): No parking involved: Setting pathMode={driverExtInstance.pathMode}\");\n#endif\n\t\t\t\t\t}\n\n\t\t\t\t\tushort homeId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[driverCitizenId].m_homeBuilding;\n\t\t\t\t\tbool calcEndPos;\n\t\t\t\t\tVector3 parkPos;\n\n\t\t\t\t\tVector3 returnPos = searchAtCurrentPos ? (Vector3)vehicleData.m_targetPos3 : endPos;\n\t\t\t\t\tif (AdvancedParkingManager.Instance.FindParkingSpaceForCitizen(returnPos, vehicleData.Info, ref driverExtInstance, homeId, targetBuildingId == homeId, vehicleID, allowTourists, out parkPos, ref endPosA, out calcEndPos)) {\n\t\t\t\t\t\tcalculateEndPos = calcEndPos;\n\t\t\t\t\t\tallowRandomParking = false;\n\t\t\t\t\t\tmovingToParkingPos = true;\n\n\t\t\t\t\t\tif (!driverExtInstance.CalculateReturnPath(parkPos, returnPos)) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Could not calculate return path for citizen instance {driverExtInstance.instanceId}, vehicle {vehicleID}. Resetting instance.\");\n#endif\n\t\t\t\t\t\t\tdriverExtInstance.Reset();\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (driverExtInstance.pathMode == ExtPathMode.CalculatingCarPathToAltParkPos) {\n\t\t\t\t\t\t// no alternative parking spot found: abort\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): No alternative parking spot found for vehicle {vehicleID}, citizen instance {driverExtInstance.instanceId} with CurrentPathMode={driverExtInstance.pathMode}! GIVING UP.\");\n#endif\n\t\t\t\t\t\tdriverExtInstance.Reset();\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// calculate a direct path to target\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): No alternative parking spot found for vehicle {vehicleID}, citizen instance {driverExtInstance.instanceId} with CurrentPathMode={driverExtInstance.pathMode}! Setting CurrentPathMode to 'CalculatingCarPath'.\");\n#endif\n\t\t\t\t\t\tdriverExtInstance.pathMode = ExtPathMode.CalculatingCarPathToTarget;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\textPathType = driverExtInstance.GetPathType();\n\t\t\t\tdriverExtInstance.atOutsideConnection = Constants.ManagerFactory.ExtCitizenInstanceManager.IsAtOutsideConnection(driverInstanceId, ref driverInstance, ref driverExtInstance, startPos);\n\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\n\t\t\tNetInfo.LaneType laneTypes = NetInfo.LaneType.Vehicle;\n\t\t\tif (!movingToParkingPos) {\n\t\t\t\tlaneTypes |= NetInfo.LaneType.Pedestrian;\n\t\t\t\t\n\t\t\t\tif (Options.prohibitPocketCars && (driverInstance.m_flags & CitizenInstance.Flags.CannotUseTransport) == CitizenInstance.Flags.None) {\n\t\t\t\t\t/*\n\t\t\t\t\t * citizen may use public transport\n\t\t\t\t\t */\n\t\t\t\t\tlaneTypes |= NetInfo.LaneType.PublicTransport;\n\n\t\t\t\t\tuint citizenId = driverInstance.m_citizen;\n\t\t\t\t\tif (citizenId != 0u && (Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenId].m_flags & Citizen.Flags.Evacuating) != Citizen.Flags.None) {\n\t\t\t\t\t\tlaneTypes |= NetInfo.LaneType.EvacuationTransport;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tVehicleInfo.VehicleType vehicleTypes = this.m_info.m_vehicleType;\n\t\t\tbool allowUnderground = (vehicleData.m_flags & Vehicle.Flags.Underground) != 0;\n\t\t\tbool randomParking = false;\n\t\t\tbool combustionEngine = this.m_info.m_class.m_subService == ItemClass.SubService.ResidentialLow;\n\t\t\tif (allowRandomParking && // NON-STOCK CODE\n\t\t\t\t!movingToParkingPos &&\n\t\t\t\ttargetBuildingId != 0 &&\n\t\t\t\t(\n\t\t\t\t\tSingleton<BuildingManager>.instance.m_buildings.m_buffer[(int)targetBuildingId].Info.m_class.m_service > ItemClass.Service.Office ||\n\t\t\t\t\t(driverInstance.m_flags & CitizenInstance.Flags.TargetIsNode) != CitizenInstance.Flags.None\n\t\t\t\t)) {\n\t\t\t\trandomParking = true;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (fineDebug)\n\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Requesting path-finding for passenger car {vehicleID}, startPos={startPos}, endPos={endPos}, extPathType={extPathType}\");\n#endif\n\n\t\t\t// NON-STOCK CODE START\n\t\t\tif (!foundStartingPos) {\n\t\t\t\tfoundStartingPos = CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, vehicleTypes, allowUnderground, false, 32f, out startPosA, out startPosB, out sqrDistA, out sqrDistB);\n\t\t\t}\n\n\t\t\tbool foundEndPos = !calculateEndPos || driverInstance.Info.m_citizenAI.FindPathPosition(driverInstanceId, ref driverInstance, endPos, Options.prohibitPocketCars && (targetBuildingId == 0 || (Singleton<BuildingManager>.instance.m_buildings.m_buffer[targetBuildingId].m_flags & Building.Flags.IncomingOutgoing) == Building.Flags.None) ? NetInfo.LaneType.Pedestrian : (laneTypes | NetInfo.LaneType.Pedestrian), vehicleTypes, undergroundTarget, out endPosA);\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tif (foundStartingPos &&\n\t\t\t\tfoundEndPos) { // NON-STOCK CODE\n\n\t\t\t\tif (!startBothWays || sqrDistA < 10f) {\n\t\t\t\t\tstartPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tPathUnit.Position endPosB = default(PathUnit.Position);\n\t\t\t\tSimulationManager simMan = Singleton<SimulationManager>.instance;\n\t\t\t\tuint path;\n\t\t\t\tPathUnit.Position dummyPathPos = default(PathUnit.Position);\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = extPathType;\n\t\t\t\targs.extVehicleType = ExtVehicleType.PassengerCar;\n\t\t\t\targs.vehicleId = vehicleID;\n\t\t\t\targs.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\t\targs.buildIndex = simMan.m_currentBuildIndex;\n\t\t\t\targs.startPosA = startPosA;\n\t\t\t\targs.startPosB = startPosB;\n\t\t\t\targs.endPosA = endPosA;\n\t\t\t\targs.endPosB = endPosB;\n\t\t\t\targs.vehiclePosition = dummyPathPos;\n\t\t\t\targs.laneTypes = laneTypes;\n\t\t\t\targs.vehicleTypes = vehicleTypes;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = this.IsHeavyVehicle();\n\t\t\t\targs.hasCombustionEngine = this.CombustionEngine();\n\t\t\t\targs.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.ignoreCosts = false;\n\t\t\t\targs.randomParking = randomParking;\n\t\t\t\targs.stablePath = false;\n\t\t\t\targs.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\n\t\t\t\tif (CustomPathManager._instance.CreatePath(out path, ref simMan.m_randomizer, args)) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtStartPathFind({vehicleID}): Path-finding starts for passenger car {vehicleID}, path={path}, startPosA.segment={startPosA.m_segment}, startPosA.lane={startPosA.m_lane}, startPosA.offset={startPosA.m_offset}, startPosB.segment={startPosB.m_segment}, startPosB.lane={startPosB.m_lane}, startPosB.offset={startPosB.m_offset}, laneType={laneTypes}, vehicleType={vehicleTypes}, endPosA.segment={endPosA.m_segment}, endPosA.lane={endPosA.m_lane}, endPosA.offset={endPosA.m_offset}, endPosB.segment={endPosB.m_segment}, endPosB.lane={endPosB.m_lane}, endPosB.offset={endPosB.m_offset}\");\n#endif\n\t\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tvehicleData.m_path = path;\n\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.WaitingPath;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\tdriverExtInstance.Reset();\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic void CustomUpdateParkedVehicle(ushort parkedId, ref VehicleParked data) {\n\t\t\tfloat x = this.m_info.m_generatedInfo.m_size.x;\n\t\t\tfloat z = this.m_info.m_generatedInfo.m_size.z;\n\t\t\tuint ownerCitizenId = data.m_ownerCitizen;\n\t\t\tushort homeID = 0;\n\t\t\tif (ownerCitizenId != 0u) {\n\t\t\t\thomeID = Singleton<CitizenManager>.instance.m_citizens.m_buffer[ownerCitizenId].m_homeBuilding;\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n\t\t\tif (!AdvancedParkingManager.Instance.TryMoveParkedVehicle(parkedId, ref data, data.m_position, GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToBuilding, homeID)) {\n\t\t\t\tSingleton<VehicleManager>.instance.ReleaseParkedVehicle(parkedId);\n\t\t\t}\n\t\t\t// NON-STOCK CODE END\n\t\t}\n\n\t\tpublic bool CustomParkVehicle(ushort vehicleID, ref Vehicle vehicleData, PathUnit.Position pathPos, uint nextPath, int nextPositionIndex, out byte segmentOffset) {\n\t\t\tCitizenManager citizenManager = Singleton<CitizenManager>.instance;\n\n\t\t\t// TODO remove this:\n\t\t\tuint driverCitizenId = 0u;\n\t\t\tushort driverCitizenInstanceId = 0;\n\t\t\tushort targetBuildingId = 0; // NON-STOCK CODE\n\t\t\tuint curCitizenUnitId = vehicleData.m_citizenUnits;\n\t\t\tint numIterations = 0;\n\t\t\twhile (curCitizenUnitId != 0u && driverCitizenId == 0u) {\n\t\t\t\tuint nextUnit = citizenManager.m_units.m_buffer[curCitizenUnitId].m_nextUnit;\n\t\t\t\tfor (int i = 0; i < 5; i++) {\n\t\t\t\t\tuint citizenId = citizenManager.m_units.m_buffer[curCitizenUnitId].GetCitizen(i);\n\t\t\t\t\tif (citizenId != 0u) {\n\t\t\t\t\t\tdriverCitizenInstanceId = citizenManager.m_citizens.m_buffer[citizenId].m_instance;\n\t\t\t\t\t\tif (driverCitizenInstanceId != 0) {\n\t\t\t\t\t\t\tdriverCitizenId = citizenManager.m_instances.m_buffer[(int)driverCitizenInstanceId].m_citizen;\n\t\t\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\t\t\ttargetBuildingId = citizenManager.m_instances.m_buffer[(int)driverCitizenInstanceId].m_targetBuilding;\n\t\t\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcurCitizenUnitId = nextUnit;\n\t\t\t\tif (++numIterations > 524288) {\n\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"ExtParkVehicle\")) {\n#endif\n\t\t\treturn ExtParkVehicle(vehicleID, ref vehicleData, driverCitizenId, ref citizenManager.m_citizens.m_buffer[driverCitizenId], driverCitizenInstanceId, ref citizenManager.m_instances.m_buffer[driverCitizenInstanceId], ref ExtCitizenInstanceManager.Instance.ExtInstances[driverCitizenInstanceId], targetBuildingId, pathPos, nextPath, nextPositionIndex, out segmentOffset);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t}\n\n\t\tinternal bool ExtParkVehicle(ushort vehicleID, ref Vehicle vehicleData, uint driverCitizenId, ref Citizen driverCitizen, ushort driverCitizenInstanceId, ref CitizenInstance driverInstance, ref ExtCitizenInstance driverExtInstance, ushort targetBuildingId, PathUnit.Position pathPos, uint nextPath, int nextPositionIndex, out byte segmentOffset) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleID) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == driverExtInstance.instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == driverInstance.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == driverInstance.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == driverInstance.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n#endif\n\n\t\t\tPathManager pathManager = Singleton<PathManager>.instance;\n\t\t\tCitizenManager citizenManager = Singleton<CitizenManager>.instance;\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tVehicleManager vehicleManager = Singleton<VehicleManager>.instance;\n\n\t\t\t// NON-STOCK CODE START\n\t\t\tbool prohibitPocketCars = false;\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tif (driverCitizenId != 0u) {\n\t\t\t\tif (Options.prohibitPocketCars && driverCitizenInstanceId != 0) {\n\t\t\t\t\tprohibitPocketCars = true;\n\t\t\t\t}\n\n\t\t\t\tuint laneID = PathManager.GetLaneID(pathPos);\n\t\t\t\tsegmentOffset = (byte)Singleton<SimulationManager>.instance.m_randomizer.Int32(1, 254);\n\t\t\t\tVector3 refPos;\n\t\t\t\tVector3 vector;\n\t\t\t\tnetManager.m_lanes.m_buffer[laneID].CalculatePositionAndDirection((float)segmentOffset * 0.003921569f, out refPos, out vector);\n\t\t\t\tNetInfo info = netManager.m_segments.m_buffer[(int)pathPos.m_segment].Info;\n\t\t\t\tbool isSegmentInverted = (netManager.m_segments.m_buffer[(int)pathPos.m_segment].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None;\n\t\t\t\tbool isPosNegative = info.m_lanes[(int)pathPos.m_lane].m_position < 0f;\n\t\t\t\tvector.Normalize();\n\t\t\t\tVector3 searchDir;\n\t\t\t\tif (isSegmentInverted != isPosNegative) {\n\t\t\t\t\tsearchDir.x = -vector.z;\n\t\t\t\t\tsearchDir.y = 0f;\n\t\t\t\t\tsearchDir.z = vector.x;\n\t\t\t\t} else {\n\t\t\t\t\tsearchDir.x = vector.z;\n\t\t\t\t\tsearchDir.y = 0f;\n\t\t\t\t\tsearchDir.z = -vector.x;\n\t\t\t\t}\n\t\t\t\tushort homeID = 0;\n\t\t\t\tif (driverCitizenId != 0u) {\n\t\t\t\t\thomeID = driverCitizen.m_homeBuilding;\n\t\t\t\t}\n\t\t\t\tVector3 parkPos = default(Vector3);\n\t\t\t\tQuaternion parkRot = default(Quaternion);\n\t\t\t\tfloat parkOffset = -1f;\n\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tbool foundParkingSpace = false;\n\n\t\t\t\tif (prohibitPocketCars) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Vehicle {vehicleID} tries to park on a parking position now (flags: {vehicleData.m_flags})! CurrentPathMode={driverExtInstance.pathMode} path={vehicleData.m_path} pathPositionIndex={vehicleData.m_pathPositionIndex} segmentId={pathPos.m_segment} laneIndex={pathPos.m_lane} offset={pathPos.m_offset} nextPath={nextPath} refPos={refPos} searchDir={searchDir} home={homeID} driverCitizenId={driverCitizenId} driverCitizenInstanceId={driverCitizenInstanceId}\");\n#endif\n\n\t\t\t\t\tif (driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.DrivingToAltParkPos || driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.DrivingToKnownParkPos) {\n\t\t\t\t\t\t// try to use previously found parking space\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"Vehicle {vehicleID} tries to park on an (alternative) parking position now! CurrentPathMode={driverExtInstance.pathMode} altParkingSpaceLocation={driverExtInstance.parkingSpaceLocation} altParkingSpaceLocationId={driverExtInstance.parkingSpaceLocationId}\");\n#endif\n\n\t\t\t\t\t\tswitch (driverExtInstance.parkingSpaceLocation) {\n\t\t\t\t\t\t\tcase ExtCitizenInstance.ExtParkingSpaceLocation.RoadSide:\n\t\t\t\t\t\t\t\tuint parkLaneID; int parkLaneIndex;\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"Vehicle {vehicleID} wants to park road-side @ segment {driverExtInstance.parkingSpaceLocationId}\");\n#endif\n\t\t\t\t\t\t\t\tfoundParkingSpace = AdvancedParkingManager.Instance.FindParkingSpaceRoadSideForVehiclePos(this.m_info, 0, driverExtInstance.parkingSpaceLocationId, refPos, out parkPos, out parkRot, out parkOffset, out parkLaneID, out parkLaneIndex);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ExtCitizenInstance.ExtParkingSpaceLocation.Building:\n\t\t\t\t\t\t\t\tfloat maxDist = 9999f;\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"Vehicle {vehicleID} wants to park @ building {driverExtInstance.parkingSpaceLocationId}\");\n#endif\n\t\t\t\t\t\t\t\tfoundParkingSpace = AdvancedParkingManager.Instance.FindParkingSpacePropAtBuilding(this.m_info, homeID, 0, driverExtInstance.parkingSpaceLocationId, ref Singleton<BuildingManager>.instance.m_buildings.m_buffer[driverExtInstance.parkingSpaceLocationId], pathPos.m_segment, refPos, ref maxDist, true, out parkPos, out parkRot, out parkOffset);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n#if DEBUG\n\t\t\t\t\t\t\t\tLog.Error($\"No alternative parking position stored for vehicle {vehicleID}! PathMode={driverExtInstance.pathMode}\");\n#endif\n\t\t\t\t\t\t\t\tfoundParkingSpace = CustomFindParkingSpace(this.m_info, homeID, refPos, searchDir, pathPos.m_segment, out parkPos, out parkRot, out parkOffset);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!foundParkingSpace) {\n\t\t\t\t\tfoundParkingSpace = /*prohibitPocketCars ?*/\n\t\t\t\t\t\tCustomFindParkingSpace(this.m_info, homeID, refPos, searchDir, pathPos.m_segment, out parkPos, out parkRot, out parkOffset) /*:\n\t\t\t\t\t\tFindParkingSpace(homeID, refPos, searchDir, pathPos.m_segment, this.m_info.m_generatedInfo.m_size.x, this.m_info.m_generatedInfo.m_size.z, out parkPos, out parkRot, out parkOffset)*/;\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Found parking space? {foundParkingSpace}. parkPos={parkPos}, parkRot={parkRot}, parkOffset={parkOffset}\");\n#endif\n\t\t\t\t}\n\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t\tushort parkedVehicleId = 0;\n\t\t\t\tbool parkedCarCreated = foundParkingSpace && vehicleManager.CreateParkedVehicle(out parkedVehicleId, ref Singleton<SimulationManager>.instance.m_randomizer, this.m_info, parkPos, parkRot, driverCitizenId);\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Parked car created? {parkedCarCreated}\");\n#endif\n\n\t\t\t\tif (foundParkingSpace && parkedCarCreated) {\n\t\t\t\t\t// we have reached a parking position\n#if DEBUG\n\t\t\t\t\tfloat sqrDist = (refPos - parkPos).sqrMagnitude;\n\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Vehicle {vehicleID} succeeded in parking! CurrentPathMode={driverExtInstance.pathMode} sqrDist={sqrDist}\");\n#endif\n\n\t\t\t\t\tdriverCitizen.SetParkedVehicle(driverCitizenId, parkedVehicleId);\n\t\t\t\t\tif (parkOffset >= 0f) {\n\t\t\t\t\t\tsegmentOffset = (byte)(parkOffset * 255f);\n\t\t\t\t\t}\n\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\tif (prohibitPocketCars) {\n\t\t\t\t\t\tif ((driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.DrivingToAltParkPos || driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.DrivingToKnownParkPos) && targetBuildingId != 0) {\n\t\t\t\t\t\t\t// decrease parking space demand of target building\n\t\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[targetBuildingId].ModifyParkingSpaceDemand(parkPos, GlobalConfig.Instance.ParkingAI.MinFoundParkPosParkingSpaceDemandDelta, GlobalConfig.Instance.ParkingAI.MaxFoundParkPosParkingSpaceDemandDelta);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t//if (driverExtInstance.CurrentPathMode == ExtCitizenInstance.PathMode.DrivingToAltParkPos || driverExtInstance.CurrentPathMode == ExtCitizenInstance.PathMode.DrivingToKnownParkPos) {\n\t\t\t\t\t\t// we have reached an (alternative) parking position and succeeded in finding a parking space\n\t\t\t\t\t\tdriverExtInstance.pathMode = ExtCitizenInstance.ExtPathMode.RequiresWalkingPathToTarget;\n\t\t\t\t\t\tdriverExtInstance.failedParkingAttempts = 0;\n\t\t\t\t\t\tdriverExtInstance.parkingSpaceLocation = ExtCitizenInstance.ExtParkingSpaceLocation.None;\n\t\t\t\t\t\tdriverExtInstance.parkingSpaceLocationId = 0;\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Vehicle {vehicleID} has reached an (alternative) parking position! CurrentPathMode={driverExtInstance.pathMode} position={parkPos}\");\n#endif\n\t\t\t\t\t\t//}\n\t\t\t\t\t}\n\t\t\t\t} else if (prohibitPocketCars) {\n\t\t\t\t\t// could not find parking space. vehicle would despawn.\n\t\t\t\t\tif (\n\t\t\t\t\t\ttargetBuildingId != 0 &&\n\t\t\t\t\t\t(Singleton<BuildingManager>.instance.m_buildings.m_buffer[targetBuildingId].m_flags & Building.Flags.IncomingOutgoing) != Building.Flags.None &&\n\t\t\t\t\t\t(refPos - Singleton<BuildingManager>.instance.m_buildings.m_buffer[targetBuildingId].m_position).magnitude <= GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance\n\t\t\t\t\t) {\n\t\t\t\t\t\t// vehicle is at target and target is an outside connection: accept despawn\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Driver citizen instance {driverCitizenInstanceId} wants to park at an outside connection. Aborting.\");\n#endif\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Find parking space in the vicinity, redo path-finding to the parking space, park the vehicle and do citizen path-finding to the current target\n\n\t\t\t\t\tif (!foundParkingSpace && (driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.DrivingToAltParkPos || driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.DrivingToKnownParkPos) && targetBuildingId != 0) {\n\t\t\t\t\t\t// increase parking space demand of target building\n\t\t\t\t\t\tif (driverExtInstance.failedParkingAttempts > 1) {\n\t\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[targetBuildingId].AddParkingSpaceDemand(GlobalConfig.Instance.ParkingAI.FailedParkingSpaceDemandIncrement * (uint)(driverExtInstance.failedParkingAttempts - 1));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!foundParkingSpace) {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Parking failed for vehicle {vehicleID}: Could not find parking space. ABORT.\");\n#endif\n\t\t\t\t\t\t++driverExtInstance.failedParkingAttempts;\n\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Parking failed for vehicle {vehicleID}: Parked car could not be created. ABORT.\");\n#endif\n\t\t\t\t\t\tdriverExtInstance.failedParkingAttempts = GlobalConfig.Instance.ParkingAI.MaxParkingAttempts + 1;\n\t\t\t\t\t}\n\t\t\t\t\tdriverExtInstance.pathMode = ExtCitizenInstance.ExtPathMode.ParkingFailed;\n\t\t\t\t\tdriverExtInstance.parkingPathStartPosition = pathPos;\n\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Parking failed for vehicle {vehicleID}! (flags: {vehicleData.m_flags}) pathPos segment={pathPos.m_segment}, lane={pathPos.m_lane}, offset={pathPos.m_offset}. Trying to find parking space in the vicinity. FailedParkingAttempts={driverExtInstance.failedParkingAttempts}, CurrentPathMode={driverExtInstance.pathMode} foundParkingSpace={foundParkingSpace}\");\n#endif\n\n\t\t\t\t\t// invalidate paths of all passengers in order to force path recalculation\n\t\t\t\t\tuint curUnitId = vehicleData.m_citizenUnits;\n\t\t\t\t\tint numIter = 0;\n\t\t\t\t\twhile (curUnitId != 0u) {\n\t\t\t\t\t\tuint nextUnit = citizenManager.m_units.m_buffer[curUnitId].m_nextUnit;\n\t\t\t\t\t\tfor (int i = 0; i < 5; i++) {\n\t\t\t\t\t\t\tuint curCitizenId = citizenManager.m_units.m_buffer[curUnitId].GetCitizen(i);\n\t\t\t\t\t\t\tif (curCitizenId != 0u) {\n\t\t\t\t\t\t\t\tushort citizenInstanceId = citizenManager.m_citizens.m_buffer[curCitizenId].m_instance;\n\t\t\t\t\t\t\t\tif (citizenInstanceId != 0) {\n\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Releasing path for citizen instance {citizenInstanceId} sitting in vehicle {vehicleID} (was {citizenManager.m_instances.m_buffer[citizenInstanceId].m_path}).\");\n#endif\n\t\t\t\t\t\t\t\t\tif (citizenInstanceId != driverCitizenInstanceId) {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Resetting pathmode for passenger citizen instance {citizenInstanceId} sitting in vehicle {vehicleID} (was {ExtCitizenInstanceManager.Instance.ExtInstances[citizenInstanceId].pathMode}).\");\n#endif\n\n\t\t\t\t\t\t\t\t\t\tExtCitizenInstanceManager.Instance.ExtInstances[citizenInstanceId].Reset();\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (citizenManager.m_instances.m_buffer[citizenInstanceId].m_path != 0) {\n\t\t\t\t\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(citizenManager.m_instances.m_buffer[citizenInstanceId].m_path);\n\t\t\t\t\t\t\t\t\t\tcitizenManager.m_instances.m_buffer[citizenInstanceId].m_path = 0u;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcurUnitId = nextUnit;\n\t\t\t\t\t\tif (++numIter > CitizenManager.MAX_UNIT_COUNT) {\n\t\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsegmentOffset = pathPos.m_offset;\n\t\t\t}\n\n\t\t\t// parking has succeeded\n\t\t\tif (driverCitizenId != 0u) {\n\t\t\t\tuint curCitizenUnitId = vehicleData.m_citizenUnits;\n\t\t\t\tint numIter = 0;\n\t\t\t\twhile (curCitizenUnitId != 0u) {\n\t\t\t\t\tuint nextUnit = citizenManager.m_units.m_buffer[curCitizenUnitId].m_nextUnit;\n\t\t\t\t\tfor (int j = 0; j < 5; j++) {\n\t\t\t\t\t\tuint citId = citizenManager.m_units.m_buffer[curCitizenUnitId].GetCitizen(j);\n\t\t\t\t\t\tif (citId != 0u) {\n\t\t\t\t\t\t\tushort citizenInstanceId = citizenManager.m_citizens.m_buffer[citId].m_instance;\n\t\t\t\t\t\t\tif (citizenInstanceId != 0) {\n\t\t\t\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\t\t\t\tif (prohibitPocketCars) {\n\t\t\t\t\t\t\t\t\tif (driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.RequiresWalkingPathToTarget) {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Parking succeeded: Doing nothing for citizen instance {citizenInstanceId}! path: {citizenManager.m_instances.m_buffer[(int)citizenInstanceId].m_path}\");\n#endif\n\t\t\t\t\t\t\t\t\t\tExtCitizenInstanceManager.Instance.ExtInstances[citizenInstanceId].pathMode = ExtCitizenInstance.ExtPathMode.RequiresWalkingPathToTarget;\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\t\t\t\t\tif (pathManager.AddPathReference(nextPath)) {\n\t\t\t\t\t\t\t\t\tif (citizenManager.m_instances.m_buffer[(int)citizenInstanceId].m_path != 0u) {\n\t\t\t\t\t\t\t\t\t\tpathManager.ReleasePath(citizenManager.m_instances.m_buffer[(int)citizenInstanceId].m_path);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcitizenManager.m_instances.m_buffer[(int)citizenInstanceId].m_path = nextPath;\n\t\t\t\t\t\t\t\t\tcitizenManager.m_instances.m_buffer[(int)citizenInstanceId].m_pathPositionIndex = (byte)nextPositionIndex;\n\t\t\t\t\t\t\t\t\tcitizenManager.m_instances.m_buffer[(int)citizenInstanceId].m_lastPathOffset = segmentOffset;\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Parking succeeded (default): Setting path of citizen instance {citizenInstanceId} to {nextPath}!\");\n#endif\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcurCitizenUnitId = nextUnit;\n\t\t\t\t\tif (++numIter > 524288) {\n\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (prohibitPocketCars) {\n\t\t\t\tif (driverExtInstance.pathMode == ExtCitizenInstance.ExtPathMode.RequiresWalkingPathToTarget) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomPassengerCarAI.ExtParkVehicle({vehicleID}): Parking succeeded (alternative parking spot): Citizen instance {driverExtInstance} has to walk for the remaining path!\");\n#endif\n\t\t\t\t\t/*driverExtInstance.CurrentPathMode = ExtCitizenInstance.PathMode.CalculatingWalkingPathToTarget;\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"Setting CurrentPathMode of vehicle {vehicleID} to {driverExtInstance.CurrentPathMode}\");*/\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate static bool CustomFindParkingSpace(VehicleInfo vehicleInfo, ushort homeID, Vector3 refPos, Vector3 searchDir, ushort segmentId, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[22];\n#endif\n\n\t\t\tfloat searchRadius = Options.prohibitPocketCars ? 32f : 16f;\n\t\t\tuint chanceOfParkingOffRoad = 3u;\n\n\t\t\tVector3 searchMagnitude = refPos + searchDir * 16f;\n\n\t\t\tif (GlobalConfig.RushHourParkingSearchRadius != null) {\n\t\t\t\tsearchRadius = (int)GlobalConfig.RushHourParkingSearchRadius;\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"RushHour's Improved Parking AI is active. searchRadius={searchRadius}\");\n#endif\n\t\t\t\tchanceOfParkingOffRoad = 80u;\n\t\t\t} else {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug(\"RushHour's Improved Parking AI is NOT active.\");\n#endif\n\t\t\t}\n\n\t\t\t/*if (Options.prohibitPocketCars) {\n\t\t\t\t//searchRadius = Mathf.Max(32f, searchRadius);\n\t\t\t}*/\n\n\t\t\tVector3 refPos2 = refPos + searchDir * 16f;\n\t\t\tif (Singleton<SimulationManager>.instance.m_randomizer.Int32(chanceOfParkingOffRoad) == 0) {\n\t\t\t\tfloat width = vehicleInfo.m_generatedInfo.m_size.x;\n\t\t\t\tfloat length = vehicleInfo.m_generatedInfo.m_size.z;\n\n\t\t\t\tif (FindParkingSpaceRoadSide(0, segmentId, refPos, width - 0.2f, length, out parkPos, out parkRot, out parkOffset)) {\n\t\t\t\t\tif (Options.parkingRestrictionsEnabled) {\n\t\t\t\t\t\tVector3 innerParkPos;\n\t\t\t\t\t\tuint laneId;\n\t\t\t\t\t\tint laneIndex;\n\t\t\t\t\t\tfloat laneOffset;\n\t\t\t\t\t\tif (Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].GetClosestLanePosition(refPos, NetInfo.LaneType.Parking, VehicleInfo.VehicleType.Car, out innerParkPos, out laneId, out laneIndex, out laneOffset)) {\n#if BENCHMARK\n\t\t\t\t\t\t\tusing (var bm = new Benchmark(null, \"IsParkingAllowed.1\")) {\n#endif\n\t\t\t\t\t\t\tif (ParkingRestrictionsManager.Instance.IsParkingAllowed(segmentId, Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info.m_lanes[laneIndex].m_finalDirection)) {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"FindParkingSpaceBuilding.1\")) {\n#endif\n\t\t\t\tif (AdvancedParkingManager.Instance.FindParkingSpaceBuilding(vehicleInfo, homeID, 0, segmentId, refPos2, GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance, searchRadius, out parkPos, out parkRot, out parkOffset)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t} else {\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"FindParkingSpaceBuilding.2\")) {\n#endif\n\t\t\t\tif (AdvancedParkingManager.Instance.FindParkingSpaceBuilding(vehicleInfo, homeID, 0, segmentId, refPos2, GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance, searchRadius, out parkPos, out parkRot, out parkOffset)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\n\t\t\t\tfloat width = vehicleInfo.m_generatedInfo.m_size.x;\n\t\t\t\tfloat length = vehicleInfo.m_generatedInfo.m_size.z;\n\n\t\t\t\tif (FindParkingSpaceRoadSide(0, segmentId, refPos, width - 0.2f, length, out parkPos, out parkRot, out parkOffset)) {\n\t\t\t\t\tif (Options.parkingRestrictionsEnabled) {\n\t\t\t\t\t\tVector3 innerParkPos;\n\t\t\t\t\t\tuint laneId;\n\t\t\t\t\t\tint laneIndex;\n\t\t\t\t\t\tfloat laneOffset;\n\t\t\t\t\t\tif (Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].GetClosestLanePosition(refPos, NetInfo.LaneType.Parking, VehicleInfo.VehicleType.Car, out innerParkPos, out laneId, out laneIndex, out laneOffset)) {\n#if BENCHMARK\n\t\t\t\t\t\t\tusing (var bm = new Benchmark(null, \"IsParkingAllowed.2\")) {\n#endif\n\t\t\t\t\t\t\tif (ParkingRestrictionsManager.Instance.IsParkingAllowed(segmentId, Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info.m_lanes[laneIndex].m_finalDirection)) {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tinternal static bool FindParkingSpaceRoadSide(ushort ignoreParked, ushort requireSegment, Vector3 refPos, float width, float length, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset) {\n\t\t\tLog.Error(\"FindParkingSpaceRoadSide is not overridden!\");\n\t\t\tparkPos = Vector3.zero;\n\t\t\tparkRot = Quaternion.identity;\n\t\t\tparkOffset = 0f;\n\t\t\treturn false;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tinternal static bool FindParkingSpace(bool isElectric, ushort homeID, Vector3 refPos, Vector3 searchDir, ushort segment, float width, float length, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset) {\n\t\t\tLog.Error(\"FindParkingSpace is not overridden!\");\n\t\t\tparkPos = Vector3.zero;\n\t\t\tparkRot = Quaternion.identity;\n\t\t\tparkOffset = 0f;\n\t\t\treturn false;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tinternal static bool FindParkingSpaceProp(bool isElectric, ushort ignoreParked, PropInfo info, Vector3 position, float angle, bool fixedHeight, Vector3 refPos, float width, float length, ref float maxDistance, ref Vector3 parkPos, ref Quaternion parkRot) {\n\t\t\tLog.Error(\"FindParkingSpaceProp is not overridden!\");\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomPoliceCarAI.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons.Benchmark;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing UnityEngine;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\tclass CustomPoliceCarAI : CarAI {\n\t\tpublic bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {\n#if DEBUG\n\t\t\t//Log._Debug($\"CustomPoliceCarAI.CustomStartPathFind called for vehicle {vehicleID}\");\n#endif\n\n\t\t\tExtVehicleType vehicleType = ExtVehicleType.None;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"OnStartPathFind\")) {\n#endif\n\t\t\t\t vehicleType = VehicleStateManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, (vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0 ? ExtVehicleType.Emergency : ExtVehicleType.Service);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\tVehicleInfo info = this.m_info;\n\t\t\tbool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;\n\t\t\tPathUnit.Position startPosA;\n\t\t\tPathUnit.Position startPosB;\n\t\t\tfloat startDistSqrA;\n\t\t\tfloat startDistSqrB;\n\t\t\tPathUnit.Position endPosA;\n\t\t\tPathUnit.Position endPosB;\n\t\t\tfloat endDistSqrA;\n\t\t\tfloat endDistSqrB;\n\t\t\tif (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) &&\n\t\t\t\tCustomPathManager.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)) {\n\t\t\t\tif (!startBothWays || startDistSqrA < 10f) {\n\t\t\t\t\tstartPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tif (!endBothWays || endDistSqrA < 10f) {\n\t\t\t\t\tendPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tuint path;\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = ExtCitizenInstance.ExtPathType.None;\n\t\t\t\targs.extVehicleType = vehicleType;\n\t\t\t\targs.vehicleId = vehicleID;\n\t\t\t\targs.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\t\targs.buildIndex = Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\t\targs.startPosA = startPosA;\n\t\t\t\targs.startPosB = startPosB;\n\t\t\t\targs.endPosA = endPosA;\n\t\t\t\targs.endPosB = endPosB;\n\t\t\t\targs.vehiclePosition = default(PathUnit.Position);\n\t\t\t\targs.laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;\n\t\t\t\targs.vehicleTypes = info.m_vehicleType;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = this.IsHeavyVehicle();\n\t\t\t\targs.hasCombustionEngine = this.CombustionEngine();\n\t\t\t\targs.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.ignoreCosts = false;\n\t\t\t\targs.randomParking = false;\n\t\t\t\targs.stablePath = false;\n\t\t\t\targs.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\n\t\t\t\t//Log._Debug($\"CustomPoliceCarAI.CustomStartPathFind: Vehicle {vehicleID}, type {vehicleType}\");\n\t\t\t\tif (CustomPathManager._instance.CreatePath(out path, ref Singleton<SimulationManager>.instance.m_randomizer, args)) {\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tvehicleData.m_path = path;\n\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.WaitingPath;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomResidentAI.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Globalization;\nusing ColossalFramework.Math;\nusing CSUtil.Commons;\nusing CSUtil.Commons.Benchmark;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.UI;\nusing UnityEngine;\nusing static TrafficManager.Traffic.Data.ExtCitizen;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\n\nnamespace TrafficManager.Custom.AI {\n\tpublic class CustomResidentAI : ResidentAI {\n\t\tpublic string CustomGetLocalizedStatus(ushort instanceID, ref CitizenInstance data, out InstanceID target) {\n\t\t\tbool addCustomStatus = false;\n\t\t\tString ret = GetStockLocalizedStatus(instanceID, ref data, out addCustomStatus, out target);\n\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"EnrichLocalizedCitizenStatus\")) {\n#endif\n\t\t\t\tif (Options.prohibitPocketCars && addCustomStatus) {\n\t\t\t\t\tret = AdvancedParkingManager.Instance.EnrichLocalizedCitizenStatus(ret, ref ExtCitizenInstanceManager.Instance.ExtInstances[instanceID], ref ExtCitizenManager.Instance.ExtCitizens[data.m_citizen]);\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tprivate String GetStockLocalizedStatus(ushort instanceID, ref CitizenInstance data, out bool addCustomStatus, out InstanceID target) {\n\t\t\tif ((data.m_flags & (CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating)) != CitizenInstance.Flags.None) {\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\taddCustomStatus = false;\n\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_CONFUSED\");\n\t\t\t}\n\n\t\t\tCitizenManager citMan = Singleton<CitizenManager>.instance;\n\t\t\tuint citizenId = data.m_citizen;\n\t\t\tbool isStudent = false;\n\t\t\tushort homeId = 0;\n\t\t\tushort workId = 0;\n\t\t\tushort vehicleId = 0;\n\t\t\tif (citizenId != 0u) {\n\t\t\t\thomeId = citMan.m_citizens.m_buffer[citizenId].m_homeBuilding;\n\t\t\t\tworkId = citMan.m_citizens.m_buffer[citizenId].m_workBuilding;\n\t\t\t\tvehicleId = citMan.m_citizens.m_buffer[citizenId].m_vehicle;\n\t\t\t\tisStudent = ((citMan.m_citizens.m_buffer[citizenId].m_flags & Citizen.Flags.Student) != Citizen.Flags.None);\n\t\t\t}\n\t\t\tushort targetBuilding = data.m_targetBuilding;\n\t\t\tif (targetBuilding == 0) {\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\taddCustomStatus = false;\n\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_CONFUSED\");\n\t\t\t}\n\n\t\t\tif ((data.m_flags & CitizenInstance.Flags.TargetIsNode) != CitizenInstance.Flags.None) {\n\t\t\t\tif (vehicleId != 0) {\n\t\t\t\t\tVehicleManager vehManager = Singleton<VehicleManager>.instance;\n\t\t\t\t\tVehicleInfo vehicleInfo = vehManager.m_vehicles.m_buffer[vehicleId].Info;\n\t\t\t\t\tif (vehicleInfo.m_class.m_service == ItemClass.Service.Residential && vehicleInfo.m_vehicleType != VehicleInfo.VehicleType.Bicycle) {\n\t\t\t\t\t\tif (vehicleInfo.m_vehicleAI.GetOwnerID(vehicleId, ref vehManager.m_vehicles.m_buffer[vehicleId]).Citizen == citizenId) {\n\t\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\t\ttarget.NetNode = targetBuilding;\n\t\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\t\treturn ColossalFramework.Globalization.Locale.Get(\"CITIZEN_STATUS_DRIVINGTO\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (vehicleInfo.m_class.m_service == ItemClass.Service.PublicTransport || vehicleInfo.m_class.m_service == ItemClass.Service.Disaster) {\n\t\t\t\t\t\tushort transportLine = Singleton<NetManager>.instance.m_nodes.m_buffer[targetBuilding].m_transportLine;\n\t\t\t\t\t\tif ((data.m_flags & CitizenInstance.Flags.WaitingTaxi) != 0) {\n\t\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\t\treturn ColossalFramework.Globalization.Locale.Get(\"CITIZEN_STATUS_WAITING_TAXI\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (vehManager.m_vehicles.m_buffer[vehicleId].m_transportLine != transportLine) {\n\t\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\t\ttarget.NetNode = targetBuilding;\n\t\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\t\treturn ColossalFramework.Globalization.Locale.Get(\"CITIZEN_STATUS_TRAVELLINGTO\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ((data.m_flags & CitizenInstance.Flags.OnTour) != 0) {\n\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\ttarget.NetNode = targetBuilding;\n\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\treturn ColossalFramework.Globalization.Locale.Get(\"CITIZEN_STATUS_VISITING\");\n\t\t\t\t}\n\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\ttarget.NetNode = targetBuilding;\n\t\t\t\taddCustomStatus = true;\n\t\t\t\treturn ColossalFramework.Globalization.Locale.Get(\"CITIZEN_STATUS_GOINGTO\");\n\t\t\t}\n\n\t\t\tbool isOutsideConnection = (Singleton<BuildingManager>.instance.m_buildings.m_buffer[(int)targetBuilding].m_flags & Building.Flags.IncomingOutgoing) != Building.Flags.None;\n\t\t\tbool hangsAround = data.m_path == 0u && (data.m_flags & CitizenInstance.Flags.HangAround) != CitizenInstance.Flags.None;\n\t\t\tif (vehicleId != 0) {\n\t\t\t\tVehicleManager vehicleMan = Singleton<VehicleManager>.instance;\n\t\t\t\tVehicleInfo vehicleInfo = vehicleMan.m_vehicles.m_buffer[(int)vehicleId].Info;\n\t\t\t\tif (vehicleInfo.m_class.m_service == ItemClass.Service.Residential && vehicleInfo.m_vehicleType != VehicleInfo.VehicleType.Bicycle) {\n\t\t\t\t\tif (vehicleInfo.m_vehicleAI.GetOwnerID(vehicleId, ref vehicleMan.m_vehicles.m_buffer[(int)vehicleId]).Citizen == citizenId) {\n\t\t\t\t\t\tif (isOutsideConnection) {\n\t\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_DRIVINGTO_OUTSIDE\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (targetBuilding == homeId) {\n\t\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_DRIVINGTO_HOME\");\n\t\t\t\t\t\t} else if (targetBuilding == workId) {\n\t\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\t\treturn Locale.Get((!isStudent) ? \"CITIZEN_STATUS_DRIVINGTO_WORK\" : \"CITIZEN_STATUS_DRIVINGTO_SCHOOL\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\t\ttarget.Building = targetBuilding;\n\t\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_DRIVINGTO\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (vehicleInfo.m_class.m_service == ItemClass.Service.PublicTransport || vehicleInfo.m_class.m_service == ItemClass.Service.Disaster) {\n\t\t\t\t\tif ((data.m_flags & CitizenInstance.Flags.WaitingTaxi) != CitizenInstance.Flags.None) {\n\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_WAITING_TAXI\");\n\t\t\t\t\t}\n\t\t\t\t\tif (isOutsideConnection) {\n\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_TRAVELLINGTO_OUTSIDE\");\n\t\t\t\t\t}\n\t\t\t\t\tif (targetBuilding == homeId) {\n\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_TRAVELLINGTO_HOME\");\n\t\t\t\t\t}\n\t\t\t\t\tif (targetBuilding == workId) {\n\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\treturn Locale.Get((!isStudent) ? \"CITIZEN_STATUS_TRAVELLINGTO_WORK\" : \"CITIZEN_STATUS_TRAVELLINGTO_SCHOOL\");\n\t\t\t\t\t}\n\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\ttarget.Building = targetBuilding;\n\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_TRAVELLINGTO\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isOutsideConnection) {\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\taddCustomStatus = true;\n\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_GOINGTO_OUTSIDE\");\n\t\t\t}\n\n\t\t\tif (targetBuilding == homeId) {\n\t\t\t\tif (hangsAround) {\n\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\taddCustomStatus = false;\n\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_AT_HOME\");\n\t\t\t\t}\n\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\taddCustomStatus = true;\n\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_GOINGTO_HOME\");\n\t\t\t} else if (targetBuilding == workId) {\n\t\t\t\tif (hangsAround) {\n\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\taddCustomStatus = false;\n\t\t\t\t\treturn Locale.Get((!isStudent) ? \"CITIZEN_STATUS_AT_WORK\" : \"CITIZEN_STATUS_AT_SCHOOL\");\n\t\t\t\t}\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\taddCustomStatus = true;\n\t\t\t\treturn Locale.Get((!isStudent) ? \"CITIZEN_STATUS_GOINGTO_WORK\" : \"CITIZEN_STATUS_GOINGTO_SCHOOL\");\n\t\t\t} else {\n\t\t\t\tif (hangsAround) {\n\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\ttarget.Building = targetBuilding;\n\t\t\t\t\taddCustomStatus = false;\n\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_VISITING\");\n\t\t\t\t}\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\ttarget.Building = targetBuilding;\n\t\t\t\taddCustomStatus = true;\n\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_GOINGTO\");\n\t\t\t}\n\t\t}\n\n\t\tpublic VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance citizenData, bool forceCar, out VehicleInfo trailer) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceID) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == citizenData.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == citizenData.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == citizenData.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n#endif\n\n\t\t\ttrailer = null;\n\n\t\t\tif (citizenData.m_citizen == 0u) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t\n\t\t\t// NON-STOCK CODE START\n\t\t\tbool forceTaxi = false;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"forceTaxi\")) {\n#endif\n\t\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\t\tif (ExtCitizenInstanceManager.Instance.ExtInstances[instanceID].pathMode == ExtPathMode.TaxiToTarget) {\n\t\t\t\t\t\tforceTaxi = true;\n\t\t\t\t\t}\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tCitizen.AgeGroup ageGroup = CustomCitizenAI.GetAgeGroup(citizenData.Info.m_agePhase);\n\n\t\t\tint carProb;\n\t\t\tint bikeProb;\n\t\t\tint taxiProb;\n\t\t\t// NON-STOCK CODE START\n\t\t\tif (forceTaxi) {\n\t\t\t\tcarProb = 0;\n\t\t\t\tbikeProb = 0;\n\t\t\t\ttaxiProb = 100;\n\t\t\t} else\n\t\t\t// NON-STOCK CODE END\n\t\t\tif (forceCar || (citizenData.m_flags & CitizenInstance.Flags.BorrowCar) != CitizenInstance.Flags.None) {\n\t\t\t\tcarProb = 100;\n\t\t\t\tbikeProb = 0;\n\t\t\t\ttaxiProb = 0;\n\t\t\t} else {\n\t\t\t\tcarProb = GetCarProbability(instanceID, ref citizenData, ageGroup);\n\t\t\t\tbikeProb = GetBikeProbability(instanceID, ref citizenData, ageGroup);\n\t\t\t\ttaxiProb = GetTaxiProbability(instanceID, ref citizenData, ageGroup);\n\t\t\t}\n\t\t\tRandomizer randomizer = new Randomizer(citizenData.m_citizen);\n\t\t\tbool useCar = randomizer.Int32(100u) < carProb;\n\t\t\tbool useBike = !useCar && randomizer.Int32(100u) < bikeProb;\n\t\t\tbool useTaxi = !useCar && !useBike && randomizer.Int32(100u) < taxiProb;\n\t\t\tbool useElectricCar = false;\n\t\t\tif (useCar) {\n\t\t\t\tint electricProb = GetElectricCarProbability(instanceID, ref citizenData, this.m_info.m_agePhase);\n\t\t\t\tuseElectricCar = randomizer.Int32(100u) < electricProb;\n\t\t\t}\n\n\t\t\tItemClass.Service service = ItemClass.Service.Residential;\n\t\t\tItemClass.SubService subService = useElectricCar ? ItemClass.SubService.ResidentialLowEco : ItemClass.SubService.ResidentialLow;\n\t\t\tif (useTaxi) {\n\t\t\t\tservice = ItemClass.Service.PublicTransport;\n\t\t\t\tsubService = ItemClass.SubService.PublicTransportTaxi;\n\t\t\t}\n\t\t\t// NON-STOCK CODE START\n\t\t\tVehicleInfo carInfo = null;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"find-parked-vehicle\")) {\n#endif\n\t\t\t\tif (Options.prohibitPocketCars && useCar) {\n\t\t\t\t\tushort parkedVehicleId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenData.m_citizen].m_parkedVehicle;\n\t\t\t\t\tif (parkedVehicleId != 0) {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomResidentAI.CustomGetVehicleInfo({instanceID}): Citizen instance {instanceID} owns a parked vehicle {parkedVehicleId}. Reusing vehicle info.\");\n#endif\n\t\t\t\t\t\tcarInfo = Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info;\n\t\t\t\t\t}\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\n\t\t\tif (carInfo == null && (useCar || useTaxi)) {\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t\tcarInfo = Singleton<VehicleManager>.instance.GetRandomVehicleInfo(ref randomizer, service, subService, ItemClass.Level.Level1);\n\t\t\t}\n\n\t\t\tif (useBike) {\n\t\t\t\tVehicleInfo bikeInfo = Singleton<VehicleManager>.instance.GetRandomVehicleInfo(ref randomizer, ItemClass.Service.Residential, ItemClass.SubService.ResidentialHigh, (ageGroup != Citizen.AgeGroup.Child) ? ItemClass.Level.Level2 : ItemClass.Level.Level1);\n\t\t\t\tif (bikeInfo != null) {\n\t\t\t\t\treturn bikeInfo;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ((useCar || useTaxi) && carInfo != null) {\n\t\t\t\treturn carInfo;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate int GetTaxiProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) {\n\t\t\tLog.Error(\"CustomResidentAI.GetTaxiProbability called!\");\n\t\t\treturn 20;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate int GetBikeProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) {\n\t\t\tLog.Error(\"CustomResidentAI.GetBikeProbability called!\");\n\t\t\treturn 20;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate int GetCarProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) {\n\t\t\tLog.Error(\"CustomResidentAI.GetCarProbability called!\");\n\t\t\treturn 20;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate int GetElectricCarProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgePhase agePhase) {\n\t\t\tLog.Error(\"CustomResidentAI.GetElectricCarProbability called!\");\n\t\t\treturn 20;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomRoadAI.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing ColossalFramework;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Geometry;\nusing UnityEngine;\nusing ColossalFramework.Math;\nusing System.Threading;\nusing TrafficManager.UI;\nusing TrafficManager.State;\nusing TrafficManager.Manager;\nusing TrafficManager.UI.SubTools;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Geometry.Impl;\nusing CSUtil.Commons.Benchmark;\nusing TrafficManager.TrafficLight.Data;\n\nnamespace TrafficManager.Custom.AI {\n\tpublic class CustomRoadAI : RoadBaseAI {\n\t\tprivate static ushort lastSimulatedSegmentId = 0;\n\t\tprivate static byte trafficMeasurementMod = 0;\n\n\t\tpublic void CustomNodeSimulationStep(ushort nodeId, ref NetNode data) {\n\t\t\tif (Options.timedLightsEnabled) {\n\t\t\t\ttry {\n\t\t\t\t\t//tlsMan.SimulationStep();\n\n\t\t\t\t\tbool callStockSimStep = true;\n\n#if BENCHMARK\n\t\t\t\t\tusing (var bm = new Benchmark(null, \"callStockSimStep\")) {\n#endif\n\t\t\t\t\t\tcallStockSimStep = !Options.timedLightsEnabled || !TrafficLightSimulationManager.Instance.TrafficLightSimulations[nodeId].IsSimulationRunning();\n#if BENCHMARK\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tif (callStockSimStep) {\n\t\t\t\t\t\tOriginalSimulationStep(nodeId, ref data);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Warning($\"CustomNodeSimulationStep: An error occurred: {e.ToString()}\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tOriginalSimulationStep(nodeId, ref data);\n\t\t\t}\n\t\t}\n\n\t\tpublic void CustomSegmentSimulationStep(ushort segmentID, ref NetSegment data) {\n\t\t\t//try {\n\t\t\t// TODO check if this is required\n\t\t\t\tuint curLaneId = data.m_lanes;\n\t\t\t\tint numLanes = data.Info.m_lanes.Length;\n\t\t\t\tuint laneIndex = 0;\n\n\t\t\t\twhile (laneIndex < numLanes && curLaneId != 0u) {\n\t\t\t\t\tFlags.applyLaneArrowFlags(curLaneId);\n\n\t\t\t\t\tlaneIndex++;\n\t\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t}\n\n\t\t\t//SegmentEndManager.Instance.SegmentSimulationStep(segmentID);\n\t\t\t/*} catch (Exception e) {\n\t\t\t\tLog.Error($\"Error occured while housekeeping segment {segmentID}: \" + e.ToString());\n\t\t\t}*/\n\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"traffic-measurement\")) {\n#endif\n\t\t\t\tif (!Options.isStockLaneChangerUsed()) {\n\t\t\t\t\tif (segmentID < lastSimulatedSegmentId) {\n\t\t\t\t\t\t// segment simulation restart\n\t\t\t\t\t\t++trafficMeasurementMod;\n\t\t\t\t\t\tif (trafficMeasurementMod >= 4)\n\t\t\t\t\t\t\ttrafficMeasurementMod = 0;\n\t\t\t\t\t}\n\t\t\t\t\tlastSimulatedSegmentId = segmentID;\n\n\t\t\t\t\tbool doTrafficMeasurement = true;\n\t\t\t\t\tif (Options.simAccuracy == 1 || Options.simAccuracy == 2) {\n\t\t\t\t\t\tdoTrafficMeasurement = (segmentID & 1) == trafficMeasurementMod;\n\t\t\t\t\t} else if (Options.simAccuracy >= 3) {\n\t\t\t\t\t\tdoTrafficMeasurement = (segmentID & 3) == trafficMeasurementMod;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (doTrafficMeasurement) {\n\t\t\t\t\t\t//try {\n\t\t\t\t\t\t\tTrafficMeasurementManager.Instance.SimulationStep(segmentID, ref data);\n\t\t\t\t\t\t/*} catch (Exception e) {\n\t\t\t\t\t\t\tLog.Error(\"Error occured while calculating lane traffic density: \" + e.ToString());\n\t\t\t\t\t\t}*/\n\t\t\t\t\t}\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\n\t\t\tOriginalSimulationStep(segmentID, ref data);\n\t\t}\n\n\t\tpublic static void GetTrafficLightState(\n#if DEBUG\n\t\t\tushort vehicleId, ref Vehicle vehicleData,\n#endif\n\t\t\tushort nodeId, ushort fromSegmentId, byte fromLaneIndex, ushort toSegmentId, ref NetSegment segmentData, uint frame, out RoadBaseAI.TrafficLightState vehicleLightState, out RoadBaseAI.TrafficLightState pedestrianLightState) {\n\n\t\t\tbool callStockMethod = true;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"callStockMethod\")) {\n#endif\n\t\t\t\tcallStockMethod = !Options.timedLightsEnabled || !TrafficLightSimulationManager.Instance.TrafficLightSimulations[nodeId].IsSimulationRunning();\n#if BENCHMARK\n\t\t\t}\n#endif\n\n\t\t\tif (callStockMethod) {\n\t\t\t\tRoadBaseAI.GetTrafficLightState(nodeId, ref segmentData, frame, out vehicleLightState, out pedestrianLightState);\n\t\t\t} else {\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"GetCustomTrafficLightState\")) {\n#endif\n\t\t\t\t\tGetCustomTrafficLightState(\n#if DEBUG\n\t\t\t\t\t\tvehicleId, ref vehicleData,\n#endif\n\t\t\t\t\t\tnodeId, fromSegmentId, fromLaneIndex, toSegmentId, out vehicleLightState, out pedestrianLightState, ref TrafficLightSimulationManager.Instance.TrafficLightSimulations[nodeId]);\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t}\n\n\t\tpublic static void GetTrafficLightState(\n#if DEBUG\n\t\t\tushort vehicleId, ref Vehicle vehicleData,\n#endif\n\t\t\tushort nodeId, ushort fromSegmentId, byte fromLaneIndex, ushort toSegmentId, ref NetSegment segmentData, uint frame, out RoadBaseAI.TrafficLightState vehicleLightState, out RoadBaseAI.TrafficLightState pedestrianLightState, out bool vehicles, out bool pedestrians) {\n\n\n\t\t\tbool callStockMethod = true;\n\t\t\t\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"callStockMethod\")) {\n#endif\n\t\t\t\tcallStockMethod = !Options.timedLightsEnabled || !TrafficLightSimulationManager.Instance.TrafficLightSimulations[nodeId].IsSimulationRunning();\n#if BENCHMARK\n\t\t\t}\n#endif\n\n\t\t\tif (callStockMethod) {\n\t\t\t\tRoadBaseAI.GetTrafficLightState(nodeId, ref segmentData, frame, out vehicleLightState, out pedestrianLightState, out vehicles, out pedestrians);\n\t\t\t} else {\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"GetCustomTrafficLightState\")) {\n#endif\n\t\t\t\t\tGetCustomTrafficLightState(\n#if DEBUG\n\t\t\t\t\t\tvehicleId, ref vehicleData,\n#endif\n\t\t\t\t\t\tnodeId, fromSegmentId, fromLaneIndex, toSegmentId, out vehicleLightState, out pedestrianLightState, ref TrafficLightSimulationManager.Instance.TrafficLightSimulations[nodeId]);\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t\tvehicles = false;\n\t\t\t\t\tpedestrians = false;\n\t\t\t}\n\t\t}\n\n\t\t// TODO refactor to manager\n\t\tprivate static void GetCustomTrafficLightState(\n#if DEBUG\n\t\t\tushort vehicleId, ref Vehicle vehicleData,\n#endif\n\t\t\tushort nodeId, ushort fromSegmentId, byte fromLaneIndex, ushort toSegmentId, out RoadBaseAI.TrafficLightState vehicleLightState, out RoadBaseAI.TrafficLightState pedestrianLightState, ref TrafficLightSimulation nodeSim) {\n\n\t\t\t// get responsible traffic light\n\t\t\t//Log._Debug($\"GetTrafficLightState: Getting custom light for vehicle {vehicleId} @ node {nodeId}, segment {fromSegmentId}, lane {fromLaneIndex}.\");\n\t\t\tSegmentGeometry geometry = SegmentGeometry.Get(fromSegmentId);\n\t\t\tif (geometry == null) {\n\t\t\t\tLog.Error($\"GetTrafficLightState: No geometry information @ node {nodeId}, segment {fromSegmentId}.\");\n\t\t\t\tvehicleLightState = TrafficLightState.Green;\n\t\t\t\tpedestrianLightState = TrafficLightState.Green;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// determine node position at `fromSegment` (start/end)\n\t\t\tbool isStartNode = geometry.StartNodeId() == nodeId;\n\n\t\t\tICustomSegmentLights lights = CustomSegmentLightsManager.Instance.GetSegmentLights(fromSegmentId, isStartNode, false);\n\n\t\t\tif (lights != null) {\n\t\t\t\t// get traffic lights state for pedestrians\n\t\t\t\tpedestrianLightState = (lights.PedestrianLightState != null) ? (RoadBaseAI.TrafficLightState)lights.PedestrianLightState : RoadBaseAI.TrafficLightState.Green;\n\t\t\t} else {\n\t\t\t\tpedestrianLightState = TrafficLightState.Green;\n\t\t\t\tLog._Debug($\"GetTrafficLightState: No pedestrian light @ node {nodeId}, segment {fromSegmentId} found.\");\n\t\t\t}\n\n\t\t\tICustomSegmentLight light = lights == null ? null : lights.GetCustomLight(fromLaneIndex);\n\t\t\tif (lights == null || light == null) {\n\t\t\t\t//Log.Warning($\"GetTrafficLightState: No custom light for vehicle {vehicleId} @ node {nodeId}, segment {fromSegmentId}, lane {fromLaneIndex} found. lights null? {lights == null} light null? {light == null}\");\n\t\t\t\tvehicleLightState = RoadBaseAI.TrafficLightState.Green;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// get traffic light state from responsible traffic light\n\t\t\tif (toSegmentId == fromSegmentId) {\n\t\t\t\tvehicleLightState = Constants.ServiceFactory.SimulationService.LeftHandDrive ? light.LightRight : light.LightLeft;\n\t\t\t} else if (geometry.IsLeftSegment(toSegmentId, isStartNode)) {\n\t\t\t\tvehicleLightState = light.LightLeft;\n\t\t\t} else if (geometry.IsRightSegment(toSegmentId, isStartNode)) {\n\t\t\t\tvehicleLightState = light.LightRight;\n\t\t\t} else {\n\t\t\t\tvehicleLightState = light.LightMain;\n\t\t\t}\n#if DEBUG\n\t\t\t//Log._Debug($\"GetTrafficLightState: Getting light for vehicle {vehicleId} @ node {nodeId}, segment {fromSegmentId}, lane {fromLaneIndex}. vehicleLightState={vehicleLightState}, pedestrianLightState={pedestrianLightState}\");\n#endif\n\t\t}\n\n\t\tpublic static void CustomSetTrafficLightState(ushort nodeID, ref NetSegment segmentData, uint frame, RoadBaseAI.TrafficLightState vehicleLightState, RoadBaseAI.TrafficLightState pedestrianLightState, bool vehicles, bool pedestrians) {\n\t\t\tOriginalSetTrafficLightState(false, nodeID, ref segmentData, frame, vehicleLightState, pedestrianLightState, vehicles, pedestrians);\n\t\t}\n\n\t\tpublic static void OriginalSetTrafficLightState(bool customCall, ushort nodeID, ref NetSegment segmentData, uint frame, RoadBaseAI.TrafficLightState vehicleLightState, RoadBaseAI.TrafficLightState pedestrianLightState, bool vehicles, bool pedestrians) {\n\t\t\t// NON-STOCK CODE START\n\t\t\tbool callStockCode = true;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"callStockCode\")) {\n#endif\n\t\t\t\tcallStockCode = customCall || !Options.timedLightsEnabled || !TrafficLightSimulationManager.Instance.TrafficLightSimulations[nodeID].IsSimulationRunning();\n#if BENCHMARK\n\t\t\t}\n#endif\n\n\t\t\tif (callStockCode) {\n\t\t\t\t/// NON-STOCK CODE END ///\n\t\t\t\tint num = (int)pedestrianLightState << 2 | (int)vehicleLightState;\n\t\t\t\tif (segmentData.m_startNode == nodeID) {\n\t\t\t\t\tif ((frame >> 8 & 1u) == 0u) {\n\t\t\t\t\t\tsegmentData.m_trafficLightState0 = (byte)((int)(segmentData.m_trafficLightState0 & 240) | num);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsegmentData.m_trafficLightState1 = (byte)((int)(segmentData.m_trafficLightState1 & 240) | num);\n\t\t\t\t\t}\n\t\t\t\t\tif (vehicles) {\n\t\t\t\t\t\tsegmentData.m_flags |= NetSegment.Flags.TrafficStart;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsegmentData.m_flags &= ~NetSegment.Flags.TrafficStart;\n\t\t\t\t\t}\n\t\t\t\t\tif (pedestrians) {\n\t\t\t\t\t\tsegmentData.m_flags |= NetSegment.Flags.CrossingStart;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsegmentData.m_flags &= ~NetSegment.Flags.CrossingStart;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif ((frame >> 8 & 1u) == 0u) {\n\t\t\t\t\t\tsegmentData.m_trafficLightState0 = (byte)((int)(segmentData.m_trafficLightState0 & 15) | num << 4);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsegmentData.m_trafficLightState1 = (byte)((int)(segmentData.m_trafficLightState1 & 15) | num << 4);\n\t\t\t\t\t}\n\t\t\t\t\tif (vehicles) {\n\t\t\t\t\t\tsegmentData.m_flags |= NetSegment.Flags.TrafficEnd;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsegmentData.m_flags &= ~NetSegment.Flags.TrafficEnd;\n\t\t\t\t\t}\n\t\t\t\t\tif (pedestrians) {\n\t\t\t\t\t\tsegmentData.m_flags |= NetSegment.Flags.CrossingEnd;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsegmentData.m_flags &= ~NetSegment.Flags.CrossingEnd;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} // NON-STOCK CODE\n\t\t}\n\n\t\tpublic void CustomClickNodeButton(ushort nodeID, ref NetNode data, int index) {\n\t\t\tif ((data.m_flags & NetNode.Flags.Junction) != NetNode.Flags.None &&\n\t\t\t\tSingleton<InfoManager>.instance.CurrentMode == InfoManager.InfoMode.TrafficRoutes &&\n\t\t\t\tSingleton<InfoManager>.instance.CurrentSubMode == InfoManager.SubInfoMode.WaterPower) {\n\t\t\t\tif (index == -1) {\n\t\t\t\t\t/*data.m_flags ^= NetNode.Flags.TrafficLights;\n\t\t\t\t\tdata.m_flags |= NetNode.Flags.CustomTrafficLights;*/\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\tToggleTrafficLightsTool toggleTool = (ToggleTrafficLightsTool)UIBase.GetTrafficManagerTool(true).GetSubTool(ToolMode.SwitchTrafficLight);\n\t\t\t\t\ttoggleTool.ToggleTrafficLight(nodeID, ref data, false);\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\tthis.UpdateNodeFlags(nodeID, ref data);\n\t\t\t\t\tSingleton<NetManager>.instance.m_yieldLights.Disable();\n\t\t\t\t} else if (index >= 1 && index <= 8 && (data.m_flags & (NetNode.Flags.TrafficLights | NetNode.Flags.OneWayIn)) == NetNode.Flags.None) {\n\t\t\t\t\tushort segmentId = data.GetSegment(index - 1);\n\t\t\t\t\tif (segmentId != 0) {\n\t\t\t\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\t\t\t\tNetInfo info = netManager.m_segments.m_buffer[(int)segmentId].Info;\n\t\t\t\t\t\tif ((info.m_vehicleTypes & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Tram)) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\t\tbool flag = netManager.m_segments.m_buffer[(int)segmentId].m_startNode == nodeID;\n\t\t\t\t\t\t\tNetSegment.Flags flags = (!flag) ? NetSegment.Flags.YieldEnd : NetSegment.Flags.YieldStart;\n\t\t\t\t\t\t\tnetManager.m_segments.m_buffer[segmentId].m_flags ^= flags;\n\t\t\t\t\t\t\tnetManager.m_segments.m_buffer[(int)segmentId].UpdateLanes(segmentId, true);\n\t\t\t\t\t\t\tSingleton<NetManager>.instance.m_yieldLights.Disable();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void CustomUpdateLanes(ushort segmentID, ref NetSegment data, bool loading) {\n\t\t\t// stock code start\n\t\t\tNetManager instance = Singleton<NetManager>.instance;\n\t\t\tbool flag = Singleton<SimulationManager>.instance.m_metaData.m_invertTraffic == SimulationMetaData.MetaBool.True;\n\t\t\tVector3 vector;\n\t\t\tVector3 a;\n\t\t\tbool smoothStart;\n\t\t\tdata.CalculateCorner(segmentID, true, true, true, out vector, out a, out smoothStart);\n\t\t\tVector3 a2;\n\t\t\tVector3 b;\n\t\t\tbool smoothEnd;\n\t\t\tdata.CalculateCorner(segmentID, true, false, true, out a2, out b, out smoothEnd);\n\t\t\tVector3 a3;\n\t\t\tVector3 b2;\n\t\t\tdata.CalculateCorner(segmentID, true, true, false, out a3, out b2, out smoothStart);\n\t\t\tVector3 vector2;\n\t\t\tVector3 a4;\n\t\t\tdata.CalculateCorner(segmentID, true, false, false, out vector2, out a4, out smoothEnd);\n\t\t\tif ((data.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\tdata.m_cornerAngleStart = (byte)(Mathf.RoundToInt(Mathf.Atan2(a3.z - vector.z, a3.x - vector.x) * 40.7436638f) & 255);\n\t\t\t\tdata.m_cornerAngleEnd = (byte)(Mathf.RoundToInt(Mathf.Atan2(a2.z - vector2.z, a2.x - vector2.x) * 40.7436638f) & 255);\n\t\t\t} else {\n\t\t\t\tdata.m_cornerAngleStart = (byte)(Mathf.RoundToInt(Mathf.Atan2(vector.z - a3.z, vector.x - a3.x) * 40.7436638f) & 255);\n\t\t\t\tdata.m_cornerAngleEnd = (byte)(Mathf.RoundToInt(Mathf.Atan2(vector2.z - a2.z, vector2.x - a2.x) * 40.7436638f) & 255);\n\t\t\t}\n\t\t\tint num = 0;\n\t\t\tint num2 = 0;\n\t\t\tint num3 = 0;\n\t\t\tint num4 = 0;\n\t\t\tint num5 = 0;\n\t\t\tint num6 = 0;\n\t\t\tbool flag2 = false;\n\t\t\tbool flag3 = false;\n\t\t\tinstance.m_nodes.m_buffer[(int)data.m_endNode].CountLanes(data.m_endNode, segmentID, NetInfo.Direction.Forward, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, -data.m_endDirection, ref num, ref num2, ref num3, ref num4, ref num5, ref num6);\n\t\t\tif ((instance.m_nodes.m_buffer[(int)data.m_endNode].m_flags & (NetNode.Flags.End | NetNode.Flags.Middle | NetNode.Flags.Bend | NetNode.Flags.Outside)) != NetNode.Flags.None) {\n\t\t\t\tif (num + num2 + num3 == 0) {\n\t\t\t\t\tflag3 = true;\n\t\t\t\t} else {\n\t\t\t\t\tflag2 = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tint num7 = 0;\n\t\t\tint num8 = 0;\n\t\t\tint num9 = 0;\n\t\t\tint num10 = 0;\n\t\t\tint num11 = 0;\n\t\t\tint num12 = 0;\n\t\t\tbool flag4 = false;\n\t\t\tbool flag5 = false;\n\t\t\tinstance.m_nodes.m_buffer[(int)data.m_startNode].CountLanes(data.m_startNode, segmentID, NetInfo.Direction.Forward, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, -data.m_startDirection, ref num7, ref num8, ref num9, ref num10, ref num11, ref num12);\n\t\t\tif ((instance.m_nodes.m_buffer[(int)data.m_startNode].m_flags & (NetNode.Flags.End | NetNode.Flags.Middle | NetNode.Flags.Bend | NetNode.Flags.Outside)) != NetNode.Flags.None) {\n\t\t\t\tif (num7 + num8 + num9 == 0) {\n\t\t\t\t\tflag5 = true;\n\t\t\t\t} else {\n\t\t\t\t\tflag4 = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tNetLane.Flags flags = NetLane.Flags.None;\n\t\t\tif (num4 != 0 && num == 0) {\n\t\t\t\tflags |= (((data.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? NetLane.Flags.EndOneWayLeft : NetLane.Flags.StartOneWayLeft);\n\t\t\t}\n\t\t\tif (num6 != 0 && num3 == 0) {\n\t\t\t\tflags |= (((data.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? NetLane.Flags.EndOneWayRight : NetLane.Flags.StartOneWayRight);\n\t\t\t}\n\t\t\tif (num10 != 0 && num7 == 0) {\n\t\t\t\tflags |= (((data.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? NetLane.Flags.StartOneWayLeft : NetLane.Flags.EndOneWayLeft);\n\t\t\t}\n\t\t\tif (num12 != 0 && num9 == 0) {\n\t\t\t\tflags |= (((data.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? NetLane.Flags.StartOneWayRight : NetLane.Flags.EndOneWayRight);\n\t\t\t}\n\t\t\tif ((data.m_flags & NetSegment.Flags.YieldStart) != NetSegment.Flags.None) {\n\t\t\t\tflags |= (((data.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? NetLane.Flags.YieldStart : NetLane.Flags.YieldEnd);\n\t\t\t}\n\t\t\tif ((data.m_flags & NetSegment.Flags.YieldEnd) != NetSegment.Flags.None) {\n\t\t\t\tflags |= (((data.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? NetLane.Flags.YieldEnd : NetLane.Flags.YieldStart);\n\t\t\t}\n\t\t\tfloat num13 = 0f;\n\t\t\tfloat num14 = 0f;\n\t\t\tuint num15 = 0u;\n\t\t\tuint num16 = data.m_lanes;\n\t\t\tfor (int i = 0; i < this.m_info.m_lanes.Length; i++) {\n\t\t\t\tif (num16 == 0u) {\n\t\t\t\t\tif (!Singleton<NetManager>.instance.CreateLanes(out num16, ref Singleton<SimulationManager>.instance.m_randomizer, segmentID, 1)) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (num15 != 0u) {\n\t\t\t\t\t\tinstance.m_lanes.m_buffer[num15].m_nextLane = num16;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdata.m_lanes = num16;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tNetInfo.Lane lane = this.m_info.m_lanes[i];\n\t\t\t\tfloat num17 = lane.m_position / (this.m_info.m_halfWidth * 2f) + 0.5f;\n\t\t\t\tif ((data.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\t\tnum17 = 1f - num17;\n\t\t\t\t}\n\t\t\t\tVector3 vector3 = vector + (a3 - vector) * num17;\n\t\t\t\tVector3 startDir = Vector3.Lerp(a, b2, num17);\n\t\t\t\tVector3 vector4 = vector2 + (a2 - vector2) * num17;\n\t\t\t\tVector3 endDir = Vector3.Lerp(a4, b, num17);\n\t\t\t\tvector3.y += lane.m_verticalOffset;\n\t\t\t\tvector4.y += lane.m_verticalOffset;\n\t\t\t\tVector3 b3;\n\t\t\t\tVector3 c;\n\t\t\t\tNetSegment.CalculateMiddlePoints(vector3, startDir, vector4, endDir, smoothStart, smoothEnd, out b3, out c);\n\t\t\t\tNetLane.Flags flags2 = (NetLane.Flags)instance.m_lanes.m_buffer[num16].m_flags;\n\t\t\t\tNetLane.Flags flags3 = flags;\n\t\t\t\tflags2 &= ~(NetLane.Flags.Forward | NetLane.Flags.Left | NetLane.Flags.Right | NetLane.Flags.Merge | NetLane.Flags.YieldStart | NetLane.Flags.YieldEnd | NetLane.Flags.StartOneWayLeft | NetLane.Flags.StartOneWayRight | NetLane.Flags.EndOneWayLeft | NetLane.Flags.EndOneWayRight);\n\t\t\t\tif ((byte)(lane.m_finalDirection & NetInfo.Direction.Both) == 2) {\n\t\t\t\t\tflags3 &= ~NetLane.Flags.YieldEnd;\n\t\t\t\t}\n\t\t\t\tif ((byte)(lane.m_finalDirection & NetInfo.Direction.Both) == 1) {\n\t\t\t\t\tflags3 &= ~NetLane.Flags.YieldStart;\n\t\t\t\t}\n\t\t\t\tif ((lane.m_vehicleType & VehicleInfo.VehicleType.Monorail) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\tflags3 &= ~(NetLane.Flags.YieldStart | NetLane.Flags.YieldEnd);\n\t\t\t\t}\n\t\t\t\tflags2 |= flags3;\n\t\t\t\tif (flag) {\n\t\t\t\t\tflags2 |= NetLane.Flags.Inverted;\n\t\t\t\t} else {\n\t\t\t\t\tflags2 &= ~NetLane.Flags.Inverted;\n\t\t\t\t}\n\t\t\t\tint num18 = 0;\n\t\t\t\tint num19 = 255;\n\t\t\t\tif ((byte)(lane.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {\n\t\t\t\t\tbool flag6 = (byte)(lane.m_finalDirection & NetInfo.Direction.Forward) != 0 == ((data.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None);\n\t\t\t\t\tint num20;\n\t\t\t\t\tint num21;\n\t\t\t\t\tint num22;\n\t\t\t\t\tif (flag6) {\n\t\t\t\t\t\tnum20 = num;\n\t\t\t\t\t\tnum21 = num2;\n\t\t\t\t\t\tnum22 = num3;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnum20 = num7;\n\t\t\t\t\t\tnum21 = num8;\n\t\t\t\t\t\tnum22 = num9;\n\t\t\t\t\t}\n\t\t\t\t\tint num23;\n\t\t\t\t\tint num24;\n\t\t\t\t\tif ((byte)(lane.m_finalDirection & NetInfo.Direction.Forward) != 0) {\n\t\t\t\t\t\tnum23 = lane.m_similarLaneIndex;\n\t\t\t\t\t\tnum24 = lane.m_similarLaneCount - lane.m_similarLaneIndex - 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnum23 = lane.m_similarLaneCount - lane.m_similarLaneIndex - 1;\n\t\t\t\t\t\tnum24 = lane.m_similarLaneIndex;\n\t\t\t\t\t}\n\t\t\t\t\tint num25 = num20 + num21 + num22;\n\t\t\t\t\tnum18 = 255;\n\t\t\t\t\tnum19 = 0;\n\t\t\t\t\tif (num25 != 0) {\n\t\t\t\t\t\tif (lane.m_similarLaneCount > num25 && num25 > 0) {\n\t\t\t\t\t\t\tnum18 = num25 * num23 / lane.m_similarLaneCount;\n\t\t\t\t\t\t\tnum19 = num25 - num25 * num24 / lane.m_similarLaneCount;\n\t\t\t\t\t\t\tflags2 |= NetLane.Flags.Merge;\n\t\t\t\t\t\t\tif (num18 < num20) {\n\t\t\t\t\t\t\t\tflags2 |= NetLane.Flags.Left;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (num25 - num19 < num22) {\n\t\t\t\t\t\t\t\tflags2 |= NetLane.Flags.Right;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (num21 != 0 && num18 < num20 + num21 && num19 > num20) {\n\t\t\t\t\t\t\t\tflags2 |= NetLane.Flags.Forward;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tint num26;\n\t\t\t\t\t\t\tint num27;\n\t\t\t\t\t\t\tif (lane.m_similarLaneCount >= num25) {\n\t\t\t\t\t\t\t\tnum26 = num20;\n\t\t\t\t\t\t\t\tnum27 = num22;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tnum26 = num20 * lane.m_similarLaneCount / (num25 + (num21 >> 1));\n\t\t\t\t\t\t\t\tnum27 = num22 * lane.m_similarLaneCount / (num25 + (num21 >> 1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tint num28 = num26;\n\t\t\t\t\t\t\tint num29 = lane.m_similarLaneCount - num26 - num27;\n\t\t\t\t\t\t\tint num30 = num27;\n\t\t\t\t\t\t\tif (num29 > 0) {\n\t\t\t\t\t\t\t\tif (num20 > num26) {\n\t\t\t\t\t\t\t\t\tnum28++;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (num22 > num27) {\n\t\t\t\t\t\t\t\t\tnum30++;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (num23 < num28) {\n\t\t\t\t\t\t\t\tint num31 = (num23 * num20 + num28 - 1) / num28;\n\t\t\t\t\t\t\t\tint num32 = ((num23 + 1) * num20 + num28 - 1) / num28;\n\t\t\t\t\t\t\t\tif (num32 > num31) {\n\t\t\t\t\t\t\t\t\tflags2 |= NetLane.Flags.Left;\n\t\t\t\t\t\t\t\t\tnum18 = Mathf.Min(num18, num31);\n\t\t\t\t\t\t\t\t\tnum19 = Mathf.Max(num19, num32);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (num23 >= num26 && num24 >= num27 && num21 != 0) {\n\t\t\t\t\t\t\t\tif (lane.m_similarLaneCount > num25) {\n\t\t\t\t\t\t\t\t\tnum26++;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tint num33 = num20 + ((num23 - num26) * num21 + num29 - 1) / num29;\n\t\t\t\t\t\t\t\tint num34 = num20 + ((num23 + 1 - num26) * num21 + num29 - 1) / num29;\n\t\t\t\t\t\t\t\tif (num34 > num33) {\n\t\t\t\t\t\t\t\t\tflags2 |= NetLane.Flags.Forward;\n\t\t\t\t\t\t\t\t\tnum18 = Mathf.Min(num18, num33);\n\t\t\t\t\t\t\t\t\tnum19 = Mathf.Max(num19, num34);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (num24 < num30) {\n\t\t\t\t\t\t\t\tint num35 = num25 - ((num24 + 1) * num22 + num30 - 1) / num30;\n\t\t\t\t\t\t\t\tint num36 = num25 - (num24 * num22 + num30 - 1) / num30;\n\t\t\t\t\t\t\t\tif (num36 > num35) {\n\t\t\t\t\t\t\t\t\tflags2 |= NetLane.Flags.Right;\n\t\t\t\t\t\t\t\t\tnum18 = Mathf.Min(num18, num35);\n\t\t\t\t\t\t\t\t\tnum19 = Mathf.Max(num19, num36);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (this.m_highwayRules) {\n\t\t\t\t\t\t\t\tif ((flags2 & NetLane.Flags.LeftRight) == NetLane.Flags.Left) {\n\t\t\t\t\t\t\t\t\tif ((flags2 & NetLane.Flags.Forward) == NetLane.Flags.None || (num21 >= 2 && num20 == 1)) {\n\t\t\t\t\t\t\t\t\t\tnum19 = Mathf.Min(num19, num18 + 1);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else if ((flags2 & NetLane.Flags.LeftRight) == NetLane.Flags.Right && ((flags2 & NetLane.Flags.Forward) == NetLane.Flags.None || (num21 >= 2 && num22 == 1))) {\n\t\t\t\t\t\t\t\t\tnum18 = Mathf.Max(num18, num19 - 1);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (flag6) {\n\t\t\t\t\t\tif (flag2) {\n\t\t\t\t\t\t\tflags2 &= ~(NetLane.Flags.Forward | NetLane.Flags.Left | NetLane.Flags.Right);\n\t\t\t\t\t\t} else if (flag3) {\n\t\t\t\t\t\t\tflags2 |= NetLane.Flags.Forward;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (flag4) {\n\t\t\t\t\t\tflags2 &= ~(NetLane.Flags.Forward | NetLane.Flags.Left | NetLane.Flags.Right);\n\t\t\t\t\t} else if (flag5) {\n\t\t\t\t\t\tflags2 |= NetLane.Flags.Forward;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tinstance.m_lanes.m_buffer[num16].m_bezier = new Bezier3(vector3, b3, c, vector4);\n\t\t\t\tinstance.m_lanes.m_buffer[num16].m_segment = segmentID;\n\t\t\t\tinstance.m_lanes.m_buffer[num16].m_flags = (ushort)flags2;\n\t\t\t\tinstance.m_lanes.m_buffer[num16].m_firstTarget = (byte)num18;\n\t\t\t\tinstance.m_lanes.m_buffer[num16].m_lastTarget = (byte)num19;\n\t\t\t\tnum13 += instance.m_lanes.m_buffer[num16].UpdateLength();\n\t\t\t\tnum14 += 1f;\n\t\t\t\tnum15 = num16;\n\t\t\t\tnum16 = instance.m_lanes.m_buffer[num16].m_nextLane;\n\t\t\t}\n\t\t\tif (num14 != 0f) {\n\t\t\t\tdata.m_averageLength = num13 / num14;\n\t\t\t} else {\n\t\t\t\tdata.m_averageLength = 0f;\n\t\t\t}\n\t\t\tbool flag7 = false;\n\t\t\tif (data.m_averageLength < 11f && (instance.m_nodes.m_buffer[(int)data.m_startNode].m_flags & NetNode.Flags.Junction) != NetNode.Flags.None && (instance.m_nodes.m_buffer[(int)data.m_endNode].m_flags & NetNode.Flags.Junction) != NetNode.Flags.None) {\n\t\t\t\tflag7 = true;\n\t\t\t}\n\t\t\tnum16 = data.m_lanes;\n\t\t\tint num37 = 0;\n\t\t\twhile (num37 < this.m_info.m_lanes.Length && num16 != 0u) {\n\t\t\t\tNetLane.Flags flags4 = (NetLane.Flags)instance.m_lanes.m_buffer[num16].m_flags & ~NetLane.Flags.JoinedJunction;\n\t\t\t\tif (flag7) {\n\t\t\t\t\tflags4 |= NetLane.Flags.JoinedJunction;\n\t\t\t\t}\n\t\t\t\tinstance.m_lanes.m_buffer[num16].m_flags = (ushort)flags4;\n\t\t\t\tnum16 = instance.m_lanes.m_buffer[num16].m_nextLane;\n\t\t\t\tnum37++;\n\t\t\t}\n\t\t\tif (!loading) {\n\t\t\t\tint num38 = Mathf.Max((int)((data.m_bounds.min.x - 16f) / 64f + 135f), 0);\n\t\t\t\tint num39 = Mathf.Max((int)((data.m_bounds.min.z - 16f) / 64f + 135f), 0);\n\t\t\t\tint num40 = Mathf.Min((int)((data.m_bounds.max.x + 16f) / 64f + 135f), 269);\n\t\t\t\tint num41 = Mathf.Min((int)((data.m_bounds.max.z + 16f) / 64f + 135f), 269);\n\t\t\t\tfor (int j = num39; j <= num41; j++) {\n\t\t\t\t\tfor (int k = num38; k <= num40; k++) {\n\t\t\t\t\t\tushort num42 = instance.m_nodeGrid[j * 270 + k];\n\t\t\t\t\t\tint num43 = 0;\n\t\t\t\t\t\twhile (num42 != 0) {\n\t\t\t\t\t\t\tNetInfo info = instance.m_nodes.m_buffer[(int)num42].Info;\n\t\t\t\t\t\t\tVector3 position = instance.m_nodes.m_buffer[(int)num42].m_position;\n\t\t\t\t\t\t\tfloat num44 = Mathf.Max(Mathf.Max(data.m_bounds.min.x - 16f - position.x, data.m_bounds.min.z - 16f - position.z), Mathf.Max(position.x - data.m_bounds.max.x - 16f, position.z - data.m_bounds.max.z - 16f));\n\t\t\t\t\t\t\tif (num44 < 0f) {\n\t\t\t\t\t\t\t\tinfo.m_netAI.NearbyLanesUpdated(num42, ref instance.m_nodes.m_buffer[(int)num42]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnum42 = instance.m_nodes.m_buffer[(int)num42].m_nextGridNode;\n\t\t\t\t\t\t\tif (++num43 >= 32768) {\n\t\t\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (this.m_info.m_hasPedestrianLanes && (this.m_info.m_hasForwardVehicleLanes || this.m_info.m_hasBackwardVehicleLanes)) {\n\t\t\t\t\tCheckBuildings(segmentID, ref data);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// stock code end\n\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"applyLaneArrowFlags\")) {\n#endif\n\t\t\t\ttry {\n\t\t\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\t\t\t// update lane arrows\n\t\t\t\t\tuint laneId = netManager.m_segments.m_buffer[segmentID].m_lanes;\n\t\t\t\t\twhile (laneId != 0) {\n\t\t\t\t\t\tif (!Flags.applyLaneArrowFlags(laneId)) {\n\t\t\t\t\t\t\tFlags.removeLaneArrowFlags(laneId);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlaneId = netManager.m_lanes.m_buffer[laneId].m_nextLane;\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Error occured in CustomRoadAI.CustomUpdateLanes @ seg. {segmentID}: \" + e.ToString());\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\t\t}\n\n\t\tpublic static void CustomGetTrafficLightNodeState(ushort nodeID, ref NetNode nodeData, ushort segmentID, ref NetSegment segmentData, ref NetNode.Flags flags, ref Color color) {\n\t\t\tbool customSim = false;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"customSim\")) {\n#endif\n\t\t\t\tcustomSim = Options.timedLightsEnabled && TrafficLightSimulationManager.Instance.TrafficLightSimulations[nodeID].IsSimulationRunning();\n#if BENCHMARK\n\t\t\t}\n#endif\n\n\t\t\tuint num = Singleton<SimulationManager>.instance.m_referenceFrameIndex - 15u;\n\t\t\tuint num2 = (uint)(((int)nodeID << 8) / 32768);\n\t\t\tuint num3 = num - num2 & 255u;\n\t\t\tRoadBaseAI.TrafficLightState trafficLightState;\n\t\t\tRoadBaseAI.TrafficLightState trafficLightState2;\n\t\t\tRoadBaseAI.GetTrafficLightState(nodeID, ref segmentData, num - num2, out trafficLightState, out trafficLightState2);\n\t\t\tcolor.a = 0.5f;\n\t\t\tswitch (trafficLightState) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tcolor.g = 1f;\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.RedToGreen:\n\t\t\t\t\tif (customSim) {\n\t\t\t\t\t\tcolor.r = 1f;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (num3 < 45u) {\n\t\t\t\t\t\t\tcolor.g = 0f;\n\t\t\t\t\t\t} else if (num3 < 60u) {\n\t\t\t\t\t\t\tcolor.r = 1f;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcolor.g = 1f;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\tcolor.g = 0f;\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.GreenToRed:\n\t\t\t\t\tif (customSim) {\n\t\t\t\t\t\tcolor.r = 1f;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (num3 < 45u) {\n\t\t\t\t\t\t\tcolor.r = 1f;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcolor.g = 0f;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tswitch (trafficLightState2) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tcolor.b = 1f;\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.RedToGreen:\n\t\t\t\t\tif (customSim) {\n\t\t\t\t\t\tcolor.b = 0f;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (num3 < 45u) {\n\t\t\t\t\t\t\tcolor.b = 0f;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcolor.b = 1f;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\tcolor.b = 0f;\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.GreenToRed:\n\t\t\t\t\tif (customSim) {\n\t\t\t\t\t\tcolor.b = 0f;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (num3 < 45u) {\n\t\t\t\t\t\t\tif ((num3 / 8u & 1u) == 1u) {\n\t\t\t\t\t\t\t\tcolor.b = 1f;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcolor.b = 0f;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n#region stock code\n\n\t\tpublic void OriginalSimulationStep(ushort nodeID, ref NetNode data) { // pure stock code\n\t\t\tif ((data.m_flags & NetNode.Flags.TrafficLights) != NetNode.Flags.None) {\n\t\t\t\tif ((data.m_flags & NetNode.Flags.LevelCrossing) != NetNode.Flags.None) {\n\t\t\t\t\tTrainTrackBaseAI.LevelCrossingSimulationStep(nodeID, ref data);\n\t\t\t\t} else {\n\t\t\t\t\tRoadBaseAI.TrafficLightSimulationStep(nodeID, ref data);\n\t\t\t\t}\n\t\t\t}\n\t\t\tNetManager instance = Singleton<NetManager>.instance;\n\t\t\tint num = 0;\n\t\t\tif (this.m_noiseAccumulation != 0) {\n\t\t\t\tint num2 = 0;\n\t\t\t\tfor (int i = 0; i < 8; i++) {\n\t\t\t\t\tushort segment = data.GetSegment(i);\n\t\t\t\t\tif (segment != 0) {\n\t\t\t\t\t\tnum += (int)instance.m_segments.m_buffer[(int)segment].m_noiseDensity;\n\t\t\t\t\t\tnum2++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (num2 != 0) {\n\t\t\t\t\tnum /= num2;\n\t\t\t\t}\n\t\t\t}\n\t\t\tint num3 = 100 - (num - 100) * (num - 100) / 100;\n\t\t\tint num4 = this.m_noiseAccumulation * num3 / 100;\n\t\t\tif (num4 != 0) {\n\t\t\t\tSingleton<ImmaterialResourceManager>.instance.AddResource(ImmaterialResourceManager.Resource.NoisePollution, num4, data.m_position, this.m_noiseRadius);\n\t\t\t}\n\t\t\tif ((data.m_problems & Notification.Problem.RoadNotConnected) != Notification.Problem.None && (data.m_flags & NetNode.Flags.Original) != NetNode.Flags.None) {\n\t\t\t\tGuideController properties = Singleton<GuideManager>.instance.m_properties;\n\t\t\t\tif (properties != null) {\n\t\t\t\t\tinstance.m_outsideNodeNotConnected.Activate(properties.m_outsideNotConnected, nodeID, Notification.Problem.RoadNotConnected, false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void OriginalSimulationStep(ushort segmentID, ref NetSegment data) { // stock + custom code\n\t\t\t// base.SimulationStep START\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tVector3 startNodePos = netManager.m_nodes.m_buffer[data.m_startNode].m_position;\n\t\t\tVector3 endNodePos = netManager.m_nodes.m_buffer[data.m_endNode].m_position;\n\n\t\t\tif (this.HasMaintenanceCost(segmentID, ref data)) {\n\t\t\t\tint maintenanceCost = this.GetMaintenanceCost(startNodePos, endNodePos);\n\t\t\t\tbool simulate = (ulong)(Singleton<SimulationManager>.instance.m_currentFrameIndex >> 8 & 15u) == (ulong)((long)(segmentID & 15));\n\t\t\t\tif (maintenanceCost != 0) {\n\t\t\t\t\tif (simulate) {\n\t\t\t\t\t\tmaintenanceCost = maintenanceCost * 16 / 100 - maintenanceCost / 100 * 15;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmaintenanceCost /= 100;\n\t\t\t\t\t}\n\t\t\t\t\tSingleton<EconomyManager>.instance.FetchResource(EconomyManager.Resource.Maintenance, maintenanceCost, this.m_info.m_class);\n\t\t\t\t}\n\t\t\t\tif (simulate) {\n\t\t\t\t\tfloat startNodeElevation = (float)netManager.m_nodes.m_buffer[data.m_startNode].m_elevation;\n\t\t\t\t\tfloat endNodeElevation = (float)netManager.m_nodes.m_buffer[data.m_endNode].m_elevation;\n\t\t\t\t\tif (this.IsUnderground()) {\n\t\t\t\t\t\tstartNodeElevation = -startNodeElevation;\n\t\t\t\t\t\tendNodeElevation = -endNodeElevation;\n\t\t\t\t\t}\n\t\t\t\t\tint constructionCost = this.GetConstructionCost(startNodePos, endNodePos, startNodeElevation, endNodeElevation);\n\t\t\t\t\tif (constructionCost != 0) {\n\t\t\t\t\t\tStatisticBase statisticBase = Singleton<StatisticsManager>.instance.Acquire<StatisticInt64>(StatisticType.CityValue);\n\t\t\t\t\t\tif (statisticBase != null) {\n\t\t\t\t\t\t\tstatisticBase.Add(constructionCost);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// base.SimulationStep END\n\n\t\t\tSimulationManager simManager = Singleton<SimulationManager>.instance;\n\t\t\tNotification.Problem problem = Notification.RemoveProblems(data.m_problems, Notification.Problem.Flood | Notification.Problem.Snow);\n\t\t\tif ((data.m_flags & NetSegment.Flags.AccessFailed) != NetSegment.Flags.None && Singleton<SimulationManager>.instance.m_randomizer.Int32(16u) == 0) {\n\t\t\t\tdata.m_flags &= ~NetSegment.Flags.AccessFailed;\n\t\t\t}\n\t\t\tfloat totalLength = 0f;\n\t\t\tuint curLaneId = data.m_lanes;\n\t\t\tint laneIndex = 0;\n\t\t\twhile (laneIndex < this.m_info.m_lanes.Length && curLaneId != 0u) {\n\t\t\t\tNetInfo.Lane lane = this.m_info.m_lanes[laneIndex];\n\t\t\t\tif ((byte)(lane.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0 && (lane.m_vehicleType & ~VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\ttotalLength += netManager.m_lanes.m_buffer[curLaneId].m_length;\n\t\t\t\t}\n\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\tlaneIndex++;\n\t\t\t}\n\n\t\t\tint trafficDensity = 0;\n            int noiseDensity = 0;\n\t\t\tif (data.m_trafficBuffer == 65535) {\n\t\t\t\tif ((data.m_flags & NetSegment.Flags.Blocked) == NetSegment.Flags.None) {\n\t\t\t\t\tdata.m_flags |= NetSegment.Flags.Blocked;\n\t\t\t\t\tdata.m_modifiedIndex = simManager.m_currentBuildIndex++;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdata.m_flags &= ~NetSegment.Flags.Blocked;\n\t\t\t\tint lengthDenominator = Mathf.RoundToInt(totalLength) << 4;\n\t\t\t\tif (lengthDenominator != 0) {\n\t\t\t\t\ttrafficDensity = (int)((byte)Mathf.Min((int)(data.m_trafficBuffer * 100) / lengthDenominator, 100));\n                    noiseDensity = (int)((byte)Mathf.Min((int)(data.m_noiseBuffer * 250) / lengthDenominator, 100));\n                }\n\t\t\t}\n\t\t\tdata.m_trafficBuffer = 0;\n            data.m_noiseBuffer = 0;\n            if (trafficDensity > (int)data.m_trafficDensity) {\n\t\t\t\tdata.m_trafficDensity = (byte)Mathf.Min((int)(data.m_trafficDensity + 5), trafficDensity);\n\t\t\t} else if (trafficDensity < (int)data.m_trafficDensity) {\n\t\t\t\tdata.m_trafficDensity = (byte)Mathf.Max((int)(data.m_trafficDensity - 5), trafficDensity);\n\t\t\t}\n            if (noiseDensity > (int)data.m_noiseDensity){\n                data.m_noiseDensity = (byte)Mathf.Min((int)(data.m_noiseDensity + 2), noiseDensity);\n            } else if (noiseDensity < (int)data.m_noiseDensity){\n                data.m_noiseDensity = (byte)Mathf.Max((int)(data.m_noiseDensity - 2), noiseDensity);\n            }\n            //Vector3 startNodePos = netManager.m_nodes.m_buffer[(int)data.m_startNode].m_position;\n            //Vector3 endNodePos = netManager.m_nodes.m_buffer[(int)data.m_endNode].m_position;\n            Vector3 middlePoint = (startNodePos + endNodePos) * 0.5f;\n\t\t\tbool flooded = false;\n\t\t\tif ((this.m_info.m_setVehicleFlags & Vehicle.Flags.Underground) == 0) {\n\t\t\t\tfloat waterLevelAtMiddlePoint = Singleton<TerrainManager>.instance.WaterLevel(VectorUtils.XZ(middlePoint));\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t// Rainfall compatibility\n\t\t\t\tfloat _roadwayFloodedTolerance = LoadingExtension.IsRainfallLoaded ? (float)PlayerPrefs.GetInt(\"RF_RoadwayFloodedTolerance\", 100)/100f : 1f;\n\t\t\t\tif (waterLevelAtMiddlePoint > middlePoint.y + _roadwayFloodedTolerance && waterLevelAtMiddlePoint > 0f) {\n\t\t\t\t\tflooded = true;\n\t\t\t\t\tdata.m_flags |= NetSegment.Flags.Flooded;\n\t\t\t\t\tproblem = Notification.AddProblems(problem, Notification.Problem.Flood | Notification.Problem.MajorProblem);\n\t\t\t\t\tVector3 min = data.m_bounds.min;\n\t\t\t\t\tVector3 max = data.m_bounds.max;\n\t\t\t\t\tRoadBaseAI.FloodParkedCars(min.x, min.z, max.x, max.z);\n\t\t\t\t} else {\n\t\t\t\t\tdata.m_flags &= ~NetSegment.Flags.Flooded;\n\t\t\t\t\tfloat _roadwayFloodingTolerance = LoadingExtension.IsRainfallLoaded ? (float)PlayerPrefs.GetInt(\"RF_RoadwayFloodingTolerance\", 50)/100f : 0f;\n\t\t\t\t\tif (waterLevelAtMiddlePoint > middlePoint.y + _roadwayFloodingTolerance && waterLevelAtMiddlePoint > 0f) {\n\t\t\t\t\t\tflooded = true;\n\t\t\t\t\t\tproblem = Notification.AddProblems(problem, Notification.Problem.Flood);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t}\n\t\t\tDistrictManager districtManager = Singleton<DistrictManager>.instance;\n\t\t\tbyte districtId = districtManager.GetDistrict(middlePoint);\n\t\t\tDistrictPolicies.CityPlanning cityPlanningPolicies = districtManager.m_districts.m_buffer[(int)districtId].m_cityPlanningPolicies;\n\t\t\tint noisePollution = (int)(100 - (data.m_noiseDensity - 100) * (data.m_noiseDensity - 100) / 100);\n\t\t\tif ((this.m_info.m_vehicleTypes & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None) {\n\t\t\t\tif ((this.m_info.m_setVehicleFlags & Vehicle.Flags.Underground) == 0) {\n\t\t\t\t\tif (flooded && (data.m_flags & (NetSegment.Flags.AccessFailed | NetSegment.Flags.Blocked)) == NetSegment.Flags.None && simManager.m_randomizer.Int32(10u) == 0) {\n\t\t\t\t\t\tTransferManager.TransferOffer offer = default(TransferManager.TransferOffer);\n\t\t\t\t\t\toffer.Priority = 4;\n\t\t\t\t\t\toffer.NetSegment = segmentID;\n\t\t\t\t\t\toffer.Position = middlePoint;\n\t\t\t\t\t\toffer.Amount = 1;\n\t\t\t\t\t\tSingleton<TransferManager>.instance.AddOutgoingOffer(TransferManager.TransferReason.FloodWater, offer);\n\t\t\t\t\t}\n\n\t\t\t\t\tint wetness = (int)data.m_wetness;\n\t\t\t\t\tif (!netManager.m_treatWetAsSnow) {\n\t\t\t\t\t\tif (flooded) {\n\t\t\t\t\t\t\twetness = 255;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tint wetnessDelta = -(wetness + 63 >> 5);\n\t\t\t\t\t\t\tfloat rainIntensity = Singleton<WeatherManager>.instance.SampleRainIntensity(middlePoint, false);\n\t\t\t\t\t\t\tif (rainIntensity != 0f) {\n\t\t\t\t\t\t\t\tint wetnessIncreaseDueToRain = Mathf.RoundToInt(Mathf.Min(rainIntensity * 4000f, 1000f));\n\t\t\t\t\t\t\t\twetnessDelta += simManager.m_randomizer.Int32(wetnessIncreaseDueToRain, wetnessIncreaseDueToRain + 99) / 100;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\twetness = Mathf.Clamp(wetness + wetnessDelta, 0, 255);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (this.m_accumulateSnow) {\n\t\t\t\t\t\tif (flooded) {\n\t\t\t\t\t\t\twetness = 128;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfloat rainIntensity = Singleton<WeatherManager>.instance.SampleRainIntensity(middlePoint, false);\n\t\t\t\t\t\t\tif (rainIntensity != 0f) {\n\t\t\t\t\t\t\t\tint minWetnessDelta = Mathf.RoundToInt(rainIntensity * 400f);\n\t\t\t\t\t\t\t\tint wetnessDelta = simManager.m_randomizer.Int32(minWetnessDelta, minWetnessDelta + 99) / 100;\n\t\t\t\t\t\t\t\tif (Singleton<UnlockManager>.instance.Unlocked(UnlockManager.Feature.Snowplow)) {\n\t\t\t\t\t\t\t\t\twetness = Mathf.Min(wetness + wetnessDelta, 255);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\twetness = Mathf.Min(wetness + wetnessDelta, 128);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (Singleton<SimulationManager>.instance.m_randomizer.Int32(4u) == 0) {\n\t\t\t\t\t\t\t\twetness = Mathf.Max(wetness - 1, 0);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (wetness >= 64 && (data.m_flags & (NetSegment.Flags.AccessFailed | NetSegment.Flags.Blocked | NetSegment.Flags.Flooded)) == NetSegment.Flags.None && simManager.m_randomizer.Int32(10u) == 0) {\n\t\t\t\t\t\t\t\tTransferManager.TransferOffer offer = default(TransferManager.TransferOffer);\n\t\t\t\t\t\t\t\toffer.Priority = wetness / 50;\n\t\t\t\t\t\t\t\toffer.NetSegment = segmentID;\n\t\t\t\t\t\t\t\toffer.Position = middlePoint;\n\t\t\t\t\t\t\t\toffer.Amount = 1;\n\t\t\t\t\t\t\t\tSingleton<TransferManager>.instance.AddOutgoingOffer(TransferManager.TransferReason.Snow, offer);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (wetness >= 192) {\n\t\t\t\t\t\t\t\tproblem = Notification.AddProblems(problem, Notification.Problem.Snow);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdistrictManager.m_districts.m_buffer[districtId].m_productionData.m_tempSnowCover += (uint)wetness;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (wetness != (int)data.m_wetness) {\n\t\t\t\t\t\tif (Mathf.Abs((int)data.m_wetness - wetness) > 10) {\n\t\t\t\t\t\t\tdata.m_wetness = (byte)wetness;\n\t\t\t\t\t\t\tInstanceID empty = InstanceID.Empty;\n\t\t\t\t\t\t\tempty.NetSegment = segmentID;\n\t\t\t\t\t\t\tnetManager.AddSmoothColor(empty);\n\t\t\t\t\t\t\tempty.NetNode = data.m_startNode;\n\t\t\t\t\t\t\tnetManager.AddSmoothColor(empty);\n\t\t\t\t\t\t\tempty.NetNode = data.m_endNode;\n\t\t\t\t\t\t\tnetManager.AddSmoothColor(empty);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdata.m_wetness = (byte)wetness;\n\t\t\t\t\t\t\tnetManager.m_wetnessChanged = 256;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tint minConditionDelta;\n\t\t\t\tif ((cityPlanningPolicies & DistrictPolicies.CityPlanning.StuddedTires) != DistrictPolicies.CityPlanning.None) {\n\t\t\t\t\tnoisePollution = noisePollution * 3 + 1 >> 1;\n\t\t\t\t\tminConditionDelta = Mathf.Min(700, (int)(50 + data.m_trafficDensity * 6));\n\t\t\t\t} else {\n\t\t\t\t\tminConditionDelta = Mathf.Min(500, (int)(50 + data.m_trafficDensity * 4));\n\t\t\t\t}\n\t\t\t\tif (!this.m_highwayRules) {\n\t\t\t\t\tint conditionDelta = simManager.m_randomizer.Int32(minConditionDelta, minConditionDelta + 99) / 100;\n\t\t\t\t\tdata.m_condition = (byte)Mathf.Max((int)data.m_condition - conditionDelta, 0);\n\t\t\t\t\tif (data.m_condition < 192 && (data.m_flags & (NetSegment.Flags.AccessFailed | NetSegment.Flags.Blocked | NetSegment.Flags.Flooded)) == NetSegment.Flags.None && simManager.m_randomizer.Int32(20u) == 0) {\n\t\t\t\t\t\tTransferManager.TransferOffer offer = default(TransferManager.TransferOffer);\n\t\t\t\t\t\toffer.Priority = (int)((255 - data.m_condition) / 50);\n\t\t\t\t\t\toffer.NetSegment = segmentID;\n\t\t\t\t\t\toffer.Position = middlePoint;\n\t\t\t\t\t\toffer.Amount = 1;\n\t\t\t\t\t\tSingleton<TransferManager>.instance.AddIncomingOffer(TransferManager.TransferReason.RoadMaintenance, offer);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!this.m_highwayRules) {\n\t\t\t\tif ((cityPlanningPolicies & DistrictPolicies.CityPlanning.HeavyTrafficBan) != DistrictPolicies.CityPlanning.None) {\n\t\t\t\t\tdata.m_flags |= NetSegment.Flags.HeavyBan;\n\t\t\t\t} else {\n\t\t\t\t\tdata.m_flags &= ~NetSegment.Flags.HeavyBan;\n\t\t\t\t}\n\t\t\t\tif ((cityPlanningPolicies & DistrictPolicies.CityPlanning.BikeBan) != DistrictPolicies.CityPlanning.None) {\n\t\t\t\t\tdata.m_flags |= NetSegment.Flags.BikeBan;\n\t\t\t\t} else {\n\t\t\t\t\tdata.m_flags &= ~NetSegment.Flags.BikeBan;\n\t\t\t\t}\n\t\t\t\tif ((cityPlanningPolicies & DistrictPolicies.CityPlanning.OldTown) != DistrictPolicies.CityPlanning.None) {\n\t\t\t\t\tdata.m_flags |= NetSegment.Flags.CarBan;\n\t\t\t\t} else {\n\t\t\t\t\tdata.m_flags &= ~NetSegment.Flags.CarBan;\n\t\t\t\t}\n\t\t\t}\n\t\t\tint noisePollutionResource = this.m_noiseAccumulation * noisePollution / 100;\n\t\t\tif (noisePollutionResource != 0) {\n\t\t\t\tfloat distance = Vector3.Distance(startNodePos, endNodePos);\n\t\t\t\tint relNoiseRadius = Mathf.FloorToInt(distance / this.m_noiseRadius);\n\t\t\t\tfor (int i = 0; i < relNoiseRadius; i++) {\n\t\t\t\t\tVector3 position3 = Vector3.Lerp(startNodePos, endNodePos, (float)(i + 1) / (float)(relNoiseRadius + 1));\n\t\t\t\t\tSingleton<ImmaterialResourceManager>.instance.AddResource(ImmaterialResourceManager.Resource.NoisePollution, noisePollutionResource, position3, this.m_noiseRadius);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (data.m_trafficDensity >= 50 && data.m_averageLength < 25f && (netManager.m_nodes.m_buffer[(int)data.m_startNode].m_flags & (NetNode.Flags.LevelCrossing | NetNode.Flags.TrafficLights)) == NetNode.Flags.TrafficLights && (netManager.m_nodes.m_buffer[(int)data.m_endNode].m_flags & (NetNode.Flags.LevelCrossing | NetNode.Flags.TrafficLights)) == NetNode.Flags.TrafficLights) {\n\t\t\t\tGuideController guideCtrl = Singleton<GuideManager>.instance.m_properties;\n\t\t\t\tif (guideCtrl != null) {\n\t\t\t\t\tSingleton<NetManager>.instance.m_shortRoadTraffic.Activate(guideCtrl.m_shortRoadTraffic, segmentID, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((data.m_flags & NetSegment.Flags.Collapsed) != NetSegment.Flags.None) {\n\t\t\t\tGuideController guideCtrl = Singleton<GuideManager>.instance.m_properties;\n\t\t\t\tif (guideCtrl != null) {\n\t\t\t\t\tSingleton<NetManager>.instance.m_roadDestroyed.Activate(guideCtrl.m_roadDestroyed, segmentID, false);\n\t\t\t\t\tSingleton<NetManager>.instance.m_roadDestroyed2.Activate(guideCtrl.m_roadDestroyed2, this.m_info.m_class.m_service);\n\t\t\t\t}\n\t\t\t\tif ((ulong)(simManager.m_currentFrameIndex >> 8 & 15u) == (ulong)((long)(segmentID & 15))) {\n\t\t\t\t\tint delta = Mathf.RoundToInt(data.m_averageLength);\n\t\t\t\t\tStatisticBase statisticBase = Singleton<StatisticsManager>.instance.Acquire<StatisticInt32>(StatisticType.DestroyedLength);\n\t\t\t\t\tstatisticBase.Add(delta);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdata.m_problems = problem;\n\t\t}\n#endregion\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomShipAI.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing CSUtil.Commons.Benchmark;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing UnityEngine;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\tclass CustomShipAI : ShipAI {\n\t\tpublic bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) {\n#if DEBUG\n\t\t\t//Log._Debug($\"CustomShipAI.CustomStartPathFind called for vehicle {vehicleID}\");\n#endif\n\n\t\t\t/// NON-STOCK CODE START ///\n\t\t\tExtVehicleType vehicleType = ExtVehicleType.None;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"vehicleType\")) {\n#endif\n\t\t\t\tvehicleType = vehicleData.Info.m_vehicleAI is PassengerShipAI ? ExtVehicleType.PassengerShip : ExtVehicleType.CargoVehicle;\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t/// NON-STOCK CODE END ///\n\n\t\t\tVehicleInfo info = this.m_info;\n\t\t\tPathUnit.Position startPosA;\n\t\t\tPathUnit.Position startPosB;\n\t\t\tfloat startSqrDistA;\n\t\t\tfloat startSqrDistB;\n\t\t\tPathUnit.Position endPosA;\n\t\t\tPathUnit.Position endPosB;\n\t\t\tfloat endSqrDistA;\n\t\t\tfloat endSqrDistB;\n\t\t\tif (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, info.m_vehicleType, false, false, 64f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB) &&\n\t\t\t\tCustomPathManager.FindPathPosition(endPos, ItemClass.Service.PublicTransport, NetInfo.LaneType.Vehicle, info.m_vehicleType, false, false, 64f, out endPosA, out endPosB, out endSqrDistA, out endSqrDistB)) {\n\t\t\t\tif (!startBothWays || startSqrDistA < 10f) {\n\t\t\t\t\tstartPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tif (!endBothWays || endSqrDistA < 10f) {\n\t\t\t\t\tendPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tuint path;\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = ExtCitizenInstance.ExtPathType.None;\n\t\t\t\targs.extVehicleType = vehicleType;\n\t\t\t\targs.vehicleId = vehicleID;\n\t\t\t\targs.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\t\targs.buildIndex = Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\t\targs.startPosA = startPosA;\n\t\t\t\targs.startPosB = startPosB;\n\t\t\t\targs.endPosA = endPosA;\n\t\t\t\targs.endPosB = endPosB;\n\t\t\t\targs.vehiclePosition = default(PathUnit.Position);\n\t\t\t\targs.laneTypes = NetInfo.LaneType.Vehicle;\n\t\t\t\targs.vehicleTypes = info.m_vehicleType;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = false;\n\t\t\t\targs.hasCombustionEngine = false;\n\t\t\t\targs.ignoreBlocked = false;\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.ignoreCosts = false;\n\t\t\t\targs.randomParking = false;\n\t\t\t\targs.stablePath = false;\n\t\t\t\targs.skipQueue = false;\n\n\t\t\t\tif (CustomPathManager._instance.CreatePath(out path, ref Singleton<SimulationManager>.instance.m_randomizer, args)) {\n\t\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tvehicleData.m_path = path;\n\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.WaitingPath;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomTaxiAI.cs",
    "content": "﻿using ColossalFramework;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing UnityEngine;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\tclass CustomTaxiAI : CarAI {\n\t\tpublic static ushort GetPassengerInstance(ushort vehicleID, ref Vehicle data) {\n\t\t\tCitizenManager instance = Singleton<CitizenManager>.instance;\n\t\t\tuint curUnitId = data.m_citizenUnits;\n\t\t\tint numIterations = 0;\n\t\t\twhile (curUnitId != 0u) {\n\t\t\t\tuint nextUnit = instance.m_units.m_buffer[curUnitId].m_nextUnit;\n\t\t\t\tfor (int i = 0; i < 5; i++) {\n\t\t\t\t\tuint citizenId = instance.m_units.m_buffer[curUnitId].GetCitizen(i);\n\t\t\t\t\tif (citizenId != 0u) {\n\t\t\t\t\t\tushort citizenInstanceId = instance.m_citizens.m_buffer[citizenId].m_instance;\n\t\t\t\t\t\tif (citizenInstanceId != 0) {\n\t\t\t\t\t\t\treturn citizenInstanceId;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcurUnitId = nextUnit;\n\t\t\t\tif (++numIterations > 524288) {\n\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tpublic bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays, bool undergroundTarget) {\n#if DEBUG\n\t\t\t//Log._Debug($\"CustomTaxiAI.CustomStartPathFind called for vehicle {vehicleID}\");\n#endif\n\n\t\t\tCitizenManager instance = Singleton<CitizenManager>.instance;\n\t\t\tushort passengerInstanceId = CustomTaxiAI.GetPassengerInstance(vehicleID, ref vehicleData);\n\t\t\tif (passengerInstanceId == 0 || (instance.m_instances.m_buffer[(int)passengerInstanceId].m_flags & CitizenInstance.Flags.Character) != CitizenInstance.Flags.None) {\n\t\t\t\treturn base.StartPathFind(vehicleID, ref vehicleData, startPos, endPos, startBothWays, endBothWays, undergroundTarget);\n\t\t\t}\n\t\t\tVehicleInfo info = this.m_info;\n\t\t\tCitizenInfo info2 = instance.m_instances.m_buffer[(int)passengerInstanceId].Info;\n\t\t\tNetInfo.LaneType laneTypes = NetInfo.LaneType.Vehicle | NetInfo.LaneType.Pedestrian | NetInfo.LaneType.TransportVehicle;\n\t\t\tVehicleInfo.VehicleType vehicleTypes = this.m_info.m_vehicleType;\n\t\t\tbool allowUnderground = (vehicleData.m_flags & Vehicle.Flags.Underground) != 0;\n\t\t\tPathUnit.Position startPosA;\n\t\t\tPathUnit.Position startPosB;\n\t\t\tfloat startSqrDistA;\n\t\t\tfloat startSqrDistB;\n\t\t\tPathUnit.Position endPosA;\n\t\t\tif (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) &&\n\t\t\t\tinfo2.m_citizenAI.FindPathPosition(passengerInstanceId, ref instance.m_instances.m_buffer[(int)passengerInstanceId], endPos, laneTypes, vehicleTypes, undergroundTarget, out endPosA)) {\n\t\t\t\tif ((instance.m_instances.m_buffer[(int)passengerInstanceId].m_flags & CitizenInstance.Flags.CannotUseTransport) == CitizenInstance.Flags.None) {\n\t\t\t\t\tlaneTypes |= NetInfo.LaneType.PublicTransport;\n\n\t\t\t\t\tuint citizenId = instance.m_instances.m_buffer[passengerInstanceId].m_citizen;\n\t\t\t\t\tif (citizenId != 0u && (instance.m_citizens.m_buffer[citizenId].m_flags & Citizen.Flags.Evacuating) != Citizen.Flags.None) {\n\t\t\t\t\t\tlaneTypes |= NetInfo.LaneType.EvacuationTransport;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!startBothWays || startSqrDistA < 10f) {\n\t\t\t\t\tstartPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tPathUnit.Position endPosB = default(PathUnit.Position);\n\t\t\t\tSimulationManager simMan = Singleton<SimulationManager>.instance;\n\t\t\t\tuint path;\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = ExtCitizenInstance.ExtPathType.None;\n\t\t\t\targs.extVehicleType = ExtVehicleType.Taxi;\n\t\t\t\targs.vehicleId = vehicleID;\n\t\t\t\targs.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\t\targs.buildIndex = simMan.m_currentBuildIndex;\n\t\t\t\targs.startPosA = startPosA;\n\t\t\t\targs.startPosB = startPosB;\n\t\t\t\targs.endPosA = endPosA;\n\t\t\t\targs.endPosB = endPosB;\n\t\t\t\targs.vehiclePosition = default(PathUnit.Position);\n\t\t\t\targs.laneTypes = laneTypes;\n\t\t\t\targs.vehicleTypes = vehicleTypes;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = this.IsHeavyVehicle();\n\t\t\t\targs.hasCombustionEngine = this.CombustionEngine();\n\t\t\t\targs.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.ignoreCosts = false;\n\t\t\t\targs.randomParking = false;\n\t\t\t\targs.stablePath = false;\n\t\t\t\targs.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\n\t\t\t\tif (CustomPathManager._instance.CreatePath(out path, ref simMan.m_randomizer, args)) {\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tvehicleData.m_path = path;\n\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.WaitingPath;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomTouristAI.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Globalization;\nusing ColossalFramework.Math;\nusing CSUtil.Commons;\nusing CSUtil.Commons.Benchmark;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\n\nnamespace TrafficManager.Custom.AI {\n\tpublic class CustomTouristAI : TouristAI {\n\t\tpublic string CustomGetLocalizedStatus(ushort instanceID, ref CitizenInstance data, out InstanceID target) {\n\t\t\tbool addCustomStatus = false;\n\t\t\tString ret = GetStockLocalizedStatus(instanceID, ref data, out addCustomStatus, out target);\n\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"EnrichLocalizedCitizenStatus\")) {\n#endif\n\t\t\t\tif (Options.prohibitPocketCars && addCustomStatus) {\n\t\t\t\t\tret = AdvancedParkingManager.Instance.EnrichLocalizedCitizenStatus(ret, ref ExtCitizenInstanceManager.Instance.ExtInstances[instanceID], ref ExtCitizenManager.Instance.ExtCitizens[data.m_citizen]);\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tprivate String GetStockLocalizedStatus(ushort instanceID, ref CitizenInstance data, out bool addCustomStatus, out InstanceID target) {\n\t\t\tif ((data.m_flags & (CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating)) != CitizenInstance.Flags.None) {\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\taddCustomStatus = false;\n\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_CONFUSED\");\n\t\t\t}\n\n\t\t\tCitizenManager instance = Singleton<CitizenManager>.instance;\n\t\t\tuint citizenId = data.m_citizen;\n\t\t\tushort vehicleId = 0;\n\t\t\tif (citizenId != 0u) {\n\t\t\t\tvehicleId = instance.m_citizens.m_buffer[citizenId].m_vehicle;\n\t\t\t}\n\n\t\t\tushort targetBuilding = data.m_targetBuilding;\n\t\t\tif (targetBuilding == 0) {\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\taddCustomStatus = false;\n\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_CONFUSED\");\n\t\t\t}\n\n\t\t\tif ((data.m_flags & CitizenInstance.Flags.TargetIsNode) != 0) {\n\t\t\t\tif (vehicleId != 0) {\n\t\t\t\t\tVehicleManager vehManager = Singleton<VehicleManager>.instance;\n\t\t\t\t\tVehicleInfo info = vehManager.m_vehicles.m_buffer[vehicleId].Info;\n\t\t\t\t\tif (info.m_class.m_service == ItemClass.Service.Residential && info.m_vehicleType != VehicleInfo.VehicleType.Bicycle) {\n\t\t\t\t\t\tif (info.m_vehicleAI.GetOwnerID(vehicleId, ref vehManager.m_vehicles.m_buffer[vehicleId]).Citizen == citizenId) {\n\t\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\t\ttarget.NetNode = targetBuilding;\n\t\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\t\treturn ColossalFramework.Globalization.Locale.Get(\"CITIZEN_STATUS_DRIVINGTO\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (info.m_class.m_service == ItemClass.Service.PublicTransport || info.m_class.m_service == ItemClass.Service.Disaster) {\n\t\t\t\t\t\tushort transportLine = Singleton<NetManager>.instance.m_nodes.m_buffer[targetBuilding].m_transportLine;\n\t\t\t\t\t\tif ((data.m_flags & CitizenInstance.Flags.WaitingTaxi) != 0) {\n\t\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\t\treturn ColossalFramework.Globalization.Locale.Get(\"CITIZEN_STATUS_WAITING_TAXI\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (vehManager.m_vehicles.m_buffer[vehicleId].m_transportLine != transportLine) {\n\t\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\t\ttarget.NetNode = targetBuilding;\n\t\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\t\treturn ColossalFramework.Globalization.Locale.Get(\"CITIZEN_STATUS_TRAVELLINGTO\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ((data.m_flags & CitizenInstance.Flags.OnTour) != 0) {\n\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\ttarget.NetNode = targetBuilding;\n\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\treturn ColossalFramework.Globalization.Locale.Get(\"CITIZEN_STATUS_VISITING\");\n\t\t\t\t}\n\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\ttarget.NetNode = targetBuilding;\n\t\t\t\taddCustomStatus = true;\n\t\t\t\treturn ColossalFramework.Globalization.Locale.Get(\"CITIZEN_STATUS_GOINGTO\");\n\t\t\t}\n\n\t\t\tbool isOutsideConnection = (Singleton<BuildingManager>.instance.m_buildings.m_buffer[(int)targetBuilding].m_flags & Building.Flags.IncomingOutgoing) != Building.Flags.None;\n\t\t\tbool hangsAround = data.m_path == 0u && (data.m_flags & CitizenInstance.Flags.HangAround) != CitizenInstance.Flags.None;\n\t\t\t\n\t\t\tif (vehicleId != 0) {\n\t\t\t\tVehicleManager vehManager = Singleton<VehicleManager>.instance;\n\t\t\t\tVehicleInfo vehicleInfo = vehManager.m_vehicles.m_buffer[(int)vehicleId].Info;\n\t\t\t\tif (vehicleInfo.m_class.m_service == ItemClass.Service.Residential && vehicleInfo.m_vehicleType != VehicleInfo.VehicleType.Bicycle) {\n\t\t\t\t\tif (vehicleInfo.m_vehicleAI.GetOwnerID(vehicleId, ref vehManager.m_vehicles.m_buffer[(int)vehicleId]).Citizen == citizenId) {\n\t\t\t\t\t\tif (isOutsideConnection) {\n\t\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_DRIVINGTO_OUTSIDE\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\ttarget.Building = targetBuilding;\n\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_DRIVINGTO\");\n\t\t\t\t\t}\n\t\t\t\t} else if (vehicleInfo.m_class.m_service == ItemClass.Service.PublicTransport || vehicleInfo.m_class.m_service == ItemClass.Service.Disaster) {\n\t\t\t\t\tif (isOutsideConnection) {\n\t\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_TRAVELLINGTO_OUTSIDE\");\n\t\t\t\t\t}\n\t\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\t\ttarget.Building = targetBuilding;\n\t\t\t\t\taddCustomStatus = true;\n\t\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_TRAVELLINGTO\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isOutsideConnection) {\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\taddCustomStatus = true;\n\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_GOINGTO_OUTSIDE\");\n\t\t\t}\n\n\t\t\tif (hangsAround) {\n\t\t\t\ttarget = InstanceID.Empty;\n\t\t\t\ttarget.Building = targetBuilding;\n\t\t\t\taddCustomStatus = false;\n\t\t\t\treturn Locale.Get(\"CITIZEN_STATUS_VISITING\");\n\t\t\t}\n\n\t\t\ttarget = InstanceID.Empty;\n\t\t\ttarget.Building = targetBuilding;\n\t\t\taddCustomStatus = true;\n\t\t\treturn Locale.Get(\"CITIZEN_STATUS_GOINGTO\");\n\t\t}\n\n\t\tpublic VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance citizenData, bool forceCar, out VehicleInfo trailer) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceID) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == citizenData.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == citizenData.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == citizenData.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n#endif\n\n\t\t\ttrailer = null;\n\n\t\t\tif (citizenData.m_citizen == 0u) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n\t\t\tbool forceTaxi = false;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"forceTaxi\")) {\n#endif\n\t\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\t\tif (ExtCitizenInstanceManager.Instance.ExtInstances[instanceID].pathMode == ExtPathMode.TaxiToTarget) {\n\t\t\t\t\t\tforceTaxi = true;\n\t\t\t\t\t}\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tCitizen.Wealth wealthLevel = Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenData.m_citizen].WealthLevel;\n\t\t\tint carProb;\n\t\t\tint bikeProb;\n\t\t\tint taxiProb;\n\t\t\t// NON-STOCK CODE START\n\t\t\tif (forceTaxi) {\n\t\t\t\tcarProb = 0;\n\t\t\t\tbikeProb = 0;\n\t\t\t\ttaxiProb = 100;\n\t\t\t} else\n\t\t\t// NON-STOCK CODE END\n\t\t\tif (forceCar || (citizenData.m_flags & CitizenInstance.Flags.BorrowCar) != CitizenInstance.Flags.None) {\n\t\t\t\tcarProb = 100;\n\t\t\t\tbikeProb = 0;\n\t\t\t\ttaxiProb = 0;\n\t\t\t} else {\n\t\t\t\tcarProb = GetCarProbability();\n\t\t\t\tbikeProb = GetBikeProbability();\n\t\t\t\ttaxiProb = GetTaxiProbability();\n\t\t\t}\n\t\t\tRandomizer randomizer = new Randomizer(citizenData.m_citizen);\n\t\t\tbool useCar = randomizer.Int32(100u) < carProb;\n\t\t\tbool useBike = !useCar && randomizer.Int32(100u) < bikeProb;\n\t\t\tbool useTaxi = !useCar && !useBike && randomizer.Int32(100u) < taxiProb;\n\t\t\tbool useCamper = false;\n\t\t\tbool useElectricCar = false;\n\t\t\tif (useCar) {\n\t\t\t\tint camperProb = this.GetCamperProbability(wealthLevel);\n\t\t\t\tuseCamper = randomizer.Int32(100u) < camperProb;\n\n\t\t\t\tif (!useCamper) {\n\t\t\t\t\tint electricProb = GetElectricCarProbability(wealthLevel);\n\t\t\t\t\tuseElectricCar = randomizer.Int32(100u) < electricProb;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tItemClass.Service service = ItemClass.Service.Residential;\n\t\t\tItemClass.SubService subService = useElectricCar ? ItemClass.SubService.ResidentialLowEco : ItemClass.SubService.ResidentialLow;\n\t\t\tif (useTaxi) {\n\t\t\t\tservice = ItemClass.Service.PublicTransport;\n\t\t\t\tsubService = ItemClass.SubService.PublicTransportTaxi;\n\t\t\t}\n\t\t\t// NON-STOCK CODE START\n\t\t\tVehicleInfo carInfo = null;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"find-parked-vehicle\")) {\n#endif\n\t\t\t\tif (Options.prohibitPocketCars && useCar && !useTaxi) {\n\t\t\t\t\tushort parkedVehicleId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenData.m_citizen].m_parkedVehicle;\n\t\t\t\t\tif (parkedVehicleId != 0) {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomTouristAI.CustomGetVehicleInfo({instanceID}): Citizen instance {instanceID} owns a parked vehicle {parkedVehicleId}. Reusing vehicle info.\");\n#endif\n\t\t\t\t\t\tcarInfo = Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info;\n\t\t\t\t\t}\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\n\t\t\tif (carInfo == null && (useCar || useTaxi)) {\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t\tif (useCamper) {\n\t\t\t\t\tRandomizer randomizer2 = randomizer;\n\t\t\t\t\tcarInfo = Singleton<VehicleManager>.instance.GetRandomVehicleInfo(ref randomizer, service, subService, ItemClass.Level.Level2);\n\t\t\t\t\tif (carInfo == null || carInfo.m_vehicleAI is CarTrailerAI) {\n\t\t\t\t\t\ttrailer = carInfo;\n\t\t\t\t\t\trandomizer = randomizer2;\n\t\t\t\t\t\tcarInfo = Singleton<VehicleManager>.instance.GetRandomVehicleInfo(ref randomizer, service, subService, ItemClass.Level.Level1);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tcarInfo = Singleton<VehicleManager>.instance.GetRandomVehicleInfo(ref randomizer, service, subService, ItemClass.Level.Level1);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (useBike) {\n\t\t\t\tVehicleInfo bikeInfo = Singleton<VehicleManager>.instance.GetRandomVehicleInfo(ref randomizer, ItemClass.Service.Residential, ItemClass.SubService.ResidentialHigh, ItemClass.Level.Level2);\n\t\t\t\tif (bikeInfo != null) {\n\t\t\t\t\treturn bikeInfo;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ((useCar || useTaxi) && carInfo != null) {\n\t\t\t\treturn carInfo;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate int GetTaxiProbability() {\n\t\t\tLog.Error(\"CustomTouristAI.GetTaxiProbability called!\");\n\t\t\treturn 20;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate int GetBikeProbability() {\n\t\t\tLog.Error(\"CustomTouristAI.GetBikeProbability called!\");\n\t\t\treturn 20;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate int GetCarProbability() {\n\t\t\tLog.Error(\"CustomTouristAI.GetCarProbability called!\");\n\t\t\treturn 20;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate int GetElectricCarProbability(Citizen.Wealth wealth) {\n\t\t\tLog.Error(\"CustomTouristAI.GetElectricCarProbability called!\");\n\t\t\treturn 20;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate int GetCamperProbability(Citizen.Wealth wealth) {\n\t\t\tLog.Error(\"CustomTouristAI.GetCamperProbability called!\");\n\t\t\treturn 20;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomTrainAI.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing UnityEngine;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing System.Runtime.CompilerServices;\nusing TrafficManager.Traffic.Data;\nusing CSUtil.Commons.Benchmark;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\tpublic class CustomTrainAI : TrainAI { // TODO inherit from VehicleAI (in order to keep the correct references to `base`)\n\t\tpublic void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) {\n\t\t\t\tbyte pathFindFlags = Singleton<PathManager>.instance.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;\n\n\t\t\t\tif ((pathFindFlags & PathUnit.FLAG_READY) != 0) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.PathFindReady(vehicleId, ref vehicleData);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLog.Warning($\"TrainAI.PathFindReady({vehicleId}) for vehicle {vehicleData.Info?.m_class?.name} threw an exception: {e.ToString()}\");\n\t\t\t\t\t\tvehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t\tvehicleData.m_path = 0u;\n\t\t\t\t\t\tvehicleData.Unspawn(vehicleId);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) {\n\t\t\t\t\tvehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;\n\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\tvehicleData.m_path = 0u;\n\t\t\t\t\tvehicleData.Unspawn(vehicleId);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) {\n\t\t\t\t\tthis.TrySpawn(vehicleId, ref vehicleData);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"UpdateVehiclePosition\")) {\n#endif\n\t\t\t\tVehicleStateManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\tif (!Options.isStockLaneChangerUsed() && (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0) {\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"LogTraffic\")) {\n#endif\n\t\t\t\t\t// Advanced AI traffic measurement\n\t\t\t\t\tVehicleStateManager.Instance.LogTraffic(vehicleId);\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tbool reversed = (vehicleData.m_flags & Vehicle.Flags.Reversed) != 0;\n\t\t\tushort connectedVehicleId;\n\t\t\tif (reversed) {\n\t\t\t\tconnectedVehicleId = vehicleData.GetLastVehicle(vehicleId);\n\t\t\t} else {\n\t\t\t\tconnectedVehicleId = vehicleId;\n\t\t\t}\n\n\t\t\tVehicleManager instance = Singleton<VehicleManager>.instance;\n\t\t\tVehicleInfo info = instance.m_vehicles.m_buffer[(int)connectedVehicleId].Info;\n\t\t\tinfo.m_vehicleAI.SimulationStep(connectedVehicleId, ref instance.m_vehicles.m_buffer[(int)connectedVehicleId], vehicleId, ref vehicleData, 0);\n\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbool newReversed = (vehicleData.m_flags & Vehicle.Flags.Reversed) != 0;\n\t\t\tif (newReversed != reversed) {\n\t\t\t\treversed = newReversed;\n\t\t\t\tif (reversed) {\n\t\t\t\t\tconnectedVehicleId = vehicleData.GetLastVehicle(vehicleId);\n\t\t\t\t} else {\n\t\t\t\t\tconnectedVehicleId = vehicleId;\n\t\t\t\t}\n\t\t\t\tinfo = instance.m_vehicles.m_buffer[(int)connectedVehicleId].Info;\n\t\t\t\tinfo.m_vehicleAI.SimulationStep(connectedVehicleId, ref instance.m_vehicles.m_buffer[(int)connectedVehicleId], vehicleId, ref vehicleData, 0);\n\t\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tnewReversed = ((vehicleData.m_flags & Vehicle.Flags.Reversed) != 0);\n\t\t\t\tif (newReversed != reversed) {\n\t\t\t\t\tSingleton<VehicleManager>.instance.ReleaseVehicle(vehicleId);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (reversed) {\n\t\t\t\tconnectedVehicleId = instance.m_vehicles.m_buffer[(int)connectedVehicleId].m_leadingVehicle;\n\t\t\t\tint num2 = 0;\n\t\t\t\twhile (connectedVehicleId != 0) {\n\t\t\t\t\tinfo = instance.m_vehicles.m_buffer[(int)connectedVehicleId].Info;\n\t\t\t\t\tinfo.m_vehicleAI.SimulationStep(connectedVehicleId, ref instance.m_vehicles.m_buffer[(int)connectedVehicleId], vehicleId, ref vehicleData, 0);\n\t\t\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconnectedVehicleId = instance.m_vehicles.m_buffer[(int)connectedVehicleId].m_leadingVehicle;\n\t\t\t\t\tif (++num2 > 16384) {\n\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconnectedVehicleId = instance.m_vehicles.m_buffer[(int)connectedVehicleId].m_trailingVehicle;\n\t\t\t\tint num3 = 0;\n\t\t\t\twhile (connectedVehicleId != 0) {\n\t\t\t\t\tinfo = instance.m_vehicles.m_buffer[(int)connectedVehicleId].Info;\n\t\t\t\t\tinfo.m_vehicleAI.SimulationStep(connectedVehicleId, ref instance.m_vehicles.m_buffer[(int)connectedVehicleId], vehicleId, ref vehicleData, 0);\n\t\t\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconnectedVehicleId = instance.m_vehicles.m_buffer[(int)connectedVehicleId].m_trailingVehicle;\n\t\t\t\t\tif (++num3 > 16384) {\n\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo)) == 0) {\n\t\t\t\tSingleton<VehicleManager>.instance.ReleaseVehicle(vehicleId);\n\t\t\t} else if (vehicleData.m_blockCounter == 255) {\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tbool mayDespawn = true;\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"MayDespawn\")) {\n#endif\n\t\t\t\t\tmayDespawn = VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData);\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (mayDespawn) {\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\tSingleton<VehicleManager>.instance.ReleaseVehicle(vehicleId);\n\t\t\t\t} // NON-STOCK CODE\n\t\t\t}\n\t\t}\n\n\t\tpublic void CustomSimulationStep(ushort vehicleID, ref Vehicle vehicleData, ref Vehicle.Frame frameData, ushort leaderID, ref Vehicle leaderData, int lodPhysics) {\n\t\t\tbool reversed = (leaderData.m_flags & Vehicle.Flags.Reversed) != (Vehicle.Flags)0;\n\t\t\tushort frontVehicleId = (!reversed) ? vehicleData.m_leadingVehicle : vehicleData.m_trailingVehicle;\n\t\t\tVehicleInfo vehicleInfo;\n\t\t\tif (leaderID != vehicleID) {\n\t\t\t\tvehicleInfo = leaderData.Info;\n\t\t\t} else {\n\t\t\t\tvehicleInfo = this.m_info;\n\t\t\t}\n\t\t\tTrainAI trainAI = vehicleInfo.m_vehicleAI as TrainAI;\n\t\t\tif (frontVehicleId != 0) {\n\t\t\t\tframeData.m_position += frameData.m_velocity * 0.4f;\n\t\t\t} else {\n\t\t\t\tframeData.m_position += frameData.m_velocity * 0.5f;\n\t\t\t}\n\t\t\tframeData.m_swayPosition += frameData.m_swayVelocity * 0.5f;\n\n\t\t\tVector3 posBeforeWheelRot = frameData.m_position;\n\t\t\tVector3 posAfterWheelRot = frameData.m_position;\n\t\t\tVector3 wheelBaseRot = frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);\n\t\t\tif (reversed) {\n\t\t\t\tposBeforeWheelRot -= wheelBaseRot;\n\t\t\t\tposAfterWheelRot += wheelBaseRot;\n\t\t\t} else {\n\t\t\t\tposBeforeWheelRot += wheelBaseRot;\n\t\t\t\tposAfterWheelRot -= wheelBaseRot;\n\t\t\t}\n\n\t\t\tfloat acceleration = this.m_info.m_acceleration;\n\t\t\tfloat braking = this.m_info.m_braking;\n\t\t\tfloat curSpeed = frameData.m_velocity.magnitude;\n\n\t\t\tVector3 beforeRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posBeforeWheelRot;\n\t\t\tfloat beforeRotToTargetPos1DiffSqrMag = beforeRotToTargetPos1Diff.sqrMagnitude;\n\n\t\t\tQuaternion curInvRot = Quaternion.Inverse(frameData.m_rotation);\n\t\t\tVector3 curveTangent = curInvRot * frameData.m_velocity;\n\n\t\t\tVector3 forward = Vector3.forward;\n\t\t\tVector3 targetMotion = Vector3.zero;\n\t\t\tfloat targetSpeed = 0f;\n\t\t\tfloat motionFactor = 0.5f;\n\n\t\t\tif (frontVehicleId != 0) {\n\t\t\t\tVehicleManager vehMan = Singleton<VehicleManager>.instance;\n\t\t\t\tVehicle.Frame frontVehLastFrameData = vehMan.m_vehicles.m_buffer[(int)frontVehicleId].GetLastFrameData();\n\t\t\t\tVehicleInfo frontVehInfo = vehMan.m_vehicles.m_buffer[(int)frontVehicleId].Info;\n\n\t\t\t\tfloat attachOffset;\n\t\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags)0 != reversed) {\n\t\t\t\t\tattachOffset = this.m_info.m_attachOffsetBack - this.m_info.m_generatedInfo.m_size.z * 0.5f;\n\t\t\t\t} else {\n\t\t\t\t\tattachOffset = this.m_info.m_attachOffsetFront - this.m_info.m_generatedInfo.m_size.z * 0.5f;\n\t\t\t\t}\n\n\t\t\t\tfloat frontAttachOffset;\n\t\t\t\tif ((vehMan.m_vehicles.m_buffer[(int)frontVehicleId].m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags)0 != reversed) {\n\t\t\t\t\tfrontAttachOffset = frontVehInfo.m_attachOffsetFront - frontVehInfo.m_generatedInfo.m_size.z * 0.5f;\n\t\t\t\t} else {\n\t\t\t\t\tfrontAttachOffset = frontVehInfo.m_attachOffsetBack - frontVehInfo.m_generatedInfo.m_size.z * 0.5f;\n\t\t\t\t}\n\n\t\t\t\tVector3 posMinusAttachOffset = frameData.m_position;\n\t\t\t\tif (reversed) {\n\t\t\t\t\tposMinusAttachOffset += frameData.m_rotation * new Vector3(0f, 0f, attachOffset);\n\t\t\t\t} else {\n\t\t\t\t\tposMinusAttachOffset -= frameData.m_rotation * new Vector3(0f, 0f, attachOffset);\n\t\t\t\t}\n\n\t\t\t\tVector3 frontPosPlusAttachOffset = frontVehLastFrameData.m_position;\n\t\t\t\tif (reversed) {\n\t\t\t\t\tfrontPosPlusAttachOffset -= frontVehLastFrameData.m_rotation * new Vector3(0f, 0f, frontAttachOffset);\n\t\t\t\t} else {\n\t\t\t\t\tfrontPosPlusAttachOffset += frontVehLastFrameData.m_rotation * new Vector3(0f, 0f, frontAttachOffset);\n\t\t\t\t}\n\n\t\t\t\tVector3 frontPosMinusWheelBaseRot = frontVehLastFrameData.m_position;\n\t\t\t\twheelBaseRot = frontVehLastFrameData.m_rotation * new Vector3(0f, 0f, frontVehInfo.m_generatedInfo.m_wheelBase * 0.5f);\n\t\t\t\tif (reversed) {\n\t\t\t\t\tfrontPosMinusWheelBaseRot += wheelBaseRot;\n\t\t\t\t} else {\n\t\t\t\t\tfrontPosMinusWheelBaseRot -= wheelBaseRot;\n\t\t\t\t}\n\n\t\t\t\tif (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) {\n\t\t\t\t\tint someIndex = -1;\n\t\t\t\t\tInvokeUpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posAfterWheelRot, 0, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posAfterWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);\n\t\t\t\t\tbeforeRotToTargetPos1DiffSqrMag = 0f;\n\t\t\t\t}\n\n\t\t\t\tfloat maxAttachDist = Mathf.Max(Vector3.Distance(posMinusAttachOffset, frontPosPlusAttachOffset), 2f);\n\t\t\t\tfloat one = 1f;\n\t\t\t\tfloat maxAttachSqrDist = maxAttachDist * maxAttachDist;\n\t\t\t\tfloat oneSqr = one * one;\n\t\t\t\tint i = 0;\n\t\t\t\tif (beforeRotToTargetPos1DiffSqrMag < maxAttachSqrDist) {\n\t\t\t\t\tif (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {\n\t\t\t\t\t\tInvokeUpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, posAfterWheelRot, posBeforeWheelRot, 0, ref leaderData, ref i, 1, 2, maxAttachSqrDist, oneSqr);\n\t\t\t\t\t}\n\t\t\t\t\twhile (i < 4) {\n\t\t\t\t\t\tvehicleData.SetTargetPos(i, vehicleData.GetTargetPos(i - 1));\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t\tbeforeRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posBeforeWheelRot;\n\t\t\t\t\tbeforeRotToTargetPos1DiffSqrMag = beforeRotToTargetPos1Diff.sqrMagnitude;\n\t\t\t\t}\n\n\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\tNetManager netMan = Singleton<NetManager>.instance;\n\t\t\t\t\tbyte pathPosIndex = vehicleData.m_pathPositionIndex;\n\t\t\t\t\tbyte lastPathOffset = vehicleData.m_lastPathOffset;\n\t\t\t\t\tif (pathPosIndex == 255) {\n\t\t\t\t\t\tpathPosIndex = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tPathManager pathMan = Singleton<PathManager>.instance;\n\t\t\t\t\tPathUnit.Position curPathPos;\n\t\t\t\t\tif (pathMan.m_pathUnits.m_buffer[vehicleData.m_path].GetPosition(pathPosIndex >> 1, out curPathPos)) {\n\t\t\t\t\t\tnetMan.m_segments.m_buffer[(int)curPathPos.m_segment].AddTraffic(Mathf.RoundToInt(this.m_info.m_generatedInfo.m_size.z * 3f), this.GetNoiseLevel());\n\t\t\t\t\t\tPathUnit.Position nextPathPos; // NON-STOCK CODE\n\t\t\t\t\t\tif ((pathPosIndex & 1) == 0 || lastPathOffset == 0 || (leaderData.m_flags & Vehicle.Flags.WaitingPath) != (Vehicle.Flags)0) {\n\t\t\t\t\t\t\tuint laneId = PathManager.GetLaneID(curPathPos);\n\t\t\t\t\t\t\tif (laneId != 0u) {\n\t\t\t\t\t\t\t\tnetMan.m_lanes.m_buffer[laneId].ReserveSpace(this.m_info.m_generatedInfo.m_size.z);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (pathMan.m_pathUnits.m_buffer[vehicleData.m_path].GetNextPosition(pathPosIndex >> 1, out nextPathPos)) {\n\t\t\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\t\t\tushort transitNodeId;\n\t\t\t\t\t\t\tif (curPathPos.m_offset < 128) {\n\t\t\t\t\t\t\t\ttransitNodeId = netMan.m_segments.m_buffer[curPathPos.m_segment].m_startNode;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\ttransitNodeId = netMan.m_segments.m_buffer[curPathPos.m_segment].m_endNode;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tbool spaceReservationAllowed = true;\n#if BENCHMARK\n\t\t\t\t\t\t\tusing (var bm = new Benchmark(null, \"IsSpaceReservationAllowed\")) {\n#endif\n\t\t\t\t\t\t\t\tspaceReservationAllowed = VehicleBehaviorManager.Instance.IsSpaceReservationAllowed(transitNodeId, curPathPos, nextPathPos);\n#if BENCHMARK\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\tif (spaceReservationAllowed) {\n\t\t\t\t\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\t\t\t\t\tuint nextLaneId = PathManager.GetLaneID(nextPathPos);\n\t\t\t\t\t\t\t\tif (nextLaneId != 0u) {\n\t\t\t\t\t\t\t\t\tnetMan.m_lanes.m_buffer[nextLaneId].ReserveSpace(this.m_info.m_generatedInfo.m_size.z);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} // NON-STOCK CODE\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbeforeRotToTargetPos1Diff = curInvRot * beforeRotToTargetPos1Diff;\n\t\t\t\tfloat negTotalAttachLen = -((this.m_info.m_generatedInfo.m_wheelBase + frontVehInfo.m_generatedInfo.m_wheelBase) * 0.5f + attachOffset + frontAttachOffset);\n\t\t\t\tbool hasPath = false;\n\t\t\t\tif (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {\n\t\t\t\t\tfloat u1;\n\t\t\t\t\tfloat u2;\n\t\t\t\t\tif (Line3.Intersect(posBeforeWheelRot, vehicleData.m_targetPos1, frontPosMinusWheelBaseRot, negTotalAttachLen, out u1, out u2)) {\n\t\t\t\t\t\ttargetMotion = beforeRotToTargetPos1Diff * Mathf.Clamp(Mathf.Min(u1, u2) / 0.6f, 0f, 2f);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tLine3.DistanceSqr(posBeforeWheelRot, vehicleData.m_targetPos1, frontPosMinusWheelBaseRot, out u1);\n\t\t\t\t\t\ttargetMotion = beforeRotToTargetPos1Diff * Mathf.Clamp(u1 / 0.6f, 0f, 2f);\n\t\t\t\t\t}\n\t\t\t\t\thasPath = true;\n\t\t\t\t}\n\n\t\t\t\tif (hasPath) {\n\t\t\t\t\tif (Vector3.Dot(frontPosMinusWheelBaseRot - posBeforeWheelRot, posBeforeWheelRot - posAfterWheelRot) < 0f) {\n\t\t\t\t\t\tmotionFactor = 0f;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfloat frontPosBeforeToAfterWheelRotDist = Vector3.Distance(frontPosMinusWheelBaseRot, posBeforeWheelRot);\n\t\t\t\t\tmotionFactor = 0f;\n\t\t\t\t\ttargetMotion = curInvRot * ((frontPosMinusWheelBaseRot - posBeforeWheelRot) * (Mathf.Max(0f, frontPosBeforeToAfterWheelRotDist - negTotalAttachLen) / Mathf.Max(1f, frontPosBeforeToAfterWheelRotDist * 0.6f)));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfloat estimatedFrameDist = (curSpeed + acceleration) * (0.5f + 0.5f * (curSpeed + acceleration) / braking);\n\t\t\t\tfloat maxSpeedAdd = Mathf.Max(curSpeed + acceleration, 2f);\n\t\t\t\tfloat meanSpeedAdd = Mathf.Max((estimatedFrameDist - maxSpeedAdd) / 2f, 1f);\n\t\t\t\tfloat maxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd;\n\t\t\t\tfloat meanSpeedAddSqr = meanSpeedAdd * meanSpeedAdd;\n\t\t\t\tif (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) {\n\t\t\t\t\tint someIndex = -1;\n\t\t\t\t\tInvokeUpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posAfterWheelRot, leaderID, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posAfterWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);\n\t\t\t\t\tbeforeRotToTargetPos1DiffSqrMag = 0f;\n\t\t\t\t}\n\n\t\t\t\tint posIndex = 0;\n\t\t\t\tbool flag3 = false;\n\t\t\t\tif ((beforeRotToTargetPos1DiffSqrMag < maxSpeedAddSqr || vehicleData.m_targetPos3.w < 0.01f) && (leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0) {\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tInvokeUpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, posAfterWheelRot, posBeforeWheelRot, leaderID, ref leaderData, ref posIndex, 1, 4, maxSpeedAddSqr, meanSpeedAddSqr);\n\t\t\t\t\t}\n\t\t\t\t\tif (posIndex < 4) {\n\t\t\t\t\t\tflag3 = true;\n\t\t\t\t\t\twhile (posIndex < 4) {\n\t\t\t\t\t\t\tvehicleData.SetTargetPos(posIndex, vehicleData.GetTargetPos(posIndex - 1));\n\t\t\t\t\t\t\tposIndex++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbeforeRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posBeforeWheelRot;\n\t\t\t\t\tbeforeRotToTargetPos1DiffSqrMag = beforeRotToTargetPos1Diff.sqrMagnitude;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif ((leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0 && this.m_info.m_vehicleType != VehicleInfo.VehicleType.Monorail) {\n\t\t\t\t\tCustomForceTrafficLights(vehicleID, ref vehicleData, curSpeed > 0.1f); // NON-STOCK CODE\n\t\t\t\t}\n\n\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\tNetManager netMan = Singleton<NetManager>.instance;\n\t\t\t\t\tbyte pathPosIndex = vehicleData.m_pathPositionIndex;\n\t\t\t\t\tbyte lastPathOffset = vehicleData.m_lastPathOffset;\n\t\t\t\t\tif (pathPosIndex == 255) {\n\t\t\t\t\t\tpathPosIndex = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tPathManager pathMan = Singleton<PathManager>.instance;\n\t\t\t\t\tPathUnit.Position curPathPos;\n\t\t\t\t\tif (pathMan.m_pathUnits.m_buffer[vehicleData.m_path].GetPosition(pathPosIndex >> 1, out curPathPos)) {\n\t\t\t\t\t\tnetMan.m_segments.m_buffer[curPathPos.m_segment].AddTraffic(Mathf.RoundToInt(this.m_info.m_generatedInfo.m_size.z * 3f), this.GetNoiseLevel());\n\t\t\t\t\t\tPathUnit.Position nextPathPos;\n\t\t\t\t\t\tif ((pathPosIndex & 1) == 0 || lastPathOffset == 0 || (leaderData.m_flags & Vehicle.Flags.WaitingPath) != (Vehicle.Flags)0) {\n\t\t\t\t\t\t\tuint laneId = PathManager.GetLaneID(curPathPos);\n\t\t\t\t\t\t\tif (laneId != 0u) {\n\t\t\t\t\t\t\t\tnetMan.m_lanes.m_buffer[laneId].ReserveSpace(this.m_info.m_generatedInfo.m_size.z, vehicleID);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (pathMan.m_pathUnits.m_buffer[vehicleData.m_path].GetNextPosition(pathPosIndex >> 1, out nextPathPos)) {\n\t\t\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\t\t\tushort transitNodeId;\n\t\t\t\t\t\t\tif (curPathPos.m_offset < 128) {\n\t\t\t\t\t\t\t\ttransitNodeId = netMan.m_segments.m_buffer[curPathPos.m_segment].m_startNode;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\ttransitNodeId = netMan.m_segments.m_buffer[curPathPos.m_segment].m_endNode;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tbool spaceReservationAllowed = true;\n#if BENCHMARK\n\t\t\t\t\t\t\tusing (var bm = new Benchmark(null, \"IsSpaceReservationAllowed\")) {\n#endif\n\t\t\t\t\t\t\t\tspaceReservationAllowed = VehicleBehaviorManager.Instance.IsSpaceReservationAllowed(transitNodeId, curPathPos, nextPathPos);\n#if BENCHMARK\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\tif (spaceReservationAllowed) {\n\t\t\t\t\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\t\t\t\t\tuint nextLaneId = PathManager.GetLaneID(nextPathPos);\n\t\t\t\t\t\t\t\tif (nextLaneId != 0u) {\n\t\t\t\t\t\t\t\t\tnetMan.m_lanes.m_buffer[nextLaneId].ReserveSpace(this.m_info.m_generatedInfo.m_size.z, vehicleID);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} // NON-STOCK CODE\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfloat maxSpeed;\n\t\t\t\tif ((leaderData.m_flags & Vehicle.Flags.Stopped) != (Vehicle.Flags)0) {\n\t\t\t\t\tmaxSpeed = 0f;\n\t\t\t\t} else {\n\t\t\t\t\tmaxSpeed = Mathf.Min(vehicleData.m_targetPos1.w, GetMaxSpeed(leaderID, ref leaderData));\n\t\t\t\t}\n\n\t\t\t\tbeforeRotToTargetPos1Diff = curInvRot * beforeRotToTargetPos1Diff;\n\t\t\t\tif (reversed) {\n\t\t\t\t\tbeforeRotToTargetPos1Diff = -beforeRotToTargetPos1Diff;\n\t\t\t\t}\n\n\t\t\t\tbool blocked = false;\n\t\t\t\tfloat forwardLen = 0f;\n\t\t\t\tif (beforeRotToTargetPos1DiffSqrMag > 1f) {\n\t\t\t\t\tforward = VectorUtils.NormalizeXZ(beforeRotToTargetPos1Diff, out forwardLen);\n\t\t\t\t\tif (forwardLen > 1f) {\n\t\t\t\t\t\tVector3 fwd = beforeRotToTargetPos1Diff;\n\t\t\t\t\t\tmaxSpeedAdd = Mathf.Max(curSpeed, 2f);\n\t\t\t\t\t\tmaxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd;\n\t\t\t\t\t\tif (beforeRotToTargetPos1DiffSqrMag > maxSpeedAddSqr) {\n\t\t\t\t\t\t\tfloat num20 = maxSpeedAdd / Mathf.Sqrt(beforeRotToTargetPos1DiffSqrMag);\n\t\t\t\t\t\t\tfwd.x *= num20;\n\t\t\t\t\t\t\tfwd.y *= num20;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (fwd.z < -1f) {\n\t\t\t\t\t\t\tif (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {\n\t\t\t\t\t\t\t\tVector3 targetPos0TargetPos1Diff = vehicleData.m_targetPos1 - vehicleData.m_targetPos0;\n\t\t\t\t\t\t\t\ttargetPos0TargetPos1Diff = curInvRot * targetPos0TargetPos1Diff;\n\t\t\t\t\t\t\t\tif (reversed) {\n\t\t\t\t\t\t\t\t\ttargetPos0TargetPos1Diff = -targetPos0TargetPos1Diff;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (targetPos0TargetPos1Diff.z < -0.01f) {\n\t\t\t\t\t\t\t\t\tif (beforeRotToTargetPos1Diff.z < Mathf.Abs(beforeRotToTargetPos1Diff.x) * -10f) {\n\t\t\t\t\t\t\t\t\t\tif (curSpeed < 0.01f) {\n\t\t\t\t\t\t\t\t\t\t\tReverse(leaderID, ref leaderData);\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tfwd.z = 0f;\n\t\t\t\t\t\t\t\t\t\tbeforeRotToTargetPos1Diff = Vector3.zero;\n\t\t\t\t\t\t\t\t\t\tmaxSpeed = 0f;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tposBeforeWheelRot = posAfterWheelRot + Vector3.Normalize(vehicleData.m_targetPos1 - vehicleData.m_targetPos0) * this.m_info.m_generatedInfo.m_wheelBase;\n\t\t\t\t\t\t\t\t\t\tposIndex = -1;\n\t\t\t\t\t\t\t\t\t\tInvokeUpdatePathTargetPositions(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);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tposIndex = -1;\n\t\t\t\t\t\t\t\t\tInvokeUpdatePathTargetPositions(trainAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posAfterWheelRot, leaderID, ref leaderData, ref posIndex, 0, 0, Vector3.SqrMagnitude(posAfterWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);\n\t\t\t\t\t\t\t\t\tvehicleData.m_targetPos1 = posBeforeWheelRot;\n\t\t\t\t\t\t\t\t\tfwd.z = 0f;\n\t\t\t\t\t\t\t\t\tbeforeRotToTargetPos1Diff = Vector3.zero;\n\t\t\t\t\t\t\t\t\tmaxSpeed = 0f;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmotionFactor = 0f;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tforward = VectorUtils.NormalizeXZ(fwd, out forwardLen);\n\t\t\t\t\t\tfloat curve = Mathf.PI / 2f /* 1.57079637f*/ * (1f - forward.z); // <- constant: a bit inaccurate PI/2\n\t\t\t\t\t\tif (forwardLen > 1f) {\n\t\t\t\t\t\t\tcurve /= forwardLen;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmaxSpeed = Mathf.Min(maxSpeed, this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1000f, curve));\n\t\t\t\t\t\tfloat targetDist = forwardLen;\n\t\t\t\t\t\tmaxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos2.w, braking));\n\t\t\t\t\t\ttargetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos2 - vehicleData.m_targetPos1);\n\t\t\t\t\t\tmaxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos3.w, braking));\n\t\t\t\t\t\ttargetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos3 - vehicleData.m_targetPos2);\n\t\t\t\t\t\tmaxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, 0f, braking));\n\t\t\t\t\t\tif (maxSpeed < curSpeed) {\n\t\t\t\t\t\t\tfloat brake = Mathf.Max(acceleration, Mathf.Min(braking, curSpeed));\n\t\t\t\t\t\t\ttargetSpeed = Mathf.Max(maxSpeed, curSpeed - brake);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfloat accel = Mathf.Max(acceleration, Mathf.Min(braking, -curSpeed));\n\t\t\t\t\t\t\ttargetSpeed = Mathf.Min(maxSpeed, curSpeed + accel);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (curSpeed < 0.1f && flag3 && vehicleInfo.m_vehicleAI.ArriveAtDestination(leaderID, ref leaderData)) {\n\t\t\t\t\tleaderData.Unspawn(leaderID);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif ((leaderData.m_flags & Vehicle.Flags.Stopped) == (Vehicle.Flags)0 && maxSpeed < 0.1f) {\n\t\t\t\t\tblocked = true;\n\t\t\t\t}\n\n\t\t\t\tif (blocked) {\n\t\t\t\t\tleaderData.m_blockCounter = (byte)Mathf.Min((int)(leaderData.m_blockCounter + 1), 255);\n\t\t\t\t} else {\n\t\t\t\t\tleaderData.m_blockCounter = 0;\n\t\t\t\t}\n\n\t\t\t\tif (forwardLen > 1f) {\n\t\t\t\t\tif (reversed) {\n\t\t\t\t\t\tforward = -forward;\n\t\t\t\t\t}\n\t\t\t\t\ttargetMotion = forward * targetSpeed;\n\t\t\t\t} else {\n\t\t\t\t\tif (reversed) {\n\t\t\t\t\t\tbeforeRotToTargetPos1Diff = -beforeRotToTargetPos1Diff;\n\t\t\t\t\t}\n\t\t\t\t\tVector3 vel = Vector3.ClampMagnitude(beforeRotToTargetPos1Diff * 0.5f - curveTangent, braking);\n\t\t\t\t\ttargetMotion = curveTangent + vel;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tVector3 springs = targetMotion - curveTangent;\n\t\t\tVector3 targetAfterWheelRotMotion = frameData.m_rotation * targetMotion;\n\t\t\tVector3 posAfterWheelRotToTargetDiff = Vector3.Normalize((Vector3)vehicleData.m_targetPos0 - posAfterWheelRot) * (targetMotion.magnitude * motionFactor);\n\t\t\tposBeforeWheelRot += targetAfterWheelRotMotion;\n\t\t\tposAfterWheelRot += posAfterWheelRotToTargetDiff;\n\n\t\t\tVector3 targetPos;\n\t\t\tif (reversed) {\n\t\t\t\tframeData.m_rotation = Quaternion.LookRotation(posAfterWheelRot - posBeforeWheelRot);\n\t\t\t\ttargetPos = posBeforeWheelRot + frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);\n\t\t\t} else {\n\t\t\t\tframeData.m_rotation = Quaternion.LookRotation(posBeforeWheelRot - posAfterWheelRot);\n\t\t\t\ttargetPos = posBeforeWheelRot - frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);\n\t\t\t}\n\t\t\tframeData.m_velocity = targetPos - frameData.m_position;\n\n\t\t\tif (frontVehicleId != 0) {\n\t\t\t\tframeData.m_position += frameData.m_velocity * 0.6f;\n\t\t\t} else {\n\t\t\t\tframeData.m_position += frameData.m_velocity * 0.5f;\n\t\t\t}\n\t\t\tframeData.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;\n\t\t\tframeData.m_swayPosition += frameData.m_swayVelocity * 0.5f;\n\t\t\tframeData.m_steerAngle = 0f;\n\t\t\tframeData.m_travelDistance += targetMotion.z;\n\t\t\tframeData.m_lightIntensity.x = ((!reversed) ? 5f : 0f);\n\t\t\tframeData.m_lightIntensity.y = ((!reversed) ? 0f : 5f);\n\t\t\tframeData.m_lightIntensity.z = 0f;\n\t\t\tframeData.m_lightIntensity.w = 0f;\n\t\t\tframeData.m_underground = ((vehicleData.m_flags & Vehicle.Flags.Underground) != (Vehicle.Flags)0);\n\t\t\tframeData.m_transition = ((vehicleData.m_flags & Vehicle.Flags.Transition) != (Vehicle.Flags)0);\n\t\t\t//base.SimulationStep(vehicleID, ref vehicleData, ref frameData, leaderID, ref leaderData, lodPhysics);\n\t\t}\n\n\t\tpublic void TmCalculateSegmentPositionPathFinder(ushort vehicleID, ref Vehicle vehicleData, PathUnit.Position position, uint laneID, byte offset, out Vector3 pos, out Vector3 dir, out float maxSpeed) {\n\t\t\tNetManager instance = Singleton<NetManager>.instance;\n\t\t\tinstance.m_lanes.m_buffer[laneID].CalculatePositionAndDirection((float)offset * 0.003921569f, out pos, out dir);\n\t\t\tNetInfo info = instance.m_segments.m_buffer[(int)position.m_segment].Info;\n\t\t\tif (info.m_lanes != null && info.m_lanes.Length > (int)position.m_lane) {\n\t\t\t\tfloat laneSpeedLimit = 1;\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"GetLockFreeGameSpeedLimit\")) {\n#endif\n\t\t\t\t\tlaneSpeedLimit = 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;\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t\tmaxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, laneSpeedLimit, instance.m_lanes.m_buffer[laneID].m_curve);\n\t\t\t} else {\n\t\t\t\tmaxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1f, 0f);\n\t\t\t}\n\t\t}\n\n\t\tpublic bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) {\n#if DEBUG\n\t\t\t//Log._Debug($\"CustomTrainAI.CustomStartPathFind called for vehicle {vehicleID}\");\n#endif\n\n\t\t\t/// NON-STOCK CODE START ///\n\t\t\tExtVehicleType vehicleType = ExtVehicleType.None;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"OnStartPathFind\")) {\n#endif\n\t\t\t\tvehicleType = VehicleStateManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, null);\n\t\t\t\tif (vehicleType == ExtVehicleType.None) {\n#if DEBUG\n\t\t\t\t\tLog.Warning($\"CustomTrainAI.CustomStartPathFind: Vehicle {vehicleID} does not have a valid vehicle type!\");\n#endif\n\t\t\t\t\tvehicleType = ExtVehicleType.RailVehicle;\n\t\t\t\t} else if (vehicleType == ExtVehicleType.CargoTrain) {\n\t\t\t\t\tvehicleType = ExtVehicleType.CargoVehicle;\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t/// NON-STOCK CODE END ///\n\n\t\t\tVehicleInfo info = this.m_info;\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Spawned) == 0 && Vector3.Distance(startPos, endPos) < 100f) {\n\t\t\t\tstartPos = endPos;\n\t\t\t}\n\t\t\tbool allowUnderground;\n\t\t\tbool allowUnderground2;\n\t\t\tif (info.m_vehicleType == VehicleInfo.VehicleType.Metro) {\n\t\t\t\tallowUnderground = true;\n\t\t\t\tallowUnderground2 = true;\n\t\t\t} else {\n\t\t\t\tallowUnderground = ((vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0);\n\t\t\t\tallowUnderground2 = false;\n\t\t\t}\n\t\t\tPathUnit.Position startPosA;\n\t\t\tPathUnit.Position startPosB;\n\t\t\tfloat startSqrDistA;\n\t\t\tfloat startSqrDistB;\n\t\t\tPathUnit.Position endPosA;\n\t\t\tPathUnit.Position endPosB;\n\t\t\tfloat endSqrDistA;\n\t\t\tfloat endSqrDistB;\n\t\t\tif (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) &&\n\t\t\t\tCustomPathManager.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)) {\n\t\t\t\tif (!startBothWays || startSqrDistB > startSqrDistA * 1.2f) {\n\t\t\t\t\tstartPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tif (!endBothWays || endSqrDistB > endSqrDistA * 1.2f) {\n\t\t\t\t\tendPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tuint path;\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = ExtCitizenInstance.ExtPathType.None;\n\t\t\t\targs.extVehicleType = vehicleType;\n\t\t\t\targs.vehicleId = vehicleID;\n\t\t\t\targs.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\t\targs.buildIndex = Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\t\targs.startPosA = startPosA;\n\t\t\t\targs.startPosB = startPosB;\n\t\t\t\targs.endPosA = endPosA;\n\t\t\t\targs.endPosB = endPosB;\n\t\t\t\targs.vehiclePosition = default(PathUnit.Position);\n\t\t\t\targs.laneTypes = NetInfo.LaneType.Vehicle;\n\t\t\t\targs.vehicleTypes = info.m_vehicleType;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = false;\n\t\t\t\targs.hasCombustionEngine = false;\n\t\t\t\targs.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.ignoreCosts = false;\n\t\t\t\targs.randomParking = false;\n\t\t\t\targs.stablePath = true;\n\t\t\t\targs.skipQueue = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\t\tif (CustomPathManager._instance.CreatePath(out path, ref Singleton<SimulationManager>.instance.m_randomizer, args)) {\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tvehicleData.m_path = path;\n\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.WaitingPath;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic 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) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tushort nextSourceNodeId;\n\t\t\tif (nextOffset < nextPosition.m_offset) {\n\t\t\t\tnextSourceNodeId = netManager.m_segments.m_buffer[(int)nextPosition.m_segment].m_startNode;\n\t\t\t} else {\n\t\t\t\tnextSourceNodeId = netManager.m_segments.m_buffer[(int)nextPosition.m_segment].m_endNode;\n\t\t\t}\n\n\t\t\tushort refTargetNodeId;\n\t\t\tif (refOffset == 0) {\n\t\t\t\trefTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_startNode;\n\t\t\t} else {\n\t\t\t\trefTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_endNode;\n\t\t\t}\n\n#if DEBUG\n\t\t\tbool 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);\n\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"CustomTrainAI.CustomCheckNextLane({vehicleId}) called.\\n\" +\n\t\t\t\t\t$\"\\trefPosition.m_segment={refPosition.m_segment}, refPosition.m_offset={refPosition.m_offset}\\n\" +\n\t\t\t\t\t$\"\\tnextPosition.m_segment={nextPosition.m_segment}, nextPosition.m_offset={nextPosition.m_offset}\\n\" +\n\t\t\t\t\t$\"\\trefLaneId={refLaneId}, refOffset={refOffset}\\n\" +\n\t\t\t\t\t$\"\\tprevLaneId={nextLaneId}, prevOffset={nextOffset}\\n\" +\n\t\t\t\t\t$\"\\tnextSourceNodeId={nextSourceNodeId}\\n\" +\n\t\t\t\t\t$\"\\trefTargetNodeId={refTargetNodeId}, refTargetNodeId={refTargetNodeId}\");\n\t\t\t}\n#endif\n\n\t\t\tVehicle.Frame lastFrameData = vehicleData.GetLastFrameData();\n\t\t\tfloat sqrVelocity = lastFrameData.m_velocity.sqrMagnitude;\n\n\t\t\tVector3 lastPosPlusRot = lastFrameData.m_position;\n\t\t\tVector3 lastPosMinusRot = lastFrameData.m_position;\n\t\t\tVector3 rotationAdd = lastFrameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);\n\t\t\tlastPosPlusRot += rotationAdd;\n\t\t\tlastPosMinusRot -= rotationAdd;\n\t\t\tfloat breakingDist = 0.5f * sqrVelocity / this.m_info.m_braking;\n\t\t\tfloat distToTargetAfterRot = Vector3.Distance(lastPosPlusRot, bezier.a);\n\t\t\tfloat distToTargetBeforeRot = Vector3.Distance(lastPosMinusRot, bezier.a);\n\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"CustomTrainAI.CustomCheckNextLane({vehicleId}): lastPos={lastFrameData.m_position} lastPosMinusRot={lastPosMinusRot} lastPosPlusRot={lastPosPlusRot} rotationAdd={rotationAdd} breakingDist={breakingDist} distToTargetAfterRot={distToTargetAfterRot} distToTargetBeforeRot={distToTargetBeforeRot}\");\n#endif\n\n\t\t\tif (Mathf.Min(distToTargetAfterRot, distToTargetBeforeRot) >= breakingDist - 5f) {\n\t\t\t\t/*VehicleManager vehMan = Singleton<VehicleManager>.instance;\n\t\t\t\tushort firstVehicleId = vehicleData.GetFirstVehicle(vehicleId);\n\t\t\t\tif (VehicleBehaviorManager.Instance.MayDespawn(ref vehMan.m_vehicles.m_buffer[firstVehicleId]) || vehMan.m_vehicles.m_buffer[firstVehicleId].m_blockCounter < 100) {*/ // NON-STOCK CODE\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomTrainAI.CustomCheckNextLane({vehicleId}): Checking for free space on lane {nextLaneId}.\");\n#endif\n\n\t\t\t\t\tif (!netManager.m_lanes.m_buffer[nextLaneId].CheckSpace(1000f, vehicleId)) {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomTrainAI.CustomCheckNextLane({vehicleId}): No space available on lane {nextLaneId}. ABORT.\");\n#endif\n\t\t\t\t\t\tvehicleData.m_flags2 |= Vehicle.Flags2.Yielding;\n\t\t\t\t\t\tvehicleData.m_waitCounter = 0;\n\t\t\t\t\t\tmaxSpeed = 0f;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tVector3 bezierMiddlePoint = bezier.Position(0.5f);\n\t\t\t\t\tSegment3 segment;\n\t\t\t\t\tif (Vector3.SqrMagnitude(vehicleData.m_segment.a - bezierMiddlePoint) < Vector3.SqrMagnitude(bezier.a - bezierMiddlePoint)) {\n\t\t\t\t\t\tsegment = new Segment3(vehicleData.m_segment.a, bezierMiddlePoint);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsegment = new Segment3(bezier.a, bezierMiddlePoint);\n\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomTrainAI.CustomCheckNextLane({vehicleId}): Checking for overlap (1). segment.a={segment.a} segment.b={segment.b}\");\n#endif\n\t\t\t\t\tif (segment.LengthSqr() >= 3f) {\n\t\t\t\t\t\tsegment.a += (segment.b - segment.a).normalized * 2.5f;\n\t\t\t\t\t\tif (CustomTrainAI.CheckOverlap(vehicleId, ref vehicleData, segment, vehicleId)) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomTrainAI.CustomCheckNextLane({vehicleId}): Overlap detected (1). segment.LengthSqr()={segment.LengthSqr()} segment.a={segment.a} ABORT.\");\n#endif\n\t\t\t\t\t\t\tvehicleData.m_flags2 |= Vehicle.Flags2.Yielding;\n\t\t\t\t\t\t\tvehicleData.m_waitCounter = 0;\n\t\t\t\t\t\t\tmaxSpeed = 0f;\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tsegment = new Segment3(bezierMiddlePoint, bezier.d);\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomTrainAI.CustomCheckNextLane({vehicleId}): Checking for overlap (2). segment.a={segment.a} segment.b={segment.b}\");\n#endif\n\t\t\t\t\tif (segment.LengthSqr() >= 1f && CustomTrainAI.CheckOverlap(vehicleId, ref vehicleData, segment, vehicleId)) {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomTrainAI.CustomCheckNextLane({vehicleId}): Overlap detected (2). ABORT.\");\n#endif\n\t\t\t\t\t\tvehicleData.m_flags2 |= Vehicle.Flags2.Yielding;\n\t\t\t\t\t\tvehicleData.m_waitCounter = 0;\n\t\t\t\t\t\tmaxSpeed = 0f;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t//} // NON-STOCK CODE\n\n\t\t\t\t//if (this.m_info.m_vehicleType != VehicleInfo.VehicleType.Monorail) { // NON-STOCK CODE\n\t\t\t\tif (nextSourceNodeId == refTargetNodeId) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomTrainAI.CustomCheckNextLane({vehicleId}): Checking if vehicle is allowed to change segment.\");\n#endif\n\t\t\t\t\tfloat oldMaxSpeed = maxSpeed;\n\t\t\t\t\tbool mayChange = true;\n#if BENCHMARK\n\t\t\t\t\tusing (var bm = new Benchmark(null, \"MayChangeSegment\")) {\n#endif\n\t\t\t\t\t\tmayChange = 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, out maxSpeed);\n#if BENCHMARK\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tif (!mayChange) {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomTrainAI.CustomCheckNextLane({vehicleId}): Vehicle is NOT allowed to change segment. ABORT.\");\n#endif\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else {\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"UpdateVehiclePosition\")) {\n#endif\n\t\t\t\t\t\tVehicleStateManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData/*, lastFrameData.m_velocity.magnitude*/);\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t\t\t}\n\t\t\t\t\tmaxSpeed = oldMaxSpeed;\n\t\t\t\t}\n\t\t\t\t//} // NON-STOCK CODE\n\t\t\t}\n\t\t}\n\n\t\tprivate void CustomForceTrafficLights(ushort vehicleID, ref Vehicle vehicleData, bool reserveSpace) {\n\t\t\tuint pathUnitId = vehicleData.m_path;\n\t\t\tif (pathUnitId != 0u) {\n\t\t\t\tNetManager netMan = Singleton<NetManager>.instance;\n\t\t\t\tPathManager pathMan = Singleton<PathManager>.instance;\n\t\t\t\tbyte pathPosIndex = vehicleData.m_pathPositionIndex;\n\t\t\t\tif (pathPosIndex == 255) {\n\t\t\t\t\tpathPosIndex = 0;\n\t\t\t\t}\n\t\t\t\tpathPosIndex = (byte)(pathPosIndex >> 1);\n\t\t\t\tbool stopLoop = false; // NON-STOCK CODE\n\t\t\t\tfor (int i = 0; i < 6; i++) {\n\t\t\t\t\tPathUnit.Position position;\n\t\t\t\t\tif (!pathMan.m_pathUnits.m_buffer[pathUnitId].GetPosition(pathPosIndex, out position)) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\tushort transitNodeId;\n\t\t\t\t\tif (position.m_offset < 128) {\n\t\t\t\t\t\ttransitNodeId = netMan.m_segments.m_buffer[position.m_segment].m_startNode;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttransitNodeId = netMan.m_segments.m_buffer[position.m_segment].m_endNode;\n\t\t\t\t\t}\n\n#if BENCHMARK\n\t\t\t\t\tusing (var bm = new Benchmark(null, \"IsSpaceReservationAllowed\")) {\n#endif\n\t\t\t\t\t\tif (Options.timedLightsEnabled) {\n\t\t\t\t\t\t\t// when a TTL is active only reserve space if it shows green\n\t\t\t\t\t\t\tPathUnit.Position nextPos;\n\t\t\t\t\t\t\tif (pathMan.m_pathUnits.m_buffer[pathUnitId].GetNextPosition(pathPosIndex, out nextPos)) {\n\t\t\t\t\t\t\t\tif (!VehicleBehaviorManager.Instance.IsSpaceReservationAllowed(transitNodeId, position, nextPos)) {\n\t\t\t\t\t\t\t\t\tstopLoop = true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t\t}\n#endif\n\t\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\t\tif (reserveSpace && i >= 1 && i <= 2) {\n\t\t\t\t\t\tuint laneID = PathManager.GetLaneID(position);\n\t\t\t\t\t\tif (laneID != 0u) {\n\t\t\t\t\t\t\treserveSpace = netMan.m_lanes.m_buffer[laneID].ReserveSpace(this.m_info.m_generatedInfo.m_size.z, vehicleID);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tForceTrafficLights(transitNodeId, position); // NON-STOCK CODE\n\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\tif (stopLoop) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\t\tif ((pathPosIndex += 1) >= pathMan.m_pathUnits.m_buffer[pathUnitId].m_positionCount) {\n\t\t\t\t\t\tpathUnitId = pathMan.m_pathUnits.m_buffer[pathUnitId].m_nextPathUnit;\n\t\t\t\t\t\tpathPosIndex = 0;\n\t\t\t\t\t\tif (pathUnitId == 0u) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// slightly modified version of TrainAI.ForceTrafficLights(PathUnit.Position)\n\t\tprivate static void ForceTrafficLights(ushort transitNodeId, PathUnit.Position position) {\n\t\t\tNetManager netMan = Singleton<NetManager>.instance;\n\t\t\tif ((netMan.m_nodes.m_buffer[(int)transitNodeId].m_flags & NetNode.Flags.TrafficLights) != NetNode.Flags.None) {\n\t\t\t\tuint frame = Singleton<SimulationManager>.instance.m_currentFrameIndex;\n\t\t\t\tuint shiftedNodeId = (uint)(((int)transitNodeId << 8) / 32768);\n\t\t\t\tuint rand = frame - shiftedNodeId & 255u;\n\t\t\t\tRoadBaseAI.TrafficLightState vehicleLightState;\n\t\t\t\tRoadBaseAI.TrafficLightState pedestrianLightState;\n\t\t\t\tbool vehicles;\n\t\t\t\tbool pedestrians;\n\t\t\t\tRoadBaseAI.GetTrafficLightState(transitNodeId, ref netMan.m_segments.m_buffer[(int)position.m_segment], frame - shiftedNodeId, out vehicleLightState, out pedestrianLightState, out vehicles, out pedestrians);\n\t\t\t\tif (!vehicles && rand >= 196u) {\n\t\t\t\t\tvehicles = true;\n\t\t\t\t\tRoadBaseAI.SetTrafficLightState(transitNodeId, ref netMan.m_segments.m_buffer[(int)position.m_segment], frame - shiftedNodeId, vehicleLightState, pedestrianLightState, vehicles, pedestrians);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprotected static bool CheckOverlap(ushort vehicleID, ref Vehicle vehicleData, Segment3 segment, ushort ignoreVehicle) {\n\t\t\tLog.Error(\"CustomTrainAI.CheckOverlap (1) called.\");\n\t\t\treturn false;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprotected static ushort CheckOverlap(ushort vehicleID, ref Vehicle vehicleData, Segment3 segment, ushort ignoreVehicle, ushort otherID, ref Vehicle otherData, ref bool overlap, Vector3 min, Vector3 max) {\n\t\t\tLog.Error(\"CustomTrainAI.CheckOverlap (2) called.\");\n\t\t\treturn 0;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate static void InitializePath(ushort vehicleID, ref Vehicle vehicleData) {\n\t\t\tLog.Error(\"CustomTrainAI.InitializePath called\");\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tpublic static void InvokeUpdatePathTargetPositions(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) {\n\t\t\tLog.Error($\"CustomTrainAI.InvokeUpdatePathTargetPositions called! trainAI={trainAI}\");\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate static void Reverse(ushort leaderID, ref Vehicle leaderData) {\n\t\t\tLog.Error(\"CustomTrainAI.Reverse called\");\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate static float GetMaxSpeed(ushort leaderID, ref Vehicle leaderData) {\n\t\t\tLog.Error(\"CustomTrainAI.GetMaxSpeed called\");\n\t\t\treturn 0f;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate static float CalculateMaxSpeed(float targetDist, float targetSpeed, float maxBraking) {\n\t\t\tLog.Error(\"CustomTrainAI.CalculateMaxSpeed called\");\n\t\t\treturn 0f;\n\t\t}\n\t}\n}"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomTramBaseAI.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing UnityEngine;\nusing TrafficManager.Manager;\nusing TrafficManager.Traffic;\nusing CSUtil.Commons;\nusing System.Runtime.CompilerServices;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Traffic.Data;\nusing CSUtil.Commons.Benchmark;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\tclass CustomTramBaseAI : TramBaseAI { // TODO inherit from VehicleAI (in order to keep the correct references to `base`)\n\t\tpublic void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vector3 physicsLodRefPos) {\n\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) {\n\t\t\t\tbyte pathFindFlags = Singleton<PathManager>.instance.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;\n\n\t\t\t\tif ((pathFindFlags & PathUnit.FLAG_READY) != 0) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.PathfindSuccess(vehicleId, ref vehicleData);\n\t\t\t\t\t\tthis.PathFindReady(vehicleId, ref vehicleData);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLog.Warning($\"TramBaseAI.PathFindSuccess/PathFindReady({vehicleId}) threw an exception: {e.ToString()}\");\n\t\t\t\t\t\tvehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t\tvehicleData.m_path = 0u;\n\t\t\t\t\t\tthis.PathfindFailure(vehicleId, ref vehicleData);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) {\n\t\t\t\t\tvehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;\n\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\tvehicleData.m_path = 0u;\n\t\t\t\t\tthis.PathfindFailure(vehicleId, ref vehicleData);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.WaitingSpace) != 0) {\n\t\t\t\t\tthis.TrySpawn(vehicleId, ref vehicleData);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"UpdateVehiclePosition\")) {\n#endif\n\t\t\t\tVehicleStateManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\tif (!Options.isStockLaneChangerUsed() && (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0) {\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"LogTraffic\")) {\n#endif\n\t\t\t\t\t// Advanced AI traffic measurement\n\t\t\t\t\tVehicleStateManager.Instance.LogTraffic(vehicleId);\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tVehicleManager instance = Singleton<VehicleManager>.instance;\n\t\t\tVehicleInfo info = instance.m_vehicles.m_buffer[(int)vehicleId].Info;\n\t\t\tinfo.m_vehicleAI.SimulationStep(vehicleId, ref instance.m_vehicles.m_buffer[(int)vehicleId], vehicleId, ref vehicleData, 0);\n\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tushort trailingVehicle = instance.m_vehicles.m_buffer[(int)vehicleId].m_trailingVehicle;\n\t\t\tint num = 0;\n\t\t\twhile (trailingVehicle != 0) {\n\t\t\t\tinfo = instance.m_vehicles.m_buffer[(int)trailingVehicle].Info;\n\t\t\t\tinfo.m_vehicleAI.SimulationStep(trailingVehicle, ref instance.m_vehicles.m_buffer[(int)trailingVehicle], vehicleId, ref vehicleData, 0);\n\t\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttrailingVehicle = instance.m_vehicles.m_buffer[(int)trailingVehicle].m_trailingVehicle;\n\t\t\t\tif (++num > 16384) {\n\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.Spawned | Vehicle.Flags.WaitingPath | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo)) == 0) {\n\t\t\t\tSingleton<VehicleManager>.instance.ReleaseVehicle(vehicleId);\n\t\t\t} else if (vehicleData.m_blockCounter == 255) {\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tbool mayDespawn = true;\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"MayDespawn\")) {\n#endif\n\t\t\t\t\tmayDespawn = VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData);\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (mayDespawn) {\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\tSingleton<VehicleManager>.instance.ReleaseVehicle(vehicleId);\n\t\t\t\t} // NON-STOCK CODE\n\t\t\t}\n\t\t}\n\n\t\tprivate static void InitializePath(ushort vehicleID, ref Vehicle vehicleData) {\n\t\t\tLog.Error(\"CustomTrainAI.InitializePath called\");\n\t\t}\n\n\t\tpublic bool CustomStartPathFind(ushort vehicleID, ref Vehicle vehicleData, Vector3 startPos, Vector3 endPos, bool startBothWays, bool endBothWays) {\n#if DEBUG\n\t\t\t//Log._Debug($\"CustomTramBaseAI.CustomStartPathFind called for vehicle {vehicleID}\");\n#endif\n\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"OnStartPathFind\")) {\n#endif\n\t\t\tVehicleStateManager.Instance.OnStartPathFind(vehicleID, ref vehicleData, null);\n#if BENCHMARK\n\t\t\t}\n#endif\n\n\t\t\tVehicleInfo info = this.m_info;\n\t\t\tbool allowUnderground;\n\t\t\tbool allowUnderground2;\n\t\t\tif (info.m_vehicleType == VehicleInfo.VehicleType.Metro) {\n\t\t\t\tallowUnderground = true;\n\t\t\t\tallowUnderground2 = true;\n\t\t\t} else {\n\t\t\t\tallowUnderground = ((vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0);\n\t\t\t\tallowUnderground2 = false;\n\t\t\t}\n\t\t\tPathUnit.Position startPosA;\n\t\t\tPathUnit.Position startPosB;\n\t\t\tfloat startSqrDistA;\n\t\t\tfloat startSqrDistB;\n\t\t\tPathUnit.Position endPosA;\n\t\t\tPathUnit.Position endPosB;\n\t\t\tfloat endSqrDistA;\n\t\t\tfloat endSqrDistB;\n\t\t\tif (CustomPathManager.FindPathPosition(startPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle, info.m_vehicleType, allowUnderground, false, 32f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB) &&\n\t\t\t\tCustomPathManager.FindPathPosition(endPos, ItemClass.Service.Road, NetInfo.LaneType.Vehicle, info.m_vehicleType, allowUnderground2, false, 32f, out endPosA, out endPosB, out endSqrDistA, out endSqrDistB)) {\n\t\t\t\tif (!startBothWays || startSqrDistB > startSqrDistA * 1.2f) {\n\t\t\t\t\tstartPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tif (!endBothWays || endSqrDistB > endSqrDistA * 1.2f) {\n\t\t\t\t\tendPosB = default(PathUnit.Position);\n\t\t\t\t}\n\t\t\t\tuint path;\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = ExtCitizenInstance.ExtPathType.None;\n\t\t\t\targs.extVehicleType = ExtVehicleType.Tram;\n\t\t\t\targs.vehicleId = vehicleID;\n\t\t\t\targs.spawned = (vehicleData.m_flags & Vehicle.Flags.Spawned) != 0;\n\t\t\t\targs.buildIndex = Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\t\targs.startPosA = startPosA;\n\t\t\t\targs.startPosB = startPosB;\n\t\t\t\targs.endPosA = endPosA;\n\t\t\t\targs.endPosB = endPosB;\n\t\t\t\targs.vehiclePosition = default(PathUnit.Position);\n\t\t\t\targs.laneTypes = NetInfo.LaneType.Vehicle;\n\t\t\t\targs.vehicleTypes = info.m_vehicleType;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = false;\n\t\t\t\targs.hasCombustionEngine = false;\n\t\t\t\targs.ignoreBlocked = this.IgnoreBlocked(vehicleID, ref vehicleData);\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.ignoreCosts = false;\n\t\t\t\targs.randomParking = false;\n\t\t\t\targs.stablePath = true;\n\t\t\t\targs.skipQueue = true;\n\n\t\t\t\tif (CustomPathManager._instance.CreatePath(out path, ref Singleton<SimulationManager>.instance.m_randomizer, args)) {\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tvehicleData.m_path = path;\n\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.WaitingPath;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic 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) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tushort prevSourceNodeId;\n\t\t\tushort prevTargetNodeId;\n\t\t\tif (prevOffset < prevPosition.m_offset) {\n\t\t\t\tprevSourceNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_startNode;\n\t\t\t\tprevTargetNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_endNode;\n\t\t\t} else {\n\t\t\t\tprevSourceNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_endNode;\n\t\t\t\tprevTargetNodeId = netManager.m_segments.m_buffer[prevPosition.m_segment].m_startNode;\n\t\t\t}\n\n\t\t\tushort refTargetNodeId;\n\t\t\tif (refOffset == 0) {\n\t\t\t\trefTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_startNode;\n\t\t\t} else {\n\t\t\t\trefTargetNodeId = netManager.m_segments.m_buffer[(int)refPosition.m_segment].m_endNode;\n\t\t\t}\n\n#if DEBUG\n\t\t\tbool 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);\n\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"CustomTramBaseAI.CustomCalculateSegmentPosition({vehicleId}) called.\\n\" +\n\t\t\t\t\t$\"\\trefPosition.m_segment={refPosition.m_segment}, refPosition.m_offset={refPosition.m_offset}\\n\" +\n\t\t\t\t\t$\"\\tprevPosition.m_segment={prevPosition.m_segment}, prevPosition.m_offset={prevPosition.m_offset}\\n\" +\n\t\t\t\t\t$\"\\tnextPosition.m_segment={nextPosition.m_segment}, nextPosition.m_offset={nextPosition.m_offset}\\n\" +\n\t\t\t\t\t$\"\\trefLaneId={refLaneId}, refOffset={refOffset}\\n\" +\n\t\t\t\t\t$\"\\tprevLaneId={prevLaneId}, prevOffset={prevOffset}\\n\" +\n\t\t\t\t\t$\"\\tprevSourceNodeId={prevSourceNodeId}, prevTargetNodeId={prevTargetNodeId}\\n\" +\n\t\t\t\t\t$\"\\trefTargetNodeId={refTargetNodeId}, refTargetNodeId={refTargetNodeId}\\n\" +\n\t\t\t\t\t$\"\\tindex={index}\");\n\t\t\t}\n\n\n#endif\n\n\t\t\tVehicle.Frame lastFrameData = vehicleData.GetLastFrameData();\n\t\t\tfloat sqrVelocity = lastFrameData.m_velocity.sqrMagnitude;\n\n\t\t\tnetManager.m_lanes.m_buffer[prevLaneId].CalculatePositionAndDirection((float)prevOffset * 0.003921569f, out pos, out dir);\n\t\t\tVector3 b = netManager.m_lanes.m_buffer[refLaneId].CalculatePosition((float)refOffset * 0.003921569f);\n\t\t\tVector3 a = lastFrameData.m_position;\n\t\t\tVector3 a2 = lastFrameData.m_position;\n\t\t\tVector3 b2 = lastFrameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);\n\t\t\ta += b2;\n\t\t\ta2 -= b2;\n\t\t\tfloat crazyValue = 0.5f * sqrVelocity / this.m_info.m_braking;\n\t\t\tfloat a3 = Vector3.Distance(a, b);\n\t\t\tfloat b3 = Vector3.Distance(a2, b);\n\t\t\tif (Mathf.Min(a3, b3) >= crazyValue - 1f) {\n\t\t\t\t// dead stock code\n\t\t\t\t/*Segment3 segment;\n\t\t\t\tsegment.a = pos;\n\t\t\t\tif (prevOffset < prevPosition.m_offset) {\n\t\t\t\t\tsegment.b = pos + dir.normalized * this.m_info.m_generatedInfo.m_size.z;\n\t\t\t\t} else {\n\t\t\t\t\tsegment.b = pos - dir.normalized * this.m_info.m_generatedInfo.m_size.z;\n\t\t\t\t}*/\n\t\t\t\tif (prevSourceNodeId == refTargetNodeId) {\n#if BENCHMARK\n\t\t\t\t\tusing (var bm = new Benchmark(null, \"MayChangeSegment\")) {\n#endif\n\t\t\t\t\tif (!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, out maxSpeed)) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else {\n#if BENCHMARK\t\n\t\t\t\t\t\tusing (var bm = new Benchmark(null, \"UpdateVehiclePosition\")) {\n#endif\n\t\t\t\t\t\tVehicleStateManager.Instance.UpdateVehiclePosition(vehicleId, ref vehicleData/*, lastFrameData.m_velocity.magnitude*/);\n#if BENCHMARK\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tNetInfo info = netManager.m_segments.m_buffer[(int)prevPosition.m_segment].Info;\n\t\t\tif (info.m_lanes != null && info.m_lanes.Length > (int)prevPosition.m_lane) {\n\t\t\t\tfloat speedLimit = 1;\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"GetLockFreeGameSpeedLimit\")) {\n#endif\n\t\t\t\t\tspeedLimit = 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;\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t\tmaxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, speedLimit, netManager.m_lanes.m_buffer[prevLaneId].m_curve);\n\t\t\t} else {\n\t\t\t\tmaxSpeed = this.CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f);\n\t\t\t}\n\t\t}\n\n\t\tpublic void CustomCalculateSegmentPositionPathFinder(ushort vehicleID, ref Vehicle vehicleData, PathUnit.Position position, uint laneID, byte offset, out Vector3 pos, out Vector3 dir, out float maxSpeed) {\n\t\t\tNetManager instance = Singleton<NetManager>.instance;\n\t\t\tinstance.m_lanes.m_buffer[laneID].CalculatePositionAndDirection((float)offset * 0.003921569f, out pos, out dir);\n\t\t\tNetInfo info = instance.m_segments.m_buffer[(int)position.m_segment].Info;\n\t\t\tif (info.m_lanes != null && info.m_lanes.Length > (int)position.m_lane) {\n\t\t\t\tfloat speedLimit = 1;\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"GetLockFreeGameSpeedLimit\")) {\n#endif\n\t\t\t\t\tspeedLimit = 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;\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t\tmaxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, speedLimit, instance.m_lanes.m_buffer[laneID].m_curve);\n\t\t\t} else {\n\t\t\t\tmaxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1f, 0f);\n\t\t\t}\n\t\t}\n\n\t\tpublic void CustomSimulationStep(ushort vehicleID, ref Vehicle vehicleData, ref Vehicle.Frame frameData, ushort leaderID, ref Vehicle leaderData, int lodPhysics) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[16] && GlobalConfig.Instance.Debug.NodeId == vehicleID;\n#endif\n\n\t\t\tushort leadingVehicle = vehicleData.m_leadingVehicle;\n\t\t\tuint currentFrameIndex = Singleton<SimulationManager>.instance.m_currentFrameIndex;\n\t\t\tVehicleInfo leaderInfo;\n\t\t\tif (leaderID != vehicleID) {\n\t\t\t\tleaderInfo = leaderData.Info;\n\t\t\t} else {\n\t\t\t\tleaderInfo = this.m_info;\n\t\t\t}\n\t\t\tTramBaseAI tramBaseAI = leaderInfo.m_vehicleAI as TramBaseAI;\n\n\t\t\tif (leadingVehicle != 0) {\n\t\t\t\tframeData.m_position += frameData.m_velocity * 0.4f;\n\t\t\t} else {\n\t\t\t\tframeData.m_position += frameData.m_velocity * 0.5f;\n\t\t\t}\n\n\t\t\tframeData.m_swayPosition += frameData.m_swayVelocity * 0.5f;\n\t\t\tVector3 wheelBaseRot = frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);\n\t\t\tVector3 posAfterWheelRot = frameData.m_position + wheelBaseRot;\n\t\t\tVector3 posBeforeWheelRot = frameData.m_position - wheelBaseRot;\n\n\t\t\tfloat acceleration = this.m_info.m_acceleration;\n\t\t\tfloat braking = this.m_info.m_braking;\n\t\t\tfloat curSpeed = frameData.m_velocity.magnitude;\n\n\t\t\tVector3 afterRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posAfterWheelRot;\n\t\t\tfloat afterRotToTargetPos1DiffSqrMag = afterRotToTargetPos1Diff.sqrMagnitude;\n\n\t\t\tQuaternion curInvRot = Quaternion.Inverse(frameData.m_rotation);\n\t\t\tVector3 curveTangent = curInvRot * frameData.m_velocity;\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): ================================================\");\n\t\t\t\tLog._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}\");\n\t\t\t}\n#endif\n\n\t\t\tVector3 forward = Vector3.forward;\n\t\t\tVector3 targetMotion = Vector3.zero;\n\t\t\tfloat targetSpeed = 0f;\n\t\t\tfloat motionFactor = 0.5f;\n\t\t\tfloat turnAngle = 0f;\n\t\t\tif (leadingVehicle != 0) {\n\t\t\t\tVehicleManager vehMan = Singleton<VehicleManager>.instance;\n\t\t\t\tVehicle.Frame leadingVehLastFrameData = vehMan.m_vehicles.m_buffer[(int)leadingVehicle].GetLastFrameData();\n\t\t\t\tVehicleInfo leadingVehInfo = vehMan.m_vehicles.m_buffer[(int)leadingVehicle].Info;\n\n\t\t\t\tfloat attachOffset;\n\t\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags)0) {\n\t\t\t\t\tattachOffset = this.m_info.m_attachOffsetBack - this.m_info.m_generatedInfo.m_size.z * 0.5f;\n\t\t\t\t} else {\n\t\t\t\t\tattachOffset = this.m_info.m_attachOffsetFront - this.m_info.m_generatedInfo.m_size.z * 0.5f;\n\t\t\t\t}\n\n\t\t\t\tfloat leadingAttachOffset;\n\t\t\t\tif ((vehMan.m_vehicles.m_buffer[(int)leadingVehicle].m_flags & Vehicle.Flags.Inverted) != (Vehicle.Flags)0) {\n\t\t\t\t\tleadingAttachOffset = leadingVehInfo.m_attachOffsetFront - leadingVehInfo.m_generatedInfo.m_size.z * 0.5f;\n\t\t\t\t} else {\n\t\t\t\t\tleadingAttachOffset = leadingVehInfo.m_attachOffsetBack - leadingVehInfo.m_generatedInfo.m_size.z * 0.5f;\n\t\t\t\t}\n\n\t\t\t\tVector3 curPosMinusRotAttachOffset = frameData.m_position - frameData.m_rotation * new Vector3(0f, 0f, attachOffset);\n\t\t\t\tVector3 leadingPosPlusRotAttachOffset = leadingVehLastFrameData.m_position + leadingVehLastFrameData.m_rotation * new Vector3(0f, 0f, leadingAttachOffset);\n\n\t\t\t\twheelBaseRot = leadingVehLastFrameData.m_rotation * new Vector3(0f, 0f, leadingVehInfo.m_generatedInfo.m_wheelBase * 0.5f);\n\t\t\t\tVector3 leadingPosBeforeWheelRot = leadingVehLastFrameData.m_position - wheelBaseRot;\n\n\t\t\t\tif (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) {\n\t\t\t\t\tint someIndex = -1;\n\t\t\t\t\tInvokeUpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posBeforeWheelRot, 0, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posBeforeWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);\n\t\t\t\t\tafterRotToTargetPos1DiffSqrMag = 0f;\n\t\t\t\t}\n\n\t\t\t\tfloat attachRotDist = Mathf.Max(Vector3.Distance(curPosMinusRotAttachOffset, leadingPosPlusRotAttachOffset), 2f);\n\n\t\t\t\tfloat one = 1f;\n\t\t\t\tfloat attachRotSqrDist = attachRotDist * attachRotDist;\n\t\t\t\tfloat oneSqr = one * one;\n\t\t\t\tint i = 0;\n\t\t\t\tif (afterRotToTargetPos1DiffSqrMag < attachRotSqrDist) {\n\t\t\t\t\tif (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {\n\t\t\t\t\t\tInvokeUpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, posBeforeWheelRot, posAfterWheelRot, 0, ref leaderData, ref i, 1, 2, attachRotSqrDist, oneSqr);\n\t\t\t\t\t}\n\t\t\t\t\twhile (i < 4) {\n\t\t\t\t\t\tvehicleData.SetTargetPos(i, vehicleData.GetTargetPos(i - 1));\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t\tafterRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posAfterWheelRot;\n\t\t\t\t\tafterRotToTargetPos1DiffSqrMag = afterRotToTargetPos1Diff.sqrMagnitude;\n\t\t\t\t}\n\t\t\t\tafterRotToTargetPos1Diff = curInvRot * afterRotToTargetPos1Diff;\n\n\t\t\t\tfloat negTotalAttachLen = -((this.m_info.m_generatedInfo.m_wheelBase + leadingVehInfo.m_generatedInfo.m_wheelBase) * 0.5f + attachOffset + leadingAttachOffset);\n\t\t\t\tbool hasPath = false;\n\t\t\t\tif (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {\n\t\t\t\t\tfloat u1;\n\t\t\t\t\tfloat u2;\n\t\t\t\t\tif (Line3.Intersect(posAfterWheelRot, vehicleData.m_targetPos1, leadingPosBeforeWheelRot, negTotalAttachLen, out u1, out u2)) {\n\t\t\t\t\t\ttargetMotion = afterRotToTargetPos1Diff * Mathf.Clamp(Mathf.Min(u1, u2) / 0.6f, 0f, 2f);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tLine3.DistanceSqr(posAfterWheelRot, vehicleData.m_targetPos1, leadingPosBeforeWheelRot, out u1);\n\t\t\t\t\t\ttargetMotion = afterRotToTargetPos1Diff * Mathf.Clamp(u1 / 0.6f, 0f, 2f);\n\t\t\t\t\t}\n\t\t\t\t\thasPath = true;\n\t\t\t\t}\n\n\t\t\t\tif (hasPath) {\n\t\t\t\t\tif (Vector3.Dot(leadingPosBeforeWheelRot - posAfterWheelRot, posAfterWheelRot - posBeforeWheelRot) < 0f) {\n\t\t\t\t\t\tmotionFactor = 0f;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfloat leadingPosBeforeToAfterWheelRotDist = Vector3.Distance(leadingPosBeforeWheelRot, posAfterWheelRot);\n\t\t\t\t\tmotionFactor = 0f;\n\t\t\t\t\ttargetMotion = curInvRot * ((leadingPosBeforeWheelRot - posAfterWheelRot) * (Mathf.Max(0f, leadingPosBeforeToAfterWheelRotDist - negTotalAttachLen) / Mathf.Max(1f, leadingPosBeforeToAfterWheelRotDist * 0.6f)));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfloat 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;\n\t\t\t\tfloat maxSpeedAdd = Mathf.Max(curSpeed + acceleration, 2f);\n\t\t\t\tfloat meanSpeedAdd = Mathf.Max((estimatedFrameDist - maxSpeedAdd) / 2f, 2f);\n\t\t\t\tfloat maxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd;\n\t\t\t\tfloat meanSpeedAddSqr = meanSpeedAdd * meanSpeedAdd;\n\t\t\t\tif (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) {\n\t\t\t\t\tint someIndex = -1;\n\t\t\t\t\tInvokeUpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posBeforeWheelRot, leaderID, ref leaderData, ref someIndex, 0, 0, Vector3.SqrMagnitude(posBeforeWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);\n\t\t\t\t\tafterRotToTargetPos1DiffSqrMag = 0f;\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): dot < 0\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._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}\");\n\t\t\t\t}\n#endif\n\n\t\t\t\tint posIndex = 0;\n\t\t\t\tbool hasValidPathTargetPos = false;\n\t\t\t\tif ((afterRotToTargetPos1DiffSqrMag < maxSpeedAddSqr || vehicleData.m_targetPos3.w < 0.01f) && (leaderData.m_flags & (Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped)) == (Vehicle.Flags)0) {\n\t\t\t\t\tif (vehicleData.m_path != 0u) {\n\t\t\t\t\t\tInvokeUpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, posBeforeWheelRot, posAfterWheelRot, leaderID, ref leaderData, ref posIndex, 1, 4, maxSpeedAddSqr, meanSpeedAddSqr);\n\t\t\t\t\t}\n\t\t\t\t\tif (posIndex < 4) {\n\t\t\t\t\t\thasValidPathTargetPos = true;\n\t\t\t\t\t\twhile (posIndex < 4) {\n\t\t\t\t\t\t\tvehicleData.SetTargetPos(posIndex, vehicleData.GetTargetPos(posIndex - 1));\n\t\t\t\t\t\t\tposIndex++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tafterRotToTargetPos1Diff = (Vector3)vehicleData.m_targetPos1 - posAfterWheelRot;\n\t\t\t\t\tafterRotToTargetPos1DiffSqrMag = afterRotToTargetPos1Diff.sqrMagnitude;\n\t\t\t\t}\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): posIndex={posIndex} hasValidPathTargetPos={hasValidPathTargetPos}\");\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (leaderData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {\n\t\t\t\t\tNetManager netMan = Singleton<NetManager>.instance;\n\t\t\t\t\tbyte leaderPathPosIndex = leaderData.m_pathPositionIndex;\n\t\t\t\t\tbyte leaderLastPathOffset = leaderData.m_lastPathOffset;\n\t\t\t\t\tif (leaderPathPosIndex == 255) {\n\t\t\t\t\t\tleaderPathPosIndex = 0;\n\t\t\t\t\t}\n\t\t\t\t\tint noise;\n\t\t\t\t\tfloat leaderLen = 1f + leaderData.CalculateTotalLength(leaderID, out noise);\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): leaderPathPosIndex={leaderPathPosIndex} leaderLastPathOffset={leaderLastPathOffset} leaderPathPosIndex={leaderPathPosIndex} leaderLen={leaderLen}\");\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t// reserve space / add traffic\n\t\t\t\t\tPathManager pathMan = Singleton<PathManager>.instance;\n\t\t\t\t\tPathUnit.Position pathPos;\n\t\t\t\t\tif (pathMan.m_pathUnits.m_buffer[leaderData.m_path].GetPosition(leaderPathPosIndex >> 1, out pathPos)) {\n\t\t\t\t\t\tnetMan.m_segments.m_buffer[(int)pathPos.m_segment].AddTraffic(Mathf.RoundToInt(leaderLen * 2.5f), noise);\n\t\t\t\t\t\tbool reservedSpaceOnCurrentLane = false;\n\t\t\t\t\t\tif ((leaderPathPosIndex & 1) == 0 || leaderLastPathOffset == 0) {\n\t\t\t\t\t\t\tuint laneId = PathManager.GetLaneID(pathPos);\n\t\t\t\t\t\t\tif (laneId != 0u) {\n\t\t\t\t\t\t\t\tVector3 curPathOffsetPos = netMan.m_lanes.m_buffer[laneId].CalculatePosition((float)pathPos.m_offset * 0.003921569f);\n\t\t\t\t\t\t\t\tfloat speedAdd = 0.5f * curSpeed * curSpeed / this.m_info.m_braking;\n\t\t\t\t\t\t\t\tfloat afterWheelRotCurPathOffsetDist = Vector3.Distance(posAfterWheelRot, curPathOffsetPos);\n\t\t\t\t\t\t\t\tfloat beforeWheelRotCurPathOffsetDist = Vector3.Distance(posBeforeWheelRot, curPathOffsetPos);\n\t\t\t\t\t\t\t\tif (Mathf.Min(afterWheelRotCurPathOffsetDist, beforeWheelRotCurPathOffsetDist) >= speedAdd - 1f) {\n\t\t\t\t\t\t\t\t\tnetMan.m_lanes.m_buffer[laneId].ReserveSpace(leaderLen);\n\t\t\t\t\t\t\t\t\treservedSpaceOnCurrentLane = true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!reservedSpaceOnCurrentLane && pathMan.m_pathUnits.m_buffer[leaderData.m_path].GetNextPosition(leaderPathPosIndex >> 1, out pathPos)) {\n\t\t\t\t\t\t\tuint nextLaneId = PathManager.GetLaneID(pathPos);\n\t\t\t\t\t\t\tif (nextLaneId != 0u) {\n\t\t\t\t\t\t\t\tnetMan.m_lanes.m_buffer[nextLaneId].ReserveSpace(leaderLen);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((ulong)(currentFrameIndex >> 4 & 15u) == (ulong)((long)(leaderID & 15))) {\n\t\t\t\t\t\t// check if vehicle can proceed to next path position\n\n\t\t\t\t\t\tbool canProceeed = false;\n\t\t\t\t\t\tuint curLeaderPathId = leaderData.m_path;\n\t\t\t\t\t\tint curLeaderPathPosIndex = leaderPathPosIndex >> 1;\n\t\t\t\t\t\tint k = 0;\n\t\t\t\t\t\twhile (k < 5) {\n\t\t\t\t\t\t\tbool invalidPos;\n\t\t\t\t\t\t\tif (PathUnit.GetNextPosition(ref curLeaderPathId, ref curLeaderPathPosIndex, out pathPos, out invalidPos)) {\n\t\t\t\t\t\t\t\tuint laneId = PathManager.GetLaneID(pathPos);\n\t\t\t\t\t\t\t\tif (laneId != 0u && !netMan.m_lanes.m_buffer[laneId].CheckSpace(leaderLen)) {\n\t\t\t\t\t\t\t\t\tk++;\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (invalidPos) {\n\t\t\t\t\t\t\t\tthis.InvalidPath(vehicleID, ref vehicleData, leaderID, ref leaderData);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcanProceeed = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!canProceeed) {\n\t\t\t\t\t\t\tleaderData.m_flags |= Vehicle.Flags.Congestion;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfloat maxSpeed;\n\t\t\t\tif ((leaderData.m_flags & Vehicle.Flags.Stopped) != (Vehicle.Flags)0) {\n\t\t\t\t\tmaxSpeed = 0f;\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is stopped. maxSpeed={maxSpeed}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t} else {\n\t\t\t\t\tmaxSpeed = Mathf.Min(vehicleData.m_targetPos1.w, GetMaxSpeed(leaderID, ref leaderData));\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is not stopped. maxSpeed={maxSpeed}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): Start of second part. curSpeed={curSpeed} curInvRot={curInvRot}\");\n\t\t\t\t}\n#endif\n\n\t\t\t\tafterRotToTargetPos1Diff = curInvRot * afterRotToTargetPos1Diff;\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1Diff={afterRotToTargetPos1Diff} (old afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag})\");\n\t\t\t\t}\n#endif\n\t\t\t\tVector3 zero = Vector3.zero;\n\t\t\t\tbool blocked = false;\n\t\t\t\tfloat forwardLen = 0f;\n\t\t\t\tif (afterRotToTargetPos1DiffSqrMag > 1f) { // TODO why is this not recalculated?\n\t\t\t\t\tforward = VectorUtils.NormalizeXZ(afterRotToTargetPos1Diff, out forwardLen);\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1DiffSqrMag > 1f. forward={forward} forwardLen={forwardLen}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tif (forwardLen > 1f) {\n\t\t\t\t\t\tVector3 fwd = afterRotToTargetPos1Diff;\n\t\t\t\t\t\tmaxSpeedAdd = Mathf.Max(curSpeed, 2f);\n\t\t\t\t\t\tmaxSpeedAddSqr = maxSpeedAdd * maxSpeedAdd;\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): forwardLen > 1f. fwd={fwd} maxSpeedAdd={maxSpeedAdd} maxSpeedAddSqr={maxSpeedAddSqr}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tif (afterRotToTargetPos1DiffSqrMag > maxSpeedAddSqr) {\n\t\t\t\t\t\t\tfloat fwdLimiter = maxSpeedAdd / Mathf.Sqrt(afterRotToTargetPos1DiffSqrMag);\n\t\t\t\t\t\t\tfwd.x *= fwdLimiter;\n\t\t\t\t\t\t\tfwd.y *= fwdLimiter;\n\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1DiffSqrMag > maxSpeedAddSqr. afterRotToTargetPos1DiffSqrMag={afterRotToTargetPos1DiffSqrMag} maxSpeedAddSqr={maxSpeedAddSqr} fwdLimiter={fwdLimiter} fwd={fwd}\");\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (fwd.z < -1f) { // !!!\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): fwd.z < -1f. fwd={fwd}\");\n\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\tif (vehicleData.m_path != 0u && (leaderData.m_flags & Vehicle.Flags.WaitingPath) == (Vehicle.Flags)0) {\n\t\t\t\t\t\t\t\tVector3 targetPos0TargetPos1Diff = vehicleData.m_targetPos1 - vehicleData.m_targetPos0;\n\t\t\t\t\t\t\t\tif ((curInvRot * targetPos0TargetPos1Diff).z < -0.01f) { // !!!\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): (curInvRot * targetPos0TargetPos1Diff).z < -0.01f. curInvRot={curInvRot} targetPos0TargetPos1Diff={targetPos0TargetPos1Diff}\");\n\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\tif (afterRotToTargetPos1Diff.z < Mathf.Abs(afterRotToTargetPos1Diff.x) * -10f) { // !!!\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): afterRotToTargetPos1Diff.z < Mathf.Abs(afterRotToTargetPos1Diff.x) * -10f. fwd={fwd} targetPos0TargetPos1Diff={targetPos0TargetPos1Diff} afterRotToTargetPos1Diff={afterRotToTargetPos1Diff}\");\n\t\t\t\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t\t\t\t/*fwd.z = 0f;\n\t\t\t\t\t\t\t\t\t\tafterRotToTargetPos1Diff = Vector3.zero;*/\n\t\t\t\t\t\t\t\t\t\tmaxSpeed = 0.5f; // NON-STOCK CODE\n\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): (1) set maxSpeed={maxSpeed}\");\n\t\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tposAfterWheelRot = posBeforeWheelRot + Vector3.Normalize(vehicleData.m_targetPos1 - vehicleData.m_targetPos0) * this.m_info.m_generatedInfo.m_wheelBase;\n\t\t\t\t\t\t\t\t\t\tposIndex = -1;\n\t\t\t\t\t\t\t\t\t\tInvokeUpdatePathTargetPositions(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);\n\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\t\tLog._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}\");\n\t\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tposIndex = -1;\n\t\t\t\t\t\t\t\t\tInvokeUpdatePathTargetPositions(tramBaseAI, vehicleID, ref vehicleData, vehicleData.m_targetPos0, posBeforeWheelRot, leaderID, ref leaderData, ref posIndex, 0, 0, Vector3.SqrMagnitude(posBeforeWheelRot - (Vector3)vehicleData.m_targetPos0) + 1f, 1f);\n\t\t\t\t\t\t\t\t\tvehicleData.m_targetPos1 = posAfterWheelRot;\n\t\t\t\t\t\t\t\t\tfwd.z = 0f;\n\t\t\t\t\t\t\t\t\tafterRotToTargetPos1Diff = Vector3.zero;\n\t\t\t\t\t\t\t\t\tmaxSpeed = 0f;\n\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is waiting for a path. posIndex={posIndex} vehicleData.m_targetPos1={vehicleData.m_targetPos1} fwd={fwd} afterRotToTargetPos1Diff={afterRotToTargetPos1Diff} maxSpeed={maxSpeed}\");\n\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmotionFactor = 0f;\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): Reset motion factor. motionFactor={motionFactor}\");\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tforward = VectorUtils.NormalizeXZ(fwd, out forwardLen);\n\t\t\t\t\t\tfloat curve = Mathf.PI/2f /* 1.57079637f*/ * (1f - forward.z); // <- constant: a bit inaccurate PI/2\n\t\t\t\t\t\tif (forwardLen > 1f) {\n\t\t\t\t\t\t\tcurve /= forwardLen;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfloat targetDist = forwardLen;\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): targetDist={targetDist} fwd={fwd} curve={curve} maxSpeed={maxSpeed}\");\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tif (vehicleData.m_targetPos1.w < 0.1f) {\n\t\t\t\t\t\t\tmaxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1000f, curve);\n\t\t\t\t\t\t\tmaxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos1.w, braking * 0.9f));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tmaxSpeed = Mathf.Min(maxSpeed, this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1000f, curve));\n\t\t\t\t\t\t}\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): [1] maxSpeed={maxSpeed}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tmaxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos2.w, braking * 0.9f));\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): [2] maxSpeed={maxSpeed}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\ttargetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos2 - vehicleData.m_targetPos1);\n\t\t\t\t\t\tmaxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, vehicleData.m_targetPos3.w, braking * 0.9f));\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): [3] maxSpeed={maxSpeed}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\ttargetDist += VectorUtils.LengthXZ(vehicleData.m_targetPos3 - vehicleData.m_targetPos2);\n\t\t\t\t\t\tif (vehicleData.m_targetPos3.w < 0.01f) {\n\t\t\t\t\t\t\ttargetDist = Mathf.Max(0f, targetDist + (this.m_info.m_generatedInfo.m_wheelBase - this.m_info.m_generatedInfo.m_size.z) * 0.5f);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmaxSpeed = Mathf.Min(maxSpeed, CalculateMaxSpeed(targetDist, 0f, braking * 0.9f));\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): [4] maxSpeed={maxSpeed}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tCarAI.CheckOtherVehicles(vehicleID, ref vehicleData, ref frameData, ref maxSpeed, ref blocked, ref zero, estimatedFrameDist, braking * 0.9f, lodPhysics);\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): CheckOtherVehicles finished. blocked={blocked}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tif (maxSpeed < curSpeed) {\n\t\t\t\t\t\t\tfloat brake = Mathf.Max(acceleration, Mathf.Min(braking, curSpeed));\n\t\t\t\t\t\t\ttargetSpeed = Mathf.Max(maxSpeed, curSpeed - brake);\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): maxSpeed < curSpeed. maxSpeed={maxSpeed} curSpeed={curSpeed} brake={brake} targetSpeed={targetSpeed}\");\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfloat accel = Mathf.Max(acceleration, Mathf.Min(braking, -curSpeed));\n\t\t\t\t\t\t\ttargetSpeed = Mathf.Min(maxSpeed, curSpeed + accel);\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): maxSpeed >= curSpeed. maxSpeed={maxSpeed} curSpeed={curSpeed} accel={accel} targetSpeed={targetSpeed}\");\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (curSpeed < 0.1f && hasValidPathTargetPos && leaderInfo.m_vehicleAI.ArriveAtDestination(leaderID, ref leaderData)) {\n\t\t\t\t\tleaderData.Unspawn(leaderID);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif ((leaderData.m_flags & Vehicle.Flags.Stopped) == (Vehicle.Flags)0 && maxSpeed < 0.1f) {\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"CustomTramBaseAI.SimulationStep({vehicleID}): Vehicle is not stopped but maxSpeed < 0.1. maxSpeed={maxSpeed}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tblocked = true;\n\t\t\t\t}\n\t\t\t\tif (blocked) {\n\t\t\t\t\tleaderData.m_blockCounter = (byte)Mathf.Min((int)(leaderData.m_blockCounter + 1), 255);\n\t\t\t\t} else {\n\t\t\t\t\tleaderData.m_blockCounter = 0;\n\t\t\t\t}\n\t\t\t\tif (forwardLen > 1f) {\n\t\t\t\t\tturnAngle = Mathf.Asin(forward.x) * Mathf.Sign(targetSpeed);\n\t\t\t\t\ttargetMotion = forward * targetSpeed;\n\t\t\t\t} else {\n\t\t\t\t\tVector3 vel = Vector3.ClampMagnitude(afterRotToTargetPos1Diff * 0.5f - curveTangent, braking);\n\t\t\t\t\ttargetMotion = curveTangent + vel;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbool mayBlink = (currentFrameIndex + (uint)leaderID & 16u) != 0u;\n\t\t\tVector3 springs = targetMotion - curveTangent;\n\t\t\tVector3 targetAfterWheelRotMotion = frameData.m_rotation * targetMotion;\n\t\t\tVector3 targetBeforeWheelRotMotion = Vector3.Normalize((Vector3)vehicleData.m_targetPos0 - posBeforeWheelRot) * (targetMotion.magnitude * motionFactor);\n\t\t\ttargetBeforeWheelRotMotion -= targetAfterWheelRotMotion * (Vector3.Dot(targetAfterWheelRotMotion, targetBeforeWheelRotMotion) / Mathf.Max(1f, targetAfterWheelRotMotion.sqrMagnitude));\n\t\t\tposAfterWheelRot += targetAfterWheelRotMotion;\n\t\t\tposBeforeWheelRot += targetBeforeWheelRotMotion;\n\t\t\tframeData.m_rotation = Quaternion.LookRotation(posAfterWheelRot - posBeforeWheelRot);\n\t\t\tVector3 targetPos = posAfterWheelRot - frameData.m_rotation * new Vector3(0f, 0f, this.m_info.m_generatedInfo.m_wheelBase * 0.5f);\n\t\t\tframeData.m_velocity = targetPos - frameData.m_position;\n\t\t\tif (leadingVehicle != 0) {\n\t\t\t\tframeData.m_position += frameData.m_velocity * 0.6f;\n\t\t\t} else {\n\t\t\t\tframeData.m_position += frameData.m_velocity * 0.5f;\n\t\t\t}\n\t\t\tframeData.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;\n\t\t\tframeData.m_swayPosition += frameData.m_swayVelocity * 0.5f;\n\t\t\tframeData.m_steerAngle = 0f;\n\t\t\tframeData.m_travelDistance += targetMotion.z;\n\t\t\tif (leadingVehicle != 0) {\n\t\t\t\tframeData.m_lightIntensity = Singleton<VehicleManager>.instance.m_vehicles.m_buffer[(int)leaderID].GetLastFrameData().m_lightIntensity;\n\t\t\t} else {\n\t\t\t\tframeData.m_lightIntensity.x = 5f;\n\t\t\t\tframeData.m_lightIntensity.y = ((springs.z >= -0.1f) ? 0.5f : 5f);\n\t\t\t\tframeData.m_lightIntensity.z = ((turnAngle >= -0.1f || !mayBlink) ? 0f : 5f);\n\t\t\t\tframeData.m_lightIntensity.w = ((turnAngle <= 0.1f || !mayBlink) ? 0f : 5f);\n\t\t\t}\n\t\t\tframeData.m_underground = ((vehicleData.m_flags & Vehicle.Flags.Underground) != (Vehicle.Flags)0);\n\t\t\tframeData.m_transition = ((vehicleData.m_flags & Vehicle.Flags.Transition) != (Vehicle.Flags)0);\n\t\t\t//base.SimulationStep(vehicleID, ref vehicleData, ref frameData, leaderID, ref leaderData, lodPhysics);\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tpublic static void InvokeUpdatePathTargetPositions(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) {\n\t\t\tLog.Error($\"CustomTramBaseAI.InvokeUpdatePathTargetPositions called! tramBaseAI={tramBaseAI}\");\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate static float GetMaxSpeed(ushort leaderID, ref Vehicle leaderData) {\n\t\t\tLog.Error(\"CustomTrainAI.GetMaxSpeed called\");\n\t\t\treturn 0f;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate static float CalculateMaxSpeed(float targetDist, float targetSpeed, float maxBraking) {\n\t\t\tLog.Error(\"CustomTrainAI.CalculateMaxSpeed called\");\n\t\t\treturn 0f;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomTransportLineAI.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing CSUtil.Commons.Benchmark;\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.Geometry;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing UnityEngine;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.AI {\n\tclass CustomTransportLineAI : TransportLineAI { // TODO inherit from NetAI (in order to keep the correct references to `base`)\n\t\tpublic static bool CustomStartPathFind(ushort segmentID, ref NetSegment data, ItemClass.Service netService, ItemClass.Service netService2, VehicleInfo.VehicleType vehicleType, bool skipQueue) {\n\t\t\tif (data.m_path != 0u) {\n\t\t\t\tSingleton<PathManager>.instance.ReleasePath(data.m_path);\n\t\t\t\tdata.m_path = 0u;\n\t\t\t}\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tif ((netManager.m_nodes.m_buffer[(int)data.m_startNode].m_flags & NetNode.Flags.Ambiguous) != NetNode.Flags.None) {\n\t\t\t\tfor (int i = 0; i < 8; i++) {\n\t\t\t\t\tushort segment = netManager.m_nodes.m_buffer[(int)data.m_startNode].GetSegment(i);\n\t\t\t\t\tif (segment != 0 && segment != segmentID && netManager.m_segments.m_buffer[(int)segment].m_path != 0u) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((netManager.m_nodes.m_buffer[(int)data.m_endNode].m_flags & NetNode.Flags.Ambiguous) != NetNode.Flags.None) {\n\t\t\t\tfor (int j = 0; j < 8; j++) {\n\t\t\t\t\tushort segment2 = netManager.m_nodes.m_buffer[(int)data.m_endNode].GetSegment(j);\n\t\t\t\t\tif (segment2 != 0 && segment2 != segmentID && netManager.m_segments.m_buffer[(int)segment2].m_path != 0u) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tVector3 position = netManager.m_nodes.m_buffer[(int)data.m_startNode].m_position;\n\t\t\tVector3 position2 = netManager.m_nodes.m_buffer[(int)data.m_endNode].m_position;\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[18];\n\t\t\tif (debug)\n\t\t\t\tLog._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}\");\n#endif\n\n\t\t\tPathUnit.Position startPosA;\n\t\t\tPathUnit.Position startPosB;\n\t\t\tfloat startSqrDistA;\n\t\t\tfloat startSqrDistB;\n\t\t\tif (!CustomPathManager.FindPathPosition(position, netService, netService2, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, vehicleType, true, false, 32f, out startPosA, out startPosB, out startSqrDistA, out startSqrDistB)) {\n\t\t\t\tCustomTransportLineAI.CheckSegmentProblems(segmentID, ref data);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tPathUnit.Position endPosA;\n\t\t\tPathUnit.Position endPosB;\n\t\t\tfloat endSqrDistA;\n\t\t\tfloat endSqrDistB;\n\t\t\tif (!CustomPathManager.FindPathPosition(position2, netService, netService2, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, vehicleType, true, false, 32f, out endPosA, out endPosB, out endSqrDistA, out endSqrDistB)) {\n\t\t\t\tCustomTransportLineAI.CheckSegmentProblems(segmentID, ref data);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif ((netManager.m_nodes.m_buffer[(int)data.m_startNode].m_flags & NetNode.Flags.Fixed) != NetNode.Flags.None) {\n\t\t\t\tstartPosB = default(PathUnit.Position);\n\t\t\t}\n\n\t\t\tif ((netManager.m_nodes.m_buffer[(int)data.m_endNode].m_flags & NetNode.Flags.Fixed) != NetNode.Flags.None) {\n\t\t\t\tendPosB = default(PathUnit.Position);\n\t\t\t}\n\n\t\t\tif (vehicleType != VehicleInfo.VehicleType.None) {\n\t\t\t\tstartPosA.m_offset = 128;\n\t\t\t\tstartPosB.m_offset = 128;\n\t\t\t\tendPosA.m_offset = 128;\n\t\t\t\tendPosB.m_offset = 128;\n\t\t\t} else {\n\t\t\t\tstartPosA.m_offset = (byte)Mathf.Clamp(startPosA.m_offset, 1, 254);\n\t\t\t\tstartPosB.m_offset = (byte)Mathf.Clamp(startPosB.m_offset, 1, 254);\n\t\t\t\tendPosA.m_offset = (byte)Mathf.Clamp(endPosA.m_offset, 1, 254);\n\t\t\t\tendPosB.m_offset = (byte)Mathf.Clamp(endPosB.m_offset, 1, 254);\n\t\t\t}\n\n\t\t\tbool stopLane = CustomTransportLineAI.GetStopLane(ref startPosA, vehicleType);\n\t\t\tbool stopLane2 = CustomTransportLineAI.GetStopLane(ref startPosB, vehicleType);\n\t\t\tbool stopLane3 = CustomTransportLineAI.GetStopLane(ref endPosA, vehicleType);\n\t\t\tbool stopLane4 = CustomTransportLineAI.GetStopLane(ref endPosB, vehicleType);\n\n\t\t\tif ((!stopLane && !stopLane2) || (!stopLane3 && !stopLane4)) {\n\t\t\t\tCustomTransportLineAI.CheckSegmentProblems(segmentID, ref data);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tExtVehicleType extVehicleType = ExtVehicleType.None;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"extVehicleType\")) {\n#endif\n\t\t\t\tif ((vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None)\n\t\t\t\t\textVehicleType = ExtVehicleType.Bus;\n\t\t\t\tif ((vehicleType & (VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Monorail)) != VehicleInfo.VehicleType.None)\n\t\t\t\t\textVehicleType = ExtVehicleType.PassengerTrain;\n\t\t\t\tif ((vehicleType & VehicleInfo.VehicleType.Tram) != VehicleInfo.VehicleType.None)\n\t\t\t\t\textVehicleType = ExtVehicleType.Tram;\n\t\t\t\tif ((vehicleType & VehicleInfo.VehicleType.Ship) != VehicleInfo.VehicleType.None)\n\t\t\t\t\textVehicleType = ExtVehicleType.PassengerShip;\n\t\t\t\tif ((vehicleType & VehicleInfo.VehicleType.Plane) != VehicleInfo.VehicleType.None)\n\t\t\t\t\textVehicleType = ExtVehicleType.PassengerPlane;\n\t\t\t\tif ((vehicleType & VehicleInfo.VehicleType.Ferry) != VehicleInfo.VehicleType.None)\n\t\t\t\t\textVehicleType = ExtVehicleType.Ferry;\n\t\t\t\tif ((vehicleType & VehicleInfo.VehicleType.Blimp) != VehicleInfo.VehicleType.None)\n\t\t\t\t\textVehicleType = ExtVehicleType.Blimp;\n\t\t\t\tif ((vehicleType & VehicleInfo.VehicleType.CableCar) != VehicleInfo.VehicleType.None)\n\t\t\t\t\textVehicleType = ExtVehicleType.CableCar;\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t//Log._Debug($\"Transport line. extVehicleType={extVehicleType}\");\n\t\t\tuint path;\n\t\t\t// NON-STOCK CODE START\n\t\t\tPathCreationArgs args;\n\t\t\targs.extPathType = ExtCitizenInstance.ExtPathType.None;\n\t\t\targs.extVehicleType = extVehicleType;\n\t\t\targs.vehicleId = 0;\n\t\t\targs.spawned = true;\n\t\t\targs.buildIndex = Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\targs.startPosA = startPosA;\n\t\t\targs.startPosB = startPosB;\n\t\t\targs.endPosA = endPosA;\n\t\t\targs.endPosB = endPosB;\n\t\t\targs.vehiclePosition = default(PathUnit.Position);\n\t\t\targs.vehicleTypes = vehicleType;\n\t\t\targs.isHeavyVehicle = false;\n\t\t\targs.hasCombustionEngine = false;\n\t\t\targs.ignoreBlocked = true;\n\t\t\targs.ignoreFlooded = false;\n\t\t\targs.ignoreCosts = false;\n\t\t\targs.randomParking = false;\n\t\t\targs.stablePath = true;\n\t\t\targs.skipQueue = skipQueue;\n\n\t\t\tif (vehicleType == VehicleInfo.VehicleType.None) {\n\t\t\t\targs.laneTypes = NetInfo.LaneType.Pedestrian;\n\t\t\t\targs.maxLength = 160000f;\n\t\t\t} else {\n\t\t\t\targs.laneTypes = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t}\n\n\t\t\tif (CustomPathManager._instance.CreatePath(out path, ref Singleton<SimulationManager>.instance.m_randomizer, args)) {\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t\tif (startPosA.m_segment != 0 && startPosB.m_segment != 0) {\n\t\t\t\t\tnetManager.m_nodes.m_buffer[data.m_startNode].m_flags |= NetNode.Flags.Ambiguous;\n\t\t\t\t} else {\n\t\t\t\t\tnetManager.m_nodes.m_buffer[data.m_startNode].m_flags &= ~NetNode.Flags.Ambiguous;\n\t\t\t\t}\n\t\t\t\tif (endPosA.m_segment != 0 && endPosB.m_segment != 0) {\n\t\t\t\t\tnetManager.m_nodes.m_buffer[data.m_endNode].m_flags |= NetNode.Flags.Ambiguous;\n\t\t\t\t} else {\n\t\t\t\t\tnetManager.m_nodes.m_buffer[data.m_endNode].m_flags &= ~NetNode.Flags.Ambiguous;\n\t\t\t\t}\n\t\t\t\tdata.m_path = path;\n\t\t\t\tdata.m_flags |= NetSegment.Flags.WaitingPath;\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._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}]\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tCustomTransportLineAI.CheckSegmentProblems(segmentID, ref data);\n\t\t\treturn true;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate static bool GetStopLane(ref PathUnit.Position pos, VehicleInfo.VehicleType vehicleType) {\n\t\t\tLog.Error($\"CustomTransportLineAI.GetStopLane called.\");\n\t\t\treturn false;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate static void CheckSegmentProblems(ushort segmentID, ref NetSegment data) {\n\t\t\tLog.Error($\"CustomTransportLineAI.CheckSegmentProblems called.\");\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tinternal static void CheckNodeProblems(ushort nodeID, ref NetNode data) {\n\t\t\tLog.Error($\"CustomTransportLineAI.CheckNodeProblems called.\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/CustomVehicleAI.cs",
    "content": "﻿#define QUEUEDSTATSx\n\nusing ColossalFramework;\nusing ColossalFramework.Math;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing UnityEngine;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing System.Runtime.CompilerServices;\nusing CSUtil.Commons;\nusing CSUtil.Commons.Benchmark;\n\nnamespace TrafficManager.Custom.AI {\n\tclass CustomVehicleAI : VehicleAI { // TODO inherit from PrefabAI (in order to keep the correct references to `base`)\n\t\t//private static readonly int MIN_BLOCK_COUNTER_PATH_RECALC_VALUE = 3;\n\n\t\tpublic void CustomCalculateSegmentPosition(ushort vehicleID, ref Vehicle vehicleData, PathUnit.Position nextPosition, PathUnit.Position position, uint laneID, byte offset, PathUnit.Position prevPos, uint prevLaneID, byte prevOffset, int index, out Vector3 pos, out Vector3 dir, out float maxSpeed) {\n\t\t\tCalculateSegPos(vehicleID, ref vehicleData, position, laneID, offset, out pos, out dir, out maxSpeed);\n\t\t}\n\n\t\tpublic void CustomCalculateSegmentPositionPathFinder(ushort vehicleID, ref Vehicle vehicleData, PathUnit.Position position, uint laneID, byte offset, out Vector3 pos, out Vector3 dir, out float maxSpeed) {\n\t\t\tCalculateSegPos(vehicleID, ref vehicleData, position, laneID, offset, out pos, out dir, out maxSpeed);\n\t\t}\n\n\t\tprotected virtual void CalculateSegPos(ushort vehicleID, ref Vehicle vehicleData, PathUnit.Position position, uint laneID, byte offset, out Vector3 pos, out Vector3 dir, out float maxSpeed) {\n\t\t\tNetManager instance = Singleton<NetManager>.instance;\n\t\t\tinstance.m_lanes.m_buffer[laneID].CalculatePositionAndDirection((float)offset * 0.003921569f, out pos, out dir);\n\t\t\tNetInfo info = instance.m_segments.m_buffer[(int)position.m_segment].Info;\n\t\t\tif (info.m_lanes != null && info.m_lanes.Length > (int)position.m_lane) {\n\t\t\t\tfloat laneSpeedLimit;\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"GetLockFreeGameSpeedLimit\")) {\n#endif\n\t\t\t\t\tlaneSpeedLimit = 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;\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t\tmaxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, laneSpeedLimit, instance.m_lanes.m_buffer[laneID].m_curve);\n\t\t\t} else {\n\t\t\t\tmaxSpeed = this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1f, 0f);\n\t\t\t}\n\t\t}\n\n\t\tprotected void CustomUpdatePathTargetPositions(ushort vehicleID, ref Vehicle vehicleData, Vector3 refPos, ref int targetPosIndex, int maxTargetPosIndex, float minSqrDistanceA, float minSqrDistanceB) {\n\t\t\tPathManager pathMan = Singleton<PathManager>.instance;\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tVector4 targetPos = vehicleData.m_targetPos0;\n\t\t\ttargetPos.w = 1000f;\n\t\t\tfloat minSqrDistA = minSqrDistanceA;\n\t\t\tuint pathId = vehicleData.m_path;\n\t\t\tbyte finePathPosIndex = vehicleData.m_pathPositionIndex;\n\t\t\tbyte lastPathOffset = vehicleData.m_lastPathOffset;\n\n\t\t\t// initial position\n\t\t\tif (finePathPosIndex == 255) {\n\t\t\t\tfinePathPosIndex = 0;\n\t\t\t\tif (targetPosIndex <= 0) {\n\t\t\t\t\tvehicleData.m_pathPositionIndex = 0;\n\t\t\t\t}\n\n\t\t\t\tif (!Singleton<PathManager>.instance.m_pathUnits.m_buffer[pathId].CalculatePathPositionOffset(finePathPosIndex >> 1, targetPos, out lastPathOffset)) {\n\t\t\t\t\tthis.InvalidPath(vehicleID, ref vehicleData, vehicleID, ref vehicleData);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// get current path position, check for errors\n\t\t\tPathUnit.Position currentPosition;\n\t\t\tif (!pathMan.m_pathUnits.m_buffer[pathId].GetPosition(finePathPosIndex >> 1, out currentPosition)) {\n\t\t\t\tthis.InvalidPath(vehicleID, ref vehicleData, vehicleID, ref vehicleData);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// get current segment info, check for errors\n\t\t\tNetInfo curSegmentInfo = netManager.m_segments.m_buffer[(int)currentPosition.m_segment].Info;\n\t\t\tif (curSegmentInfo.m_lanes.Length <= (int)currentPosition.m_lane) {\n\t\t\t\tthis.InvalidPath(vehicleID, ref vehicleData, vehicleID, ref vehicleData);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// main loop\n\t\t\tuint curLaneId = PathManager.GetLaneID(currentPosition);\n\t\t\tNetInfo.Lane laneInfo = curSegmentInfo.m_lanes[(int)currentPosition.m_lane];\n\t\t\tBezier3 bezier;\n\t\t\tbool firstIter = true; // NON-STOCK CODE\n\t\t\twhile (true) {\n\t\t\t\tif ((finePathPosIndex & 1) == 0) {\n\t\t\t\t\t// vehicle is not in transition\n\t\t\t\t\tif (laneInfo.m_laneType != NetInfo.LaneType.CargoVehicle) {\n\t\t\t\t\t\tbool first = true;\n\t\t\t\t\t\twhile (lastPathOffset != currentPosition.m_offset) {\n\t\t\t\t\t\t\t// catch up and update target position until we get to the current segment offset\n\t\t\t\t\t\t\tif (first) {\n\t\t\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tfloat distDiff = Mathf.Sqrt(minSqrDistA) - Vector3.Distance(targetPos, refPos);\n\t\t\t\t\t\t\t\tint pathOffsetDelta;\n\t\t\t\t\t\t\t\tif (distDiff < 0f) {\n\t\t\t\t\t\t\t\t\tpathOffsetDelta = 4;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tpathOffsetDelta = 4 + Mathf.Max(0, Mathf.CeilToInt(distDiff * 256f / (netManager.m_lanes.m_buffer[curLaneId].m_length + 1f)));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (lastPathOffset > currentPosition.m_offset) {\n\t\t\t\t\t\t\t\t\tlastPathOffset = (byte)Mathf.Max((int)lastPathOffset - pathOffsetDelta, (int)currentPosition.m_offset);\n\t\t\t\t\t\t\t\t} else if (lastPathOffset < currentPosition.m_offset) {\n\t\t\t\t\t\t\t\t\tlastPathOffset = (byte)Mathf.Min((int)lastPathOffset + pathOffsetDelta, (int)currentPosition.m_offset);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tVector3 curSegPos;\n\t\t\t\t\t\t\tVector3 curSegDir;\n\t\t\t\t\t\t\tfloat curSegOffset;\n\t\t\t\t\t\t\tthis.CalculateSegmentPosition(vehicleID, ref vehicleData, currentPosition, curLaneId, lastPathOffset, out curSegPos, out curSegDir, out curSegOffset);\n\t\t\t\t\t\t\ttargetPos.Set(curSegPos.x, curSegPos.y, curSegPos.z, Mathf.Min(targetPos.w, curSegOffset));\n\t\t\t\t\t\t\tfloat refPosSqrDist = (curSegPos - refPos).sqrMagnitude;\n\t\t\t\t\t\t\tif (refPosSqrDist >= minSqrDistA) {\n\t\t\t\t\t\t\t\tif (targetPosIndex <= 0) {\n\t\t\t\t\t\t\t\t\tvehicleData.m_lastPathOffset = lastPathOffset;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tvehicleData.SetTargetPos(targetPosIndex++, targetPos);\n\t\t\t\t\t\t\t\tminSqrDistA = minSqrDistanceB;\n\t\t\t\t\t\t\t\trefPos = targetPos;\n\t\t\t\t\t\t\t\ttargetPos.w = 1000f;\n\t\t\t\t\t\t\t\tif (targetPosIndex == maxTargetPosIndex) {\n\t\t\t\t\t\t\t\t\t// maximum target position index reached\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// set vehicle in transition\n\t\t\t\t\tfinePathPosIndex += 1;\n\t\t\t\t\tlastPathOffset = 0;\n\t\t\t\t\tif (targetPosIndex <= 0) {\n\t\t\t\t\t\tvehicleData.m_pathPositionIndex = finePathPosIndex;\n\t\t\t\t\t\tvehicleData.m_lastPathOffset = lastPathOffset;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ((vehicleData.m_flags2 & Vehicle.Flags2.EndStop) != 0) {\n\t\t\t\t\tif (targetPosIndex <= 0) {\n\t\t\t\t\t\ttargetPos.w = 0f;\n\t\t\t\t\t\tif (VectorUtils.LengthSqrXZ(vehicleData.GetLastFrameVelocity()) < 0.01f) {\n\t\t\t\t\t\t\tvehicleData.m_flags2 &= ~Vehicle.Flags2.EndStop;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttargetPos.w = 1f;\n\t\t\t\t\t}\n\t\t\t\t\twhile (targetPosIndex < maxTargetPosIndex) {\n\t\t\t\t\t\tvehicleData.SetTargetPos(targetPosIndex++, targetPos);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// vehicle is in transition now\n\n\t\t\t\t/*\n\t\t\t\t * coarse path position format: 0..11 (always equals 'fine path position' / 2 == 'fine path position' >> 1)\n\t\t\t\t * fine path position format: 0..23\n\t\t\t\t */\n\n\t\t\t\t// find next path unit (or abort if at path end)\n\t\t\t\tint nextCoarsePathPosIndex = (finePathPosIndex >> 1) + 1;\n\t\t\t\tuint nextPathId = pathId;\n\t\t\t\tif (nextCoarsePathPosIndex >= (int)pathMan.m_pathUnits.m_buffer[pathId].m_positionCount) {\n\t\t\t\t\tnextCoarsePathPosIndex = 0;\n\t\t\t\t\tnextPathId = pathMan.m_pathUnits.m_buffer[pathId].m_nextPathUnit;\n\t\t\t\t\tif (nextPathId == 0u) {\n\t\t\t\t\t\tif (targetPosIndex <= 0) {\n\t\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(vehicleData.m_path);\n\t\t\t\t\t\t\tvehicleData.m_path = 0u;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttargetPos.w = 1f;\n\t\t\t\t\t\tvehicleData.SetTargetPos(targetPosIndex++, targetPos);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// check for errors\n\t\t\t\tPathUnit.Position nextPathPos;\n\t\t\t\tif (!pathMan.m_pathUnits.m_buffer[nextPathId].GetPosition(nextCoarsePathPosIndex, out nextPathPos)) {\n\t\t\t\t\tthis.InvalidPath(vehicleID, ref vehicleData, vehicleID, ref vehicleData);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// check for errors\n\t\t\t\tNetInfo nextSegmentInfo = netManager.m_segments.m_buffer[(int)nextPathPos.m_segment].Info;\n\t\t\t\tif (nextSegmentInfo.m_lanes.Length <= (int)nextPathPos.m_lane) {\n\t\t\t\t\tthis.InvalidPath(vehicleID, ref vehicleData, vehicleID, ref vehicleData);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// find next lane (emergency vehicles / dynamic lane selection)\n\t\t\t\tint bestLaneIndex = nextPathPos.m_lane;\n\t\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Emergency2) != (Vehicle.Flags)0) {\n\t\t\t\t\tbestLaneIndex = FindBestLane(vehicleID, ref vehicleData, nextPathPos);\n\t\t\t\t} else {\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\tif (firstIter &&\n\t\t\t\t\t\tthis.m_info.m_vehicleType == VehicleInfo.VehicleType.Car &&\n\t\t\t\t\t\t!this.m_info.m_isLargeVehicle\n\t\t\t\t\t) {\n\t\t\t\t\t\tbool mayFindBestLane = false;\n#if BENCHMARK\n\t\t\t\t\t\tusing (var bm = new Benchmark(null, \"MayFindBestLane\")) {\n#endif\n\t\t\t\t\t\t\tmayFindBestLane = VehicleBehaviorManager.Instance.MayFindBestLane(vehicleID, ref vehicleData, ref VehicleStateManager.Instance.VehicleStates[vehicleID]);\n#if BENCHMARK\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tif (mayFindBestLane) {\n\t\t\t\t\t\t\tuint next2PathId = nextPathId;\n\t\t\t\t\t\t\tint next2PathPosIndex = nextCoarsePathPosIndex;\n\t\t\t\t\t\t\tbool next2Invalid;\n\t\t\t\t\t\t\tPathUnit.Position next2PathPos;\n\t\t\t\t\t\t\tNetInfo next2SegmentInfo = null;\n\t\t\t\t\t\t\tPathUnit.Position next3PathPos;\n\t\t\t\t\t\t\tNetInfo next3SegmentInfo = null;\n\t\t\t\t\t\t\tPathUnit.Position next4PathPos;\n\t\t\t\t\t\t\tif (PathUnit.GetNextPosition(ref next2PathId, ref next2PathPosIndex, out next2PathPos, out next2Invalid)) {\n\t\t\t\t\t\t\t\tnext2SegmentInfo = netManager.m_segments.m_buffer[(int)next2PathPos.m_segment].Info;\n\n\t\t\t\t\t\t\t\tuint next3PathId = next2PathId;\n\t\t\t\t\t\t\t\tint next3PathPosIndex = next2PathPosIndex;\n\t\t\t\t\t\t\t\tbool next3Invalid;\n\t\t\t\t\t\t\t\tif (PathUnit.GetNextPosition(ref next3PathId, ref next3PathPosIndex, out next3PathPos, out next3Invalid)) {\n\t\t\t\t\t\t\t\t\tnext3SegmentInfo = netManager.m_segments.m_buffer[(int)next3PathPos.m_segment].Info;\n\n\t\t\t\t\t\t\t\t\tuint next4PathId = next3PathId;\n\t\t\t\t\t\t\t\t\tint next4PathPosIndex = next3PathPosIndex;\n\t\t\t\t\t\t\t\t\tbool next4Invalid;\n\t\t\t\t\t\t\t\t\tif (!PathUnit.GetNextPosition(ref next4PathId, ref next4PathPosIndex, out next4PathPos, out next4Invalid)) {\n\t\t\t\t\t\t\t\t\t\tnext4PathPos = default(PathUnit.Position);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tnext3PathPos = default(PathUnit.Position);\n\t\t\t\t\t\t\t\t\tnext4PathPos = default(PathUnit.Position);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tnext2PathPos = default(PathUnit.Position);\n\t\t\t\t\t\t\t\tnext3PathPos = default(PathUnit.Position);\n\t\t\t\t\t\t\t\tnext4PathPos = default(PathUnit.Position);\n\t\t\t\t\t\t\t}\n\n#if BENCHMARK\n\t\t\t\t\t\t\tusing (var bm = new Benchmark(null, \"FindBestLane\")) {\n#endif\n\t\t\t\t\t\t\t\tbestLaneIndex = VehicleBehaviorManager.Instance.FindBestLane(vehicleID, ref vehicleData, ref VehicleStateManager.Instance.VehicleStates[vehicleID], curLaneId, currentPosition, curSegmentInfo, nextPathPos, nextSegmentInfo, next2PathPos, next2SegmentInfo, next3PathPos, next3SegmentInfo, next4PathPos);\n#if BENCHMARK\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// update lane index\n\t\t\t\tif (bestLaneIndex != (int)nextPathPos.m_lane) {\n\t\t\t\t\tnextPathPos.m_lane = (byte)bestLaneIndex;\n\t\t\t\t\tpathMan.m_pathUnits.m_buffer[nextPathId].SetPosition(nextCoarsePathPosIndex, nextPathPos);\n#if BENCHMARK\n\t\t\t\t\tusing (var bm = new Benchmark(null, \"AddTraffic\")) {\n#endif\n\t\t\t\t\t\t// prevent multiple lane changes to the same lane from happening at the same time\n\t\t\t\t\t\tTrafficMeasurementManager.Instance.AddTraffic(nextPathPos.m_segment, nextPathPos.m_lane\n#if MEASUREDENSITY\n\t\t\t\t\t\t\t\t, VehicleStateManager.Instance.VehicleStates[vehicleID].totalLength\n#endif\n\t\t\t\t\t\t\t\t, 0); // NON-STOCK CODE\n#if BENCHMARK\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\n\t\t\t\t// check for errors\n\t\t\t\tuint nextLaneId = PathManager.GetLaneID(nextPathPos);\n\t\t\t\tNetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[(int)nextPathPos.m_lane];\n\t\t\t\tushort curSegStartNodeId = netManager.m_segments.m_buffer[(int)currentPosition.m_segment].m_startNode;\n\t\t\t\tushort curSegEndNodeId = netManager.m_segments.m_buffer[(int)currentPosition.m_segment].m_endNode;\n\t\t\t\tushort nextSegStartNodeId = netManager.m_segments.m_buffer[(int)nextPathPos.m_segment].m_startNode;\n\t\t\t\tushort nextSegEndNodeId = netManager.m_segments.m_buffer[(int)nextPathPos.m_segment].m_endNode;\n\t\t\t\tif (nextSegStartNodeId != curSegStartNodeId &&\n\t\t\t\t\tnextSegStartNodeId != curSegEndNodeId &&\n\t\t\t\t\tnextSegEndNodeId != curSegStartNodeId &&\n\t\t\t\t\tnextSegEndNodeId != curSegEndNodeId &&\n\t\t\t\t\t((netManager.m_nodes.m_buffer[(int)curSegStartNodeId].m_flags | netManager.m_nodes.m_buffer[(int)curSegEndNodeId].m_flags) & NetNode.Flags.Disabled) == NetNode.Flags.None &&\n\t\t\t\t\t((netManager.m_nodes.m_buffer[(int)nextSegStartNodeId].m_flags | netManager.m_nodes.m_buffer[(int)nextSegEndNodeId].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None) {\n\t\t\t\t\tthis.InvalidPath(vehicleID, ref vehicleData, vehicleID, ref vehicleData);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// park vehicle\n\t\t\t\tif (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian) {\n\t\t\t\t\tif (vehicleID != 0 && (vehicleData.m_flags & Vehicle.Flags.Parking) == (Vehicle.Flags)0) {\n\t\t\t\t\t\tbyte inOffset = currentPosition.m_offset;\n\t\t\t\t\t\tbyte outOffset = currentPosition.m_offset;\n\t\t\t\t\t\tif (this.ParkVehicle(vehicleID, ref vehicleData, currentPosition, nextPathId, nextCoarsePathPosIndex << 1, out outOffset)) {\n\t\t\t\t\t\t\tif (outOffset != inOffset) {\n\t\t\t\t\t\t\t\tif (targetPosIndex <= 0) {\n\t\t\t\t\t\t\t\t\tvehicleData.m_pathPositionIndex = (byte)((int)vehicleData.m_pathPositionIndex & -2);\n\t\t\t\t\t\t\t\t\tvehicleData.m_lastPathOffset = inOffset;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcurrentPosition.m_offset = outOffset;\n\t\t\t\t\t\t\t\tpathMan.m_pathUnits.m_buffer[(int)((UIntPtr)pathId)].SetPosition(finePathPosIndex >> 1, currentPosition);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.Parking;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.InvalidPath(vehicleID, ref vehicleData, vehicleID, ref vehicleData);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// check for errors\n\t\t\t\tif ((byte)(nextLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.CargoVehicle | NetInfo.LaneType.TransportVehicle)) == 0) {\n\t\t\t\t\tthis.InvalidPath(vehicleID, ref vehicleData, vehicleID, ref vehicleData);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// change vehicle\n\t\t\t\tif (nextLaneInfo.m_vehicleType != this.m_info.m_vehicleType &&\n\t\t\t\t\tthis.NeedChangeVehicleType(vehicleID, ref vehicleData, nextPathPos, nextLaneId, nextLaneInfo.m_vehicleType, ref targetPos)\n\t\t\t\t) {\n\t\t\t\t\tfloat targetPos0ToRefPosSqrDist = ((Vector3)targetPos - refPos).sqrMagnitude;\n\t\t\t\t\tif (targetPos0ToRefPosSqrDist >= minSqrDistA) {\n\t\t\t\t\t\tvehicleData.SetTargetPos(targetPosIndex++, targetPos);\n\t\t\t\t\t}\n\t\t\t\t\tif (targetPosIndex <= 0) {\n\t\t\t\t\t\twhile (targetPosIndex < maxTargetPosIndex) {\n\t\t\t\t\t\t\tvehicleData.SetTargetPos(targetPosIndex++, targetPos);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (nextPathId != vehicleData.m_path) {\n\t\t\t\t\t\t\tSingleton<PathManager>.instance.ReleaseFirstUnit(ref vehicleData.m_path);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvehicleData.m_pathPositionIndex = (byte)(nextCoarsePathPosIndex << 1);\n\t\t\t\t\t\tPathUnit.CalculatePathPositionOffset(nextLaneId, targetPos, out vehicleData.m_lastPathOffset);\n\t\t\t\t\t\tif (vehicleID != 0 && !this.ChangeVehicleType(vehicleID, ref vehicleData, nextPathPos, nextLaneId)) {\n\t\t\t\t\t\t\tthis.InvalidPath(vehicleID, ref vehicleData, vehicleID, ref vehicleData);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\twhile (targetPosIndex < maxTargetPosIndex) {\n\t\t\t\t\t\t\tvehicleData.SetTargetPos(targetPosIndex++, targetPos);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// unset leaving flag\n\t\t\t\tif (nextPathPos.m_segment != currentPosition.m_segment && vehicleID != 0) {\n\t\t\t\t\tvehicleData.m_flags &= ~Vehicle.Flags.Leaving;\n\t\t\t\t}\n\n\t\t\t\t// calculate next segment offset\n\t\t\t\tbyte nextSegOffset = 0;\n\t\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Flying) != (Vehicle.Flags)0) {\n\t\t\t\t\tnextSegOffset = (byte)((nextPathPos.m_offset < 128) ? 255 : 0);\n\t\t\t\t} else if (curLaneId != nextLaneId && laneInfo.m_laneType != NetInfo.LaneType.CargoVehicle) {\n\t\t\t\t\tPathUnit.CalculatePathPositionOffset(nextLaneId, targetPos, out nextSegOffset);\n\t\t\t\t\tbezier = default(Bezier3);\n\t\t\t\t\tVector3 curSegDir;\n\t\t\t\t\tfloat maxSpeed;\n\t\t\t\t\tthis.CalculateSegmentPosition(vehicleID, ref vehicleData, currentPosition, curLaneId, currentPosition.m_offset, out bezier.a, out curSegDir, out maxSpeed);\n\t\t\t\t\tbool calculateNextNextPos = lastPathOffset == 0;\n\t\t\t\t\tif (calculateNextNextPos) {\n\t\t\t\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Reversed) != (Vehicle.Flags)0) {\n\t\t\t\t\t\t\tcalculateNextNextPos = (vehicleData.m_trailingVehicle == 0);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcalculateNextNextPos = (vehicleData.m_leadingVehicle == 0);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tVector3 nextSegDir;\n\t\t\t\t\tfloat nextMaxSpeed;\n\t\t\t\t\tif (calculateNextNextPos) {\n\t\t\t\t\t\tPathUnit.Position nextNextPathPos;\n\t\t\t\t\t\tif (!pathMan.m_pathUnits.m_buffer[nextPathId].GetNextPosition(nextCoarsePathPosIndex, out nextNextPathPos)) {\n\t\t\t\t\t\t\tnextNextPathPos = default(PathUnit.Position);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.CalculateSegmentPosition(vehicleID, ref vehicleData, nextNextPathPos, nextPathPos, nextLaneId, nextSegOffset, currentPosition, curLaneId, currentPosition.m_offset, targetPosIndex, out bezier.d, out nextSegDir, out nextMaxSpeed);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.CalculateSegmentPosition(vehicleID, ref vehicleData, nextPathPos, nextLaneId, nextSegOffset, out bezier.d, out nextSegDir, out nextMaxSpeed);\n\t\t\t\t\t}\n\t\t\t\t\tif (nextMaxSpeed < 0.01f || (netManager.m_segments.m_buffer[(int)nextPathPos.m_segment].m_flags & (NetSegment.Flags.Collapsed | NetSegment.Flags.Flooded)) != NetSegment.Flags.None) {\n\t\t\t\t\t\tif (targetPosIndex <= 0) {\n\t\t\t\t\t\t\tvehicleData.m_lastPathOffset = lastPathOffset;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttargetPos = bezier.a;\n\t\t\t\t\t\ttargetPos.w = 0f;\n\t\t\t\t\t\twhile (targetPosIndex < maxTargetPosIndex) {\n\t\t\t\t\t\t\tvehicleData.SetTargetPos(targetPosIndex++, targetPos);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (currentPosition.m_offset == 0) {\n\t\t\t\t\t\tcurSegDir = -curSegDir;\n\t\t\t\t\t}\n\t\t\t\t\tif (nextSegOffset < nextPathPos.m_offset) {\n\t\t\t\t\t\tnextSegDir = -nextSegDir;\n\t\t\t\t\t}\n\t\t\t\t\tcurSegDir.Normalize();\n\t\t\t\t\tnextSegDir.Normalize();\n\t\t\t\t\tfloat dist;\n\t\t\t\t\tNetSegment.CalculateMiddlePoints(bezier.a, curSegDir, bezier.d, nextSegDir, true, true, out bezier.b, out bezier.c, out dist);\n\t\t\t\t\tif (dist > 1f) {\n\t\t\t\t\t\tushort nextNodeId;\n\t\t\t\t\t\tif (nextSegOffset == 0) {\n\t\t\t\t\t\t\tnextNodeId = netManager.m_segments.m_buffer[(int)nextPathPos.m_segment].m_startNode;\n\t\t\t\t\t\t} else if (nextSegOffset == 255) {\n\t\t\t\t\t\t\tnextNodeId = netManager.m_segments.m_buffer[(int)nextPathPos.m_segment].m_endNode;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextNodeId = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfloat curve = 1.57079637f * (1f + Vector3.Dot(curSegDir, nextSegDir));\n\t\t\t\t\t\tif (dist > 1f) {\n\t\t\t\t\t\t\tcurve /= dist;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnextMaxSpeed = Mathf.Min(nextMaxSpeed, this.CalculateTargetSpeed(vehicleID, ref vehicleData, 1000f, curve));\n\t\t\t\t\t\twhile (lastPathOffset < 255) {\n\t\t\t\t\t\t\tfloat distDiff = Mathf.Sqrt(minSqrDistA) - Vector3.Distance(targetPos, refPos);\n\t\t\t\t\t\t\tint pathOffsetDelta;\n\t\t\t\t\t\t\tif (distDiff < 0f) {\n\t\t\t\t\t\t\t\tpathOffsetDelta = 8;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpathOffsetDelta = 8 + Mathf.Max(0, Mathf.CeilToInt(distDiff * 256f / (dist + 1f)));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlastPathOffset = (byte)Mathf.Min((int)lastPathOffset + pathOffsetDelta, 255);\n\t\t\t\t\t\t\tVector3 bezierPos = bezier.Position((float)lastPathOffset * 0.003921569f);\n\t\t\t\t\t\t\ttargetPos.Set(bezierPos.x, bezierPos.y, bezierPos.z, Mathf.Min(targetPos.w, nextMaxSpeed));\n\t\t\t\t\t\t\tfloat sqrMagnitude2 = (bezierPos - refPos).sqrMagnitude;\n\t\t\t\t\t\t\tif (sqrMagnitude2 >= minSqrDistA) {\n\t\t\t\t\t\t\t\tif (targetPosIndex <= 0) {\n\t\t\t\t\t\t\t\t\tvehicleData.m_lastPathOffset = lastPathOffset;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (nextNodeId != 0) {\n\t\t\t\t\t\t\t\t\tthis.UpdateNodeTargetPos(vehicleID, ref vehicleData, nextNodeId, ref netManager.m_nodes.m_buffer[(int)nextNodeId], ref targetPos, targetPosIndex);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tvehicleData.SetTargetPos(targetPosIndex++, targetPos);\n\t\t\t\t\t\t\t\tminSqrDistA = minSqrDistanceB;\n\t\t\t\t\t\t\t\trefPos = targetPos;\n\t\t\t\t\t\t\t\ttargetPos.w = 1000f;\n\t\t\t\t\t\t\t\tif (targetPosIndex == maxTargetPosIndex) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tPathUnit.CalculatePathPositionOffset(nextLaneId, targetPos, out nextSegOffset);\n\t\t\t\t}\n\n\t\t\t\t// check for arrival\n\t\t\t\tif (targetPosIndex <= 0) {\n\t\t\t\t\tif ((netManager.m_segments.m_buffer[nextPathPos.m_segment].m_flags & NetSegment.Flags.Untouchable) != 0 && (netManager.m_segments.m_buffer[currentPosition.m_segment].m_flags & NetSegment.Flags.Untouchable) == NetSegment.Flags.None) {\n\t\t\t\t\t\tushort ownerBuildingId = NetSegment.FindOwnerBuilding(nextPathPos.m_segment, 363f);\n\t\t\t\t\t\tif (ownerBuildingId != 0) {\n\t\t\t\t\t\t\tBuildingManager buildingMan = Singleton<BuildingManager>.instance;\n\t\t\t\t\t\t\tBuildingInfo ownerBuildingInfo = buildingMan.m_buildings.m_buffer[ownerBuildingId].Info;\n\t\t\t\t\t\t\tInstanceID itemID = default(InstanceID);\n\t\t\t\t\t\t\titemID.Vehicle = vehicleID;\n\t\t\t\t\t\t\townerBuildingInfo.m_buildingAI.EnterBuildingSegment(ownerBuildingId, ref buildingMan.m_buildings.m_buffer[ownerBuildingId], nextPathPos.m_segment, nextPathPos.m_offset, itemID);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (nextCoarsePathPosIndex == 0) {\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleaseFirstUnit(ref vehicleData.m_path);\n\t\t\t\t\t}\n\t\t\t\t\tif (nextCoarsePathPosIndex >= (int)(pathMan.m_pathUnits.m_buffer[(int)((UIntPtr)nextPathId)].m_positionCount - 1) && pathMan.m_pathUnits.m_buffer[(int)((UIntPtr)nextPathId)].m_nextPathUnit == 0u && vehicleID != 0) {\n\t\t\t\t\t\tthis.ArrivingToDestination(vehicleID, ref vehicleData);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// prepare next loop iteration: go to next path position\n\t\t\t\tpathId = nextPathId;\n\t\t\t\tfinePathPosIndex = (byte)(nextCoarsePathPosIndex << 1);\n\t\t\t\tlastPathOffset = nextSegOffset;\n\t\t\t\tif (targetPosIndex <= 0) {\n\t\t\t\t\tvehicleData.m_pathPositionIndex = finePathPosIndex;\n\t\t\t\t\tvehicleData.m_lastPathOffset = lastPathOffset;\n\t\t\t\t\tvehicleData.m_flags = ((vehicleData.m_flags & ~(Vehicle.Flags.OnGravel | Vehicle.Flags.Underground | Vehicle.Flags.Transition)) | nextSegmentInfo.m_setVehicleFlags);\n\t\t\t\t\tif (this.LeftHandDrive(nextLaneInfo)) {\n\t\t\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.LeftHandDrive;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvehicleData.m_flags &= (Vehicle.Flags.Created | Vehicle.Flags.Deleted | Vehicle.Flags.Spawned | Vehicle.Flags.Inverted | Vehicle.Flags.TransferToTarget | Vehicle.Flags.TransferToSource | Vehicle.Flags.Emergency1 | Vehicle.Flags.Emergency2 | Vehicle.Flags.WaitingPath | Vehicle.Flags.Stopped | Vehicle.Flags.Leaving | Vehicle.Flags.Arriving | Vehicle.Flags.Reversed | Vehicle.Flags.TakingOff | Vehicle.Flags.Flying | Vehicle.Flags.Landing | Vehicle.Flags.WaitingSpace | Vehicle.Flags.WaitingCargo | Vehicle.Flags.GoingBack | Vehicle.Flags.WaitingTarget | Vehicle.Flags.Importing | Vehicle.Flags.Exporting | Vehicle.Flags.Parking | Vehicle.Flags.CustomName | Vehicle.Flags.OnGravel | Vehicle.Flags.WaitingLoading | Vehicle.Flags.Congestion | Vehicle.Flags.DummyTraffic | Vehicle.Flags.Underground | Vehicle.Flags.Transition | Vehicle.Flags.InsideBuilding);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcurrentPosition = nextPathPos;\n\t\t\t\tcurLaneId = nextLaneId;\n\t\t\t\tlaneInfo = nextLaneInfo;\n\t\t\t\tfirstIter = false; // NON-STOCK CODE\n\t\t\t}\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate static int FindBestLane(ushort vehicleID, ref Vehicle vehicleData, PathUnit.Position position) {\n\t\t\tLog.Error(\"CustomVehicleAI.FindBestLane called\");\n\t\t\treturn 0;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/AI/README.md",
    "content": "# TM:PE -- /Custom/AI\nDetoured *AI classes.\n## Classes\n- **CustomAmbulanceAI**: Path-finding detours for ambulances.\n- **CustomBuildingAI**: Building detours. Used when the Parking AI is active.\n- **CustomBusAI**: Path-finding detours for busses. \n- **CustomCarAI**: Path-finding detours for cars. Updates current vehicle positions and prevents despawning (CustomSimulationStep), implements reckless driving, checks for custom speed limits (CalcMaxSpeed) and makes cars obey both priority rules and custom traffic lights (call to MayChangeSegment in CustomCalculateSegmentPosition).\n- **CustomCargoTruckAI**: Path-finding detours for all kinds of cargo trucks. \n- **CustomCitizenAI**: Path-finding detours for citizens (= both residents and tourists). Manages usage of public transport and spawning of pocket cars.\n- **CustomCommonBuildingAI**: Common building detours. Used when the Parking AI is active.  \n- **CustomFireTruckAI**: Path-finding detours for fire trucks. \n- **CustomHumanAI**: Implements checks for custom traffic lights for humans.\n- **CustomPassengerCarAI**: Path-finding detours for passenger cars. \n- **CustomPoliceCarAI**: Path-finding detours for police cars.\n- **CustomResidentAI**: Parking AI detours for residents. \n- **CustomRoadAI**: Manages traffic density and current speed measurement values for every segment (CustomSegmentSimulationStep). Initiates the timed traffic light simulation (CustomNodeSimulationStep). Detours traffic light getters/setters in order to allow for custom traffic lights.\n- **CustomShipAI**: Path-finding detours for ships.\n- **CustomTaxiAI**: Path-finding detours for taxis.\n- **CustomTouristAI**: Parking AI detours for residents. \n- **CustomTrainAI**: Path-finding detours for trains. Updates current vehicle positions and prevents despawning (CustomSimulationStep), checks for custom speed limits (TmCalculateSegmentPositionPathFinder) and makes trains obey both priority rules and custom traffic lights (call to MayChangeSegment in CustomCheckNextLane).\n- **CustomTramBaseAI**: Path-finding detours for trams. Updates current vehicle positions and prevents despawning (CustomSimulationStep), checks for custom speed limits (CustomCalculateSegmentPosition and CustomCalculateSegmentPositionPathFinder) and makes trams obey both priority rules and custom traffic lights (call to MayChangeSegment in CustomCalculateSegmentPosition).\n- **CustomTransportLineAI**: Path-finding detours for public transport lines.  \n- **CustomVehicleAI**: Implements checks whether a vehicle may move to the next segment (MayChangeSegment). This includes checking priority rules and custom traffic lights. Checks for custom speed limits (CalculateSegPos)."
  },
  {
    "path": "TLM/TLM/Custom/Data/CustomVehicle.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing CSUtil.Commons;\nusing CSUtil.Commons.Benchmark;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing UnityEngine;\n\nnamespace TrafficManager.Custom.Data {\n\tpublic static class CustomVehicle {\n\t\tpublic static void Spawn(ref Vehicle vehicleData, ushort vehicleId) {\n\t\t\t//Log._Debug($\"CustomVehicle.Spawn({vehicleId}) called.\");\n\n\t\t\tVehicleManager vehManager = Singleton<VehicleManager>.instance;\n\t\t\tVehicleInfo vehicleInfo = vehicleData.Info;\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Spawned) == (Vehicle.Flags)0) {\n\t\t\t\tvehicleData.m_flags |= Vehicle.Flags.Spawned;\n\t\t\t\tvehManager.AddToGrid(vehicleId, ref vehicleData, vehicleInfo.m_isLargeVehicle);\n\t\t\t}\n\n\t\t\tif (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0) {\n\t\t\t\tushort trailingVehicle = vehicleData.m_trailingVehicle;\n\t\t\t\tint numIter = 0;\n\t\t\t\twhile (trailingVehicle != 0) {\n\t\t\t\t\tvehManager.m_vehicles.m_buffer[trailingVehicle].Spawn(trailingVehicle);\n\t\t\t\t\ttrailingVehicle = vehManager.m_vehicles.m_buffer[trailingVehicle].m_trailingVehicle;\n\t\t\t\t\tif (++numIter > VehicleManager.MAX_VEHICLE_COUNT) {\n\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle == 0 && vehicleInfo.m_trailers != null) {\n\t\t\t\tbool hasTrailers = vehicleInfo.m_vehicleAI.VerticalTrailers();\n\t\t\t\tushort curVehicleId = vehicleId;\n\t\t\t\tbool reversed = (vehManager.m_vehicles.m_buffer[curVehicleId].m_flags & Vehicle.Flags.Reversed) != (Vehicle.Flags)0;\n\t\t\t\tVehicle.Frame lastFrameData = vehicleData.GetLastFrameData();\n\t\t\t\tfloat length = (!hasTrailers) ? (vehicleInfo.m_generatedInfo.m_size.z * 0.5f) : 0f;\n\t\t\t\tlength -= (((vehicleData.m_flags & Vehicle.Flags.Inverted) == (Vehicle.Flags)0) ? vehicleInfo.m_attachOffsetBack : vehicleInfo.m_attachOffsetFront);\n\t\t\t\tRandomizer randomizer = new Randomizer((int)vehicleId);\n\t\t\t\tint trailerCount = 0;\n\t\t\t\tfor (int i = 0; i < vehicleInfo.m_trailers.Length; i++) {\n\t\t\t\t\tif (randomizer.Int32(100u) < vehicleInfo.m_trailers[i].m_probability) {\n\t\t\t\t\t\tVehicleInfo trailerInfo = vehicleInfo.m_trailers[i].m_info;\n\t\t\t\t\t\tbool inverted = randomizer.Int32(100u) < vehicleInfo.m_trailers[i].m_invertProbability;\n\t\t\t\t\t\tlength += ((!hasTrailers) ? (trailerInfo.m_generatedInfo.m_size.z * 0.5f) : trailerInfo.m_generatedInfo.m_size.y);\n\t\t\t\t\t\tlength -= ((!inverted) ? trailerInfo.m_attachOffsetFront : trailerInfo.m_attachOffsetBack);\n\t\t\t\t\t\tVector3 position = lastFrameData.m_position - lastFrameData.m_rotation * new Vector3(0f, (!hasTrailers) ? 0f : length, (!hasTrailers) ? length : 0f);\n\t\t\t\t\t\tushort trailerVehicleId;\n\t\t\t\t\t\tif (vehManager.CreateVehicle(out trailerVehicleId, ref Singleton<SimulationManager>.instance.m_randomizer, trailerInfo, position, (TransferManager.TransferReason)vehicleData.m_transferType, false, false)) {\n\t\t\t\t\t\t\tvehManager.m_vehicles.m_buffer[(int)curVehicleId].m_trailingVehicle = trailerVehicleId;\n\t\t\t\t\t\t\tvehManager.m_vehicles.m_buffer[(int)trailerVehicleId].m_leadingVehicle = curVehicleId;\n\t\t\t\t\t\t\tvehManager.m_vehicles.m_buffer[(int)trailerVehicleId].m_gateIndex = vehicleData.m_gateIndex;\n\t\t\t\t\t\t\tif (inverted) {\n\t\t\t\t\t\t\t\tvehManager.m_vehicles.m_buffer[trailerVehicleId].m_flags |= Vehicle.Flags.Inverted;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (reversed) {\n\t\t\t\t\t\t\t\tvehManager.m_vehicles.m_buffer[trailerVehicleId].m_flags |= Vehicle.Flags.Reversed;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tvehManager.m_vehicles.m_buffer[(int)trailerVehicleId].m_frame0.m_rotation = lastFrameData.m_rotation;\n\t\t\t\t\t\t\tvehManager.m_vehicles.m_buffer[(int)trailerVehicleId].m_frame1.m_rotation = lastFrameData.m_rotation;\n\t\t\t\t\t\t\tvehManager.m_vehicles.m_buffer[(int)trailerVehicleId].m_frame2.m_rotation = lastFrameData.m_rotation;\n\t\t\t\t\t\t\tvehManager.m_vehicles.m_buffer[(int)trailerVehicleId].m_frame3.m_rotation = lastFrameData.m_rotation;\n\t\t\t\t\t\t\ttrailerInfo.m_vehicleAI.FrameDataUpdated(trailerVehicleId, ref vehManager.m_vehicles.m_buffer[(int)trailerVehicleId], ref vehManager.m_vehicles.m_buffer[(int)trailerVehicleId].m_frame0);\n\t\t\t\t\t\t\tCustomVehicle.Spawn(ref vehManager.m_vehicles.m_buffer[(int)trailerVehicleId], trailerVehicleId); // NON-STOCK CODE\n\t\t\t\t\t\t\tcurVehicleId = trailerVehicleId;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlength += ((!hasTrailers) ? (trailerInfo.m_generatedInfo.m_size.z * 0.5f) : 0f);\n\t\t\t\t\t\tlength -= ((!inverted) ? trailerInfo.m_attachOffsetBack : trailerInfo.m_attachOffsetFront);\n\t\t\t\t\t\tif (++trailerCount == vehicleInfo.m_maxTrailerCount) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"OnSpawnVehicle\")) {\n#endif\n\t\t\t\tVehicleStateManager.Instance.OnSpawnVehicle(vehicleId, ref vehicleData);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\t\t}\n\n\t\tpublic static void Unspawn(ref Vehicle vehicleData, ushort vehicleId) {\n\t\t\t//Log._Debug($\"CustomVehicle.Unspawn({vehicleId}) called.\");\n\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"OnDespawnVehicle\")) {\n#endif\n\t\t\t\tVehicleStateManager.Instance.OnDespawnVehicle(vehicleId, ref vehicleData);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tVehicleManager vehManager = Singleton<VehicleManager>.instance;\n\t\t\tif (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0) {\n\t\t\t\tushort curVehicleId = vehicleData.m_trailingVehicle;\n\t\t\t\tvehicleData.m_trailingVehicle = 0;\n\t\t\t\tint numIters = 0;\n\t\t\t\twhile (curVehicleId != 0) {\n\t\t\t\t\tushort trailingVehicleId = vehManager.m_vehicles.m_buffer[(int)curVehicleId].m_trailingVehicle;\n\t\t\t\t\tvehManager.m_vehicles.m_buffer[(int)curVehicleId].m_leadingVehicle = 0;\n\t\t\t\t\tvehManager.m_vehicles.m_buffer[(int)curVehicleId].m_trailingVehicle = 0;\n\t\t\t\t\tvehManager.ReleaseVehicle(curVehicleId);\n\t\t\t\t\tcurVehicleId = trailingVehicleId;\n\t\t\t\t\tif (++numIters > 16384) {\n\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Spawned) != (Vehicle.Flags)0) {\n\t\t\t\tVehicleInfo info = vehicleData.Info;\n\t\t\t\tif (info != null) {\n\t\t\t\t\tvehManager.RemoveFromGrid(vehicleId, ref vehicleData, info.m_isLargeVehicle);\n\t\t\t\t}\n\t\t\t\tvehicleData.m_flags &= ~Vehicle.Flags.Spawned;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/Data/README.md",
    "content": "# TM:PE -- /Custom/Data\nDetoured data structs.\n## Classes\n- **CustomVehicle**: Detoured for checking when vehicle are spawned/unspawned. "
  },
  {
    "path": "TLM/TLM/Custom/Manager/CustomCitizenManager.cs",
    "content": "﻿using ColossalFramework.Math;\nusing CSUtil.Commons;\nusing CSUtil.Commons.Benchmark;\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing UnityEngine;\n\nnamespace TrafficManager.Custom.Manager {\n\tpublic class CustomCitizenManager : CitizenManager {\n\n\t\tpublic void CustomReleaseCitizenInstance(ushort instanceId) {\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"OnReleaseInstance\")) {\n#endif\n\t\t\t\tExtCitizenInstanceManager.Instance.OnReleaseInstance(instanceId);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\tthis.ReleaseCitizenInstanceImplementation(instanceId, ref this.m_instances.m_buffer[(int)instanceId]);\n\t\t}\n\n\t\tpublic void CustomReleaseCitizen(uint citizenId) {\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"OnReleaseCitizen\")) {\n#endif\n\t\t\t\tExtCitizenManager.Instance.OnReleaseCitizen(citizenId);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\tthis.ReleaseCitizenImplementation(citizenId, ref this.m_citizens.m_buffer[citizenId]);\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate void ReleaseCitizenInstanceImplementation(ushort instanceId, ref CitizenInstance instanceData) {\n\t\t\tLog.Error(\"CustomCitizenManager.ReleaseCitizenInstanceImplementation called.\");\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate void ReleaseCitizenImplementation(uint citizenId, ref Citizen citizen) {\n\t\t\tLog.Error(\"CustomCitizenManager.ReleaseCitizenImplementation called.\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/Manager/CustomNetManager.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing CSUtil.Commons.Benchmark;\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.State;\nusing UnityEngine;\n\nnamespace TrafficManager.Custom.Manager {\n\tpublic class CustomNetManager : NetManager {\n\t\tpublic void CustomFinalizeSegment(ushort segment, ref NetSegment data) {\n\t\t\tVector3 vector = (this.m_nodes.m_buffer[(int)data.m_startNode].m_position + this.m_nodes.m_buffer[(int)data.m_endNode].m_position) * 0.5f;\n\t\t\tint num = Mathf.Clamp((int)(vector.x / 64f + 135f), 0, 269);\n\t\t\tint num2 = Mathf.Clamp((int)(vector.z / 64f + 135f), 0, 269);\n\t\t\tint num3 = num2 * 270 + num;\n\t\t\tushort num4 = 0;\n\t\t\tushort num5 = this.m_segmentGrid[num3];\n\t\t\tint num6 = 0;\n\t\t\twhile (num5 != 0) {\n\t\t\t\tif (num5 == segment) {\n\t\t\t\t\tif (num4 == 0) {\n\t\t\t\t\t\tthis.m_segmentGrid[num3] = data.m_nextGridSegment;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.m_segments.m_buffer[(int)num4].m_nextGridSegment = data.m_nextGridSegment;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tnum4 = num5;\n\t\t\t\tnum5 = this.m_segments.m_buffer[(int)num5].m_nextGridSegment;\n\t\t\t\tif (++num6 > 65536) {\n\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tdata.m_nextGridSegment = 0;\n\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"StartRecalculation\")) {\n#endif\n\t\t\t\ttry {\n#if DEBUGGEO\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\t\tLog.Warning($\"CustomNetManager: CustomFinalizeSegment {segment}\");\n#endif\n\t\t\t\t\tSegmentGeometry.Get(segment, true).StartRecalculation(GeometryCalculationMode.Propagate);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Error occured in CustomNetManager.CustomFinalizeSegment @ seg. {segment}: \" + e.ToString());\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\t\t}\n\n\t\tpublic void CustomUpdateSegment(ushort segment, ushort fromNode, int level) {\n\t\t\tthis.m_updatedSegments[segment >> 6] |= 1uL << (int)segment;\n\t\t\tthis.m_segmentsUpdated = true;\n\t\t\tif (level <= 0) {\n\t\t\t\tushort startNode = this.m_segments.m_buffer[(int)segment].m_startNode;\n\t\t\t\tushort endNode = this.m_segments.m_buffer[(int)segment].m_endNode;\n\t\t\t\tif (startNode != 0 && startNode != fromNode) {\n\t\t\t\t\tthis.UpdateNode(startNode, segment, level + 1);\n\t\t\t\t}\n\t\t\t\tif (endNode != 0 && endNode != fromNode) {\n\t\t\t\t\tthis.UpdateNode(endNode, segment, level + 1);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"StartRecalculation\")) {\n#endif\n\t\t\t\ttry {\n#if DEBUG\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\t\tLog.Warning($\"CustomNetManager: CustomUpdateSegment {segment}\");\n#endif\n\t\t\t\t\tSegmentGeometry.Get(segment, true).StartRecalculation(GeometryCalculationMode.Propagate);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Error occured in CustomNetManager.CustomUpdateSegment @ seg. {segment}: \" + e.ToString());\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\t\t}\n\n\t\t// TODO remove\n#if DEBUG\n\t\tprivate void CustomMoveNode(ushort node, ref NetNode data, Vector3 position) {\n\t\t\tvar connClass = data.Info.GetConnectionClass();\n\t\t\tif (connClass == null || connClass.m_service == ItemClass.Service.PublicTransport) {\n\t\t\t\tLog.Warning($\"CustomNetManager.CustomMoveNode({node}, ..., {position}): old position: {data.m_position} -- flags: {data.m_flags}, problems: {data.m_problems}, transport line: {data.m_transportLine}\");\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < 8; i++) {\n\t\t\t\tushort segment = data.GetSegment(i);\n\t\t\t\tif (segment != 0) {\n\t\t\t\t\tCustomFinalizeSegment(segment, ref this.m_segments.m_buffer[(int)segment]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.FinalizeNode(node, ref data);\n\t\t\tdata.m_position = position;\n\t\t\tthis.InitializeNode(node, ref data);\n\t\t\tfor (int j = 0; j < 8; j++) {\n\t\t\t\tushort segment2 = data.GetSegment(j);\n\t\t\t\tif (segment2 != 0) {\n\t\t\t\t\tInitializeSegment(segment2, ref this.m_segments.m_buffer[(int)segment2]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.UpdateNode(node);\n\t\t}\n\n\t\t// TODO remove\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate void FinalizeNode(ushort node, ref NetNode data) {\n\t\t\tLog.Error($\"CustomNetManager.FinalizeNode called\");\n\t\t}\n\n\t\t// TODO remove\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate void InitializeNode(ushort node, ref NetNode data) {\n\t\t\tLog.Error($\"CustomNetManager.InitializeNode called\");\n\t\t}\n\n\t\t// TODO remove\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate void InitializeSegment(ushort segmentId, ref NetSegment data) {\n\t\t\tLog.Error($\"CustomNetManager.InitializeNode called\");\n\t\t}\n#endif\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/Manager/CustomVehicleManager.cs",
    "content": "﻿using ColossalFramework.Math;\nusing CSUtil.Commons;\nusing CSUtil.Commons.Benchmark;\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing UnityEngine;\n\nnamespace TrafficManager.Custom.Manager {\n\tpublic class CustomVehicleManager : VehicleManager {\n\t\tpublic void CustomReleaseVehicle(ushort vehicleId) {\n#if DEBUG\n\t\t\t//Log._Debug($\"CustomVehicleManager.CustomReleaseVehicle({vehicleId})\");\n#endif\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"OnReleaseVehicle\")) {\n#endif\n\t\t\t\tVehicleStateManager.Instance.OnReleaseVehicle(vehicleId, ref this.m_vehicles.m_buffer[vehicleId]);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\tReleaseVehicleImplementation(vehicleId, ref this.m_vehicles.m_buffer[vehicleId]);\n\t\t}\n\n\t\tpublic bool CustomCreateVehicle(out ushort vehicleId, ref Randomizer r, VehicleInfo info, Vector3 position, TransferManager.TransferReason type, bool transferToSource, bool transferToTarget) {\n\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"keep-spare-vehicles\")) {\n#endif\n\t\t\t\tif (this.m_vehicleCount > VehicleManager.MAX_VEHICLE_COUNT - 5) {\n\t\t\t\t\t// prioritize service vehicles and public transport when hitting the vehicle limit\n\t\t\t\t\tItemClass.Service service = info.GetService();\n\t\t\t\t\tif (service == ItemClass.Service.Residential || service == ItemClass.Service.Industrial || service == ItemClass.Service.Commercial || service == ItemClass.Service.Office) {\n\t\t\t\t\t\tvehicleId = 0;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tushort vehId;\n\t\t\tif (this.m_vehicles.CreateItem(out vehId, ref r)) {\n\t\t\t\tvehicleId = vehId;\n\t\t\t\tVehicle.Frame frame = new Vehicle.Frame(position, Quaternion.identity);\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_flags = Vehicle.Flags.Created;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_flags2 = (Vehicle.Flags2)0;\n\t\t\t\tif (transferToSource) {\n\t\t\t\t\tthis.m_vehicles.m_buffer[vehicleId].m_flags = (this.m_vehicles.m_buffer[vehicleId].m_flags | Vehicle.Flags.TransferToSource);\n\t\t\t\t}\n\t\t\t\tif (transferToTarget) {\n\t\t\t\t\tthis.m_vehicles.m_buffer[vehicleId].m_flags = (this.m_vehicles.m_buffer[vehicleId].m_flags | Vehicle.Flags.TransferToTarget);\n\t\t\t\t}\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].Info = info;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_frame0 = frame;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_frame1 = frame;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_frame2 = frame;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_frame3 = frame;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_targetPos0 = Vector4.zero;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_targetPos1 = Vector4.zero;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_targetPos2 = Vector4.zero;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_targetPos3 = Vector4.zero;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_sourceBuilding = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_targetBuilding = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_transferType = (byte)type;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_transferSize = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_waitCounter = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_blockCounter = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_nextGridVehicle = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_nextOwnVehicle = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_nextGuestVehicle = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_nextLineVehicle = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_transportLine = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_leadingVehicle = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_trailingVehicle = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_cargoParent = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_firstCargo = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_nextCargo = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_citizenUnits = 0u;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_path = 0u;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_lastFrame = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_pathPositionIndex = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_lastPathOffset = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_gateIndex = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_waterSource = 0;\n\t\t\t\tthis.m_vehicles.m_buffer[(int)vehicleId].m_touristCount = 0;\n\t\t\t\tinfo.m_vehicleAI.CreateVehicle(vehicleId, ref this.m_vehicles.m_buffer[vehicleId]);\n\t\t\t\tinfo.m_vehicleAI.FrameDataUpdated(vehicleId, ref this.m_vehicles.m_buffer[vehicleId], ref this.m_vehicles.m_buffer[vehicleId].m_frame0);\n\t\t\t\tthis.m_vehicleCount = (int)(this.m_vehicles.ItemCount() - 1u);\n\n\t\t\t\t// NON-STOCK CODE START\n#if BENCHMARK\n\t\t\t\tusing (var bm = new Benchmark(null, \"OnCreateVehicle\")) {\n#endif\n\t\t\t\t\tVehicleStateManager.Instance.OnCreateVehicle(vehicleId, ref this.m_vehicles.m_buffer[vehicleId]); // NON-STOCK CODE\n#if BENCHMARK\n\t\t\t\t}\n#endif\n\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tvehicleId = 0;\n\t\t\treturn false;\n\t\t}\n\n\t\t[MethodImpl(MethodImplOptions.NoInlining)]\n\t\tprivate void ReleaseVehicleImplementation(ushort vehicleId, ref Vehicle vehicleData) {\n\t\t\tLog.Error(\"CustomVehicleManager.ReleaseVehicleImplementation called.\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/Manager/README.md",
    "content": "# TM:PE -- /Custom/Manager\nDetoured *Manager classes.\n## Classes\n- **CustomCitizenManager**: Implements detours for checking if citizens instances are being released. Notifies the ExtCitizenInstanceManager.\n- **CustomNetManager**: Implements detours for checking if segments/nodes are being added/updated/removed. Recalculates segment/node geometries if necessary.\n- **CustomVehicleManager**: Implements detours for checking if vehicles are begin created/released. Determines the **ExtVehicleType** of a vehicle as soon as it is created. "
  },
  {
    "path": "TLM/TLM/Custom/PathFinding/CustomPathFind.cs",
    "content": "﻿#define DEBUGLOCKSx\n#define COUNTSEGMENTSTONEXTJUNCTIONx\n\nusing System;\nusing System.Reflection;\nusing System.Threading;\nusing ColossalFramework;\nusing ColossalFramework.Math;\nusing ColossalFramework.UI;\nusing TrafficManager.Geometry;\nusing UnityEngine;\nusing System.Collections.Generic;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.State;\nusing TrafficManager.Manager;\nusing TrafficManager.Traffic;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\nusing TrafficManager.Traffic.Data;\n\nnamespace TrafficManager.Custom.PathFinding {\n\tpublic class CustomPathFind : PathFind {\n\t\tprivate struct BufferItem {\n\t\t\tpublic PathUnit.Position m_position;\n\t\t\tpublic float m_comparisonValue;\n\t\t\tpublic float m_methodDistance;\n\t\t\tpublic float m_duration;\n\t\t\tpublic uint m_laneID;\n\t\t\tpublic NetInfo.Direction m_direction;\n\t\t\tpublic NetInfo.LaneType m_lanesUsed;\n\t\t\tpublic VehicleInfo.VehicleType m_vehiclesUsed;\n\t\t\tpublic float m_trafficRand;\n#if COUNTSEGMENTSTONEXTJUNCTION\n\t\t\tpublic uint m_numSegmentsToNextJunction;\n#endif\n\t\t}\n\n\t\tprivate enum LaneChangingCostCalculationMode {\n\t\t\tNone,\n\t\t\tByLaneDistance,\n\t\t\tByGivenDistance\n\t\t}\n\n\t\tprivate const float SEGMENT_MIN_AVERAGE_LENGTH = 30f;\n\t\tprivate const float LANE_DENSITY_DISCRETIZATION = 25f;\n\t\tprivate const float LANE_USAGE_DISCRETIZATION = 25f;\n\t\tprivate const float BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR = 0.003921569f;\n\n\t\t//Expose the private fields\n\t\tFieldInfo _fieldpathUnits;\n\t\tFieldInfo _fieldQueueFirst;\n\t\tFieldInfo _fieldQueueLast;\n\t\tFieldInfo _fieldQueueLock;\n\t\tFieldInfo _fieldCalculating;\n\t\tFieldInfo _fieldTerminated;\n\t\tFieldInfo _fieldPathFindThread;\n\n\t\tprivate Array32<PathUnit> PathUnits {\n\t\t\tget { return _fieldpathUnits.GetValue(this) as Array32<PathUnit>; }\n\t\t\tset { _fieldpathUnits.SetValue(this, value); }\n\t\t}\n\n\t\tprivate uint QueueFirst {\n\t\t\tget { return (uint)_fieldQueueFirst.GetValue(this); }\n\t\t\tset { _fieldQueueFirst.SetValue(this, value); }\n\t\t}\n\n\t\tprivate uint QueueLast {\n\t\t\tget { return (uint)_fieldQueueLast.GetValue(this); }\n\t\t\tset { _fieldQueueLast.SetValue(this, value); }\n\t\t}\n\n\t\tprivate uint Calculating {\n\t\t\tget { return (uint)_fieldCalculating.GetValue(this); }\n\t\t\tset { _fieldCalculating.SetValue(this, value); }\n\t\t}\n\n\t\tprivate object QueueLock {\n\t\t\tget { return _fieldQueueLock.GetValue(this); }\n\t\t\tset { _fieldQueueLock.SetValue(this, value); }\n\t\t}\n\n\t\tprivate object _bufferLock;\n\t\tinternal Thread CustomPathFindThread {\n\t\t\tget { return (Thread)_fieldPathFindThread.GetValue(this); }\n\t\t\tset { _fieldPathFindThread.SetValue(this, value); }\n\t\t}\n\n\t\tprivate bool Terminated {\n\t\t\tget { return (bool)_fieldTerminated.GetValue(this); }\n\t\t\tset { _fieldTerminated.SetValue(this, value); }\n\t\t}\n\t\tprivate int m_bufferMinPos;\n\t\tprivate int m_bufferMaxPos;\n\t\tprivate uint[] m_laneLocation;\n\t\tprivate PathUnit.Position[] m_laneTarget;\n\t\tprivate BufferItem[] m_buffer;\n\t\tprivate int[] m_bufferMin;\n\t\tprivate int[] m_bufferMax;\n\t\tprivate float m_maxLength;\n\t\tprivate uint m_startLaneA;\n\t\tprivate uint m_startLaneB;\n\t\tprivate ushort m_startSegmentA;\n\t\tprivate ushort m_startSegmentB;\n\t\tprivate uint m_endLaneA;\n\t\tprivate uint m_endLaneB;\n\t\tprivate uint m_vehicleLane;\n\t\tprivate byte m_startOffsetA;\n\t\tprivate byte m_startOffsetB;\n\t\tprivate byte m_vehicleOffset;\n\t\tprivate NetSegment.Flags m_carBanMask;\n\t\tprivate bool m_isHeavyVehicle;\n\t\tprivate bool m_ignoreBlocked;\n\t\tprivate bool m_stablePath;\n\t\tprivate bool m_randomParking;\n\t\tprivate bool m_transportVehicle;\n\t\tprivate bool m_ignoreCost;\n\t\tprivate PathUnitQueueItem queueItem;\n\t\tprivate NetSegment.Flags m_disableMask;\n\t\t/*private ExtVehicleType? _extVehicleType;\n\t\tprivate ushort? _vehicleId;\n\t\tprivate ExtCitizenInstance.ExtPathType? _extPathType;*/\n\t\tprivate bool m_isRoadVehicle;\n\t\tprivate bool m_isLaneArrowObeyingEntity;\n\t\tprivate bool m_isLaneConnectionObeyingEntity;\n\t\tprivate bool m_leftHandDrive;\n\t\t//private float _speedRand;\n\t\t//private bool _extPublicTransport;\n\t\t//private static ushort laneChangeRandCounter = 0;\n#if DEBUG\n\t\tpublic uint m_failedPathFinds = 0;\n\t\tpublic uint m_succeededPathFinds = 0;\n\t\tprivate bool m_debug = false;\n\t\tprivate IDictionary<ushort, IList<ushort>> m_debugPositions = null;\n#endif\n\t\tpublic int pfId = 0;\n\t\tprivate Randomizer m_pathRandomizer;\n\t\tprivate uint m_pathFindIndex;\n\t\tprivate NetInfo.LaneType m_laneTypes;\n\t\tprivate VehicleInfo.VehicleType m_vehicleTypes;\n\n\t\tprivate GlobalConfig m_conf = null;\n\n\t\tprivate static readonly CustomSegmentLightsManager customTrafficLightsManager = CustomSegmentLightsManager.Instance;\n\t\tprivate static readonly JunctionRestrictionsManager junctionManager = JunctionRestrictionsManager.Instance;\n\t\tprivate static readonly VehicleRestrictionsManager vehicleRestrictionsManager = VehicleRestrictionsManager.Instance;\n\t\tprivate static readonly SpeedLimitManager speedLimitManager = SpeedLimitManager.Instance;\n\t\tprivate static readonly TrafficMeasurementManager trafficMeasurementManager = TrafficMeasurementManager.Instance;\n\t\tprivate static readonly RoutingManager routingManager = RoutingManager.Instance;\n\n\t\tpublic bool IsMasterPathFind = false;\n\n\t\tprotected virtual void Awake() {\n#if DEBUG\n\t\t\tLog._Debug($\"CustomPathFind.Awake called.\");\n#endif\n\n\t\t\tvar stockPathFindType = typeof(PathFind);\n\t\t\tconst BindingFlags fieldFlags = BindingFlags.NonPublic | BindingFlags.Instance;\n\n\t\t\t_fieldpathUnits = stockPathFindType.GetField(\"m_pathUnits\", fieldFlags);\n\t\t\t_fieldQueueFirst = stockPathFindType.GetField(\"m_queueFirst\", fieldFlags);\n\t\t\t_fieldQueueLast = stockPathFindType.GetField(\"m_queueLast\", fieldFlags);\n\t\t\t_fieldQueueLock = stockPathFindType.GetField(\"m_queueLock\", fieldFlags);\n\t\t\t_fieldTerminated = stockPathFindType.GetField(\"m_terminated\", fieldFlags);\n\t\t\t_fieldCalculating = stockPathFindType.GetField(\"m_calculating\", fieldFlags);\n\t\t\t_fieldPathFindThread = stockPathFindType.GetField(\"m_pathFindThread\", fieldFlags);\n\n\t\t\tm_buffer = new BufferItem[65536]; // 2^16\n\t\t\t_bufferLock = PathManager.instance.m_bufferLock;\n\t\t\tPathUnits = PathManager.instance.m_pathUnits;\n#if DEBUG\n\t\t\tif (QueueLock == null) {\n\t\t\t\tLog._Debug($\"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.Awake: QueueLock is null. Creating.\");\n\t\t\t\tQueueLock = new object();\n\t\t\t} else {\n\t\t\t\tLog._Debug($\"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.Awake: QueueLock is NOT null.\");\n\t\t\t}\n#else\n\t\t\tQueueLock = new object();\n#endif\n\t\t\tm_laneLocation = new uint[262144]; // 2^18\n\t\t\tm_laneTarget = new PathUnit.Position[262144]; // 2^18\n\t\t\tm_bufferMin = new int[1024]; // 2^10\n\t\t\tm_bufferMax = new int[1024]; // 2^10\n\n\t\t\tm_pathfindProfiler = new ThreadProfiler();\n\t\t\tCustomPathFindThread = new Thread(PathFindThread) { Name = \"Pathfind\" };\n\t\t\tCustomPathFindThread.Priority = SimulationManager.SIMULATION_PRIORITY;\n\t\t\tCustomPathFindThread.Start();\n\t\t\tif (!CustomPathFindThread.IsAlive) {\n\t\t\t\t//CODebugBase<LogChannel>.Error(LogChannel.Core, \"Path find thread failed to start!\");\n\t\t\t\tLog.Error($\"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) Path find thread failed to start!\");\n\t\t\t}\n\n\t\t}\n\n\t\tprotected virtual void OnDestroy() {\n#if DEBUGLOCKS\n\t\t\tuint lockIter = 0;\n#endif\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(QueueLock);\n\t\t\t\tTerminated = true;\n\t\t\t\tMonitor.PulseAll(QueueLock);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error(\"CustomPathFind.OnDestroy Error: \" + e.ToString());\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(QueueLock);\n\t\t\t}\n\t\t}\n\n\t\tpublic new bool CalculatePath(uint unit, bool skipQueue) {\n\t\t\treturn ExtCalculatePath(unit, skipQueue);\n\t\t}\n\n\t\tpublic bool ExtCalculatePath(uint unit, bool skipQueue) {\n\t\t\tif (CustomPathManager._instance.AddPathReference(unit)) {\n\t\t\t\ttry {\n\t\t\t\t\tMonitor.Enter(QueueLock);\n\n\t\t\t\t\tif (skipQueue) {\n\n\t\t\t\t\t\tif (this.QueueLast == 0u) {\n\t\t\t\t\t\t\tthis.QueueLast = unit;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tCustomPathManager._instance.queueItems[unit].nextPathUnitId = QueueFirst;\n\t\t\t\t\t\t\t//this.PathUnits.m_buffer[unit].m_nextPathUnit = this.QueueFirst;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.QueueFirst = unit;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (this.QueueLast == 0u) {\n\t\t\t\t\t\t\tthis.QueueFirst = unit;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tCustomPathManager._instance.queueItems[QueueLast].nextPathUnitId = unit;\n\t\t\t\t\t\t\t//this.PathUnits.m_buffer[this.QueueLast].m_nextPathUnit = unit;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.QueueLast = unit;\n\t\t\t\t\t}\n\t\t\t\t\tthis.PathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_CREATED;\n\t\t\t\t\t++this.m_queuedPathFindCount;\n\t\t\t\t\t\n\t\t\t\t\tMonitor.Pulse(this.QueueLock);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.CalculatePath({unit}, {skipQueue}): Error: {e.ToString()}\");\n\t\t\t\t} finally {\n\t\t\t\t\tMonitor.Exit(this.QueueLock);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// PathFind\n\t\tprotected void PathFindImplementation(uint unit, ref PathUnit data) {\n\t\t\tm_conf = GlobalConfig.Instance; // NON-STOCK CODE\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tthis.m_laneTypes = (NetInfo.LaneType)this.PathUnits.m_buffer[unit].m_laneTypes;\n\t\t\tthis.m_vehicleTypes = (VehicleInfo.VehicleType)this.PathUnits.m_buffer[unit].m_vehicleTypes;\n\t\t\tthis.m_maxLength = this.PathUnits.m_buffer[unit].m_length;\n\t\t\tthis.m_pathFindIndex = (this.m_pathFindIndex + 1u & 32767u);\n\t\t\tthis.m_pathRandomizer = new Randomizer(unit);\n\n\t\t\tthis.m_carBanMask = NetSegment.Flags.CarBan;\n\t\t\tthis.m_isHeavyVehicle = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 16) != 0);\n\t\t\tif (m_isHeavyVehicle) {\n\t\t\t\tthis.m_carBanMask |= NetSegment.Flags.HeavyBan;\n\t\t\t}\n\t\t\tif ((this.PathUnits.m_buffer[unit].m_simulationFlags & 4) != 0) {\n\t\t\t\tthis.m_carBanMask |= NetSegment.Flags.WaitingPath;\n\t\t\t}\n\t\t\tthis.m_ignoreBlocked = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 32) != 0);\n\t\t\tthis.m_stablePath = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 64) != 0);\n\t\t\tthis.m_randomParking = ((this.PathUnits.m_buffer[unit].m_simulationFlags & 128) != 0);\n\t\t\tthis.m_transportVehicle = ((byte)(this.m_laneTypes & NetInfo.LaneType.TransportVehicle) != 0);\n\t\t\tthis.m_ignoreCost = (this.m_stablePath || (this.PathUnits.m_buffer[unit].m_simulationFlags & 8) != 0);\n\t\t\tthis.m_disableMask = (NetSegment.Flags.Collapsed | NetSegment.Flags.PathFailed);\n\t\t\tif ((this.PathUnits.m_buffer[unit].m_simulationFlags & 2) == 0) {\n\t\t\t\tthis.m_disableMask |= NetSegment.Flags.Flooded;\n\t\t\t}\n\t\t\t//this._speedRand = 0;\n\t\t\tthis.m_leftHandDrive = Constants.ServiceFactory.SimulationService.LeftHandDrive;\n\t\t\tthis.m_isRoadVehicle = (queueItem.vehicleType & ExtVehicleType.RoadVehicle) != ExtVehicleType.None;\n\t\t\tthis.m_isLaneArrowObeyingEntity = (m_vehicleTypes & LaneArrowManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None &&\n\t\t\t\t\t\t\t\t\t\t(queueItem.vehicleType & LaneArrowManager.EXT_VEHICLE_TYPES) != ExtVehicleType.None;\n\t\t\tthis.m_isLaneConnectionObeyingEntity = (m_vehicleTypes & LaneConnectionManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t(queueItem.vehicleType & LaneConnectionManager.EXT_VEHICLE_TYPES) != ExtVehicleType.None;\n#if DEBUGNEWPF && DEBUG\n\t\t\tbool debug = this.m_debug = m_conf.Debug.Switches[0] &&\n\t\t\t\t((m_conf.Debug.ExtVehicleType == ExtVehicleType.None && queueItem.vehicleType == ExtVehicleType.None) || (queueItem.vehicleType & m_conf.Debug.ExtVehicleType) != ExtVehicleType.None) &&\n\t\t\t\t(m_conf.Debug.StartSegmentId == 0 || data.m_position00.m_segment == m_conf.Debug.StartSegmentId || data.m_position02.m_segment == m_conf.Debug.StartSegmentId) &&\n\t\t\t\t(m_conf.Debug.EndSegmentId == 0 || data.m_position01.m_segment == m_conf.Debug.EndSegmentId || data.m_position03.m_segment == m_conf.Debug.EndSegmentId) &&\n\t\t\t\t(m_conf.Debug.VehicleId == 0 || queueItem.vehicleId == m_conf.Debug.VehicleId)\n\t\t\t\t;\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"CustomPathFind.PathFindImplementation: START calculating path unit {unit}, type {queueItem.vehicleType}\");\n\t\t\t\tm_debugPositions = new Dictionary<ushort, IList<ushort>>();\n\t\t\t}\n#endif\n\n\t\t\tif ((byte)(this.m_laneTypes & NetInfo.LaneType.Vehicle) != 0) {\n\t\t\t\tthis.m_laneTypes |= NetInfo.LaneType.TransportVehicle;\n\t\t\t}\n\t\t\tint posCount = (int)(this.PathUnits.m_buffer[unit].m_positionCount & 15);\n\t\t\tint vehiclePosIndicator = this.PathUnits.m_buffer[unit].m_positionCount >> 4;\n\t\t\tBufferItem bufferItemStartA;\n\t\t\tif (data.m_position00.m_segment != 0 && posCount >= 1) {\n\t\t\t\tthis.m_startLaneA = PathManager.GetLaneID(data.m_position00);\n\t\t\t\tthis.m_startSegmentA = data.m_position00.m_segment; // NON-STOCK CODE\n\t\t\t\tthis.m_startOffsetA = data.m_position00.m_offset;\n\t\t\t\tbufferItemStartA.m_laneID = this.m_startLaneA;\n\t\t\t\tbufferItemStartA.m_position = data.m_position00;\n\t\t\t\tthis.GetLaneDirection(data.m_position00, out bufferItemStartA.m_direction, out bufferItemStartA.m_lanesUsed, out bufferItemStartA.m_vehiclesUsed);\n\t\t\t\tbufferItemStartA.m_comparisonValue = 0f;\n\t\t\t\tbufferItemStartA.m_duration = 0f;\n#if COUNTSEGMENTSTONEXTJUNCTION\n\t\t\t\tbufferItemStartA.m_numSegmentsToNextJunction = 0;\n#endif\n\t\t\t} else {\n\t\t\t\tthis.m_startLaneA = 0u;\n\t\t\t\tthis.m_startSegmentA = 0; // NON-STOCK CODE\n\t\t\t\tthis.m_startOffsetA = 0;\n\t\t\t\tbufferItemStartA = default(BufferItem);\n\t\t\t}\n\t\t\tBufferItem bufferItemStartB;\n\t\t\tif (data.m_position02.m_segment != 0 && posCount >= 3) {\n\t\t\t\tthis.m_startLaneB = PathManager.GetLaneID(data.m_position02);\n\t\t\t\tthis.m_startSegmentB = data.m_position02.m_segment; // NON-STOCK CODE\n\t\t\t\tthis.m_startOffsetB = data.m_position02.m_offset;\n\t\t\t\tbufferItemStartB.m_laneID = this.m_startLaneB;\n\t\t\t\tbufferItemStartB.m_position = data.m_position02;\n\t\t\t\tthis.GetLaneDirection(data.m_position02, out bufferItemStartB.m_direction, out bufferItemStartB.m_lanesUsed, out bufferItemStartB.m_vehiclesUsed);\n\t\t\t\tbufferItemStartB.m_comparisonValue = 0f;\n\t\t\t\tbufferItemStartB.m_duration = 0f;\n#if COUNTSEGMENTSTONEXTJUNCTION\n\t\t\t\tbufferItemStartB.m_numSegmentsToNextJunction = 0;\n#endif\n\t\t\t} else {\n\t\t\t\tthis.m_startLaneB = 0u;\n\t\t\t\tthis.m_startSegmentB = 0; // NON-STOCK CODE\n\t\t\t\tthis.m_startOffsetB = 0;\n\t\t\t\tbufferItemStartB = default(BufferItem);\n\t\t\t}\n\t\t\tBufferItem bufferItemEndA;\n\t\t\tif (data.m_position01.m_segment != 0 && posCount >= 2) {\n\t\t\t\tthis.m_endLaneA = PathManager.GetLaneID(data.m_position01);\n\t\t\t\tbufferItemEndA.m_laneID = this.m_endLaneA;\n\t\t\t\tbufferItemEndA.m_position = data.m_position01;\n\t\t\t\tthis.GetLaneDirection(data.m_position01, out bufferItemEndA.m_direction, out bufferItemEndA.m_lanesUsed, out bufferItemEndA.m_vehiclesUsed);\n\t\t\t\tbufferItemEndA.m_methodDistance = 0.01f;\n\t\t\t\tbufferItemEndA.m_comparisonValue = 0f;\n\t\t\t\tbufferItemEndA.m_duration = 0f;\n\t\t\t\tbufferItemEndA.m_trafficRand = 0; // NON-STOCK CODE\n#if COUNTSEGMENTSTONEXTJUNCTION\n\t\t\t\tbufferItemEndA.m_numSegmentsToNextJunction = 0;\n#endif\n\t\t\t} else {\n\t\t\t\tthis.m_endLaneA = 0u;\n\t\t\t\tbufferItemEndA = default(BufferItem);\n\t\t\t}\n\t\t\tBufferItem bufferItemEndB;\n\t\t\tif (data.m_position03.m_segment != 0 && posCount >= 4) {\n\t\t\t\tthis.m_endLaneB = PathManager.GetLaneID(data.m_position03);\n\t\t\t\tbufferItemEndB.m_laneID = this.m_endLaneB;\n\t\t\t\tbufferItemEndB.m_position = data.m_position03;\n\t\t\t\tthis.GetLaneDirection(data.m_position03, out bufferItemEndB.m_direction, out bufferItemEndB.m_lanesUsed, out bufferItemEndB.m_vehiclesUsed);\n\t\t\t\tbufferItemEndB.m_methodDistance = 0.01f;\n\t\t\t\tbufferItemEndB.m_comparisonValue = 0f;\n\t\t\t\tbufferItemEndB.m_duration = 0f;\n\t\t\t\tbufferItemEndB.m_trafficRand = 0; // NON-STOCK CODE\n#if COUNTSEGMENTSTONEXTJUNCTION\n\t\t\t\tbufferItemEndB.m_numSegmentsToNextJunction = 0;\n#endif\n\t\t\t} else {\n\t\t\t\tthis.m_endLaneB = 0u;\n\t\t\t\tbufferItemEndB = default(BufferItem);\n\t\t\t}\n\t\t\tif (data.m_position11.m_segment != 0 && vehiclePosIndicator >= 1) {\n\t\t\t\tthis.m_vehicleLane = PathManager.GetLaneID(data.m_position11);\n\t\t\t\tthis.m_vehicleOffset = data.m_position11.m_offset;\n\t\t\t} else {\n\t\t\t\tthis.m_vehicleLane = 0u;\n\t\t\t\tthis.m_vehicleOffset = 0;\n\t\t\t}\n#if DEBUGNEWPF && DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"CustomPathFind.PathFindImplementation: Preparing calculating path unit {unit}, type {queueItem.vehicleType}:\\n\" +\n\t\t\t\t\t$\"\\tbufferItemStartA: segment={bufferItemStartA.m_position.m_segment} lane={bufferItemStartA.m_position.m_lane} off={bufferItemStartA.m_position.m_offset} laneId={bufferItemStartA.m_laneID}\\n\" +\n\t\t\t\t\t$\"\\tbufferItemStartB: segment={bufferItemStartB.m_position.m_segment} lane={bufferItemStartB.m_position.m_lane} off={bufferItemStartB.m_position.m_offset} laneId={bufferItemStartB.m_laneID}\\n\" +\n\t\t\t\t\t$\"\\tbufferItemEndA: segment={bufferItemEndA.m_position.m_segment} lane={bufferItemEndA.m_position.m_lane} off={bufferItemEndA.m_position.m_offset} laneId={bufferItemEndA.m_laneID}\\n\" +\n\t\t\t\t\t$\"\\tbufferItemEndB: segment={bufferItemEndB.m_position.m_segment} lane={bufferItemEndB.m_position.m_lane} off={bufferItemEndB.m_position.m_offset} laneId={bufferItemEndB.m_laneID}\\n\" +\n\t\t\t\t\t$\"\\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\"\n\t\t\t\t\t);\n\t\t\t}\n#endif\n\t\t\tBufferItem finalBufferItem = default(BufferItem);\n\t\t\tbyte startOffset = 0;\n\t\t\tthis.m_bufferMinPos = 0;\n\t\t\tthis.m_bufferMaxPos = -1;\n\t\t\tif (this.m_pathFindIndex == 0u) {\n\t\t\t\tuint maxUInt = 4294901760u;\n\t\t\t\tfor (int i = 0; i < 262144; ++i) {\n\t\t\t\t\tthis.m_laneLocation[i] = maxUInt;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (int j = 0; j < 1024; ++j) {\n\t\t\t\tthis.m_bufferMin[j] = 0;\n\t\t\t\tthis.m_bufferMax[j] = -1;\n\t\t\t}\n\t\t\tif (bufferItemEndA.m_position.m_segment != 0) {\n\t\t\t\t++this.m_bufferMax[0];\n\t\t\t\tthis.m_buffer[++this.m_bufferMaxPos] = bufferItemEndA;\n\t\t\t}\n\t\t\tif (bufferItemEndB.m_position.m_segment != 0) {\n\t\t\t\t++this.m_bufferMax[0];\n\t\t\t\tthis.m_buffer[++this.m_bufferMaxPos] = bufferItemEndB;\n\t\t\t}\n\t\t\tbool canFindPath = false;\n\n\t\t\twhile (this.m_bufferMinPos <= this.m_bufferMaxPos) {\n\t\t\t\tint bufMin = this.m_bufferMin[this.m_bufferMinPos];\n\t\t\t\tint bufMax = this.m_bufferMax[this.m_bufferMinPos];\n\t\t\t\tif (bufMin > bufMax) {\n\t\t\t\t\t++this.m_bufferMinPos;\n\t\t\t\t} else {\n\t\t\t\t\tthis.m_bufferMin[this.m_bufferMinPos] = bufMin + 1;\n\t\t\t\t\tBufferItem candidateItem = this.m_buffer[(this.m_bufferMinPos << 6) + bufMin];\n\t\t\t\t\tif (candidateItem.m_position.m_segment == bufferItemStartA.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartA.m_position.m_lane) {\n\t\t\t\t\t\t// we reached startA\n\t\t\t\t\t\tif ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0 && candidateItem.m_position.m_offset >= this.m_startOffsetA) {\n\t\t\t\t\t\t\tfinalBufferItem = candidateItem;\n\t\t\t\t\t\t\tstartOffset = this.m_startOffsetA;\n\t\t\t\t\t\t\tcanFindPath = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0 && candidateItem.m_position.m_offset <= this.m_startOffsetA) {\n\t\t\t\t\t\t\tfinalBufferItem = candidateItem;\n\t\t\t\t\t\t\tstartOffset = this.m_startOffsetA;\n\t\t\t\t\t\t\tcanFindPath = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (candidateItem.m_position.m_segment == bufferItemStartB.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartB.m_position.m_lane) {\n\t\t\t\t\t\t// we reached startB\n\t\t\t\t\t\tif ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0 && candidateItem.m_position.m_offset >= this.m_startOffsetB) {\n\t\t\t\t\t\t\tfinalBufferItem = candidateItem;\n\t\t\t\t\t\t\tstartOffset = this.m_startOffsetB;\n\t\t\t\t\t\t\tcanFindPath = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0 && candidateItem.m_position.m_offset <= this.m_startOffsetB) {\n\t\t\t\t\t\t\tfinalBufferItem = candidateItem;\n\t\t\t\t\t\t\tstartOffset = this.m_startOffsetB;\n\t\t\t\t\t\t\tcanFindPath = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// explore the path\n\t\t\t\t\tif ((byte)(candidateItem.m_direction & NetInfo.Direction.Forward) != 0) {\n\t\t\t\t\t\tushort startNode = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode;\n\t\t\t\t\t\tuint laneRoutingIndex = routingManager.GetLaneEndRoutingIndex(candidateItem.m_laneID, true);\n\t\t\t\t\t\tthis.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);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((byte)(candidateItem.m_direction & NetInfo.Direction.Backward) != 0) {\n\t\t\t\t\t\tushort endNode = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode;\n\t\t\t\t\t\tuint laneRoutingIndex = routingManager.GetLaneEndRoutingIndex(candidateItem.m_laneID, false);\n\t\t\t\t\t\tthis.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);\n\t\t\t\t\t}\n\n\t\t\t\t\t// handle special nodes (e.g. bus stops)\n\t\t\t\t\tint num6 = 0;\n\t\t\t\t\tushort specialNodeId = netManager.m_lanes.m_buffer[candidateItem.m_laneID].m_nodes;\n\t\t\t\t\tif (specialNodeId != 0) {\n\t\t\t\t\t\tushort startNode2 = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode;\n\t\t\t\t\t\tushort endNode2 = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode;\n\t\t\t\t\t\tbool nodesDisabled = ((netManager.m_nodes.m_buffer[startNode2].m_flags | netManager.m_nodes.m_buffer[endNode2].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None;\n\t\t\t\t\t\twhile (specialNodeId != 0) {\n\t\t\t\t\t\t\tNetInfo.Direction direction = NetInfo.Direction.None;\n\t\t\t\t\t\t\tbyte laneOffset = netManager.m_nodes.m_buffer[specialNodeId].m_laneOffset;\n\t\t\t\t\t\t\tif (laneOffset <= candidateItem.m_position.m_offset) {\n\t\t\t\t\t\t\t\tdirection |= NetInfo.Direction.Forward;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (laneOffset >= candidateItem.m_position.m_offset) {\n\t\t\t\t\t\t\t\tdirection |= NetInfo.Direction.Backward;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif ((byte)(candidateItem.m_direction & direction) != 0 && (!nodesDisabled || (netManager.m_nodes.m_buffer[specialNodeId].m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None)) {\n#if DEBUGNEWPF && DEBUG\n\t\t\t\t\t\t\t\tif (debug && (m_conf.Debug.NodeId <= 0 || specialNodeId == m_conf.Debug.NodeId)) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"CustomPathFind.PathFindImplementation: Handling special node for path unit {unit}, type {queueItem.vehicleType}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"\\tcandidateItem.m_position.m_segment={candidateItem.m_position.m_segment}\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"\\tcandidateItem.m_position.m_lane={candidateItem.m_position.m_lane}\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"\\tcandidateItem.m_laneID={candidateItem.m_laneID}\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"\\tspecialNodeId={specialNodeId}\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"\\tstartNode2={startNode2}\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"\\tendNode2={endNode2}\\n\"\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\tthis.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);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tspecialNodeId = netManager.m_nodes.m_buffer[specialNodeId].m_nextLaneNode;\n\t\t\t\t\t\t\tif (++num6 == 32768) {\n\t\t\t\t\t\t\t\tLog.Warning(\"Special loop: Too many iterations\");\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!canFindPath) {\n\t\t\t\t// we could not find a path\n\t\t\t\tPathUnits.m_buffer[(int)unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;\n#if DEBUG\n\t\t\t\t++m_failedPathFinds;\n\n#if DEBUGNEWPF\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Could not find path for unit {unit} -- path-finding failed during process\");\n\t\t\t\t\tstring reachableBuf = \"\";\n\t\t\t\t\tstring unreachableBuf = \"\";\n\t\t\t\t\tforeach (KeyValuePair<ushort, IList<ushort>> e in m_debugPositions) {\n\t\t\t\t\t\tstring buf = $\"{e.Key} -> {e.Value.CollectionToString()}\\n\";\n\t\t\t\t\t\tif (e.Value.Count <= 0) {\n\t\t\t\t\t\t\tunreachableBuf += buf;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treachableBuf += buf;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tLog._Debug($\"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Reachability graph for unit {unit}:\\n== REACHABLE ==\\n\" + reachableBuf + \"\\n== UNREACHABLE ==\\n\" + unreachableBuf);\n\t\t\t\t}\n#endif\n#endif\n\t\t\t\t//CustomPathManager._instance.ResetQueueItem(unit);\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// we could calculate a valid path\n\n\t\t\tfloat duration = (this.m_laneTypes != NetInfo.LaneType.Pedestrian) ? finalBufferItem.m_duration : finalBufferItem.m_methodDistance;\n\t\t\tthis.PathUnits.m_buffer[unit].m_length = duration;\n\t\t\tthis.PathUnits.m_buffer[unit].m_laneTypes = (byte)finalBufferItem.m_lanesUsed; // NON-STOCK CODE\n\t\t\tthis.PathUnits.m_buffer[unit].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed; // NON-STOCK CODE\n#if DEBUG\n\t\t\t/*if (_conf.Debug.Switches[4])\n\t\t\t\tLog._Debug($\"Lane/Vehicle types of path unit {unit}: {finalBufferItem.m_lanesUsed} / {finalBufferItem.m_vehiclesUsed}\");*/\n#endif\n\t\t\tuint currentPathUnitId = unit;\n\t\t\tint currentItemPositionCount = 0;\n\t\t\tint sumOfPositionCounts = 0;\n\t\t\tPathUnit.Position currentPosition = finalBufferItem.m_position;\n\t\t\tif ((currentPosition.m_segment != bufferItemEndA.m_position.m_segment || currentPosition.m_lane != bufferItemEndA.m_position.m_lane || currentPosition.m_offset != bufferItemEndA.m_position.m_offset) &&\n\t\t\t\t(currentPosition.m_segment != bufferItemEndB.m_position.m_segment || currentPosition.m_lane != bufferItemEndB.m_position.m_lane || currentPosition.m_offset != bufferItemEndB.m_position.m_offset)) {\n\t\t\t\t// the found starting position differs from the desired end position\n\t\t\t\tif (startOffset != currentPosition.m_offset) {\n\t\t\t\t\t// the offsets differ: copy the found starting position and modify the offset to fit the desired offset\n\t\t\t\t\tPathUnit.Position position2 = currentPosition;\n\t\t\t\t\tposition2.m_offset = startOffset;\n\t\t\t\t\tthis.PathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, position2);\n\t\t\t\t\t// now we have: [desired starting position]\n\t\t\t\t}\n\t\t\t\t// add the found starting position to the path unit\n\t\t\t\tthis.PathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition);\n\t\t\t\tcurrentPosition = this.m_laneTarget[finalBufferItem.m_laneID]; // go to the next path position\n\n\t\t\t\t// now we have either [desired starting position, found starting position] or [found starting position], depending on if the found starting position matched the desired\n\t\t\t}\n\n\t\t\t// beginning with the starting position, going to the target position: assemble the path units\n\t\t\tfor (int k = 0; k < 262144; ++k) {\n\t\t\t\t//pfCurrentState = 6;\n\t\t\t\tthis.PathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition); // add the next path position to the current unit\n\n\t\t\t\tif ((currentPosition.m_segment == bufferItemEndA.m_position.m_segment && currentPosition.m_lane == bufferItemEndA.m_position.m_lane && currentPosition.m_offset == bufferItemEndA.m_position.m_offset) ||\n\t\t\t\t\t(currentPosition.m_segment == bufferItemEndB.m_position.m_segment && currentPosition.m_lane == bufferItemEndB.m_position.m_lane && currentPosition.m_offset == bufferItemEndB.m_position.m_offset)) {\n\t\t\t\t\t// we have reached the end position\n\n\t\t\t\t\tthis.PathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;\n\t\t\t\t\tsumOfPositionCounts += currentItemPositionCount; // add position count of last unit to sum\n\t\t\t\t\tif (sumOfPositionCounts != 0) {\n\t\t\t\t\t\t// for each path unit from start to target: calculate length (distance) to target\n\t\t\t\t\t\tcurrentPathUnitId = 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)\n\t\t\t\t\t\tcurrentItemPositionCount = (int)this.PathUnits.m_buffer[unit].m_positionCount;\n\t\t\t\t\t\tint totalIter = 0;\n\t\t\t\t\t\twhile (currentPathUnitId != 0u) {\n\t\t\t\t\t\t\tthis.PathUnits.m_buffer[currentPathUnitId].m_length = duration * (float)(sumOfPositionCounts - currentItemPositionCount) / (float)sumOfPositionCounts;\n\t\t\t\t\t\t\tcurrentItemPositionCount += (int)this.PathUnits.m_buffer[currentPathUnitId].m_positionCount;\n\t\t\t\t\t\t\tcurrentPathUnitId = this.PathUnits.m_buffer[currentPathUnitId].m_nextPathUnit;\n\t\t\t\t\t\t\tif (++totalIter >= 262144) {\n#if DEBUG\n\t\t\t\t\t\t\t\tLog.Error(\"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: PathFindImplementation: Invalid list detected.\");\n#endif\n\t\t\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#if DEBUG\n\t\t\t\t\t//Log._Debug($\"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Path found (pfCurrentState={pfCurrentState}) for unit {unit}\");\n#endif\n\t\t\t\t\tPathUnits.m_buffer[(int)unit].m_pathFindFlags |= PathUnit.FLAG_READY; // Path found\n#if DEBUG\n\t\t\t\t\t++m_succeededPathFinds;\n\n#if DEBUGNEWPF\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Path-find succeeded for unit {unit}\");\n#endif\n#endif\n\t\t\t\t\t//CustomPathManager._instance.ResetQueueItem(unit);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// We have not reached the target position yet \n\t\t\t\tif (currentItemPositionCount == 12) {\n\t\t\t\t\t// the current path unit is full, we need a new one\n\t\t\t\t\tuint createdPathUnitId;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tMonitor.Enter(_bufferLock);\n\t\t\t\t\t\tif (!this.PathUnits.CreateItem(out createdPathUnitId, ref this.m_pathRandomizer)) {\n\t\t\t\t\t\t\t// we failed to create a new path unit, thus the path-finding also failed\n\t\t\t\t\t\t\tPathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;\n#if DEBUG\n\t\t\t\t\t\t\t++m_failedPathFinds;\n\n#if DEBUGNEWPF\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Could not find path for unit {unit} -- Could not create path unit\");\n#endif\n#endif\n\t\t\t\t\t\t\t//CustomPathManager._instance.ResetQueueItem(unit);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.PathUnits.m_buffer[createdPathUnitId] = this.PathUnits.m_buffer[(int)currentPathUnitId];\n\t\t\t\t\t\tthis.PathUnits.m_buffer[createdPathUnitId].m_referenceCount = 1;\n\t\t\t\t\t\tthis.PathUnits.m_buffer[createdPathUnitId].m_pathFindFlags = PathUnit.FLAG_READY;\n\t\t\t\t\t\tthis.PathUnits.m_buffer[currentPathUnitId].m_nextPathUnit = createdPathUnitId;\n\t\t\t\t\t\tthis.PathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;\n\t\t\t\t\t\tthis.PathUnits.m_buffer[currentPathUnitId].m_laneTypes = (byte)finalBufferItem.m_lanesUsed; // NON-STOCK CODE (this is not accurate!)\n\t\t\t\t\t\tthis.PathUnits.m_buffer[currentPathUnitId].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed; // NON-STOCK CODE (this is not accurate!)\n\t\t\t\t\t\tsumOfPositionCounts += currentItemPositionCount;\n\t\t\t\t\t\tSingleton<PathManager>.instance.m_pathUnitCount = (int)(this.PathUnits.ItemCount() - 1u);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLog.Error($\"(PF #{m_pathFindIndex}, T#{Thread.CurrentThread.ManagedThreadId}, Id #{pfId}) CustomPathFind.PathFindImplementation Error: {e.ToString()}\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tMonitor.Exit(this._bufferLock);\n\t\t\t\t\t}\n\t\t\t\t\tcurrentPathUnitId = createdPathUnitId;\n\t\t\t\t\tcurrentItemPositionCount = 0;\n\t\t\t\t}\n\n\t\t\t\tuint laneID = PathManager.GetLaneID(currentPosition);\n#if PFTRAFFICSTATS\n\t\t\t\t// NON-STOCK CODE START\n#if MEASUREDENSITY\n\t\t\t\tif (!Options.isStockLaneChangerUsed()) {\n\t\t\t\t\tNetInfo.Lane laneInfo = Singleton<NetManager>.instance.m_segments.m_buffer[currentPosition.m_segment].Info.m_lanes[currentPosition.m_lane];\n\t\t\t\t\tif ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None)\n\t\t\t\t\t\ttrafficMeasurementManager.AddTraffic(currentPosition.m_segment, currentPosition.m_lane, (ushort)(this._isHeavyVehicle || _extVehicleType == ExtVehicleType.Bus ? 75 : 25), null);\n\t\t\t\t}\n#endif\n\t\t\t\tif (!Options.isStockLaneChangerUsed()) {\n\t\t\t\t\tNetInfo.Lane laneInfo = Singleton<NetManager>.instance.m_segments.m_buffer[currentPosition.m_segment].Info.m_lanes[currentPosition.m_lane];\n\t\t\t\t\tif ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\ttrafficMeasurementManager.AddPathFindTraffic(currentPosition.m_segment, currentPosition.m_lane);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// NON-STOCK CODE END\n#endif\n\t\t\t\tcurrentPosition = this.m_laneTarget[laneID];\n\t\t\t}\n\t\t\tPathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;\n#if DEBUG\n\t\t\t++m_failedPathFinds;\n\n#if DEBUGNEWPF\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Could not find path for unit {unit} -- internal error: for loop break\");\n#endif\n#endif\n\t\t\t//CustomPathManager._instance.ResetQueueItem(unit);\n#if DEBUG\n\t\t\t//Log._Debug($\"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this._pathFindIndex}: Cannot find path (pfCurrentState={pfCurrentState}) for unit {unit}\");\n#endif\n\t\t}\n\n\t\t// be aware:\n\t\t//   (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!\n\t\t//   (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.\n\n\t\t// 1\n\t\tprivate void ProcessItemMain(uint unitId, BufferItem item, ref NetSegment prevSegment, SegmentRoutingData prevSegmentRouting, LaneEndRoutingData prevLaneEndRouting, ushort nextNodeId, bool nextIsStartNode, ref NetNode nextNode, byte connectOffset, bool isMiddle) {\n#if DEBUGNEWPF && DEBUG\n\t\t\tbool debug = this.m_debug && (m_conf.Debug.NodeId <= 0 || nextNodeId == m_conf.Debug.NodeId);\n\t\t\tbool debugPed = debug && m_conf.Debug.Switches[12];\n\t\t\tif (debug) {\n\t\t\t\tif (! m_debugPositions.ContainsKey(item.m_position.m_segment)) {\n\t\t\t\t\tm_debugPositions[item.m_position.m_segment] = new List<ushort>();\n\t\t\t\t}\n\t\t\t}\n#else\n\t\t\tbool debug = false;\n\t\t\tbool debugPed = false;\n#endif\n\t\t\t//Log.Message($\"THREAD #{Thread.CurrentThread.ManagedThreadId} Path finder: \" + this._pathFindIndex + \" vehicle types: \" + this._vehicleTypes);\n#if DEBUGNEWPF && DEBUG\n\t\t\t//bool debug = isTransportVehicle && isMiddle && item.m_position.m_segment == 13550;\n\t\t\tList<String> logBuf = null;\n\t\t\tif (debug)\n\t\t\t\tlogBuf = new List<String>();\n#endif\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tbool prevIsPedestrianLane = false;\n\t\t\t//bool prevIsBusLane = false; // non-stock\n\t\t\tbool prevIsBicycleLane = false;\n\t\t\tbool prevIsCenterPlatform = false;\n\t\t\tbool prevIsElevated = false;\n\t\t\tbool prevIsCarLane = false;\n\t\t\tint prevRelSimilarLaneIndex = 0; // inner/outer similar index\n\t\t\t//int prevInnerSimilarLaneIndex = 0; // similar index, starting with 0 at leftmost lane in right hand traffic\n\t\t\tint prevOuterSimilarLaneIndex = 0; // similar index, starting with 0 at rightmost lane in right hand traffic\n\t\t\tNetInfo prevSegmentInfo = prevSegment.Info;\n\t\t\tNetInfo.Lane prevLaneInfo = null;\n\t\t\tbyte prevSimilarLaneCount = 0;\n\t\t\tif ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {\n\t\t\t\tprevLaneInfo = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];\n\t\t\t\tprevIsPedestrianLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian);\n\t\t\t\tprevIsBicycleLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Vehicle && (prevLaneInfo.m_vehicleType & this.m_vehicleTypes) == VehicleInfo.VehicleType.Bicycle);\n\t\t\t\tprevIsCarLane = (prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None && (prevLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None;\n\t\t\t\t//prevIsBusLane = (prevLane.m_laneType == NetInfo.LaneType.TransportVehicle && (prevLane.m_vehicleType & this._vehicleTypes & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None);\n\t\t\t\tprevIsCenterPlatform = prevLaneInfo.m_centerPlatform;\n\t\t\t\tprevIsElevated = prevLaneInfo.m_elevated;\n\t\t\t\tprevSimilarLaneCount = (byte)prevLaneInfo.m_similarLaneCount;\n\t\t\t\t//prevInnerSimilarLaneIndex = RoutingManager.Instance.CalcInnerSimilarLaneIndex(prevLaneInfo);\n\t\t\t\tprevOuterSimilarLaneIndex = RoutingManager.Instance.CalcOuterSimilarLaneIndex(prevLaneInfo);\n\t\t\t\tif ((byte)(prevLaneInfo.m_finalDirection & NetInfo.Direction.Forward) != 0) {\n\t\t\t\t\tprevRelSimilarLaneIndex = prevLaneInfo.m_similarLaneIndex;\n\t\t\t\t} else {\n\t\t\t\t\tprevRelSimilarLaneIndex = prevLaneInfo.m_similarLaneCount - prevLaneInfo.m_similarLaneIndex - 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tint firstPrevSimilarLaneIndexFromInner = prevRelSimilarLaneIndex;\n\t\t\tushort prevSegmentId = item.m_position.m_segment;\n\t\t\tif (isMiddle) {\n\t\t\t\tfor (int i = 0; i < 8; ++i) {\n\t\t\t\t\tushort nextSegmentId = nextNode.GetSegment(i);\n\t\t\t\t\tif (nextSegmentId <= 0)\n\t\t\t\t\t\tcontinue;\n\n#if DEBUGNEWPF\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tthis.ProcessItemCosts(debug, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[(int)nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, !prevIsPedestrianLane, prevIsPedestrianLane, isMiddle);\n\t\t\t\t}\n\t\t\t} else if (prevIsPedestrianLane) {\n\t\t\t\tbool allowPedSwitch = (this.m_laneTypes & NetInfo.LaneType.Pedestrian) != 0;\n\t\t\t\tif (!prevIsElevated) {\n\t\t\t\t\t// explore pedestrian lanes\n\t\t\t\t\tint prevLaneIndex = (int)item.m_position.m_lane;\n\t\t\t\t\tif (nextNode.Info.m_class.m_service != ItemClass.Service.Beautification) {\n\t\t\t\t\t\tif (allowPedSwitch) { // NON-STOCK CODE\n\t\t\t\t\t\t\tbool canCrossStreet = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;\n\t\t\t\t\t\t\tbool isOnCenterPlatform = prevIsCenterPlatform && (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Junction)) == NetNode.Flags.None;\n\t\t\t\t\t\t\tushort nextLeftSegment = prevSegmentId;\n\t\t\t\t\t\t\tushort nextRightSegment = prevSegmentId;\n\t\t\t\t\t\t\tint leftLaneIndex;\n\t\t\t\t\t\t\tint rightLaneIndex;\n\t\t\t\t\t\t\tuint leftLaneId;\n\t\t\t\t\t\t\tuint rightLaneId;\n\t\t\t\t\t\t\tprevSegment.GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, prevLaneIndex, isOnCenterPlatform, out leftLaneIndex, out rightLaneIndex, out leftLaneId, out rightLaneId);\n\t\t\t\t\t\t\tif (leftLaneId == 0u || rightLaneId == 0u) {\n\t\t\t\t\t\t\t\tushort leftSegment;\n\t\t\t\t\t\t\t\tushort rightSegment;\n\t\t\t\t\t\t\t\tprevSegment.GetLeftAndRightSegments(nextNodeId, out leftSegment, out rightSegment);\n\t\t\t\t\t\t\t\tint numIter = 0;\n\t\t\t\t\t\t\t\twhile (leftSegment != 0 && leftSegment != prevSegmentId && leftLaneId == 0u) {\n\t\t\t\t\t\t\t\t\tint someLeftLaneIndex;\n\t\t\t\t\t\t\t\t\tint someRightLaneIndex;\n\t\t\t\t\t\t\t\t\tuint someLeftLaneId;\n\t\t\t\t\t\t\t\t\tuint someRightLaneId;\n\t\t\t\t\t\t\t\t\tnetManager.m_segments.m_buffer[(int)leftSegment].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);\n\t\t\t\t\t\t\t\t\tif (someRightLaneId != 0u) {\n\t\t\t\t\t\t\t\t\t\tnextLeftSegment = leftSegment;\n\t\t\t\t\t\t\t\t\t\tleftLaneIndex = someRightLaneIndex;\n\t\t\t\t\t\t\t\t\t\tleftLaneId = someRightLaneId;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tleftSegment = netManager.m_segments.m_buffer[(int)leftSegment].GetLeftSegment(nextNodeId);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (++numIter == 8) {\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tnumIter = 0;\n\t\t\t\t\t\t\t\twhile (rightSegment != 0 && rightSegment != prevSegmentId && rightLaneId == 0u) {\n\t\t\t\t\t\t\t\t\tint someLeftLaneIndex;\n\t\t\t\t\t\t\t\t\tint someRightLaneIndex;\n\t\t\t\t\t\t\t\t\tuint someLeftLaneId;\n\t\t\t\t\t\t\t\t\tuint someRightLaneId;\n\t\t\t\t\t\t\t\t\tnetManager.m_segments.m_buffer[(int)rightSegment].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);\n\t\t\t\t\t\t\t\t\tif (someLeftLaneId != 0u) {\n\t\t\t\t\t\t\t\t\t\tnextRightSegment = rightSegment;\n\t\t\t\t\t\t\t\t\t\trightLaneIndex = someLeftLaneIndex;\n\t\t\t\t\t\t\t\t\t\trightLaneId = someLeftLaneId;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\trightSegment = netManager.m_segments.m_buffer[(int)rightSegment].GetRightSegment(nextNodeId);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (++numIter == 8) {\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (leftLaneId != 0u && (nextLeftSegment != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debugPed) {\n\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring left segment\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_extPathType={queueItem.pathType}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_extVehicleType={queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isRoadVehicle={m_isRoadVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isHeavyVehicle={m_isHeavyVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_stablePath={m_stablePath}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\\n\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextLeftSegment={nextLeftSegment}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"leftLaneId={leftLaneId}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"mayCrossStreet={canCrossStreet}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"isOnCenterPlatform={isOnCenterPlatform}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\"\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\tthis.ProcessItemPedBicycle(debugPed, item, nextNodeId, nextLeftSegment, ref prevSegment, ref netManager.m_segments.m_buffer[(int)nextLeftSegment], connectOffset, connectOffset, leftLaneIndex, leftLaneId); // ped\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (rightLaneId != 0u && rightLaneId != leftLaneId && (nextRightSegment != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debugPed) {\n\t\t\t\t\t\t\t\t\tlogBuf.Add($\"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring right segment\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_extPathType={queueItem.pathType}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_extVehicleType={queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isRoadVehicle={m_isRoadVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isHeavyVehicle={m_isHeavyVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_stablePath={m_stablePath}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\\n\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextRightSegment={nextRightSegment}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"rightLaneId={rightLaneId}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"mayCrossStreet={canCrossStreet}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"isOnCenterPlatform={isOnCenterPlatform}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\"\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\tthis.ProcessItemPedBicycle(debugPed, item, nextNodeId, nextRightSegment, ref prevSegment, ref netManager.m_segments.m_buffer[(int)nextRightSegment], connectOffset, connectOffset, rightLaneIndex, rightLaneId); // ped\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// switch from bicycle lane to pedestrian lane\n\t\t\t\t\t\tint nextLaneIndex;\n\t\t\t\t\t\tuint nextLaneId;\n\t\t\t\t\t\tif ((this.m_vehicleTypes & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None &&\n\t\t\t\t\t\t\tprevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Bicycle, out nextLaneIndex, out nextLaneId)) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\tif (debugPed) {\n\t\t\t\t\t\t\t\tlogBuf.Add($\"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring bicycle switch\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_extPathType={queueItem.pathType}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_extVehicleType={queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isRoadVehicle={m_isRoadVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isHeavyVehicle={m_isHeavyVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_stablePath={m_stablePath}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\\n\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextLaneIndex={nextLaneIndex}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextLaneId={nextLaneId}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\tthis.ProcessItemPedBicycle(debugPed, item, nextNodeId, prevSegmentId, ref prevSegment, ref prevSegment, connectOffset, connectOffset, nextLaneIndex, nextLaneId); // bicycle\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// we are going from pedestrian lane to a beautification node\n\n\t\t\t\t\t\tfor (int j = 0; j < 8; ++j) {\n\t\t\t\t\t\t\tushort nextSegmentId = nextNode.GetSegment(j);\n\t\t\t\t\t\t\tif (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t\tthis.ProcessItemCosts(debug, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[(int)nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, false, true, isMiddle);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\t// switch from vehicle to pedestrian lane (parking)\n\t\t\t\t\tbool parkingAllowed = true;\n\t\t\t\t\tif (Options.prohibitPocketCars) {\n\t\t\t\t\t\tif (queueItem.vehicleType == ExtVehicleType.PassengerCar) {\n\t\t\t\t\t\t\tif ((item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {\n\t\t\t\t\t\t\t\t// if pocket cars are prohibited, a citizen may only park their car once per path\n\t\t\t\t\t\t\t\tparkingAllowed = false;\n\t\t\t\t\t\t\t} else if ((item.m_lanesUsed & NetInfo.LaneType.PublicTransport) == NetInfo.LaneType.None) {\n\t\t\t\t\t\t\t\t// if the citizen is walking to their target (= no public transport used), the passenger car must be parked in the very last moment\n\t\t\t\t\t\t\t\tparkingAllowed = item.m_laneID == m_endLaneA || item.m_laneID == m_endLaneB;\n\t\t\t\t\t\t\t\t/*if (_conf.Debug.Switches[4]) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"Path unit {unitId}: public transport has not been used. \");\n\t\t\t\t\t\t\t\t}*/\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (parkingAllowed) {\n\t\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t\tNetInfo.LaneType laneType = this.m_laneTypes & ~NetInfo.LaneType.Pedestrian;\n\t\t\t\t\t\tVehicleInfo.VehicleType vehicleType = this.m_vehicleTypes & ~VehicleInfo.VehicleType.Bicycle;\n\t\t\t\t\t\tif ((byte)(item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {\n\t\t\t\t\t\t\tlaneType &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tint nextLaneIndex2;\n\t\t\t\t\t\tuint nextlaneId2;\n\t\t\t\t\t\tif (laneType != NetInfo.LaneType.None &&\n\t\t\t\t\t\t\tvehicleType != VehicleInfo.VehicleType.None &&\n\t\t\t\t\t\t\tprevSegment.GetClosestLane(prevLaneIndex, laneType, vehicleType, out nextLaneIndex2, out nextlaneId2)) {\n\t\t\t\t\t\t\tNetInfo.Lane lane5 = prevSegmentInfo.m_lanes[nextLaneIndex2];\n\t\t\t\t\t\t\tbyte connectOffset2;\n\t\t\t\t\t\t\tif ((prevSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None == ((byte)(lane5.m_finalDirection & NetInfo.Direction.Backward) != 0)) {\n\t\t\t\t\t\t\t\tconnectOffset2 = 1;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconnectOffset2 = 254;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tCustomPathFind.BufferItem item2 = item;\n\t\t\t\t\t\t\tif (this.m_randomParking) {\n\t\t\t\t\t\t\t\titem2.m_comparisonValue += (float)this.m_pathRandomizer.Int32(300u) / this.m_maxLength;\n\t\t\t\t\t\t\t}\n#if DEBUGNEWPF\n\t\t\t\t\t\t\tif (debugPed) {\n\t\t\t\t\t\t\t\tlogBuf.Add($\"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring parking switch\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_extPathType={queueItem.pathType}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_extVehicleType={queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isRoadVehicle={m_isRoadVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isHeavyVehicle={m_isHeavyVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_stablePath={m_stablePath}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\\n\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextLaneIndex2={nextLaneIndex2}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextlaneId2={nextlaneId2}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\tthis.ProcessItemPedBicycle(debugPed, item2, nextNodeId, prevSegmentId, ref prevSegment, ref prevSegment, connectOffset2, 128, nextLaneIndex2, nextlaneId2); // ped\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// we are going to a non-pedestrian lane\n\n\t\t\t\tbool allowPedestrian = (byte)(this.m_laneTypes & NetInfo.LaneType.Pedestrian) != 0; // allow pedestrian switching to vehicle?\n\t\t\t\tbool nextIsBeautificationNode = nextNode.Info.m_class.m_service == ItemClass.Service.Beautification;\n\t\t\t\tbool allowBicycle = false; // is true if cim may switch from a pedestrian lane to a bike lane\n\t\t\t\tbyte parkingConnectOffset = 0;\n\t\t\t\tif (allowPedestrian) {\n\t\t\t\t\tif (prevIsBicycleLane) {\n\t\t\t\t\t\t// we are going to a bicycle lane\n\t\t\t\t\t\tparkingConnectOffset = connectOffset;\n\t\t\t\t\t\tallowBicycle = nextIsBeautificationNode;\n\t\t\t\t\t} else if (this.m_vehicleLane != 0u) {\n\t\t\t\t\t\t// there is a parked vehicle position\n\t\t\t\t\t\tif (this.m_vehicleLane != item.m_laneID) {\n\t\t\t\t\t\t\t// we have not reached the parked vehicle yet\n\t\t\t\t\t\t\tallowPedestrian = false;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// pedestrian switches to parked vehicle\n\t\t\t\t\t\t\tparkingConnectOffset = this.m_vehicleOffset;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (this.m_stablePath) {\n\t\t\t\t\t\t// enter a bus\n\t\t\t\t\t\tparkingConnectOffset = 128;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// pocket car spawning\n\t\t\t\t\t\tif (Options.prohibitPocketCars &&\n\t\t\t\t\t\t\t\tqueueItem.vehicleType == ExtVehicleType.PassengerCar &&\n\t\t\t\t\t\t\t\t(queueItem.pathType == ExtCitizenInstance.ExtPathType.WalkingOnly || (queueItem.pathType == ExtCitizenInstance.ExtPathType.DrivingOnly && item.m_position.m_segment != m_startSegmentA && item.m_position.m_segment != m_startSegmentB))) {\n\t\t\t\t\t\t\tallowPedestrian = false;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tparkingConnectOffset = (byte)this.m_pathRandomizer.UInt32(1u, 254u);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ((this.m_vehicleTypes & (VehicleInfo.VehicleType.Ferry /* | VehicleInfo.VehicleType.Monorail*/)) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\t// monorail / ferry\n\n\t\t\t\t\tfor (int k = 0; k < 8; k++) {\n\t\t\t\t\t\tushort nextSegmentId = nextNode.GetSegment(k);\n\t\t\t\t\t\tif (nextSegmentId == 0 || nextSegmentId == prevSegmentId) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.ProcessItemCosts(debug, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, allowBicycle, isMiddle);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None /*&&\n\t\t\t\t\t\t(this._vehicleTypes & VehicleInfo.VehicleType.Monorail) == VehicleInfo.VehicleType.None*/) {\n\t\t\t\t\t\tthis.ProcessItemCosts(debug, item, nextNodeId, prevSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref prevSegment, ref prevRelSimilarLaneIndex, connectOffset, true, false, isMiddle);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// road vehicles, trams, trains, metros, monorails, etc.\n\n\n\t\t\t\t\t// specifies if vehicles should follow lane arrows\n\t\t\t\t\tbool isStrictLaneChangePolicyEnabled = false;\n\t\t\t\t\t// specifies if the entity is allowed to u-turn (in general)\n\t\t\t\t\tbool isEntityAllowedToUturn = (this.m_vehicleTypes & (VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Monorail)) == VehicleInfo.VehicleType.None;\n\t\t\t\t\t// specifies if thes next node allows for u-turns\n\t\t\t\t\tbool isUturnAllowedHere = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;\n\t\t\t\t\t/*\n\t\t\t\t\t * specifies if u-turns are handled by custom code.\n\t\t\t\t\t * If not (performCustomVehicleUturns == false) AND the vanilla u-turn condition (stockUturn) evaluates to true, then u-turns are handled by the vanilla code\n\t\t\t\t\t */\n\t\t\t\t\t//bool performCustomVehicleUturns = false;\n\t\t\t\t\tbool prevIsRouted = prevLaneEndRouting.routed\n#if DEBUG\n\t\t\t\t\t\t&& !m_conf.Debug.Switches[11]\n#endif\n\t\t\t\t\t;\n\n\t\t\t\t\tif (prevIsRouted) {\n\t\t\t\t\t\tbool prevIsOutgoingOneWay = nextIsStartNode ? prevSegmentRouting.startNodeOutgoingOneWay : prevSegmentRouting.endNodeOutgoingOneWay;\n\t\t\t\t\t\tbool nextIsUntouchable = (nextNode.m_flags & (NetNode.Flags.Untouchable)) != NetNode.Flags.None;\n\t\t\t\t\t\tbool nextIsTransitionOrJunction = (nextNode.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) != NetNode.Flags.None;\n\t\t\t\t\t\tbool nextIsBend = (nextNode.m_flags & (NetNode.Flags.Bend)) != NetNode.Flags.None;\n\n\t\t\t\t\t\t// determine if the vehicle may u-turn at the target node according to customization\n\t\t\t\t\t\tisUturnAllowedHere =\n\t\t\t\t\t\t\tisUturnAllowedHere || // stock u-turn points\n\t\t\t\t\t\t\t(Options.junctionRestrictionsEnabled &&\n\t\t\t\t\t\t\tm_isRoadVehicle && // only road vehicles may perform u-turns\n\t\t\t\t\t\t\tjunctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode) && // only do u-turns if allowed\n\t\t\t\t\t\t\t!nextIsBeautificationNode && // no u-turns at beautification nodes // TODO refactor to JunctionManager\n\t\t\t\t\t\t\tprevIsCarLane && // u-turns for road vehicles only\n\t\t\t\t\t\t\t!m_isHeavyVehicle && // only small vehicles may perform u-turns\n\t\t\t\t\t\t\t(nextIsTransitionOrJunction || nextIsBend) && // perform u-turns at transitions, junctions and bend nodes // TODO refactor to JunctionManager\n\t\t\t\t\t\t\t!prevIsOutgoingOneWay); // do not u-turn on one-ways // TODO refactor to JunctionManager\n\n\t\t\t\t\t\tisStrictLaneChangePolicyEnabled =\n\t\t\t\t\t\t\t!nextIsBeautificationNode && // do not obey lane arrows at beautification nodes\n\t\t\t\t\t\t\t!nextIsUntouchable &&\n\t\t\t\t\t\t\tm_isLaneArrowObeyingEntity &&\n\t\t\t\t\t\t\t//nextIsTransitionOrJunction && // follow lane arrows only at transitions and junctions\n\t\t\t\t\t\t\t!(\n#if DEBUG\n\t\t\t\t\t\t\tOptions.allRelaxed || // debug option: all vehicle may ignore lane arrows\n#endif\n\t\t\t\t\t\t\t(Options.relaxedBusses && queueItem.vehicleType == ExtVehicleType.Bus)); // option: busses may ignore lane arrows\n\n\t\t\t\t\t\t/*if (! performCustomVehicleUturns) {\n\t\t\t\t\t\t\tisUturnAllowedHere = false;\n\t\t\t\t\t\t}*/\n\t\t\t\t\t\t//isEntityAllowedToUturn = isEntityAllowedToUturn && !performCustomVehicleUturns;\n\n\n#if DEBUGNEWPF\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane} (id {item.m_laneID}), node {nextNodeId} ({nextIsStartNode}):\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_extPathType={queueItem.pathType}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_extVehicleType={queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_isRoadVehicle={m_isRoadVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_isHeavyVehicle={m_isHeavyVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_vehicleLane={m_vehicleLane}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_stablePath={m_stablePath}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\\n\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"prevIsOutgoingOneWay={prevIsOutgoingOneWay}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"prevIsRouted={prevIsRouted}\\n\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"isNextBeautificationNode={nextIsBeautificationNode}\\n\" +\n\t\t\t\t\t\t\t\t//\"\\t\" + $\"nextIsRealJunction={nextIsRealJunction}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsTransitionOrJunction={nextIsTransitionOrJunction}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsBend={nextIsBend}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsUntouchable={nextIsUntouchable}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"allowBicycle={allowBicycle}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"isCustomUturnAllowed={junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode)}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"isStrictLaneArrowPolicyEnabled={isStrictLaneChangePolicyEnabled}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"isEntityAllowedToUturn={isEntityAllowedToUturn}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"isUturnAllowedHere={isUturnAllowedHere}\\n\"\n\t\t\t\t\t\t\t\t//\"\\t\" + $\"performCustomVehicleUturns={performCustomVehicleUturns}\\n\"\n\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t} else {\n#if DEBUGNEWPF\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}):\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_extPathType={queueItem.pathType}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_extVehicleType={queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_isRoadVehicle={m_isRoadVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_isHeavyVehicle={m_isHeavyVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_stablePath={m_stablePath}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\\n\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"prevIsRouted={prevIsRouted}\\n\\n\"\n\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t}\n\n\t\t\t\t\tif (allowBicycle || !prevIsRouted) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * pedestrian to bicycle lane switch or no routing information available:\n\t\t\t\t\t\t *\t\tif pedestrian lanes should be explored (allowBicycle == true): do this here\n\t\t\t\t\t\t *\t\tif previous segment has custom routing (prevIsRouted == true): do NOT explore vehicle lanes here, else: vanilla exploration of vehicle lanes\n\t\t\t\t\t\t*/\n\n#if DEBUGNEWPF\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tlogBuf.Add(\n\t\t\t\t\t\t\t\t$\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"-> using DEFAULT exploration mode\\n\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t/*if (performCustomVehicleUturns) {\n\t\t\t\t\t\t\tisUturnAllowedHere = true;\n\t\t\t\t\t\t\tisEntityAllowedToUturn = true;\n\t\t\t\t\t\t}*/\n\n\t\t\t\t\t\tushort nextSegmentId = prevSegment.GetRightSegment(nextNodeId);\n\t\t\t\t\t\tfor (int k = 0; k < 8; ++k) {\n\t\t\t\t\t\t\tif (nextSegmentId == 0 || nextSegmentId == prevSegmentId) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (ProcessItemCosts(debug, item, nextNodeId, nextSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, !prevIsRouted, allowBicycle, isMiddle)) {\n\t\t\t\t\t\t\t\t// exceptional u-turns\n\t\t\t\t\t\t\t\tisUturnAllowedHere = true;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tnextSegmentId = netManager.m_segments.m_buffer[nextSegmentId].GetRightSegment(nextNodeId);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (prevIsRouted) {\n\t\t\t\t\t\t/* routed vehicle paths */\n\n#if DEBUGNEWPF\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tlogBuf.Add(\n\t\t\t\t\t\t\t\t$\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"-> using CUSTOM exploration mode\\n\"\n\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\tbool canUseLane = CanUseLane(debug, item.m_position.m_segment, prevSegmentInfo, item.m_position.m_lane, prevLaneInfo);\n\t\t\t\t\t\tLaneTransitionData[] laneTransitions = prevLaneEndRouting.transitions;\n\t\t\t\t\t\tif (laneTransitions != null && (canUseLane || Options.vehicleRestrictionsAggression != VehicleRestrictionsAggression.Strict)) {\n\n#if DEBUGNEWPF\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tlogBuf.Add(\n\t\t\t\t\t\t\t\t\t$\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"CUSTOM exploration\\n\"\n\t\t\t\t\t\t\t\t\t);\n#endif\n\n\t\t\t\t\t\t\tLaneChangingCostCalculationMode laneChangingCostCalculationMode = LaneChangingCostCalculationMode.None; // lane changing cost calculation mode to use\n\t\t\t\t\t\t\tfloat? segmentSelectionCost = null; // cost for using that particular segment\n\t\t\t\t\t\t\tfloat? laneSelectionCost = null; // cost for using that particular lane\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t * (1) Apply vehicle restrictions\n\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t */\n\n\t\t\t\t\t\t\tif (! canUseLane) {\n\t\t\t\t\t\t\t\tlaneSelectionCost = VehicleRestrictionsManager.PATHFIND_PENALTIES[(int)Options.vehicleRestrictionsAggression];\n\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"applied vehicle restrictions for vehicle {queueItem.vehicleId}, type {queueItem.vehicleType}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"=> laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (m_isRoadVehicle &&\n\t\t\t\t\t\t\t\tprevLaneInfo != null &&\n\t\t\t\t\t\t\t\tprevIsCarLane) {\n\n\t\t\t\t\t\t\t\tif (Options.advancedAI) {\n\t\t\t\t\t\t\t\t\tlaneChangingCostCalculationMode = LaneChangingCostCalculationMode.ByGivenDistance;\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"AI is active, prev is car lane and we are a car\\n\"\n\t\t\t\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t\t * (2) Apply car ban district policies\n\t\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t\t */\n\n\t\t\t\t\t\t\t\t// Apply costs for traffic ban policies\n\t\t\t\t\t\t\t\tif ((prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&\n\t\t\t\t\t\t\t\t\t\t(prevLaneInfo.m_vehicleType & this.m_vehicleTypes) == VehicleInfo.VehicleType.Car &&\n\t\t\t\t\t\t\t\t\t\t(netManager.m_segments.m_buffer[item.m_position.m_segment].m_flags & this.m_carBanMask) != NetSegment.Flags.None) {\n\t\t\t\t\t\t\t\t\t// heavy vehicle ban / car ban (\"Old Town\" policy)\n\t\t\t\t\t\t\t\t\tif (laneSelectionCost == null) {\n\t\t\t\t\t\t\t\t\t\tlaneSelectionCost = 1f;\n\t\t\t\t\t\t\t\t\t}\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\tfloat? oldLaneSelectionCost = laneSelectionCost;\n#endif\n\t\t\t\t\t\t\t\t\tlaneSelectionCost *= 7.5f;\n\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"applied heavy vehicle ban / car ban ('Old Town' policy):\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"oldLaneSelectionCost={oldLaneSelectionCost}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"=> laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t\t * (3) Apply costs for using/not using transport lanes\n\t\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t\t */\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t * (1) busses should prefer transport lanes\n\t\t\t\t\t\t\t\t * (2) regular traffic should prefer regular lanes\n\t\t\t\t\t\t\t\t * (3) taxis, service vehicles and emergency vehicles may choose freely between regular and transport lanes\n\t\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\t\tif ((prevLaneInfo.m_laneType & NetInfo.LaneType.TransportVehicle) != NetInfo.LaneType.None) {\n\t\t\t\t\t\t\t\t\t// previous lane is a public transport lane\n\t\t\t\t\t\t\t\t\tif ((queueItem.vehicleType & ExtVehicleType.Bus) != ExtVehicleType.None) {\n\t\t\t\t\t\t\t\t\t\tif (laneSelectionCost == null) {\n\t\t\t\t\t\t\t\t\t\t\tlaneSelectionCost = 1f;\n\t\t\t\t\t\t\t\t\t\t}\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\t\tfloat? oldLaneSelectionCost = laneSelectionCost;\n#endif\n\t\t\t\t\t\t\t\t\t\tlaneSelectionCost *= m_conf.PathFinding.PublicTransportLaneReward; // (1)\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"applied bus-on-transport lane reward:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"oldLaneSelectionCost={oldLaneSelectionCost}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"=> laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\t\t\t\t} else if ((queueItem.vehicleType & (ExtVehicleType.RoadPublicTransport | ExtVehicleType.Service | ExtVehicleType.Emergency)) == ExtVehicleType.None) {\n\t\t\t\t\t\t\t\t\t\tif (laneSelectionCost == null) {\n\t\t\t\t\t\t\t\t\t\t\tlaneSelectionCost = 1f;\n\t\t\t\t\t\t\t\t\t\t}\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\t\tfloat? oldLaneSelectionCost = laneSelectionCost;\n#endif\n\t\t\t\t\t\t\t\t\t\tlaneSelectionCost *= m_conf.PathFinding.PublicTransportLanePenalty; // (2)\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"applied car-on-transport lane penalty:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"oldLaneSelectionCost={oldLaneSelectionCost}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"=> laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// (3), do nothing\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t\t * (4) Apply costs for large vehicles using inner lanes on highways\n\t\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t\t */\n\n\t\t\t\t\t\t\t\tbool nextIsJunction = (netManager.m_nodes.m_buffer[nextNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction;\n\t\t\t\t\t\t\t\tbool nextIsRealJunction = nextIsJunction && (netManager.m_nodes.m_buffer[nextNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);\n\t\t\t\t\t\t\t\tushort prevNodeId = (nextNodeId == prevSegment.m_startNode) ? prevSegment.m_endNode : prevSegment.m_startNode;\n\t\t\t\t\t\t\t\t//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);\n\t\t\t\t\t\t\t\tif (prevLaneInfo.m_similarLaneCount > 1) {\n\t\t\t\t\t\t\t\t\tif (m_isHeavyVehicle &&\n\t\t\t\t\t\t\t\t\t\tOptions.preferOuterLane &&\n\t\t\t\t\t\t\t\t\t\tprevSegmentRouting.highway &&\n\t\t\t\t\t\t\t\t\t\tm_pathRandomizer.Int32(m_conf.PathFinding.HeavyVehicleInnerLanePenaltySegmentSel) == 0\n\t\t\t\t\t\t\t\t\t\t/* && (netManager.m_nodes.m_buffer[prevNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) != NetNode.Flags.None */\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\t// penalize large vehicles for using inner lanes\n\t\t\t\t\t\t\t\t\t\tif (laneSelectionCost == null) {\n\t\t\t\t\t\t\t\t\t\t\tlaneSelectionCost = 1f;\n\t\t\t\t\t\t\t\t\t\t}\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\t\tfloat? oldLaneSelectionCost = laneSelectionCost;\n#endif\n\t\t\t\t\t\t\t\t\t\tfloat prevRelOuterLane = ((float)prevOuterSimilarLaneIndex / (float)(prevLaneInfo.m_similarLaneCount - 1));\n\t\t\t\t\t\t\t\t\t\tlaneSelectionCost *= 1f + m_conf.PathFinding.HeavyVehicleMaxInnerLanePenalty * prevRelOuterLane;\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"applied inner lane penalty:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"oldLaneSelectionCost={oldLaneSelectionCost}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"=> laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t\t\t * (5) Apply costs for randomized lane selection in front of junctions and highway transitions\n\t\t\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\t\t\tif (Options.advancedAI &&\n\t\t\t\t\t\t\t\t\t\t\t\t!m_stablePath &&\n\t\t\t\t\t\t\t\t\t\t\t\t!m_isHeavyVehicle &&\n\t\t\t\t\t\t\t\t\t\t\t\tnextIsJunction &&\n\t\t\t\t\t\t\t\t\t\t\t\tm_pathRandomizer.Int32(m_conf.AdvancedVehicleAI.LaneRandomizationJunctionSel) == 0) {\n\t\t\t\t\t\t\t\t\t\t// randomized lane selection at junctions\n\t\t\t\t\t\t\t\t\t\tif (laneSelectionCost == null) {\n\t\t\t\t\t\t\t\t\t\t\tlaneSelectionCost = 1f;\n\t\t\t\t\t\t\t\t\t\t}\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\t\tfloat? oldLaneSelectionCost = laneSelectionCost;\n#endif\n\t\t\t\t\t\t\t\t\t\tlaneSelectionCost *= 1f + m_pathRandomizer.Int32(2) * m_conf.AdvancedVehicleAI.LaneRandomizationCostFactor;\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"applied lane randomizations at junctions:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"oldLaneSelectionCost={oldLaneSelectionCost}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"=> laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t\t * (6) Apply junction costs\n\t\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\t\t if (Options.advancedAI && nextIsJunction && prevSegmentRouting.highway) {\n\t\t\t\t\t\t\t\t\tif (segmentSelectionCost == null) {\n\t\t\t\t\t\t\t\t\t\tsegmentSelectionCost = 1f;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tsegmentSelectionCost *= 1f + m_conf.AdvancedVehicleAI.JunctionBaseCost;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t\t * (7) Apply traffic measurement costs for segment selection\n\t\t\t\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\t\tif (Options.advancedAI && (queueItem.vehicleType & (ExtVehicleType.RoadVehicle & ~ExtVehicleType.Bus)) != ExtVehicleType.None && !m_stablePath) {\n\t\t\t\t\t\t\t\t\t// segment selection based on segment traffic volume\n\t\t\t\t\t\t\t\t\tNetInfo.Direction prevFinalDir = nextIsStartNode ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;\n\t\t\t\t\t\t\t\t\tprevFinalDir = ((prevSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? prevFinalDir : NetInfo.InvertDirection(prevFinalDir);\n\t\t\t\t\t\t\t\t\tTrafficMeasurementManager.SegmentDirTrafficData prevDirTrafficData = trafficMeasurementManager.segmentDirTrafficData[trafficMeasurementManager.GetDirIndex(item.m_position.m_segment, prevFinalDir)];\n\n\t\t\t\t\t\t\t\t\tfloat segmentTraffic = Mathf.Clamp(1f - (float)prevDirTrafficData.meanSpeed / (float)TrafficMeasurementManager.REF_REL_SPEED + item.m_trafficRand, 0, 1f);\n\n\t\t\t\t\t\t\t\t\tif (segmentSelectionCost == null) {\n\t\t\t\t\t\t\t\t\t\tsegmentSelectionCost = 1f;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tsegmentSelectionCost *= 1f +\n\t\t\t\t\t\t\t\t\t\tm_conf.AdvancedVehicleAI.TrafficCostFactor *\n\t\t\t\t\t\t\t\t\t\tsegmentTraffic;\n\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"applied traffic measurement costs for segment selection:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"segmentTraffic={segmentTraffic}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"=> segmentSelectionCost={segmentSelectionCost}\\n\"\n\t\t\t\t\t\t\t\t\t\t\t);\n#endif\n\n\t\t\t\t\t\t\t\t\tif (m_conf.AdvancedVehicleAI.LaneDensityRandInterval > 0 && nextIsRealJunction) {\n\t\t\t\t\t\t\t\t\t\titem.m_trafficRand = 0.01f * ((float)m_pathRandomizer.Int32((uint)m_conf.AdvancedVehicleAI.LaneDensityRandInterval + 1u) - m_conf.AdvancedVehicleAI.LaneDensityRandInterval / 2f);\n\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"updated item.m_trafficRand:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"=> item.m_trafficRand={item.m_trafficRand}\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"calculated traffic stats:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_extVehicleType={queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isRoadVehicle={m_isRoadVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isHeavyVehicle={m_isHeavyVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\\n\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"laneSelectionCost={laneSelectionCost}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"segmentSelectionCost={segmentSelectionCost}\\n\"\n\t\t\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfor (int k = 0; k < laneTransitions.Length; ++k) {\n\t\t\t\t\t\t\t\tushort nextSegmentId = laneTransitions[k].segmentId;\n\n\t\t\t\t\t\t\t\tif (nextSegmentId == 0) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\tlogBuf.Add(\n\t\t\t\t\t\t\t\t\t\t\t$\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"CUSTOM exploration\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"transition iteration {k}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"{laneTransitions[k].ToString()}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"*** SKIPPING *** (nextSegmentId=0)\\n\"\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tbool uturn = nextSegmentId == prevSegmentId;\n\t\t\t\t\t\t\t\tif (uturn) {\n\t\t\t\t\t\t\t\t\t// prevent double/forbidden exploration of previous segment by vanilla code during this method execution\n\t\t\t\t\t\t\t\t\tif (! isEntityAllowedToUturn || ! isUturnAllowedHere) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\t\tlogBuf.Add(\n\t\t\t\t\t\t\t\t\t\t\t\t$\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"CUSTOM exploration\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"transition iteration {k}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"{laneTransitions[k].ToString()}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"*** SKIPPING *** (u-turns prohibited)\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (laneTransitions[k].type == LaneEndTransitionType.Invalid) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\tlogBuf.Add(\n\t\t\t\t\t\t\t\t\t\t\t$\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"CUSTOM exploration\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"transition iteration {k}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"{laneTransitions[k].ToString()}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"*** SKIPPING *** (invalid transition)\\n\"\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// allow vehicles to ignore strict lane routing when moving off\n\t\t\t\t\t\t\t\tbool relaxedLaneChanging =\n\t\t\t\t\t\t\t\t\tm_isRoadVehicle &&\n\t\t\t\t\t\t\t\t\t(queueItem.vehicleType & (ExtVehicleType.Service | ExtVehicleType.PublicTransport | ExtVehicleType.Emergency)) != ExtVehicleType.None &&\n\t\t\t\t\t\t\t\t\tqueueItem.vehicleId == 0 &&\n\t\t\t\t\t\t\t\t\t(laneTransitions[k].laneId == m_startLaneA || laneTransitions[k].laneId == m_startLaneB);\n\n\t\t\t\t\t\t\t\tif (! relaxedLaneChanging &&\n\t\t\t\t\t\t\t\t\t(isStrictLaneChangePolicyEnabled && laneTransitions[k].type == LaneEndTransitionType.Relaxed)) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\tlogBuf.Add(\n\t\t\t\t\t\t\t\t\t\t\t$\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"CUSTOM exploration\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"transition iteration {k}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"{laneTransitions[k].ToString()}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"relaxedLaneChanging={relaxedLaneChanging}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"isStrictLaneChangePolicyEnabled={relaxedLaneChanging}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"*** SKIPPING *** (incompatible lane)\\n\"\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tlogBuf.Add(\n\t\t\t\t\t\t\t\t\t\t$\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"CUSTOM exploration\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"transition iteration {k}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"{laneTransitions[k].ToString()}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"> PERFORMING EXPLORATION NOW <\\n\"\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t\tbool foundForced = false;\n\t\t\t\t\t\t\t\tint prevLaneIndexFromInner = prevRelSimilarLaneIndex;\n\t\t\t\t\t\t\t\tif (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)) {\n\t\t\t\t\t\t\t\t\t\t// process exceptional u-turning in vanilla code\n\t\t\t\t\t\t\t\t\t\tisUturnAllowedHere = true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!prevIsRouted && isEntityAllowedToUturn && isUturnAllowedHere) {\n#if DEBUGNEWPF\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tlogBuf.Add($\"path unit {unitId}\\n\" +\n\t\t\t\t\t\t\t\t$\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"-> exploring DEFAULT u-turn\\n\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tthis.ProcessItemCosts(debug, item, nextNodeId, prevSegmentId, ref prevSegment, /*prevSegmentRouting,*/ ref prevSegment, ref prevRelSimilarLaneIndex, connectOffset, true, false, isMiddle);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (allowPedestrian) {\n\t\t\t\t\t// switch from walking to driving a car, bus, etc.\n\t\t\t\t\tint nextLaneIndex;\n\t\t\t\t\tuint nextLaneId;\n\t\t\t\t\tif (prevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Pedestrian, this.m_vehicleTypes, out nextLaneIndex, out nextLaneId)) {\n#if DEBUGNEWPF\n\t\t\t\t\t\tif (debugPed) {\n\t\t\t\t\t\t\tlogBuf.Add($\"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring vehicle switch\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_extPathType={queueItem.pathType}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_extVehicleType={queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_isRoadVehicle={m_isRoadVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_isHeavyVehicle={m_isHeavyVehicle}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_stablePath={m_stablePath}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\\n\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextLaneIndex={nextLaneIndex}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextLaneId={nextLaneId}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tthis.ProcessItemPedBicycle(debugPed, item, nextNodeId, prevSegmentId, ref prevSegment, ref prevSegment, parkingConnectOffset, parkingConnectOffset, nextLaneIndex, nextLaneId); // ped\n\t\t\t\t\t}\n\t\t\t\t} // allowPedSwitch\n\t\t\t} // !prevIsPedestrianLane\n\n\t\t\t// [18/05/06] conditions commented out because cims could not go to an outside connection with path \"walk -> public transport -> walk -> car\"\n\t\t\tif (nextNode.m_lane != 0u /*&&\n\t\t\t\t(!Options.prohibitPocketCars ||\n\t\t\t\tqueueItem.vehicleType != ExtVehicleType.PassengerCar ||\n\t\t\t\t(item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) == NetInfo.LaneType.None)*/) {\n\t\t\t\t// transport lines, cargo lines, etc.\n\n\t\t\t\tbool targetDisabled = (nextNode.m_flags & (NetNode.Flags.Disabled | NetNode.Flags.DisableOnlyMiddle)) == NetNode.Flags.Disabled;\n\t\t\t\tushort nextSegmentId = netManager.m_lanes.m_buffer[nextNode.m_lane].m_segment;\n\t\t\t\tif (nextSegmentId != 0 && nextSegmentId != item.m_position.m_segment) {\n#if DEBUGNEWPF\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId} ({nextIsStartNode}): Exploring transport segment\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"_extPathType={queueItem.pathType}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"_vehicleTypes={m_vehicleTypes}, _laneTypes={m_laneTypes}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"_extVehicleType={queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"_isRoadVehicle={m_isRoadVehicle}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"_isHeavyVehicle={m_isHeavyVehicle}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"_stablePath={m_stablePath}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneConnectionObeyingEntity={m_isLaneConnectionObeyingEntity}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\\n\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"nextNode.m_lane={nextNode.m_lane}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"nextSegmentId={nextSegmentId}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"nextIsStartNode={nextIsStartNode}\\n\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tthis.ProcessItemPublicTransport(debug, item, nextNodeId, targetDisabled, nextSegmentId, ref prevSegment, ref netManager.m_segments.m_buffer[nextSegmentId], nextNode.m_lane, nextNode.m_laneOffset, connectOffset);\n\t\t\t\t}\n\t\t\t}\n\n#if DEBUGNEWPF\n\t\t\tif (debug) {\n\t\t\t\tFlushMainLog(logBuf, unitId);\n\t\t\t}\n#endif\n\t\t}\n\n\t\t// 2\n\t\tprivate void ProcessItemPublicTransport(bool debug, BufferItem item, ushort nextNodeId, bool targetDisabled, ushort nextSegmentId, ref NetSegment prevSegment, ref NetSegment nextSegment, uint nextLaneId, byte offset, byte connectOffset) {\n\t\t\tif ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tif (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) {\n\t\t\t\treturn;\n\t\t\t}\n\n#if COUNTSEGMENTSTONEXTJUNCTION\n\t\t\tbool nextIsRegularNode = nextNodeId == prevSegment.m_startNode || nextNodeId == prevSegment.m_endNode;\n\t\t\tbool prevIsRealJunction = false;\n\t\t\tif (nextIsRegularNode) {\n\t\t\t\t// no lane changing directly in front of a junction\n\t\t\t\tushort prevNodeId = (nextNodeId == prevSegment.m_startNode) ? prevSegment.m_endNode : prevSegment.m_startNode;\n\t\t\t\tprevIsRealJunction = (netManager.m_nodes.m_buffer[prevNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction &&\n\t\t\t\t\t(netManager.m_nodes.m_buffer[prevNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);\n\t\t\t}\n#endif\n\n\t\t\tNetInfo nextSegmentInfo = nextSegment.Info;\n\t\t\tNetInfo prevSegmentInfo = prevSegment.Info;\n\t\t\tint nextNumLanes = nextSegmentInfo.m_lanes.Length;\n\t\t\tuint curLaneId = nextSegment.m_lanes;\n\t\t\tfloat prevMaxSpeed = 1f;\n\t\t\tfloat prevSpeed = 1f;\n\t\t\tNetInfo.LaneType prevLaneType = NetInfo.LaneType.None;\n\t\t\tif ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {\n\t\t\t\tNetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];\n\t\t\t\tprevMaxSpeed = GetLaneSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, prevLaneInfo); // NON-STOCK CODE\n\t\t\t\tprevLaneType = prevLaneInfo.m_laneType;\n\t\t\t\tif ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {\n\t\t\t\t\tprevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t}\n\t\t\t\tprevSpeed = this.CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // NON-STOCK CODE\n\t\t\t}\n\t\t\tfloat segLength;\n\t\t\tif (prevLaneType == NetInfo.LaneType.PublicTransport) {\n\t\t\t\tsegLength = netManager.m_lanes.m_buffer[item.m_laneID].m_length;\n\t\t\t} else {\n\t\t\t\tsegLength = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, prevSegment.m_averageLength);\n\t\t\t}\n\t\t\tfloat offsetLength = (float)Mathf.Abs((int)(connectOffset - item.m_position.m_offset)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * segLength;\n\t\t\tfloat methodDistance = item.m_methodDistance + offsetLength;\n\t\t\tfloat comparisonValue = item.m_comparisonValue + offsetLength / (prevSpeed * this.m_maxLength);\n\t\t\tfloat duration = item.m_duration + offsetLength / prevMaxSpeed; \n\t\t\tVector3 b = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\n\t\t\tif (! this.m_ignoreCost) {\n\t\t\t\tint ticketCost = netManager.m_lanes.m_buffer[item.m_laneID].m_ticketCost;\n\t\t\t\tif (ticketCost != 0) {\n\t\t\t\t\tcomparisonValue += (float)(ticketCost * this.m_pathRandomizer.Int32(2000u)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tuint laneIndex = 0;\n#if DEBUG\n\t\t\tint wIter = 0;\n#endif\n\t\t\twhile (laneIndex < nextNumLanes && curLaneId != 0u) {\n#if DEBUG\n\t\t\t\t++wIter;\n\t\t\t\tif (wIter >= 20) {\n\t\t\t\t\tLog.Error(\"Too many iterations in ProcessItem2!\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (nextLaneId == curLaneId) {\n\t\t\t\t\tNetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[laneIndex];\n\t\t\t\t\tif (nextLaneInfo.CheckType(this.m_laneTypes, this.m_vehicleTypes)) {\n\t\t\t\t\t\tVector3 a = netManager.m_lanes.m_buffer[nextLaneId].CalculatePosition((float)offset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\t\t\t\tfloat distance = Vector3.Distance(a, b);\n\t\t\t\t\t\tBufferItem nextItem;\n#if COUNTSEGMENTSTONEXTJUNCTION\n\t\t\t\t\t\t// NON-STOCK CODE START //\n\t\t\t\t\t\tif (prevIsRealJunction) {\n\t\t\t\t\t\t\tnextItem.m_numSegmentsToNextJunction = 0;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextItem.m_numSegmentsToNextJunction = item.m_numSegmentsToNextJunction + 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// NON-STOCK CODE END //\n#endif\n\t\t\t\t\t\tnextItem.m_position.m_segment = nextSegmentId;\n\t\t\t\t\t\tnextItem.m_position.m_lane = (byte)laneIndex;\n\t\t\t\t\t\tnextItem.m_position.m_offset = offset;\n\t\t\t\t\t\tif ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {\n\t\t\t\t\t\t\tnextItem.m_methodDistance = 0f;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextItem.m_methodDistance = methodDistance + distance;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfloat nextMaxSpeed = GetLaneSpeedLimit(nextSegmentId, (byte)laneIndex, curLaneId, nextLaneInfo); // NON-STOCK CODE\n\t\t\t\t\t\tif (nextLaneInfo.m_laneType != NetInfo.LaneType.Pedestrian || nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance || m_stablePath) {\n\t\t\t\t\t\t\tnextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this.m_maxLength);\n\t\t\t\t\t\t\tnextItem.m_duration = duration + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);\n\t\t\t\t\t\t\tif ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\t\t\t\t\tnextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tnextItem.m_direction = nextLaneInfo.m_finalDirection;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (nextLaneId == this.m_startLaneA) {\n\t\t\t\t\t\t\t\tif (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < this.m_startOffsetA) &&\n\t\t\t\t\t\t\t\t\t((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > this.m_startOffsetA)) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfloat nextSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE\n\t\t\t\t\t\t\t\tfloat nextOffsetDistance = (float)Mathf.Abs((int)nextItem.m_position.m_offset - (int)this.m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += nextOffsetDistance * nextSegment.m_averageLength / (nextSpeed * this.m_maxLength);\n\t\t\t\t\t\t\t\tnextItem.m_duration += nextOffsetDistance * nextSegment.m_averageLength / nextSpeed;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (nextLaneId == this.m_startLaneB) {\n\t\t\t\t\t\t\t\tif (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < this.m_startOffsetB) &&\n\t\t\t\t\t\t\t\t\t((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > this.m_startOffsetB)) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfloat nextSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE\n\t\t\t\t\t\t\t\tfloat nextOffsetDistance = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetB)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += nextOffsetDistance * nextSegment.m_averageLength / (nextSpeed * this.m_maxLength);\n\t\t\t\t\t\t\t\tnextItem.m_duration += nextOffsetDistance * nextSegment.m_averageLength / nextSpeed;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tnextItem.m_laneID = nextLaneId;\n\t\t\t\t\t\t\tnextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);\n\t\t\t\t\t\t\tnextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);\n\t\t\t\t\t\t\tnextItem.m_trafficRand = 0;\n#if DEBUGNEWPF\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tm_debugPositions[item.m_position.m_segment].Add(nextItem.m_position.m_segment);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\tthis.AddBufferItem(nextItem, item.m_position);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t++laneIndex;\n\t\t\t}\n\t\t}\n\n\t\tprivate 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) {\n\t\t\tbool foundForced = false;\n\t\t\treturn 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);\n\t\t}\n\n\t\t// 3\n\t\tprivate 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) {\n#if DEBUGNEWPF && DEBUG\n\t\t\tdebug = debug && m_conf.Debug.Switches[1];\n#else\n\t\t\tdebug = false;\n#endif\n\n#if DEBUGNEWPF && DEBUG\n\t\t\tList<String> logBuf = null;\n\t\t\tif (debug)\n\t\t\t\tlogBuf = new List<String>();\n#endif\n\n\t\t\tfoundForced = false;\n\t\t\tbool blocked = false;\n\t\t\tif ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {\n#if DEBUGNEWPF\n\t\t\t\tif (debug) {\n\t\t\t\t\tlogBuf.Add($\"Segment is PathFailed or flooded: {nextSegment.m_flags}\");\n\t\t\t\t\tlogBuf.Add(\"-- method returns --\");\n\t\t\t\t\tFlushCostLog(logBuf);\n\t\t\t\t}\n#endif\n\t\t\t\treturn blocked;\n\t\t\t}\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tNetInfo nextSegmentInfo = nextSegment.Info;\n\t\t\tNetInfo prevSegmentInfo = prevSegment.Info;\n\t\t\tint nextNumLanes = nextSegmentInfo.m_lanes.Length;\n\t\t\tNetInfo.Direction nextDir = (nextNodeId != nextSegment.m_startNode) ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;\n\t\t\tNetInfo.Direction nextFinalDir = ((nextSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? nextDir : NetInfo.InvertDirection(nextDir);\n\t\t\tfloat turningAngle = 1f;\n\n#if DEBUGNEWPF\n\t\t\tif (debug)\n\t\t\t\tlogBuf.Add($\"isStockLaneChangerUsed={Options.isStockLaneChangerUsed()}, _extVehicleType={queueItem.vehicleType}, nonBus={(queueItem.vehicleType & (ExtVehicleType.RoadVehicle & ~ExtVehicleType.Bus)) != ExtVehicleType.None}, _stablePath={m_stablePath}, enablePedestrian={enablePedestrian}, enableVehicle={enableVehicle}\");\n#endif\n\n\t\t\tfloat prevMaxSpeed = 1f;\n\t\t\tfloat prevLaneSpeed = 1f;\n\t\t\tNetInfo.LaneType prevLaneType = NetInfo.LaneType.None;\n\t\t\tVehicleInfo.VehicleType prevVehicleType = VehicleInfo.VehicleType.None;\n\t\t\t// NON-STOCK CODE START //\n\t\t\tbool nextIsStartNodeOfPrevSegment = prevSegment.m_startNode == nextNodeId;\n\t\t\tushort sourceNodeId = nextIsStartNodeOfPrevSegment ? prevSegment.m_endNode : prevSegment.m_startNode;\n\t\t\tbool prevIsJunction = (netManager.m_nodes.m_buffer[sourceNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction;\n#if COUNTSEGMENTSTONEXTJUNCTION\n\t\t\tbool prevIsRealJunction = prevIsJunction &&\n\t\t\t\t\t\t\t\t\t  (netManager.m_nodes.m_buffer[sourceNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);\n#endif\n\t\t\t/*bool nextIsRealJunction = (netManager.m_nodes.m_buffer[nextNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction &&\n\t\t\t\t\t\t\t\t\t  (netManager.m_nodes.m_buffer[nextNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);*/\n\t\t\tint prevOuterSimilarLaneIndex = -1;\n\t\t\tNetInfo.Lane prevLaneInfo = null;\n\t\t\t// NON-STOCK CODE END //\n\t\t\tif ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {\n\t\t\t\tprevLaneInfo = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];\n\t\t\t\tprevLaneType = prevLaneInfo.m_laneType;\n\t\t\t\tprevVehicleType = prevLaneInfo.m_vehicleType;\n\t\t\t\tprevMaxSpeed = GetLaneSpeedLimit(item.m_position.m_segment, item.m_position.m_lane, item.m_laneID, prevLaneInfo); // NON-STOCK CODE\n\t\t\t\tprevLaneSpeed = this.CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // NON-STOCK CODE\n\t\t\t\t\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tif ((byte)(prevLaneInfo.m_direction & NetInfo.Direction.Forward) != 0) {\n\t\t\t\t\tprevOuterSimilarLaneIndex = prevLaneInfo.m_similarLaneCount - prevLaneInfo.m_similarLaneIndex - 1;\n\t\t\t\t} else {\n\t\t\t\t\tprevOuterSimilarLaneIndex = prevLaneInfo.m_similarLaneIndex;\n\t\t\t\t}\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t}\n\n\t\t\tif (prevLaneType == NetInfo.LaneType.Vehicle && (prevVehicleType & VehicleInfo.VehicleType.Car) == VehicleInfo.VehicleType.None) {\n\t\t\t\t// check turning angle\n\n\t\t\t\tturningAngle = 0.01f - Mathf.Min(nextSegmentInfo.m_maxTurnAngleCos, prevSegmentInfo.m_maxTurnAngleCos);\n\t\t\t\tif (turningAngle < 1f) {\n\t\t\t\t\tVector3 prevDirection;\n\t\t\t\t\tif (nextNodeId == prevSegment.m_startNode) {\n\t\t\t\t\t\tprevDirection = prevSegment.m_startDirection;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprevDirection = prevSegment.m_endDirection;\n\t\t\t\t\t}\n\t\t\t\t\tVector3 nextDirection;\n\t\t\t\t\tif ((nextDir & NetInfo.Direction.Forward) != NetInfo.Direction.None) {\n\t\t\t\t\t\tnextDirection = nextSegment.m_endDirection;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextDirection = nextSegment.m_startDirection;\n\t\t\t\t\t}\n\t\t\t\t\tfloat dirDotProd = prevDirection.x * nextDirection.x + prevDirection.z * nextDirection.z;\n\t\t\t\t\tif (dirDotProd >= turningAngle) {\n#if DEBUGNEWPF\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tlogBuf.Add($\"turningAngle < 1f! dirDotProd={dirDotProd} >= turningAngle{turningAngle}!\");\n\t\t\t\t\t\t\tlogBuf.Add(\"-- method returns --\");\n\t\t\t\t\t\t\tFlushCostLog(logBuf);\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn blocked;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfloat prevDist;\n\t\t\tif (prevLaneType == NetInfo.LaneType.PublicTransport) {\n\t\t\t\tprevDist = netManager.m_lanes.m_buffer[item.m_laneID].m_length;\n\t\t\t} else {\n\t\t\t\tprevDist = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, prevSegment.m_averageLength);\n\t\t\t}\n\n\t\t\tfloat prevCost = prevDist;\n\n\n#if DEBUGNEWPF\n\t\t\tfloat oldPrevCost = prevCost;\n#endif\n\n\t\t\t// NON-STOCK CODE START\n\t\t\tif (segmentSelectionCost != null) {\n\t\t\t\tprevCost *= (float)segmentSelectionCost;\n\t\t\t}\n\n\t\t\tif (laneSelectionCost != null) {\n\t\t\t\tprevCost *= (float)laneSelectionCost;\n\t\t\t}\n\t\t\t// NON-STOCK CODE END\n\n#if DEBUGNEWPF\n\t\t\tif (debug)\n\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\"\\t\" + $\"applied traffic cost factors:\\n\" +\n\t\t\t\t\"\\t\" + $\"oldPrevCost={oldPrevCost}\\n\" +\n\t\t\t\t\"\\t\" + $\"=> prevCost={prevCost}\\n\"\n\t\t\t\t);\n#endif\n\n\t\t\t// stock code check for vehicle ban policies removed\n\t\t\t// stock code for transport lane usage control removed\n\n\t\t\t// calculate ticket costs\n\t\t\tfloat ticketCosts = 0f;\n\t\t\tif (! this.m_ignoreCost) {\n\t\t\t\tint ticketCost = netManager.m_lanes.m_buffer[item.m_laneID].m_ticketCost;\n\t\t\t\tif (ticketCost != 0) {\n\t\t\t\t\tticketCosts += (float)(ticketCost * this.m_pathRandomizer.Int32(2000u)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {\n\t\t\t\tprevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t}\n\t\t\tfloat prevOffsetCost = (float)Mathf.Abs((int)(connectOffset - item.m_position.m_offset)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevCost;\n\t\t\tfloat prevMethodDist = item.m_methodDistance + (float)Mathf.Abs((int)connectOffset - (int)item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevDist;\n\t\t\tfloat prevDuration = item.m_duration + prevOffsetCost / prevMaxSpeed;\n\t\t\t// NON-STOCK: vehicle restriction are applied to previous segment length in MainPathFind (not here, and not to prevOffsetCost)\n\t\t\tfloat prevComparisonPlusOffsetCostOverSpeed = item.m_comparisonValue + prevOffsetCost / (prevLaneSpeed * this.m_maxLength);\n\n\t\t\tif (!m_stablePath) {\n\t\t\t\t// CO randomization. Only randomizes over segments, not over lanes.\n\t\t\t\tif (segmentSelectionCost == null) { // NON-STOCK CODE\n\t\t\t\t\tRandomizer randomizer = new Randomizer(this.m_pathFindIndex << 16 | (uint)item.m_position.m_segment);\n\t\t\t\t\tprevOffsetCost *= (float)(randomizer.Int32(900, 1000 + (int)(prevSegment.m_trafficDensity * 10)) + this.m_pathRandomizer.Int32(20u)) * 0.001f;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tVector3 prevLaneConnectPos = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\tint newLaneIndexFromInner = laneIndexFromInner;\n\t\t\tbool transitionNode = (netManager.m_nodes.m_buffer[nextNodeId].m_flags & NetNode.Flags.Transition) != NetNode.Flags.None;\n\t\t\tNetInfo.LaneType allowedLaneTypes = this.m_laneTypes;\n\t\t\tVehicleInfo.VehicleType allowedVehicleTypes = this.m_vehicleTypes;\n\n\t\t\tif (!enableVehicle) {\n\t\t\t\tallowedVehicleTypes &= VehicleInfo.VehicleType.Bicycle;\n\t\t\t\tif (allowedVehicleTypes == VehicleInfo.VehicleType.None) {\n\t\t\t\t\tallowedLaneTypes &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!enablePedestrian) {\n\t\t\t\tallowedLaneTypes &= ~NetInfo.LaneType.Pedestrian;\n\t\t\t}\n\n#if DEBUGNEWPF\n\t\t\tif (debug)\n\t\t\t\tlogBuf.Add($\"allowedVehicleTypes={allowedVehicleTypes} allowedLaneTypes={allowedLaneTypes}\");\n#endif\n\n\t\t\t// NON-STOCK CODE START //\n\t\t\tfloat laneChangeBaseCosts = 1f;\n\t\t\tfloat junctionBaseCosts = 1f;\n\t\t\tif (laneChangingCostCalculationMode != LaneChangingCostCalculationMode.None) {\n\t\t\t\tfloat rand = (float)this.m_pathRandomizer.Int32(101u) / 100f;\n\t\t\t\tlaneChangeBaseCosts = m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost + rand * (m_conf.AdvancedVehicleAI.LaneChangingBaseMaxCost - m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost);\n\t\t\t\tif (prevIsJunction) {\n\t\t\t\t\tjunctionBaseCosts = m_conf.AdvancedVehicleAI.LaneChangingJunctionBaseCost;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE END //\n\n\t\t\tuint laneIndex = forcedLaneIndex != null ? (uint)forcedLaneIndex : 0u; // NON-STOCK CODE, forcedLaneIndex is not null if the next node is a (real) junction\n\t\t\tuint curLaneId = (uint)(forcedLaneId != null ? forcedLaneId : nextSegment.m_lanes); // NON-STOCK CODE, forceLaneId is not null if the next node is a (real) junction\n\t\t\twhile (laneIndex < nextNumLanes && curLaneId != 0u) {\n\t\t\t\t// NON-STOCK CODE START //\n\t\t\t\tif (forcedLaneIndex != null && laneIndex != forcedLaneIndex) {\n#if DEBUGNEWPF\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tlogBuf.Add($\"forceLaneIndex break! laneIndex={laneIndex}\");\n#endif\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t// NON-STOCK CODE END //\n\t\t\t\tNetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[laneIndex];\n\n\t\t\t\tif ((byte)(nextLaneInfo.m_finalDirection & nextFinalDir) != 0) {\n\t\t\t\t\t// lane direction is compatible\n#if DEBUGNEWPF\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tlogBuf.Add($\"Lane direction check passed: {nextLaneInfo.m_finalDirection}\");\n#endif\n\t\t\t\t\tif (nextLaneInfo.CheckType(allowedLaneTypes, allowedVehicleTypes) &&\n\t\t\t\t\t\t\t(nextSegmentId != item.m_position.m_segment || laneIndex != (int)item.m_position.m_lane)) {\n\t\t\t\t\t\t// vehicle types match and no u-turn to the previous lane\n\n#if DEBUGNEWPF\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tlogBuf.Add($\"vehicle type check passed: {nextLaneInfo.CheckType(allowedLaneTypes, allowedVehicleTypes)} && {(nextSegmentId != item.m_position.m_segment || laneIndex != (int)item.m_position.m_lane)}\");\n#endif\n\n\t\t\t\t\t\t// NON-STOCK CODE START //\n\t\t\t\t\t\tfloat nextMaxSpeed = GetLaneSpeedLimit(nextSegmentId, (byte)laneIndex, curLaneId, nextLaneInfo);\n\t\t\t\t\t\tfloat customDeltaCost = 0f;\n\t\t\t\t\t\t// NON-STOCK CODE END //\n\n\t\t\t\t\t\tVector3 nextLaneEndPointPos;\n\t\t\t\t\t\tif ((nextDir & NetInfo.Direction.Forward) != NetInfo.Direction.None) {\n\t\t\t\t\t\t\tnextLaneEndPointPos = netManager.m_lanes.m_buffer[curLaneId].m_bezier.d;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextLaneEndPointPos = netManager.m_lanes.m_buffer[curLaneId].m_bezier.a;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfloat transitionCost = Vector3.Distance(nextLaneEndPointPos, prevLaneConnectPos); // This gives the distance of the previous to next lane endpoints.\n\n#if DEBUGNEWPF\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tlogBuf.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}\");\n#endif\n\n\t\t\t\t\t\tif (transitionNode) {\n\t\t\t\t\t\t\ttransitionCost *= 2f;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tBufferItem nextItem;\n#if COUNTSEGMENTSTONEXTJUNCTION\n\t\t\t\t\t\t// NON-STOCK CODE START //\n\t\t\t\t\t\tif (prevIsRealJunction) {\n\t\t\t\t\t\t\tnextItem.m_numSegmentsToNextJunction = 0;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextItem.m_numSegmentsToNextJunction = item.m_numSegmentsToNextJunction + 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// NON-STOCK CODE END //\n#endif\n\t\t\t\t\t\tnextItem.m_comparisonValue = ticketCosts;\n\t\t\t\t\t\tnextItem.m_position.m_segment = nextSegmentId;\n\t\t\t\t\t\tnextItem.m_position.m_lane = (byte)laneIndex;\n\t\t\t\t\t\tnextItem.m_position.m_offset = (byte)(((nextDir & NetInfo.Direction.Forward) == 0) ? 0 : 255);\n\t\t\t\t\t\tif ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {\n\t\t\t\t\t\t\tnextItem.m_methodDistance = 0f;\n\t\t\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\t\t\tif (Options.realisticPublicTransport && isMiddle && nextLaneInfo.m_laneType == NetInfo.LaneType.PublicTransport && (item.m_lanesUsed & NetInfo.LaneType.PublicTransport) != NetInfo.LaneType.None) {\n\t\t\t\t\t\t\t\t// apply penalty when switching public transport vehicles\n\t\t\t\t\t\t\t\tfloat 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);\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tlogBuf.Add($\"applying public transport transition penalty: {transportTransitionPenalty}\");\n#endif\n\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += transportTransitionPenalty;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextItem.m_methodDistance = prevMethodDist + transitionCost;\n\t\t\t\t\t\t}\n\n#if DEBUGNEWPF\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tlogBuf.Add($\"checking if methodDistance is in range: {nextLaneInfo.m_laneType != NetInfo.LaneType.Pedestrian} || {nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance} ({nextItem.m_methodDistance})\");\n#endif\n\n\t\t\t\t\t\tif (nextLaneInfo.m_laneType != NetInfo.LaneType.Pedestrian || nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance || m_stablePath) {\n\n\t\t\t\t\t\t\t// NON-STOCK CODE START //\n\t\t\t\t\t\t\tif (laneChangingCostCalculationMode == LaneChangingCostCalculationMode.None) {\n\t\t\t\t\t\t\t\tfloat transitionCostOverMeanMaxSpeed = transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this.m_maxLength);\n\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += prevComparisonPlusOffsetCostOverSpeed + transitionCostOverMeanMaxSpeed; // stock code\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += item.m_comparisonValue;\n\t\t\t\t\t\t\t\tcustomDeltaCost = transitionCost + prevOffsetCost; // customDeltaCost now holds the costs for driving on the segment + costs for changing the segment\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tlogBuf.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}\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnextItem.m_duration = prevDuration + transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);\n\n\t\t\t\t\t\t\t// account for public tranport transition costs on non-PT paths\n\t\t\t\t\t\t\tif (\n#if DEBUG\n\t\t\t\t\t\t\t\t!m_conf.Debug.Switches[20] &&\n#endif\n\t\t\t\t\t\t\t\tOptions.realisticPublicTransport &&\n\t\t\t\t\t\t\t\t(curLaneId == this.m_startLaneA || curLaneId == this.m_startLaneB) &&\n\t\t\t\t\t\t\t\t(item.m_lanesUsed & (NetInfo.LaneType.Pedestrian | NetInfo.LaneType.PublicTransport)) == NetInfo.LaneType.Pedestrian) {\n\t\t\t\t\t\t\t\tfloat transportTransitionPenalty = (2f * m_conf.PathFinding.PublicTransportTransitionMaxPenalty) / (0.25f * this.m_maxLength);\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tlogBuf.Add($\"applying public transport transition penalty on non-PT path: {transportTransitionPenalty}\");\n#endif\n\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += transportTransitionPenalty;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// NON-STOCK CODE END //\n\n\t\t\t\t\t\t\tnextItem.m_direction = nextDir;\n\t\t\t\t\t\t\tif (curLaneId == this.m_startLaneA) {\n\t\t\t\t\t\t\t\tif (((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)) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"Current lane is start lane A. goto next lane\");\n#endif\n\t\t\t\t\t\t\t\t\tgoto CONTINUE_LANE_LOOP;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfloat nextLaneSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE\n\t\t\t\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetA)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\t\t\t\t\t\t\t\tfloat nextSegLength = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, nextSegment.m_averageLength);\n\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegLength / (nextLaneSpeed * this.m_maxLength);\n\t\t\t\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegLength / nextLaneSpeed;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (curLaneId == this.m_startLaneB) {\n\t\t\t\t\t\t\t\tif (((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)) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"Current lane is start lane B. goto next lane\");\n#endif\n\t\t\t\t\t\t\t\t\tgoto CONTINUE_LANE_LOOP;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfloat nextLaneSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE\n\t\t\t\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetB)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\t\t\t\t\t\t\t\tfloat nextSegLength = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, nextSegment.m_averageLength);\n\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegLength / (nextLaneSpeed * this.m_maxLength);\n\t\t\t\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegLength / nextLaneSpeed;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (!this.m_ignoreBlocked && (nextSegment.m_flags & NetSegment.Flags.Blocked) != NetSegment.Flags.None &&\n\t\t\t\t\t\t\t\t(byte)(nextLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {\n\t\t\t\t\t\t\t\t// NON-STOCK CODE START //\n\t\t\t\t\t\t\t\tif (laneChangingCostCalculationMode != LaneChangingCostCalculationMode.None) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\tfloat oldCustomDeltaCost = customDeltaCost;\n#endif\n\t\t\t\t\t\t\t\t\t// apply vanilla game restriction policies\n\t\t\t\t\t\t\t\t\tcustomDeltaCost *= 10f;\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"applied blocked road cost factor on activated AI:\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"oldCustomDeltaCost={oldCustomDeltaCost}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"=> customDeltaCost={customDeltaCost}\\n\"\n\t\t\t\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// NON-STOCK CODE END //\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tlogBuf.Add($\"Applying blocked road cost factor on disabled advanced AI\");\n#endif\n\n\t\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += 0.1f;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tblocked = true;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ((byte)(nextLaneInfo.m_laneType & prevLaneType) != 0 && nextLaneInfo.m_vehicleType == prevVehicleType) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tlogBuf.Add($\"Applying stock lane changing costs. obeyStockLaneArrows={obeyStockLaneArrows}\");\n#endif\n\n\n\t\t\t\t\t\t\t\tif (obeyStockLaneArrows) {\n\t\t\t\t\t\t\t\t\t// this is CO's way of matching lanes between segments\n\t\t\t\t\t\t\t\t\tint firstTarget = (int)netManager.m_lanes.m_buffer[curLaneId].m_firstTarget;\n\t\t\t\t\t\t\t\t\tint lastTarget = (int)netManager.m_lanes.m_buffer[curLaneId].m_lastTarget;\n\t\t\t\t\t\t\t\t\tif (laneIndexFromInner < firstTarget || laneIndexFromInner >= lastTarget) {\n\t\t\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += Mathf.Max(1f, transitionCost * 3f - 3f) / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this.m_maxLength);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} // NON-STOCK CODE\n\n\t\t\t\t\t\t\t\t// stock code that prohibits cars to be on public transport lanes removed\n\t\t\t\t\t\t\t\t/*if (!this._transportVehicle && nextLaneInfo.m_laneType == NetInfo.LaneType.TransportVehicle) {\n\t\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += 20f / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this._maxLength);\n\t\t\t\t\t\t\t\t}*/\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// NON-STOCK CODE START //\n\t\t\t\t\t\t\tbool addItem = true; // should we add the next item to the buffer?\n\t\t\t\t\t\t\tif (laneChangingCostCalculationMode != LaneChangingCostCalculationMode.None) {\n\t\t\t\t\t\t\t\t// Advanced AI cost calculation\n\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tlogBuf.Add($\"Calculating advanced AI lane changing costs\");\n#endif\n\n\t\t\t\t\t\t\t\tint laneDist; // absolute lane distance\n\t\t\t\t\t\t\t\tif (laneChangingCostCalculationMode == LaneChangingCostCalculationMode.ByGivenDistance && forcedLaneDist != null) {\n\t\t\t\t\t\t\t\t\tlaneDist = (byte)forcedLaneDist;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tint nextOuterSimilarLaneIndex;\n\t\t\t\t\t\t\t\t\tif ((byte)(nextLaneInfo.m_direction & NetInfo.Direction.Forward) != 0) {\n\t\t\t\t\t\t\t\t\t\tnextOuterSimilarLaneIndex = nextLaneInfo.m_similarLaneCount - nextLaneInfo.m_similarLaneIndex - 1;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tnextOuterSimilarLaneIndex = nextLaneInfo.m_similarLaneIndex;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tint relLaneDist = nextOuterSimilarLaneIndex - prevOuterSimilarLaneIndex; // relative lane distance (positive: change to more outer lane, negative: change to more inner lane)\n\t\t\t\t\t\t\t\t\tlaneDist = Math.Abs(relLaneDist);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// apply lane changing costs\n\t\t\t\t\t\t\t\tfloat laneMetric = 1f;\n\t\t\t\t\t\t\t\tbool relaxedLaneChanging = queueItem.vehicleId == 0 && (curLaneId == m_startLaneA || curLaneId == m_startLaneB);\n\t\t\t\t\t\t\t\tif (laneDist > 0 && !relaxedLaneChanging) {\n\t\t\t\t\t\t\t\t\tlaneMetric = 1f + laneDist *\n\t\t\t\t\t\t\t\t\t\tjunctionBaseCosts *\n\t\t\t\t\t\t\t\t\t\tlaneChangeBaseCosts * // road type based lane changing cost factor\n\t\t\t\t\t\t\t\t\t\t(laneDist > 1 ? m_conf.AdvancedVehicleAI.MoreThanOneLaneChangingCostFactor : 1f); // additional costs for changing multiple lanes at once\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// on highways: avoid lane changing before junctions: multiply with inverted distance to next junction\n\t\t\t\t\t\t\t\t/*float junctionMetric = 1f;\n\t\t\t\t\t\t\t\tif (prevSegmentRouting.highway && nextSegmentRouting.highway && // we are on a highway road\n\t\t\t\t\t\t\t\t\t!nextIsRealJunction && // next is not a junction\n\t\t\t\t\t\t\t\t\tlaneDist > 0) {\n\t\t\t\t\t\t\t\t\tuint dist = _pathRandomizer.UInt32(_conf.MinHighwayInterchangeSegments, (_conf.MaxHighwayInterchangeSegments >= _conf.MinHighwayInterchangeSegments ? _conf.MaxHighwayInterchangeSegments : _conf.MinHighwayInterchangeSegments) + 1u);\n\t\t\t\t\t\t\t\t\tif (nextItem.m_numSegmentsToNextJunction < dist) {\n\t\t\t\t\t\t\t\t\t\tjunctionMetric = _conf.HighwayInterchangeLaneChangingBaseCost * (float)(dist - nextItem.m_numSegmentsToNextJunction);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}*/\n\n\t\t\t\t\t\t\t\t// total metric value\n\t\t\t\t\t\t\t\tfloat metric = laneMetric/* * junctionMetric*/;\n\n\t\t\t\t\t\t\t\t//float oldTransitionDistanceOverMaxSpeed = transitionCostOverMeanMaxSpeed;\n\t\t\t\t\t\t\t\tfloat finalDeltaCost = (metric * customDeltaCost) / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * this.m_maxLength);\n\n//\t\t\t\t\t\t\t\tif (finalDeltaCost < 0f) {\n//\t\t\t\t\t\t\t\t\t// should never happen\n//#if DEBUG\n//\t\t\t\t\t\t\t\t\tLog.Error($\"THREAD #{Thread.CurrentThread.ManagedThreadId}, PF {this._pathFindIndex}: distanceOverMeanMaxSpeed < 0! seg. {nextSegmentId}, lane {laneIndex}, off {nextItem.m_position.m_offset} -> {item.m_position.m_segment}, lane {item.m_position.m_lane}, off {item.m_position.m_offset}. distanceOverMeanMaxSpeed={finalDeltaCost}, prevSpeed={prevUsage}\"/* + \", prevSpeed={prevSpeed}\"*/);\n//#endif\n//\t\t\t\t\t\t\t\t\tfinalDeltaCost = 0f;\n//\t\t\t\t\t\t\t\t} else if (Single.IsNaN(finalDeltaCost) || Single.IsInfinity(finalDeltaCost)) {\n//\t\t\t\t\t\t\t\t\t// Fallback if we mess something up. Should never happen.\n//#if DEBUG\n//\t\t\t\t\t\t\t\t\t//if (costDebug)\n//\t\t\t\t\t\t\t\t\tLog.Error($\"Pathfinder ({this._pathFindIndex}): distanceOverMeanMaxSpeed is NaN or Infinity: seg. {nextSegmentId}, lane {laneIndex}, off {nextItem.m_position.m_offset} -> {item.m_position.m_segment}, lane {item.m_position.m_lane}, off {item.m_position.m_offset}. {finalDeltaCost} // nextMaxSpeed={nextMaxSpeed} prevMaxSpeed={prevMaxSpeed} nextMaxSpeed={nextMaxSpeed} laneDist={laneDist} laneMetric={laneMetric} metric={metric}\");\n//#endif\n//#if DEBUGNEWPF\n//\t\t\t\t\t\t\t\t\tLog.Error($\"THREAD #{Thread.CurrentThread.ManagedThreadId}, PF {this._pathFindIndex}: deltaCostOverMeanMaxSpeed is NaN! deltaCostOverMeanMaxSpeed={finalDeltaCost}\");\n//#endif\n//\t\t\t\t\t\t\t\t\tfinalDeltaCost = oldTransitionDistanceOverMaxSpeed;\n//\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += finalDeltaCost;\n\n\t\t\t\t\t\t\t\tif (nextItem.m_comparisonValue > 1f) {\n\t\t\t\t\t\t\t\t\t// comparison value got too big. Do not add the lane to the buffer\n\t\t\t\t\t\t\t\t\taddItem = false;\n\t\t\t\t\t\t\t\t}\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"-> TRANSIT to seg. {nextSegmentId}, lane {laneIndex}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"prevMaxSpeed={prevMaxSpeed}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextMaxSpeed={nextMaxSpeed}\\n\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"laneChangingCostCalculationMode={laneChangingCostCalculationMode}\\n\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"laneDist={laneDist}\\n\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"_extVehicleType={queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"laneChangeRoadBaseCost={laneChangeBaseCosts}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"moreThanOneLaneCost={(laneDist > 1 ? m_conf.AdvancedVehicleAI.MoreThanOneLaneChangingCostFactor : 1f)}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"=> laneMetric={laneMetric}\\n\" +\n\t\t\t\t\t\t\t\t\t\t//\"\\t\" + $\"=> junctionMetric={junctionMetric}\\n\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"=> metric={metric}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"deltaCostOverMeanMaxSpeed={finalDeltaCost}\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextItem.m_comparisonValue={nextItem.m_comparisonValue}\\n\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"=> addItem={addItem}\\n\"\n\t\t\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (forcedLaneIndex != null && laneIndex == forcedLaneIndex && addItem) {\n\t\t\t\t\t\t\t\tfoundForced = true;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (addItem) {\n\t\t\t\t\t\t\t\t// NON-STOCK CODE END //\n\n\t\t\t\t\t\t\t\tnextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);\n\t\t\t\t\t\t\t\tnextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);\n\t\t\t\t\t\t\t\tnextItem.m_laneID = curLaneId;\n\t\t\t\t\t\t\t\tnextItem.m_trafficRand = item.m_trafficRand;\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tlogBuf.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}\");\n\t\t\t\t\t\t\t\t\tm_debugPositions[item.m_position.m_segment].Add(nextItem.m_position.m_segment);\n\t\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t\tthis.AddBufferItem(nextItem, item.m_position);\n\t\t\t\t\t\t\t\t// NON-STOCK CODE START //\n\t\t\t\t\t\t\t} else {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tlogBuf.Add($\"item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"-> item seg. {nextSegmentId}, lane {laneIndex} NOT ADDED\\n\"\n\t\t\t\t\t\t\t\t\t\t);\n#endif\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// NON-STOCK CODE END //\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tgoto CONTINUE_LANE_LOOP;\n\t\t\t\t}\n\n\t\t\t\tif ((byte)(nextLaneInfo.m_laneType & prevLaneType) != 0 && (nextLaneInfo.m_vehicleType & prevVehicleType) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\t++newLaneIndexFromInner;\n\t\t\t\t}\n\n\t\t\t\tCONTINUE_LANE_LOOP:\n\n\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t++laneIndex;\n\t\t\t\tcontinue;\n\t\t\t} // foreach lane\n\t\t\tlaneIndexFromInner = newLaneIndexFromInner;\n\n#if DEBUGNEWPF\n\t\t\tif (debug) {\n\t\t\t\tlogBuf.Add(\"-- method returns --\");\n\t\t\t\tFlushCostLog(logBuf);\n\t\t\t}\n#endif\n\t\t\treturn blocked;\n\t\t}\n\n#if DEBUGNEWPF\n\t\tprivate void FlushCostLog(List<String> logBuf) {\n\t\t\tif (logBuf == null)\n\t\t\t\treturn;\n\n\t\t\tforeach (String toLog in logBuf) {\n\t\t\t\tLog._Debug($\"Pathfinder ({this.m_pathFindIndex}) for unit {Calculating} *COSTS*: \" + toLog);\n\t\t\t}\n\t\t\tlogBuf.Clear();\n\t\t}\n\n\t\tprivate void FlushMainLog(List<String> logBuf, uint unitId) {\n\t\t\tif (logBuf == null)\n\t\t\t\treturn;\n\n\t\t\tforeach (String toLog in logBuf) {\n\t\t\t\tLog._Debug($\"Pathfinder ({this.m_pathFindIndex}) for unit {Calculating} *MAIN*: \" + toLog);\n\t\t\t}\n\t\t\tlogBuf.Clear();\n\t\t}\n#endif\n\n\t\t// 4\n\t\tprivate void ProcessItemPedBicycle(bool debug, BufferItem item, ushort nextNodeId, ushort nextSegmentId, ref NetSegment prevSegment, ref NetSegment nextSegment, byte connectOffset, byte laneSwitchOffset, int nextLaneIndex, uint nextLaneId) {\n#if DEBUGNEWPF && DEBUG\n\t\t\tList<String> logBuf = null;\n\t\t\tif (debug) {\n\t\t\t\tlogBuf = new List<String>();\n\t\t\t\tlogBuf.Add($\"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: exploring\\n\" +\n\t\t\t\t\t\t\"\\t\" + $\"nextSegmentId={nextSegmentId}\\n\" +\n\t\t\t\t\t\t\"\\t\" + $\"connectOffset={connectOffset}\\n\" +\n\t\t\t\t\t\t\"\\t\" + $\"laneSwitchOffset={laneSwitchOffset}\\n\" +\n\t\t\t\t\t\t\"\\t\" + $\"nextLaneIndex={nextLaneIndex}\\n\" +\n\t\t\t\t\t\t\"\\t\" + $\"nextLaneId={nextLaneId}\\n\");\n\t\t\t}\n#endif\n\n\t\t\tif ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {\n#if DEBUGNEWPF\n\t\t\t\tif (debug) {\n\t\t\t\t\tlogBuf.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\" +\n\t\t\t\t\t\t\"\\t\" + $\"nextSegment.m_flags={nextSegment.m_flags}\\n\" +\n\t\t\t\t\t\t\"\\t\" + $\"_disableMask={m_disableMask}\\n\");\n\t\t\t\t\tFlushCostLog(logBuf);\n\t\t\t\t}\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\t// NON-STOCK CODE START\n\t\t\tbool nextIsRegularNode = nextNodeId == nextSegment.m_startNode || nextNodeId == nextSegment.m_endNode;\n#if COUNTSEGMENTSTONEXTJUNCTION\n\t\t\tbool prevIsRealJunction = false;\n#endif\n\t\t\tif (nextIsRegularNode) {\n\t\t\t\tbool nextIsStartNode = nextNodeId == nextSegment.m_startNode;\n\t\t\t\tushort prevNodeId = (nextNodeId == prevSegment.m_startNode) ? prevSegment.m_endNode : prevSegment.m_startNode;\n#if COUNTSEGMENTSTONEXTJUNCTION\n\t\t\t\tprevIsRealJunction = (netManager.m_nodes.m_buffer[prevNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) != NetNode.Flags.Junction &&\n\t\t\t\t\t\t\t\t\t (netManager.m_nodes.m_buffer[prevNodeId].m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut);\n#endif\n\n\t\t\t\t// check if pedestrians are not allowed to cross here\n\t\t\t\tif (!junctionManager.IsPedestrianCrossingAllowed(nextSegmentId, nextIsStartNode)) {\n#if DEBUGNEWPF\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tlogBuf.Add($\"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- pedestrian crossing not allowed!\\n\");\n\t\t\t\t\t\tFlushCostLog(logBuf);\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// check if pedestrian light won't change to green\n\t\t\t\tICustomSegmentLights lights = customTrafficLightsManager.GetSegmentLights(nextSegmentId, nextIsStartNode, false);\n\t\t\t\tif (lights != null) {\n\t\t\t\t\tif (lights.InvalidPedestrianLight) {\n#if DEBUGNEWPF\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tlogBuf.Add($\"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- invalid pedestrian lights!\\n\");\n\t\t\t\t\t\t\tFlushCostLog(logBuf);\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// NON-STOCK CODE END\n\t\t\t\n\t\t\t// NON-STOCK CODE START\n\t\t\t/*if (!_allowEscapeTransport) {\n\t\t\t\tushort transportLineId = netManager.m_nodes.m_buffer[targetNodeId].m_transportLine;\n\t\t\t\tif (transportLineId != 0 && Singleton<TransportManager>.instance.m_lines.m_buffer[transportLineId].Info.m_transportType == TransportInfo.TransportType.EvacuationBus)\n\t\t\t\t\treturn;\n\t\t\t}*/\n\t\t\t// NON-STOCK CODE END\n\t\t\tNetInfo nextSegmentInfo = nextSegment.Info;\n\t\t\tNetInfo prevSegmentInfo = prevSegment.Info;\n\t\t\tint numLanes = nextSegmentInfo.m_lanes.Length;\n\t\t\tfloat distance;\n\t\t\tbyte offset;\n\t\t\tVector3 b = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)laneSwitchOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\tif (nextSegmentId == item.m_position.m_segment) {\n\t\t\t\t// next segment is previous segment\n\t\t\t\tVector3 a = netManager.m_lanes.m_buffer[nextLaneId].CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\t\tdistance = Vector3.Distance(a, b);\n\t\t\t\toffset = connectOffset;\n\t\t\t} else {\n\t\t\t\t// next segment differs from previous segment\n\t\t\t\tNetInfo.Direction direction = (nextNodeId != nextSegment.m_startNode) ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;\n\t\t\t\tVector3 a;\n\t\t\t\tif ((byte)(direction & NetInfo.Direction.Forward) != 0) {\n\t\t\t\t\ta = netManager.m_lanes.m_buffer[nextLaneId].m_bezier.d;\n\t\t\t\t} else {\n\t\t\t\t\ta = netManager.m_lanes.m_buffer[nextLaneId].m_bezier.a;\n\t\t\t\t}\n\t\t\t\tdistance = Vector3.Distance(a, b);\n\t\t\t\toffset = (byte)(((direction & NetInfo.Direction.Forward) == 0) ? 0 : 255);\n\t\t\t}\n\t\t\tfloat prevMaxSpeed = 1f;\n\t\t\tfloat prevSpeed = 1f;\n\t\t\tNetInfo.LaneType laneType = NetInfo.LaneType.None;\n\t\t\tVehicleInfo.VehicleType vehicleType = VehicleInfo.VehicleType.None; // NON-STOCK CODE\n\t\t\tif ((int)item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {\n\t\t\t\tNetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[(int)item.m_position.m_lane];\n\t\t\t\tprevMaxSpeed = 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\n\t\t\t\tlaneType = prevLaneInfo.m_laneType;\n\t\t\t\tvehicleType = prevLaneInfo.m_vehicleType; // NON-STOCK CODE\n\t\t\t\tif ((byte)(laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {\n\t\t\t\t\tlaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t}\n\t\t\t\tprevSpeed = this.CalculateLaneSpeed(prevMaxSpeed, laneSwitchOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // NON-STOCK CODE\n\t\t\t}\n\n\t\t\tfloat segLength;\n\t\t\tif (laneType == NetInfo.LaneType.PublicTransport) {\n\t\t\t\tsegLength = netManager.m_lanes.m_buffer[item.m_laneID].m_length;\n\t\t\t} else {\n\t\t\t\tsegLength = Mathf.Max(SEGMENT_MIN_AVERAGE_LENGTH, prevSegment.m_averageLength);\n\t\t\t}\n\t\t\tfloat offsetLength = (float)Mathf.Abs((int)(laneSwitchOffset - item.m_position.m_offset)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * segLength;\n\t\t\tfloat methodDistance = item.m_methodDistance + offsetLength;\n\t\t\tfloat comparisonValue = item.m_comparisonValue + offsetLength / (prevSpeed * this.m_maxLength);\n\t\t\tfloat duration = item.m_duration + offsetLength / prevMaxSpeed;\n\n\t\t\tif (! this.m_ignoreCost) {\n\t\t\t\tint ticketCost = netManager.m_lanes.m_buffer[item.m_laneID].m_ticketCost;\n\t\t\t\tif (ticketCost != 0) {\n\t\t\t\t\tcomparisonValue += (float)(ticketCost * this.m_pathRandomizer.Int32(2000u)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (nextLaneIndex < numLanes) {\n\t\t\t\tNetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];\n\t\t\t\tBufferItem nextItem;\n#if COUNTSEGMENTSTONEXTJUNCTION\n\t\t\t\t// NON-STOCK CODE START //\n\t\t\t\tif (prevIsRealJunction) {\n\t\t\t\t\tnextItem.m_numSegmentsToNextJunction = 0;\n\t\t\t\t} else {\n\t\t\t\t\tnextItem.m_numSegmentsToNextJunction = item.m_numSegmentsToNextJunction + 1;\n\t\t\t\t}\n\t\t\t\t// NON-STOCK CODE END //\n#endif\n\t\t\t\tnextItem.m_position.m_segment = nextSegmentId;\n\t\t\t\tnextItem.m_position.m_lane = (byte)nextLaneIndex;\n\t\t\t\tnextItem.m_position.m_offset = offset;\n\t\t\t\tif ((nextLaneInfo.m_laneType & laneType) == NetInfo.LaneType.None) {\n\t\t\t\t\tnextItem.m_methodDistance = 0f;\n\t\t\t\t} else {\n\t\t\t\t\tif (item.m_methodDistance == 0f) {\n\t\t\t\t\t\tcomparisonValue += 100f / (0.25f * this.m_maxLength);\n\t\t\t\t\t}\n\t\t\t\t\tnextItem.m_methodDistance = methodDistance + distance;\n\t\t\t\t}\n\n\t\t\t\tfloat nextMaxSpeed = GetLaneSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo); // NON-STOCK CODE\n\t\t\t\tif (nextLaneInfo.m_laneType != NetInfo.LaneType.Pedestrian || nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance || m_stablePath) {\n\t\t\t\t\tnextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.25f * this.m_maxLength);\n\t\t\t\t\tnextItem.m_duration = duration + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);\n\t\t\t\t\tif ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\t\t\tnextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextItem.m_direction = nextLaneInfo.m_finalDirection;\n\t\t\t\t\t}\n\t\t\t\t\tif (nextLaneId == this.m_startLaneA) {\n\t\t\t\t\t\tif (((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)) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tlogBuf.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\");\n\t\t\t\t\t\t\t\tFlushCostLog(logBuf);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfloat nextSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE\n\t\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetA)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\t\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * this.m_maxLength);\n\t\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;\n\t\t\t\t\t}\n\t\t\t\t\tif (nextLaneId == this.m_startLaneB) {\n\t\t\t\t\t\tif (((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)) {\n#if DEBUGNEWPF\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tlogBuf.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\");\n\t\t\t\t\t\t\t\tFlushCostLog(logBuf);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfloat nextSpeed = this.CalculateLaneSpeed(nextMaxSpeed, this.m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo); // NON-STOCK CODE\n\t\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs((int)(nextItem.m_position.m_offset - this.m_startOffsetB)) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\t\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * this.m_maxLength);\n\t\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;\n\t\t\t\t\t}\n\t\t\t\t\tnextItem.m_laneID = nextLaneId;\n\t\t\t\t\tnextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);\n\t\t\t\t\tnextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);\n\t\t\t\t\tnextItem.m_trafficRand = 0;\n#if DEBUGNEWPF\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tlogBuf.Add($\"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: *ADDING*\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"nextItem.m_laneID={nextItem.m_laneID}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"nextItem.m_lanesUsed={nextItem.m_lanesUsed}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"nextItem.m_vehiclesUsed={nextItem.m_vehiclesUsed }\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"nextItem.m_comparisonValue={nextItem.m_comparisonValue}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"nextItem.m_methodDistance={nextItem.m_methodDistance}\\n\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\tFlushCostLog(logBuf);\n\n\t\t\t\t\t\tm_debugPositions[item.m_position.m_segment].Add(nextItem.m_position.m_segment);\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tthis.AddBufferItem(nextItem, item.m_position);\n\t\t\t\t} else {\n#if DEBUGNEWPF\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tlogBuf.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\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"nextItem.m_methodDistance={nextItem.m_methodDistance}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}\\n\"\n\t\t\t\t\t\t);\n\t\t\t\t\t\tFlushCostLog(logBuf);\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\t\t\t} else {\n#if DEBUGNEWPF\n\t\t\t\tif (debug) {\n\t\t\t\t\tlogBuf.Add($\"*PED* item: seg. {item.m_position.m_segment}, lane {item.m_position.m_lane}, node {nextNodeId}: -NOT ADDING- nextLaneIndex >= numLanes ({nextLaneIndex} >= {numLanes})!\\n\");\n\t\t\t\t\tFlushCostLog(logBuf);\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t}\n\n\t\tprivate float CalculateLaneSpeed(float speedLimit, byte startOffset, byte endOffset, ref NetSegment segment, NetInfo.Lane laneInfo) {\n\t\t\t/*if ((laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram)) != VehicleInfo.VehicleType.None)\n\t\t\t\tspeedLimit = laneInfo.m_speedLimit;\n\t\t\t*/\n\t\t\tNetInfo.Direction direction = ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? laneInfo.m_finalDirection : NetInfo.InvertDirection(laneInfo.m_finalDirection);\n\t\t\tif ((byte)(direction & NetInfo.Direction.Avoid) == 0) {\n\t\t\t\treturn speedLimit;\n\t\t\t}\n\t\t\tif (endOffset > startOffset && direction == NetInfo.Direction.AvoidForward) {\n\t\t\t\treturn speedLimit * 0.1f;\n\t\t\t}\n\t\t\tif (endOffset < startOffset && direction == NetInfo.Direction.AvoidBackward) {\n\t\t\t\treturn speedLimit * 0.1f;\n\t\t\t}\n\t\t\treturn speedLimit * 0.2f;\n\t\t}\n\n\t\tprivate void AddBufferItem(BufferItem item, PathUnit.Position target) {\n\t\t\tuint laneLocation = m_laneLocation[item.m_laneID];\n\t\t\tuint locPathFindIndex = laneLocation >> 16; // upper 16 bit, expected (?) path find index\n\t\t\tint bufferIndex = (int)(laneLocation & 65535u); // lower 16 bit\n\t\t\tint comparisonBufferPos;\n\t\t\tif (locPathFindIndex == m_pathFindIndex) {\n\t\t\t\tif (item.m_comparisonValue >= m_buffer[bufferIndex].m_comparisonValue) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tint bufferPosIndex = bufferIndex >> 6; // arithmetic shift (sign stays), upper 10 bit\n\t\t\t\tint bufferPos = bufferIndex & -64; // upper 10 bit (no shift)\n\t\t\t\tif (bufferPosIndex < m_bufferMinPos || (bufferPosIndex == m_bufferMinPos && bufferPos < m_bufferMin[bufferPosIndex])) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcomparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);\n\t\t\t\tif (comparisonBufferPos == bufferPosIndex) {\n\t\t\t\t\tm_buffer[bufferIndex] = item;\n\t\t\t\t\tm_laneTarget[item.m_laneID] = target;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tint newBufferIndex = bufferPosIndex << 6 | m_bufferMax[bufferPosIndex]--;\n\t\t\t\tBufferItem bufferItem = m_buffer[newBufferIndex];\n\t\t\t\tm_laneLocation[bufferItem.m_laneID] = laneLocation;\n\t\t\t\tm_buffer[bufferIndex] = bufferItem;\n\t\t\t} else {\n\t\t\t\tcomparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);\n\t\t\t}\n\n\t\t\tif (comparisonBufferPos >= 1024) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (comparisonBufferPos < 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\twhile (m_bufferMax[comparisonBufferPos] == 63) {\n\t\t\t\t++comparisonBufferPos;\n\t\t\t\tif (comparisonBufferPos == 1024) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (comparisonBufferPos > m_bufferMaxPos) {\n\t\t\t\tm_bufferMaxPos = comparisonBufferPos;\n\t\t\t}\n\n\t\t\tbufferIndex = (comparisonBufferPos << 6 | ++m_bufferMax[comparisonBufferPos]);\n\t\t\tm_buffer[bufferIndex] = item;\n\t\t\tm_laneLocation[item.m_laneID] = (m_pathFindIndex << 16 | (uint)bufferIndex);\n\t\t\tm_laneTarget[item.m_laneID] = target;\n\t\t}\n\n\t\tprivate void GetLaneDirection(PathUnit.Position pathPos, out NetInfo.Direction direction, out NetInfo.LaneType laneType, out VehicleInfo.VehicleType vehicleType) {\n\t\t\tNetManager instance = Singleton<NetManager>.instance;\n\t\t\tNetInfo info = instance.m_segments.m_buffer[pathPos.m_segment].Info;\n\t\t\tif (info.m_lanes.Length > pathPos.m_lane) {\n\t\t\t\tdirection = info.m_lanes[pathPos.m_lane].m_finalDirection;\n\t\t\t\tlaneType = info.m_lanes[pathPos.m_lane].m_laneType;\n\t\t\t\tvehicleType = info.m_lanes[pathPos.m_lane].m_vehicleType;\n\t\t\t\tif ((instance.m_segments.m_buffer[pathPos.m_segment].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\t\tdirection = NetInfo.InvertDirection(direction);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdirection = NetInfo.Direction.None;\n\t\t\t\tlaneType = NetInfo.LaneType.None;\n\t\t\t\tvehicleType = VehicleInfo.VehicleType.None;\n\t\t\t}\n\t\t}\n\n\t\tprivate void PathFindThread() {\n\t\t\twhile (true) {\n\t\t\t\t//Log.Message($\"Pathfind Thread #{Thread.CurrentThread.ManagedThreadId} iteration!\");\n\t\t\t\ttry {\n\t\t\t\t\tMonitor.Enter(QueueLock);\n\n\t\t\t\t\twhile (QueueFirst == 0u && !Terminated) {\n\t\t\t\t\t\tMonitor.Wait(QueueLock);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Terminated) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tCalculating = QueueFirst;\n\t\t\t\t\tQueueFirst = CustomPathManager._instance.queueItems[Calculating].nextPathUnitId;\n\t\t\t\t\t//QueueFirst = PathUnits.m_buffer[Calculating].m_nextPathUnit;\n\t\t\t\t\tif (QueueFirst == 0u) {\n\t\t\t\t\t\tQueueLast = 0u;\n\t\t\t\t\t\tm_queuedPathFindCount = 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t--m_queuedPathFindCount;\n\t\t\t\t\t}\n\n\t\t\t\t\tCustomPathManager._instance.queueItems[Calculating].nextPathUnitId = 0u;\n\t\t\t\t\t//PathUnits.m_buffer[Calculating].m_nextPathUnit = 0u;\n\n\t\t\t\t\t// check if path unit is created\n\t\t\t\t\t/*if ((PathUnits.m_buffer[Calculating].m_pathFindFlags & PathUnit.FLAG_CREATED) == 0) {\n\t\t\t\t\t\tLog.Warning($\"CustomPathFind: Refusing to calculate path unit {Calculating} which is not created!\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}*/\n\n\t\t\t\t\tPathUnits.m_buffer[Calculating].m_pathFindFlags = (byte)((PathUnits.m_buffer[Calculating].m_pathFindFlags & ~PathUnit.FLAG_CREATED) | PathUnit.FLAG_CALCULATING);\n\t\t\t\t\tthis.queueItem = CustomPathManager._instance.queueItems[Calculating];\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.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()}\");\n\t\t\t\t} finally {\n\t\t\t\t\tMonitor.Exit(QueueLock);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// calculate path unit\n\t\t\t\ttry {\n\t\t\t\t\tPathFindImplementation(Calculating, ref PathUnits.m_buffer[Calculating]);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tLog.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()}\");\n\t\t\t\t\t//UIView.ForwardException(ex);\n\n#if DEBUG\n\t\t\t\t\t++m_failedPathFinds;\n\n#if DEBUGNEWPF\n\t\t\t\t\tbool debug = this.m_debug;\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"THREAD #{Thread.CurrentThread.ManagedThreadId} PF {this.m_pathFindIndex}: Could not find path for unit {Calculating} -- exception occurred in PathFindImplementation\");\n#endif\n#endif\n\t\t\t\t\t//CustomPathManager._instance.ResetQueueItem(Calculating);\n\n\t\t\t\t\tPathUnits.m_buffer[Calculating].m_pathFindFlags |= PathUnit.FLAG_FAILED;\n\t\t\t\t} finally {\n\t\t\t\t}\n\t\t\t\t//tCurrentState = 10;\n#if DEBUGLOCKS\n\t\t\t\tlockIter = 0;\n#endif\n\n\t\t\t\ttry {\n\t\t\t\t\tMonitor.Enter(QueueLock);\n\n\t\t\t\t\tPathUnits.m_buffer[Calculating].m_pathFindFlags = (byte)(PathUnits.m_buffer[Calculating].m_pathFindFlags & ~PathUnit.FLAG_CALCULATING);\n\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\ttry {\n\t\t\t\t\t\tMonitor.Enter(_bufferLock);\n\t\t\t\t\t\tCustomPathManager._instance.queueItems[Calculating].queued = false;\n\t\t\t\t\t\tCustomPathManager._instance.ReleasePath(Calculating);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tMonitor.Exit(this._bufferLock);\n\t\t\t\t\t}\n\t\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\t\tCalculating = 0u;\n\t\t\t\t\tMonitor.Pulse(QueueLock);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.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()}\");\n\t\t\t\t} finally {\n\t\t\t\t\tMonitor.Exit(QueueLock);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if a given lane may be used by the vehicle whose path is currently being calculated.\n\t\t/// </summary>\n\t\t/// <param name=\"debug\"></param>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"laneIndex\"></param>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <param name=\"laneInfo\"></param>\n\t\t/// <returns></returns>\n\t\tprotected virtual bool CanUseLane(bool debug, ushort segmentId, NetInfo segmentInfo, uint laneIndex, NetInfo.Lane laneInfo) {\n\t\t\tif (!Options.vehicleRestrictionsEnabled)\n\t\t\t\treturn true;\n\n\t\t\tif (queueItem.vehicleType == ExtVehicleType.None || queueItem.vehicleType == ExtVehicleType.Tram)\n\t\t\t\treturn true;\n\n\t\t\t/*if (laneInfo == null)\n\t\t\t\tlaneInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info.m_lanes[laneIndex];*/\n\n\t\t\tif ((laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) == VehicleInfo.VehicleType.None)\n\t\t\t\treturn true;\n\n\t\t\tExtVehicleType allowedTypes = vehicleRestrictionsManager.GetAllowedVehicleTypes(segmentId, segmentInfo, laneIndex, laneInfo, VehicleRestrictionsMode.Configured);\n\n\t\t\treturn ((allowedTypes & queueItem.vehicleType) != ExtVehicleType.None);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the speed limit for the given lane.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"laneIndex\"></param>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <param name=\"lane\"></param>\n\t\t/// <returns></returns>\n\t\tprotected virtual float GetLaneSpeedLimit(ushort segmentId, byte laneIndex, uint laneId, NetInfo.Lane lane) {\n\t\t\treturn Options.customSpeedLimitsEnabled ? speedLimitManager.GetLockFreeGameSpeedLimit(segmentId, laneIndex, laneId, lane) : lane.m_speedLimit;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/PathFinding/CustomPathFind2.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing ColossalFramework.UI;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing TrafficManager.TrafficLight;\nusing UnityEngine;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Custom.PathFinding {\n\tpublic class CustomPathFind2 : PathFind {\n\t\tprivate const float BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR = 0.003921569f;\n\t\tprivate const float TICKET_COST_CONVERSION_FACTOR = BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;\n\n#if ROUTING\n\t\tprivate readonly RoutingManager m_routingManager = RoutingManager.Instance;\n#endif\n#if JUNCTIONRESTRICTIONS\n\t\tprivate readonly JunctionRestrictionsManager m_junctionManager = JunctionRestrictionsManager.Instance;\n#endif\n#if VEHICLERESTRICTIONS\n\t\tprivate readonly VehicleRestrictionsManager m_vehicleRestrictionsManager = VehicleRestrictionsManager.Instance;\n#endif\n#if SPEEDLIMITS\n\t\tprivate readonly SpeedLimitManager m_speedLimitManager = SpeedLimitManager.Instance;\n#endif\n#if CUSTOMTRAFFICLIGHTS\n\t\tprivate readonly CustomSegmentLightsManager m_customTrafficLightsManager = CustomSegmentLightsManager.Instance;\n#endif\n#if ADVANCEDAI && ROUTING\n\t\tprivate readonly TrafficMeasurementManager m_trafficMeasurementManager = TrafficMeasurementManager.Instance;\n#endif\n\t\tprivate GlobalConfig m_conf = null;\n\n\t\tprivate struct BufferItem {\n\t\t\tpublic PathUnit.Position m_position;\n\t\t\tpublic float m_comparisonValue;\n\t\t\tpublic float m_methodDistance;\n\t\t\tpublic float m_duration;\n\t\t\tpublic uint m_laneID;\n\t\t\tpublic NetInfo.Direction m_direction;\n\t\t\tpublic NetInfo.LaneType m_lanesUsed;\n#if PARKINGAI\n\t\t\tpublic VehicleInfo.VehicleType m_vehiclesUsed;\n#endif\n#if ADVANCEDAI && ROUTING\n\t\t\tpublic float m_trafficRand;\n#endif\n\t\t\tpublic override string ToString() {\n\t\t\t\treturn $\"[BufferItem\\n\" +\n\t\t\t\t\"\\t\" + $\"m_position=(s#({m_position.m_segment}), l#({m_position.m_lane}), o#({m_position.m_offset}))\\n\" +\n\t\t\t\t\"\\t\" + $\"m_laneID={m_laneID}\\n\" +\n\t\t\t\t\"\\t\" + $\"m_comparisonValue={m_comparisonValue}\\n\" +\n\t\t\t\t\"\\t\" + $\"m_methodDistance={m_methodDistance}\\n\" +\n\t\t\t\t\"\\t\" + $\"m_duration={m_duration}\\n\" +\n\t\t\t\t\"\\t\" + $\"m_direction={m_direction}\\n\" +\n\t\t\t\t\"\\t\" + $\"m_lanesUsed={m_lanesUsed}\\n\" +\n#if PARKINGAI\n\t\t\t\t\"\\t\" + $\"m_vehiclesUsed={m_vehiclesUsed}\\n\" +\n#endif\n#if ADVANCEDAI && ROUTING\n\t\t\t\t\"\\t\" + $\"m_trafficRand={m_trafficRand}\\n\" +\n#endif\n\t\t\t\t\"BufferItem]\";\n\t\t\t}\n\t\t}\n\n\t\tprivate enum LaneChangingCostCalculationMode {\n\t\t\tNone,\n\t\t\tByLaneDistance,\n\t\t\tByGivenDistance\n\t\t}\n\n\t\t// private stock fields\n\t\tFieldInfo pathUnitsField;\n\t\tFieldInfo queueFirstField;\n\t\tFieldInfo queueLastField;\n\t\tFieldInfo queueLockField;\n\t\tFieldInfo calculatingField;\n\t\tFieldInfo terminatedField;\n\t\tFieldInfo pathFindThreadField;\n\n\t\tprivate Array32<PathUnit> m_pathUnits {\n\t\t\tget { return pathUnitsField.GetValue(this) as Array32<PathUnit>; }\n\t\t\tset { pathUnitsField.SetValue(this, value); }\n\t\t}\n\n\t\tprivate uint m_queueFirst {\n\t\t\tget { return (uint)queueFirstField.GetValue(this); }\n\t\t\tset { queueFirstField.SetValue(this, value); }\n\t\t}\n\n\t\tprivate uint m_queueLast {\n\t\t\tget { return (uint)queueLastField.GetValue(this); }\n\t\t\tset { queueLastField.SetValue(this, value); }\n\t\t}\n\n\t\tprivate uint m_calculating {\n\t\t\tget { return (uint)calculatingField.GetValue(this); }\n\t\t\tset { calculatingField.SetValue(this, value); }\n\t\t}\n\n\t\tprivate object m_queueLock {\n\t\t\tget { return queueLockField.GetValue(this); }\n\t\t\tset { queueLockField.SetValue(this, value); }\n\t\t}\n\n\t\tprivate Thread m_customPathFindThread {\n\t\t\tget { return (Thread)pathFindThreadField.GetValue(this); }\n\t\t\tset { pathFindThreadField.SetValue(this, value); }\n\t\t}\n\n\t\tprivate bool m_terminated {\n\t\t\tget { return (bool)terminatedField.GetValue(this); }\n\t\t\tset { terminatedField.SetValue(this, value); }\n\t\t}\n\n\t\t// stock fields\n\t\tpublic ThreadProfiler m_pathfindProfiler;\n\t\tprivate object m_bufferLock;\n\t\tprivate int m_bufferMinPos;\n\t\tprivate int m_bufferMaxPos;\n\t\tprivate uint[] m_laneLocation;\n\t\tprivate PathUnit.Position[] m_laneTarget;\n\t\tprivate BufferItem[] m_buffer;\n\t\tprivate int[] m_bufferMin;\n\t\tprivate int[] m_bufferMax;\n\t\tprivate float m_maxLength;\n\t\tprivate uint m_startLaneA;\n\t\tprivate uint m_startLaneB;\n\t\tprivate uint m_endLaneA;\n\t\tprivate uint m_endLaneB;\n\t\tprivate uint m_vehicleLane;\n\t\tprivate byte m_startOffsetA;\n\t\tprivate byte m_startOffsetB;\n\t\tprivate byte m_vehicleOffset;\n\t\tprivate NetSegment.Flags m_carBanMask;\n\t\tprivate bool m_ignoreBlocked;\n\t\tprivate bool m_stablePath;\n\t\tprivate bool m_randomParking;\n\t\tprivate bool m_transportVehicle;\n\t\tprivate bool m_ignoreCost;\n\t\tprivate NetSegment.Flags m_disableMask;\n\t\tprivate Randomizer m_pathRandomizer;\n\t\tprivate uint m_pathFindIndex;\n\t\tprivate NetInfo.LaneType m_laneTypes;\n\t\tprivate VehicleInfo.VehicleType m_vehicleTypes;\n\n\t\t// custom fields\n\t\tprivate PathUnitQueueItem m_queueItem;\n\t\tprivate bool m_isHeavyVehicle;\n#if DEBUG\n\t\tpublic uint m_failedPathFinds = 0;\n\t\tpublic uint m_succeededPathFinds = 0;\n\t\tprivate bool m_debug = false;\n\t\tprivate IDictionary<ushort, IList<ushort>> m_debugPositions = null;\n#endif\n#if PARKINGAI || JUNCTIONRESTRICTIONS\n\t\tprivate ushort m_startSegmentA;\n\t\tprivate ushort m_startSegmentB;\n#endif\n#if ROUTING\n\t\tprivate bool m_isRoadVehicle;\n\t\tprivate bool m_isLaneArrowObeyingEntity;\n\t\t//private bool m_isLaneConnectionObeyingEntity;\n#endif\n\n\t\tprivate void Awake() {\n\t\t\tType stockPathFindType = typeof(PathFind);\n\t\t\tconst BindingFlags fieldFlags = BindingFlags.NonPublic | BindingFlags.Instance;\n\n\t\t\tpathUnitsField = stockPathFindType.GetField(\"m_pathUnits\", fieldFlags);\n\t\t\tqueueFirstField = stockPathFindType.GetField(\"m_queueFirst\", fieldFlags);\n\t\t\tqueueLastField = stockPathFindType.GetField(\"m_queueLast\", fieldFlags);\n\t\t\tqueueLockField = stockPathFindType.GetField(\"m_queueLock\", fieldFlags);\n\t\t\tterminatedField = stockPathFindType.GetField(\"m_terminated\", fieldFlags);\n\t\t\tcalculatingField = stockPathFindType.GetField(\"m_calculating\", fieldFlags);\n\t\t\tpathFindThreadField = stockPathFindType.GetField(\"m_pathFindThread\", fieldFlags);\n\n\t\t\tm_pathfindProfiler = new ThreadProfiler();\n\t\t\tm_laneLocation = new uint[262144];\n\t\t\tm_laneTarget = new PathUnit.Position[262144];\n\t\t\tm_buffer = new BufferItem[65536];\n\t\t\tm_bufferMin = new int[1024];\n\t\t\tm_bufferMax = new int[1024];\n\t\t\tm_queueLock = new object();\n\t\t\tm_bufferLock = Singleton<PathManager>.instance.m_bufferLock;\n\t\t\tm_pathUnits = Singleton<PathManager>.instance.m_pathUnits;\n\t\t\tm_customPathFindThread = new Thread(PathFindThread);\n\t\t\tm_customPathFindThread.Name = \"Pathfind\";\n\t\t\tm_customPathFindThread.Priority = SimulationManager.SIMULATION_PRIORITY;\n\t\t\tm_customPathFindThread.Start();\n\n\t\t\tif (!m_customPathFindThread.IsAlive) {\n\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Path find thread failed to start!\");\n\t\t\t}\n\t\t}\n\n\t\tprivate void OnDestroy() {\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(m_queueLock);\n\t\t\t\tm_terminated = true;\n\t\t\t\tMonitor.PulseAll(m_queueLock);\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(m_queueLock);\n\t\t\t}\n\t\t}\n\n\t\tpublic new bool CalculatePath(uint unit, bool skipQueue) {\n\t\t\treturn ExtCalculatePath(unit, skipQueue);\n\t\t}\n\n\t\tpublic bool ExtCalculatePath(uint unit, bool skipQueue) {\n\t\t\tif (CustomPathManager._instance.AddPathReference(unit)) {\n\t\t\t\ttry {\n\t\t\t\t\tMonitor.Enter(m_queueLock);\n\n\t\t\t\t\tif (skipQueue) {\n\t\t\t\t\t\tif (m_queueLast == 0) {\n\t\t\t\t\t\t\tm_queueLast = unit;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\t\t\tCustomPathManager._instance.queueItems[unit].nextPathUnitId = m_queueFirst;\n\t\t\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t\t\t// PathUnits.m_buffer[unit].m_nextPathUnit = QueueFirst; // stock code commented\n\t\t\t\t\t\t}\n\t\t\t\t\t\tm_queueFirst = unit;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (m_queueLast == 0) {\n\t\t\t\t\t\t\tm_queueFirst = unit;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\t\t\tCustomPathManager._instance.queueItems[m_queueLast].nextPathUnitId = unit;\n\t\t\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t\t\t// PathUnits.m_buffer[QueueLast].m_nextPathUnit = unit; // stock code commented\n\t\t\t\t\t\t}\n\t\t\t\t\t\tm_queueLast = unit;\n\t\t\t\t\t}\n\n\t\t\t\t\tm_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_CREATED;\n\t\t\t\t\tm_queuedPathFindCount++;\n\n\t\t\t\t\tMonitor.Pulse(m_queueLock);\n\t\t\t\t} finally {\n\t\t\t\t\tMonitor.Exit(m_queueLock);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate void PathFindImplementation(uint unit, ref PathUnit data) {\n\t\t\tm_conf = GlobalConfig.Instance; // NON-STOCK CODE\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tm_laneTypes = (NetInfo.LaneType)m_pathUnits.m_buffer[unit].m_laneTypes;\n\t\t\tm_vehicleTypes = (VehicleInfo.VehicleType)m_pathUnits.m_buffer[unit].m_vehicleTypes;\n\t\t\tm_maxLength = m_pathUnits.m_buffer[unit].m_length;\n\t\t\tm_pathFindIndex = (m_pathFindIndex + 1 & 0x7FFF);\n\t\t\tm_pathRandomizer = new Randomizer(unit);\n\t\t\tm_carBanMask = NetSegment.Flags.CarBan;\n\n\t\t\tm_isHeavyVehicle = (m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IS_HEAVY) != 0; // NON-STOCK CODE (refactored)\n\t\t\tif (m_isHeavyVehicle) {\n\t\t\t\tm_carBanMask |= NetSegment.Flags.HeavyBan;\n\t\t\t}\n\n\t\t\tif ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_READY) != 0) {\n\t\t\t\tm_carBanMask |= NetSegment.Flags.WaitingPath;\n\t\t\t}\n\n\t\t\tm_ignoreBlocked = ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IGNORE_BLOCKED) != 0);\n\t\t\tm_stablePath = ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_STABLE_PATH) != 0);\n\t\t\tm_randomParking = ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_RANDOM_PARKING) != 0);\n\t\t\tm_transportVehicle = ((m_laneTypes & NetInfo.LaneType.TransportVehicle) != NetInfo.LaneType.None);\n\t\t\tm_ignoreCost = (m_stablePath || (m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IGNORE_COST) != 0);\n\t\t\tm_disableMask = (NetSegment.Flags.Collapsed | NetSegment.Flags.PathFailed);\n\n\t\t\tif ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IGNORE_FLOODED) == 0) {\n\t\t\t\tm_disableMask |= NetSegment.Flags.Flooded;\n\t\t\t}\n\n\t\t\tif ((m_laneTypes & NetInfo.LaneType.Vehicle) != NetInfo.LaneType.None) {\n\t\t\t\tm_laneTypes |= NetInfo.LaneType.TransportVehicle;\n\t\t\t}\n\n#if ROUTING\n\t\t\tm_isRoadVehicle =\n\t\t\t\t(m_queueItem.vehicleType & ExtVehicleType.RoadVehicle) != ExtVehicleType.None;\n\n\t\t\tm_isLaneArrowObeyingEntity =\n#if DEBUG\n\t\t\t\t! Options.allRelaxed && // debug option: all vehicle may ignore lane arrows\n#endif\n\t\t\t\t(! Options.relaxedBusses || m_queueItem.vehicleType != ExtVehicleType.Bus) && // option: busses may ignore lane arrows\n\t\t\t\t(m_vehicleTypes & LaneArrowManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None &&\n\t\t\t\t(m_queueItem.vehicleType & LaneArrowManager.EXT_VEHICLE_TYPES) != ExtVehicleType.None\n\t\t\t;\n#endif\n\n#if DEBUG\n\t\t\tm_debug = m_conf.Debug.Switches[0] &&\n\t\t\t\t(m_conf.Debug.ExtVehicleType == ExtVehicleType.None || m_queueItem.vehicleType == m_conf.Debug.ExtVehicleType) &&\n\t\t\t\t(m_conf.Debug.StartSegmentId == 0 || data.m_position00.m_segment == m_conf.Debug.StartSegmentId || data.m_position02.m_segment == m_conf.Debug.StartSegmentId) &&\n\t\t\t\t(m_conf.Debug.EndSegmentId == 0 || data.m_position01.m_segment == m_conf.Debug.EndSegmentId || data.m_position03.m_segment == m_conf.Debug.EndSegmentId) &&\n\t\t\t\t(m_conf.Debug.VehicleId == 0 || m_queueItem.vehicleId == m_conf.Debug.VehicleId)\n\t\t\t;\n\t\t\tif (m_debug) {\n\t\t\t\tm_debugPositions = new Dictionary<ushort, IList<ushort>>();\n\t\t\t}\n#endif\n\n\t\t\tint posCount = m_pathUnits.m_buffer[unit].m_positionCount & 0xF;\n\t\t\tint vehiclePosIndicator = m_pathUnits.m_buffer[unit].m_positionCount >> 4;\n\t\t\tBufferItem bufferItemStartA = default(BufferItem);\n\t\t\tif (data.m_position00.m_segment != 0 && posCount >= 1) {\n#if PARKINGAI || JUNCTIONRESTRICTIONS\n\t\t\t\tm_startSegmentA = data.m_position00.m_segment; // NON-STOCK CODE\n#endif\n\t\t\t\tm_startLaneA = PathManager.GetLaneID(data.m_position00);\n\t\t\t\tm_startOffsetA = data.m_position00.m_offset;\n\t\t\t\tbufferItemStartA.m_laneID = m_startLaneA;\n\t\t\t\tbufferItemStartA.m_position = data.m_position00;\n\t\t\t\tGetLaneDirection(data.m_position00, out bufferItemStartA.m_direction, out bufferItemStartA.m_lanesUsed\n#if PARKINGAI\n\t\t\t\t\t, out bufferItemStartA.m_vehiclesUsed\n#endif\n\t\t\t\t);\n\t\t\t\tbufferItemStartA.m_comparisonValue = 0f;\n\t\t\t\tbufferItemStartA.m_duration = 0f;\n\t\t\t} else {\n#if PARKINGAI || JUNCTIONRESTRICTIONS\n\t\t\t\tm_startSegmentA = 0; // NON-STOCK CODE\n#endif\n\t\t\t\tm_startLaneA = 0u;\n\t\t\t\tm_startOffsetA = 0;\n\t\t\t}\n\n\t\t\tBufferItem bufferItemStartB = default(BufferItem);\n\t\t\tif (data.m_position02.m_segment != 0 && posCount >= 3) {\n#if PARKINGAI || JUNCTIONRESTRICTIONS\n\t\t\t\tm_startSegmentB = data.m_position02.m_segment; // NON-STOCK CODE\n#endif\n\t\t\t\tm_startLaneB = PathManager.GetLaneID(data.m_position02);\n\t\t\t\tm_startOffsetB = data.m_position02.m_offset;\n\t\t\t\tbufferItemStartB.m_laneID = m_startLaneB;\n\t\t\t\tbufferItemStartB.m_position = data.m_position02;\n\t\t\t\tGetLaneDirection(data.m_position02, out bufferItemStartB.m_direction, out bufferItemStartB.m_lanesUsed\n#if PARKINGAI\n\t\t\t\t\t, out bufferItemStartB.m_vehiclesUsed\n#endif\n\t\t\t\t);\n\t\t\t\tbufferItemStartB.m_comparisonValue = 0f;\n\t\t\t\tbufferItemStartB.m_duration = 0f;\n\t\t\t} else {\n#if PARKINGAI || JUNCTIONRESTRICTIONS\n\t\t\t\tm_startSegmentB = 0; // NON-STOCK CODE\n#endif\n\t\t\t\tm_startLaneB = 0u;\n\t\t\t\tm_startOffsetB = 0;\n\t\t\t}\n\n\t\t\tBufferItem bufferItemEndA = default(BufferItem);\n\t\t\tif (data.m_position01.m_segment != 0 && posCount >= 2) {\n\t\t\t\tm_endLaneA = PathManager.GetLaneID(data.m_position01);\n\t\t\t\tbufferItemEndA.m_laneID = m_endLaneA;\n\t\t\t\tbufferItemEndA.m_position = data.m_position01;\n\t\t\t\tGetLaneDirection(data.m_position01, out bufferItemEndA.m_direction, out bufferItemEndA.m_lanesUsed\n#if PARKINGAI\n\t\t\t\t\t, out bufferItemEndA.m_vehiclesUsed\n#endif\n\t\t\t\t);\n\t\t\t\tbufferItemEndA.m_methodDistance = 0.01f;\n\t\t\t\tbufferItemEndA.m_comparisonValue = 0f;\n\t\t\t\tbufferItemEndA.m_duration = 0f;\n\t\t\t} else {\n\t\t\t\tm_endLaneA = 0u;\n\t\t\t}\n\n\t\t\tBufferItem bufferItemEndB = default(BufferItem);\n\t\t\tif (data.m_position03.m_segment != 0 && posCount >= 4) {\n\t\t\t\tm_endLaneB = PathManager.GetLaneID(data.m_position03);\n\t\t\t\tbufferItemEndB.m_laneID = m_endLaneB;\n\t\t\t\tbufferItemEndB.m_position = data.m_position03;\n\t\t\t\tGetLaneDirection(data.m_position03, out bufferItemEndB.m_direction, out bufferItemEndB.m_lanesUsed\n#if PARKINGAI\n\t\t\t\t\t, out bufferItemEndB.m_vehiclesUsed\n#endif\n\t\t\t\t);\n\t\t\t\tbufferItemEndB.m_methodDistance = 0.01f;\n\t\t\t\tbufferItemEndB.m_comparisonValue = 0f;\n\t\t\t\tbufferItemEndB.m_duration = 0f;\n\t\t\t} else {\n\t\t\t\tm_endLaneB = 0u;\n\t\t\t}\n\n\t\t\tif (data.m_position11.m_segment != 0 && vehiclePosIndicator >= 1) {\n\t\t\t\tm_vehicleLane = PathManager.GetLaneID(data.m_position11);\n\t\t\t\tm_vehicleOffset = data.m_position11.m_offset;\n\t\t\t} else {\n\t\t\t\tm_vehicleLane = 0u;\n\t\t\t\tm_vehicleOffset = 0;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (m_debug) {\n\t\t\t\tDebug(unit, $\"PathFindImplementation: Preparing calculation:\\n\" +\n\t\t\t\t\t$\"\\tbufferItemStartA: segment={bufferItemStartA.m_position.m_segment} lane={bufferItemStartA.m_position.m_lane} off={bufferItemStartA.m_position.m_offset} laneId={bufferItemStartA.m_laneID}\\n\" +\n\t\t\t\t\t$\"\\tbufferItemStartB: segment={bufferItemStartB.m_position.m_segment} lane={bufferItemStartB.m_position.m_lane} off={bufferItemStartB.m_position.m_offset} laneId={bufferItemStartB.m_laneID}\\n\" +\n\t\t\t\t\t$\"\\tbufferItemEndA: segment={bufferItemEndA.m_position.m_segment} lane={bufferItemEndA.m_position.m_lane} off={bufferItemEndA.m_position.m_offset} laneId={bufferItemEndA.m_laneID}\\n\" +\n\t\t\t\t\t$\"\\tbufferItemEndB: segment={bufferItemEndB.m_position.m_segment} lane={bufferItemEndB.m_position.m_lane} off={bufferItemEndB.m_position.m_offset} laneId={bufferItemEndB.m_laneID}\\n\" +\n\t\t\t\t\t$\"\\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\" +\n\t\t\t\t\t$\"Properties:\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_maxLength={m_maxLength}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_startLaneA={m_startLaneA}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_startLaneB={m_startLaneB}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_endLaneA={m_endLaneA}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_endLaneB={m_endLaneB}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_startOffsetA={m_startOffsetA}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_startOffsetB={m_startOffsetB}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_vehicleLane={m_vehicleLane}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_vehicleOffset={m_vehicleOffset}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_carBanMask={m_carBanMask}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_disableMask={m_disableMask}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_ignoreBlocked={m_ignoreBlocked}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_stablePath={m_stablePath}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_randomParking={m_randomParking}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_transportVehicle={m_transportVehicle}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_ignoreCost={m_ignoreCost}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_pathFindIndex={m_pathFindIndex}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_laneTypes={m_laneTypes}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_vehicleTypes={m_vehicleTypes}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_queueItem={m_queueItem}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_isHeavyVehicle={m_isHeavyVehicle}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_failedPathFinds={m_failedPathFinds}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_succeededPathFinds={m_succeededPathFinds}\\n\" +\n#if PARKINGAI || JUNCTIONRESTRICTIONS\n\t\t\t\t\t\"\\t\" + $\"m_startSegmentA={m_startSegmentA}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_startSegmentB={m_startSegmentB}\\n\" +\n#endif\n#if ROUTING\n\t\t\t\t\t\"\\t\" + $\"m_isRoadVehicle={m_isRoadVehicle}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"m_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\"\n#endif\n\t\t\t\t);\n\t\t\t}\n#endif\n\n\t\t\tBufferItem finalBufferItem = default(BufferItem);\n\t\t\tbyte startOffset = 0;\n\t\t\tm_bufferMinPos = 0;\n\t\t\tm_bufferMaxPos = -1;\n\n\t\t\tif (m_pathFindIndex == 0) {\n\t\t\t\tuint num3 = 4294901760u;\n\t\t\t\tfor (int i = 0; i < 262144; i++) {\n\t\t\t\t\tm_laneLocation[i] = num3;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (int j = 0; j < 1024; j++) {\n\t\t\t\tm_bufferMin[j] = 0;\n\t\t\t\tm_bufferMax[j] = -1;\n\t\t\t}\n\n\t\t\tif (bufferItemEndA.m_position.m_segment != 0) {\n\t\t\t\tm_bufferMax[0]++;\n\t\t\t\tm_buffer[++m_bufferMaxPos] = bufferItemEndA;\n\t\t\t}\n\n\t\t\tif (bufferItemEndB.m_position.m_segment != 0) {\n\t\t\t\tm_bufferMax[0]++;\n\t\t\t\tm_buffer[++m_bufferMaxPos] = bufferItemEndB;\n\t\t\t}\n\n\t\t\tbool canFindPath = false;\n\t\t\twhile (m_bufferMinPos <= m_bufferMaxPos) {\n\t\t\t\tint bufMin = m_bufferMin[m_bufferMinPos];\n\t\t\t\tint bufMax = m_bufferMax[m_bufferMinPos];\n\n\t\t\t\tif (bufMin > bufMax) {\n\t\t\t\t\tm_bufferMinPos++;\n\t\t\t\t} else {\n\t\t\t\t\tm_bufferMin[m_bufferMinPos] = bufMin + 1;\n\t\t\t\t\tBufferItem candidateItem = m_buffer[(m_bufferMinPos << 6) + bufMin];\n\t\t\t\t\tif (candidateItem.m_position.m_segment == bufferItemStartA.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartA.m_position.m_lane) {\n\t\t\t\t\t\tif ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None && candidateItem.m_position.m_offset >= m_startOffsetA) {\n\t\t\t\t\t\t\tfinalBufferItem = candidateItem;\n\t\t\t\t\t\t\tstartOffset = m_startOffsetA;\n\t\t\t\t\t\t\tcanFindPath = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None && candidateItem.m_position.m_offset <= m_startOffsetA) {\n\t\t\t\t\t\t\tfinalBufferItem = candidateItem;\n\t\t\t\t\t\t\tstartOffset = m_startOffsetA;\n\t\t\t\t\t\t\tcanFindPath = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (candidateItem.m_position.m_segment == bufferItemStartB.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartB.m_position.m_lane) {\n\t\t\t\t\t\tif ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None && candidateItem.m_position.m_offset >= m_startOffsetB) {\n\t\t\t\t\t\t\tfinalBufferItem = candidateItem;\n\t\t\t\t\t\t\tstartOffset = m_startOffsetB;\n\t\t\t\t\t\t\tcanFindPath = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None && candidateItem.m_position.m_offset <= m_startOffsetB) {\n\t\t\t\t\t\t\tfinalBufferItem = candidateItem;\n\t\t\t\t\t\t\tstartOffset = m_startOffsetB;\n\t\t\t\t\t\t\tcanFindPath = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tushort startNodeId = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode;\n\t\t\t\t\tushort endNodeId = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode;\n\n\t\t\t\t\tif ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None) {\n\t\t\t\t\t\tProcessItemMain(\n#if DEBUG\n\t\t\t\t\t\t\tunit,\n#endif\n\t\t\t\t\t\t\tcandidateItem, 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);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None) {\n\t\t\t\t\t\tProcessItemMain(\n#if DEBUG\n\t\t\t\t\t\t\tunit,\n#endif\n\t\t\t\t\t\t\tcandidateItem, 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);\n\t\t\t\t\t}\n\n\t\t\t\t\tint numIter = 0;\n\t\t\t\t\tushort specialNodeId = netManager.m_lanes.m_buffer[candidateItem.m_laneID].m_nodes;\n\t\t\t\t\tif (specialNodeId != 0) {\n\t\t\t\t\t\tbool nodesDisabled = ((netManager.m_nodes.m_buffer[startNodeId].m_flags | netManager.m_nodes.m_buffer[endNodeId].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None;\n\n\t\t\t\t\t\twhile (specialNodeId != 0) {\n\t\t\t\t\t\t\tNetInfo.Direction direction = NetInfo.Direction.None;\n\t\t\t\t\t\t\tbyte laneOffset = netManager.m_nodes.m_buffer[specialNodeId].m_laneOffset;\n\n\t\t\t\t\t\t\tif (laneOffset <= candidateItem.m_position.m_offset) {\n\t\t\t\t\t\t\t\tdirection |= NetInfo.Direction.Forward;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (laneOffset >= candidateItem.m_position.m_offset) {\n\t\t\t\t\t\t\t\tdirection |= NetInfo.Direction.Backward;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ((candidateItem.m_direction & direction) != NetInfo.Direction.None && (!nodesDisabled || (netManager.m_nodes.m_buffer[specialNodeId].m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None)) {\n#if DEBUG\n\t\t\t\t\t\t\t\tif (m_debug && (m_conf.Debug.NodeId <= 0 || specialNodeId == m_conf.Debug.NodeId)) {\n\t\t\t\t\t\t\t\t\tDebug(unit, $\"PathFindImplementation: Handling special node for path unit {unit}, type {m_queueItem.vehicleType}:\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"\\tcandidateItem.m_position.m_segment={candidateItem.m_position.m_segment}\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"\\tcandidateItem.m_position.m_lane={candidateItem.m_position.m_lane}\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"\\tcandidateItem.m_laneID={candidateItem.m_laneID}\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"\\tspecialNodeId={specialNodeId}\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"\\tstartNodeId={startNodeId}\\n\" +\n\t\t\t\t\t\t\t\t\t\t$\"\\tendNodeId={endNodeId}\\n\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\tProcessItemMain(\n#if DEBUG\n\t\t\t\t\t\t\t\t\tunit,\n#endif\n\t\t\t\t\t\t\t\t\tcandidateItem, 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);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tspecialNodeId = netManager.m_nodes.m_buffer[specialNodeId].m_nextLaneNode;\n\n\t\t\t\t\t\t\tif (++numIter == 32768) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!canFindPath) {\n\t\t\t\tm_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;\n\t\t\t\t// NON-STOCK CODE START\n#if DEBUG\n\t\t\t\t++m_failedPathFinds;\n\n\t\t\t\tif (m_debug) {\n\t\t\t\t\tDebug(unit, $\"PathFindImplementation: Path-find failed: Could not find path\");\n\t\t\t\t\tstring reachableBuf = \"\";\n\t\t\t\t\tstring unreachableBuf = \"\";\n\t\t\t\t\tforeach (KeyValuePair<ushort, IList<ushort>> e in m_debugPositions) {\n\t\t\t\t\t\tstring buf = $\"{e.Key} -> {e.Value.CollectionToString()}\\n\";\n\t\t\t\t\t\tif (e.Value.Count <= 0) {\n\t\t\t\t\t\t\tunreachableBuf += buf;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treachableBuf += buf;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tDebug(unit, $\"PathFindImplementation: Reachability graph:\\n== REACHABLE ==\\n\" + reachableBuf + \"\\n== UNREACHABLE ==\\n\" + unreachableBuf);\n\t\t\t\t}\n#endif\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t} else {\n\t\t\t\tfloat duration = (m_laneTypes != NetInfo.LaneType.Pedestrian && (m_laneTypes & NetInfo.LaneType.Pedestrian) != NetInfo.LaneType.None) ? finalBufferItem.m_duration : finalBufferItem.m_methodDistance;\n\t\t\t\tm_pathUnits.m_buffer[unit].m_length = duration;\n\t\t\t\tm_pathUnits.m_buffer[unit].m_speed = (byte)Mathf.Clamp(finalBufferItem.m_methodDistance * 100f / Mathf.Max(0.01f, finalBufferItem.m_duration), 0f, 255f);\n#if PARKINGAI\n\t\t\t\tm_pathUnits.m_buffer[unit].m_laneTypes = (byte)finalBufferItem.m_lanesUsed;\n\t\t\t\tm_pathUnits.m_buffer[unit].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed;\n#endif\n\n\t\t\t\tuint currentPathUnitId = unit;\n\t\t\t\tint currentItemPositionCount = 0;\n\t\t\t\tint sumOfPositionCounts = 0;\n\t\t\t\tPathUnit.Position currentPosition = finalBufferItem.m_position;\n\n\t\t\t\tif ((currentPosition.m_segment != bufferItemEndA.m_position.m_segment || currentPosition.m_lane != bufferItemEndA.m_position.m_lane || currentPosition.m_offset != bufferItemEndA.m_position.m_offset) &&\n\t\t\t\t\t(currentPosition.m_segment != bufferItemEndB.m_position.m_segment || currentPosition.m_lane != bufferItemEndB.m_position.m_lane || currentPosition.m_offset != bufferItemEndB.m_position.m_offset)) {\n\t\t\t\t\tif (startOffset != currentPosition.m_offset) {\n\t\t\t\t\t\tPathUnit.Position position2 = currentPosition;\n\t\t\t\t\t\tposition2.m_offset = startOffset;\n\t\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, position2);\n\t\t\t\t\t}\n\n\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition);\n\t\t\t\t\tcurrentPosition = m_laneTarget[finalBufferItem.m_laneID];\n\t\t\t\t}\n\n\t\t\t\tfor (int k = 0; k < 262144; k++) {\n\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition);\n\n\t\t\t\t\tif ((currentPosition.m_segment == bufferItemEndA.m_position.m_segment && currentPosition.m_lane == bufferItemEndA.m_position.m_lane && currentPosition.m_offset == bufferItemEndA.m_position.m_offset) ||\n\t\t\t\t\t(currentPosition.m_segment == bufferItemEndB.m_position.m_segment && currentPosition.m_lane == bufferItemEndB.m_position.m_lane && currentPosition.m_offset == bufferItemEndB.m_position.m_offset)) {\n\t\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;\n\t\t\t\t\t\tsumOfPositionCounts += currentItemPositionCount;\n\t\t\t\t\t\tif (sumOfPositionCounts != 0) {\n\t\t\t\t\t\t\tcurrentPathUnitId = m_pathUnits.m_buffer[unit].m_nextPathUnit;\n\t\t\t\t\t\t\tcurrentItemPositionCount = m_pathUnits.m_buffer[unit].m_positionCount;\n\t\t\t\t\t\t\tint numIter = 0;\n\t\t\t\t\t\t\twhile (currentPathUnitId != 0) {\n\t\t\t\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].m_length = duration * (float)(sumOfPositionCounts - currentItemPositionCount) / (float)sumOfPositionCounts;\n\t\t\t\t\t\t\t\tcurrentItemPositionCount += m_pathUnits.m_buffer[currentPathUnitId].m_positionCount;\n\t\t\t\t\t\t\t\tcurrentPathUnitId = m_pathUnits.m_buffer[currentPathUnitId].m_nextPathUnit;\n\t\t\t\t\t\t\t\tif (++numIter >= 262144) {\n\t\t\t\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tm_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_READY;\n\t\t\t\t\t\t// NON-STOCK CODE START\n#if DEBUG\n\t\t\t\t\t\t++m_succeededPathFinds;\n\n\t\t\t\t\t\tif (m_debug) {\n\t\t\t\t\t\t\tDebug(unit, $\"PathFindImplementation: Path-find succeeded\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (currentItemPositionCount == 12) {\n\t\t\t\t\t\tuint createdPathUnitId;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tMonitor.Enter(m_bufferLock);\n\n\t\t\t\t\t\t\tif (!m_pathUnits.CreateItem(out createdPathUnitId, ref m_pathRandomizer)) {\n\t\t\t\t\t\t\t\tm_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;\n\t\t\t\t\t\t\t\t// NON-STOCK CODE START\n#if DEBUG\n\t\t\t\t\t\t\t\t++m_failedPathFinds;\n\n\t\t\t\t\t\t\t\tif (m_debug) {\n\t\t\t\t\t\t\t\t\tDebug(unit, $\"Path-finding failed: Could not create path unit\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[createdPathUnitId] = m_pathUnits.m_buffer[currentPathUnitId];\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[createdPathUnitId].m_referenceCount = 1;\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[createdPathUnitId].m_pathFindFlags = PathUnit.FLAG_READY;\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].m_nextPathUnit = createdPathUnitId;\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;\n#if PARKINGAI\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].m_laneTypes = (byte)finalBufferItem.m_lanesUsed; // (this is not accurate!)\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].m_vehicleTypes = (ushort)finalBufferItem.m_vehiclesUsed; // (this is not accurate!)\n#endif\n\t\t\t\t\t\t\tsumOfPositionCounts += currentItemPositionCount;\n\t\t\t\t\t\t\tSingleton<PathManager>.instance.m_pathUnitCount = (int)(m_pathUnits.ItemCount() - 1);\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tMonitor.Exit(m_bufferLock);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcurrentPathUnitId = createdPathUnitId;\n\t\t\t\t\t\tcurrentItemPositionCount = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tuint laneID = PathManager.GetLaneID(currentPosition);\n\t\t\t\t\tcurrentPosition = m_laneTarget[laneID];\n\t\t\t\t}\n\n\t\t\t\tm_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;\n\t\t\t\t// NON-STOCK CODE START\n#if DEBUG\n\t\t\t\t++m_failedPathFinds;\n\n\t\t\t\tif (m_debug) {\n\t\t\t\t\tDebug(unit, $\"Path-finding failed: Internal loop break error\");\n\t\t\t\t}\n#endif\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t}\n\t\t}\n\n#if DEBUG\n\t\tprivate void Debug(uint unit, string message) {\n\t\t\tLog._Debug(\n\t\t\t\t$\"PF T#({Thread.CurrentThread.ManagedThreadId}) IDX#({m_pathFindIndex}):\\n\"\n\t\t\t\t+ $\"UNIT({unit})\\n\"\n\t\t\t\t+ message\n\t\t\t);\n\t\t}\n\n\t\tprivate void Debug(uint unit, BufferItem item, string message) {\n\t\t\tLog._Debug(\n\t\t\t\t$\"PF T#({Thread.CurrentThread.ManagedThreadId}) IDX#({m_pathFindIndex}):\\n\"\n\t\t\t\t+ $\"UNIT({unit}): s#({item.m_position.m_segment}), l#({item.m_position.m_lane})\\n\"\n\t\t\t\t+ $\"ITEM({item})\\n\"\n\t\t\t\t+ message\n\t\t\t);\n\t\t}\n\n\t\tprivate void Debug(uint unit, BufferItem item, ushort nextSegmentId, string message) {\n\t\t\tLog._Debug(\n\t\t\t\t$\"PF T#({Thread.CurrentThread.ManagedThreadId}) IDX#({m_pathFindIndex}):\\n\"\n\t\t\t\t+ $\"UNIT({unit}): s#({item.m_position.m_segment}), l#({item.m_position.m_lane}) -> s#({nextSegmentId})\\n\"\n\t\t\t\t+ $\"ITEM({item})\\n\"\n\t\t\t\t+ message\n\t\t\t);\n\t\t}\n\n\t\tprivate void Debug(uint unit, BufferItem item, ushort nextSegmentId, int nextLaneIndex, uint nextLaneId, string message) {\n\t\t\tLog._Debug(\n\t\t\t\t$\"PF T#({Thread.CurrentThread.ManagedThreadId}) IDX#({m_pathFindIndex}):\\n\"\n\t\t\t\t+ $\"UNIT({unit}): s#({item.m_position.m_segment}), l#({item.m_position.m_lane}) -> s#({nextSegmentId}), l#({nextLaneIndex}), lid#({nextLaneId})\\n\"\n\t\t\t\t+ $\"ITEM({item})\\n\"\n\t\t\t\t+ message\n\t\t\t);\n\t\t}\n#endif\n\n\t\t// 1\n\t\tprivate void ProcessItemMain(\n#if DEBUG\n\t\t\tuint unitId,\n#endif\n\t\t\tBufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, ushort nextNodeId, ref NetNode nextNode, byte connectOffset, bool isMiddle) {\n#if DEBUG\n\t\t\tbool debug = this.m_debug && (m_conf.Debug.NodeId <= 0 || nextNodeId == m_conf.Debug.NodeId);\n\t\t\tif (debug) {\n\t\t\t\tif (!m_debugPositions.ContainsKey(item.m_position.m_segment)) {\n\t\t\t\t\tm_debugPositions[item.m_position.m_segment] = new List<ushort>();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (debug) {\n\t\t\t\tDebug(unitId, item, $\"ProcessItemMain called.\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextNodeId={nextNodeId}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"connectOffset={connectOffset}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"isMiddle={isMiddle}\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tushort prevSegmentId = item.m_position.m_segment;\n\t\t\tbyte prevLaneIndex = item.m_position.m_lane;\n\n\t\t\tbool prevIsPedestrianLane = false;\n\t\t\tbool prevIsBicycleLane = false;\n\t\t\tbool prevIsCenterPlatform = false;\n\t\t\tbool prevIsElevated = false;\n#if ADVANCEDAI && ROUTING\n\t\t\t// NON-STOCK CODE START\n\t\t\tbool prevIsCarLane = false;\n\t\t\t// NON-STOCK CODE END\n#endif\n\t\t\tint prevRelSimilarLaneIndex = 0;\n\t\t\t// NON-STOCK CODE START\n\t\t\tfloat prevMaxSpeed = 1f;\n\t\t\tfloat prevLaneSpeed = 1f;\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tNetInfo prevSegmentInfo = prevSegment.Info;\n\t\t\tif (prevLaneIndex < prevSegmentInfo.m_lanes.Length) {\n\t\t\t\tNetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];\n\t\t\t\tprevIsPedestrianLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian);\n\t\t\t\tprevIsBicycleLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Vehicle && (prevLaneInfo.m_vehicleType & m_vehicleTypes) == VehicleInfo.VehicleType.Bicycle);\n\t\t\t\tprevIsCenterPlatform = prevLaneInfo.m_centerPlatform;\n\t\t\t\tprevIsElevated = prevLaneInfo.m_elevated;\n\n#if (ADVANCEDAI || PARKINGAI) && ROUTING\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tprevIsCarLane =\n\t\t\t\t\t(prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&\n\t\t\t\t\t(prevLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None\n\t\t\t\t;\n\t\t\t\t// NON-STOCK CODE END\n#endif\n\n\t\t\t\t// NON-STOCK CODE START\n#if SPEEDLIMITS\n\t\t\t\tprevMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(prevSegmentId, prevLaneIndex, item.m_laneID, prevLaneInfo);\n#else\n\t\t\t\tprevMaxSpeed = prevLaneInfo.m_speedLimit;\n#endif\n\t\t\t\tprevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo);\n\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\tprevRelSimilarLaneIndex = (((prevLaneInfo.m_finalDirection & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? (prevLaneInfo.m_similarLaneCount - prevLaneInfo.m_similarLaneIndex - 1) : prevLaneInfo.m_similarLaneIndex);\n\t\t\t}\n\n\t\t\tif (isMiddle) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: middle: Exploring middle node\\n\" +\n\t\t\t\t\t\t\"\\t\" + $\"nextNodeId={nextNodeId}\"\n\t\t\t\t\t);\n\t\t\t\t}\n#endif\n\t\t\t\tfor (int i = 0; i < 8; i++) {\n\t\t\t\t\tushort nextSegmentId = nextNode.GetSegment(i);\n\t\t\t\t\tif (nextSegmentId != 0) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: middle: Exploring next segment behind middle node\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextSegmentId={nextSegmentId}\");\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tProcessItemCosts(\n#if DEBUG\n\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\titem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, true, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, !prevIsPedestrianLane, prevIsPedestrianLane\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (prevIsPedestrianLane) {\n\t\t\t\t// we are going to a pedestrian lane\n\t\t\t\tif (!prevIsElevated) {\n\t\t\t\t\tif (nextNode.Info.m_class.m_service != ItemClass.Service.Beautification) {\n\t\t\t\t\t\tbool canCrossStreet = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;\n\t\t\t\t\t\tbool isOnCenterPlatform = prevIsCenterPlatform && (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Junction)) == NetNode.Flags.None;\n\t\t\t\t\t\tushort nextLeftSegmentId = prevSegmentId;\n\t\t\t\t\t\tushort nextRightSegmentId = prevSegmentId;\n\t\t\t\t\t\tint leftLaneIndex;\n\t\t\t\t\t\tint rightLaneIndex;\n\t\t\t\t\t\tuint leftLaneId;\n\t\t\t\t\t\tuint rightLaneId;\n\n\t\t\t\t\t\tprevSegment.GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, prevLaneIndex, isOnCenterPlatform, out leftLaneIndex, out rightLaneIndex, out leftLaneId, out rightLaneId);\n\n\t\t\t\t\t\tif (leftLaneId == 0 || rightLaneId == 0) {\n\t\t\t\t\t\t\tushort leftSegmentId;\n\t\t\t\t\t\t\tushort rightSegmentId;\n\t\t\t\t\t\t\tprevSegment.GetLeftAndRightSegments(nextNodeId, out leftSegmentId, out rightSegmentId);\n\n\t\t\t\t\t\t\tint numIter = 0;\n\t\t\t\t\t\t\twhile (leftSegmentId != 0 && leftSegmentId != prevSegmentId && leftLaneId == 0) {\n\t\t\t\t\t\t\t\tint someLeftLaneIndex;\n\t\t\t\t\t\t\t\tint someRightLaneIndex;\n\t\t\t\t\t\t\t\tuint someLeftLaneId;\n\t\t\t\t\t\t\t\tuint someRightLaneId;\n\t\t\t\t\t\t\t\tnetManager.m_segments.m_buffer[leftSegmentId].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);\n\n\t\t\t\t\t\t\t\tif (someRightLaneId != 0) {\n\t\t\t\t\t\t\t\t\tnextLeftSegmentId = leftSegmentId;\n\t\t\t\t\t\t\t\t\tleftLaneIndex = someRightLaneIndex;\n\t\t\t\t\t\t\t\t\tleftLaneId = someRightLaneId;\n\t\t\t\t\t\t\t\t\tbreak; // NON-STOCK CODE\n\t\t\t\t\t\t\t\t} else {\n#if JUNCTIONRESTRICTIONS\n\t\t\t\t\t\t\t\t\t// next segment does not have pedestrian lanes but cims need to cross it to reach the next segment\n\t\t\t\t\t\t\t\t\tif (!m_junctionManager.IsPedestrianCrossingAllowed(leftSegmentId, netManager.m_segments.m_buffer[leftSegmentId].m_startNode == nextNodeId)) {\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\tleftSegmentId = netManager.m_segments.m_buffer[leftSegmentId].GetLeftSegment(nextNodeId);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (++numIter == 8) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tnumIter = 0;\n\t\t\t\t\t\t\twhile (rightSegmentId != 0 && rightSegmentId != prevSegmentId && rightLaneId == 0) {\n\t\t\t\t\t\t\t\tint someLeftLaneIndex;\n\t\t\t\t\t\t\t\tint someRightLaneIndex;\n\t\t\t\t\t\t\t\tuint someLeftLaneId;\n\t\t\t\t\t\t\t\tuint someRightLaneId;\n\t\t\t\t\t\t\t\tnetManager.m_segments.m_buffer[rightSegmentId].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);\n\n\t\t\t\t\t\t\t\tif (someLeftLaneId != 0) {\n\t\t\t\t\t\t\t\t\tnextRightSegmentId = rightSegmentId;\n\t\t\t\t\t\t\t\t\trightLaneIndex = someLeftLaneIndex;\n\t\t\t\t\t\t\t\t\trightLaneId = someLeftLaneId;\n\t\t\t\t\t\t\t\t\tbreak; // NON-STOCK CODE\n\t\t\t\t\t\t\t\t} else {\n#if JUNCTIONRESTRICTIONS\n\t\t\t\t\t\t\t\t\t// next segment does not have pedestrian lanes but cims need to cross it to reach the next segment\n\t\t\t\t\t\t\t\t\tif (!m_junctionManager.IsPedestrianCrossingAllowed(rightSegmentId, netManager.m_segments.m_buffer[rightSegmentId].m_startNode == nextNodeId)) {\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\trightSegmentId = netManager.m_segments.m_buffer[rightSegmentId].GetRightSegment(nextNodeId);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (++numIter == 8) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (leftLaneId != 0 && (nextLeftSegmentId != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: ped -> ped: Exploring left pedestrian lane\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"leftLaneId={leftLaneId}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextLeftSegmentId={nextLeftSegmentId}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"canCrossStreet={canCrossStreet}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"isOnCenterPlatform={isOnCenterPlatform}\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\tProcessItemPedBicycle(\n#if DEBUG\n\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\t\titem, 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);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (rightLaneId != 0 && rightLaneId != leftLaneId && (nextRightSegmentId != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: ped -> ped: Exploring right pedestrian lane\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"leftLaneId={leftLaneId}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"rightLaneId={rightLaneId}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextRightSegmentId={nextRightSegmentId}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"canCrossStreet={canCrossStreet}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"isOnCenterPlatform={isOnCenterPlatform}\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\tProcessItemPedBicycle(\n#if DEBUG\n\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\t\titem, 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);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// switch from bicycle lane to pedestrian lane\n\t\t\t\t\t\tint nextLaneIndex;\n\t\t\t\t\t\tuint nextLaneId;\n\t\t\t\t\t\tif ((m_vehicleTypes & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None &&\n\t\t\t\t\t\t\tprevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Bicycle, out nextLaneIndex, out nextLaneId)) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: bicycle -> ped: Exploring bicycle switch\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"leftLaneId={leftLaneId}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"rightLaneId={rightLaneId}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextRightSegmentId={nextRightSegmentId}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"canCrossStreet={canCrossStreet}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"isOnCenterPlatform={isOnCenterPlatform}\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\tProcessItemPedBicycle(\n#if DEBUG\n\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\t\titem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, nextLaneIndex, nextLaneId, ref netManager.m_lanes.m_buffer[nextLaneId], connectOffset, connectOffset);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: beautification -> ped: Exploring pedestrian lane to beautficiation node\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextNodeId={nextNodeId}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t// we are going from pedestrian lane to a beautification node\n\t\t\t\t\t\tfor (int j = 0; j < 8; j++) {\n\t\t\t\t\t\t\tushort nextSegmentId = nextNode.GetSegment(j);\n\t\t\t\t\t\t\tif (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: beautification -> ped: Exploring next segment behind beautification node\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextSegmentId={nextSegmentId}\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t\tProcessItemCosts(\n#if DEBUG\n\t\t\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\t\t\titem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, false, true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// prepare switching from a vehicle to pedestrian lane\n\t\t\t\t\tNetInfo.LaneType nextLaneType = m_laneTypes & ~NetInfo.LaneType.Pedestrian;\n\t\t\t\t\tVehicleInfo.VehicleType nextVehicleType = m_vehicleTypes & ~VehicleInfo.VehicleType.Bicycle;\n\t\t\t\t\tif ((item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {\n\t\t\t\t\t\tnextLaneType &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> ped: Prepared parameters\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"m_queueItem.vehicleType={m_queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"nextVehicleType={nextVehicleType}\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"nextLaneType={nextLaneType}\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\tbool parkingAllowed = true;\n\n#if PARKINGAI\n\t\t\t\t\t// Parking AI: Determine if parking is allowed\n\t\t\t\t\tif (Options.prohibitPocketCars) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> ped: Parking AI: Determining if parking is allowed here\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"m_queueItem.vehicleType={m_queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextVehicleType={nextVehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextLaneType={nextLaneType}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"item.m_lanesUsed={item.m_lanesUsed}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"m_endLaneA={m_endLaneA}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"m_endLaneB={m_endLaneB}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tif (m_queueItem.vehicleType == ExtVehicleType.PassengerCar &&\n\t\t\t\t\t\t\t(nextVehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None &&\n\t\t\t\t\t\t\t((nextLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None)) {\n\t\t\t\t\t\t\tif ((item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {\n\t\t\t\t\t\t\t\t/* if pocket cars are prohibited, a citizen may only park their car once per path */\n\t\t\t\t\t\t\t\tparkingAllowed = false;\n\t\t\t\t\t\t\t} else if ((item.m_lanesUsed & NetInfo.LaneType.PublicTransport) == NetInfo.LaneType.None) {\n\t\t\t\t\t\t\t\t/* if the citizen is walking to their target (= no public transport used), the passenger car must be parked in the very last moment */\n\t\t\t\t\t\t\t\tparkingAllowed = item.m_laneID == m_endLaneA || item.m_laneID == m_endLaneB;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> ped: Parking AI: Parking allowed here? {parkingAllowed}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t}\n#endif\n\t\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\t\tint sameSegLaneIndex;\n\t\t\t\t\tuint sameSegLaneId;\n\t\t\t\t\tif (parkingAllowed && // NON-STOCK CODE\n\t\t\t\t\t\tnextLaneType != NetInfo.LaneType.None &&\n\t\t\t\t\t\tnextVehicleType != VehicleInfo.VehicleType.None &&\n\t\t\t\t\t\tprevSegment.GetClosestLane(prevLaneIndex, nextLaneType, nextVehicleType, out sameSegLaneIndex, out sameSegLaneId)\n\t\t\t\t\t) {\n\t\t\t\t\t\tNetInfo.Lane sameSegLaneInfo = prevSegmentInfo.m_lanes[sameSegLaneIndex];\n\t\t\t\t\t\tbyte sameSegConnectOffset = (byte)(((prevSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None == ((sameSegLaneInfo.m_finalDirection & NetInfo.Direction.Backward) != NetInfo.Direction.None)) ? 1 : 254);\n\t\t\t\t\t\tBufferItem nextItem = item;\n\t\t\t\t\t\tif (m_randomParking) {\n\t\t\t\t\t\t\tnextItem.m_comparisonValue += (float)m_pathRandomizer.Int32(300u) / m_maxLength;\n\t\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> ped: Exploring parking\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextLaneType={nextLaneType}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextVehicleType={nextVehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"nextLaneType={nextLaneType}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"sameSegConnectOffset={sameSegConnectOffset}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"m_randomParking={m_randomParking}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tProcessItemPedBicycle(\n#if DEBUG\n\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\tnextItem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, sameSegLaneIndex, sameSegLaneId, ref netManager.m_lanes.m_buffer[sameSegLaneId], sameSegConnectOffset, 128);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// We are going to a non-pedestrian lane\n\n\t\t\t\tbool nextIsBeautificationNode = nextNode.Info.m_class.m_service == ItemClass.Service.Beautification; // NON-STOCK CODE (refactored)\n\t\t\t\tbool allowPedestrian = (m_laneTypes & NetInfo.LaneType.Pedestrian) != NetInfo.LaneType.None; // allow switching from pedestrian lane to a non-pedestrian lane?\n\t\t\t\tbool allowBicycle = false; // allow switching from a pedestrian lane to a bike lane?\n\t\t\t\tbyte switchConnectOffset = 0; // lane switching offset\n\t\t\t\tif (allowPedestrian) {\n\t\t\t\t\tif (prevIsBicycleLane) {\n\t\t\t\t\t\t// we are going to a bicycle lane\n\t\t\t\t\t\tswitchConnectOffset = connectOffset;\n\t\t\t\t\t\tallowBicycle = nextIsBeautificationNode;\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: ped -> vehicle: Switching to a bicycle may be allowed here\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"switchConnectOffset={switchConnectOffset}\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"allowBicycle={allowBicycle}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t} else if (m_vehicleLane != 0) {\n\t\t\t\t\t\t// there is a parked vehicle position\n\t\t\t\t\t\tif (m_vehicleLane != item.m_laneID) {\n\t\t\t\t\t\t\t// we have not reached the parked vehicle yet\n\t\t\t\t\t\t\tallowPedestrian = false;\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: ped -> vehicle: Entering a parked vehicle is not allowed here\");\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// pedestrian switches to parked vehicle\n\t\t\t\t\t\t\tswitchConnectOffset = m_vehicleOffset;\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: ped -> vehicle: Entering a parked vehicle is allowed here\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"switchConnectOffset={switchConnectOffset}\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (m_stablePath) {\n\t\t\t\t\t\t// enter a bus\n\t\t\t\t\t\tswitchConnectOffset = 128;\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: ped -> vehicle: Entering a bus is allowed here\\n\" +\n\t\t\t\t\t\t\t\t\"\\t\" + $\"switchConnectOffset={switchConnectOffset}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// pocket car spawning\n#if PARKINGAI\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tOptions.prohibitPocketCars\n\t\t\t\t\t\t) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: ped -> vehicle: Parking AI: Determining if spawning pocket cars is allowed\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"m_queueItem.pathType={m_queueItem.pathType}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"prevIsCarLane={prevIsCarLane}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"m_queueItem.vehicleType={m_queueItem.vehicleType}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"m_startSegmentA={m_startSegmentA}\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"m_startSegmentB={m_startSegmentB}\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t(m_queueItem.pathType == ExtCitizenInstance.ExtPathType.WalkingOnly && prevIsCarLane) || \n\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\tm_queueItem.pathType == ExtCitizenInstance.ExtPathType.DrivingOnly &&\n\t\t\t\t\t\t\t\t\tm_queueItem.vehicleType == ExtVehicleType.PassengerCar &&\n\t\t\t\t\t\t\t\t\t((item.m_position.m_segment != m_startSegmentA && item.m_position.m_segment != m_startSegmentB) || !prevIsCarLane)\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t/* allow pocket cars only if an instant driving path is required and we are at the start segment */\n\t\t\t\t\t\t\t\t/* disallow pocket cars on walking paths */\n\t\t\t\t\t\t\t\tallowPedestrian = false;\n\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: ped -> vehicle: Parking AI: Spawning pocket cars is not allowed here\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tswitchConnectOffset = (byte)m_pathRandomizer.UInt32(1u, 254u);\n\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: ped -> vehicle: Parking AI: Spawning pocket cars is allowed here\\n\" +\n\t\t\t\t\t\t\t\t\t\t\"\\t\" + $\"switchConnectOffset={switchConnectOffset}\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n#endif\n\t\t\t\t\t\t\tswitchConnectOffset = (byte)m_pathRandomizer.UInt32(1u, 254u);\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: ped -> vehicle: Spawning pocket cars is allowed here\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"switchConnectOffset={switchConnectOffset}\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\n#if PARKINGAI\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tushort nextSegmentId = 0;\n\t\t\t\tif ((m_vehicleTypes & (VehicleInfo.VehicleType.Ferry\n#if !ROUTING\n\t\t\t\t\t| VehicleInfo.VehicleType.Monorail\n#endif\n\t\t\t\t\t)) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\t// ferry (/ monorail)\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> vehicle: Exploring ferry routes\");\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tbool isUturnAllowedHere = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;\n\t\t\t\t\tfor (int k = 0; k < 8; k++) {\n\t\t\t\t\t\tnextSegmentId = nextNode.GetSegment(k);\n\t\t\t\t\t\tif (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> vehicle: Exploring ferry route\\n\" +\n\t\t\t\t\t\t\t\t\t\"\\t\" + $\"nextSegmentId={nextSegmentId}\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\tProcessItemCosts(\n#if DEBUG\n\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\t\titem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, allowBicycle);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (isUturnAllowedHere\n#if !ROUTING\n\t\t\t\t\t\t&& (m_vehicleTypes & VehicleInfo.VehicleType.Monorail) == VehicleInfo.VehicleType.None\n#endif\n\t\t\t\t\t\t) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> vehicle: Exploring ferry u-turn\");\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tnextSegmentId = prevSegmentId;\n\t\t\t\t\t\tProcessItemCosts(\n#if DEBUG\n\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\titem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, false);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// road vehicles / trams / trains / metros (/ monorails) / etc.\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> vehicle: Exploring vehicle routes\");\n\t\t\t\t\t}\n#endif\n\n\n#if ROUTING\n\t\t\t\t\tbool exploreUturn = false;\n#else\n\t\t\t\t\tbool exploreUturn = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;\n#endif\n\n#if ROUTING\n\t\t\t\t\tbool prevIsRouted = false;\n\t\t\t\t\tuint laneRoutingIndex = 0;\n\t\t\t\t\tbool nextIsStartNode = nextNodeId == prevSegment.m_startNode;\n\t\t\t\t\tif (nextIsStartNode || nextNodeId == prevSegment.m_endNode) {\n\t\t\t\t\t\tlaneRoutingIndex = m_routingManager.GetLaneEndRoutingIndex(item.m_laneID, nextIsStartNode);\n\t\t\t\t\t\tprevIsRouted = m_routingManager.laneEndBackwardRoutings[laneRoutingIndex].routed;\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> vehicle: Is previous segment routed? {prevIsRouted}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t}\n\n\t\t\t\t\tif (allowBicycle || !prevIsRouted) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t* pedestrian to bicycle lane switch or no routing information available:\n\t\t\t\t\t\t*\t\tif pedestrian lanes should be explored (allowBicycle == true): do this here\n\t\t\t\t\t\t*\t\tif previous segment has custom routing (prevIsRouted == true): do NOT explore vehicle lanes here, else: vanilla exploration of vehicle lanes\n\t\t\t\t\t\t*/\n\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: bicycle -> vehicle / stock vehicle routing\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"prevIsRouted={prevIsRouted}\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"allowBicycle={allowBicycle}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t// NON-STOCK CODE END\n#endif\n\t\t\t\t\t\tnextSegmentId = prevSegment.GetRightSegment(nextNodeId);\n\t\t\t\t\t\tfor (int l = 0; l < 8; l++) {\n\t\t\t\t\t\t\tif (nextSegmentId == 0) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (nextSegmentId == prevSegmentId) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: bicycle -> vehicle / stock vehicle routing: exploring next segment\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextSegmentId={nextSegmentId}\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\tif (ProcessItemCosts(\n#if DEBUG\n\t\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\t\titem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, ref nextNode, false, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset,\n#if ROUTING\n\t\t\t\t\t\t\t\t!prevIsRouted // NON-STOCK CODE\n#else\n\t\t\t\t\t\t\t\ttrue\n#endif\n\t\t\t\t\t\t\t\t, allowBicycle)\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\texploreUturn = true; // allow exceptional u-turns\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: bicycle -> vehicle / stock vehicle routing: exceptional u-turn allowed\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextSegmentId={nextSegmentId}\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tnextSegmentId = netManager.m_segments.m_buffer[nextSegmentId].GetRightSegment(nextNodeId);\n\t\t\t\t\t\t}\n#if ROUTING\n\t\t\t\t\t} // NON-STOCK CODE\n#endif\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> vehicle: Custom routing\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"Options.advancedAI={Options.advancedAI}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"prevIsRouted={prevIsRouted}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"m_isRoadVehicle={m_isRoadVehicle}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"prevIsCarLane={prevIsCarLane}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"m_stablePath={Options.advancedAI}\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\tfloat segmentSelectionCost = 1f;\n\t\t\t\t\tfloat laneSelectionCost = 1f;\n\t\t\t\t\tfloat laneChangingCost = 1f;\n\t\t\t\t\tbool enableAdvancedAI = false;\n\t\t\t\t\t// NON-STOCK CODE END\n\n#if ADVANCEDAI && ROUTING\n\t\t\t\t\t/*\n\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t * Calculate Advanced Vehicle AI cost factors\n\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t */\n\t\t\t\t\tif (\n\t\t\t\t\t\tOptions.advancedAI &&\n\t\t\t\t\t\tprevIsRouted &&\n\t\t\t\t\t\tm_isRoadVehicle &&\n\t\t\t\t\t\tprevIsCarLane\n\t\t\t\t\t) {\n\t\t\t\t\t\tenableAdvancedAI = true;\n\t\t\t\t\t\tif (!m_stablePath) {\n\t\t\t\t\t\t\tCalculateAdvancedAiCostFactors(\n#if DEBUG\n\t\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\t\tref item, ref prevSegment, ref prevLane, nextNodeId, ref nextNode, ref segmentSelectionCost, ref laneSelectionCost, ref laneChangingCost\n\t\t\t\t\t\t\t);\n\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> vehicle: Custom routing with activated Advanced Vehicle AI: Calculated cost factors\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"segmentSelectionCost={segmentSelectionCost}\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"laneChangingCost={laneChangingCost}\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> vehicle: Custom routing with activated Advanced Vehicle AI and stable path: Using default cost factors\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"segmentSelectionCost={segmentSelectionCost}\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"laneChangingCost={laneChangingCost}\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#endif\n\n#if ROUTING\n\t\t\t\t\tif (prevIsRouted) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> vehicle: Custom routing: Exploring custom routes\");\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\texploreUturn = false; // custom routing processes regular u-turns\n\t\t\t\t\t\tif (ProcessItemRouted(\n#if DEBUG\n\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\titem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed\n#if ADVANCEDAI\n\t\t\t\t\t\t\t, enableAdvancedAI, laneChangingCost,\n#endif\n\t\t\t\t\t\t\tsegmentSelectionCost, laneSelectionCost, nextNodeId, ref nextNode, false, m_routingManager.segmentRoutings[prevSegmentId], m_routingManager.laneEndBackwardRoutings[laneRoutingIndex], connectOffset, prevRelSimilarLaneIndex\n\t\t\t\t\t\t)) {\n\t\t\t\t\t\t\texploreUturn = true; // allow exceptional u-turns\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> vehicle: Custom routing: No custom routing present\");\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tif (!exploreUturn) {\n\t\t\t\t\t\t\t// no exceptional u-turns allowed: allow regular u-turns\n\t\t\t\t\t\t\texploreUturn = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;\n\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> vehicle: Custom routing: Allowing regular u-turns:\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"exploreUturn={exploreUturn}\\n\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tif (exploreUturn && (m_vehicleTypes & VehicleInfo.VehicleType.Tram) == VehicleInfo.VehicleType.None) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: vehicle -> vehicle: Exploring stock u-turn\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"exploreUturn={exploreUturn}\\n\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tProcessItemCosts(\n#if DEBUG\n\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\titem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed,\n#if ADVANCEDAI && ROUTING\n\t\t\t\t\t\t\tfalse, 0f,\n#endif\n\t\t\t\t\t\t\tnextNodeId, ref nextNode, false, prevSegmentId, ref prevSegment,\n#if ROUTING\n\t\t\t\t\t\t\tsegmentSelectionCost, laneSelectionCost, null,\n#endif\n\t\t\t\t\t\t\tref prevRelSimilarLaneIndex, connectOffset, true, false\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (allowPedestrian) {\n\t\t\t\t\tint nextLaneIndex;\n\t\t\t\t\tuint nextLaneId;\n\t\t\t\t\tif (prevSegment.GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Pedestrian, m_vehicleTypes, out nextLaneIndex, out nextLaneId)) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: ped -> vehicle: Exploring switch\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextLaneIndex={nextLaneIndex}\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextLaneId={nextLaneId}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tProcessItemPedBicycle(\n#if DEBUG\n\t\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\t\titem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, prevSegmentId, ref prevSegment, nextNodeId, ref nextNode, nextLaneIndex, nextLaneId, ref netManager.m_lanes.m_buffer[nextLaneId], switchConnectOffset, switchConnectOffset);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (nextNode.m_lane != 0) {\n\t\t\t\tbool targetDisabled = (nextNode.m_flags & (NetNode.Flags.Disabled | NetNode.Flags.DisableOnlyMiddle)) == NetNode.Flags.Disabled;\n\t\t\t\tushort nextSegmentId = netManager.m_lanes.m_buffer[nextNode.m_lane].m_segment;\n\t\t\t\tif (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemMain: transport -> *: Exploring special node\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"nextSegmentId={nextSegmentId}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"nextNode.m_lane={nextNode.m_lane}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"targetDisabled={targetDisabled}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"nextNodeId={nextNodeId}\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tProcessItemPublicTransport(\n#if DEBUG\n\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\titem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed, nextNodeId, targetDisabled, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], nextNode.m_lane, nextNode.m_laneOffset, connectOffset);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// 2\n\t\tprivate void ProcessItemPublicTransport(\n#if DEBUG\n\t\t\tbool debug, uint unitId,\n#endif\n\t\t\tBufferItem 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) {\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemPublicTransport called.\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"prevMaxSpeed={prevMaxSpeed}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"prevLaneSpeed={prevLaneSpeed}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextNodeId={nextNodeId}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"targetDisabled={targetDisabled}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextLaneId={nextLaneId}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"offset={offset}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"connectOffset={connectOffset}\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\n\t\t\tif ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemPublicTransport: Aborting: Disable mask\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"m_disableMask={m_disableMask}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"nextSegment.m_flags={nextSegment.m_flags}\\n\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tif (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) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemPublicTransport: Aborting: Target disabled\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tNetInfo nextSegmentInfo = nextSegment.Info;\n\t\t\tNetInfo prevSegmentInfo = prevSegment.Info;\n\t\t\tint nextNumLanes = nextSegmentInfo.m_lanes.Length;\n\t\t\t// float prevMaxSpeed = 1f; // stock code commented\n\t\t\t// float prevLaneSpeed = 1f; // stock code commented\n\t\t\tNetInfo.LaneType prevLaneType = NetInfo.LaneType.None;\n\t\t\tif (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {\n\t\t\t\tNetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];\n\t\t\t\t// prevMaxSpeed = prevLaneInfo.m_speedLimit; // stock code commented\n\t\t\t\t// prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // stock code commented\n\t\t\t\tprevLaneType = prevLaneInfo.m_laneType;\n\t\t\t\tif ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {\n\t\t\t\t\tprevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfloat prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? prevSegment.m_averageLength : prevLane.m_length;\n\t\t\tfloat offsetLength = (float)Mathf.Abs(connectOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength;\n\t\t\tfloat methodDistance = item.m_methodDistance + offsetLength;\n\t\t\tfloat comparisonValue = item.m_comparisonValue + offsetLength / (prevLaneSpeed * m_maxLength);\n\t\t\tfloat duration = item.m_duration + offsetLength / prevMaxSpeed;\n\t\t\tVector3 b = prevLane.CalculatePosition((float)(int)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\n\t\t\tif (!m_ignoreCost) {\n\t\t\t\tint ticketCost = prevLane.m_ticketCost;\n\t\t\t\tif (ticketCost != 0) {\n\t\t\t\t\tcomparisonValue += (float)(ticketCost * m_pathRandomizer.Int32(2000u)) * TICKET_COST_CONVERSION_FACTOR;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tint nextLaneIndex = 0;\n\t\t\tuint curLaneId = nextSegment.m_lanes;\n\t\t\twhile (true) {\n\t\t\t\tif (nextLaneIndex < nextNumLanes && curLaneId != 0) {\n\t\t\t\t\tif (nextLaneId != curLaneId) {\n\t\t\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t\t\tnextLaneIndex++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemPublicTransport: Aborting: Next lane not found\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $\"ProcessItemPublicTransport: Exploring next lane\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextLaneIndex={nextLaneIndex}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextLaneId={nextLaneId}\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\n\t\t\tNetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];\n\t\t\tif (nextLaneInfo.CheckType(m_laneTypes, m_vehicleTypes)) {\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $\"ProcessItemPublicTransport: Next lane compatible\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"nextLaneInfo.m_vehicleType={nextLaneInfo.m_vehicleType}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}\"\n\t\t\t\t\t);\n\t\t\t\t}\n#endif\n\n\t\t\t\tVector3 a = netManager.m_lanes.m_buffer[nextLaneId].CalculatePosition((float)(int)offset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\t\tfloat distance = Vector3.Distance(a, b);\n\t\t\t\tBufferItem nextItem = default(BufferItem);\n\n\t\t\t\tnextItem.m_position.m_segment = nextSegmentId;\n\t\t\t\tnextItem.m_position.m_lane = (byte)nextLaneIndex;\n\t\t\t\tnextItem.m_position.m_offset = offset;\n\n\t\t\t\tif ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {\n\t\t\t\t\tnextItem.m_methodDistance = 0f;\n\t\t\t\t} else {\n\t\t\t\t\tnextItem.m_methodDistance = methodDistance + distance;\n\t\t\t\t}\n\n\t\t\t\tif (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance) && !m_stablePath) { // NON-STOCK CODE (custom walking distance)\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $\"ProcessItemPublicTransport: Aborting: Max. walking distance exceeded\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_methodDistance={nextItem.m_methodDistance}\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tfloat nextMaxSpeed;\n#if SPEEDLIMITS\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tnextMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo);\n\t\t\t\t// NON-STOCK CODE END\n#else\n\t\t\t\tnextMaxSpeed = nextLaneInfo.m_speedLimit;\n#endif\n\n\t\t\t\tnextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength);\n\t\t\t\tnextItem.m_duration = duration + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);\n\n\t\t\t\tif ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\t\tnextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);\n\t\t\t\t} else {\n\t\t\t\t\tnextItem.m_direction = nextLaneInfo.m_finalDirection;\n\t\t\t\t}\n\n\t\t\t\tif (nextLaneId == m_startLaneA) {\n\t\t\t\t\tif (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetA) &&\n\t\t\t\t\t\t((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetA)) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $\"ProcessItemPublicTransport: Aborting: Invalid offset/direction on start lane A\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_direction={nextItem.m_direction}\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"m_startOffsetA={m_startOffsetA}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat nextSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);\n\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\n\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);\n\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;\n\t\t\t\t}\n\n\t\t\t\tif (nextLaneId == m_startLaneB) {\n\t\t\t\t\tif (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetB) &&\n\t\t\t\t\t\t((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetB)) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $\"ProcessItemPublicTransport: Aborting: Invalid offset/direction on start lane B\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_direction={nextItem.m_direction}\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"m_startOffsetB={m_startOffsetB}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat nextSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);\n\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetB) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\n\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);\n\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;\n\t\t\t\t}\n\n\t\t\t\tnextItem.m_laneID = nextLaneId;\n\t\t\t\tnextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);\n#if PARKINGAI\n\t\t\t\tnextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);\n#endif\n#if ADVANCEDAI && ROUTING\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tnextItem.m_trafficRand = item.m_trafficRand;\n\t\t\t\t// NON-STOCK CODE END\n#endif\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, curLaneId, $\"ProcessItemPublicTransport: Adding next item\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"nextItem={nextItem}\"\n\t\t\t\t\t);\n\t\t\t\t}\n#endif\n\n\t\t\t\tAddBufferItem(\n#if DEBUG\n\t\t\t\t\tdebug,\n#endif\n\t\t\t\t\tnextItem, item.m_position\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n#if ADVANCEDAI && ROUTING\n\t\t// 3a (non-routed, no adv. AI)\n\t\tprivate bool ProcessItemCosts(\n#if DEBUG\n\t\t\tbool debug, uint unitId,\n#endif\n\t\t\tBufferItem 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) {\n\t\t\treturn ProcessItemCosts(\n#if DEBUG\n\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\titem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed,\n#if ADVANCEDAI && ROUTING\n\t\t\t\tfalse, 0f,\n#endif\n\t\t\t\tnextNodeId, ref nextNode, isMiddle, nextSegmentId, ref nextSegment,\n#if ROUTING\n\t\t\t\t1f, 1f, null,\n#endif\n\t\t\tref laneIndexFromInner, connectOffset, enableVehicle, enablePedestrian);\n\t\t}\n#endif\n\n\t\t// 3b\n\t\tprivate bool ProcessItemCosts(\n#if DEBUG\n\t\t\tbool debug, uint unitId,\n#endif\n\t\t\tBufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed,\n#if ADVANCEDAI && ROUTING\n\t\t\tbool enableAdvancedAI, float laneChangingCost,\n#endif\n\t\t\tushort nextNodeId, ref NetNode nextNode, bool isMiddle, ushort nextSegmentId, ref NetSegment nextSegment,\n#if ROUTING\n\t\t\tfloat segmentSelectionCost, float laneSelectionCost, LaneTransitionData? transition,\n#endif\n\t\t\tref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian\n\t\t) {\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemCosts called.\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"prevMaxSpeed={prevMaxSpeed}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"prevLaneSpeed={prevLaneSpeed}\\n\"\n#if ADVANCEDAI && ROUTING\n\t\t\t\t\t+ \"\\t\" + $\"enableAdvancedAI={enableAdvancedAI}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"laneChangingCost={laneChangingCost}\\n\"\n#endif\n\t\t\t\t\t+ \"\\t\" + $\"nextNodeId={nextNodeId}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"isMiddle={isMiddle}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextSegmentId={nextSegmentId}\\n\"\n#if ROUTING\n\t\t\t\t\t+ \"\\t\" + $\"segmentSelectionCost={segmentSelectionCost}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"transition={transition}\\n\"\n#endif\n\t\t\t\t\t+ \"\\t\" + $\"laneIndexFromInner={laneIndexFromInner}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"connectOffset={connectOffset}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"enableVehicle={enableVehicle}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"enablePedestrian={enablePedestrian}\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\n\t\t\tbool blocked = false;\n\t\t\tif ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemCosts: Aborting: Disable mask\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"m_disableMask={m_disableMask}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"nextSegment.m_flags={nextSegment.m_flags}\\n\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn blocked;\n\t\t\t}\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tNetInfo nextSegmentInfo = nextSegment.Info;\n\t\t\tNetInfo prevSegmentInfo = prevSegment.Info;\n\t\t\tint nextNumLanes = nextSegmentInfo.m_lanes.Length;\n\t\t\tNetInfo.Direction nextDir = (nextNodeId != nextSegment.m_startNode) ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;\n\t\t\tNetInfo.Direction nextFinalDir = ((nextSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? nextDir : NetInfo.InvertDirection(nextDir);\n\t\t\t// float prevMaxSpeed = 1f; // stock code commented\n\t\t\t// float prevLaneSpeed = 1f; // stock code commented\n\t\t\tNetInfo.LaneType prevLaneType = NetInfo.LaneType.None;\n\t\t\tVehicleInfo.VehicleType prevVehicleType = VehicleInfo.VehicleType.None;\n\t\t\tif (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {\n\t\t\t\tNetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];\n\t\t\t\tprevLaneType = prevLaneInfo.m_laneType;\n\t\t\t\tprevVehicleType = prevLaneInfo.m_vehicleType;\n\t\t\t\t// prevMaxSpeed = prevLaneInfo.m_speedLimit; // stock code commented\n\t\t\t\t// prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, connectOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // stock code commented\n\t\t\t}\n\n\t\t\tbool acuteTurningAngle = false;\n\t\t\tif (prevLaneType == NetInfo.LaneType.Vehicle && (prevVehicleType & VehicleInfo.VehicleType.Car) == VehicleInfo.VehicleType.None) {\n\t\t\t\tfloat turningAngle = 0.01f - Mathf.Min(nextSegmentInfo.m_maxTurnAngleCos, prevSegmentInfo.m_maxTurnAngleCos);\n\t\t\t\tif (turningAngle < 1f) {\n\t\t\t\t\tVector3 vector = (nextNodeId != prevSegment.m_startNode) ? prevSegment.m_endDirection : prevSegment.m_startDirection;\n\t\t\t\t\tVector3 vector2 = ((nextDir & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? nextSegment.m_startDirection : nextSegment.m_endDirection;\n\t\t\t\t\tfloat dirDotProd = vector.x * vector2.x + vector.z * vector2.z;\n\t\t\t\t\tif (dirDotProd >= turningAngle) {\n\t\t\t\t\t\tacuteTurningAngle = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfloat prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? prevSegment.m_averageLength : prevLane.m_length;\n\t\t\tfloat offsetLength = (float)Mathf.Abs(connectOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength;\n\t\t\tfloat methodDistance = item.m_methodDistance + offsetLength;\n\t\t\tfloat duration = item.m_duration + offsetLength / prevMaxSpeed;\n\n\t\t\tif (!m_stablePath) {\n#if ADVANCEDAI && ROUTING\n\t\t\t\tif (!enableAdvancedAI) {\n#endif\n\t\t\t\t\toffsetLength *= (float)(new Randomizer(m_pathFindIndex << 16 | item.m_position.m_segment).Int32(900, 1000 + prevSegment.m_trafficDensity * 10) + m_pathRandomizer.Int32(20u)) * 0.001f;\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemCosts: Applied stock segment randomization cost factor\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"offsetLength={offsetLength}\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n#endif\n#if ADVANCEDAI && ROUTING\n\t\t\t\t}\n#endif\n\t\t\t}\n\n\t\t\tif ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None && (prevVehicleType & m_vehicleTypes) == VehicleInfo.VehicleType.Car && (prevSegment.m_flags & m_carBanMask) != NetSegment.Flags.None) {\n\t\t\t\toffsetLength *= 7.5f;\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemCosts: Applied stock car ban cost factor\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"offsetLength={offsetLength}\"\n\t\t\t\t\t);\n\t\t\t\t}\n#endif\n\t\t\t}\n\n\t\t\tif (m_transportVehicle && prevLaneType == NetInfo.LaneType.TransportVehicle) {\n\t\t\t\toffsetLength *= 0.95f;\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemCosts: Applied stock transport vehicle cost factor\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"offsetLength={offsetLength}\"\n\t\t\t\t\t);\n\t\t\t\t}\n#endif\n\t\t\t}\n\n#if ROUTING\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemCosts: Applying custom selection cost factors\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"offsetLength={offsetLength}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"segmentSelectionCost={segmentSelectionCost}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\t\t\toffsetLength *= segmentSelectionCost;\n\t\t\toffsetLength *= laneSelectionCost;\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemCosts: Applied custom selection cost factors\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"offsetLength={offsetLength}\"\n\t\t\t\t);\n\t\t\t}\n#endif\n#endif\n\n\t\t\tfloat baseLength = offsetLength / (prevLaneSpeed * m_maxLength); // NON-STOCK CODE\n\t\t\tfloat comparisonValue = item.m_comparisonValue; // NON-STOCK CODE\n#if ROUTING\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemCosts: Calculated base length\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"baseLength={baseLength}\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\t\t\tif (\n#if ADVANCEDAI\n\t\t\t\t!enableAdvancedAI &&\n#endif\n\t\t\t\t!m_stablePath) {\n\t\t\t\tcomparisonValue += baseLength;\n\t\t\t}\n#endif\n\t\t\tint ticketCost = prevLane.m_ticketCost;\n\t\t\tif (!m_ignoreCost && ticketCost != 0) {\n\t\t\t\tcomparisonValue += (float)(ticketCost * m_pathRandomizer.Int32(2000u)) * TICKET_COST_CONVERSION_FACTOR;\n\t\t\t}\n\n\t\t\tif ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {\n\t\t\t\tprevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t}\n\n\t\t\tVector3 b = prevLane.CalculatePosition((float)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\tint newLaneIndexFromInner = laneIndexFromInner;\n\t\t\tbool isTransition = (nextNode.m_flags & NetNode.Flags.Transition) != NetNode.Flags.None;\n\n\t\t\tNetInfo.LaneType allowedLaneTypes = m_laneTypes;\n\t\t\tVehicleInfo.VehicleType allowedVehicleTypes = m_vehicleTypes;\n\t\t\tif (!enableVehicle) {\n\t\t\t\tallowedVehicleTypes &= VehicleInfo.VehicleType.Bicycle;\n\t\t\t\tif (allowedVehicleTypes == VehicleInfo.VehicleType.None) {\n\t\t\t\t\tallowedLaneTypes &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!enablePedestrian) {\n\t\t\t\tallowedLaneTypes &= ~NetInfo.LaneType.Pedestrian;\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n\t\t\tbool applyTransportTransferPenalty =\n\t\t\t\tOptions.realisticPublicTransport &&\n\t\t\t\t!m_stablePath &&\n\t\t\t\t(allowedLaneTypes & (NetInfo.LaneType.PublicTransport | NetInfo.LaneType.Pedestrian)) == (NetInfo.LaneType.PublicTransport | NetInfo.LaneType.Pedestrian) &&\n\t\t\t\tm_conf.PathFinding.PublicTransportTransitionMinPenalty >= 0 &&\n\t\t\t\tm_conf.PathFinding.PublicTransportTransitionMaxPenalty > m_conf.PathFinding.PublicTransportTransitionMinPenalty\n\t\t\t;\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemCosts: Shall apply transport transfer penalty?\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"applyTransportTransferPenalty={applyTransportTransferPenalty}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"Options.realisticPublicTransport={Options.realisticPublicTransport}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"allowedLaneTypes={allowedLaneTypes}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"allowedVehicleTypes={allowedVehicleTypes}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"m_conf.PathFinding.PublicTransportTransitionMinPenalty={m_conf.PathFinding.PublicTransportTransitionMinPenalty}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"m_conf.PathFinding.PublicTransportTransitionMaxPenalty={m_conf.PathFinding.PublicTransportTransitionMaxPenalty}\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\n\t\t\tint nextLaneIndex = 0;\n\t\t\tuint nextLaneId = nextSegment.m_lanes;\n\t\t\tint maxNextLaneIndex = nextNumLanes - 1;\n#if ADVANCEDAI && ROUTING\n\t\t\tbyte laneDist = 0;\n#endif\n#if ROUTING\n\t\t\tif (transition != null) {\n\t\t\t\tLaneTransitionData trans = (LaneTransitionData)transition;\n\t\t\t\tif (trans.laneIndex >= 0 && trans.laneIndex <= maxNextLaneIndex) {\n\t\t\t\t\tnextLaneIndex = trans.laneIndex;\n\t\t\t\t\tnextLaneId = trans.laneId;\n\t\t\t\t\tmaxNextLaneIndex = nextLaneIndex;\n\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemCosts: Invalid transition detected. Skipping.\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn blocked;\n\t\t\t\t}\n\n\t\t\t\tlaneDist = trans.distance;\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemCosts: Custom transition given\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"nextLaneIndex={nextLaneIndex}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"nextLaneId={nextLaneId}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"maxNextLaneIndex={maxNextLaneIndex}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"laneDist={laneDist}\"\n\t\t\t\t\t);\n\t\t\t\t}\n#endif\n\t\t\t} else {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, nextSegmentId, $\"ProcessItemCosts: No custom transition given\");\n\t\t\t\t}\n#endif\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tfor (; nextLaneIndex <= maxNextLaneIndex && nextLaneId != 0; nextLaneIndex++) {\n\t\t\t\tNetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];\n\t\t\t\tif ((nextLaneInfo.m_finalDirection & nextFinalDir) != NetInfo.Direction.None) {\n\t\t\t\t\tif (nextLaneInfo.CheckType(allowedLaneTypes, allowedVehicleTypes) && (nextSegmentId != item.m_position.m_segment || nextLaneIndex != item.m_position.m_lane)) {\n\t\t\t\t\t\tif (acuteTurningAngle && nextLaneInfo.m_laneType == NetInfo.LaneType.Vehicle && (nextLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) == VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tBufferItem nextItem = default(BufferItem);\n\n\t\t\t\t\t\tVector3 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;\n\t\t\t\t\t\tfloat transitionCost = Vector3.Distance(a, b);\n\t\t\t\t\t\tif (isTransition) {\n\t\t\t\t\t\t\ttransitionCost *= 2f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ticketCost != 0 && netManager.m_lanes.m_buffer[nextLaneId].m_ticketCost != 0) {\n\t\t\t\t\t\t\ttransitionCost *= 10f;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfloat nextMaxSpeed;\n#if SPEEDLIMITS\n\t\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\t\tnextMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo);\n\t\t\t\t\t\t// NON-STOCK CODE END\n#else\n\t\t\t\t\t\tnextMaxSpeed = nextLaneInfo.m_speedLimit;\n#endif\n\n\t\t\t\t\t\tfloat transitionCostOverMeanMaxSpeed = transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength);\n#if ADVANCEDAI && ROUTING\n\t\t\t\t\t\tif (!enableAdvancedAI) {\n#endif\n\t\t\t\t\t\t\tif (!this.m_stablePath && (netManager.m_lanes.m_buffer[nextLaneId].m_flags & (ushort)NetLane.Flags.Merge) != 0) {\n\t\t\t\t\t\t\t\tint firstTarget = netManager.m_lanes.m_buffer[nextLaneId].m_firstTarget;\n\t\t\t\t\t\t\t\tint lastTarget = netManager.m_lanes.m_buffer[nextLaneId].m_lastTarget;\n\t\t\t\t\t\t\t\ttransitionCostOverMeanMaxSpeed *= (float)new Randomizer(this.m_pathFindIndex ^ nextLaneId).Int32(1000, (lastTarget - firstTarget + 2) * 1000) * 0.001f;\n\t\t\t\t\t\t\t}\n#if ADVANCEDAI && ROUTING\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tnextItem.m_position.m_segment = nextSegmentId;\n\t\t\t\t\t\tnextItem.m_position.m_lane = (byte)nextLaneIndex;\n\t\t\t\t\t\tnextItem.m_position.m_offset = (byte)(((nextDir & NetInfo.Direction.Forward) != NetInfo.Direction.None) ? 255 : 0);\n\t\t\t\t\t\tif ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {\n\t\t\t\t\t\t\tnextItem.m_methodDistance = 0f;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextItem.m_methodDistance = methodDistance + transitionCost;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance) && !m_stablePath) { // NON-STOCK CODE (custom walking distance)\n\t\t\t\t\t\t\tnextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\t\tif (applyTransportTransferPenalty) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tisMiddle &&\n\t\t\t\t\t\t\t\t(nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None &&\n\t\t\t\t\t\t\t\t(item.m_lanesUsed & NetInfo.LaneType.PublicTransport) != NetInfo.LaneType.None &&\n\t\t\t\t\t\t\t\tnextLaneInfo.m_laneType == NetInfo.LaneType.PublicTransport\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// apply penalty when switching between public transport lines\n\t\t\t\t\t\t\t\tfloat 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);\n\t\t\t\t\t\t\t\ttransitionCostOverMeanMaxSpeed += transportTransitionPenalty;\n\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemCosts: Applied transport transfer penalty on PT change\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"transportTransitionPenalty={transportTransitionPenalty}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"transitionCostOverMeanMaxSpeed={transitionCostOverMeanMaxSpeed}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"isMiddle={isMiddle}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"prevLaneType={prevLaneType}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"item.m_lanesUsed={item.m_lanesUsed}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\t\t(nextLaneId == m_startLaneA || nextLaneId == m_startLaneB) &&\n\t\t\t\t\t\t\t\t(item.m_lanesUsed & (NetInfo.LaneType.Pedestrian | NetInfo.LaneType.PublicTransport)) == NetInfo.LaneType.Pedestrian\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t// account for public tranport transition costs on non-PT paths\n\t\t\t\t\t\t\t\tfloat transportTransitionPenalty = (2f * m_conf.PathFinding.PublicTransportTransitionMaxPenalty) / (0.5f * this.m_maxLength);\n\t\t\t\t\t\t\t\ttransitionCostOverMeanMaxSpeed += transportTransitionPenalty;\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemCosts: Applied transport transfer penalty on non-PT path\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"transportTransitionPenalty={transportTransitionPenalty}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"transitionCostOverMeanMaxSpeed={transitionCostOverMeanMaxSpeed}\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\t\t\tnextItem.m_comparisonValue = comparisonValue + transitionCostOverMeanMaxSpeed;\n\t\t\t\t\t\tnextItem.m_duration = duration + transitionCost / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);\n\t\t\t\t\t\tnextItem.m_direction = nextDir;\n\n\t\t\t\t\t\tif (nextLaneId == m_startLaneA) {\n\t\t\t\t\t\t\tif (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetA) &&\n\t\t\t\t\t\t\t\t((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetA)) {\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemCosts: Skipping: Invalid offset/direction on start lane A\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_direction={nextItem.m_direction}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"m_startOffsetA={m_startOffsetA}\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t\tnextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfloat nextLaneSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);\n\t\t\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\n\t\t\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextLaneSpeed * m_maxLength);\n\t\t\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextLaneSpeed;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (nextLaneId == m_startLaneB) {\n\t\t\t\t\t\t\tif (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetB) &&\n\t\t\t\t\t\t\t\t((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetB)) {\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemCosts: Skipping: Invalid offset/direction on start lane B\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_direction={nextItem.m_direction}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"m_startOffsetB={m_startOffsetB}\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t\tnextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfloat nextLaneSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);\n\t\t\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetB) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\n\t\t\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextLaneSpeed * m_maxLength);\n\t\t\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextLaneSpeed;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!m_ignoreBlocked && (nextSegment.m_flags & NetSegment.Flags.Blocked) != NetSegment.Flags.None &&\n\t\t\t\t\t\t\t(nextLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tnextItem.m_comparisonValue += 0.1f;\n\t\t\t\t\t\t\tblocked = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tnextItem.m_laneID = nextLaneId;\n\t\t\t\t\t\tnextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);\n#if PARKINGAI\n\t\t\t\t\t\tnextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);\n#endif\n#if ADVANCEDAI && ROUTING\n\t\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\t\tnextItem.m_trafficRand = item.m_trafficRand;\n\t\t\t\t\t\t// NON-STOCK CODE END\n#endif\n\n#if ROUTING\n#if ADVANCEDAI\n\t\t\t\t\t\tif (enableAdvancedAI) {\n\t\t\t\t\t\t\tfloat adjustedBaseLength = baseLength;\n\t\t\t\t\t\t\tif (m_queueItem.spawned || (nextLaneId != m_startLaneA && nextLaneId != m_startLaneB)) {\n\t\t\t\t\t\t\t\tif (laneDist != 0) {\n\t\t\t\t\t\t\t\t\t// apply lane changing costs\n\t\t\t\t\t\t\t\t\tadjustedBaseLength *=\n\t\t\t\t\t\t\t\t\t\t1f +\n\t\t\t\t\t\t\t\t\t\tlaneDist *\n\t\t\t\t\t\t\t\t\t\tlaneChangingCost *\n\t\t\t\t\t\t\t\t\t\t(laneDist > 1 ? m_conf.AdvancedVehicleAI.MoreThanOneLaneChangingCostFactor : 1f); // additional costs for changing multiple lanes at once\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tnextItem.m_comparisonValue += adjustedBaseLength;\n\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemCosts: Applied Advanced Vehicle AI\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"baseLength={baseLength}\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"adjustedBaseLength={adjustedBaseLength}\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"laneDist={laneDist}\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"laneChangingCost={laneChangingCost}\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t} else\n#endif\n\t\t\t\t\t\tif (m_stablePath) {\n\t\t\t\t\t\t\t// all non-road vehicles with stable paths (trains, trams, etc.): apply lane distance factor\n\t\t\t\t\t\t\tfloat adjustedBaseLength = baseLength;\n\t\t\t\t\t\t\tadjustedBaseLength *= 1 + laneDist;\n\t\t\t\t\t\t\tnextItem.m_comparisonValue += adjustedBaseLength;\n\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemCosts: Applied stable path lane distance costs\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"baseLength={baseLength}\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"adjustedBaseLength={adjustedBaseLength}\\n\"\n\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"laneDist={laneDist}\"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t(nextLaneInfo.m_laneType & prevLaneType) != NetInfo.LaneType.None &&\n\t\t\t\t\t\t\t(nextLaneInfo.m_vehicleType & m_vehicleTypes) != VehicleInfo.VehicleType.None\n\t\t\t\t\t\t) {\n#if ADVANCEDAI && ROUTING\n\t\t\t\t\t\t\tif (! enableAdvancedAI) {\n#endif\n\t\t\t\t\t\t\t\tint firstTarget = netManager.m_lanes.m_buffer[nextLaneId].m_firstTarget;\n\t\t\t\t\t\t\t\tint lastTarget = netManager.m_lanes.m_buffer[nextLaneId].m_lastTarget;\n\t\t\t\t\t\t\t\tif (laneIndexFromInner < firstTarget || laneIndexFromInner >= lastTarget) {\n\t\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += Mathf.Max(1f, transitionCost * 3f - 3f) / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength);\n\t\t\t\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemCosts: stock lane change costs\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"firstTarget={firstTarget}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"lastTarget={lastTarget}\\n\"\n\t\t\t\t\t\t\t\t\t\t+ \"\\t\" + $\"laneIndexFromInner={laneIndexFromInner}\"\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n#endif\n#if ADVANCEDAI && ROUTING\n\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!m_transportVehicle &&\n\t\t\t\t\t\t\t\tnextLaneInfo.m_laneType == NetInfo.LaneType.TransportVehicle\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tnextItem.m_comparisonValue += 20f / ((prevMaxSpeed + nextMaxSpeed) * 0.5f * m_maxLength);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemCosts: Adding next item\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem={nextItem}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tAddBufferItem(\n#if DEBUG\n\t\t\t\t\t\t\tdebug,\n#endif\n\t\t\t\t\t\t\tnextItem, item.m_position\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemCosts: Lane type and/or vehicle type mismatch or same segment/lane. Skipping.\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"allowedLaneTypes={allowedLaneTypes}\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"allowedVehicleTypes={allowedVehicleTypes}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t}\n\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemCosts: Lane direction mismatch. Skipping.\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"nextLaneInfo.m_finalDirection={nextLaneInfo.m_finalDirection}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"nextFinalDir={nextFinalDir}\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tif ((nextLaneInfo.m_laneType & prevLaneType) != NetInfo.LaneType.None && (nextLaneInfo.m_vehicleType & prevVehicleType) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\tnewLaneIndexFromInner++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tnextLaneId = netManager.m_lanes.m_buffer[nextLaneId].m_nextLane;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlaneIndexFromInner = newLaneIndexFromInner;\n\t\t\treturn blocked;\n\t\t}\n\n\t\t// 4\n\t\tprivate void ProcessItemPedBicycle(\n#if DEBUG\n\t\t\tbool debug, uint unitId,\n#endif\n\t\t\tBufferItem 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) {\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemPedBicycle called.\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"prevMaxSpeed={prevMaxSpeed}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"prevLaneSpeed={prevLaneSpeed}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextSegmentId={nextSegmentId}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextNodeId={nextNodeId}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextLaneIndex={nextLaneIndex}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextLaneId={nextLaneId}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"connectOffset={connectOffset}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"laneSwitchOffset={laneSwitchOffset}\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\n\t\t\tif ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemPedBicycle: Aborting: Disable mask\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"m_disableMask={m_disableMask}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"nextSegment.m_flags={nextSegment.m_flags}\\n\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// NON-STOCK CODE START\n#if JUNCTIONRESTRICTIONS || CUSTOMTRAFFICLIGHTS\n\t\t\tif (Options.junctionRestrictionsEnabled || Options.timedLightsEnabled) {\n\t\t\t\tbool nextIsStartNode = nextNodeId == nextSegment.m_startNode;\n\t\t\t\tif (nextIsStartNode || nextNodeId == nextSegment.m_endNode) {\n#if JUNCTIONRESTRICTIONS\n\t\t\t\t\tif (Options.junctionRestrictionsEnabled && item.m_position.m_segment == nextSegmentId) {\n\t\t\t\t\t\t// check if pedestrians are not allowed to cross here\n\t\t\t\t\t\tif (!m_junctionManager.IsPedestrianCrossingAllowed(nextSegmentId, nextIsStartNode)) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemPedBicycle: Aborting: Pedestrian crossing prohibited\");\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#endif\n\n#if CUSTOMTRAFFICLIGHTS\n\t\t\t\t\tif (Options.timedLightsEnabled) {\n\t\t\t\t\t\t// check if pedestrian light won't change to green\n\t\t\t\t\t\tICustomSegmentLights lights = m_customTrafficLightsManager.GetSegmentLights(nextSegmentId, nextIsStartNode, false);\n\t\t\t\t\t\tif (lights != null && lights.InvalidPedestrianLight) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemPedBicycle: Aborting: Invalid pedestrian light\");\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\t// NON-STOCK CODE END\n\n\t\t\tNetInfo nextSegmentInfo = nextSegment.Info;\n\t\t\tNetInfo prevSegmentInfo = prevSegment.Info;\n\t\t\tint nextNumLanes = nextSegmentInfo.m_lanes.Length;\n\t\t\tfloat distance;\n\t\t\tbyte offset;\n\t\t\tif (nextSegmentId == item.m_position.m_segment) {\n\t\t\t\tVector3 b = prevLane.CalculatePosition((float)(int)laneSwitchOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\t\tVector3 a = nextLane.CalculatePosition((float)(int)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\t\tdistance = Vector3.Distance(a, b);\n\t\t\t\toffset = connectOffset;\n\t\t\t} else {\n\t\t\t\tNetInfo.Direction direction = (NetInfo.Direction)((nextNodeId != nextSegment.m_startNode) ? 1 : 2);\n\t\t\t\tVector3 b = prevLane.CalculatePosition((float)(int)laneSwitchOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\t\tVector3 a = ((direction & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? nextLane.m_bezier.a : nextLane.m_bezier.d;\n\t\t\t\tdistance = Vector3.Distance(a, b);\n\t\t\t\toffset = (byte)(((direction & NetInfo.Direction.Forward) != NetInfo.Direction.None) ? 255 : 0);\n\t\t\t}\n\n\t\t\t// float prevMaxSpeed = 1f; // stock code commented\n\t\t\t// float prevLaneSpeed = 1f; // stock code commented\n\t\t\tNetInfo.LaneType prevLaneType = NetInfo.LaneType.None;\n\t\t\tif (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {\n\t\t\t\tNetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];\n\t\t\t\t// prevMaxSpeed = prevLaneInfo.m_speedLimit; // stock code commented\n\t\t\t\t// prevLaneSpeed = CalculateLaneSpeed(prevMaxSpeed, laneSwitchOffset, item.m_position.m_offset, ref prevSegment, prevLaneInfo); // stock code commented\n\t\t\t\tprevLaneType = prevLaneInfo.m_laneType;\n\t\t\t\tif ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {\n\t\t\t\t\tprevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfloat prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? prevSegment.m_averageLength : prevLane.m_length;\n\t\t\tfloat offsetLength = (float)Mathf.Abs(laneSwitchOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength;\n\t\t\tfloat methodDistance = item.m_methodDistance + offsetLength;\n\t\t\tfloat comparisonValue = item.m_comparisonValue + offsetLength / (prevLaneSpeed * m_maxLength);\n\t\t\tfloat duration = item.m_duration + offsetLength / prevMaxSpeed;\n\n\t\t\tif (!m_ignoreCost) {\n\t\t\t\tint ticketCost = prevLane.m_ticketCost;\n\t\t\t\tif (ticketCost != 0) {\n\t\t\t\t\tcomparisonValue += (float)(ticketCost * m_pathRandomizer.Int32(2000u)) * TICKET_COST_CONVERSION_FACTOR;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (nextLaneIndex < nextNumLanes) {\n\t\t\t\tNetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];\n\t\t\t\tBufferItem nextItem = default(BufferItem);\n\n\t\t\t\tnextItem.m_position.m_segment = nextSegmentId;\n\t\t\t\tnextItem.m_position.m_lane = (byte)nextLaneIndex;\n\t\t\t\tnextItem.m_position.m_offset = offset;\n\n\t\t\t\tif ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {\n\t\t\t\t\tnextItem.m_methodDistance = 0f;\n\t\t\t\t} else {\n\t\t\t\t\tif (item.m_methodDistance == 0f) {\n\t\t\t\t\t\tcomparisonValue += 100f / (0.25f * m_maxLength);\n\t\t\t\t\t}\n\t\t\t\t\tnextItem.m_methodDistance = methodDistance + distance;\n\t\t\t\t}\n\n\t\t\t\tif (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < m_conf.PathFinding.MaxWalkingDistance) && !m_stablePath) { // NON-STOCK CODE (custom walking distance)\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemPedBicycle: Aborting: Max. walking distance exceeded\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_methodDistance={nextItem.m_methodDistance}\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tfloat nextMaxSpeed;\n#if SPEEDLIMITS\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tnextMaxSpeed = m_speedLimitManager.GetLockFreeGameSpeedLimit(nextSegmentId, (byte)nextLaneIndex, nextLaneId, nextLaneInfo);\n\t\t\t\t// NON-STOCK CODE END\n#else\n\t\t\t\tnextMaxSpeed = nextLaneInfo.m_speedLimit;\n#endif\n\n\t\t\t\tnextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.25f * m_maxLength);\n\t\t\t\tnextItem.m_duration = duration + distance / ((prevMaxSpeed + nextMaxSpeed) * 0.5f);\n\n\t\t\t\tif ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\t\tnextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);\n\t\t\t\t} else {\n\t\t\t\t\tnextItem.m_direction = nextLaneInfo.m_finalDirection;\n\t\t\t\t}\n\n\t\t\t\tif (nextLaneId == m_startLaneA) {\n\t\t\t\t\tif (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetA) &&\n\t\t\t\t\t\t((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetA)) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemPedBicycle: Aborting: Invalid offset/direction on start lane A\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_direction={nextItem.m_direction}\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"m_startOffsetA={m_startOffsetA}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfloat nextSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);\n\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\n\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);\n\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;\n\t\t\t\t}\n\n\t\t\t\tif (nextLaneId == m_startLaneB) {\n\t\t\t\t\tif (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetB) &&\n\t\t\t\t\t\t((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetB)) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemPedBicycle: Aborting: Invalid offset/direction on start lane B\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_direction={nextItem.m_direction}\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"nextItem.m_position.m_offset={nextItem.m_position.m_offset}\\n\"\n\t\t\t\t\t\t\t\t+ \"\\t\" + $\"m_startOffsetB={m_startOffsetB}\"\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfloat nextSpeed = CalculateLaneSpeed(nextMaxSpeed, m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);\n\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetB) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\n\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);\n\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;\n\t\t\t\t}\n\n\t\t\t\tnextItem.m_laneID = nextLaneId;\n\t\t\t\tnextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);\n#if PARKINGAI\n\t\t\t\tnextItem.m_vehiclesUsed = (item.m_vehiclesUsed | nextLaneInfo.m_vehicleType);\n#endif\n#if ADVANCEDAI && ROUTING\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tnextItem.m_trafficRand = item.m_trafficRand;\n\t\t\t\t// NON-STOCK CODE END\n#endif\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, nextSegmentId, nextLaneIndex, nextLaneId, $\"ProcessItemPedBicycle: Adding next item\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"nextItem={nextItem}\"\n\t\t\t\t\t);\n\t\t\t\t}\n#endif\n\n\t\t\t\tAddBufferItem(\n#if DEBUG\n\t\t\t\t\tdebug,\n#endif\n\t\t\t\t\tnextItem, item.m_position\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n#if ROUTING\n\t\t// 5 (custom: process routed vehicle paths)\n\t\tprivate bool ProcessItemRouted(\n#if DEBUG\n\t\t\tbool debug, uint unitId,\n#endif\n\t\t\tBufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, float prevMaxSpeed, float prevLaneSpeed,\n#if ADVANCEDAI\n\t\t\tbool enableAdvancedAI, float laneChangingCost,\n#endif\n\t\t\tfloat segmentSelectionCost, float laneSelectionCost, ushort nextNodeId, ref NetNode nextNode, bool isMiddle, SegmentRoutingData prevSegmentRouting, LaneEndRoutingData prevLaneEndRouting, byte connectOffset, int prevInnerSimilarLaneIndex\n\t\t) {\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unitId, item, $\"ProcessItemRouted called.\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"prevMaxSpeed={prevMaxSpeed}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"prevLaneSpeed={prevLaneSpeed}\\n\"\n#if ADVANCEDAI\n\t\t\t\t\t+ \"\\t\" + $\"enableAdvancedAI={enableAdvancedAI}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"laneChangingCost={laneChangingCost}\\n\"\n#endif\n\t\t\t\t\t+ \"\\t\" + $\"segmentSelectionCost={segmentSelectionCost}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextNodeId={nextNodeId}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"isMiddle={isMiddle}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"prevSegmentRouting={prevSegmentRouting}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"prevLaneEndRouting={prevLaneEndRouting}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"connectOffset={connectOffset}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"prevInnerSimilarLaneIndex={prevInnerSimilarLaneIndex}\\n\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\n\t\t\t/*\n\t\t\t * =======================================================================================================\n\t\t\t * Fetch lane end transitions, check if there are any present\n\t\t\t * =======================================================================================================\n\t\t\t */\n\t\t\tLaneTransitionData[] laneTransitions = prevLaneEndRouting.transitions;\n\t\t\tif (laneTransitions == null) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, $\"ProcessItemRouted: Aborting: No lane transitions\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tushort prevSegmentId = item.m_position.m_segment;\n\t\t\tint prevLaneIndex = item.m_position.m_lane;\n\t\t\tNetInfo prevSegmentInfo = prevSegment.Info;\n\t\t\tif (prevLaneIndex >= prevSegmentInfo.m_lanes.Length) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, $\"ProcessItemRouted: Aborting: Invalid lane index\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tNetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];\n\n#if VEHICLERESTRICTIONS\n\t\t\t/*\n\t\t\t * =======================================================================================================\n\t\t\t * Check vehicle restrictions, especially bans\n\t\t\t * =======================================================================================================\n\t\t\t */\n\t\t\tbool canUseLane = CanUseLane(prevSegmentId, prevSegmentInfo, prevLaneIndex, prevLaneInfo);\n\t\t\tif (! canUseLane && Options.vehicleRestrictionsAggression == VehicleRestrictionsAggression.Strict) {\n\t\t\t\t// vehicle is strictly prohibited to use this lane\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, $\"ProcessItemRouted: Vehicle restrictions: Aborting: Strict vehicle restrictions active\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n#endif\n\n\t\t\tbool strictLaneRouting =\n\t\t\t\tm_isLaneArrowObeyingEntity &&\n\t\t\t\tnextNode.Info.m_class.m_service != ItemClass.Service.Beautification &&\n\t\t\t\t(nextNode.m_flags & NetNode.Flags.Untouchable) == NetNode.Flags.None\n\t\t\t;\n\t\t\tbool prevIsCarLane =\n\t\t\t\t(prevLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&\n\t\t\t\t(prevLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None\n\t\t\t;\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unitId, item, $\"ProcessItemRouted: Strict lane routing? {strictLaneRouting}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"m_isLaneArrowObeyingEntity={m_isLaneArrowObeyingEntity}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextNode.Info.m_class.m_service={nextNode.Info.m_class.m_service}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"nextNode.m_flags={nextNode.m_flags}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"prevIsCarLane={prevIsCarLane}\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\n\t\t\t/*\n\t\t\t * =======================================================================================================\n\t\t\t * Check if u-turns may be performed\n\t\t\t * =======================================================================================================\n\t\t\t */\n\t\t\tbool isUturnAllowedHere = false; // is u-turn allowed at this place?\n\t\t\tif ((this.m_vehicleTypes & (VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Monorail)) == VehicleInfo.VehicleType.None) { // is vehicle able to perform a u-turn?\n\t\t\t\tbool isStockUturnPoint = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;\n#if JUNCTIONRESTRICTIONS\n\t\t\t\tif (Options.junctionRestrictionsEnabled) {\n\t\t\t\t\tbool nextIsStartNode = nextNodeId == prevSegment.m_startNode;\n\t\t\t\t\tbool prevIsOutgoingOneWay = nextIsStartNode ? prevSegmentRouting.startNodeOutgoingOneWay : prevSegmentRouting.endNodeOutgoingOneWay;\n\n\t\t\t\t\t// determine if the vehicle may u-turn at the target node, according to customization\n\t\t\t\t\tisUturnAllowedHere =\n\t\t\t\t\t\tm_isRoadVehicle && // only road vehicles may perform u-turns\n\t\t\t\t\t\tprevIsCarLane && // u-turns for road vehicles only\n\t\t\t\t\t\t(!m_isHeavyVehicle || isStockUturnPoint) && // only small vehicles may perform u-turns OR everyone at stock u-turn points\n\t\t\t\t\t\t!prevIsOutgoingOneWay && // do not u-turn on one-ways\n\t\t\t\t\t\t(\n\t\t\t\t\t\t\tm_junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode)\n\t\t\t\t\t\t\t/*|| // only do u-turns if allowed\n\t\t\t\t\t\t\t(!m_queueItem.spawned && // or a yet unspawned vehicle ...\n\t\t\t\t\t\t\t(prevSegmentId == m_startSegmentA || prevSegmentId == m_startSegmentB)) // ... starts at the current segment*/\n\t\t\t\t\t\t)\n\t\t\t\t\t;\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemRouted: Junction restrictions: Is u-turn allowed here? {isUturnAllowedHere}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"m_isRoadVehicle={m_isRoadVehicle}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"prevIsCarLane={prevIsCarLane}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"m_isHeavyVehicle={m_isHeavyVehicle}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"isStockUturnPoint={isStockUturnPoint}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"prevIsOutgoingOneWay={prevIsOutgoingOneWay}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"m_junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode)={m_junctionManager.IsUturnAllowed(prevSegmentId, nextIsStartNode)}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"m_queueItem.vehicleId={m_queueItem.vehicleId}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"m_queueItem.spawned={m_queueItem.spawned}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"prevSegmentId={prevSegmentId}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"m_startSegmentA={m_startSegmentA}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"m_startSegmentB={m_startSegmentB}\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n#endif\n\t\t\t\t} else {\n#endif\n\t\t\t\t\tisUturnAllowedHere = isStockUturnPoint;\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, $\"ProcessItemRouted: Junction restrictions disabled: Is u-turn allowed here? {isUturnAllowedHere}\");\n\t\t\t\t\t}\n#endif\n\n#if JUNCTIONRESTRICTIONS\n\t\t\t\t}\n#endif\n\t\t\t}\n\n#if VEHICLERESTRICTIONS\n\t\t\t/*\n\t\t\t * =======================================================================================================\n\t\t\t * Apply vehicle restriction costs\n\t\t\t * =======================================================================================================\n\t\t\t */\n\t\t\tif (!canUseLane) {\n\t\t\t\tlaneSelectionCost *= VehicleRestrictionsManager.PATHFIND_PENALTIES[(int)Options.vehicleRestrictionsAggression];\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, $\"ProcessItemRouted: Vehicle restrictions: Applied lane costs\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"laneSelectionCost={laneSelectionCost}\"\n\t\t\t\t\t);\n\t\t\t\t}\n#endif\n\t\t\t}\n#endif\n\n\t\t\t/*\n\t\t\t * =======================================================================================================\n\t\t\t * Apply costs for large vehicles using inner lanes on highways\n\t\t\t * =======================================================================================================\n\t\t\t */\n\t\t\tif (Options.preferOuterLane &&\n\t\t\t\tm_isHeavyVehicle &&\n\t\t\t\tm_isRoadVehicle &&\n\t\t\t\tprevIsCarLane &&\n\t\t\t\tprevSegmentRouting.highway &&\n\t\t\t\tprevLaneInfo.m_similarLaneCount > 1 &&\n\t\t\t\tm_pathRandomizer.Int32(m_conf.PathFinding.HeavyVehicleInnerLanePenaltySegmentSel) == 0) {\n\n\t\t\t\tint prevOuterSimilarLaneIndex = m_routingManager.CalcOuterSimilarLaneIndex(prevLaneInfo);\n\t\t\t\tfloat prevRelOuterLane = ((float)prevOuterSimilarLaneIndex / (float)(prevLaneInfo.m_similarLaneCount - 1));\n\t\t\t\tlaneSelectionCost *= 1f + m_conf.PathFinding.HeavyVehicleMaxInnerLanePenalty * prevRelOuterLane;\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, $\"ProcessItemRouted: Heavy trucks prefer outer lanes on highways: Applied lane costs\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"Options.preferOuterLane={Options.preferOuterLane}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"m_isHeavyVehicle={m_isHeavyVehicle}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"m_isRoadVehicle={m_isRoadVehicle}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"prevIsCarLane={prevIsCarLane}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"prevSegmentRouting.highway={prevSegmentRouting.highway}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"prevLaneInfo.m_similarLaneCount={prevLaneInfo.m_similarLaneCount}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"prevOuterSimilarLaneIndex={prevOuterSimilarLaneIndex}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"prevRelOuterLane={prevRelOuterLane}\"\n\t\t\t\t\t);\n\t\t\t\t}\n#endif\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unitId, item, $\"ProcessItemRouted: Final cost factors:\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"segmentSelectionCost={segmentSelectionCost}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"laneSelectionCost={laneSelectionCost}\\n\"\n\t\t\t\t\t+ \"\\t\" + $\"laneChangingCost={laneChangingCost}\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\n\t\t\t/*\n\t\t\t * =======================================================================================================\n\t\t\t * Explore available lane end routings\n\t\t\t * =======================================================================================================\n\t\t\t */\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tbool blocked = false;\n\t\t\tbool uturnExplored = false;\n\t\t\tfor (int k = 0; k < laneTransitions.Length; ++k) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $\"ProcessItemRouted: Exploring lane transition #{k}: {laneTransitions[k]}\");\n\t\t\t\t}\n#endif\n\n\t\t\t\tushort nextSegmentId = laneTransitions[k].segmentId;\n\n\t\t\t\tif (nextSegmentId == 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (laneTransitions[k].type == LaneEndTransitionType.Invalid) {\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $\"ProcessItemRouted: Skipping transition: Transition is invalid\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (nextSegmentId == prevSegmentId) {\n\t\t\t\t\tif (!isUturnAllowedHere) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tDebug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $\"ProcessItemRouted: Skipping transition: U-turn is not allowed here\");\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t// prevent double/forbidden exploration of previous segment by vanilla code during this method execution\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $\"ProcessItemRouted: Processing transition: Exploring u-turn\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\t// we are going to explore a regular u-turn\n\t\t\t\t\tuturnExplored = true;\n\t\t\t\t}\n\n\t\t\t\t// allow vehicles to ignore strict lane routing when moving off\n\t\t\t\tbool relaxedLaneRouting =\n\t\t\t\t\tm_isRoadVehicle &&\n\t\t\t\t\t((!m_queueItem.spawned || (m_queueItem.vehicleType & (ExtVehicleType.PublicTransport | ExtVehicleType.Emergency)) != ExtVehicleType.None) &&\n\t\t\t\t\t (laneTransitions[k].laneId == m_startLaneA || laneTransitions[k].laneId == m_startLaneB));\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $\"ProcessItemRouted: Relaxed lane routing? {relaxedLaneRouting}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"relaxedLaneRouting={relaxedLaneRouting}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"m_isRoadVehicle={m_isRoadVehicle}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"m_queueItem.spawned={m_queueItem.spawned}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"m_queueItem.vehicleType={m_queueItem.vehicleType}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"m_queueItem.vehicleId={m_queueItem.vehicleId}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"m_startLaneA={m_startLaneA}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"m_startLaneB={m_startLaneB}\"\n\t\t\t\t\t);\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (\n\t\t\t\t\t!relaxedLaneRouting &&\n\t\t\t\t\t(strictLaneRouting && laneTransitions[k].type == LaneEndTransitionType.Relaxed)\n\t\t\t\t) {\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $\"ProcessItemRouted: Aborting: Cannot explore relaxed lane\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"relaxedLaneRouting={relaxedLaneRouting}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"strictLaneRouting={strictLaneRouting}\\n\"\n\t\t\t\t\t\t\t+ \"\\t\" + $\"laneTransitions[k].type={laneTransitions[k].type}\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unitId, item, laneTransitions[k].segmentId, laneTransitions[k].laneIndex, laneTransitions[k].laneId, $\"ProcessItemRouted: Exploring lane transition now\\n\"\n#if ADVANCEDAI\n\t\t\t\t\t\t+ \"\\t\" + $\"enableAdvancedAI={enableAdvancedAI}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"laneChangingCost={laneChangingCost}\\n\"\n#endif\n\t\t\t\t\t\t+ \"\\t\" + $\"segmentSelectionCost={segmentSelectionCost}\\n\"\n\t\t\t\t\t\t+ \"\\t\" + $\"laneSelectionCost={laneSelectionCost}\"\n\t\t\t\t\t);\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (\n\t\t\t\t\tProcessItemCosts(\n#if DEBUG\n\t\t\t\t\t\tdebug, unitId,\n#endif\n\t\t\t\t\t\titem, ref prevSegment, ref prevLane, prevMaxSpeed, prevLaneSpeed,\n#if ADVANCEDAI\n\t\t\t\t\t\tenableAdvancedAI, laneChangingCost,\n#endif\n\t\t\t\t\t\tnextNodeId, ref nextNode, isMiddle, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], segmentSelectionCost, laneSelectionCost, laneTransitions[k], ref prevInnerSimilarLaneIndex, connectOffset, true, false\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tblocked = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn blocked && !uturnExplored;\n\t\t}\n#endif\n\n\t\tprivate void AddBufferItem(\n#if DEBUG\n\t\t\tbool debug,\n#endif\n\t\t\tBufferItem item, PathUnit.Position target\n\t\t) {\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tm_debugPositions[target.m_segment].Add(item.m_position.m_segment);\n\t\t\t}\n#endif\n\n\t\t\tuint laneLocation = m_laneLocation[item.m_laneID];\n\t\t\tuint locPathFindIndex = laneLocation >> 16; // upper 16 bit, expected (?) path find index\n\t\t\tint bufferIndex = (int)(laneLocation & 65535u); // lower 16 bit\n\t\t\tint comparisonBufferPos;\n\n\t\t\tif (locPathFindIndex == m_pathFindIndex) {\n\t\t\t\tif (item.m_comparisonValue >= m_buffer[bufferIndex].m_comparisonValue) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tint bufferPosIndex = bufferIndex >> 6; // arithmetic shift (sign stays), upper 10 bit\n\t\t\t\tint bufferPos = bufferIndex & -64; // upper 10 bit (no shift)\n\t\t\t\tif (bufferPosIndex < m_bufferMinPos || (bufferPosIndex == m_bufferMinPos && bufferPos < m_bufferMin[bufferPosIndex])) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcomparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);\n\t\t\t\tif (comparisonBufferPos == bufferPosIndex) {\n\t\t\t\t\tm_buffer[bufferIndex] = item;\n\t\t\t\t\tm_laneTarget[item.m_laneID] = target;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tint newBufferIndex = bufferPosIndex << 6 | m_bufferMax[bufferPosIndex]--;\n\t\t\t\tBufferItem bufferItem = m_buffer[newBufferIndex];\n\t\t\t\tm_laneLocation[bufferItem.m_laneID] = laneLocation;\n\t\t\t\tm_buffer[bufferIndex] = bufferItem;\n\t\t\t} else {\n\t\t\t\tcomparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);\n\t\t\t}\n\n\t\t\tif (comparisonBufferPos >= 1024 || comparisonBufferPos < 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\twhile (m_bufferMax[comparisonBufferPos] == 63) {\n\t\t\t\t++comparisonBufferPos;\n\t\t\t\tif (comparisonBufferPos == 1024) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (comparisonBufferPos > m_bufferMaxPos) {\n\t\t\t\tm_bufferMaxPos = comparisonBufferPos;\n\t\t\t}\n\n\t\t\tbufferIndex = (comparisonBufferPos << 6 | ++m_bufferMax[comparisonBufferPos]);\n\t\t\tm_buffer[bufferIndex] = item;\n\t\t\tm_laneLocation[item.m_laneID] = (m_pathFindIndex << 16 | (uint)bufferIndex);\n\t\t\tm_laneTarget[item.m_laneID] = target;\n\t\t}\n\n\t\tprivate float CalculateLaneSpeed(float maxSpeed, byte startOffset, byte endOffset, ref NetSegment segment, NetInfo.Lane laneInfo) {\n\t\t\tNetInfo.Direction direction = ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? laneInfo.m_finalDirection : NetInfo.InvertDirection(laneInfo.m_finalDirection);\n\t\t\tif ((direction & NetInfo.Direction.Avoid) != NetInfo.Direction.None) {\n\t\t\t\tif (endOffset > startOffset && direction == NetInfo.Direction.AvoidForward) {\n\t\t\t\t\treturn maxSpeed * 0.1f;\n\t\t\t\t}\n\t\t\t\tif (endOffset < startOffset && direction == NetInfo.Direction.AvoidBackward) {\n\t\t\t\t\treturn maxSpeed * 0.1f;\n\t\t\t\t}\n\t\t\t\treturn maxSpeed * 0.2f;\n\t\t\t}\n\t\t\treturn maxSpeed;\n\t\t}\n\n\t\tprivate void GetLaneDirection(PathUnit.Position pathPos, out NetInfo.Direction direction, out NetInfo.LaneType laneType\n#if PARKINGAI\n\t\t\t, out VehicleInfo.VehicleType vehicleType\n#endif\n\t\t\t) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tNetInfo info = netManager.m_segments.m_buffer[pathPos.m_segment].Info;\n\t\t\tif (info.m_lanes.Length > pathPos.m_lane) {\n\t\t\t\tdirection = info.m_lanes[pathPos.m_lane].m_finalDirection;\n\t\t\t\tlaneType = info.m_lanes[pathPos.m_lane].m_laneType;\n#if PARKINGAI\n\t\t\t\tvehicleType = info.m_lanes[pathPos.m_lane].m_vehicleType;\n#endif\n\t\t\t\tif ((netManager.m_segments.m_buffer[pathPos.m_segment].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\t\tdirection = NetInfo.InvertDirection(direction);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdirection = NetInfo.Direction.None;\n\t\t\t\tlaneType = NetInfo.LaneType.None;\n#if PARKINGAI\n\t\t\t\tvehicleType = VehicleInfo.VehicleType.None;\n#endif\n\t\t\t}\n\t\t}\n\n#if VEHICLERESTRICTIONS\n\t\tprivate bool CanUseLane(ushort segmentId, NetInfo segmentInfo, int laneIndex, NetInfo.Lane laneInfo) {\n\t\t\tif (!Options.vehicleRestrictionsEnabled ||\n\t\t\t\tm_queueItem.vehicleType == ExtVehicleType.None ||\n\t\t\t\tm_queueItem.vehicleType == ExtVehicleType.Tram ||\n\t\t\t\t(laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) == VehicleInfo.VehicleType.None) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tExtVehicleType allowedTypes = m_vehicleRestrictionsManager.GetAllowedVehicleTypes(segmentId, segmentInfo, (uint)laneIndex, laneInfo, VehicleRestrictionsMode.Configured);\n\n\t\t\treturn ((allowedTypes & m_queueItem.vehicleType) != ExtVehicleType.None);\n\t\t}\n#endif\n\n#if ADVANCEDAI && ROUTING\n\t\tprivate void CalculateAdvancedAiCostFactors(\n#if DEBUG\n\t\t\tbool debug, uint unit,\n#endif\n\t\t\tref BufferItem item, ref NetSegment prevSegment, ref NetLane prevLane, ushort nextNodeId, ref NetNode nextNode, ref float segmentSelectionCost, ref float laneSelectionCost, ref float laneChangingCost\n\t\t) {\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unit, item, $\"CalculateAdvancedAiCostFactors called.\\n\" +\n\t\t\t\t\t\"\\t\" + $\"nextNodeId={nextNodeId}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"segmentSelectionCost={segmentSelectionCost}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"laneSelectionCost={laneSelectionCost}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"laneChangingCost={laneChangingCost}\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\n\t\t\tNetInfo prevSegmentInfo = prevSegment.Info;\n\t\t\tbool nextIsJunction = (nextNode.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) == NetNode.Flags.Junction;\n\n\t\t\tif (nextIsJunction) {\n\t\t\t\t/*\n\t\t\t\t * =======================================================================================================\n\t\t\t\t * Calculate costs for randomized lane selection behind junctions and highway transitions\n\t\t\t\t * =======================================================================================================\n\t\t\t\t */\n\t\t\t\t// TODO check if highway transitions are actually covered by this code\n\t\t\t\tif (\n\t\t\t\t\t!m_isHeavyVehicle &&\n\t\t\t\t\tm_conf.AdvancedVehicleAI.LaneRandomizationJunctionSel > 0 &&\n\t\t\t\t\tm_pathRandomizer.Int32(m_conf.AdvancedVehicleAI.LaneRandomizationJunctionSel) == 0 &&\n\t\t\t\t\tm_pathRandomizer.Int32((uint)prevSegmentInfo.m_lanes.Length) == 0\n\t\t\t\t) {\n\t\t\t\t\t// randomized lane selection at junctions\n\t\t\t\t\tlaneSelectionCost *= 1f + m_conf.AdvancedVehicleAI.LaneRandomizationCostFactor;\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unit, item, $\"CalculateAdvancedAiCostFactors: Calculated randomized lane selection costs\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"laneSelectionCost={laneSelectionCost}\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * =======================================================================================================\n\t\t\t\t * Calculate junction costs\n\t\t\t\t * =======================================================================================================\n\t\t\t\t */\n\t\t\t\t// TODO if (prevSegmentRouting.highway) ?\n\t\t\t\tsegmentSelectionCost *= 1f + m_conf.AdvancedVehicleAI.JunctionBaseCost;\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tDebug(unit, item, $\"CalculateAdvancedAiCostFactors: Calculated junction costs\\n\" +\n\t\t\t\t\t\t\"\\t\" + $\"segmentSelectionCost={segmentSelectionCost}\"\n\t\t\t\t\t);\n\t\t\t\t}\n#endif\n\t\t\t}\n\n\t\t\tbool nextIsStartNode = prevSegment.m_startNode == nextNodeId;\n\t\t\tbool nextIsEndNode = nextNodeId == prevSegment.m_endNode;\n\t\t\tif (nextIsStartNode || nextIsEndNode) { // next node is a regular node\n\t\t\t\t/*\n\t\t\t\t * =======================================================================================================\n\t\t\t\t * Calculate traffic measurement costs for segment selection\n\t\t\t\t * =======================================================================================================\n\t\t\t\t */\n\t\t\t\tNetInfo.Direction prevFinalDir = nextIsStartNode ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;\n\t\t\t\tprevFinalDir = ((prevSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? prevFinalDir : NetInfo.InvertDirection(prevFinalDir);\n\t\t\t\tTrafficMeasurementManager.SegmentDirTrafficData prevDirTrafficData =\n\t\t\t\t\tm_trafficMeasurementManager.segmentDirTrafficData[m_trafficMeasurementManager.GetDirIndex(item.m_position.m_segment, prevFinalDir)];\n\n\t\t\t\tfloat segmentTraffic = Mathf.Clamp(1f - (float)prevDirTrafficData.meanSpeed / (float)TrafficMeasurementManager.REF_REL_SPEED + item.m_trafficRand, 0, 1f);\n\n\t\t\t\tsegmentSelectionCost *= 1f +\n\t\t\t\t\tm_conf.AdvancedVehicleAI.TrafficCostFactor *\n\t\t\t\t\tsegmentTraffic;\n\n\t\t\t\tif (\n\t\t\t\t\tm_conf.AdvancedVehicleAI.LaneDensityRandInterval > 0 &&\n\t\t\t\t\tnextIsJunction &&\n\t\t\t\t\t(nextNode.m_flags & (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)) != (NetNode.Flags.OneWayIn | NetNode.Flags.OneWayOut)\n\t\t\t\t) {\n\t\t\t\t\titem.m_trafficRand = 0.01f * ((float)m_pathRandomizer.Int32((uint)m_conf.AdvancedVehicleAI.LaneDensityRandInterval + 1u) - m_conf.AdvancedVehicleAI.LaneDensityRandInterval / 2f);\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\tm_conf.AdvancedVehicleAI.LaneChangingJunctionBaseCost > 0 &&\n\t\t\t\t\t(Singleton<NetManager>.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\n\t\t\t\t) {\n\t\t\t\t\t/*\n\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t * Calculate lane changing base cost factor when in front of junctions\n\t\t\t\t\t * =======================================================================================================\n\t\t\t\t\t */\n\t\t\t\t\tlaneChangingCost *= m_conf.AdvancedVehicleAI.LaneChangingJunctionBaseCost;\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unit, item, $\"CalculateAdvancedAiCostFactors: Calculated in-front-of-junction lane changing costs\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"laneChangingCost={laneChangingCost}\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * =======================================================================================================\n\t\t\t\t * Calculate general lane changing base cost factor\n\t\t\t\t * =======================================================================================================\n\t\t\t\t */\n\t\t\t\tif (\n\t\t\t\t\tm_conf.AdvancedVehicleAI.LaneChangingBaseMinCost > 0 &&\n\t\t\t\t\tm_conf.AdvancedVehicleAI.LaneChangingBaseMaxCost > m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost\n\t\t\t\t) {\n\t\t\t\t\tfloat rand = (float)m_pathRandomizer.Int32(101u) / 100f;\n\t\t\t\t\tlaneChangingCost *= m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost + rand * (m_conf.AdvancedVehicleAI.LaneChangingBaseMaxCost - m_conf.AdvancedVehicleAI.LaneChangingBaseMinCost);\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tDebug(unit, item, $\"CalculateAdvancedAiCostFactors: Calculated base lane changing costs\\n\" +\n\t\t\t\t\t\t\t\"\\t\" + $\"laneChangingCost={laneChangingCost}\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tDebug(unit, item, $\"CalculateAdvancedAiCostFactors: Calculated cost factors\\n\" +\n\t\t\t\t\t\"\\t\" + $\"segmentSelectionCost={segmentSelectionCost}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"laneSelectionCost={laneSelectionCost}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"laneChangingCost={laneChangingCost}\"\n\t\t\t\t);\n\t\t\t}\n#endif\n\t\t}\n#endif\n\n\t\tprivate void PathFindThread() {\n\t\t\twhile (true) {\n\t\t\t\ttry {\n\t\t\t\t\tMonitor.Enter(m_queueLock);\n\n\t\t\t\t\twhile (m_queueFirst == 0 && !m_terminated) {\n\t\t\t\t\t\tMonitor.Wait(m_queueLock);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (m_terminated) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tm_calculating = m_queueFirst;\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\tm_queueFirst = CustomPathManager._instance.queueItems[m_calculating].nextPathUnitId;\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t// QueueFirst = PathUnits.m_buffer[Calculating].m_nextPathUnit; // stock code commented\n\n\t\t\t\t\tif (m_queueFirst == 0) {\n\t\t\t\t\t\tm_queueLast = 0u;\n\t\t\t\t\t\tm_queuedPathFindCount = 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tm_queuedPathFindCount--;\n\t\t\t\t\t}\n\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\tCustomPathManager._instance.queueItems[m_calculating].nextPathUnitId = 0u;\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t// PathUnits.m_buffer[Calculating].m_nextPathUnit = 0u; // stock code commented\n\n\t\t\t\t\tm_pathUnits.m_buffer[m_calculating].m_pathFindFlags = (byte)((m_pathUnits.m_buffer[m_calculating].m_pathFindFlags & ~PathUnit.FLAG_CREATED) | PathUnit.FLAG_CALCULATING);\n\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\tm_queueItem = CustomPathManager._instance.queueItems[m_calculating];\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t} finally {\n\t\t\t\t\tMonitor.Exit(m_queueLock);\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tm_pathfindProfiler.BeginStep();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tPathFindImplementation(m_calculating, ref m_pathUnits.m_buffer[m_calculating]);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tm_pathfindProfiler.EndStep();\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tUIView.ForwardException(ex);\n\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Path find error: \" + ex.Message + \"\\n\" + ex.StackTrace);\n\t\t\t\t\tm_pathUnits.m_buffer[m_calculating].m_pathFindFlags |= PathUnit.FLAG_FAILED;\n\t\t\t\t\t// NON-STOCK CODE START\n#if DEBUG\n\t\t\t\t\t++m_failedPathFinds;\n#endif\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tMonitor.Enter(m_queueLock);\n\n\t\t\t\t\tm_pathUnits.m_buffer[m_calculating].m_pathFindFlags = (byte)(m_pathUnits.m_buffer[m_calculating].m_pathFindFlags & ~PathUnit.FLAG_CALCULATING);\n\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\ttry {\n\t\t\t\t\t\tMonitor.Enter(m_bufferLock);\n\t\t\t\t\t\tCustomPathManager._instance.queueItems[m_calculating].queued = false;\n\t\t\t\t\t\tCustomPathManager._instance.ReleasePath(m_calculating);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tMonitor.Exit(m_bufferLock);\n\t\t\t\t\t}\n\t\t\t\t\t// NON-STOCK CODE END\n\t\t\t\t\t// Singleton<PathManager>.instance.ReleasePath(Calculating); // stock code commented\n\n\t\t\t\t\tm_calculating = 0u;\n\t\t\t\t\tMonitor.Pulse(m_queueLock);\n\t\t\t\t} finally {\n\t\t\t\t\tMonitor.Exit(m_queueLock);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/PathFinding/CustomPathManager.cs",
    "content": "#define QUEUEDSTATSx\n#define DEBUGPF3x\n\nusing System;\nusing System.Reflection;\nusing System.Threading;\nusing ColossalFramework;\nusing ColossalFramework.Math;\nusing JetBrains.Annotations;\nusing UnityEngine;\nusing TrafficManager.Geometry;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Util;\nusing CSUtil.Commons;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\nusing TrafficManager.Traffic.Data;\n\n// ReSharper disable InconsistentNaming\n\nnamespace TrafficManager.Custom.PathFinding {\n\tpublic class CustomPathManager : PathManager {\n\t\tpublic struct PathCreationArgs {\n\t\t\t/// <summary>\n\t\t\t/// Extended path type\n\t\t\t/// </summary>\n\t\t\tpublic ExtCitizenInstance.ExtPathType extPathType;\n\n\t\t\t/// <summary>\n\t\t\t/// Extended vehicle type\n\t\t\t/// </summary>\n\t\t\tpublic ExtVehicleType extVehicleType;\n\n\t\t\t/// <summary>\n\t\t\t/// (optional) vehicle id\n\t\t\t/// </summary>\n\t\t\tpublic ushort vehicleId;\n\n\t\t\t/// <summary>\n\t\t\t/// is entity alredy spawned?\n\t\t\t/// </summary>\n\t\t\tpublic bool spawned;\n\n\t\t\t/// <summary>\n\t\t\t/// Current build index\n\t\t\t/// </summary>\n\t\t\tpublic uint buildIndex;\n\n\t\t\t/// <summary>\n\t\t\t/// Start position (first alternative)\n\t\t\t/// </summary>\n\t\t\tpublic PathUnit.Position startPosA;\n\n\t\t\t/// <summary>\n\t\t\t/// Start position (second alternative, opposite road side)\n\t\t\t/// </summary>\n\t\t\tpublic PathUnit.Position startPosB;\n\n\t\t\t/// <summary>\n\t\t\t/// End position (first alternative)\n\t\t\t/// </summary>\n\t\t\tpublic PathUnit.Position endPosA;\n\n\t\t\t/// <summary>\n\t\t\t/// End position (second alternative, opposite road side)\n\t\t\t/// </summary>\n\t\t\tpublic PathUnit.Position endPosB;\n\n\t\t\t/// <summary>\n\t\t\t/// (optional) position of the parked vehicle\n\t\t\t/// </summary>\n\t\t\tpublic PathUnit.Position vehiclePosition;\n\n\t\t\t/// <summary>\n\t\t\t/// Allowed set of lane types\n\t\t\t/// </summary>\n\t\t\tpublic NetInfo.LaneType laneTypes;\n\n\t\t\t/// <summary>\n\t\t\t/// Allowed set of vehicle types\n\t\t\t/// </summary>\n\t\t\tpublic VehicleInfo.VehicleType vehicleTypes;\n\n\t\t\t/// <summary>\n\t\t\t/// Maximum allowed path length\n\t\t\t/// </summary>\n\t\t\tpublic float maxLength;\n\n\t\t\t/// <summary>\n\t\t\t/// Is the path calculated for a heavy vehicle?\n\t\t\t/// </summary>\n\t\t\tpublic bool isHeavyVehicle;\n\n\t\t\t/// <summary>\n\t\t\t/// Is the path calculated for a vehicle with a combustion engine?\n\t\t\t/// </summary>\n\t\t\tpublic bool hasCombustionEngine;\n\n\t\t\t/// <summary>\n\t\t\t/// Should blocked segments be ignored?\n\t\t\t/// </summary>\n\t\t\tpublic bool ignoreBlocked;\n\n\t\t\t/// <summary>\n\t\t\t/// Should flooded segments be ignored?\n\t\t\t/// </summary>\n\t\t\tpublic bool ignoreFlooded;\n\n\t\t\t/// <summary>\n\t\t\t/// Should path costs be ignored?\n\t\t\t/// </summary>\n\t\t\tpublic bool ignoreCosts;\n\n\t\t\t/// <summary>\n\t\t\t/// Should random parking apply?\n\t\t\t/// </summary>\n\t\t\tpublic bool randomParking;\n\n\t\t\t/// <summary>\n\t\t\t/// Should the path be stable (and not randomized)?\n\t\t\t/// </summary>\n\t\t\tpublic bool stablePath;\n\n\t\t\t/// <summary>\n\t\t\t/// Is this a high priority path?\n\t\t\t/// </summary>\n\t\t\tpublic bool skipQueue;\n\t\t}\n\n\t\tpublic struct PathUnitQueueItem {\n\t\t\tpublic uint nextPathUnitId; // access requires acquisition of CustomPathFind.QueueLock\n\t\t\tpublic ExtVehicleType vehicleType; // access requires acquisition of m_bufferLock\n\t\t\tpublic ExtPathType pathType; // access requires acquisition of m_bufferLock\n\t\t\tpublic ushort vehicleId; // access requires acquisition of m_bufferLock\n\t\t\tpublic bool queued; // access requires acquisition of m_bufferLock\n\t\t\tpublic bool spawned; // access requires acquisition of m_bufferLock\n\n\t\t\t//public void Reset() {\n\t\t\t//\tvehicleType = ExtVehicleType.None;\n\t\t\t//\tpathType = ExtPathType.None;\n\t\t\t//\tvehicleId = 0;\n\t\t\t//}\n\n\t\t\tpublic override string ToString() {\n\t\t\t\treturn $\"[PathUnitQueueItem\\n\" +\n\t\t\t\t\"\\t\" + $\"nextPathUnitId={nextPathUnitId}\\n\" +\n\t\t\t\t\"\\t\" + $\"vehicleType={vehicleType}\\n\" +\n\t\t\t\t\"\\t\" + $\"pathType={pathType}\\n\" +\n\t\t\t\t\"\\t\" + $\"vehicleId={vehicleId}\\n\" +\n\t\t\t\t\"\\t\" + $\"queued={queued}\\n\" +\n\t\t\t\t\"\\t\" + $\"spawned={spawned}\\n\" +\n\t\t\t\t\"PathUnitQueueItem]\";\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Holds a linked list of path units waiting to be calculated\n\t\t/// </summary>\n\t\tinternal PathUnitQueueItem[] queueItems;\n\n#if PF2\n\t\tprivate CustomPathFind2[] _replacementPathFinds;\n#else\n\t\tprivate CustomPathFind[] _replacementPathFinds;\n#endif\n\n\t\tpublic static CustomPathManager _instance;\n\n#if QUEUEDSTATS\n\t\tpublic static uint TotalQueuedPathFinds {\n\t\t\tget; private set;\n\t\t} = 0;\n#endif\n\n\t\tpublic static bool InitDone {\n\t\t\tget; private set;\n\t\t} = false;\n\n\t\t//On waking up, replace the stock pathfinders with the custom one\n\t\t[UsedImplicitly]\n\t\tpublic new virtual void Awake() {\n\t\t\t_instance = this;\n\t\t}\n\n\t\tpublic void UpdateWithPathManagerValues(PathManager stockPathManager) {\n\t\t\t// Needed fields come from joaofarias' csl-traffic\n\t\t\t// https://github.com/joaofarias/csl-traffic\n\n\t\t\tm_simulationProfiler = stockPathManager.m_simulationProfiler;\n\t\t\tm_drawCallData = stockPathManager.m_drawCallData;\n\t\t\tm_properties = stockPathManager.m_properties;\n\t\t\tm_pathUnitCount = stockPathManager.m_pathUnitCount;\n\t\t\tm_renderPathGizmo = stockPathManager.m_renderPathGizmo;\n\t\t\tm_pathUnits = stockPathManager.m_pathUnits;\n\t\t\tm_bufferLock = stockPathManager.m_bufferLock;\n\n\t\t\tLog._Debug(\"Waking up CustomPathManager.\");\n\n\t\t\tqueueItems = new PathUnitQueueItem[PathManager.MAX_PATHUNIT_COUNT];\n\n\t\t\tvar stockPathFinds = GetComponents<PathFind>();\n\t\t\tvar numOfStockPathFinds = stockPathFinds.Length;\n\t\t\tint numCustomPathFinds = numOfStockPathFinds;\n\n\t\t\tLog._Debug(\"Creating \" + numCustomPathFinds + \" custom PathFind objects.\");\n#if PF2\n\t\t\t_replacementPathFinds = new CustomPathFind2[numCustomPathFinds];\n#else\n\t\t\t_replacementPathFinds = new CustomPathFind[numCustomPathFinds];\n#endif\n\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(this.m_bufferLock);\n\n\t\t\t\tfor (var i = 0; i < numCustomPathFinds; i++) {\n#if PF2\n\t\t\t\t\t_replacementPathFinds[i] = gameObject.AddComponent<CustomPathFind2>();\n#else\n\t\t\t\t\t_replacementPathFinds[i] = gameObject.AddComponent<CustomPathFind>();\n\t\t\t\t\t_replacementPathFinds[i].pfId = i;\n\t\t\t\t\tif (i == 0) {\n\t\t\t\t\t\t_replacementPathFinds[i].IsMasterPathFind = true;\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\n\t\t\t\tLog._Debug(\"Setting _replacementPathFinds\");\n\t\t\t\tvar fieldInfo = typeof(PathManager).GetField(\"m_pathfinds\", BindingFlags.NonPublic | BindingFlags.Instance);\n\n\t\t\t\tLog._Debug(\"Setting m_pathfinds to custom collection\");\n\t\t\t\tfieldInfo?.SetValue(this, _replacementPathFinds);\n\n\t\t\t\tfor (var i = 0; i < numOfStockPathFinds; i++) {\n#if DEBUG\n\t\t\t\t\tLog._Debug($\"PF {i}: {stockPathFinds[i].m_queuedPathFindCount} queued path-finds\");\n#endif\n\t\t\t\t\t//stockPathFinds[i].WaitForAllPaths(); // would cause deadlock since we have a lock on m_bufferLock\n\t\t\t\t\tDestroy(stockPathFinds[i]);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(this.m_bufferLock);\n\t\t\t}\n\n\t\t\tInitDone = true;\n\t\t}\n\n\t\tpublic new void ReleasePath(uint unit) {\n#if DEBUGPF3\n\t\t\tLog.Warning($\"CustomPathManager.ReleasePath({unit}) called.\");\n#endif\n\n\t\t\tif (this.m_pathUnits.m_buffer[unit].m_simulationFlags == 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(m_bufferLock);\n\n\t\t\t\tint numIters = 0;\n\t\t\t\twhile (unit != 0u) {\n\t\t\t\t\tif (this.m_pathUnits.m_buffer[unit].m_referenceCount > 1) {\n\t\t\t\t\t\t--this.m_pathUnits.m_buffer[unit].m_referenceCount;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t/*if (this.m_pathUnits.m_buffer[unit].m_pathFindFlags == PathUnit.FLAG_CREATED) {\n\t\t\t\t\t\tLog.Error($\"Will release path unit {unit} which is CREATED!\");\n\t\t\t\t\t}*/\n\n\t\t\t\t\tuint nextPathUnit = this.m_pathUnits.m_buffer[unit].m_nextPathUnit;\n\t\t\t\t\tthis.m_pathUnits.m_buffer[unit].m_simulationFlags = 0;\n\t\t\t\t\tthis.m_pathUnits.m_buffer[unit].m_pathFindFlags = 0;\n\t\t\t\t\tthis.m_pathUnits.m_buffer[unit].m_nextPathUnit = 0u;\n\t\t\t\t\tthis.m_pathUnits.m_buffer[unit].m_referenceCount = 0;\n\t\t\t\t\tthis.m_pathUnits.ReleaseItem(unit);\n\t\t\t\t\t//queueItems[unit].Reset(); // NON-STOCK CODE\n\t\t\t\t\tunit = nextPathUnit;\n\t\t\t\t\tif (++numIters >= 262144) {\n\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.m_pathUnitCount = (int)(this.m_pathUnits.ItemCount() - 1u);\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(this.m_bufferLock);\n\t\t\t}\n\t\t}\n\n\t\tpublic bool CreatePath(out uint unit, ref Randomizer randomizer, PathCreationArgs args) {\n\t\t\tuint pathUnitId;\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(this.m_bufferLock);\n\n\t\t\t\tint numIters = 0;\n\t\t\t\twhile (true) { // NON-STOCK CODE\n\t\t\t\t\t++numIters;\n\n\t\t\t\t\tif (!this.m_pathUnits.CreateItem(out pathUnitId, ref randomizer)) {\n\t\t\t\t\t\tunit = 0u;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.m_pathUnits.m_buffer[pathUnitId].m_simulationFlags = 1;\n\t\t\t\t\tthis.m_pathUnits.m_buffer[pathUnitId].m_referenceCount = 1;\n\t\t\t\t\tthis.m_pathUnits.m_buffer[pathUnitId].m_nextPathUnit = 0u;\n\n\t\t\t\t\t// NON-STOCK CODE START\n\t\t\t\t\tif (queueItems[pathUnitId].queued) {\n\t\t\t\t\t\tReleasePath(pathUnitId);\n\n\t\t\t\t\t\tif (numIters > 10) {\n\t\t\t\t\t\t\tunit = 0u;\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tqueueItems[pathUnitId].vehicleType = args.extVehicleType;\n\t\t\t\tqueueItems[pathUnitId].vehicleId = args.vehicleId;\n\t\t\t\tqueueItems[pathUnitId].pathType = args.extPathType;\n\t\t\t\tqueueItems[pathUnitId].spawned = args.spawned;\n\t\t\t\tqueueItems[pathUnitId].queued = true;\n\t\t\t\t// NON-STOCK CODE END\n\n\t\t\t\tthis.m_pathUnitCount = (int)(this.m_pathUnits.ItemCount() - 1u);\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(this.m_bufferLock);\n\t\t\t}\n\t\t\tunit = pathUnitId;\n\n\t\t\tif (args.isHeavyVehicle) {\n\t\t\t\tthis.m_pathUnits.m_buffer[unit].m_simulationFlags |= 16;\n\t\t\t}\n\t\t\tif (args.ignoreBlocked || args.ignoreFlooded) {\n\t\t\t\tthis.m_pathUnits.m_buffer[unit].m_simulationFlags |= 32;\n\t\t\t}\n\t\t\tif (args.stablePath) {\n\t\t\t\tthis.m_pathUnits.m_buffer[unit].m_simulationFlags |= 64;\n\t\t\t}\n\t\t\tif (args.randomParking) {\n\t\t\t\tthis.m_pathUnits.m_buffer[unit].m_simulationFlags |= 2;\n\t\t\t}\n\t\t\tif (args.hasCombustionEngine) {\n\t\t\t\tthis.m_pathUnits.m_buffer[unit].m_simulationFlags |= 4;\n\t\t\t}\n\t\t\tif (args.ignoreCosts) {\n\t\t\t\tthis.m_pathUnits.m_buffer[unit].m_simulationFlags |= 8;\n\t\t\t}\n\t\t\tthis.m_pathUnits.m_buffer[unit].m_pathFindFlags = 0;\n\t\t\tthis.m_pathUnits.m_buffer[unit].m_buildIndex = args.buildIndex;\n\t\t\tthis.m_pathUnits.m_buffer[unit].m_position00 = args.startPosA;\n\t\t\tthis.m_pathUnits.m_buffer[unit].m_position01 = args.endPosA;\n\t\t\tthis.m_pathUnits.m_buffer[unit].m_position02 = args.startPosB;\n\t\t\tthis.m_pathUnits.m_buffer[unit].m_position03 = args.endPosB;\n\t\t\tthis.m_pathUnits.m_buffer[unit].m_position11 = args.vehiclePosition;\n\t\t\tthis.m_pathUnits.m_buffer[unit].m_laneTypes = (byte)args.laneTypes;\n\t\t\tthis.m_pathUnits.m_buffer[unit].m_vehicleTypes = (ushort)args.vehicleTypes;\n\t\t\tthis.m_pathUnits.m_buffer[unit].m_length = args.maxLength;\n\t\t\tthis.m_pathUnits.m_buffer[unit].m_positionCount = 20;\n\t\t\tint minQueued = 10000000;\n#if PF2\n\t\t\tCustomPathFind2 pathFind = null;\n#else\n\t\t\tCustomPathFind pathFind = null;\n#endif\n\n#if QUEUEDSTATS\n\t\t\tTotalQueuedPathFinds = 0;\n#endif\n\t\t\tfor (int i = 0; i < _replacementPathFinds.Length; ++i) {\n#if PF2\n\t\t\t\tCustomPathFind2 pathFindCandidate = _replacementPathFinds[i];\n#else\n\t\t\t\tCustomPathFind pathFindCandidate = _replacementPathFinds[i];\n#endif\n\n#if QUEUEDSTATS\n\t\t\t\tTotalQueuedPathFinds += (uint)pathFindCandidate.m_queuedPathFindCount;\n#endif\n\t\t\t\tif (pathFindCandidate.IsAvailable\n\t\t\t\t\t&& pathFindCandidate.m_queuedPathFindCount < minQueued) {\n\t\t\t\t\tminQueued = pathFindCandidate.m_queuedPathFindCount;\n\t\t\t\t\tpathFind = pathFindCandidate;\n\t\t\t\t}\n\t\t\t}\n\n#if PF2\n\t\t\tif (pathFind != null && pathFind.CalculatePath(unit, args.skipQueue)) {\n\t\t\t\treturn true;\n\t\t\t}\n#else\n\t\t\tif (pathFind != null && pathFind.ExtCalculatePath(unit, args.skipQueue)) {\n\t\t\t\treturn true;\n\t\t\t}\n#endif\n\n\t\t\t// NON-STOCK CODE START\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(this.m_bufferLock);\n\t\t\t\t\n\t\t\t\tqueueItems[pathUnitId].queued = false;\n\t\t\t\t// NON-STOCK CODE END\n\t\t\t\tthis.ReleasePath(unit);\n\n\t\t\t\t// NON-STOCK CODE START\n\t\t\t\tthis.m_pathUnitCount = (int)(this.m_pathUnits.ItemCount() - 1u);\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(this.m_bufferLock);\n\t\t\t}\n\t\t\t// NON-STOCK CODE END\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic static bool FindPathPositionWithSpiralLoop(Vector3 position, ItemClass.Service service, NetInfo.LaneType laneType, VehicleInfo.VehicleType vehicleType, NetInfo.LaneType otherLaneType, VehicleInfo.VehicleType otherVehicleType, bool allowUnderground, bool requireConnect, float maxDistance, out PathUnit.Position pathPos) {\n\t\t\treturn FindPathPositionWithSpiralLoop(position, null, service, laneType, vehicleType, otherLaneType, otherVehicleType, allowUnderground, requireConnect, maxDistance, out pathPos);\n\t\t}\n\n\t\tpublic static bool FindPathPositionWithSpiralLoop(Vector3 position, Vector3? secondaryPosition, ItemClass.Service service, NetInfo.LaneType laneType, VehicleInfo.VehicleType vehicleType, NetInfo.LaneType otherLaneType, VehicleInfo.VehicleType otherVehicleType, bool allowUnderground, bool requireConnect, float maxDistance, out PathUnit.Position pathPos) {\n\t\t\tPathUnit.Position position2;\n\t\t\tfloat distanceSqrA;\n\t\t\tfloat distanceSqrB;\n\t\t\treturn FindPathPositionWithSpiralLoop(position, secondaryPosition, service, laneType, vehicleType, otherLaneType, otherVehicleType, VehicleInfo.VehicleType.None, allowUnderground, requireConnect, maxDistance, out pathPos, out position2, out distanceSqrA, out distanceSqrB);\n\t\t}\n\n\t\tpublic static bool FindPathPositionWithSpiralLoop(Vector3 position, ItemClass.Service service, NetInfo.LaneType laneType, VehicleInfo.VehicleType vehicleType, NetInfo.LaneType otherLaneType, VehicleInfo.VehicleType otherVehicleType, bool allowUnderground, bool requireConnect, float maxDistance, out PathUnit.Position pathPosA, out PathUnit.Position pathPosB, out float distanceSqrA, out float distanceSqrB) {\n\t\t\treturn FindPathPositionWithSpiralLoop(position, null, service, laneType, vehicleType, otherLaneType, otherVehicleType, allowUnderground, requireConnect, maxDistance, out pathPosA, out pathPosB, out distanceSqrA, out distanceSqrB);\n\t\t}\n\n\t\tpublic static bool FindPathPositionWithSpiralLoop(Vector3 position, Vector3? secondaryPosition, ItemClass.Service service, NetInfo.LaneType laneType, VehicleInfo.VehicleType vehicleType, NetInfo.LaneType otherLaneType, VehicleInfo.VehicleType otherVehicleType, bool allowUnderground, bool requireConnect, float maxDistance, out PathUnit.Position pathPosA, out PathUnit.Position pathPosB, out float distanceSqrA, out float distanceSqrB) {\n\t\t\treturn FindPathPositionWithSpiralLoop(position, secondaryPosition, service, laneType, vehicleType, otherLaneType, otherVehicleType, VehicleInfo.VehicleType.None, allowUnderground, requireConnect, maxDistance, out pathPosA, out pathPosB, out distanceSqrA, out distanceSqrB);\n\t\t}\n\n\t\tpublic static bool FindPathPositionWithSpiralLoop(Vector3 position, ItemClass.Service service, NetInfo.LaneType laneType, VehicleInfo.VehicleType vehicleType, NetInfo.LaneType otherLaneType, VehicleInfo.VehicleType otherVehicleType, VehicleInfo.VehicleType stopType, bool allowUnderground, bool requireConnect, float maxDistance, out PathUnit.Position pathPosA, out PathUnit.Position pathPosB, out float distanceSqrA, out float distanceSqrB) {\n\t\t\treturn FindPathPositionWithSpiralLoop(position, null, service, laneType, vehicleType, otherLaneType, otherVehicleType, stopType, allowUnderground, requireConnect, maxDistance, out pathPosA, out pathPosB, out distanceSqrA, out distanceSqrB);\n\t\t}\n\n\t\tpublic static bool FindPathPositionWithSpiralLoop(Vector3 position, Vector3? secondaryPosition, ItemClass.Service service, NetInfo.LaneType laneType, VehicleInfo.VehicleType vehicleType, NetInfo.LaneType otherLaneType, VehicleInfo.VehicleType otherVehicleType, VehicleInfo.VehicleType stopType, bool allowUnderground, bool requireConnect, float maxDistance, out PathUnit.Position pathPosA, out PathUnit.Position pathPosB, out float distanceSqrA, out float distanceSqrB) {\n\t\t\tint iMin = Mathf.Max((int)((position.z - (float)NetManager.NODEGRID_CELL_SIZE) / (float)NetManager.NODEGRID_CELL_SIZE + (float)NetManager.NODEGRID_RESOLUTION / 2f), 0);\n\t\t\tint iMax = Mathf.Min((int)((position.z + (float)NetManager.NODEGRID_CELL_SIZE) / (float)NetManager.NODEGRID_CELL_SIZE + (float)NetManager.NODEGRID_RESOLUTION / 2f), NetManager.NODEGRID_RESOLUTION-1);\n\n\t\t\tint jMin = Mathf.Max((int)((position.x - (float)NetManager.NODEGRID_CELL_SIZE) / (float)NetManager.NODEGRID_CELL_SIZE + (float)NetManager.NODEGRID_RESOLUTION / 2f), 0);\n\t\t\tint jMax = Mathf.Min((int)((position.x + (float)NetManager.NODEGRID_CELL_SIZE) / (float)NetManager.NODEGRID_CELL_SIZE + (float)NetManager.NODEGRID_RESOLUTION / 2f), NetManager.NODEGRID_RESOLUTION - 1);\n\n\t\t\tint width = iMax-iMin+1;\n\t\t\tint height = jMax-jMin+1;\n\n\t\t\tint centerI = (int)(position.z / (float)NetManager.NODEGRID_CELL_SIZE + (float)NetManager.NODEGRID_RESOLUTION / 2f);\n\t\t\tint centerJ = (int)(position.x / (float)NetManager.NODEGRID_CELL_SIZE + (float)NetManager.NODEGRID_RESOLUTION / 2f);\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\t/*pathPosA.m_segment = 0;\n\t\t\tpathPosA.m_lane = 0;\n\t\t\tpathPosA.m_offset = 0;*/\n\t\t\tdistanceSqrA = 1E+10f;\n\t\t\t/*pathPosB.m_segment = 0;\n\t\t\tpathPosB.m_lane = 0;\n\t\t\tpathPosB.m_offset = 0;*/\n\t\t\tdistanceSqrB = 1E+10f;\n\t\t\tfloat minDist = float.MaxValue;\n\n\t\t\tPathUnit.Position myPathPosA = default(PathUnit.Position);\n\t\t\tfloat myDistanceSqrA = float.MaxValue;\n\t\t\tPathUnit.Position myPathPosB = default(PathUnit.Position);\n\t\t\tfloat myDistanceSqrB = float.MaxValue;\n\n\t\t\tint lastSpiralDist = 0;\n\t\t\tbool found = false;\n\n\t\t\tLoopUtil.SpiralLoop(centerI, centerJ, width, height, delegate (int i, int j) {\n\t\t\t\tif (i < 0 || i >= NetManager.NODEGRID_RESOLUTION || j < 0 || j >= NetManager.NODEGRID_RESOLUTION)\n\t\t\t\t\treturn true;\n\n\t\t\t\tint spiralDist = Math.Max(Math.Abs(i - centerI), Math.Abs(j - centerJ)); // maximum norm\n\n\t\t\t\tif (found && spiralDist > lastSpiralDist) {\n\t\t\t\t\t// last iteration\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tushort segmentId = netManager.m_segmentGrid[i * NetManager.NODEGRID_RESOLUTION + j];\n\t\t\t\tint iterations = 0;\n\t\t\t\twhile (segmentId != 0) {\n\t\t\t\t\tNetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info;\n\t\t\t\t\tif (segmentInfo != null &&\n\t\t\t\t\t\tsegmentInfo.m_class.m_service == service &&\n\t\t\t\t\t\t(netManager.m_segments.m_buffer[segmentId].m_flags & (NetSegment.Flags.Collapsed | NetSegment.Flags.Flooded)) == NetSegment.Flags.None &&\n\t\t\t\t\t\t(allowUnderground || !segmentInfo.m_netAI.IsUnderground())) {\n\n\t\t\t\t\t\tbool otherPassed = true;\n\t\t\t\t\t\tif (otherLaneType != NetInfo.LaneType.None || otherVehicleType != VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\t\t// check if any lane is present that matches the given conditions\n\t\t\t\t\t\t\totherPassed = false;\n\t\t\t\t\t\t\tConstants.ServiceFactory.NetService.IterateSegmentLanes(segmentId, delegate (uint laneId, ref NetLane lane, NetInfo.Lane laneInfo, ushort segtId, ref NetSegment segment, byte laneIndex) {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t(otherLaneType == NetInfo.LaneType.None || (laneInfo.m_laneType & otherLaneType) != NetInfo.LaneType.None) &&\n\t\t\t\t\t\t\t\t\t(otherVehicleType == VehicleInfo.VehicleType.None || (laneInfo.m_vehicleType & otherVehicleType) != VehicleInfo.VehicleType.None)) {\n\t\t\t\t\t\t\t\t\totherPassed = true;\n\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (otherPassed) {\n\t\t\t\t\t\t\tushort startNodeId = netManager.m_segments.m_buffer[segmentId].m_startNode;\n\t\t\t\t\t\t\tushort endNodeId = netManager.m_segments.m_buffer[segmentId].m_endNode;\n\t\t\t\t\t\t\tVector3 startNodePos = netManager.m_nodes.m_buffer[startNodeId].m_position;\n\t\t\t\t\t\t\tVector3 endNodePos = netManager.m_nodes.m_buffer[endNodeId].m_position;\n\t\n\t\t\t\t\t\t\tVector3 posA; int laneIndexA; float laneOffsetA;\n\t\t\t\t\t\t\tVector3 posB; int laneIndexB; float laneOffsetB;\n\n\t\t\t\t\t\t\tif (netManager.m_segments.m_buffer[segmentId].GetClosestLanePosition(position, laneType, vehicleType, stopType, requireConnect, out posA, out laneIndexA, out laneOffsetA, out posB, out laneIndexB, out laneOffsetB)) {\n\t\t\t\t\t\t\t\tfloat dist = Vector3.SqrMagnitude(position - posA);\n\t\t\t\t\t\t\t\tif (secondaryPosition != null)\n\t\t\t\t\t\t\t\t\tdist += Vector3.SqrMagnitude((Vector3)secondaryPosition - posA);\n\n\t\t\t\t\t\t\t\tif (dist < minDist) {\n\t\t\t\t\t\t\t\t\tfound = true;\n\n\t\t\t\t\t\t\t\t\tminDist = dist;\n\t\t\t\t\t\t\t\t\tmyPathPosA.m_segment = segmentId;\n\t\t\t\t\t\t\t\t\tmyPathPosA.m_lane = (byte)laneIndexA;\n\t\t\t\t\t\t\t\t\tmyPathPosA.m_offset = (byte)Mathf.Clamp(Mathf.RoundToInt(laneOffsetA * 255f), 0, 255);\n\t\t\t\t\t\t\t\t\tmyDistanceSqrA = dist;\n\n\t\t\t\t\t\t\t\t\tdist = Vector3.SqrMagnitude(position - posB);\n\t\t\t\t\t\t\t\t\tif (secondaryPosition != null)\n\t\t\t\t\t\t\t\t\t\tdist += Vector3.SqrMagnitude((Vector3)secondaryPosition - posB);\n\n\t\t\t\t\t\t\t\t\tif (laneIndexB < 0) {\n\t\t\t\t\t\t\t\t\t\tmyPathPosB.m_segment = 0;\n\t\t\t\t\t\t\t\t\t\tmyPathPosB.m_lane = 0;\n\t\t\t\t\t\t\t\t\t\tmyPathPosB.m_offset = 0;\n\t\t\t\t\t\t\t\t\t\tmyDistanceSqrB = float.MaxValue;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tmyPathPosB.m_segment = segmentId;\n\t\t\t\t\t\t\t\t\t\tmyPathPosB.m_lane = (byte)laneIndexB;\n\t\t\t\t\t\t\t\t\t\tmyPathPosB.m_offset = (byte)Mathf.Clamp(Mathf.RoundToInt(laneOffsetB * 255f), 0, 255);\n\t\t\t\t\t\t\t\t\t\tmyDistanceSqrB = dist;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tsegmentId = netManager.m_segments.m_buffer[segmentId].m_nextGridSegment;\n\t\t\t\t\tif (++iterations >= NetManager.MAX_SEGMENT_COUNT) {\n\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlastSpiralDist = spiralDist;\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tpathPosA = myPathPosA;\n\t\t\tdistanceSqrA = myDistanceSqrA;\n\t\t\tpathPosB = myPathPosB;\n\t\t\tdistanceSqrB = myDistanceSqrB;\n\t\t\t\n\t\t\treturn pathPosA.m_segment != 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Finds a suitable path position for a walking citizen with the given world position.\n\t\t/// </summary>\n\t\t/// <param name=\"pos\">world position</param>\n\t\t/// <param name=\"laneTypes\">allowed lane types</param>\n\t\t/// <param name=\"vehicleTypes\">allowed vehicle types</param>\n\t\t/// <param name=\"allowTransport\">public transport allowed?</param>\n\t\t/// <param name=\"allowUnderground\">underground position allowed?</param>\n\t\t/// <param name=\"position\">resulting path position</param>\n\t\t/// <returns><code>true</code> if a position could be found, <code>false</code> otherwise</returns>\n\t\tpublic static bool FindCitizenPathPosition(Vector3 pos, NetInfo.LaneType laneTypes, VehicleInfo.VehicleType vehicleTypes, bool allowTransport, bool allowUnderground, out PathUnit.Position position) {\n\t\t\t// TODO move to ExtPathManager after harmony upgrade\n\t\t\tposition = default(PathUnit.Position);\n\t\t\tfloat minDist = 1E+10f;\n\t\t\tPathUnit.Position posA;\n\t\t\tPathUnit.Position posB;\n\t\t\tfloat distA;\n\t\t\tfloat distB;\n\t\t\tif (PathManager.FindPathPosition(pos, ItemClass.Service.Road, laneTypes, vehicleTypes, allowUnderground, false, Options.prohibitPocketCars ? GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance : 32f, out posA, out posB, out distA, out distB) && distA < minDist) {\n\t\t\t\tminDist = distA;\n\t\t\t\tposition = posA;\n\t\t\t}\n\t\t\tif (PathManager.FindPathPosition(pos, ItemClass.Service.Beautification, laneTypes, vehicleTypes, allowUnderground, false, Options.prohibitPocketCars ? GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance : 32f, out posA, out posB, out distA, out distB) && distA < minDist) {\n\t\t\t\tminDist = distA;\n\t\t\t\tposition = posA;\n\t\t\t}\n\t\t\tif (allowTransport && PathManager.FindPathPosition(pos, ItemClass.Service.PublicTransport, laneTypes, vehicleTypes, allowUnderground, false, Options.prohibitPocketCars ? GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance : 32f, out posA, out posB, out distA, out distB) && distA < minDist) {\n\t\t\t\tminDist = distA;\n\t\t\t\tposition = posA;\n\t\t\t}\n\t\t\treturn position.m_segment != 0;\n\t\t}\n\n\t\t/*internal void ResetQueueItem(uint unit) {\n\t\t\tqueueItems[unit].Reset();\n\t\t}*/\n\n\t\tprivate void StopPathFinds() {\n#if PF2\n\t\t\tforeach (CustomPathFind2 pathFind in _replacementPathFinds) {\n\t\t\t\tUnityEngine.Object.Destroy(pathFind);\n\t\t\t}\n#else\n\t\t\tforeach (CustomPathFind pathFind in _replacementPathFinds) {\n\t\t\t\tUnityEngine.Object.Destroy(pathFind);\n\t\t\t}\n#endif\n\t\t}\n\n\t\tprotected virtual void OnDestroy() {\n\t\t\tLog._Debug(\"CustomPathManager: OnDestroy\");\n\t\t\tStopPathFinds();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/PathFinding/README.md",
    "content": "# TM:PE -- /Custom/PathFinding\nDetoured path-finding classes.\n## Classes\n- **CustomPathFind**: Implements modifications made with the lane changer and lane connector tool (ProcessItemMain), implements improved algorithms for lane selection at city roads and highways (ProcessItemMain) and implements the Advanced Vehicle AI (ProcessItemCosts) by reading current densities and speeds and calculating appropriate costs hereof. \n- **CustomPathManager**: Initiates custom path-finding where the **ExtVehicleType** of a vehicle is additionally passed to the **CustomPathFind** instance."
  },
  {
    "path": "TLM/TLM/Custom/PathFinding/StockPathFind.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing ColossalFramework.UI;\nusing System;\nusing System.Threading;\nusing UnityEngine;\n\nnamespace TrafficManager.Custom.PathFinding {\n\tpublic class StockPathFind : MonoBehaviour {\n\t\tprivate const float BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR = 0.003921569f;\n\t\tprivate const float TICKET_COST_CONVERSION_FACTOR = BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * 0.0001f;\n\n\t\tprivate struct BufferItem {\n\t\t\tpublic PathUnit.Position m_position;\n\t\t\tpublic float m_comparisonValue;\n\t\t\tpublic float m_methodDistance;\n\t\t\tpublic float m_duration;\n\t\t\tpublic uint m_laneID;\n\t\t\tpublic NetInfo.Direction m_direction;\n\t\t\tpublic NetInfo.LaneType m_lanesUsed;\n\t\t}\n\n\t\tprivate Array32<PathUnit> m_pathUnits;\n\t\tprivate uint m_queueFirst;\n\t\tprivate uint m_queueLast;\n\t\tprivate uint m_calculating;\n\t\tprivate object m_queueLock;\n\t\tprivate Thread m_pathFindThread;\n\t\tprivate bool m_terminated;\n\t\tpublic ThreadProfiler m_pathfindProfiler;\n\t\tpublic volatile int m_queuedPathFindCount;\n\t\tprivate object m_bufferLock;\n\t\tprivate int m_bufferMinPos;\n\t\tprivate int m_bufferMaxPos;\n\t\tprivate uint[] m_laneLocation;\n\t\tprivate PathUnit.Position[] m_laneTarget;\n\t\tprivate BufferItem[] m_buffer;\n\t\tprivate int[] m_bufferMin;\n\t\tprivate int[] m_bufferMax;\n\t\tprivate float m_maxLength;\n\t\tprivate uint m_startLaneA;\n\t\tprivate uint m_startLaneB;\n\t\tprivate uint m_endLaneA;\n\t\tprivate uint m_endLaneB;\n\t\tprivate uint m_vehicleLane;\n\t\tprivate byte m_startOffsetA;\n\t\tprivate byte m_startOffsetB;\n\t\tprivate byte m_vehicleOffset;\n\t\tprivate NetSegment.Flags m_carBanMask;\n\t\tprivate bool m_ignoreBlocked;\n\t\tprivate bool m_stablePath;\n\t\tprivate bool m_randomParking;\n\t\tprivate bool m_transportVehicle;\n\t\tprivate bool m_ignoreCost;\n\t\tprivate NetSegment.Flags m_disableMask;\n\t\tprivate Randomizer m_pathRandomizer;\n\t\tprivate uint m_pathFindIndex;\n\t\tprivate NetInfo.LaneType m_laneTypes;\n\t\tprivate VehicleInfo.VehicleType m_vehicleTypes;\n\n\t\tpublic bool IsAvailable {\n\t\t\tget {\n\t\t\t\treturn m_pathFindThread.IsAlive;\n\t\t\t}\n\t\t}\n\n\t\tprivate void Awake() {\n\t\t\tm_pathfindProfiler = new ThreadProfiler();\n\t\t\tm_laneLocation = new uint[262144];\n\t\t\tm_laneTarget = new PathUnit.Position[262144];\n\t\t\tm_buffer = new BufferItem[65536];\n\t\t\tm_bufferMin = new int[1024];\n\t\t\tm_bufferMax = new int[1024];\n\t\t\tm_queueLock = new object();\n\t\t\tm_bufferLock = Singleton<PathManager>.instance.m_bufferLock;\n\t\t\tm_pathUnits = Singleton<PathManager>.instance.m_pathUnits;\n\t\t\tm_pathFindThread = new Thread(PathFindThread);\n\t\t\tm_pathFindThread.Name = \"Pathfind\";\n\t\t\tm_pathFindThread.Priority = SimulationManager.SIMULATION_PRIORITY;\n\t\t\tm_pathFindThread.Start();\n\t\t\tif (!m_pathFindThread.IsAlive) {\n\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Path find thread failed to start!\");\n\t\t\t}\n\t\t}\n\n\t\tprivate void OnDestroy() {\n\t\t\twhile (!Monitor.TryEnter(m_queueLock, SimulationManager.SYNCHRONIZE_TIMEOUT)) {\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tm_terminated = true;\n\t\t\t\tMonitor.PulseAll(m_queueLock);\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(m_queueLock);\n\t\t\t}\n\t\t}\n\n\t\tpublic bool CalculatePath(uint unit, bool skipQueue) {\n\t\t\tif (Singleton<PathManager>.instance.AddPathReference(unit)) {\n\t\t\t\twhile (!Monitor.TryEnter(m_queueLock, SimulationManager.SYNCHRONIZE_TIMEOUT)) {\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tif (skipQueue) {\n\t\t\t\t\t\tif (m_queueLast == 0) {\n\t\t\t\t\t\t\tm_queueLast = unit;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[unit].m_nextPathUnit = m_queueFirst;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tm_queueFirst = unit;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (m_queueLast == 0) {\n\t\t\t\t\t\t\tm_queueFirst = unit;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[m_queueLast].m_nextPathUnit = unit;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tm_queueLast = unit;\n\t\t\t\t\t}\n\n\t\t\t\t\tm_pathUnits.m_buffer[unit].m_pathFindFlags |= 1;\n\t\t\t\t\tm_queuedPathFindCount++;\n\n\t\t\t\t\tMonitor.Pulse(m_queueLock);\n\t\t\t\t} finally {\n\t\t\t\t\tMonitor.Exit(m_queueLock);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic void WaitForAllPaths() {\n\t\t\twhile (!Monitor.TryEnter(m_queueLock, SimulationManager.SYNCHRONIZE_TIMEOUT)) {\n\t\t\t}\n\t\t\ttry {\n\t\t\t\twhile (true) {\n\t\t\t\t\tif (m_queueFirst == 0 && m_calculating == 0) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (!m_terminated) {\n\t\t\t\t\t\tMonitor.Wait(m_queueLock);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(m_queueLock);\n\t\t\t}\n\t\t}\n\n\t\tprivate void PathFindImplementation(uint unit, ref PathUnit data) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tm_laneTypes = (NetInfo.LaneType)m_pathUnits.m_buffer[unit].m_laneTypes;\n\t\t\tm_vehicleTypes = (VehicleInfo.VehicleType)m_pathUnits.m_buffer[unit].m_vehicleTypes;\n\t\t\tm_maxLength = m_pathUnits.m_buffer[unit].m_length;\n\t\t\tm_pathFindIndex = (m_pathFindIndex + 1 & 0x7FFF);\n\t\t\tm_pathRandomizer = new Randomizer(unit);\n\t\t\tm_carBanMask = NetSegment.Flags.CarBan;\n\n\t\t\tif ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IS_HEAVY) != 0) {\n\t\t\t\tm_carBanMask |= NetSegment.Flags.HeavyBan;\n\t\t\t}\n\n\t\t\tif ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_READY) != 0) {\n\t\t\t\tm_carBanMask |= NetSegment.Flags.WaitingPath;\n\t\t\t}\n\n\t\t\tm_ignoreBlocked = ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IGNORE_BLOCKED) != 0);\n\t\t\tm_stablePath = ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_STABLE_PATH) != 0);\n\t\t\tm_randomParking = ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_RANDOM_PARKING) != 0);\n\t\t\tm_transportVehicle = ((m_laneTypes & NetInfo.LaneType.TransportVehicle) != NetInfo.LaneType.None);\n\t\t\tm_ignoreCost = (m_stablePath || (m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IGNORE_COST) != 0);\n\t\t\tm_disableMask = (NetSegment.Flags.Collapsed | NetSegment.Flags.PathFailed);\n\n\t\t\tif ((m_pathUnits.m_buffer[unit].m_simulationFlags & PathUnit.FLAG_IGNORE_FLOODED) == 0) {\n\t\t\t\tm_disableMask |= NetSegment.Flags.Flooded;\n\t\t\t}\n\n\t\t\tif ((m_laneTypes & NetInfo.LaneType.Vehicle) != NetInfo.LaneType.None) {\n\t\t\t\tm_laneTypes |= NetInfo.LaneType.TransportVehicle;\n\t\t\t}\n\n\t\t\tint posCount = m_pathUnits.m_buffer[unit].m_positionCount & 0xF;\n\t\t\tint vehiclePosIndicator = m_pathUnits.m_buffer[unit].m_positionCount >> 4;\n\t\t\tBufferItem bufferItemStartA = default(BufferItem);\n\t\t\tif (data.m_position00.m_segment != 0 && posCount >= 1) {\n\t\t\t\tm_startLaneA = PathManager.GetLaneID(data.m_position00);\n\t\t\t\tm_startOffsetA = data.m_position00.m_offset;\n\t\t\t\tbufferItemStartA.m_laneID = m_startLaneA;\n\t\t\t\tbufferItemStartA.m_position = data.m_position00;\n\t\t\t\tGetLaneDirection(data.m_position00, out bufferItemStartA.m_direction, out bufferItemStartA.m_lanesUsed);\n\t\t\t\tbufferItemStartA.m_comparisonValue = 0f;\n\t\t\t\tbufferItemStartA.m_duration = 0f;\n\t\t\t} else {\n\t\t\t\tm_startLaneA = 0u;\n\t\t\t\tm_startOffsetA = 0;\n\t\t\t}\n\n\t\t\tBufferItem bufferItemStartB = default(BufferItem);\n\t\t\tif (data.m_position02.m_segment != 0 && posCount >= 3) {\n\t\t\t\tm_startLaneB = PathManager.GetLaneID(data.m_position02);\n\t\t\t\tm_startOffsetB = data.m_position02.m_offset;\n\t\t\t\tbufferItemStartB.m_laneID = m_startLaneB;\n\t\t\t\tbufferItemStartB.m_position = data.m_position02;\n\t\t\t\tGetLaneDirection(data.m_position02, out bufferItemStartB.m_direction, out bufferItemStartB.m_lanesUsed);\n\t\t\t\tbufferItemStartB.m_comparisonValue = 0f;\n\t\t\t\tbufferItemStartB.m_duration = 0f;\n\t\t\t} else {\n\t\t\t\tm_startLaneB = 0u;\n\t\t\t\tm_startOffsetB = 0;\n\t\t\t}\n\n\t\t\tBufferItem bufferItemEndA = default(BufferItem);\n\t\t\tif (data.m_position01.m_segment != 0 && posCount >= 2) {\n\t\t\t\tm_endLaneA = PathManager.GetLaneID(data.m_position01);\n\t\t\t\tbufferItemEndA.m_laneID = m_endLaneA;\n\t\t\t\tbufferItemEndA.m_position = data.m_position01;\n\t\t\t\tGetLaneDirection(data.m_position01, out bufferItemEndA.m_direction, out bufferItemEndA.m_lanesUsed);\n\t\t\t\tbufferItemEndA.m_methodDistance = 0.01f;\n\t\t\t\tbufferItemEndA.m_comparisonValue = 0f;\n\t\t\t\tbufferItemEndA.m_duration = 0f;\n\t\t\t} else {\n\t\t\t\tm_endLaneA = 0u;\n\t\t\t}\n\n\t\t\tBufferItem bufferItemEndB = default(BufferItem);\n\t\t\tif (data.m_position03.m_segment != 0 && posCount >= 4) {\n\t\t\t\tm_endLaneB = PathManager.GetLaneID(data.m_position03);\n\t\t\t\tbufferItemEndB.m_laneID = m_endLaneB;\n\t\t\t\tbufferItemEndB.m_position = data.m_position03;\n\t\t\t\tGetLaneDirection(data.m_position03, out bufferItemEndB.m_direction, out bufferItemEndB.m_lanesUsed);\n\t\t\t\tbufferItemEndB.m_methodDistance = 0.01f;\n\t\t\t\tbufferItemEndB.m_comparisonValue = 0f;\n\t\t\t\tbufferItemEndB.m_duration = 0f;\n\t\t\t} else {\n\t\t\t\tm_endLaneB = 0u;\n\t\t\t}\n\n\t\t\tif (data.m_position11.m_segment != 0 && vehiclePosIndicator >= 1) {\n\t\t\t\tm_vehicleLane = PathManager.GetLaneID(data.m_position11);\n\t\t\t\tm_vehicleOffset = data.m_position11.m_offset;\n\t\t\t} else {\n\t\t\t\tm_vehicleLane = 0u;\n\t\t\t\tm_vehicleOffset = 0;\n\t\t\t}\n\n\t\t\tBufferItem finalBufferItem = default(BufferItem);\n\t\t\tbyte startOffset = 0;\n\t\t\tm_bufferMinPos = 0;\n\t\t\tm_bufferMaxPos = -1;\n\n\t\t\tif (m_pathFindIndex == 0) {\n\t\t\t\tuint num3 = 4294901760u;\n\t\t\t\tfor (int i = 0; i < 262144; i++) {\n\t\t\t\t\tm_laneLocation[i] = num3;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (int j = 0; j < 1024; j++) {\n\t\t\t\tm_bufferMin[j] = 0;\n\t\t\t\tm_bufferMax[j] = -1;\n\t\t\t}\n\n\t\t\tif (bufferItemEndA.m_position.m_segment != 0) {\n\t\t\t\tm_bufferMax[0]++;\n\t\t\t\tm_buffer[++m_bufferMaxPos] = bufferItemEndA;\n\t\t\t}\n\n\t\t\tif (bufferItemEndB.m_position.m_segment != 0) {\n\t\t\t\tm_bufferMax[0]++;\n\t\t\t\tm_buffer[++m_bufferMaxPos] = bufferItemEndB;\n\t\t\t}\n\n\t\t\tbool canFindPath = false;\n\t\t\twhile (m_bufferMinPos <= m_bufferMaxPos) {\n\t\t\t\tint bufMin = m_bufferMin[m_bufferMinPos];\n\t\t\t\tint bufMax = m_bufferMax[m_bufferMinPos];\n\n\t\t\t\tif (bufMin > bufMax) {\n\t\t\t\t\tm_bufferMinPos++;\n\t\t\t\t} else {\n\t\t\t\t\tm_bufferMin[m_bufferMinPos] = bufMin + 1;\n\t\t\t\t\tBufferItem candidateItem = m_buffer[(m_bufferMinPos << 6) + bufMin];\n\t\t\t\t\tif (candidateItem.m_position.m_segment == bufferItemStartA.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartA.m_position.m_lane) {\n\t\t\t\t\t\tif ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None && candidateItem.m_position.m_offset >= m_startOffsetA) {\n\t\t\t\t\t\t\tfinalBufferItem = candidateItem;\n\t\t\t\t\t\t\tstartOffset = m_startOffsetA;\n\t\t\t\t\t\t\tcanFindPath = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None && candidateItem.m_position.m_offset <= m_startOffsetA) {\n\t\t\t\t\t\t\tfinalBufferItem = candidateItem;\n\t\t\t\t\t\t\tstartOffset = m_startOffsetA;\n\t\t\t\t\t\t\tcanFindPath = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (candidateItem.m_position.m_segment == bufferItemStartB.m_position.m_segment && candidateItem.m_position.m_lane == bufferItemStartB.m_position.m_lane) {\n\t\t\t\t\t\tif ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None && candidateItem.m_position.m_offset >= m_startOffsetB) {\n\t\t\t\t\t\t\tfinalBufferItem = candidateItem;\n\t\t\t\t\t\t\tstartOffset = m_startOffsetB;\n\t\t\t\t\t\t\tcanFindPath = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None && candidateItem.m_position.m_offset <= m_startOffsetB) {\n\t\t\t\t\t\t\tfinalBufferItem = candidateItem;\n\t\t\t\t\t\t\tstartOffset = m_startOffsetB;\n\t\t\t\t\t\t\tcanFindPath = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((candidateItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None) {\n\t\t\t\t\t\tushort startNodeId = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode;\n\t\t\t\t\t\tProcessItemMain(candidateItem, startNodeId, ref netManager.m_nodes.m_buffer[startNodeId], (byte)0, false);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((candidateItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None) {\n\t\t\t\t\t\tushort endNodeId = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode;\n\t\t\t\t\t\tProcessItemMain(candidateItem, endNodeId, ref netManager.m_nodes.m_buffer[endNodeId], (byte)255, false);\n\t\t\t\t\t}\n\n\t\t\t\t\tint numIter = 0;\n\t\t\t\t\tushort specialNodeId = netManager.m_lanes.m_buffer[candidateItem.m_laneID].m_nodes;\n\t\t\t\t\tif (specialNodeId != 0) {\n\t\t\t\t\t\tushort startNode2 = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_startNode;\n\t\t\t\t\t\tushort endNode2 = netManager.m_segments.m_buffer[candidateItem.m_position.m_segment].m_endNode;\n\t\t\t\t\t\tbool nodesDisabled = ((netManager.m_nodes.m_buffer[startNode2].m_flags | netManager.m_nodes.m_buffer[endNode2].m_flags) & NetNode.Flags.Disabled) != NetNode.Flags.None;\n\n\t\t\t\t\t\twhile (specialNodeId != 0) {\n\t\t\t\t\t\t\tNetInfo.Direction direction = NetInfo.Direction.None;\n\t\t\t\t\t\t\tbyte laneOffset = netManager.m_nodes.m_buffer[specialNodeId].m_laneOffset;\n\n\t\t\t\t\t\t\tif (laneOffset <= candidateItem.m_position.m_offset) {\n\t\t\t\t\t\t\t\tdirection |= NetInfo.Direction.Forward;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (laneOffset >= candidateItem.m_position.m_offset) {\n\t\t\t\t\t\t\t\tdirection |= NetInfo.Direction.Backward;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ((candidateItem.m_direction & direction) != NetInfo.Direction.None && (!nodesDisabled || (netManager.m_nodes.m_buffer[specialNodeId].m_flags & NetNode.Flags.Disabled) != NetNode.Flags.None)) {\n\t\t\t\t\t\t\t\tProcessItemMain(candidateItem, specialNodeId, ref netManager.m_nodes.m_buffer[specialNodeId], laneOffset, true);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tspecialNodeId = netManager.m_nodes.m_buffer[specialNodeId].m_nextLaneNode;\n\n\t\t\t\t\t\t\tif (++numIter == 32768) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!canFindPath) {\n\t\t\t\tm_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;\n\t\t\t} else {\n\t\t\t\tfloat duration = (m_laneTypes != NetInfo.LaneType.Pedestrian && (m_laneTypes & NetInfo.LaneType.Pedestrian) != NetInfo.LaneType.None) ? finalBufferItem.m_duration : finalBufferItem.m_methodDistance;\n\t\t\t\tm_pathUnits.m_buffer[unit].m_length = duration;\n\t\t\t\tm_pathUnits.m_buffer[unit].m_speed = (byte)Mathf.Clamp(finalBufferItem.m_methodDistance * 100f / Mathf.Max(0.01f, finalBufferItem.m_duration), 0f, 255f);\n\t\t\t\tuint currentPathUnitId = unit;\n\t\t\t\tint currentItemPositionCount = 0;\n\t\t\t\tint sumOfPositionCounts = 0;\n\t\t\t\tPathUnit.Position currentPosition = finalBufferItem.m_position;\n\t\t\t\tif ((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)) {\n\t\t\t\t\tif (startOffset != currentPosition.m_offset) {\n\t\t\t\t\t\tPathUnit.Position position2 = currentPosition;\n\t\t\t\t\t\tposition2.m_offset = startOffset;\n\t\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, position2);\n\t\t\t\t\t}\n\n\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition);\n\t\t\t\t\tcurrentPosition = m_laneTarget[finalBufferItem.m_laneID];\n\t\t\t\t}\n\n\t\t\t\tfor (int k = 0; k < 262144; k++) {\n\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].SetPosition(currentItemPositionCount++, currentPosition);\n\t\t\t\t\tif (currentPosition.m_segment == bufferItemEndA.m_position.m_segment && currentPosition.m_lane == bufferItemEndA.m_position.m_lane && currentPosition.m_offset == bufferItemEndA.m_position.m_offset) {\n\t\t\t\t\t\tgoto IL_0c87;\n\t\t\t\t\t}\n\t\t\t\t\tif (currentPosition.m_segment == bufferItemEndB.m_position.m_segment && currentPosition.m_lane == bufferItemEndB.m_position.m_lane && currentPosition.m_offset == bufferItemEndB.m_position.m_offset) {\n\t\t\t\t\t\tgoto IL_0c87;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (currentItemPositionCount == 12) {\n\t\t\t\t\t\twhile (!Monitor.TryEnter(m_bufferLock, SimulationManager.SYNCHRONIZE_TIMEOUT)) {\n\t\t\t\t\t\t}\n\t\t\t\t\t\tuint createdPathUnitId = default(uint);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (m_pathUnits.CreateItem(out createdPathUnitId, ref m_pathRandomizer)) {\n\t\t\t\t\t\t\t\tm_pathUnits.m_buffer[createdPathUnitId] = m_pathUnits.m_buffer[currentPathUnitId];\n\t\t\t\t\t\t\t\tm_pathUnits.m_buffer[createdPathUnitId].m_referenceCount = 1;\n\t\t\t\t\t\t\t\tm_pathUnits.m_buffer[createdPathUnitId].m_pathFindFlags = 4;\n\t\t\t\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].m_nextPathUnit = createdPathUnitId;\n\t\t\t\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;\n\t\t\t\t\t\t\t\tsumOfPositionCounts += currentItemPositionCount;\n\t\t\t\t\t\t\t\tSingleton<PathManager>.instance.m_pathUnitCount = (int)(m_pathUnits.ItemCount() - 1);\n\t\t\t\t\t\t\t\tgoto end_IL_0dbc;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\tend_IL_0dbc:;\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tMonitor.Exit(m_bufferLock);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcurrentPathUnitId = createdPathUnitId;\n\t\t\t\t\t\tcurrentItemPositionCount = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tuint laneID = PathManager.GetLaneID(currentPosition);\n\t\t\t\t\tcurrentPosition = m_laneTarget[laneID];\n\t\t\t\t\tcontinue;\n\t\t\t\t\tIL_0c87:\n\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].m_positionCount = (byte)currentItemPositionCount;\n\t\t\t\t\tsumOfPositionCounts += currentItemPositionCount;\n\t\t\t\t\tif (sumOfPositionCounts != 0) {\n\t\t\t\t\t\tcurrentPathUnitId = m_pathUnits.m_buffer[unit].m_nextPathUnit;\n\t\t\t\t\t\tcurrentItemPositionCount = m_pathUnits.m_buffer[unit].m_positionCount;\n\t\t\t\t\t\tint numIter = 0;\n\t\t\t\t\t\twhile (currentPathUnitId != 0) {\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[currentPathUnitId].m_length = duration * (float)(sumOfPositionCounts - currentItemPositionCount) / (float)sumOfPositionCounts;\n\t\t\t\t\t\t\tcurrentItemPositionCount += m_pathUnits.m_buffer[currentPathUnitId].m_positionCount;\n\t\t\t\t\t\t\tcurrentPathUnitId = m_pathUnits.m_buffer[currentPathUnitId].m_nextPathUnit;\n\t\t\t\t\t\t\tif (++numIter >= 262144) {\n\t\t\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tm_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_READY;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tm_pathUnits.m_buffer[unit].m_pathFindFlags |= PathUnit.FLAG_FAILED;\n\t\t\t}\n\t\t}\n\n\t\t// 1\n\t\tprivate void ProcessItemMain(BufferItem item, ushort nextNodeId, ref NetNode nextNode, byte connectOffset, bool isMiddle) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tbool prevIsPedestrianLane = false;\n\t\t\tbool prevIsBicycleLane = false;\n\t\t\tbool prevIsCenterPlatform = false;\n\t\t\tbool prevIsElevated = false;\n\t\t\tint prevRelSimilarLaneIndex = 0;\n\t\t\tNetInfo prevSegmentInfo = netManager.m_segments.m_buffer[item.m_position.m_segment].Info;\n\t\t\tif (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {\n\t\t\t\tNetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];\n\t\t\t\tprevIsPedestrianLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian);\n\t\t\t\tprevIsBicycleLane = (prevLaneInfo.m_laneType == NetInfo.LaneType.Vehicle && (prevLaneInfo.m_vehicleType & m_vehicleTypes) == VehicleInfo.VehicleType.Bicycle);\n\t\t\t\tprevIsCenterPlatform = prevLaneInfo.m_centerPlatform;\n\t\t\t\tprevIsElevated = prevLaneInfo.m_elevated;\n\t\t\t\tprevRelSimilarLaneIndex = (((prevLaneInfo.m_finalDirection & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? (prevLaneInfo.m_similarLaneCount - prevLaneInfo.m_similarLaneIndex - 1) : prevLaneInfo.m_similarLaneIndex);\n\t\t\t}\n\n\t\t\tif (isMiddle) {\n\t\t\t\tfor (int i = 0; i < 8; i++) {\n\t\t\t\t\tushort nextSegmentId = nextNode.GetSegment(i);\n\t\t\t\t\tif (nextSegmentId != 0) {\n\t\t\t\t\t\tProcessItemCosts(item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, !prevIsPedestrianLane, prevIsPedestrianLane);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (prevIsPedestrianLane) {\n\t\t\t\tif (!prevIsElevated) {\n\t\t\t\t\tushort prevSegmentId = item.m_position.m_segment;\n\t\t\t\t\tint prevLaneIndex = item.m_position.m_lane;\n\t\t\t\t\tif (nextNode.Info.m_class.m_service != ItemClass.Service.Beautification) {\n\t\t\t\t\t\tbool canCrossStreet = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;\n\t\t\t\t\t\tbool isOnCenterPlatform = prevIsCenterPlatform && (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Junction)) == NetNode.Flags.None;\n\t\t\t\t\t\tushort nextLeftSegmentId = prevSegmentId;\n\t\t\t\t\t\tushort nextRightSegmentId = prevSegmentId;\n\t\t\t\t\t\tint leftLaneIndex = default(int);\n\t\t\t\t\t\tint rightLaneIndex = default(int);\n\t\t\t\t\t\tuint leftLaneId = default(uint);\n\t\t\t\t\t\tuint rightLaneId = default(uint);\n\t\t\t\t\t\tnetManager.m_segments.m_buffer[prevSegmentId].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, prevLaneIndex, isOnCenterPlatform, out leftLaneIndex, out rightLaneIndex, out leftLaneId, out rightLaneId);\n\t\t\t\t\t\tif (leftLaneId == 0 || rightLaneId == 0) {\n\t\t\t\t\t\t\tushort leftSegmentId = default(ushort);\n\t\t\t\t\t\t\tushort rightSegmentId = default(ushort);\n\t\t\t\t\t\t\tnetManager.m_segments.m_buffer[prevSegmentId].GetLeftAndRightSegments(nextNodeId, out leftSegmentId, out rightSegmentId);\n\t\t\t\t\t\t\tint numIter = 0;\n\t\t\t\t\t\t\twhile (leftSegmentId != 0 && leftSegmentId != prevSegmentId && leftLaneId == 0) {\n\t\t\t\t\t\t\t\tint someLeftLaneIndex = default(int);\n\t\t\t\t\t\t\t\tint someRightLaneIndex = default(int);\n\t\t\t\t\t\t\t\tuint someLeftLaneId = default(uint);\n\t\t\t\t\t\t\t\tuint someRightLaneId = default(uint);\n\t\t\t\t\t\t\t\tnetManager.m_segments.m_buffer[leftSegmentId].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);\n\n\t\t\t\t\t\t\t\tif (someRightLaneId != 0) {\n\t\t\t\t\t\t\t\t\tnextLeftSegmentId = leftSegmentId;\n\t\t\t\t\t\t\t\t\tleftLaneIndex = someRightLaneIndex;\n\t\t\t\t\t\t\t\t\tleftLaneId = someRightLaneId;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tleftSegmentId = netManager.m_segments.m_buffer[leftSegmentId].GetLeftSegment(nextNodeId);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (++numIter == 8) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tnumIter = 0;\n\t\t\t\t\t\t\twhile (rightSegmentId != 0 && rightSegmentId != prevSegmentId && rightLaneId == 0) {\n\t\t\t\t\t\t\t\tint someLeftLaneIndex = default(int);\n\t\t\t\t\t\t\t\tint someRightLaneIndex = default(int);\n\t\t\t\t\t\t\t\tuint someLeftLaneId = default(uint);\n\t\t\t\t\t\t\t\tuint someRightLaneId = default(uint);\n\t\t\t\t\t\t\t\tnetManager.m_segments.m_buffer[rightSegmentId].GetLeftAndRightLanes(nextNodeId, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, -1, isOnCenterPlatform, out someLeftLaneIndex, out someRightLaneIndex, out someLeftLaneId, out someRightLaneId);\n\n\t\t\t\t\t\t\t\tif (someLeftLaneId != 0) {\n\t\t\t\t\t\t\t\t\tnextRightSegmentId = rightSegmentId;\n\t\t\t\t\t\t\t\t\trightLaneIndex = someLeftLaneIndex;\n\t\t\t\t\t\t\t\t\trightLaneId = someLeftLaneId;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\trightSegmentId = netManager.m_segments.m_buffer[rightSegmentId].GetRightSegment(nextNodeId);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (++numIter == 8) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (leftLaneId != 0 && (nextLeftSegmentId != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {\n\t\t\t\t\t\t\tProcessItemPedBicycle(item, nextNodeId, nextLeftSegmentId, ref netManager.m_segments.m_buffer[nextLeftSegmentId], connectOffset, connectOffset, leftLaneIndex, leftLaneId);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (rightLaneId != 0 && rightLaneId != leftLaneId && (nextRightSegmentId != prevSegmentId || canCrossStreet || isOnCenterPlatform)) {\n\t\t\t\t\t\t\tProcessItemPedBicycle(item, nextNodeId, nextRightSegmentId, ref netManager.m_segments.m_buffer[nextRightSegmentId], connectOffset, connectOffset, rightLaneIndex, rightLaneId);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tint nextLaneIndex = default(int);\n\t\t\t\t\t\tuint nextLaneId = default(uint);\n\t\t\t\t\t\tif ((m_vehicleTypes & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None && netManager.m_segments.m_buffer[prevSegmentId].GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Vehicle, VehicleInfo.VehicleType.Bicycle, out nextLaneIndex, out nextLaneId)) {\n\t\t\t\t\t\t\tProcessItemPedBicycle(item, nextNodeId, prevSegmentId, ref netManager.m_segments.m_buffer[prevSegmentId], connectOffset, connectOffset, nextLaneIndex, nextLaneId);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfor (int j = 0; j < 8; j++) {\n\t\t\t\t\t\t\tushort nextSegmentId = nextNode.GetSegment(j);\n\t\t\t\t\t\t\tif (nextSegmentId != 0 && nextSegmentId != prevSegmentId) {\n\t\t\t\t\t\t\t\tProcessItemCosts(item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, false, true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tNetInfo.LaneType nextLaneType = m_laneTypes & ~NetInfo.LaneType.Pedestrian;\n\t\t\t\t\tVehicleInfo.VehicleType nextVehicleType = m_vehicleTypes & ~VehicleInfo.VehicleType.Bicycle;\n\t\t\t\t\tif ((item.m_lanesUsed & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {\n\t\t\t\t\t\tnextLaneType &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t\t}\n\n\t\t\t\t\tint sameSegLaneIndex = default(int);\n\t\t\t\t\tuint sameSegLaneId = default(uint);\n\t\t\t\t\tif (nextLaneType != NetInfo.LaneType.None && nextVehicleType != VehicleInfo.VehicleType.None && netManager.m_segments.m_buffer[prevSegmentId].GetClosestLane(prevLaneIndex, nextLaneType, nextVehicleType, out sameSegLaneIndex, out sameSegLaneId)) {\n\t\t\t\t\t\tNetInfo.Lane sameSegLaneInfo = prevSegmentInfo.m_lanes[sameSegLaneIndex];\n\t\t\t\t\t\tbyte sameSegConnectOffset = (byte)(((netManager.m_segments.m_buffer[prevSegmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None == ((sameSegLaneInfo.m_finalDirection & NetInfo.Direction.Backward) != NetInfo.Direction.None)) ? 1 : 254);\n\t\t\t\t\t\tBufferItem nextItem = item;\n\t\t\t\t\t\tif (m_randomParking) {\n\t\t\t\t\t\t\tnextItem.m_comparisonValue += (float)m_pathRandomizer.Int32(300u) / m_maxLength;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tProcessItemPedBicycle(nextItem, nextNodeId, prevSegmentId, ref netManager.m_segments.m_buffer[prevSegmentId], sameSegConnectOffset, (byte)128, sameSegLaneIndex, sameSegLaneId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tbool allowPedestrian = (m_laneTypes & NetInfo.LaneType.Pedestrian) != NetInfo.LaneType.None;\n\t\t\t\tbool allowBicycle = false;\n\t\t\t\tbyte switchConnectOffset = 0;\n\t\t\t\tif (allowPedestrian) {\n\t\t\t\t\tif (prevIsBicycleLane) {\n\t\t\t\t\t\tswitchConnectOffset = connectOffset;\n\t\t\t\t\t\tallowBicycle = (nextNode.Info.m_class.m_service == ItemClass.Service.Beautification);\n\t\t\t\t\t} else if (m_vehicleLane != 0) {\n\t\t\t\t\t\tif (m_vehicleLane != item.m_laneID) {\n\t\t\t\t\t\t\tallowPedestrian = false;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tswitchConnectOffset = m_vehicleOffset;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tswitchConnectOffset = (byte)((!m_stablePath) ? ((byte)m_pathRandomizer.UInt32(1u, 254u)) : 128);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tushort nextSegmentId = 0;\n\t\t\t\tif ((m_vehicleTypes & (VehicleInfo.VehicleType.Ferry | VehicleInfo.VehicleType.Monorail)) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\tbool isUturnAllowedHere = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.Junction)) != NetNode.Flags.None;\n\t\t\t\t\tfor (int k = 0; k < 8; k++) {\n\t\t\t\t\t\tnextSegmentId = nextNode.GetSegment(k);\n\t\t\t\t\t\tif (nextSegmentId != 0 && nextSegmentId != item.m_position.m_segment) {\n\t\t\t\t\t\t\tProcessItemCosts(item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, allowBicycle);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (isUturnAllowedHere && (m_vehicleTypes & VehicleInfo.VehicleType.Monorail) == VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\tnextSegmentId = item.m_position.m_segment;\n\t\t\t\t\t\tProcessItemCosts(item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, false);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tbool isUturnAllowedHere = (nextNode.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;\n\t\t\t\t\tnextSegmentId = netManager.m_segments.m_buffer[item.m_position.m_segment].GetRightSegment(nextNodeId);\n\t\t\t\t\tfor (int l = 0; l < 8; l++) {\n\t\t\t\t\t\tif (nextSegmentId == 0) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (nextSegmentId == item.m_position.m_segment) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ProcessItemCosts(item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, allowBicycle)) {\n\t\t\t\t\t\t\tisUturnAllowedHere = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tnextSegmentId = netManager.m_segments.m_buffer[nextSegmentId].GetRightSegment(nextNodeId);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (isUturnAllowedHere && (m_vehicleTypes & VehicleInfo.VehicleType.Tram) == VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\tnextSegmentId = item.m_position.m_segment;\n\t\t\t\t\t\tProcessItemCosts(item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], ref prevRelSimilarLaneIndex, connectOffset, true, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (allowPedestrian) {\n\t\t\t\t\tnextSegmentId = item.m_position.m_segment;\n\t\t\t\t\tint nextLaneIndex = default(int);\n\t\t\t\t\tuint nextLaneId = default(uint);\n\t\t\t\t\tif (netManager.m_segments.m_buffer[nextSegmentId].GetClosestLane((int)item.m_position.m_lane, NetInfo.LaneType.Pedestrian, m_vehicleTypes, out nextLaneIndex, out nextLaneId)) {\n\t\t\t\t\t\tProcessItemPedBicycle(item, nextNodeId, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], switchConnectOffset, switchConnectOffset, nextLaneIndex, nextLaneId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (nextNode.m_lane != 0) {\n\t\t\t\tbool targetDisabled = (nextNode.m_flags & (NetNode.Flags.Disabled | NetNode.Flags.DisableOnlyMiddle)) == NetNode.Flags.Disabled;\n\t\t\t\tushort nextSegmentId = netManager.m_lanes.m_buffer[nextNode.m_lane].m_segment;\n\t\t\t\tif (nextSegmentId != 0 && nextSegmentId != item.m_position.m_segment) {\n\t\t\t\t\tProcessItemPublicTransport(item, nextNodeId, targetDisabled, nextSegmentId, ref netManager.m_segments.m_buffer[nextSegmentId], nextNode.m_lane, nextNode.m_laneOffset, connectOffset);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// 2\n\t\tprivate void ProcessItemPublicTransport(BufferItem item, ushort nextNodeId, bool targetDisabled, ushort nextSegmentId, ref NetSegment nextSegment, uint nextLaneId, byte offset, byte connectOffset) {\n\t\t\tif ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tif (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) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tNetInfo nextSegmentInfo = nextSegment.Info;\n\t\t\tNetInfo prevSegmentInfo = netManager.m_segments.m_buffer[item.m_position.m_segment].Info;\n\t\t\tint nextNumLanes = nextSegmentInfo.m_lanes.Length;\n\t\t\tuint curLaneId = nextSegment.m_lanes;\n\t\t\tfloat prevMaxSpeed = 1f;\n\t\t\tfloat prevSpeed = 1f;\n\t\t\tNetInfo.LaneType prevLaneType = NetInfo.LaneType.None;\n\t\t\tif (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {\n\t\t\t\tNetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];\n\t\t\t\tprevMaxSpeed = prevLaneInfo.m_speedLimit;\n\t\t\t\tprevLaneType = prevLaneInfo.m_laneType;\n\t\t\t\tif ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {\n\t\t\t\t\tprevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t}\n\t\t\t\tprevSpeed = CalculateLaneSpeed(connectOffset, item.m_position.m_offset, ref netManager.m_segments.m_buffer[item.m_position.m_segment], prevLaneInfo);\n\t\t\t}\n\t\t\tfloat prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? netManager.m_segments.m_buffer[item.m_position.m_segment].m_averageLength : netManager.m_lanes.m_buffer[item.m_laneID].m_length;\n\t\t\tfloat offsetLength = (float)Mathf.Abs(connectOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength;\n\t\t\tfloat methodDistance = item.m_methodDistance + offsetLength;\n\t\t\tfloat comparisonValue = item.m_comparisonValue + offsetLength / (prevSpeed * m_maxLength);\n\t\t\tfloat duration = item.m_duration + offsetLength / prevMaxSpeed;\n\t\t\tVector3 b = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)(int)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\tif (!m_ignoreCost) {\n\t\t\t\tint ticketCost = netManager.m_lanes.m_buffer[item.m_laneID].m_ticketCost;\n\t\t\t\tif (ticketCost != 0) {\n\t\t\t\t\tcomparisonValue += (float)(ticketCost * m_pathRandomizer.Int32(2000u)) * TICKET_COST_CONVERSION_FACTOR;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tint nextLaneIndex = 0;\n\t\t\twhile (true) {\n\t\t\t\tif (nextLaneIndex < nextNumLanes && curLaneId != 0) {\n\t\t\t\t\tif (nextLaneId != curLaneId) {\n\t\t\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t\t\tnextLaneIndex++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tNetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];\n\t\t\tif (nextLaneInfo.CheckType(m_laneTypes, m_vehicleTypes)) {\n\t\t\t\tVector3 a = netManager.m_lanes.m_buffer[nextLaneId].CalculatePosition((float)(int)offset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\t\tfloat distance = Vector3.Distance(a, b);\n\t\t\t\tBufferItem nextItem = default(BufferItem);\n\n\t\t\t\tnextItem.m_position.m_segment = nextSegmentId;\n\t\t\t\tnextItem.m_position.m_lane = (byte)nextLaneIndex;\n\t\t\t\tnextItem.m_position.m_offset = offset;\n\n\t\t\t\tif ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {\n\t\t\t\t\tnextItem.m_methodDistance = 0f;\n\t\t\t\t} else {\n\t\t\t\t\tnextItem.m_methodDistance = methodDistance + distance;\n\t\t\t\t}\n\n\t\t\t\tif (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < 1000f) && !m_stablePath) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tnextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextLaneInfo.m_speedLimit) * 0.5f * m_maxLength);\n\t\t\t\tnextItem.m_duration = duration + distance / ((prevMaxSpeed + nextLaneInfo.m_speedLimit) * 0.5f);\n\n\t\t\t\tif ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\t\tnextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);\n\t\t\t\t} else {\n\t\t\t\t\tnextItem.m_direction = nextLaneInfo.m_finalDirection;\n\t\t\t\t}\n\n\t\t\t\tif (nextLaneId == m_startLaneA) {\n\t\t\t\t\tif (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetA) &&\n\t\t\t\t\t\t((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetA)) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat nextSpeed = CalculateLaneSpeed(m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);\n\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\n\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);\n\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;\n\t\t\t\t}\n\n\t\t\t\tif (nextLaneId == m_startLaneB) {\n\t\t\t\t\tif (((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetB) &&\n\t\t\t\t\t\t((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None || nextItem.m_position.m_offset > m_startOffsetB)) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat nextSpeed = CalculateLaneSpeed(m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);\n\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetB) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\n\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);\n\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;\n\t\t\t\t}\n\n\t\t\t\tnextItem.m_laneID = nextLaneId;\n\t\t\t\tnextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);\n\n\t\t\t\tAddBufferItem(nextItem, item.m_position);\n\t\t\t}\n\t\t}\n\n\t\t// 3\n\t\tprivate bool ProcessItemCosts(BufferItem item, ushort nextNodeId, ushort nextSegmentId, ref NetSegment nextSegment, ref int laneIndexFromInner, byte connectOffset, bool enableVehicle, bool enablePedestrian) {\n\t\t\tbool blocked = false;\n\t\t\tif ((nextSegment.m_flags & m_disableMask) != 0) {\n\t\t\t\treturn blocked;\n\t\t\t}\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tNetInfo nextSegmentInfo = nextSegment.Info;\n\t\t\tNetInfo prevSegmentInfo = netManager.m_segments.m_buffer[item.m_position.m_segment].Info;\n\t\t\tint nextNumLanes = nextSegmentInfo.m_lanes.Length;\n\t\t\tuint curLaneId = nextSegment.m_lanes;\n\t\t\tNetInfo.Direction nextDir = (nextNodeId != nextSegment.m_startNode) ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;\n\t\t\tNetInfo.Direction nextFinalDir = ((nextSegment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? nextDir : NetInfo.InvertDirection(nextDir);\n\t\t\tfloat prevMaxSpeed = 1f;\n\t\t\tfloat prevLaneSpeed = 1f;\n\t\t\tNetInfo.LaneType prevLaneType = NetInfo.LaneType.None;\n\t\t\tVehicleInfo.VehicleType prevVehicleType = VehicleInfo.VehicleType.None;\n\t\t\tif (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {\n\t\t\t\tNetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];\n\t\t\t\tprevLaneType = prevLaneInfo.m_laneType;\n\t\t\t\tprevVehicleType = prevLaneInfo.m_vehicleType;\n\t\t\t\tprevMaxSpeed = prevLaneInfo.m_speedLimit;\n\t\t\t\tprevLaneSpeed = CalculateLaneSpeed(connectOffset, item.m_position.m_offset, ref netManager.m_segments.m_buffer[item.m_position.m_segment], prevLaneInfo);\n\t\t\t}\n\n\t\t\tbool acuteTurningAngle = false;\n\t\t\tif (prevLaneType == NetInfo.LaneType.Vehicle && (prevVehicleType & VehicleInfo.VehicleType.Car) == VehicleInfo.VehicleType.None) {\n\t\t\t\tfloat turningAngle = 0.01f - Mathf.Min(nextSegmentInfo.m_maxTurnAngleCos, prevSegmentInfo.m_maxTurnAngleCos);\n\t\t\t\tif (turningAngle < 1f) {\n\t\t\t\t\tVector3 vector = (nextNodeId != netManager.m_segments.m_buffer[item.m_position.m_segment].m_startNode) ? netManager.m_segments.m_buffer[item.m_position.m_segment].m_endDirection : netManager.m_segments.m_buffer[item.m_position.m_segment].m_startDirection;\n\t\t\t\t\tVector3 vector2 = ((nextDir & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? nextSegment.m_startDirection : nextSegment.m_endDirection;\n\t\t\t\t\tfloat dirDotProd = vector.x * vector2.x + vector.z * vector2.z;\n\t\t\t\t\tif (dirDotProd >= turningAngle) {\n\t\t\t\t\t\tacuteTurningAngle = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfloat prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? netManager.m_segments.m_buffer[item.m_position.m_segment].m_averageLength : netManager.m_lanes.m_buffer[item.m_laneID].m_length;\n\t\t\tfloat offsetLength = (float)Mathf.Abs(connectOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength;\n\t\t\tfloat methodDistance = item.m_methodDistance + offsetLength;\n\t\t\tfloat duration = item.m_duration + offsetLength / prevMaxSpeed;\n\n\t\t\tif (!m_stablePath) {\n\t\t\t\toffsetLength *= (float)(new Randomizer(m_pathFindIndex << 16 | item.m_position.m_segment).Int32(900, 1000 + netManager.m_segments.m_buffer[item.m_position.m_segment].m_trafficDensity * 10) + m_pathRandomizer.Int32(20u)) * 0.001f;\n\t\t\t}\n\t\t\tif ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None && (prevVehicleType & m_vehicleTypes) == VehicleInfo.VehicleType.Car && (netManager.m_segments.m_buffer[item.m_position.m_segment].m_flags & m_carBanMask) != NetSegment.Flags.None) {\n\t\t\t\toffsetLength *= 7.5f;\n\t\t\t}\n\n\t\t\tif (m_transportVehicle && prevLaneType == NetInfo.LaneType.TransportVehicle) {\n\t\t\t\toffsetLength *= 0.95f;\n\t\t\t}\n\n\t\t\tfloat comparisonValue = item.m_comparisonValue + offsetLength / (prevLaneSpeed * m_maxLength);\n\t\t\tint ticketCost = netManager.m_lanes.m_buffer[item.m_laneID].m_ticketCost;\n\t\t\tif (!this.m_ignoreCost && ticketCost != 0) {\n\t\t\t\tcomparisonValue += (float)(ticketCost * this.m_pathRandomizer.Int32(2000u)) * TICKET_COST_CONVERSION_FACTOR;\n\t\t\t}\n\t\t\tif ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0) {\n\t\t\t\tprevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t}\n\t\t\tVector3 b = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)(int)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\tint newLaneIndexFromInner = laneIndexFromInner;\n\t\t\tbool transitionNode = (netManager.m_nodes.m_buffer[nextNodeId].m_flags & NetNode.Flags.Transition) != NetNode.Flags.None;\n\t\t\tNetInfo.LaneType allowedLaneTypes = m_laneTypes;\n\t\t\tVehicleInfo.VehicleType allowedVehicleTypes = m_vehicleTypes;\n\t\t\tif (!enableVehicle) {\n\t\t\t\tallowedVehicleTypes &= VehicleInfo.VehicleType.Bicycle;\n\t\t\t\tif (allowedVehicleTypes == VehicleInfo.VehicleType.None) {\n\t\t\t\t\tallowedLaneTypes &= ~(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!enablePedestrian) {\n\t\t\t\tallowedLaneTypes &= ~NetInfo.LaneType.Pedestrian;\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < nextNumLanes && curLaneId != 0; i++) {\n\t\t\t\tNetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[i];\n\t\t\t\tfloat transitionCost;\n\t\t\t\tBufferItem nextItem = default(BufferItem);\n\t\t\t\tif ((nextLaneInfo.m_finalDirection & nextFinalDir) != 0) {\n\t\t\t\t\tif (nextLaneInfo.CheckType(allowedLaneTypes, allowedVehicleTypes) && (nextSegmentId != item.m_position.m_segment || i != item.m_position.m_lane) && (nextLaneInfo.m_finalDirection & nextFinalDir) != 0) {\n\t\t\t\t\t\tif (acuteTurningAngle && nextLaneInfo.m_laneType == NetInfo.LaneType.Vehicle && (nextLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) == VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tVector3 a = ((nextDir & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? netManager.m_lanes.m_buffer[curLaneId].m_bezier.a : netManager.m_lanes.m_buffer[curLaneId].m_bezier.d;\n\t\t\t\t\t\ttransitionCost = Vector3.Distance(a, b);\n\t\t\t\t\t\tif (transitionNode) {\n\t\t\t\t\t\t\ttransitionCost *= 2f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ticketCost != 0 && netManager.m_lanes.m_buffer[curLaneId].m_ticketCost != 0) {\n\t\t\t\t\t\t\ttransitionCost *= 10f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfloat transitionCostOverMeanMaxSpeed = transitionCost / ((prevMaxSpeed + nextLaneInfo.m_speedLimit) * 0.5f * m_maxLength);\n\t\t\t\t\t\tif (!this.m_stablePath && (netManager.m_lanes.m_buffer[curLaneId].m_flags & 0x80) != 0) {\n\t\t\t\t\t\t\tint firstTarget = netManager.m_lanes.m_buffer[curLaneId].m_firstTarget;\n\t\t\t\t\t\t\tint lastTarget = netManager.m_lanes.m_buffer[curLaneId].m_lastTarget;\n\t\t\t\t\t\t\ttransitionCostOverMeanMaxSpeed *= (float)new Randomizer(this.m_pathFindIndex ^ curLaneId).Int32(1000, (lastTarget - firstTarget + 2) * 1000) * 0.001f;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnextItem.m_position.m_segment = nextSegmentId;\n\t\t\t\t\t\tnextItem.m_position.m_lane = (byte)i;\n\t\t\t\t\t\tnextItem.m_position.m_offset = (byte)(((nextDir & NetInfo.Direction.Forward) != 0) ? 255 : 0);\n\t\t\t\t\t\tif ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {\n\t\t\t\t\t\t\tnextItem.m_methodDistance = 0f;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextItem.m_methodDistance = methodDistance + transitionCost;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < 1000f) && !m_stablePath) {\n\t\t\t\t\t\t\tgoto IL_09e6;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tnextItem.m_comparisonValue = comparisonValue + transitionCostOverMeanMaxSpeed;\n\t\t\t\t\t\tnextItem.m_duration = duration + transitionCost / ((prevMaxSpeed + nextLaneInfo.m_speedLimit) * 0.5f);\n\t\t\t\t\t\tnextItem.m_direction = nextDir;\n\n\t\t\t\t\t\tif (curLaneId == m_startLaneA) {\n\t\t\t\t\t\t\tif ((nextItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None && nextItem.m_position.m_offset >= m_startOffsetA) {\n\t\t\t\t\t\t\t\tgoto IL_06c5;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif ((nextItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None && nextItem.m_position.m_offset <= m_startOffsetA) {\n\t\t\t\t\t\t\t\tgoto IL_06c5;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tgoto IL_0765;\n\t\t\t\t\t}\n\t\t\t\t} else if ((nextLaneInfo.m_laneType & prevLaneType) != NetInfo.LaneType.None && (nextLaneInfo.m_vehicleType & prevVehicleType) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\tnewLaneIndexFromInner++;\n\t\t\t\t}\n\t\t\t\tgoto IL_09e6;\n\t\t\t\tIL_06c5:\n\t\t\t\tfloat nextLaneSpeed = CalculateLaneSpeed(m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);\n\t\t\t\tfloat nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextLaneSpeed * m_maxLength);\n\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextLaneSpeed;\n\t\t\t\tgoto IL_0765;\n\t\t\t\tIL_09e6:\n\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\tcontinue;\n\t\t\t\tIL_085e:\n\t\t\t\tif (!m_ignoreBlocked && (nextSegment.m_flags & NetSegment.Flags.Blocked) != NetSegment.Flags.None && (nextLaneInfo.m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {\n\t\t\t\t\tnextItem.m_comparisonValue += 0.1f;\n\t\t\t\t\tblocked = true;\n\t\t\t\t}\n\t\t\t\tnextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);\n\t\t\t\tnextItem.m_laneID = curLaneId;\n\t\t\t\tif ((nextLaneInfo.m_laneType & prevLaneType) != NetInfo.LaneType.None && (nextLaneInfo.m_vehicleType & m_vehicleTypes) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\tint firstTarget = netManager.m_lanes.m_buffer[curLaneId].m_firstTarget;\n\t\t\t\t\tint lastTarget = netManager.m_lanes.m_buffer[curLaneId].m_lastTarget;\n\t\t\t\t\tif (laneIndexFromInner < firstTarget || laneIndexFromInner >= lastTarget) {\n\t\t\t\t\t\tnextItem.m_comparisonValue += Mathf.Max(1f, transitionCost * 3f - 3f) / ((prevMaxSpeed + nextLaneInfo.m_speedLimit) * 0.5f * m_maxLength);\n\t\t\t\t\t}\n\t\t\t\t\tif (!m_transportVehicle && nextLaneInfo.m_laneType == NetInfo.LaneType.TransportVehicle) {\n\t\t\t\t\t\tnextItem.m_comparisonValue += 20f / ((prevMaxSpeed + nextLaneInfo.m_speedLimit) * 0.5f * m_maxLength);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tAddBufferItem(nextItem, item.m_position);\n\t\t\t\tgoto IL_09e6;\n\t\t\t\tIL_0765:\n\t\t\t\tif (curLaneId == m_startLaneB) {\n\t\t\t\t\tif ((nextItem.m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None && nextItem.m_position.m_offset >= m_startOffsetB) {\n\t\t\t\t\t\tgoto IL_07be;\n\t\t\t\t\t}\n\t\t\t\t\tif ((nextItem.m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None && nextItem.m_position.m_offset <= m_startOffsetB) {\n\t\t\t\t\t\tgoto IL_07be;\n\t\t\t\t\t}\n\t\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tgoto IL_085e;\n\t\t\t\tIL_07be:\n\t\t\t\tfloat nextLaneSpeed2 = CalculateLaneSpeed(m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);\n\t\t\t\tfloat nextOffset2 = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetB) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\t\t\t\tnextItem.m_comparisonValue += nextOffset2 * nextSegment.m_averageLength / (nextLaneSpeed2 * m_maxLength);\n\t\t\t\tnextItem.m_duration += nextOffset2 * nextSegment.m_averageLength / nextLaneSpeed2;\n\t\t\t\tgoto IL_085e;\n\t\t\t}\n\t\t\tlaneIndexFromInner = newLaneIndexFromInner;\n\t\t\treturn blocked;\n\t\t}\n\n\t\t// 4\n\t\tprivate void ProcessItemPedBicycle(BufferItem item, ushort nextNodeId, ushort nextSegmentId, ref NetSegment nextSegment, byte connectOffset, byte laneSwitchOffset, int nextLaneIndex, uint nextLaneId) {\n\t\t\tif ((nextSegment.m_flags & m_disableMask) != NetSegment.Flags.None) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tNetInfo nextSegmentInfo = nextSegment.Info;\n\t\t\tNetInfo prevSegmentInfo = netManager.m_segments.m_buffer[item.m_position.m_segment].Info;\n\t\t\tint nextNumLanes = nextSegmentInfo.m_lanes.Length;\n\t\t\tfloat distance;\n\t\t\tbyte offset;\n\t\t\tif (nextSegmentId == item.m_position.m_segment) {\n\t\t\t\tVector3 b = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)(int)laneSwitchOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\t\tVector3 a = netManager.m_lanes.m_buffer[nextLaneId].CalculatePosition((float)(int)connectOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\t\tdistance = Vector3.Distance(a, b);\n\t\t\t\toffset = connectOffset;\n\t\t\t} else {\n\t\t\t\tNetInfo.Direction direction = (NetInfo.Direction)((nextNodeId != nextSegment.m_startNode) ? 1 : 2);\n\t\t\t\tVector3 b = netManager.m_lanes.m_buffer[item.m_laneID].CalculatePosition((float)(int)laneSwitchOffset * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR);\n\t\t\t\tVector3 a = ((direction & NetInfo.Direction.Forward) == NetInfo.Direction.None) ? netManager.m_lanes.m_buffer[nextLaneId].m_bezier.a : netManager.m_lanes.m_buffer[nextLaneId].m_bezier.d;\n\t\t\t\tdistance = Vector3.Distance(a, b);\n\t\t\t\toffset = (byte)(((direction & NetInfo.Direction.Forward) != 0) ? 255 : 0);\n\t\t\t}\n\n\t\t\tfloat prevMaxSpeed = 1f;\n\t\t\tfloat prevSpeed = 1f;\n\t\t\tNetInfo.LaneType prevLaneType = NetInfo.LaneType.None;\n\t\t\tif (item.m_position.m_lane < prevSegmentInfo.m_lanes.Length) {\n\t\t\t\tNetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[item.m_position.m_lane];\n\t\t\t\tprevMaxSpeed = prevLaneInfo.m_speedLimit;\n\t\t\t\tprevLaneType = prevLaneInfo.m_laneType;\n\t\t\t\tif ((prevLaneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None) {\n\t\t\t\t\tprevLaneType = (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle);\n\t\t\t\t}\n\t\t\t\tprevSpeed = CalculateLaneSpeed(laneSwitchOffset, item.m_position.m_offset, ref netManager.m_segments.m_buffer[item.m_position.m_segment], prevLaneInfo);\n\t\t\t}\n\t\t\tfloat prevLength = (prevLaneType != NetInfo.LaneType.PublicTransport) ? netManager.m_segments.m_buffer[item.m_position.m_segment].m_averageLength : netManager.m_lanes.m_buffer[item.m_laneID].m_length;\n\t\t\tfloat offsetLength = (float)Mathf.Abs(laneSwitchOffset - item.m_position.m_offset) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR * prevLength;\n\t\t\tfloat methodDistance = item.m_methodDistance + offsetLength;\n\t\t\tfloat comparisonValue = item.m_comparisonValue + offsetLength / (prevSpeed * m_maxLength);\n\t\t\tfloat duration = item.m_duration + offsetLength / prevMaxSpeed;\n\n\t\t\tif (!m_ignoreCost) {\n\t\t\t\tint ticketCost = netManager.m_lanes.m_buffer[item.m_laneID].m_ticketCost;\n\t\t\t\tif (ticketCost != 0) {\n\t\t\t\t\tcomparisonValue += (float)(ticketCost * m_pathRandomizer.Int32(2000u)) * TICKET_COST_CONVERSION_FACTOR;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (nextLaneIndex < nextNumLanes) {\n\t\t\t\tNetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];\n\t\t\t\tBufferItem nextItem = default(BufferItem);\n\n\t\t\t\tnextItem.m_position.m_segment = nextSegmentId;\n\t\t\t\tnextItem.m_position.m_lane = (byte)nextLaneIndex;\n\t\t\t\tnextItem.m_position.m_offset = offset;\n\n\t\t\t\tif ((nextLaneInfo.m_laneType & prevLaneType) == NetInfo.LaneType.None) {\n\t\t\t\t\tnextItem.m_methodDistance = 0f;\n\t\t\t\t} else {\n\t\t\t\t\tif (item.m_methodDistance == 0f) {\n\t\t\t\t\t\tcomparisonValue += 100f / (0.25f * m_maxLength);\n\t\t\t\t\t}\n\t\t\t\t\tnextItem.m_methodDistance = methodDistance + distance;\n\t\t\t\t}\n\n\t\t\t\tif (nextLaneInfo.m_laneType == NetInfo.LaneType.Pedestrian && !(nextItem.m_methodDistance < 1000f) && !m_stablePath) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tnextItem.m_comparisonValue = comparisonValue + distance / ((prevMaxSpeed + nextLaneInfo.m_speedLimit) * 0.25f * m_maxLength);\n\t\t\t\tnextItem.m_duration = duration + distance / ((prevMaxSpeed + nextLaneInfo.m_speedLimit) * 0.5f);\n\t\t\t\tif ((nextSegment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\t\tnextItem.m_direction = NetInfo.InvertDirection(nextLaneInfo.m_finalDirection);\n\t\t\t\t} else {\n\t\t\t\t\tnextItem.m_direction = nextLaneInfo.m_finalDirection;\n\t\t\t\t}\n\n\t\t\t\tif (nextLaneId == m_startLaneA) {\n\t\t\t\t\tif ((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetA) {\n\t\t\t\t\t\tif ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (nextItem.m_position.m_offset > m_startOffsetA) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfloat nextSpeed = CalculateLaneSpeed(m_startOffsetA, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);\n\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetA) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\n\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);\n\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;\n\t\t\t\t}\n\n\t\t\t\tif (nextLaneId == m_startLaneB) {\n\t\t\t\t\tif ((nextItem.m_direction & NetInfo.Direction.Forward) == NetInfo.Direction.None || nextItem.m_position.m_offset < m_startOffsetB) {\n\t\t\t\t\t\tif ((nextItem.m_direction & NetInfo.Direction.Backward) == NetInfo.Direction.None) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (nextItem.m_position.m_offset > m_startOffsetB) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfloat nextSpeed = CalculateLaneSpeed(m_startOffsetB, nextItem.m_position.m_offset, ref nextSegment, nextLaneInfo);\n\t\t\t\t\tfloat nextOffset = (float)Mathf.Abs(nextItem.m_position.m_offset - m_startOffsetB) * BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR;\n\n\t\t\t\t\tnextItem.m_comparisonValue += nextOffset * nextSegment.m_averageLength / (nextSpeed * m_maxLength);\n\t\t\t\t\tnextItem.m_duration += nextOffset * nextSegment.m_averageLength / nextSpeed;\n\t\t\t\t}\n\n\t\t\t\tnextItem.m_laneID = nextLaneId;\n\t\t\t\tnextItem.m_lanesUsed = (item.m_lanesUsed | nextLaneInfo.m_laneType);\n\n\t\t\t\tAddBufferItem(nextItem, item.m_position);\n\t\t\t}\n\t\t}\n\n\t\tprivate void AddBufferItem(BufferItem item, PathUnit.Position target) {\n\t\t\tuint laneLocation = m_laneLocation[item.m_laneID];\n\t\t\tuint locPathFindIndex = laneLocation >> 16; // upper 16 bit, expected (?) path find index\n\t\t\tint bufferIndex = (int)(laneLocation & 65535u); // lower 16 bit\n\t\t\tint comparisonBufferPos;\n\n\t\t\tif (locPathFindIndex == m_pathFindIndex) {\n\t\t\t\tif (item.m_comparisonValue >= m_buffer[bufferIndex].m_comparisonValue) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tint bufferPosIndex = bufferIndex >> 6; // arithmetic shift (sign stays), upper 10 bit\n\t\t\t\tint bufferPos = bufferIndex & -64; // upper 10 bit (no shift)\n\t\t\t\tif (bufferPosIndex < m_bufferMinPos || (bufferPosIndex == m_bufferMinPos && bufferPos < m_bufferMin[bufferPosIndex])) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcomparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);\n\t\t\t\tif (comparisonBufferPos == bufferPosIndex) {\n\t\t\t\t\tm_buffer[bufferIndex] = item;\n\t\t\t\t\tm_laneTarget[item.m_laneID] = target;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tint newBufferIndex = bufferPosIndex << 6 | m_bufferMax[bufferPosIndex]--;\n\t\t\t\tBufferItem bufferItem = m_buffer[newBufferIndex];\n\t\t\t\tm_laneLocation[bufferItem.m_laneID] = laneLocation;\n\t\t\t\tm_buffer[bufferIndex] = bufferItem;\n\t\t\t} else {\n\t\t\t\tcomparisonBufferPos = Mathf.Max(Mathf.RoundToInt(item.m_comparisonValue * 1024f), m_bufferMinPos);\n\t\t\t}\n\n\t\t\tif (comparisonBufferPos >= 1024 || comparisonBufferPos < 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\twhile (m_bufferMax[comparisonBufferPos] == 63) {\n\t\t\t\tcomparisonBufferPos++;\n\t\t\t\tif (comparisonBufferPos == 1024) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (comparisonBufferPos > m_bufferMaxPos) {\n\t\t\t\tm_bufferMaxPos = comparisonBufferPos;\n\t\t\t}\n\n\t\t\tbufferIndex = (comparisonBufferPos << 6 | ++m_bufferMax[comparisonBufferPos]);\n\t\t\tm_buffer[bufferIndex] = item;\n\t\t\tm_laneLocation[item.m_laneID] = (uint)((int)(m_pathFindIndex << 16) | bufferIndex);\n\t\t\tm_laneTarget[item.m_laneID] = target;\n\t\t}\n\n\t\tprivate float CalculateLaneSpeed(byte startOffset, byte endOffset, ref NetSegment segment, NetInfo.Lane laneInfo) {\n\t\t\tNetInfo.Direction direction = ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? laneInfo.m_finalDirection : NetInfo.InvertDirection(laneInfo.m_finalDirection);\n\t\t\tif ((direction & NetInfo.Direction.Avoid) != 0) {\n\t\t\t\tif (endOffset > startOffset && direction == NetInfo.Direction.AvoidForward) {\n\t\t\t\t\treturn laneInfo.m_speedLimit * 0.1f;\n\t\t\t\t}\n\t\t\t\tif (endOffset < startOffset && direction == NetInfo.Direction.AvoidBackward) {\n\t\t\t\t\treturn laneInfo.m_speedLimit * 0.1f;\n\t\t\t\t}\n\t\t\t\treturn laneInfo.m_speedLimit * 0.2f;\n\t\t\t}\n\t\t\treturn laneInfo.m_speedLimit;\n\t\t}\n\n\t\tprivate void GetLaneDirection(PathUnit.Position pathPos, out NetInfo.Direction direction, out NetInfo.LaneType type) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tNetInfo info = netManager.m_segments.m_buffer[pathPos.m_segment].Info;\n\t\t\tif (info.m_lanes.Length > pathPos.m_lane) {\n\t\t\t\tdirection = info.m_lanes[pathPos.m_lane].m_finalDirection;\n\t\t\t\ttype = info.m_lanes[pathPos.m_lane].m_laneType;\n\t\t\t\tif ((netManager.m_segments.m_buffer[pathPos.m_segment].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\t\tdirection = NetInfo.InvertDirection(direction);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdirection = NetInfo.Direction.None;\n\t\t\t\ttype = NetInfo.LaneType.None;\n\t\t\t}\n\t\t}\n\n\t\tprivate void PathFindThread() {\n\t\t\twhile (true) {\n\t\t\t\tif (Monitor.TryEnter(m_queueLock, SimulationManager.SYNCHRONIZE_TIMEOUT)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\twhile (m_queueFirst == 0 && !m_terminated) {\n\t\t\t\t\t\t\tMonitor.Wait(m_queueLock);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!m_terminated) {\n\t\t\t\t\t\t\tm_calculating = m_queueFirst;\n\t\t\t\t\t\t\tm_queueFirst = m_pathUnits.m_buffer[m_calculating].m_nextPathUnit;\n\t\t\t\t\t\t\tif (m_queueFirst == 0) {\n\t\t\t\t\t\t\t\tm_queueLast = 0u;\n\t\t\t\t\t\t\t\tm_queuedPathFindCount = 0;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tm_queuedPathFindCount--;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[m_calculating].m_nextPathUnit = 0u;\n\t\t\t\t\t\t\tm_pathUnits.m_buffer[m_calculating].m_pathFindFlags = (byte)((m_pathUnits.m_buffer[m_calculating].m_pathFindFlags & -2) | 2);\n\t\t\t\t\t\t\tgoto end_IL_001a;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t\tend_IL_001a:;\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tMonitor.Exit(m_queueLock);\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tm_pathfindProfiler.BeginStep();\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tPathFindImplementation(m_calculating, ref m_pathUnits.m_buffer[m_calculating]);\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tm_pathfindProfiler.EndStep();\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tUIView.ForwardException(ex);\n\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Path find error: \" + ex.Message + \"\\n\" + ex.StackTrace);\n\t\t\t\t\t\tm_pathUnits.m_buffer[m_calculating].m_pathFindFlags |= 8;\n\t\t\t\t\t}\n\t\t\t\t\twhile (!Monitor.TryEnter(m_queueLock, SimulationManager.SYNCHRONIZE_TIMEOUT)) {\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tm_pathUnits.m_buffer[m_calculating].m_pathFindFlags = (byte)(m_pathUnits.m_buffer[m_calculating].m_pathFindFlags & -3);\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(m_calculating);\n\t\t\t\t\t\tm_calculating = 0u;\n\t\t\t\t\t\tMonitor.Pulse(m_queueLock);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tMonitor.Exit(m_queueLock);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Custom/README.md",
    "content": "# TM:PE -- /Custom\nEverything that is being detoured lands here.\n## Classes\n*none*"
  },
  {
    "path": "TLM/TLM/Geometry/GeometryCalculationMode.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Geometry {\n\tpublic enum GeometryCalculationMode {\n\t\tInit,\n\t\tPropagate,\n\t\tNoPropagate\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Geometry/ISegmentEndId.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Geometry {\n\tpublic interface ISegmentEndId : IEquatable<ISegmentEndId> {\n\t\t// TODO documentation\n\t\tushort SegmentId { get; }\n\t\tbool StartNode { get; }\n\n\t\tbool Relocate(ushort segmentId, bool startNode);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Geometry/Impl/NodeGeometry.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Threading;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Util;\n\nnamespace TrafficManager.Geometry.Impl {\n\tpublic class NodeGeometry : IEquatable<NodeGeometry> {\n\t\tpublic struct SegmentEndReplacement {\n\t\t\tpublic ISegmentEndId oldSegmentEndId;\n\t\t\tpublic ISegmentEndId newSegmentEndId;\n\n\t\t\tpublic override string ToString() {\n\t\t\t\treturn $\"[SegmentEndReplacement\\n\" +\n\t\t\t\t\t\"\\t\" + $\"oldSegmentEndId = {oldSegmentEndId}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"newSegmentEndId = {newSegmentEndId}\\n\" +\n\t\t\t\t\t\"SegmentEndReplacement]\";\n\t\t\t}\n\n\t\t\tpublic bool IsDefined() {\n\t\t\t\treturn oldSegmentEndId != null && newSegmentEndId != null;\n\t\t\t}\n\t\t}\n\n\t\tprivate const byte MAX_NUM_SEGMENTS = 8;\n\n\t\tprivate static NodeGeometry[] nodeGeometries;\n\n\t\tpublic static void PrintDebugInfo() {\n\t\t\tstring buf = \n\t\t\t\"-----------------------\\n\" +\n\t\t\t\"--- NODE GEOMETRIES ---\\n\" +\n\t\t\t\"-----------------------\";\n\t\t\tbuf += $\"Total: {nodeGeometries.Length}\\n\";\n\t\t\tforeach (NodeGeometry nodeGeo in nodeGeometries) {\n\t\t\t\tif (nodeGeo.IsValid()) {\n\t\t\t\t\tbuf += nodeGeo.ToString() + \"\\n\" +\n\t\t\t\t\t\"-------------------------\\n\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tLog.Info(buf);\n\t\t}\n\n\t\tpublic ushort NodeId {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic bool IsSimpleJunction {\n\t\t\tget {\n\t\t\t\treturn IncomingSegments == 1 || OutgoingSegments == 1;\n\t\t\t}\n\t\t}\n\n\t\tprivate ISegmentEndId lastRemovedSegmentEndId = null;\n\n\t\tpublic int IncomingSegments { get; private set; } = 0;\n\t\tpublic int OutgoingSegments { get; private set; } = 0;\n\n\t\tpublic SegmentEndReplacement CurrentSegmentReplacement = default(SegmentEndReplacement);\n\n\t\t/// <summary>\n\t\t/// Connected segment end geometries.\n\t\t/// WARNING: Individual entries may be null\n\t\t/// </summary>\n\t\tpublic SegmentEndGeometry[] SegmentEndGeometries {\n\t\t\tget; private set;\n\t\t} = new SegmentEndGeometry[MAX_NUM_SEGMENTS];\n\n\t\tpublic byte NumSegmentEnds { get; private set; } = 0;\n\n\t\t/// <summary>\n\t\t/// Holds a list of observers which are being notified as soon as the managed node's geometry is updated (but not neccessarily modified)\n\t\t/// </summary>\n\t\t//private List<IObserver<NodeGeometry>> observers = new List<IObserver<NodeGeometry>>();\n\n\t\t/// <summary>\n\t\t/// Lock object. Acquire this before accessing the HashSets.\n\t\t/// </summary>\n\t\t//public readonly object Lock = new object();\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[NodeGeometry ({NodeId})\\n\" +\n\t\t\t\t\"\\t\" + $\"IsValid() = {IsValid()}\\n\" +\n\t\t\t\t\"\\t\" + $\"IsSimpleJunction = {IsSimpleJunction}\\n\" +\n\t\t\t\t\"\\t\" + $\"IncomingSegments = {IncomingSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"OutgoingSegments = {OutgoingSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"SegmentEndGeometries = {SegmentEndGeometries.ArrayToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumSegmentEnds = {NumSegmentEnds}\\n\" +\n\t\t\t\t\"NodeGeometry]\";\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"nodeId\">id of the managed node</param>\n\t\tpublic NodeGeometry(ushort nodeId) {\n\t\t\tthis.NodeId = nodeId;\n\t\t}\n\n\t\tpublic bool IsValid() {\n\t\t\treturn Constants.ServiceFactory.NetService.IsNodeValid(NodeId);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Registers an observer.\n\t\t/// </summary>\n\t\t/// <param name=\"observer\"></param>\n\t\t/// <returns>An unsubscriber</returns>\n\t\t/*public IDisposable Subscribe(IObserver<NodeGeometry> observer) {\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(Lock);\n\t\t\t\tobservers.Add(observer);\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(Lock);\n\t\t\t}\n\t\t\treturn new GenericUnsubscriber<NodeGeometry>(observers, observer, Lock);\n\t\t}*/\n\t\t\n\t\tinternal void AddSegmentEnd(SegmentEndGeometry segEndGeo, GeometryCalculationMode calcMode) {\n#if DEBUGGEO\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\tLog._Debug($\">>> NodeGeometry: Add segment end {segEndGeo.SegmentId}, start? {segEndGeo.StartNode} @ node {NodeId}\");\n#endif\n\t\t\tif (!IsValid()) {\n\t\t\t\t//Log.Error($\"NodeGeometry: Trying to add segment {segmentId} @ invalid node {NodeId}\");\n\t\t\t\tInvalidate();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbool found = false;\n\t\t\tint freeIndex = -1;\n\t\t\tfor (int i = 0; i < MAX_NUM_SEGMENTS; ++i) {\n\t\t\t\tSegmentEndGeometry storedEndGeo = SegmentEndGeometries[i];\n\t\t\t\tif (segEndGeo.Equals(storedEndGeo)) {\n\t\t\t\t\tSegmentEndGeometries[i] = segEndGeo;\n\t\t\t\t\tfound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t} else if (storedEndGeo == null && freeIndex < 0) {\n\t\t\t\t\tfreeIndex = i;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!found) {\n\t\t\t\tif (freeIndex >= 0) {\n\t\t\t\t\tSegmentEndGeometries[freeIndex] = segEndGeo;\n\t\t\t\t} else {\n\t\t\t\t\tLog.Error($\"NodeGeometry.AddSegmentEnd: Detected inconsistency. Unable to add segment end {segEndGeo} to node {NodeId}. Maximum segment end capacity reached.\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (calcMode == GeometryCalculationMode.Propagate) {\n\t\t\t\tRecalculateSegments(segEndGeo.SegmentId);\n\t\t\t}\n\n\t\t\tif (!found && lastRemovedSegmentEndId != null) {\n\t\t\t\tCurrentSegmentReplacement.oldSegmentEndId = lastRemovedSegmentEndId;\n\t\t\t\tCurrentSegmentReplacement.newSegmentEndId = segEndGeo;\n\t\t\t\tlastRemovedSegmentEndId = null;\n\t\t\t}\n\t\t\tRecalculate();\n\t\t}\n\n\t\tinternal void RemoveSegmentEnd(SegmentEndGeometry segmentEndGeo, GeometryCalculationMode calcMode) {\n#if DEBUGGEO\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\tLog._Debug($\">>> NodeGeometry: Remove segment end {segmentEndGeo.SegmentId} @ {NodeId}, calcMode? {calcMode}\");\n#endif\n\n\t\t\tif (calcMode == GeometryCalculationMode.Init) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!IsValid()) {\n\t\t\t\t//Log.Warning($\"NodeGeometry: Trying to remove segment {segmentId} @ invalid node {NodeId}\");\n\t\t\t\tInvalidate();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < MAX_NUM_SEGMENTS; ++i) {\n\t\t\t\tif (segmentEndGeo.Equals(SegmentEndGeometries[i])) {\n\t\t\t\t\tSegmentEndGeometries[i] = null;\n\t\t\t\t\tlastRemovedSegmentEndId = segmentEndGeo;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (calcMode == GeometryCalculationMode.Propagate) {\n\t\t\t\tRecalculateSegments(segmentEndGeo.SegmentId);\n\t\t\t}\n\t\t\tRecalculate();\n\t\t}\n\n\t\tprivate void Cleanup() {\n\t\t\tIncomingSegments = 0;\n\t\t\tOutgoingSegments = 0;\n\t\t\tNumSegmentEnds = 0;\n\t\t}\n\n\t\tinternal void RecalculateSegments(ushort? ignoreSegmentId= null) {\n#if DEBUGGEO\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\tLog._Debug($\"NodeGeometry: Propagate @ {NodeId}. ignoreSegmentId={ignoreSegmentId}\");\n#endif\n\n\t\t\t// recalculate (other) segments\n\t\t\tfor (int i = 0; i < MAX_NUM_SEGMENTS; ++i) {\n\t\t\t\tif (SegmentEndGeometries[i] == null)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (ignoreSegmentId != null && SegmentEndGeometries[i].SegmentId == ignoreSegmentId)\n\t\t\t\t\tcontinue;\n#if DEBUGGEO\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\tLog._Debug($\"NodeGeometry: Recalculating segment {SegmentEndGeometries[i].SegmentId} @ {NodeId}\");\n#endif\n\t\t\t\tSegmentEndGeometries[i].GetSegmentGeometry(true).StartRecalculation(GeometryCalculationMode.NoPropagate);\n\t\t\t}\n\t\t}\n\n\t\tinternal void Recalculate() {\n#if DEBUGGEO\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\tLog._Debug($\">>> NodeGeometry: Recalculate @ {NodeId}\");\n#endif\n\n\t\t\tCleanup();\n\n\t\t\t// check if node is valid\n\t\t\tif (!IsValid()) {\n\t\t\t\tInvalidate();\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\t// calculate node properties\n\t\t\t\tbyte incomingSegments = 0;\n\t\t\t\tbyte outgoingSegments = 0;\n\t\t\t\tfor (int i = 0; i < MAX_NUM_SEGMENTS; ++i) {\n\t\t\t\t\tif (SegmentEndGeometries[i] == null)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t++NumSegmentEnds;\n#if DEBUGGEO\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\t\tLog._Debug($\"NodeGeometry.Recalculate: Iterating over segment end {SegmentEndGeometries[i].SegmentId} @ node {NodeId}\");\n#endif\n\n\t\t\t\t\tbool startNode = SegmentEndGeometries[i].StartNode;\n\t\t\t\t\tif (SegmentEndGeometries[i].GetSegmentGeometry(true).IsIncoming(startNode))\n\t\t\t\t\t\t++incomingSegments;\n\t\t\t\t\tif (SegmentEndGeometries[i].GetSegmentGeometry(true).IsOutgoing(startNode))\n\t\t\t\t\t\t++outgoingSegments;\n\t\t\t\t}\n\n\t\t\t\tIncomingSegments = incomingSegments;\n\t\t\t\tOutgoingSegments = outgoingSegments;\n#if DEBUGGEO\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\tLog._Debug($\"NodeGeometry.Recalculate: Node {NodeId} has {incomingSegments} incoming and {outgoingSegments} outgoing segments.\");\n#endif\n\t\t\t\tNotifyGeomentryManager();\n\t\t\t}\n\t\t}\n\n\t\tprotected void Invalidate() {\n\t\t\tfor (int i = 0; i < MAX_NUM_SEGMENTS; ++i) {\n\t\t\t\tSegmentEndGeometries[i] = null;\n\t\t\t}\n\t\t\tlastRemovedSegmentEndId = null;\n\t\t\tCurrentSegmentReplacement = default(SegmentEndReplacement);\n\t\t\tNotifyGeomentryManager();\n\t\t}\n\n\t\tpublic bool Equals(NodeGeometry otherNodeGeo) {\n\t\t\tif (otherNodeGeo == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn NodeId == otherNodeGeo.NodeId;\n\t\t}\n\n\t\tpublic override bool Equals(object other) {\n\t\t\tif (other == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!(other is NodeGeometry)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn Equals((NodeGeometry)other);\n\t\t}\n\n\t\tpublic override int GetHashCode() {\n\t\t\tint prime = 31;\n\t\t\tint result = 1;\n\t\t\tresult = prime * result + NodeId.GetHashCode();\n\t\t\treturn result;\n\t\t}\n\n\t\tprivate void NotifyGeomentryManager() {\n\t\t\tif (CurrentSegmentReplacement.IsDefined()) {\n\t\t\t\tConstants.ManagerFactory.GeometryManager.OnSegmentEndReplacement(CurrentSegmentReplacement);\n\t\t\t}\n\n\t\t\tCurrentSegmentReplacement.oldSegmentEndId = null;\n\t\t\tCurrentSegmentReplacement.newSegmentEndId = null;\n\t\t}\n\n\t\t// static methods\n\n\t\tinternal static void OnBeforeLoadData() {\n\t\t\tnodeGeometries = new NodeGeometry[NetManager.MAX_NODE_COUNT];\n#if DEBUGGEO\n\t\t\tLog._Debug($\"Building {nodeGeometries.Length} node geometries...\");\n#endif\n\t\t\tfor (int i = 0; i < nodeGeometries.Length; ++i) {\n\t\t\t\tnodeGeometries[i] = new NodeGeometry((ushort)i);\n\t\t\t}\n#if DEBUGGEO\n\t\t\tLog._Debug($\"Built node geometries.\");\n#endif\n\t\t}\n\n\t\tpublic static NodeGeometry Get(ushort nodeId) {\n\t\t\tif (nodeGeometries == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn nodeGeometries[nodeId];\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Geometry/Impl/SegmentEndGeometry.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing GenericGameBridge.Service;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Util;\nusing UnityEngine;\n\nnamespace TrafficManager.Geometry.Impl {\n\tpublic class SegmentEndGeometry : SegmentEndId {\n\t\tpublic ushort NodeId() {\n\t\t\treturn Constants.ServiceFactory.NetService.GetSegmentNodeId(SegmentId, StartNode);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// last known connected node\n\t\t/// </summary>\n\t\tpublic ushort LastKnownNodeId {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic ushort[] ConnectedSegments {\n\t\t\tget; private set;\n\t\t} = new ushort[7];\n\n\t\tpublic byte NumConnectedSegments {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic byte NumIncomingSegments {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic byte NumOutgoingSegments {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic ushort[] LeftSegments {\n\t\t\tget; private set;\n\t\t} = new ushort[7];\n\n\t\tpublic byte NumLeftSegments {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic ushort[] IncomingLeftSegments {\n\t\t\tget; private set;\n\t\t} = new ushort[7];\n\n\t\tpublic byte NumIncomingLeftSegments {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic ushort[] OutgoingLeftSegments {\n\t\t\tget; private set;\n\t\t} = new ushort[7];\n\n\t\tpublic byte NumOutgoingLeftSegments {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic ushort[] RightSegments {\n\t\t\tget; private set;\n\t\t} = new ushort[7];\n\n\t\tpublic byte NumRightSegments {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic ushort[] IncomingRightSegments {\n\t\t\tget; private set;\n\t\t} = new ushort[7];\n\n\t\tpublic byte NumIncomingRightSegments {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic ushort[] OutgoingRightSegments {\n\t\t\tget; private set;\n\t\t} = new ushort[7];\n\n\t\tpublic byte NumOutgoingRightSegments {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic ushort[] StraightSegments {\n\t\t\tget; private set;\n\t\t} = new ushort[7];\n\n\t\tpublic byte NumStraightSegments {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic ushort[] IncomingStraightSegments {\n\t\t\tget; private set;\n\t\t} = new ushort[7];\n\n\t\tpublic byte NumIncomingStraightSegments {\n\t\t\tget; private set;\n\t\t} = 0;\n\n\t\tpublic ushort[] OutgoingStraightSegments {\n\t\t\tget; private set;\n\t\t} = new ushort[7];\n\n\t\tpublic byte NumOutgoingStraightSegments {\n\t\t\tget; private set;\n\t\t} = 0;\n\t\t\n\t\t/// <summary>\n\t\t/// Indicates that the managed segment is only connected to highway segments at the start node\n\t\t/// </summary>\n\t\tpublic bool OnlyHighways {\n\t\t\tget; private set;\n\t\t} = false;\n\n\t\t/// <summary>\n\t\t/// Indicates that the managed segment is an outgoing one-way segment at start node. That means vehicles may come from the node.\n\t\t/// </summary>\n\t\tpublic bool OutgoingOneWay {\n\t\t\tget; private set;\n\t\t} = false;\n\n\t\t/// <summary>\n\t\t/// Indicates that the managed segment is an incoming one-way segment at start node. That means vehicles may go to the node.\n\t\t/// </summary>\n\t\tpublic bool IncomingOneWay {\n\t\t\tget; private set;\n\t\t} = false;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[SegmentEndGeometry {base.ToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NodeId() = {NodeId()}\\n\" +\n\t\t\t\t\"\\t\" + $\"IsValid() = {IsValid()}\\n\" +\n\t\t\t\t\"\\t\" + $\"LastKnownNodeId = {LastKnownNodeId}\\n\" +\n\t\t\t\t\"\\t\" + $\"ConnectedSegments = {ConnectedSegments.ArrayToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumConnectedSegments = {NumConnectedSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumIncomingSegments = {NumIncomingSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumOutgoingSegments = {NumOutgoingSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"LeftSegments = {LeftSegments.ArrayToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumLeftSegments = {NumLeftSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"IncomingLeftSegments = {IncomingLeftSegments.ArrayToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumIncomingLeftSegments = {NumIncomingLeftSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"OutgoingLeftSegments = {OutgoingLeftSegments.ArrayToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumOutgoingLeftSegments = {NumOutgoingLeftSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"RightSegments = {RightSegments.ArrayToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumRightSegments = {NumRightSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"IncomingRightSegments = {IncomingRightSegments.ArrayToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumIncomingRightSegments = {NumIncomingRightSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"OutgoingRightSegments = {OutgoingRightSegments.ArrayToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumOutgoingRightSegments = {NumOutgoingRightSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"StraightSegments = {StraightSegments.ArrayToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumStraightSegments = {NumStraightSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"IncomingStraightSegments = {IncomingStraightSegments.ArrayToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumIncomingStraightSegments = {NumIncomingStraightSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"OutgoingStraightSegments = {OutgoingStraightSegments.ArrayToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NumOutgoingStraightSegments = {NumOutgoingStraightSegments}\\n\" +\n\t\t\t\t\"\\t\" + $\"OnlyHighways = {OnlyHighways}\\n\" +\n\t\t\t\t\"\\t\" + $\"OutgoingOneWay = {OutgoingOneWay}\\n\" +\n\t\t\t\t\"\\t\" + $\"IncomingOneWay = {IncomingOneWay}\\n\" +\n\t\t\t\t\"\\t\" + $\"GetClockwiseIndex() = {GetClockwiseIndex()}\\n\" +\n\t\t\t\t\"SegmentEndGeometry]\";\n\t\t}\n\n\t\tpublic SegmentEndGeometry(ushort segmentId, bool startNode) : base(segmentId, startNode) {\n\n\t\t}\n\n\t\tpublic static SegmentEndGeometry Get(ISegmentEndId endId) {\n\t\t\treturn Get(endId.SegmentId, endId.StartNode);\n\t\t}\n\n\t\tpublic static SegmentEndGeometry Get(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentGeometry.Get(segmentId)?.GetEnd(startNode);\n\t\t}\n\n\t\tinternal void Cleanup() {\n\t\t\tfor (int i = 0; i < 7; ++i) {\n\t\t\t\tConnectedSegments[i] = 0;\n\n\t\t\t\tLeftSegments[i] = 0;\n\t\t\t\tIncomingLeftSegments[i] = 0;\n\t\t\t\tOutgoingLeftSegments[i] = 0;\n\n\t\t\t\tRightSegments[i] = 0;\n\t\t\t\tIncomingRightSegments[i] = 0;\n\t\t\t\tOutgoingRightSegments[i] = 0;\n\n\t\t\t\tStraightSegments[i] = 0;\n\t\t\t\tIncomingStraightSegments[i] = 0;\n\t\t\t\tOutgoingStraightSegments[i] = 0;\n\t\t\t}\n\t\t\tNumConnectedSegments = 0;\n\n\t\t\tNumLeftSegments = 0;\n\t\t\tNumIncomingLeftSegments = 0;\n\t\t\tNumOutgoingLeftSegments = 0;\n\n\t\t\tNumRightSegments = 0;\n\t\t\tNumIncomingRightSegments = 0;\n\t\t\tNumOutgoingRightSegments = 0;\n\n\t\t\tNumStraightSegments = 0;\n\t\t\tNumIncomingStraightSegments = 0;\n\t\t\tNumOutgoingStraightSegments = 0;\n\n\t\t\tNumIncomingSegments = 0;\n\t\t\tNumOutgoingSegments = 0;\n\n\t\t\tOnlyHighways = false;\n\t\t\tOutgoingOneWay = false;\n\t\t\tIncomingOneWay = false;\n\n\t\t\tLastKnownNodeId = 0;\n\t\t}\n\n\t\tpublic bool IsValid() {\n\t\t\tSegmentGeometry segGeo = GetSegmentGeometry();\n\t\t\tbool valid = segGeo != null && segGeo.IsValid();\n\t\t\treturn valid && NodeId() != 0;\n\t\t}\n\n\t\tpublic bool IsConnectedTo(ushort otherSegmentId) {\n\t\t\tif (! IsValid())\n\t\t\t\treturn false;\n\n\t\t\tforeach (ushort segId in ConnectedSegments)\n\t\t\t\tif (segId == otherSegmentId)\n\t\t\t\t\treturn true;\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic ushort[] GetIncomingSegments() {\n\t\t\tushort[] ret = new ushort[NumIncomingLeftSegments + NumIncomingRightSegments + NumIncomingStraightSegments];\n\t\t\tint i = 0;\n\n\t\t\tfor (int k = 0; k < 7; ++k) {\n\t\t\t\tif (IncomingLeftSegments[k] == 0)\n\t\t\t\t\tbreak;\n\t\t\t\tret[i++] = IncomingLeftSegments[k];\n\t\t\t}\n\n\t\t\tfor (int k = 0; k < 7; ++k) {\n\t\t\t\tif (IncomingStraightSegments[k] == 0)\n\t\t\t\t\tbreak;\n\t\t\t\tret[i++] = IncomingStraightSegments[k];\n\t\t\t}\n\n\t\t\tfor (int k = 0; k < 7; ++k) {\n\t\t\t\tif (IncomingRightSegments[k] == 0)\n\t\t\t\t\tbreak;\n\t\t\t\tret[i++] = IncomingRightSegments[k];\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic ushort[] GetOutgoingSegments() {\n\t\t\tushort[] ret = new ushort[NumOutgoingLeftSegments + NumOutgoingRightSegments + NumOutgoingStraightSegments];\n\t\t\tint i = 0;\n\n\t\t\tfor (int k = 0; k < 7; ++k) {\n\t\t\t\tif (OutgoingLeftSegments[k] == 0)\n\t\t\t\t\tbreak;\n\t\t\t\tret[i++] = OutgoingLeftSegments[k];\n\t\t\t}\n\n\t\t\tfor (int k = 0; k < 7; ++k) {\n\t\t\t\tif (OutgoingRightSegments[k] == 0)\n\t\t\t\t\tbreak;\n\t\t\t\tret[i++] = OutgoingRightSegments[k];\n\t\t\t}\n\n\t\t\tfor (int k = 0; k < 7; ++k) {\n\t\t\t\tif (OutgoingStraightSegments[k] == 0)\n\t\t\t\t\tbreak;\n\t\t\t\tret[i++] = OutgoingStraightSegments[k];\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic SegmentGeometry GetSegmentGeometry(bool ignoreInvalid=false) {\n\t\t\treturn SegmentGeometry.Get(SegmentId, ignoreInvalid);\n\t\t}\n\n\t\tpublic short GetClockwiseIndex() {\n\t\t\t// calculate clockwise index\n\t\t\tshort clockwiseIndex = -1;\n\t\t\tConstants.ServiceFactory.NetService.IterateNodeSegments(NodeId(), ClockDirection.Clockwise, delegate (ushort sId, ref NetSegment segment) {\n\t\t\t\t++clockwiseIndex;\n\t\t\t\t//Log._Debug($\"SegmentEndGeometry.Recalculate: Setting clockwise index of seg. {sId} to {clockwiseIndex} (we are @ seg. {SegmentId})\");\n\n\t\t\t\tif (sId == SegmentId) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\treturn clockwiseIndex;\n\t\t}\n\n\t\tinternal void Recalculate(GeometryCalculationMode calcMode) {\n#if DEBUGGEO\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\tLog._Debug($\">>> SegmentEndGeometry.Recalculate({calcMode}): seg. {SegmentId} @ node {NodeId()}\");\n#endif\n\n\t\t\tushort nodeIdBeforeRecalc = LastKnownNodeId;\n\t\t\tCleanup();\n\n\t\t\tif (!IsValid()) {\n\t\t\t\tif (calcMode == GeometryCalculationMode.Propagate && nodeIdBeforeRecalc != 0) {\n#if DEBUGGEO\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\t\tLog._Debug($\"SegmentEndGeometry.Recalculate({calcMode}): seg. {SegmentId} is not valid. nodeIdBeforeRecalc={nodeIdBeforeRecalc}. Removing segment from node.\");\n#endif\n\t\t\t\t\tNodeGeometry.Get(nodeIdBeforeRecalc).RemoveSegmentEnd(this, GeometryCalculationMode.Propagate);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t//NetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tushort nodeId = NodeId();\n\t\t\tLastKnownNodeId = nodeId;\n\n\t\t\tbool oneway;\n\t\t\tbool outgoingOneWay;\n\t\t\tSegmentGeometry.calculateOneWayAtNode(SegmentId, nodeId, out oneway, out outgoingOneWay);\n\t\t\tOutgoingOneWay = outgoingOneWay;\n\t\t\tif (oneway && ! OutgoingOneWay) {\n\t\t\t\tIncomingOneWay = true;\n\t\t\t}\n\t\t\tOnlyHighways = true;\n#if DEBUGGEO\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\tLog._Debug($\"Checking if segment {SegmentId} is connected to highways only at node {NodeId()}. OnlyHighways={OnlyHighways}\");\n#endif\n\n\t\t\t//ItemClass connectionClass = netManager.m_segments.m_buffer[SegmentId].Info.GetConnectionClass();\n\n\t\t\tushort firstClockwiseSegmentId = 0;\n\t\t\tbool hasOtherSegments = false;\n\t\t\tfor (var s = 0; s < 8; s++) {\n\t\t\t\tushort otherSegmentId = 0;\n\t\t\t\tConstants.ServiceFactory.NetService.ProcessNode(nodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\t\totherSegmentId = node.GetSegment(s);\n\t\t\t\t\tif (s == 0) {\n\t\t\t\t\t\tfirstClockwiseSegmentId = otherSegmentId;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t\tif (otherSegmentId == 0 || otherSegmentId == SegmentId || ! SegmentGeometry.IsValid(otherSegmentId))\n\t\t\t\t\tcontinue;\n\t\t\t\t/*ItemClass otherConnectionClass = Singleton<NetManager>.instance.m_segments.m_buffer[otherSegmentId].Info.GetConnectionClass();\n\t\t\t\tif (otherConnectionClass.m_service != connectionClass.m_service)\n\t\t\t\t\tcontinue;*/\n\t\t\t\thasOtherSegments = true;\n\n\t\t\t\t// determine geometry\n\t\t\t\tbool otherIsOneWay;\n\t\t\t\tbool otherIsOutgoingOneWay;\n\t\t\t\tSegmentGeometry.calculateOneWayAtNode(otherSegmentId, nodeId, out otherIsOneWay, out otherIsOutgoingOneWay);\n\t\t\t\tbool otherIsHighway = SegmentGeometry.calculateIsHighway(otherSegmentId);\n\n#if DEBUGGEO\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\tLog._Debug($\"Segment {SegmentId} is connected to segment {otherSegmentId} at node {NodeId()}. otherIsOneWay={otherIsOneWay} otherIsOutgoingOneWay={otherIsOutgoingOneWay} otherIsHighway={otherIsHighway}\");\n#endif\n\t\t\t\tif (! otherIsHighway || ! otherIsOneWay)\n\t\t\t\t\tOnlyHighways = false;\n\n\t\t\t\tArrowDirection dir = GetSegmentDir(SegmentId, otherSegmentId, nodeId);\n\t\t\t\tif (dir == ArrowDirection.Right) {\n\t\t\t\t\tRightSegments[NumRightSegments++] = otherSegmentId;\n\t\t\t\t\tif (!otherIsOutgoingOneWay) {\n\t\t\t\t\t\tIncomingRightSegments[NumIncomingRightSegments++] = otherSegmentId;\n\t\t\t\t\t\tif (!otherIsOneWay)\n\t\t\t\t\t\t\tOutgoingRightSegments[NumOutgoingRightSegments++] = otherSegmentId;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tOutgoingRightSegments[NumOutgoingRightSegments++] = otherSegmentId;\n\t\t\t\t\t}\n\t\t\t\t} else if (dir == ArrowDirection.Left) {\n\t\t\t\t\tLeftSegments[NumLeftSegments++] = otherSegmentId;\n\t\t\t\t\tif (!otherIsOutgoingOneWay) {\n\t\t\t\t\t\tIncomingLeftSegments[NumIncomingLeftSegments++] = otherSegmentId;\n\t\t\t\t\t\tif (!otherIsOneWay)\n\t\t\t\t\t\t\tOutgoingLeftSegments[NumOutgoingLeftSegments++] = otherSegmentId;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tOutgoingLeftSegments[NumOutgoingLeftSegments++] = otherSegmentId;\n\t\t\t\t\t}\n\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\tif (dir != ArrowDirection.Forward) {\n\t\t\t\t\t\tLog.Warning($\"Invalid segment direction detected: {dir} for segments {SegmentId} and {otherSegmentId}\");\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tStraightSegments[NumStraightSegments++] = otherSegmentId;\n\t\t\t\t\tif (!otherIsOutgoingOneWay) {\n\t\t\t\t\t\tIncomingStraightSegments[NumIncomingStraightSegments++] = otherSegmentId;\n\t\t\t\t\t\tif (!otherIsOneWay)\n\t\t\t\t\t\t\tOutgoingStraightSegments[NumOutgoingStraightSegments++] = otherSegmentId;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tOutgoingStraightSegments[NumOutgoingStraightSegments++] = otherSegmentId;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// reset highway lane arrows\n\t\t\t\t//Flags.removeHighwayLaneArrowFlagsAtSegment(otherSegmentId); // TODO refactor\n\n\t\t\t\tConnectedSegments[NumConnectedSegments++] = otherSegmentId;\n\t\t\t}\n\n\t\t\tNumIncomingSegments = (byte)(NumIncomingLeftSegments + NumIncomingStraightSegments + NumIncomingRightSegments);\n\t\t\tNumOutgoingSegments = (byte)(NumOutgoingLeftSegments + NumOutgoingStraightSegments + NumOutgoingRightSegments);\n\n\t\t\tif (!hasOtherSegments) {\n#if DEBUGGEO\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\tLog._Debug($\"Segment {SegmentId} is not connected to any other segments at node {NodeId()}.\");\n#endif\n\t\t\t\tOnlyHighways = false;\n\t\t\t}\n\n\t\t\t// propagate information to other segments\n\t\t\tif (calcMode == GeometryCalculationMode.Init || (calcMode == GeometryCalculationMode.Propagate && nodeIdBeforeRecalc != nodeId)) {\n\t\t\t\tif (calcMode == GeometryCalculationMode.Propagate && nodeIdBeforeRecalc != 0) {\n\t\t\t\t\tNodeGeometry.Get(nodeIdBeforeRecalc).RemoveSegmentEnd(this, GeometryCalculationMode.Propagate);\n\t\t\t\t}\n\n\t\t\t\tNodeGeometry.Get(nodeId).AddSegmentEnd(this, calcMode);\n\t\t\t}\n\t\t}\n\n\t\tpublic bool IsRightSegment(ushort toSegmentId) {\n\t\t\tbool contains = false;\n\t\t\tforeach (ushort segId in RightSegments)\n\t\t\t\tif (segId == toSegmentId) {\n\t\t\t\t\tcontains = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\treturn contains;\n\t\t}\n\n\t\tpublic bool IsLeftSegment(ushort toSegmentId) {\n\t\t\tbool contains = false;\n\t\t\tforeach (ushort segId in LeftSegments)\n\t\t\t\tif (segId == toSegmentId) {\n\t\t\t\t\tcontains = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\treturn contains;\n\t\t}\n\n\t\tpublic bool IsStraightSegment(ushort toSegmentId) {\n\t\t\tbool contains = false;\n\t\t\tforeach (ushort segId in StraightSegments)\n\t\t\t\tif (segId == toSegmentId) {\n\t\t\t\t\tcontains = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\treturn contains;\n\t\t}\n\n\t\tpublic ArrowDirection GetDirection(ushort otherSegmentId) {\n\t\t\tif (otherSegmentId == SegmentId) {\n\t\t\t\treturn ArrowDirection.Turn;\n\t\t\t}\n\n\t\t\tif (! IsConnectedTo(otherSegmentId)) {\n\t\t\t\treturn ArrowDirection.None;\n\t\t\t}\n\n\t\t\tif (otherSegmentId == SegmentId)\n\t\t\t\treturn ArrowDirection.Turn;\n\t\t\telse if (IsRightSegment(otherSegmentId))\n\t\t\t\treturn ArrowDirection.Right;\n\t\t\telse if (IsLeftSegment(otherSegmentId))\n\t\t\t\treturn ArrowDirection.Left;\n\t\t\telse if (IsStraightSegment(otherSegmentId))\n\t\t\t\treturn ArrowDirection.Forward;\n\t\t\treturn ArrowDirection.Turn;\n\t\t}\n\n\t\t// static methods\n\t\t[Obsolete]\n\t\tprivate static bool IsRightSegment(ushort fromSegment, ushort toSegment, ushort nodeid) {\n\t\t\tif (fromSegment <= 0 || toSegment <= 0)\n\t\t\t\treturn false;\n\n\t\t\treturn IsLeftSegment(toSegment, fromSegment, nodeid);\n\t\t}\n\n\t\t[Obsolete]\n\t\tprivate static bool IsLeftSegment(ushort fromSegment, ushort toSegment, ushort nodeid) {\n\t\t\tif (fromSegment <= 0 || toSegment <= 0)\n\t\t\t\treturn false;\n\n\t\t\tVector3 fromDir = GetSegmentDir(fromSegment, nodeid);\n\t\t\tfromDir.y = 0;\n\t\t\tfromDir.Normalize();\n\t\t\tVector3 toDir = GetSegmentDir(toSegment, nodeid);\n\t\t\ttoDir.y = 0;\n\t\t\ttoDir.Normalize();\n\t\t\treturn Vector3.Cross(fromDir, toDir).y >= 0.5;\n\t\t}\n\n\t\tprivate static ArrowDirection GetSegmentDir(ushort fromSegment, ushort toSegment, ushort nodeId) {\n\t\t\tif (fromSegment == 0 || toSegment == 0) {\n\t\t\t\treturn ArrowDirection.None;\n\t\t\t}\n\n\t\t\tVector3 fromDir = GetSegmentDir(fromSegment, nodeId);\n\t\t\tfromDir.y = 0;\n\t\t\tfromDir.Normalize();\n\t\t\tVector3 toDir = GetSegmentDir(toSegment, nodeId);\n\t\t\ttoDir.y = 0;\n\t\t\ttoDir.Normalize();\n\t\t\tfloat c = Vector3.Cross(fromDir, toDir).y;\n\t\t\t\n\t\t\tif (c >= 0.5f) { // [+30°, +150°]\n\t\t\t\treturn ArrowDirection.Left;\n\t\t\t} else if (c <= -0.5f) { // [-30°, -150°]\n\t\t\t\treturn ArrowDirection.Right;\n\t\t\t} else { // (-30°, +30°) / (-150°, -180°] / (+150°, +180°]\n\t\t\t\tfloat d = Vector3.Dot(fromDir, toDir);\n\t\t\t\tif (d > 0) { // (-30°, +30°)\n\t\t\t\t\tif (c > 0) { // (0°, 30°]\n\t\t\t\t\t\treturn ArrowDirection.Left;\n\t\t\t\t\t} else if (c < 0) { // (0°, -30°]\n\t\t\t\t\t\treturn ArrowDirection.Right;\n\t\t\t\t\t} else { // [0°]\n\t\t\t\t\t\treturn ArrowDirection.Turn;\n\t\t\t\t\t}\n\t\t\t\t} else { // (-150°, -180°] / (+150°, +180°]\n\t\t\t\t\treturn ArrowDirection.Forward;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static Vector3 GetSegmentDir(int segment, ushort nodeid) {\n\t\t\tvar instance = Singleton<NetManager>.instance;\n\n\t\t\tVector3 dir;\n\n\t\t\tdir = instance.m_segments.m_buffer[segment].m_startNode == nodeid ?\n\t\t\t\tinstance.m_segments.m_buffer[segment].m_startDirection :\n\t\t\t\tinstance.m_segments.m_buffer[segment].m_endDirection;\n\n\t\t\treturn dir;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Geometry/Impl/SegmentEndId.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Geometry.Impl {\n\tpublic class SegmentEndId : ISegmentEndId {\n\t\tpublic virtual ushort SegmentId { get; protected set; } = 0;\n\t\tpublic virtual bool StartNode { get; protected set; } = false;\n\n\t\tpublic SegmentEndId(ushort segmentId, bool startNode) {\n\t\t\tSegmentId = segmentId;\n\t\t\tStartNode = startNode;\n\t\t}\n\n\t\tprivate SegmentEndId() {\n\n\t\t}\n\n\t\tpublic virtual bool Relocate(ushort segmentId, bool startNode) {\n\t\t\tSegmentId = segmentId;\n\t\t\tStartNode = startNode;\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic override bool Equals(object other) {\n\t\t\tif (other == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!(other is ISegmentEndId)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn Equals((ISegmentEndId)other);\n\t\t}\n\n\t\tpublic bool Equals(ISegmentEndId otherSegEndId) {\n\t\t\tif (otherSegEndId == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn SegmentId == otherSegEndId.SegmentId && StartNode == otherSegEndId.StartNode;\n\t\t}\n\n\t\tpublic override int GetHashCode() {\n\t\t\tint prime = 31;\n\t\t\tint result = 1;\n\t\t\tresult = prime * result + SegmentId.GetHashCode();\n\t\t\tresult = prime * result + StartNode.GetHashCode();\n\t\t\treturn result;\n\t\t}\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[SegmentEndId {SegmentId} @ {StartNode}]\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Geometry/Impl/SegmentGeometry.cs",
    "content": "﻿using ColossalFramework;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Threading;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Util;\nusing UnityEngine;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager;\nusing System.Linq;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\n\nnamespace TrafficManager.Geometry.Impl {\n\t/// <summary>\n\t/// Manages segment geometry data (e.g. if a segment is one-way or not, which incoming/outgoing segments are connected at the start or end node) of one specific segment.\n\t/// Directional data (left, right, straight) is always given relatively to the managed segment.\n\t/// The terms \"incoming\"/\"outgoing\" refer to vehicles being able to move to/from the managed segment: Vehicles may to go to the managed segment if the other segment\n\t/// is \"incoming\". Vehicles may go to the other segment if it is \"outgoing\".\n\t/// \n\t/// Segment geometry data is primarily updated by the path-finding master thread (see method CustomPathFind.ProcessItemMain and field CustomPathFind.IsMasterPathFind).\n\t/// However, other methods may manually update geometry data by calling the \"Recalculate\" method. This is especially necessary for segments that are not visited by the\n\t/// path-finding algorithm (apparently if a segment is not used by any vehicle)\n\t/// </summary>\n\tpublic class SegmentGeometry : IEquatable<SegmentGeometry> {\n\t\tprivate static SegmentGeometry[] segmentGeometries;\n\n\t\tpublic static void PrintDebugInfo() {\n\t\t\tstring buf =\n\t\t\t\"--------------------------\\n\" +\n\t\t\t\"--- SEGMENT GEOMETRIES ---\\n\" +\n\t\t\t\"--------------------------\\n\";\n\t\t\tforeach (SegmentGeometry segGeo in segmentGeometries) {\n\t\t\t\tif (segGeo.IsValid()) {\n\t\t\t\t\tbuf += segGeo.ToString() + \"\\n\" +\n\t\t\t\t\t\"-------------------------\\n\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tLog.Info(buf);\n\t\t}\n\n\t\t/*public LaneGeometry[] LaneGeometries {\n\t\t\tget; private set;\n\t\t} = null;*/\n\n\t\t/// <summary>\n\t\t/// The id of the managed segment\n\t\t/// </summary>\n\t\tpublic ushort SegmentId {\n\t\t\tget; private set;\n\t\t}\n\n\t\tprivate SegmentEndGeometry startNodeGeometry;\n\t\tprivate SegmentEndGeometry endNodeGeometry;\n\n\t\tpublic SegmentEndGeometry StartNodeGeometry {\n\t\t\tget {\n\t\t\t\tif (startNodeGeometry.IsValid())\n\t\t\t\t\treturn startNodeGeometry;\n\t\t\t\telse\n\t\t\t\t\treturn null;\n\t\t\t}\n\t\t\tprivate set { startNodeGeometry = value; }\n\t\t}\n\n\t\tpublic SegmentEndGeometry EndNodeGeometry {\n\t\t\tget {\n\t\t\t\tif (endNodeGeometry.IsValid())\n\t\t\t\t\treturn endNodeGeometry;\n\t\t\t\telse\n\t\t\t\t\treturn null;\n\t\t\t}\n\t\t\tprivate set { endNodeGeometry = value; }\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Indicates that the managed segment is a one-way segment\n\t\t/// </summary>\n\t\tprivate bool oneWay = false;\n\n\t\t/// <summary>\n\t\t/// Indicates that the managed segment is a highway\n\t\t/// </summary>\n\t\tprivate bool highway = false;\n\n\t\t/// <summary>\n\t\t/// Indicates that the managed segment has a buslane\n\t\t/// </summary>\n\t\tprivate bool buslane = false;\n\n\t\t/// <summary>\n\t\t/// Constructor\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">id of the managed segment</param>\n\t\tpublic SegmentGeometry(ushort segmentId) {\n\t\t\tthis.SegmentId = segmentId;\n\t\t\tstartNodeGeometry = new SegmentEndGeometry(segmentId, true);\n\t\t\tendNodeGeometry = new SegmentEndGeometry(segmentId, false);\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Determines the start node of the managed segment\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic ushort StartNodeId() {\n\t\t\treturn Singleton<NetManager>.instance.m_segments.m_buffer[SegmentId].m_startNode;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the end node of the managed segment\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic ushort EndNodeId() {\n\t\t\treturn Singleton<NetManager>.instance.m_segments.m_buffer[SegmentId].m_endNode;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Lock object. Acquire this before accessing the HashSets.\n\t\t/// </summary>\n\t\t//public readonly object Lock = new object();\n\n\t\t/// <summary>\n\t\t/// Holds a list of observers which are being notified as soon as the managed segment's geometry is updated (but not neccessarily modified)\n\t\t/// </summary>\n\t\t//private List<IObserver<SegmentGeometry>> observers = new List<IObserver<SegmentGeometry>>();\n\n\t\tprivate bool valid = false;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[SegmentGeometry ({SegmentId})\\n\" +\n\t\t\t\t\"\\t\" + $\"IsValid() = {IsValid()}\\n\" +\n\t\t\t\t\"\\t\" + $\"oneWay = {oneWay}\\n\" +\n\t\t\t\t\"\\t\" + $\"highway = {highway}\\n\" +\n\t\t\t\t\"\\t\" + $\"buslane = {buslane}\\n\" +\n\t\t\t\t\"\\t\" + $\"StartNodeGeometry = {StartNodeGeometry}\\n\" +\n\t\t\t\t\"\\t\" + $\"EndNodeGeometry = {EndNodeGeometry}\\n\" +\n\t\t\t\t//\"\\t\" + $\"LaneGeometries = {(LaneGeometries == null ? \"<null>\" : LaneGeometries.ArrayToString())}\\n\" +\n\t\t\t\t\"SegmentGeometry]\";\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Registers an observer.\n\t\t/// </summary>\n\t\t/// <param name=\"observer\"></param>\n\t\t/// <returns>An unsubscriber</returns>\n\t\t/*public IDisposable Subscribe(IObserver<SegmentGeometry> observer) {\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(Lock);\n\t\t\t\tobservers.Add(observer);\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(Lock);\n\t\t\t}\n\t\t\treturn new GenericUnsubscriber<SegmentGeometry>(observers, observer, Lock);\n\t\t}*/\n\n\t\t[Obsolete]\n\t\tpublic static bool IsValid(ushort segmentId) {\n\t\t\treturn Constants.ServiceFactory.NetService.IsSegmentValid(segmentId);\n\t\t}\n\n\t\tpublic bool IsValid() {\n\t\t\treturn IsValid(SegmentId);\n\t\t}\n\t\t\n\t\tpublic void StartRecalculation(GeometryCalculationMode calcMode) {\n\t\t\tRecalculate(calcMode);\n\t\t\t//RecalculateLaneGeometries(calcMode);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Requests recalculation of the managed segment's geometry data. If recalculation is not enforced (argument \"force\"),\n\t\t/// recalculation is only done if recalculation has not been recently executed.\n\t\t/// </summary>\n\t\t/// <param name=\"output\">Specifies if logging should be performed</param>\n\t\t/// <param name=\"force\">Specifies if recalculation should be enforced.</param>\n\t\tpublic void Recalculate(GeometryCalculationMode calcMode) {\n#if DEBUGGEO\n\t\t\tbool output = GlobalConfig.Instance.Debug.Switches[5];\n\n\t\t\tif (output)\n\t\t\t\tLog._Debug($\">>> SegmentGeometry.Recalculate({calcMode}): called for segment {SegmentId}. IsValid()={IsValid()}, wasValid={valid}\");\n#endif\n\n\t\t\tif (!IsValid()) {\n\t\t\t\tif (valid) {\n\t\t\t\t\tvalid = false;\n\n\t\t\t\t\tif (calcMode == GeometryCalculationMode.Propagate) {\n\t\t\t\t\t\tstartNodeGeometry.Recalculate(GeometryCalculationMode.Propagate);\n\t\t\t\t\t\tendNodeGeometry.Recalculate(GeometryCalculationMode.Propagate);\n\t\t\t\t\t}\n\n\t\t\t\t\tCleanup();\n\t\t\t\t\tConstants.ManagerFactory.GeometryManager.OnUpdateSegment(this);\n\t\t\t\t\t//NotifyObservers();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvalid = true;\n\t\t\ttry {\n#if DEBUGGEO\n\t\t\t\tif (output)\n\t\t\t\t\tLog._Debug($\"Trying to get a lock for Recalculating geometries of segment {SegmentId}...\");\n#endif\n\t\t\t\t//Monitor.Enter(Lock);\n\n#if DEBUGGEO\n\t\t\t\tif (output)\n\t\t\t\t\tLog.Info($\"Recalculating geometries of segment {SegmentId} STARTED\");\n#endif\n\n\t\t\t\tCleanup();\n\n\t\t\t\toneWay = calculateIsOneWay(SegmentId);\n\t\t\t\thighway = calculateIsHighway(SegmentId);\n\t\t\t\tbuslane = calculateHasBusLane(SegmentId);\n\t\t\t\tstartNodeGeometry.Recalculate(calcMode);\n\t\t\t\tendNodeGeometry.Recalculate(calcMode);\n\n#if DEBUGGEO\n\t\t\t\tif (output) {\n\t\t\t\t\tLog.Info($\"Recalculating geometries of segment {SegmentId} FINISHED (flags={Singleton<NetManager>.instance.m_segments.m_buffer[SegmentId].m_flags})\");\n\t\t\t\t\tSegmentEndGeometry[] endGeometries = new SegmentEndGeometry[] { startNodeGeometry, endNodeGeometry };\n\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. oneWay={oneWay}\");\n\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. highway={highway}\");\n\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tforeach (SegmentEndGeometry endGeometry in endGeometries) {\n\t\t\t\t\t\tif (i == 0)\n\t\t\t\t\t\t\tLog._Debug(\"--- end @ start node ---\");\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tLog._Debug(\"--- end @ end node ---\");\n\t\t\t\t\t\tLog._Debug($\"Node {endGeometry.NodeId()} (flags={Singleton<NetManager>.instance.m_nodes.m_buffer[endGeometry.NodeId()].m_flags})\");\n\n\t\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. connectedSegments={ string.Join(\", \", endGeometry.ConnectedSegments.Select(x => x.ToString()).ToArray())}\");\n\n\t\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. leftSegments={ string.Join(\", \", endGeometry.LeftSegments.Select(x => x.ToString()).ToArray())}\");\n\t\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. incomingLeftSegments={ string.Join(\", \", endGeometry.IncomingLeftSegments.Select(x => x.ToString()).ToArray())}\");\n\t\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. outgoingLeftSegments={ string.Join(\", \", endGeometry.OutgoingLeftSegments.Select(x => x.ToString()).ToArray())}\");\n\n\t\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. rightSegments={ string.Join(\", \", endGeometry.RightSegments.Select(x => x.ToString()).ToArray())}\");\n\t\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. incomingRightSegments={ string.Join(\", \", endGeometry.IncomingRightSegments.Select(x => x.ToString()).ToArray())}\");\n\t\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. outgoingRightSegments={ string.Join(\", \", endGeometry.OutgoingRightSegments.Select(x => x.ToString()).ToArray())}\");\n\n\t\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. straightSegments={ string.Join(\", \", endGeometry.StraightSegments.Select(x => x.ToString()).ToArray())}\");\n\t\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. incomingStraightSegments={ string.Join(\", \", endGeometry.IncomingStraightSegments.Select(x => x.ToString()).ToArray())}\");\n\t\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. outgoingStraightSegments={ string.Join(\", \", endGeometry.OutgoingStraightSegments.Select(x => x.ToString()).ToArray())}\");\n\n\t\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. onlyHighways={endGeometry.OnlyHighways}\");\n\t\t\t\t\t\tLog._Debug($\"seg. {SegmentId}. outgoingOneWay={endGeometry.OutgoingOneWay}\");\n\t\t\t\t\t\t\n\t\t\t\t\t\t++i;\n\t\t\t\t\t}\n\t\t\t\t}\n#endif\n\n#if DEBUGGEO\n\t\t\t\t//Log._Debug($\"Recalculation of segment {SegmentId} completed. Valid? {IsValid()}\");\n#endif\n\t\t\t\tConstants.ManagerFactory.GeometryManager.OnUpdateSegment(this);\n\t\t\t\t//NotifyObservers();\n\t\t\t} finally {\n#if DEBUGGEO\n\t\t\t\tif (output)\n\t\t\t\t\tLog._Debug($\"Lock released after recalculating geometries of segment {SegmentId}\");\n#endif\n\t\t\t\t//Monitor.Exit(Lock);\n\t\t\t}\n\t\t}\n\n\t\tpublic SegmentEndGeometry GetEnd(bool startNode) {\n\t\t\tif (! IsValid()) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif (startNode) {\n\t\t\t\tif (StartNodeGeometry != null && StartNodeGeometry.IsValid()) {\n\t\t\t\t\treturn StartNodeGeometry;\n\t\t\t\t}\n\t\t\t} else if (EndNodeGeometry != null && EndNodeGeometry.IsValid()) {\n\t\t\t\treturn EndNodeGeometry;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic SegmentEndGeometry GetEnd(ushort nodeId) {\n\t\t\tif (!IsValid()) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tushort startNodeId = StartNodeId();\n\t\t\tif (nodeId == startNodeId) {\n\t\t\t\treturn StartNodeGeometry;\n\t\t\t} else {\n\t\t\t\tushort endNodeId = EndNodeId();\n\t\t\t\tif (nodeId == endNodeId)\n\t\t\t\t\treturn EndNodeGeometry;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Verifies the information that another is/is not connected to the managed segment. If the verification fails, a recalculation of geometry data is performed.\n\t\t/// The method does not necessarily guarantee that the segment geometry data regarding the queried segment with id \"otherSegmentId\" is correct.\n\t\t/// \n\t\t/// This method should only be called if there is a good case to believe that the other segment may be connected to the managed segment.\n\t\t/// Else, a possibly unnecessary geometry recalculation is performed.\n\t\t/// </summary>\n\t\t/// <param name=\"otherSegmentId\">The other segment that is could be connected to the managed segment.</param>\n\t\t/// <returns></returns>\n\t\t/*internal bool VerifyConnectedSegment(ushort otherSegmentId) {\n\t\t\tif ((Singleton<NetManager>.instance.m_segments.m_buffer[otherSegmentId].m_flags & NetSegment.Flags.Created) == NetSegment.Flags.None) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (otherSegmentId == SegmentId)\n\t\t\t\treturn false;\n\n\t\t\tbool segmentIsConnectedToStartNode = false;\n\t\t\tbool segmentIsConnectedToEndNode = false;\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(Lock);\n\t\t\t\tsegmentIsConnectedToStartNode = startNodeGeometry.IsConnectedTo(otherSegmentId);\n\t\t\t\tsegmentIsConnectedToEndNode = endNodeGeometry.IsConnectedTo(otherSegmentId);\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(Lock);\n\t\t\t}\n\n\t\t\tif (!segmentIsConnectedToStartNode && !segmentIsConnectedToEndNode) {\n\t\t\t\tLog.Warning($\"Neither the segments of start node {startNodeGeometry.NodeId()} nor of end node {endNodeGeometry.NodeId()} of segment {SegmentId} contain the segment {otherSegmentId}\");\n                Recalculate(true);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}*/\n\n\t\t/// <summary>\n\t\t/// Runs a simple segment geometry verification that only checks if the stored number of connected segments at start and end node. \n\t\t/// \n\t\t/// If the numbers of connected segments at the given node mismatches, a geometry recalculation is performed.\n\t\t/// </summary>\n\t\t/// <param name=\"nodeId\">Node at which segment counts should be checked</param>\n\t\t/// <returns>true if a recalculation has been performed, false otherwise</returns>\n\t\t/*internal bool VerifySegmentsByCount(bool startNode) {\n\t\t\tushort nodeId = startNode ? startNodeGeometry.NodeId() : endNodeGeometry.NodeId();\n\t\t\tif (nodeId == 0 || (Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_flags & NetNode.Flags.Created) == NetNode.Flags.None)\n\t\t\t\treturn false;\n\n\t\t\tint expectedCount = Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].CountSegments(NetSegment.Flags.Created, SegmentId);\n\t\t\tvar storedCount = CountOtherSegments(startNode);\n\t\t\tif (storedCount != expectedCount) {\n\t\t\t\tLog._Debug($\"The number of other segments (expected {expectedCount}) at node {nodeId} does not equals the stored count ({storedCount})\");\n\t\t\t\tRecalculate();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tinternal void VerifySegmentsByCount() {\n\t\t\tif (VerifySegmentsByCount(true))\n\t\t\t\treturn;\n\t\t\tVerifySegmentsByCount(false);\n\t\t}*/\n\n\t\t/*internal void VerifyCreated() {\n\t\t\tif (!IsValid() && wasValid) {\n\t\t\t\tLog._Debug($\"SegmentGeometry: Segment {SegmentId} has become invalid. Recalculating.\");\n\t\t\t\tRecalculate(true);\n\t\t\t}\n\t\t}\n\n\t\tinternal void VerifyByNodes() {\n\t\t\tif (startNodeGeometry.NodeId() != startNodeGeometry.LastKnownNodeId)\n\t\t\t\tstartNodeGeometry.Recalculate(true);\n\t\t\tif (endNodeGeometry.NodeId() != endNodeGeometry.LastKnownNodeId)\n\t\t\t\tendNodeGeometry.Recalculate(true);\n\t\t}*/\n\n\t\t/// <summary>\n\t\t/// Determines the node id at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\n\t\tpublic ushort GetNodeId(bool startNode) {\n\t\t\tif (!IsValid())\n\t\t\t\treturn 0;\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NodeId();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines all connected segments at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\t\t\n\t\tpublic ushort[] GetConnectedSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.ConnectedSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines all connected right segments at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\t\t\n\t\tpublic ushort[] GetRightSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.RightSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines all connected left segments at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\t\t\n\t\tpublic ushort[] GetLeftSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.LeftSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines all connected straight segments at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\t\t\n\t\tpublic ushort[] GetStraightSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.StraightSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines all incoming segments at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\n\t\tpublic ushort[] GetIncomingSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.GetIncomingSegments();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines all incoming straight segments at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\n\t\tpublic ushort[] GetIncomingStraightSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.IncomingStraightSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines all incoming left segments at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\n\t\tpublic ushort[] GetIncomingLeftSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.IncomingLeftSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines all incoming right segments at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\n\t\tpublic ushort[] GetIncomingRightSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.IncomingRightSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines all outgoing segments at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\n\t\tpublic ushort[] GetOutgoingSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.GetOutgoingSegments();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines all outgoing straight segments at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\n\t\tpublic ushort[] GetOutgoingStraightSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.OutgoingStraightSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines all outgoing left segments at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\n\t\tpublic ushort[] GetOutgoingLeftSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.OutgoingLeftSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines all outgoing right segments at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns></returns>\n\t\tpublic ushort[] GetOutgoingRightSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.OutgoingRightSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the number of segments connected to the managed segment at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>number of connected segments at the given node</returns>\n\t\tpublic int CountOtherSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NumConnectedSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the number of left segments connected to the managed segment at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>number of connected left segments at the given node</returns>\n\t\tpublic int CountLeftSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NumLeftSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the number of right segments connected to the managed segment at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>number of connected right segments at the given node</returns>\n\t\tpublic int CountRightSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NumRightSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the number of straight segments connected to the managed segment at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>number of connected straight segments at the given node</returns>\n\t\tpublic int CountStraightSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NumStraightSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the number of incoming segments connected to the managed segment at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>number of connected incoming left segments at the given node</returns>\n\t\tpublic int CountIncomingSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NumIncomingSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the number of incoming left segments connected to the managed segment at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>number of connected incoming left segments at the given node</returns>\n\t\tpublic int CountIncomingLeftSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NumIncomingLeftSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the number of incoming right segments connected to the managed segment at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>number of connected incoming right segments at the given node</returns>\n\t\tpublic int CountIncomingRightSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NumIncomingRightSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the number of incoming straight segments connected to the managed segment at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>number of connected incoming straight segments at the given node</returns>\n\t\tpublic int CountIncomingStraightSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NumIncomingStraightSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the number of outgoing segments connected to the managed segment at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>number of connected incoming left segments at the given node</returns>\n\t\tpublic int CountOutgoingSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NumOutgoingSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the number of outgoing left segments connected to the managed segment at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>number of connected outgoing left segments at the given node</returns>\n\t\tpublic int CountOutgoingLeftSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NumOutgoingLeftSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the number of outgoing right segments connected to the managed segment at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>number of connected outgoing right segments at the given node</returns>\n\t\tpublic int CountOutgoingRightSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NumOutgoingRightSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the number of outgoing straight segments connected to the managed segment at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>number of connected outgoing straight segments at the given node</returns>\n\t\tpublic int CountOutgoingStraightSegments(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.NumOutgoingStraightSegments;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if the managed segment is connected to left segments at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is connected to left segments at the given node, else false.</returns>\n\t\tpublic bool HasLeftSegment(bool startNode) {\n\t\t\treturn CountLeftSegments(startNode) > 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if the managed segment is connected to right segments at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is connected to right segments at the given node, else false.</returns>\n\t\tpublic bool HasRightSegment(bool startNode) {\n\t\t\treturn CountRightSegments(startNode) > 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if the managed segment is connected to straight segments at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is connected to straight segments at the given node, else false.</returns>\n\t\tpublic bool HasStraightSegment(bool startNode) {\n\t\t\treturn CountStraightSegments(startNode) > 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if the managed segment is connected to incoming left segments at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is connected to incoming left segments at the given node, else false.</returns>\n\t\tpublic bool HasIncomingLeftSegment(bool startNode) {\n\t\t\treturn CountIncomingLeftSegments(startNode) > 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if the managed segment is connected to incoming right segments at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is connected to incoming right segments at the given node, else false.</returns>\n\t\tpublic bool HasIncomingRightSegment(bool startNode) {\n\t\t\treturn CountIncomingRightSegments(startNode) > 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if the managed segment is connected to incoming straight segments at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is connected to incoming straight segments at the given node, else false.</returns>\n\t\tpublic bool HasIncomingStraightSegment(bool startNode) {\n\t\t\treturn CountIncomingStraightSegments(startNode) > 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if the managed segment is connected to outgoing left segments at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is connected to outgoing left segments at the given node, else false.</returns>\n\t\tpublic bool HasOutgoingLeftSegment(bool startNode) {\n\t\t\treturn CountOutgoingLeftSegments(startNode) > 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if the managed segment is connected to outgoing right segments at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is connected to outgoing right segments at the given node, else false.</returns>\n\t\tpublic bool HasOutgoingRightSegment(bool startNode) {\n\t\t\treturn CountOutgoingRightSegments(startNode) > 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if the managed segment is connected to outgoing straight segments at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is connected to outgoing straight segments at the given node, else false.</returns>\n\t\tpublic bool HasOutgoingStraightSegment(bool startNode) {\n\t\t\treturn CountOutgoingStraightSegments(startNode) > 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the given segment is connected to the managed segment and is a left segment at the given node relatively to the managed segment.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"toSegmentId\">other segment that ought to be left, relatively to the managed segment</param>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if the other segment is, according to the stored geometry data, connected on the left-hand side of the managed segment at the given node</returns>\n\t\tpublic bool IsLeftSegment(ushort toSegmentId, bool startNode) {\n\t\t\tif (!IsValid(toSegmentId))\n\t\t\t\treturn false;\n\t\t\tif (toSegmentId == SegmentId)\n\t\t\t\treturn false;\n\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\n\t\t\tbool contains = false;\n\t\t\tforeach (ushort segId in endGeometry.LeftSegments)\n\t\t\t\tif (segId == toSegmentId) {\n\t\t\t\t\tcontains = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\treturn contains;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the given segment is connected to the managed segment and is a right segment at the given node relatively to the managed segment.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"toSegmentId\">other segment that ought to be right, relatively to the managed segment</param>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if the other segment is, according to the stored geometry data, connected on the right-hand side of the managed segment at the given node</returns>\n\t\tpublic bool IsRightSegment(ushort toSegmentId, bool startNode) {\n\t\t\tif (!IsValid(toSegmentId))\n\t\t\t\treturn false;\n\t\t\tif (toSegmentId == SegmentId)\n\t\t\t\treturn false;\n\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.IsRightSegment(toSegmentId);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the given segment is connected to the managed segment and is a straight segment at the given node relatively to the managed segment.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"toSegmentId\">other segment that ought to be straight, relatively to the managed segment</param>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if the other segment is, according to the stored geometry data, connected straight-wise to the managed segment at the given node</returns>\n\t\tpublic bool IsStraightSegment(ushort toSegmentId, bool startNode) {\n\t\t\tif (!IsValid(toSegmentId))\n\t\t\t\treturn false;\n\t\t\tif (toSegmentId == SegmentId)\n\t\t\t\treturn false;\n\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\t\n\t\t\tbool contains = false;\n\t\t\tforeach (ushort segId in endGeometry.StraightSegments)\n\t\t\t\tif (segId == toSegmentId) {\n\t\t\t\t\tcontains = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\treturn contains;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the given segment is connected to the managed segment and is a left segment at the given node relatively to the managed segment.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"toSegmentId\">other segment that ought to be left, relatively to the managed segment</param>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if the other segment is, according to the stored geometry data, connected on the left-hand side of the managed segment at the given node</returns>\n\t\tpublic bool IsIncomingLeftSegment(ushort toSegmentId, bool startNode) {\n\t\t\tif (!IsValid(toSegmentId))\n\t\t\t\treturn false;\n\t\t\tif (toSegmentId == SegmentId)\n\t\t\t\treturn false;\n\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\n\t\t\tbool contains = false;\n\t\t\tforeach (ushort segId in endGeometry.IncomingLeftSegments)\n\t\t\t\tif (segId == toSegmentId) {\n\t\t\t\t\tcontains = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\treturn contains;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the given segment is connected to the managed segment and is a right segment at the given node relatively to the managed segment.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"toSegmentId\">other segment that ought to be right, relatively to the managed segment</param>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if the other segment is, according to the stored geometry data, connected on the right-hand side of the managed segment at the given node</returns>\n\t\tpublic bool IsIncomingRightSegment(ushort toSegmentId, bool startNode) {\n\t\t\tif (!IsValid(toSegmentId))\n\t\t\t\treturn false;\n\t\t\tif (toSegmentId == SegmentId)\n\t\t\t\treturn false;\n\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\t\n\t\t\tbool contains = false;\n\t\t\tforeach (ushort segId in endGeometry.IncomingRightSegments)\n\t\t\t\tif (segId == toSegmentId) {\n\t\t\t\t\tcontains = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\treturn contains;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the given segment is connected to the managed segment and is a straight segment at the given node relatively to the managed segment.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"toSegmentId\">other segment that ought to be straight, relatively to the managed segment</param>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if the other segment is, according to the stored geometry data, connected straight-wise to the managed segment at the given node</returns>\n\t\tpublic bool IsIncomingStraightSegment(ushort toSegmentId, bool startNode) {\n\t\t\tif (!IsValid(toSegmentId))\n\t\t\t\treturn false;\n\t\t\tif (toSegmentId == SegmentId)\n\t\t\t\treturn false;\n\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\t\n\t\t\tbool contains = false;\n\t\t\tforeach (ushort segId in endGeometry.IncomingStraightSegments)\n\t\t\t\tif (segId == toSegmentId) {\n\t\t\t\t\tcontains = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\treturn contains;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the managed segment is only connected to highways at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is only connected to highways at the given node, false otherwise</returns>\n\t\tpublic bool HasOnlyHighways(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.OnlyHighways;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the managed segment is a one-way road.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is a one-way road, false otherwise</returns>\n\t\tpublic bool IsOneWay() {\n\t\t\treturn oneWay;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the managed segment is a highway.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is a highway, false otherwise</returns>\n\t\tpublic bool IsHighway() {\n\t\t\treturn highway;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored data, the managed segment has a buslane.\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic bool HasBusLane() {\n\t\t\treturn buslane;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the managed segment is an outgoing one-way road at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is an outgoing one-way road at the given node, false otherwise</returns>\n\t\tpublic bool IsOutgoingOneWay(bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\t\t\treturn endGeometry.OutgoingOneWay;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the managed segment is an incoming one-way road at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is an incoming one-way road at the given node, false otherwise</returns>\n\t\tpublic bool IsIncomingOneWay(bool startNode) {\n\t\t\treturn (IsOneWay() && !IsOutgoingOneWay(startNode));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the managed segment is an incoming road at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is an incoming road at the given node, false otherwise</returns>\n\t\tpublic bool IsIncoming(bool startNode) {\n\t\t\treturn !IsOutgoingOneWay(startNode);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if, according to the stored geometry data, the managed segment is an outgoing road at the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>true, if, according to the stored geometry data, the managed segment is an outgoing road at the given node, false otherwise</returns>\n\t\tpublic bool IsOutgoing(bool startNode) {\n\t\t\treturn !IsIncomingOneWay(startNode);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the relative direction of the other segment relatively to the managed segment at the given node, according to the stored geometry information.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"otherSegmentId\">other segment</param>\n\t\t/// <param name=\"startNode\">defines if the segment should be checked at the start node (true) or end node (false)</param>\n\t\t/// <returns>relative direction of the other segment relatively to the managed segment at the given node</returns>\n\t\tpublic ArrowDirection GetDirection(ushort otherSegmentId, bool startNode) {\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\n\t\t\treturn endGeometry.GetDirection(otherSegmentId);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if highway merging/splitting rules are activated at the managed segment for the given node.\n\t\t/// \n\t\t/// A segment geometry verification is not performed.\n\t\t/// </summary>\n\t\t/// <param name=\"startNode\"></param>\n\t\t/// <returns></returns>\n\t\t[Obsolete]\n\t\tpublic bool AreHighwayRulesEnabled(bool startNode) {\n\t\t\tif (!Options.highwayRules)\n\t\t\t\treturn false;\n\t\t\tif (!IsIncomingOneWay(startNode))\n\t\t\t\treturn false;\n\t\t\tif (!(Singleton<NetManager>.instance.m_segments.m_buffer[SegmentId].Info.m_netAI is RoadBaseAI))\n\t\t\t\treturn false;\n\t\t\tif (!((RoadBaseAI)Singleton<NetManager>.instance.m_segments.m_buffer[SegmentId].Info.m_netAI).m_highwayRules)\n\t\t\t\treturn false;\n\n\t\t\tSegmentEndGeometry endGeometry = startNode ? startNodeGeometry : endNodeGeometry;\n\n\t\t\tif (endGeometry.NumConnectedSegments <= 1)\n\t\t\t\treturn false;\n\n\t\t\tbool nextAreOnlyOneWayHighways = true;\n\t\t\tforeach (ushort otherSegmentId in endGeometry.ConnectedSegments) {\n\t\t\t\tif (Singleton<NetManager>.instance.m_segments.m_buffer[otherSegmentId].Info.m_netAI is RoadBaseAI) {\n\t\t\t\t\tif (! SegmentGeometry.Get(otherSegmentId).IsOneWay() || !((RoadBaseAI)Singleton<NetManager>.instance.m_segments.m_buffer[otherSegmentId].Info.m_netAI).m_highwayRules) {\n\t\t\t\t\t\tnextAreOnlyOneWayHighways = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tnextAreOnlyOneWayHighways = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn nextAreOnlyOneWayHighways;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calculates if the given segment is an outgoing one-way road at the given node.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment to check</param>\n\t\t/// <param name=\"nodeId\">node the given segment shall be checked at</param>\n\t\t/// <returns>true, if the given segment is an outgoing one-way road at the given node, false otherwise</returns>\n\t\tinternal static bool calculateIsOutgoingOneWay(ushort segmentId, ushort nodeId) { // TODO move to SegmentEnd\n\t\t\tif (!IsValid(segmentId))\n\t\t\t\treturn false;\n\n\t\t\tvar instance = Singleton<NetManager>.instance;\n\n\t\t\tvar info = instance.m_segments.m_buffer[segmentId].Info;\n\n\t\t\tNetInfo.Direction dir = Constants.ServiceFactory.NetService.GetFinalSegmentEndDirection(segmentId, ref instance.m_segments.m_buffer[segmentId], instance.m_segments.m_buffer[segmentId].m_startNode == nodeId);\n\n\t\t\tvar laneId = instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tvar laneIndex = 0;\n\t\t\twhile (laneIndex < info.m_lanes.Length && laneId != 0u) {\n\t\t\t\tbool validLane = (info.m_lanes[laneIndex].m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&\n\t\t\t\t\t(info.m_lanes[laneIndex].m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Monorail)) != VehicleInfo.VehicleType.None;\n\t\t\t\t// TODO the lane types and vehicle types should be specified to make it clear which lanes we need to check\n\n\t\t\t\tif (validLane) {\n\t\t\t\t\tif ((info.m_lanes[laneIndex].m_finalDirection & dir) != NetInfo.Direction.None) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlaneId = instance.m_lanes.m_buffer[laneId].m_nextLane;\n\t\t\t\tlaneIndex++;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calculates if the given segment is a one-way road.\n\t\t/// </summary>\n\t\t/// <returns>true, if the managed segment is a one-way road, false otherwise</returns>\n\t\tprivate static bool calculateIsOneWay(ushort segmentId) {\n\t\t\tif (!IsValid(segmentId))\n\t\t\t\treturn false;\n\n\t\t\tvar instance = Singleton<NetManager>.instance;\n\n\t\t\tvar info = instance.m_segments.m_buffer[segmentId].Info;\n\n\t\t\tvar hasForward = false;\n\t\t\tvar hasBackward = false;\n\n\t\t\tvar laneId = instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tvar laneIndex = 0;\n\t\t\twhile (laneIndex < info.m_lanes.Length && laneId != 0u) {\n\t\t\t\tbool validLane = (info.m_lanes[laneIndex].m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&\n\t\t\t\t\t(info.m_lanes[laneIndex].m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Monorail)) != VehicleInfo.VehicleType.None;\n\t\t\t\t// TODO the lane types and vehicle types should be specified to make it clear which lanes we need to check\n\n\t\t\t\tif (validLane) {\n\t\t\t\t\tif ((info.m_lanes[laneIndex].m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None) {\n\t\t\t\t\t\thasForward = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((info.m_lanes[laneIndex].m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None) {\n\t\t\t\t\t\thasBackward = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (hasForward && hasBackward) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlaneId = instance.m_lanes.m_buffer[laneId].m_nextLane;\n\t\t\t\tlaneIndex++;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calculates if the given segment has a buslane.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment to check</param>\n\t\t/// <returns>true, if the given segment has a buslane, false otherwise</returns>\n\t\tinternal static bool calculateHasBusLane(ushort segmentId) {\n\t\t\tif (!IsValid(segmentId))\n\t\t\t\treturn false;\n\n\t\t\tbool ret = false;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(segmentId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\tret = calculateHasBusLane(segment.Info);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calculates if the given segment info describes a segment having a bus lane\n\t\t/// </summary>\n\t\t/// <param name=\"segmentInfo\"></param>\n\t\t/// <returns></returns>\n\t\tinternal static bool calculateHasBusLane(NetInfo segmentInfo) {\n\t\t\tfor (int laneIndex = 0; laneIndex < segmentInfo.m_lanes.Length; ++laneIndex) {\n\t\t\t\tif (segmentInfo.m_lanes[laneIndex].m_laneType == NetInfo.LaneType.TransportVehicle &&\n\t\t\t\t\t(segmentInfo.m_lanes[laneIndex].m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tinternal static void calculateOneWayAtNode(ushort segmentId, ushort nodeId, out bool isOneway, out bool isOutgoingOneWay) {\n\t\t\tif (!IsValid(segmentId)) {\n\t\t\t\tisOneway = false;\n\t\t\t\tisOutgoingOneWay = false;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar instance = Singleton<NetManager>.instance;\n\n\t\t\tvar info = instance.m_segments.m_buffer[segmentId].Info;\n\n\t\t\tvar dir = NetInfo.Direction.Forward;\n\t\t\tif (instance.m_segments.m_buffer[segmentId].m_startNode == nodeId)\n\t\t\t\tdir = NetInfo.Direction.Backward;\n\t\t\tvar dir2 = ((instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? dir : NetInfo.InvertDirection(dir);\n\n\t\t\tvar hasForward = false;\n\t\t\tvar hasBackward = false;\n\t\t\tisOutgoingOneWay = true;\n\t\t\tvar laneId = instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tvar laneIndex = 0;\n\t\t\twhile (laneIndex < info.m_lanes.Length && laneId != 0u) {\n\t\t\t\tbool validLane = (info.m_lanes[laneIndex].m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&\n\t\t\t\t\t(info.m_lanes[laneIndex].m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Monorail)) != VehicleInfo.VehicleType.None;\n\t\t\t\t// TODO the lane types and vehicle types should be specified to make it clear which lanes we need to check\n\n\t\t\t\tif (validLane) {\n\t\t\t\t\tif ((info.m_lanes[laneIndex].m_finalDirection & dir2) != NetInfo.Direction.None) {\n\t\t\t\t\t\tisOutgoingOneWay = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((info.m_lanes[laneIndex].m_direction & NetInfo.Direction.Forward) != NetInfo.Direction.None) {\n\t\t\t\t\t\thasForward = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((info.m_lanes[laneIndex].m_direction & NetInfo.Direction.Backward) != NetInfo.Direction.None) {\n\t\t\t\t\t\thasBackward = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlaneId = instance.m_lanes.m_buffer[laneId].m_nextLane;\n\t\t\t\tlaneIndex++;\n\t\t\t}\n\n\t\t\tisOneway = !(hasForward && hasBackward);\n\t\t\tif (!isOneway)\n\t\t\t\tisOutgoingOneWay = false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calculates if the given segment is a highway\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <returns></returns>\n\t\tinternal static bool calculateIsHighway(ushort segmentId) {\n\t\t\tif (!IsValid(segmentId))\n\t\t\t\treturn false;\n\n\t\t\tbool ret = false;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(segmentId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\tret = calculateIsHighway(segment.Info);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calculates if the given segment info describes a highway segment\n\t\t/// </summary>\n\t\t/// <param name=\"segmentInfo\"></param>\n\t\t/// <returns></returns>\n\t\tinternal static bool calculateIsHighway(NetInfo segmentInfo) {\n\t\t\tif (segmentInfo.m_netAI is RoadBaseAI)\n\t\t\t\treturn ((RoadBaseAI)segmentInfo.m_netAI).m_highwayRules;\n\t\t\treturn false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Clears the segment geometry data.\n\t\t/// </summary>\n\t\tprivate void Cleanup() {\n\t\t\thighway = false;\n\t\t\toneWay = false;\n\t\t\tbuslane = false;\n\n\t\t\ttry {\n\t\t\t\t//Monitor.Enter(Lock);\n\n\t\t\t\tstartNodeGeometry.Cleanup();\n\t\t\t\tendNodeGeometry.Cleanup();\n\n\t\t\t\t// reset highway lane arrows\n\t\t\t\t//Flags.removeHighwayLaneArrowFlagsAtSegment(SegmentId); // TODO refactor\n\n\t\t\t\t// clear default vehicle type cache\n\t\t\t\tVehicleRestrictionsManager.Instance.ClearCache(SegmentId);\n\t\t\t} finally {\n\t\t\t\t//Monitor.Exit(Lock);\n\t\t\t}\n\t\t}\n\n\t\tpublic bool Equals(SegmentGeometry otherSegGeo) {\n\t\t\tif (otherSegGeo == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn SegmentId == otherSegGeo.SegmentId;\n\t\t}\n\n\t\tpublic override bool Equals(object other) {\n\t\t\tif (other == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!(other is SegmentGeometry)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn Equals((SegmentGeometry)other);\n\t\t}\n\n\t\tpublic override int GetHashCode() {\n\t\t\tint prime = 31;\n\t\t\tint result = 1;\n\t\t\tresult = prime * result + SegmentId.GetHashCode();\n\t\t\treturn result;\n\t\t}\n\n\t\t// static methods\n\n\t\tstatic SegmentGeometry() {\n\t\t\tsegmentGeometries = new SegmentGeometry[NetManager.MAX_SEGMENT_COUNT];\n\t\t}\n\n\t\tinternal static void OnBeforeLoadData() {\n\t\t\tLog._Debug($\"Building {segmentGeometries.Length} segment geometries...\");\n\t\t\tfor (int i = 0; i < segmentGeometries.Length; ++i) {\n\t\t\t\tsegmentGeometries[i] = new SegmentGeometry((ushort)i);\n\t\t\t}\n\t\t\tfor (int i = 0; i < segmentGeometries.Length; ++i) {\n\t\t\t\tsegmentGeometries[i].Recalculate(GeometryCalculationMode.Init);\n\t\t\t}\n\t\t\t/*for (int i = 0; i < segmentGeometries.Length; ++i) {\n\t\t\t\tsegmentGeometries[i].RecalculateLaneGeometries(GeometryCalculationMode.Init);\n\t\t\t}*/\n\t\t\tLog._Debug($\"Calculated segment geometries.\");\n\t\t}\n\n\t\tinternal static void OnBeforeSaveData() {\n\t\t\t/*Log._Debug($\"Recalculating all segment geometries...\");\n\t\t\tfor (int i = 0; i < segmentGeometries.Length; ++i) {\n\t\t\t\tsegmentGeometries[i].Recalculate(false);\n\t\t\t}\n\t\t\tLog._Debug($\"Calculated segment geometries.\");*/\n\t\t}\n\n\t\tpublic static SegmentGeometry Get(ushort segmentId, bool ignoreInvalid=false) {\n\t\t\tif (segmentGeometries == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tSegmentGeometry segGeo = segmentGeometries[segmentId];\n\t\t\tif (segGeo == null || (! ignoreInvalid && ! segGeo.valid)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn segGeo;\n\t\t}\n\n\t\t/*private void RecalculateLaneGeometries() {\n\t\t\tRebuildLaneGeometries();\n\t\t\tforeach (LaneGeometry laneGeo in LaneGeometries) {\n\t\t\t\tlaneGeo.Recalculate();\n\t\t\t}\n\t\t}*/\n\n\t\t/*private void RebuildLaneGeometries() {\n\t\t\tLaneGeometry[] newLaneGeos = null;\n\n\t\t\tif (IsValid()) {\n\t\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(SegmentId, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\t\tnewLaneGeos = new LaneGeometry[segment.Info.m_lanes.Length];\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tint minNum = 0;\n\t\t\t\tif (LaneGeometries != null) {\n\t\t\t\t\tminNum = Math.Min(newLaneGeos.Length, LaneGeometries.Length);\n\t\t\t\t\tfor (int i = 0; i < minNum; ++i) {\n\t\t\t\t\t\tnewLaneGeos[i] = LaneGeometries[i];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (int i = minNum; i < newLaneGeos.Length; ++i) {\n\t\t\t\t\tnewLaneGeos[i] = new LaneGeometry(SegmentId, i);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tLaneGeometries = newLaneGeos;\n\t\t}*/\n\n\t\t/*private void NotifyObservers() {\n\t\t\tList<IObserver<SegmentGeometry>> myObservers = new List<IObserver<SegmentGeometry>>(observers); // in case somebody unsubscribes while iterating over subscribers\n\t\t\tforeach (IObserver<SegmentGeometry> observer in myObservers) {\n\t\t\t\ttry {\n\t\t\t\t\tobserver.OnUpdate(this);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"SegmentGeometry.NotifyObservers: An exception occured while notifying an observer of segment {SegmentId}: {e}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}*/\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Geometry/README.md",
    "content": "# TM:PE -- /Custom/Geometry\nClasses for pre-calculated properties of segments, segment ends and nodes.\n## Terminology\nAlthough the term *geometry* is being used, we are not storing angles and lengths of segments. We are instead storing relational information of two or more segments that are connected at a node (e.g. segment X is left of segment Y).\n\nThe term *segment end* represents the directional component of traffic at one segment. For example, a segment end at segment X and node Y represent the set of all lanes that allow traffic to flow from X to Y. Thus, for each segment that is not a one-way street and is connected to two nodes, two separate segment ends exist: One describing the segment's part connected to its start node and the other segment end describes the situation at the segment's end node.\n\n*Incoming* traffic at segment ends always flows to the node where *outgoing* traffic flows away from the node.\n## Classes\n- **GeometryCalculationMode**: Controls propagation when performing geometry calculations.\n- **NodeGeometry**: Holds all connected segment end geometries.   \n- **SegmentEndGeometry**: Stores information about a segment at one connected node.\n- **SegmentEndId**: Abstract class to represent segment ends\n- **SegmentGeometry**: Stores general information about a segment and holds references to both segment ends."
  },
  {
    "path": "TLM/TLM/LoadingExtension.cs",
    "content": "using System;\nusing System.Reflection;\nusing ColossalFramework;\nusing ICities;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.UI;\nusing UnityEngine;\nusing Object = UnityEngine.Object;\nusing System.Collections.Generic;\nusing TrafficManager.State;\nusing ColossalFramework.UI;\nusing ColossalFramework.Math;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.Util;\nusing TrafficManager.Custom.Manager;\nusing TrafficManager.Manager;\nusing CSUtil.Commons;\nusing TrafficManager.Custom.Data;\nusing TrafficManager.Manager.Impl;\n\nnamespace TrafficManager {\n\tpublic class LoadingExtension : LoadingExtensionBase {\n\t\tpublic class Detour {\n\t\t\tpublic MethodInfo OriginalMethod;\n\t\t\tpublic MethodInfo CustomMethod;\n\t\t\tpublic RedirectCallsState Redirect;\n\n\t\t\tpublic Detour(MethodInfo originalMethod, MethodInfo customMethod) {\n\t\t\t\tthis.OriginalMethod = originalMethod;\n\t\t\t\tthis.CustomMethod = customMethod;\n\t\t\t\tthis.Redirect = RedirectionHelper.RedirectCalls(originalMethod, customMethod);\n\t\t\t}\n\t\t}\n\n\t\t//public static LoadingExtension Instance;\n\n\t\tpublic static bool IsPathManagerReplaced {\n\t\t\tget; private set;\n\t\t} = false;\n\n\t\tpublic static bool IsRainfallLoaded {\n\t\t\tget; private set;\n\t\t} = false;\n\n\t\tpublic static bool IsRushHourLoaded {\n\t\t\tget; private set;\n\t\t} = false;\n\n\t\tpublic static CustomPathManager CustomPathManager { get; set; }\n\t\tpublic static bool DetourInited { get; set; }\n\t\tpublic static List<Detour> Detours { get; set; }\n\t\t//public static TrafficManagerMode ToolMode { get; set; }\n\t\t//public static TrafficManagerTool TrafficManagerTool { get; set; }\n#if !TAM\n\t\tpublic static UIBase BaseUI { get; private set; }\n#endif\n\n\t\tpublic static UITransportDemand TransportDemandUI { get; private set; }\n\n\t\tpublic static List<ICustomManager> RegisteredManagers { get; private set; }\n\n\t\tpublic static bool IsGameLoaded { get; private set; } = false;\n\n\t\tpublic LoadingExtension() {\n\t\t}\n\n\t\tpublic void revertDetours() {\n\t\t\tif (DetourInited) {\n\t\t\t\tLog.Info(\"Revert detours\");\n\t\t\t\tDetours.Reverse();\n\t\t\t\tforeach (Detour d in Detours) {\n\t\t\t\t\tRedirectionHelper.RevertRedirect(d.OriginalMethod, d.Redirect);\n\t\t\t\t}\n\t\t\t\tDetourInited = false;\n\t\t\t\tDetours.Clear();\n\t\t\t\tLog.Info(\"Reverting detours finished.\");\n\t\t\t}\n\t\t}\n\n\t\tpublic void initDetours() {\n\t\t\t// TODO realize detouring with annotations\n\t\t\tif (!DetourInited) {\n\t\t\t\tLog.Info(\"Init detours\");\n\t\t\t\tbool detourFailed = false;\n\n\t\t\t\t// REVERSE REDIRECTION\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomVehicleManager::ReleaseVehicleImplementation calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomVehicleManager).GetMethod(\"ReleaseVehicleImplementation\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(VehicleManager).GetMethod(\"ReleaseVehicleImplementation\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomVehicleManager::ReleaseVehicleImplementation\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n#if DEBUGBUSBUG\n\t\t\t\t// TODO remove\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomNetManager::FinalizeNode calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomNetManager).GetMethod(\"FinalizeNode\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetNode).MakeByRefType(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(NetManager).GetMethod(\"FinalizeNode\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetNode).MakeByRefType(),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomNetManager::FinalizeNode\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\t// TODO remove\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomNetManager::InitializeNode calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomNetManager).GetMethod(\"InitializeNode\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetNode).MakeByRefType(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(NetManager).GetMethod(\"InitializeNode\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetNode).MakeByRefType(),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomNetManager::InitializeNode\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\t// TODO remove\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomNetManager::InitializeSegment calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomNetManager).GetMethod(\"InitializeSegment\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetSegment).MakeByRefType(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(NetManager).GetMethod(\"InitializeSegment\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetSegment).MakeByRefType(),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomNetManager::InitializeSegment\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n#endif\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomCitizenManager::ReleaseCitizenInstanceImplementation calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomCitizenManager).GetMethod(\"ReleaseCitizenInstanceImplementation\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CitizenManager).GetMethod(\"ReleaseCitizenInstanceImplementation\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomCitizenManager::ReleaseCitizenInstanceImplementation\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomCitizenManager::ReleaseCitizenImplementation calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomCitizenManager).GetMethod(\"ReleaseCitizenImplementation\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\t\ttypeof (Citizen).MakeByRefType(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CitizenManager).GetMethod(\"ReleaseCitizenImplementation\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\t\ttypeof (Citizen).MakeByRefType(),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomCitizenManager::ReleaseCitizenImplementation\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection ResidentAI::GetTaxiProbability calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomResidentAI).GetMethod(\"GetTaxiProbability\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Citizen.AgeGroup)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(ResidentAI).GetMethod(\"GetTaxiProbability\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Citizen.AgeGroup)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect ResidentAI::GetTaxiProbability\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection ResidentAI::GetBikeProbability calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomResidentAI).GetMethod(\"GetBikeProbability\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Citizen.AgeGroup)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(ResidentAI).GetMethod(\"GetBikeProbability\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Citizen.AgeGroup)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect ResidentAI::GetBikeProbability\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection ResidentAI::GetCarProbability calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomResidentAI).GetMethod(\"GetCarProbability\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Citizen.AgeGroup)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(ResidentAI).GetMethod(\"GetCarProbability\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Citizen.AgeGroup)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect ResidentAI::GetCarProbability\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection ResidentAI::GetElectricCarProbability calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomResidentAI).GetMethod(\"GetElectricCarProbability\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Citizen.AgePhase)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(ResidentAI).GetMethod(\"GetElectricCarProbability\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Citizen.AgePhase)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect ResidentAI::GetElectricCarProbability\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection TouristAI::GetTaxiProbability calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(\n\t\t\t\t\t\t\ttypeof(CustomTouristAI).GetMethod(\"GetTaxiProbability\", BindingFlags.NonPublic | BindingFlags.Instance),\n\t\t\t\t\t\t\ttypeof(TouristAI).GetMethod(\"GetTaxiProbability\", BindingFlags.NonPublic | BindingFlags.Instance)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect TouristAI::GetTaxiProbability\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection TouristAI::GetBikeProbability calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(\n\t\t\t\t\t\t\ttypeof(CustomTouristAI).GetMethod(\"GetBikeProbability\", BindingFlags.NonPublic | BindingFlags.Instance),\n\t\t\t\t\t\t\ttypeof(TouristAI).GetMethod(\"GetBikeProbability\", BindingFlags.NonPublic | BindingFlags.Instance)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect TouristAI::GetBikeProbability\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection TouristAI::GetCarProbability calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(\n\t\t\t\t\t\t\ttypeof(CustomTouristAI).GetMethod(\"GetCarProbability\", BindingFlags.NonPublic | BindingFlags.Instance),\n\t\t\t\t\t\t\ttypeof(TouristAI).GetMethod(\"GetCarProbability\", BindingFlags.NonPublic | BindingFlags.Instance)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect TouristAI::GetCarProbability\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection TouristAI::GetElectricCarProbability calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(\n\t\t\t\t\t\t\ttypeof(CustomTouristAI).GetMethod(\"GetElectricCarProbability\", BindingFlags.NonPublic | BindingFlags.Instance),\n\t\t\t\t\t\t\ttypeof(TouristAI).GetMethod(\"GetElectricCarProbability\", BindingFlags.NonPublic | BindingFlags.Instance)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect TouristAI::GetElectricCarProbability\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection TouristAI::GetCamperProbability calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTouristAI).GetMethod(\"GetCamperProbability\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (Citizen.Wealth)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(TouristAI).GetMethod(\"GetCamperProbability\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (Citizen.Wealth)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect TouristAI::GetCamperProbability\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection HumanAI::GetBuildingTargetPosition calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomHumanAI).GetMethod(\"GetBuildingTargetPosition\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (float)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(HumanAI).GetMethod(\"GetBuildingTargetPosition\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (float)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect HumanAI::GetBuildingTargetPosition\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection HumanAI::PathfindFailure calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomHumanAI).GetMethod(\"PathfindFailure\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(HumanAI).GetMethod(\"PathfindFailure\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType()\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect HumanAI::PathfindFailure\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection HumanAI::PathfindSuccess calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomHumanAI).GetMethod(\"PathfindSuccess\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(HumanAI).GetMethod(\"PathfindSuccess\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType()\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect HumanAI::PathfindSuccess\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection HumanAI::Spawn calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomHumanAI).GetMethod(\"Spawn\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(HumanAI).GetMethod(\"Spawn\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType()\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect HumanAI::Spawn\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection HumanAI::WaitTouristVehicle calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomHumanAI).GetMethod(\"WaitTouristVehicle\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (ushort)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(HumanAI).GetMethod(\"WaitTouristVehicle\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (ushort)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect HumanAI::WaitTouristVehicle\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection PassengerCarAI::FindParkingSpaceRoadSide calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomPassengerCarAI).GetMethod(\"FindParkingSpaceRoadSide\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Quaternion).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(PassengerCarAI).GetMethod(\"FindParkingSpaceRoadSide\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Quaternion).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType()\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect PassengerCarAI::FindParkingSpaceRoadSide\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\t/*Log.Info(\"Reverse-Redirection PassengerCarAI::FindParkingSpaceBuilding calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomPassengerCarAI).GetMethod(\"FindParkingSpaceBuilding\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Quaternion).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(PassengerCarAI).GetMethod(\"FindParkingSpaceBuilding\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Quaternion).MakeByRefType()\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect PassengerCarAI::FindParkingSpaceBuilding\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}*/\n\n\t\t\t\tLog.Info(\"Reverse-Redirection PassengerCarAI::FindParkingSpace calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomPassengerCarAI).GetMethod(\"FindParkingSpace\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Quaternion).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(PassengerCarAI).GetMethod(\"FindParkingSpace\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Quaternion).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType()\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect PassengerCarAI::FindParkingSpace\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection PassengerCarAI::FindParkingSpaceProp calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomPassengerCarAI).GetMethod(\"FindParkingSpaceProp\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (PropInfo),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Quaternion).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(PassengerCarAI).GetMethod(\"FindParkingSpaceProp\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (PropInfo),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Quaternion).MakeByRefType()\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect PassengerCarAI::FindParkingSpaceProp\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CarAI::CheckOverlap calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomCarAI).GetMethod(\"CheckOverlap\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (Segment3),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CarAI).GetMethod(\"CheckOverlap\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (Segment3),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CarAI::CheckOverlap\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CarAI::CheckOtherVehicle calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomCarAI).GetMethod(\"CheckOtherVehicle\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static),\n\t\t\t\t\t\t\ttypeof(CarAI).GetMethod(\"CheckOtherVehicle\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CarAI::CheckOtherVehicle\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CarAI::CheckCitizen calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomCarAI).GetMethod(\"CheckCitizen\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static),\n\t\t\t\t\t\t\ttypeof(CarAI).GetMethod(\"CheckCitizen\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CarAI::CheckCitizen\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection TrainAI::InitializePath calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTrainAI).GetMethod(\"InitializePath\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(TrainAI).GetMethod(\"InitializePath\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect TrainAI::InitializePath\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection TramBaseAI::InitializePath calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTramBaseAI).GetMethod(\"InitializePath\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(TramBaseAI).GetMethod(\"InitializePath\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect TramBaseAI::InitializePath\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection TramBaseAI::UpdatePathTargetPositions calls\");\n\t\t\t\tMethodInfo sourceMethod = null, targetMethod = null;\n\t\t\t\ttry {\n\t\t\t\t\tsourceMethod = typeof(CustomTramBaseAI).GetMethod(\"InvokeUpdatePathTargetPositions\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Static,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (TramBaseAI),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (int).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull);\n\n\t\t\t\t\ttargetMethod = typeof(TramBaseAI).GetMethod(\"UpdatePathTargetPositions\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (int).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull);\n\n\t\t\t\t\tDetours.Add(new Detour(sourceMethod, targetMethod));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Could not reverse-redirect TramBaseAI::UpdatePathTargetPositions: {e} sourceMethod={sourceMethod} targetMethod={targetMethod}\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomTramBaseAI::GetMaxSpeed calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTramBaseAI).GetMethod(\"GetMaxSpeed\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(TramBaseAI).GetMethod(\"GetMaxSpeed\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomTramBaseAI::GetMaxSpeed\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomTramBaseAI::CalculateMaxSpeed calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTramBaseAI).GetMethod(\"CalculateMaxSpeed\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(TramBaseAI).GetMethod(\"CalculateMaxSpeed\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomTramBaseAI::CalculateMaxSpeed\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomTrainAI::CheckOverlap calls (1)\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTrainAI).GetMethod(\"CheckOverlap\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Segment3),\n\t\t\t\t\t\t\t\t\ttypeof (ushort)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(TrainAI).GetMethod(\"CheckOverlap\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Segment3),\n\t\t\t\t\t\t\t\t\ttypeof (ushort)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomRoadBaseAI::CheckOverlap (1)\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomTrainAI::CheckOverlap calls (2)\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTrainAI).GetMethod(\"CheckOverlap\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Segment3),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (bool).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(TrainAI).GetMethod(\"CheckOverlap\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Segment3),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (bool).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomTrainAI::CheckOverlap (2)\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection TrainAI::UpdatePathTargetPositions calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTrainAI).GetMethod(\"InvokeUpdatePathTargetPositions\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (TrainAI),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (int).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(TrainAI).GetMethod(\"UpdatePathTargetPositions\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (int).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect TrainAI::UpdatePathTargetPositions\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomTrainAI::Reverse calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTrainAI).GetMethod(\"Reverse\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(TrainAI).GetMethod(\"Reverse\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomTrainAI::Reverse\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomTrainAI::GetMaxSpeed calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTrainAI).GetMethod(\"GetMaxSpeed\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(TrainAI).GetMethod(\"GetMaxSpeed\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomTrainAI::GetMaxSpeed\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomTrainAI::CalculateMaxSpeed calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTrainAI).GetMethod(\"CalculateMaxSpeed\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(TrainAI).GetMethod(\"CalculateMaxSpeed\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\t\ttypeof (float)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomTrainAI::CalculateMaxSpeed\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomTransportLineAI::GetStopLane calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTransportLineAI).GetMethod(\"GetStopLane\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (PathUnit.Position).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (VehicleInfo.VehicleType)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(TransportLineAI).GetMethod(\"GetStopLane\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (PathUnit.Position).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (VehicleInfo.VehicleType)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomTransportLineAI::GetStopLane\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomTransportLineAI::CheckSegmentProblems calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTransportLineAI).GetMethod(\"CheckSegmentProblems\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetSegment).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(TransportLineAI).GetMethod(\"CheckSegmentProblems\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetSegment).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomTransportLineAI::CheckSegmentProblems\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomTransportLineAI::CheckNodeProblems calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomTransportLineAI).GetMethod(\"CheckNodeProblems\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetNode).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(TransportLineAI).GetMethod(\"CheckNodeProblems\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetNode).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomTransportLineAI::CheckNodeProblems\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Reverse-Redirection CustomVehicleAI::FindBestLane calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CustomVehicleAI).GetMethod(\"FindBestLane\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (PathUnit.Position)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(VehicleAI).GetMethod(\"FindBestLane\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (PathUnit.Position)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not reverse-redirect CustomVehicleAI::FindBestLane\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\t// FORWARD REDIRECTION\n\n\t\t\t\t/*Log.Info(\"Redirecting NetAI::AfterSplitOrMove\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(\n\t\t\t\t\t\t\ttypeof(NetAI).GetMethod(\"AfterSplitOrMove\"),\n\t\t\t\t\t\t\ttypeof(CustomNetAI).GetMethod(\"CustomAfterSplitOrMove\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect NetAI::AfterSplitOrMove.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}*/\n\n\t\t\t\tLog.Info(\"Redirecting Vehicle AI Calculate Segment Calls (1)\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(VehicleAI).GetMethod(\"CalculateSegmentPosition\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\ttypeof (byte),\n\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\ttypeof (byte),\n\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomVehicleAI).GetMethod(\"CustomCalculateSegmentPosition\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect VehicleAI::CalculateSegmentPosition (1).\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\n\t\t\t\tLog.Info(\"Redirecting Vehicle AI Calculate Segment Calls (2)\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(VehicleAI).GetMethod(\"CalculateSegmentPosition\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\ttypeof (byte),\n\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomVehicleAI).GetMethod(\"CustomCalculateSegmentPositionPathFinder\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect VehicleAI::CalculateSegmentPosition (2).\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting VehicleAI::UpdatePathTargetPositions calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(VehicleAI).GetMethod(\"UpdatePathTargetPositions\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (int).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\ttypeof (float)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomVehicleAI).GetMethod(\"CustomUpdatePathTargetPositions\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (int).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\ttypeof (float),\n\t\t\t\t\t\t\t\ttypeof (float)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect VehicleAI::UpdatePathTargetPositions.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection CitizenManager::ReleaseCitizenInstance calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CitizenManager).GetMethod(\"ReleaseCitizenInstance\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomCitizenManager).GetMethod(\"CustomReleaseCitizenInstance\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CitizenManager::ReleaseCitizenInstance\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection CitizenManager::ReleaseCitizen calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CitizenManager).GetMethod(\"ReleaseCitizen\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (uint)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomCitizenManager).GetMethod(\"CustomReleaseCitizen\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CitizenManager::ReleaseCitizen\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection VehicleManager::ReleaseVehicle calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(VehicleManager).GetMethod(\"ReleaseVehicle\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomVehicleManager).GetMethod(\"CustomReleaseVehicle\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect VehicleManager::ReleaseVehicle\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection VehicleManager::CreateVehicle calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(VehicleManager).GetMethod(\"CreateVehicle\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Randomizer).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (VehicleInfo),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\t\ttypeof (TransferManager.TransferReason),\n\t\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomVehicleManager).GetMethod(\"CustomCreateVehicle\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect VehicleManager::CreateVehicle calls\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting TramBaseAI Calculate Segment Calls (2)\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TramBaseAI).GetMethod(\"CalculateSegmentPosition\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\ttypeof (byte),\n\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomTramBaseAI).GetMethod(\"CustomCalculateSegmentPositionPathFinder\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TramBaseAI::CalculateSegmentPosition (2).\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting RoadBaseAI::ClickNodeButton calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(\n\t\t\t\t\t\t\ttypeof(RoadBaseAI).GetMethod(\"ClickNodeButton\"),\n\t\t\t\t\t\t\ttypeof(CustomRoadAI).GetMethod(\"CustomClickNodeButton\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect RoadBaseAI::ClickNodeButton\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting RoadBaseAI::GetTrafficLightNodeState calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(RoadBaseAI).GetMethod(\"GetTrafficLightNodeState\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetNode).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetSegment).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (NetNode.Flags).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Color).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomRoadAI).GetMethod(\"CustomGetTrafficLightNodeState\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect RoadBaseAI::GetTrafficLightNodeState\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\t//public static void CustomGetTrafficLightNodeState(ushort nodeID, ref NetNode nodeData, ushort segmentID, ref NetSegment segmentData, ref NetNode.Flags flags, ref Color color) {\n\n\t\t\t\tLog.Info(\"Redirecting RoadBaseAI.SimulationStep for nodes\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(RoadBaseAI).GetMethod(\"SimulationStep\", new[] { typeof(ushort), typeof(NetNode).MakeByRefType() }),\n\t\t\t\t\t\ttypeof(CustomRoadAI).GetMethod(\"CustomNodeSimulationStep\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect RoadBaseAI::SimulationStep.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting RoadBaseAI.SimulationStep for segments\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(RoadBaseAI).GetMethod(\"SimulationStep\", new[] { typeof(ushort), typeof(NetSegment).MakeByRefType() }),\n\t\t\t\t\t\ttypeof(CustomRoadAI).GetMethod(\"CustomSegmentSimulationStep\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect RoadBaseAI::SimulationStep.\");\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection BuildingAI::GetColor calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(BuildingAI).GetMethod(\"GetColor\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Building).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (InfoManager.InfoMode)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(CustomBuildingAI).GetMethod(\"CustomGetColor\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect BuildingAI::GetColor\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting CarAI::TrySpawn Calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CarAI).GetMethod(\"TrySpawn\",\n\t\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\ttypeof(CustomCarAI).GetMethod(\"TrySpawn\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CarAI::TrySpawn.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting CarAI Simulation Step Calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CarAI).GetMethod(\"SimulationStep\",\n\t\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3)\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\ttypeof(CustomCarAI).GetMethod(\"CustomSimulationStep\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CarAI::SimulationStep.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\t/*Log.Info(\"Redirecting CarAI::CheckOtherVehicles Calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CarAI).GetMethod(\"CheckOtherVehicles\",\n\t\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Static),\n\t\t\t\t\t\t\t\ttypeof(CustomCarAI).GetMethod(\"CustomCheckOtherVehicles\",\n\t\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Static\n\t\t\t\t\t\t\t\t)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CarAI::CheckOtherVehicles.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}*/\n\n\t\t\t\tLog.Info(\"Redirecting CommonBuildingAI::SimulationStep Calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CommonBuildingAI).GetMethod(\"SimulationStep\",\n\t\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Building).MakeByRefType()\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\ttypeof(CustomCommonBuildingAI).GetMethod(\"CustomSimulationStep\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CommonBuildingAI::SimulationStep.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting HumanAI Simulation Step Calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(HumanAI).GetMethod(\"SimulationStep\",\n\t\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3)\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\ttypeof(CustomHumanAI).GetMethod(\"CustomSimulationStep\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect HumanAI::SimulationStep.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting HumanAI::CheckTrafficLights Calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(HumanAI).GetMethod(\"CheckTrafficLights\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[] { typeof(ushort), typeof(ushort) },\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomHumanAI).GetMethod(\"CustomCheckTrafficLights\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect HumanAI::CheckTrafficLights.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting HumanAI::ArriveAtDestination Calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(HumanAI).GetMethod(\"ArriveAtDestination\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomHumanAI).GetMethod(\"CustomArriveAtDestination\",\n\t\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect HumanAI::ArriveAtDestination.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection ResidentAI::GetVehicleInfo calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(ResidentAI).GetMethod(\"GetVehicleInfo\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (VehicleInfo).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(CustomResidentAI).GetMethod(\"CustomGetVehicleInfo\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect ResidentAI::GetVehicleInfo\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection TouristAI::GetVehicleInfo calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TouristAI).GetMethod(\"GetVehicleInfo\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (VehicleInfo).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(CustomTouristAI).GetMethod(\"CustomGetVehicleInfo\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TouristAI::GetVehicleInfo\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting PassengerCarAI Simulation Step Calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(PassengerCarAI).GetMethod(\"SimulationStep\",\n\t\t\t\t\t\t\tnew[] { typeof(ushort), typeof(Vehicle).MakeByRefType(), typeof(Vector3) }),\n\t\t\t\t\t\t\ttypeof(CustomPassengerCarAI).GetMethod(\"CustomSimulationStep\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect PassengerCarAI::SimulationStep.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting PassengerCarAI UpdateParkedVehicle Calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(PassengerCarAI).GetMethod(\"UpdateParkedVehicle\",\n\t\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (VehicleParked).MakeByRefType()\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\ttypeof(CustomPassengerCarAI).GetMethod(\"CustomUpdateParkedVehicle\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect PassengerCarAI::UpdateParkedVehicle.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection PassengerCarAI::ParkVehicle calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(PassengerCarAI).GetMethod(\"ParkVehicle\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\ttypeof (byte).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(CustomPassengerCarAI).GetMethod(\"CustomParkVehicle\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect PassengerCarAI::ParkVehicle\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection PassengerCarAI::GetLocalizedStatus calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(PassengerCarAI).GetMethod(\"GetLocalizedStatus\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (InstanceID).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(CustomPassengerCarAI).GetMethod(\"CustomGetLocalizedStatus\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect PassengerCarAI::GetLocalizedStatus\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection ResidentAI::GetLocalizedStatus calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(ResidentAI).GetMethod(\"GetLocalizedStatus\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (InstanceID).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(CustomResidentAI).GetMethod(\"CustomGetLocalizedStatus\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect ResidentAI::GetLocalizedStatus\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection TouristAI::GetLocalizedStatus calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TouristAI).GetMethod(\"GetLocalizedStatus\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (InstanceID).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(CustomTouristAI).GetMethod(\"CustomGetLocalizedStatus\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TouristAI::GetLocalizedStatus\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting CargoTruckAI::SimulationStep calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CargoTruckAI).GetMethod(\"SimulationStep\",\n\t\t\t\t\t\t\t\tnew[] { typeof(ushort), typeof(Vehicle).MakeByRefType(), typeof(Vector3) }),\n\t\t\t\t\t\t\t\ttypeof(CustomCargoTruckAI).GetMethod(\"CustomSimulationStep\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CargoTruckAI::SimulationStep.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting TrainAI::SimulationStep calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TrainAI).GetMethod(\"SimulationStep\",\n\t\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3)\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\ttypeof(CustomTrainAI).GetMethod(\"CustomSimulationStep\",\n\t\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3)\n\t\t\t\t\t\t\t\t})));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TrainAI::SimulationStep.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirecting TrainAI::SimulationStep (2) calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TrainAI).GetMethod(\"SimulationStep\",\n\t\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle.Frame).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (int)\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\ttypeof(CustomTrainAI).GetMethod(\"CustomSimulationStep\",\n\t\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle.Frame).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (int)\n\t\t\t\t\t\t\t\t})));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TrainAI::SimulationStep (2).\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\t/*Log.Info(\"Redirecting TrainAI::TrySpawn Calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TrainAI).GetMethod(\"TrySpawn\",\n\t\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\ttypeof(CustomTrainAI).GetMethod(\"TrySpawn\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TrainAI::TrySpawn.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}*/\n\n\t\t\t\tLog.Info(\"Redirection TramBaseAI::SimulationStep calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TramBaseAI).GetMethod(\"SimulationStep\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(CustomTramBaseAI).GetMethod(\"CustomSimulationStep\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TramBaseAI::SimulationStep\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection TramBaseAI::SimulationStep (2) calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TramBaseAI).GetMethod(\"SimulationStep\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vehicle.Frame).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (int)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull), typeof(CustomTramBaseAI).GetMethod(\"CustomSimulationStep\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vehicle.Frame).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (int)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TramBaseAI::SimulationStep (2)\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\t/*Log.Info(\"Redirection TramBaseAI::ResetTargets calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TramBaseAI).GetMethod(\"ResetTargets\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static),\n\t\t\t\t\t\t\ttypeof(CustomTramBaseAI).GetMethod(\"CustomResetTargets\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Static)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TramBaseAI::ResetTargets\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}*/\n\n\t\t\t\t/*Log.Info(\"Redirecting TramBaseAI::TrySpawn Calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TramBaseAI).GetMethod(\"TrySpawn\",\n\t\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType()\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\ttypeof(CustomTramBaseAI).GetMethod(\"TrySpawn\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TramBaseAI::TrySpawn.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}*/\n\n\t\t\t\tLog.Info(\"Redirecting Car AI Calculate Segment Calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CarAI).GetMethod(\"CalculateSegmentPosition\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\ttypeof (byte),\n\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\ttypeof (byte),\n\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomCarAI).GetMethod(\"CustomCalculateSegmentPosition\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CarAI::CalculateSegmentPosition.\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection TramBaseAI Calculate Segment Position calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TramBaseAI).GetMethod(\"CalculateSegmentPosition\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\t\ttypeof (byte),\n\t\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\t\ttypeof (byte),\n\t\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomTramBaseAI).GetMethod(\"CustomCalculateSegmentPosition\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TramBaseAI::CalculateSegmentPosition\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\n\t\t\t\tLog.Info(\"Redirection PathFind::CalculatePath calls for non-Traffic++\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(PathFind).GetMethod(\"CalculatePath\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n#if PF2\n\t\t\t\t\t\t\ttypeof(CustomPathFind2).GetMethod(\"CalculatePath\")));\n#else\n\t\t\t\t\t\t\ttypeof(CustomPathFind).GetMethod(\"CalculatePath\")));\n#endif\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect PathFind::CalculatePath\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection PathManager::ReleasePath calls for non-Traffic++\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(PathManager).GetMethod(\"ReleasePath\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (uint)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomPathManager).GetMethod(\"ReleasePath\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect PathManager::ReleasePath\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection CarAI Calculate Segment Position calls for non-Traffic++\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CarAI).GetMethod(\"CalculateSegmentPosition\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\ttypeof (byte),\n\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomCarAI).GetMethod(\"CustomCalculateSegmentPositionPathFinder\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CarAI::CalculateSegmentPosition\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection TrainAI Calculate Segment Position calls for non-Traffic++\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TrainAI).GetMethod(\"CalculateSegmentPosition\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\ttypeof (byte),\n\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomTrainAI).GetMethod(\"TmCalculateSegmentPositionPathFinder\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TrainAI::CalculateSegmentPosition (2)\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection AmbulanceAI::StartPathFind calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(AmbulanceAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomAmbulanceAI).GetMethod(\"CustomStartPathFind\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect AmbulanceAI::StartPathFind\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection BusAI::StartPathFind calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(BusAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomBusAI).GetMethod(\"CustomStartPathFind\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect BusAI::StartPathFind\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection CarAI::StartPathFind calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CarAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomCarAI).GetMethod(\"CustomStartPathFind\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CarAI::StartPathFind\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection CargoTruckAI::StartPathFind calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CargoTruckAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomCargoTruckAI).GetMethod(\"CustomStartPathFind\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CargoTruckAI::StartPathFind\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection FireTruckAI::StartPathFind calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(FireTruckAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomFireTruckAI).GetMethod(\"CustomStartPathFind\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect FireTruckAI::StartPathFind\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection PassengerCarAI::StartPathFind(1) calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(PassengerCarAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomPassengerCarAI).GetMethod(\"CustomStartPathFind\",\n\t\t\t\t\t\t\tnew[] {\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t})));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect PassengerCarAI::StartPathFind(1)\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection PoliceCarAI::StartPathFind calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(PoliceCarAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomPoliceCarAI).GetMethod(\"CustomStartPathFind\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect PoliceCarAI::StartPathFind\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection TaxiAI::StartPathFind calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TaxiAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomTaxiAI).GetMethod(\"CustomStartPathFind\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TaxiAI::StartPathFind\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection TrainAI::StartPathFind calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TrainAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomTrainAI).GetMethod(\"CustomStartPathFind\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TrainAI::StartPathFind\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection ShipAI::StartPathFind calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(ShipAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomShipAI).GetMethod(\"CustomStartPathFind\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect ShipAI::StartPathFind\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection CitizenAI::StartPathFind calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CitizenAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (CitizenInstance).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (VehicleInfo),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomCitizenAI).GetMethod(\"CustomStartPathFind\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CitizenAI::StartPathFind\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection CitizenAI::FindPathPosition calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(CitizenAI).GetMethod(\"FindPathPosition\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance),\n\t\t\t\t\t\t\ttypeof(CustomCitizenAI).GetMethod(\"CustomFindPathPosition\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect CitizenAI::FindPathPosition\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection TransportLineAI::StartPathFind calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TransportLineAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (NetSegment).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (ItemClass.Service),\n\t\t\t\t\t\t\t\ttypeof (ItemClass.Service),\n\t\t\t\t\t\t\t\ttypeof (VehicleInfo.VehicleType),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomTransportLineAI).GetMethod(\"CustomStartPathFind\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TransportLineAI::StartPathFind\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection TramBaseAI::StartPathFind calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TramBaseAI).GetMethod(\"StartPathFind\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (Vector3),\n\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomTramBaseAI).GetMethod(\"CustomStartPathFind\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TramBaseAI::StartPathFind\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection RoadBaseAI::SetTrafficLightState calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(RoadBaseAI).GetMethod(\"SetTrafficLightState\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Static,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetSegment).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\t\ttypeof (RoadBaseAI.TrafficLightState),\n\t\t\t\t\t\t\t\t\ttypeof (RoadBaseAI.TrafficLightState),\n\t\t\t\t\t\t\t\t\ttypeof (bool),\n\t\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomRoadAI).GetMethod(\"CustomSetTrafficLightState\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect RoadBaseAI::SetTrafficLightState\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection RoadBaseAI::UpdateLanes calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(RoadBaseAI).GetMethod(\"UpdateLanes\",\n\t\t\t\t\t\t\tBindingFlags.Public | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetSegment).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomRoadAI).GetMethod(\"CustomUpdateLanes\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect RoadBaseAI::UpdateLanes\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection TrainAI::CheckNextLane calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TrainAI).GetMethod(\"CheckNextLane\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (float).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\t\ttypeof (byte),\n\t\t\t\t\t\t\t\t\ttypeof (PathUnit.Position),\n\t\t\t\t\t\t\t\t\ttypeof (uint),\n\t\t\t\t\t\t\t\t\ttypeof (byte),\n\t\t\t\t\t\t\t\t\ttypeof (Bezier3)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomTrainAI).GetMethod(\"CustomCheckNextLane\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TrainAI::CheckNextLane\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection TrainAI::ForceTrafficLights calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(TrainAI).GetMethod(\"ForceTrafficLights\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomTrainAI).GetMethod(\"CustomForceTrafficLights\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (Vehicle).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (bool)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect TrainAI::CheckNextLane\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\t// TODO remove\n\t\t\t\t/*Log.Info(\"Redirection NetManager::FinalizeNode calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(NetManager).GetMethod(\"FinalizeNode\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetNode).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomNetManager).GetMethod(\"CustomFinalizeNode\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect NetManager::FinalizeNode\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}*/\n\n\t\t\t\tLog.Info(\"Redirection NetManager::FinalizeSegment calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(NetManager).GetMethod(\"FinalizeSegment\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetSegment).MakeByRefType()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomNetManager).GetMethod(\"CustomFinalizeSegment\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect NetManager::FinalizeSegment\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n#if DEBUGBUSBUG\n\t\t\t\t// TODO remove\n\t\t\t\tLog.Info(\"Redirection NetManager::MoveNode calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(NetManager).GetMethod(\"MoveNode\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetNode).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomNetManager).GetMethod(\"CustomMoveNode\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (NetNode).MakeByRefType(),\n\t\t\t\t\t\t\t\t\ttypeof (Vector3)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect NetManager::MoveNode\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n#endif\n\n\t\t\t\tLog.Info(\"Redirection NetManager::UpdateSegment calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(NetManager).GetMethod(\"UpdateSegment\",\n\t\t\t\t\t\t\tBindingFlags.NonPublic | BindingFlags.Instance,\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\tnew[]\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (ushort),\n\t\t\t\t\t\t\t\t\ttypeof (int),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\ttypeof(CustomNetManager).GetMethod(\"CustomUpdateSegment\")));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect NetManager::UpdateSegment\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection Vehicle::Spawn calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(Vehicle).GetMethod(\"Spawn\", BindingFlags.Public | BindingFlags.Instance), typeof(CustomVehicle).GetMethod(\"Spawn\", BindingFlags.Public | BindingFlags.Static)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect Vehicle::Spawn\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tLog.Info(\"Redirection Vehicle::Unspawn calls\");\n\t\t\t\ttry {\n\t\t\t\t\tDetours.Add(new Detour(typeof(Vehicle).GetMethod(\"Unspawn\", BindingFlags.Public | BindingFlags.Instance), typeof(CustomVehicle).GetMethod(\"Unspawn\", BindingFlags.Public | BindingFlags.Static)));\n\t\t\t\t} catch (Exception) {\n\t\t\t\t\tLog.Error(\"Could not redirect Vehicle::Unspawn\");\n\t\t\t\t\tdetourFailed = true;\n\t\t\t\t}\n\n\t\t\t\tif (detourFailed) {\n\t\t\t\t\tLog.Info(\"Detours failed\");\n\t\t\t\t\tSingleton<SimulationManager>.instance.m_ThreadingWrapper.QueueMainThread(() => {\n\t\t\t\t\t\tUIView.library.ShowModal<ExceptionPanel>(\"ExceptionPanel\").SetMessage(\"TM:PE failed to load\", \"Traffic Manager: President Edition failed to load. You can continue playing but it's NOT recommended. Traffic Manager will not work as expected.\", true);\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tLog.Info(\"Detours successful\");\n\t\t\t\t}\n\n\t\t\t\tDetourInited = true;\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnCreated(ILoading loading) {\n\t\t\t//SelfDestruct.DestructOldInstances(this);\n\n\t\t\tbase.OnCreated(loading);\n\n\t\t\tDetours = new List<Detour>();\n\t\t\tRegisteredManagers = new List<ICustomManager>();\n\t\t\tDetourInited = false;\n\t\t\tCustomPathManager = new CustomPathManager();\n\n\t\t\tRegisterCustomManagers();\n\t\t}\n\n\t\tprivate void RegisterCustomManagers() {\n\t\t\t// TODO represent data dependencies differently\n\t\t\tRegisteredManagers.Add(GeometryManager.Instance);\n\t\t\tRegisteredManagers.Add(AdvancedParkingManager.Instance);\n\t\t\tRegisteredManagers.Add(CustomSegmentLightsManager.Instance);\n\t\t\tRegisteredManagers.Add(ExtBuildingManager.Instance);\n\t\t\tRegisteredManagers.Add(ExtCitizenInstanceManager.Instance);\n\t\t\tRegisteredManagers.Add(ExtCitizenManager.Instance);\n\t\t\tRegisteredManagers.Add(TurnOnRedManager.Instance);\n\t\t\tRegisteredManagers.Add(LaneArrowManager.Instance);\n\t\t\tRegisteredManagers.Add(LaneConnectionManager.Instance);\n\t\t\tRegisteredManagers.Add(OptionsManager.Instance);\n\t\t\tRegisteredManagers.Add(ParkingRestrictionsManager.Instance);\n\t\t\tRegisteredManagers.Add(RoutingManager.Instance);\n\t\t\tRegisteredManagers.Add(SegmentEndManager.Instance);\n\t\t\tRegisteredManagers.Add(SpeedLimitManager.Instance);\n\t\t\tRegisteredManagers.Add(TrafficLightManager.Instance);\n\t\t\tRegisteredManagers.Add(TrafficLightSimulationManager.Instance);\n\t\t\tRegisteredManagers.Add(TrafficMeasurementManager.Instance);\n\t\t\tRegisteredManagers.Add(TrafficPriorityManager.Instance);\n\t\t\tRegisteredManagers.Add(UtilityManager.Instance);\n\t\t\tRegisteredManagers.Add(VehicleRestrictionsManager.Instance);\n\t\t\tRegisteredManagers.Add(VehicleStateManager.Instance);\n\t\t\tRegisteredManagers.Add(JunctionRestrictionsManager.Instance); // depends on TurnOnRedManager, TrafficLightManager, TrafficLightSimulationManager\n\t\t}\n\n\t\tpublic override void OnReleased() {\n\t\t\tbase.OnReleased();\n\n\t\t\tUIBase.ReleaseTool();\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tLog.Info(\"OnLevelUnloading\");\n\t\t\tbase.OnLevelUnloading();\n\t\t\tif (IsPathManagerReplaced) {\n\t\t\t\tCustomPathManager._instance.WaitForAllPaths();\n\t\t\t}\n\n\t\t\t/*Object.Destroy(BaseUI);\n\t\t\tBaseUI = null;\n\t\t\tObject.Destroy(TransportDemandUI);\n\t\t\tTransportDemandUI = null;*/\n\n\t\t\ttry {\n\t\t\t\tforeach (ICustomManager manager in RegisteredManagers) {\n\t\t\t\t\tLog.Info($\"OnLevelUnloading: {manager.GetType().Name}\");\n\t\t\t\t\tmanager.OnLevelUnloading();\n\t\t\t\t}\n\t\t\t\tFlags.OnLevelUnloading();\n\t\t\t\tTranslation.OnLevelUnloading();\n\t\t\t\tGlobalConfig.OnLevelUnloading();\n\n\t\t\t\t// remove vehicle button\n\t\t\t\tvar removeVehicleButtonExtender = UIView.GetAView().gameObject.GetComponent<RemoveVehicleButtonExtender>();\n\t\t\t\tif (removeVehicleButtonExtender != null) {\n\t\t\t\t\tObject.Destroy(removeVehicleButtonExtender, 10f);\n\t\t\t\t}\n\n\t\t\t\t// remove citizen instance button\n\t\t\t\tvar removeCitizenInstanceButtonExtender = UIView.GetAView().gameObject.GetComponent<RemoveCitizenInstanceButtonExtender>();\n\t\t\t\tif (removeCitizenInstanceButtonExtender != null) {\n\t\t\t\t\tObject.Destroy(removeCitizenInstanceButtonExtender, 10f);\n\t\t\t\t}\n#if TRACE\n\t\t\t\tSingleton<CodeProfiler>.instance.OnLevelUnloading();\n#endif\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error(\"Exception unloading mod. \" + e.Message);\n\t\t\t\t// ignored - prevents collision with other mods\n\t\t\t}\n\n\t\t\trevertDetours();\n\t\t\tIsGameLoaded = false;\n\t\t}\n\n\t\tpublic override void OnLevelLoaded(LoadMode mode) {\n\t\t\tSimulationManager.UpdateMode updateMode = SimulationManager.instance.m_metaData.m_updateMode;\n\t\t\tLog.Info($\"OnLevelLoaded({mode}) called. updateMode={updateMode}\");\n\t\t\tbase.OnLevelLoaded(mode);\n\n\t\t\tLog._Debug(\"OnLevelLoaded Returned from base, calling custom code.\");\n\n\t\t\tIsGameLoaded = false;\n\t\t\tswitch (updateMode) {\n\t\t\t\tcase SimulationManager.UpdateMode.NewGameFromMap:\n\t\t\t\tcase SimulationManager.UpdateMode.NewGameFromScenario:\n\t\t\t\tcase SimulationManager.UpdateMode.LoadGame:\n\t\t\t\t\tif (BuildConfig.applicationVersion != BuildConfig.VersionToString(TrafficManagerMod.GameVersion, false)) {\n\t\t\t\t\t\tstring[] majorVersionElms = BuildConfig.applicationVersion.Split('-');\n\t\t\t\t\t\tstring[] versionElms = majorVersionElms[0].Split('.');\n\t\t\t\t\t\tuint versionA = Convert.ToUInt32(versionElms[0]);\n\t\t\t\t\t\tuint versionB = Convert.ToUInt32(versionElms[1]);\n\t\t\t\t\t\tuint versionC = Convert.ToUInt32(versionElms[2]);\n\n\t\t\t\t\t\tLog.Info($\"Detected game version v{BuildConfig.applicationVersion}\");\n\n\t\t\t\t\t\tbool isModTooOld = TrafficManagerMod.GameVersionA < versionA ||\n\t\t\t\t\t\t\t(TrafficManagerMod.GameVersionA == versionA && TrafficManagerMod.GameVersionB < versionB)/* ||\n\t\t\t\t\t\t\t(TrafficManagerMod.GameVersionA == versionA && TrafficManagerMod.GameVersionB == versionB && TrafficManagerMod.GameVersionC < versionC)*/;\n\n\t\t\t\t\t\tbool isModNewer = TrafficManagerMod.GameVersionA < versionA ||\n\t\t\t\t\t\t\t(TrafficManagerMod.GameVersionA == versionA && TrafficManagerMod.GameVersionB > versionB)/* ||\n\t\t\t\t\t\t\t(TrafficManagerMod.GameVersionA == versionA && TrafficManagerMod.GameVersionB == versionB && TrafficManagerMod.GameVersionC > versionC)*/;\n\n\t\t\t\t\t\tif (isModTooOld) {\n\t\t\t\t\t\t\tstring msg = $\"Traffic Manager: President Edition detected that you are running a newer game version ({BuildConfig.applicationVersion}) than TM:PE has been built for ({BuildConfig.VersionToString(TrafficManagerMod.GameVersion, false)}). Please be aware that TM:PE has not been updated for the newest game version yet and thus it is very likely it will not work as expected.\";\n\t\t\t\t\t\t\tLog.Error(msg);\n\t\t\t\t\t\t\tSingleton<SimulationManager>.instance.m_ThreadingWrapper.QueueMainThread(() => {\n\t\t\t\t\t\t\t\tUIView.library.ShowModal<ExceptionPanel>(\"ExceptionPanel\").SetMessage(\"TM:PE has not been updated yet\", msg, false);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (isModNewer) {\n\t\t\t\t\t\t\tstring msg = $\"Traffic Manager: President Edition has been built for game version {BuildConfig.VersionToString(TrafficManagerMod.GameVersion, false)}. You are running game version {BuildConfig.applicationVersion}. Some features of TM:PE will not work with older game versions. Please let Steam update your game.\";\n\t\t\t\t\t\t\tLog.Error(msg);\n\t\t\t\t\t\t\tSingleton<SimulationManager>.instance.m_ThreadingWrapper.QueueMainThread(() => {\n\t\t\t\t\t\t\t\tUIView.library.ShowModal<ExceptionPanel>(\"ExceptionPanel\").SetMessage(\"Your game should be updated\", msg, false);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tIsGameLoaded = true;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tLog.Info($\"OnLevelLoaded: Unsupported game mode {mode}\");\n\t\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tIsRainfallLoaded = CheckRainfallIsLoaded();\n\t\t\tIsRushHourLoaded = CheckRushHourIsLoaded();\n\n\t\t\tif (!IsPathManagerReplaced) {\n\t\t\t\ttry {\n\t\t\t\t\tLog.Info(\"Pathfinder Compatible. Setting up CustomPathManager and SimManager.\");\n\t\t\t\t\tvar pathManagerInstance = typeof(Singleton<PathManager>).GetField(\"sInstance\", BindingFlags.Static | BindingFlags.NonPublic);\n\n\t\t\t\t\tvar stockPathManager = PathManager.instance;\n\t\t\t\t\tLog._Debug($\"Got stock PathManager instance {stockPathManager.GetName()}\");\n\n\t\t\t\t\tCustomPathManager = stockPathManager.gameObject.AddComponent<CustomPathManager>();\n\t\t\t\t\tLog._Debug(\"Added CustomPathManager to gameObject List\");\n\n\t\t\t\t\tif (CustomPathManager == null) {\n\t\t\t\t\t\tLog.Error(\"CustomPathManager null. Error creating it.\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tCustomPathManager.UpdateWithPathManagerValues(stockPathManager);\n\t\t\t\t\tLog._Debug(\"UpdateWithPathManagerValues success\");\n\n\t\t\t\t\tpathManagerInstance?.SetValue(null, CustomPathManager);\n\n\t\t\t\t\tLog._Debug(\"Getting Current SimulationManager\");\n\t\t\t\t\tvar simManager =\n\t\t\t\t\t\ttypeof(SimulationManager).GetField(\"m_managers\", BindingFlags.Static | BindingFlags.NonPublic)?\n\t\t\t\t\t\t\t.GetValue(null) as FastList<ISimulationManager>;\n\n\t\t\t\t\tLog._Debug(\"Removing Stock PathManager\");\n\t\t\t\t\tsimManager?.Remove(stockPathManager);\n\n\t\t\t\t\tLog._Debug(\"Adding Custom PathManager\");\n\t\t\t\t\tsimManager?.Add(CustomPathManager);\n\n\t\t\t\t\tObject.Destroy(stockPathManager, 10f);\n\n\t\t\t\t\tLog._Debug(\"Should be custom: \" + Singleton<PathManager>.instance.GetType().ToString());\n\n\t\t\t\t\tIsPathManagerReplaced = true;\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tstring error = \"Traffic Manager: President Edition failed to load. You can continue playing but it's NOT recommended. Traffic Manager will not work as expected.\";\n\t\t\t\t\tLog.Error(error);\n\t\t\t\t\tLog.Error($\"Path manager replacement error: {ex.ToString()}\");\n\t\t\t\t\tSingleton<SimulationManager>.instance.m_ThreadingWrapper.QueueMainThread(() => {\n\t\t\t\t\t\tUIView.library.ShowModal<ExceptionPanel>(\"ExceptionPanel\").SetMessage(\"TM:PE failed to load\", error, true);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tLog.Info(\"Adding Controls to UI.\");\n\t\t\tif (BaseUI == null) {\n\t\t\t\tLog._Debug(\"Adding UIBase instance.\");\n\t\t\t\tBaseUI = ToolsModifierControl.toolController.gameObject.AddComponent<UIBase>();\n\t\t\t}\n\n\t\t\t// Init transport demand UI\n\t\t\tif (TransportDemandUI == null) {\n\t\t\t\tvar uiView = UIView.GetAView();\n\t\t\t\tTransportDemandUI = (UITransportDemand)uiView.AddUIComponent(typeof(UITransportDemand));\n\t\t\t}\n\n\t\t\t// add \"remove vehicle\" button\n\t\t\tUIView.GetAView().gameObject.AddComponent<RemoveVehicleButtonExtender>();\n\n\t\t\t// add \"remove citizen instance\" button\n\t\t\tUIView.GetAView().gameObject.AddComponent<RemoveCitizenInstanceButtonExtender>();\n\t\t\t\n\t\t\tinitDetours();\n\n\t\t\t//Log.Info(\"Fixing non-created nodes with problems...\");\n\t\t\t//FixNonCreatedNodeProblems();\n\n\t\t\tLog.Info(\"Notifying managers...\");\n\t\t\tforeach (ICustomManager manager in RegisteredManagers) {\n\t\t\t\tLog.Info($\"OnLevelLoading: {manager.GetType().Name}\");\n\t\t\t\tmanager.OnLevelLoading();\n\t\t\t}\n\n\t\t\t//InitTool();\n\t\t\t//Log._Debug($\"Current tool: {ToolManager.instance.m_properties.CurrentTool}\");\n\n\t\t\tLog.Info(\"OnLevelLoaded complete.\");\n\t\t}\n\n\t\t/*private void FixNonCreatedNodeProblems() {\n\t\t\tfor (int nodeId = 0; nodeId < NetManager.MAX_NODE_COUNT; ++nodeId) {\n\t\t\t\tif ((NetManager.instance.m_nodes.m_buffer[nodeId].m_flags & NetNode.Flags.Created) == NetNode.Flags.None) {\n\t\t\t\t\tNetManager.instance.m_nodes.m_buffer[nodeId].m_problems = Notification.Problem.None;\n\t\t\t\t\tNetManager.instance.m_nodes.m_buffer[nodeId].m_flags = NetNode.Flags.None;\n\t\t\t\t}\n\t\t\t}\n\t\t}*/\n\n\t\tprivate bool CheckRainfallIsLoaded() {\n\t\t\treturn Check3rdPartyModLoaded(\"Rainfall\");\n\t\t}\n\n\t\tprivate bool CheckRushHourIsLoaded() {\n\t\t\treturn Check3rdPartyModLoaded(\"RushHour\", true);\n\t\t}\n\n\t\tprivate bool Check3rdPartyModLoaded(string namespaceStr, bool printAll=false) {\n\t\t\tbool thirdPartyModLoaded = false;\n\n\t\t\tvar loadingWrapperLoadingExtensionsField = typeof(LoadingWrapper).GetField(\"m_LoadingExtensions\", BindingFlags.NonPublic | BindingFlags.Instance);\n\t\t\tList<ILoadingExtension> loadingExtensions = null;\n\t\t\tif (loadingWrapperLoadingExtensionsField != null) {\n\t\t\t\tloadingExtensions = (List<ILoadingExtension>)loadingWrapperLoadingExtensionsField.GetValue(Singleton<LoadingManager>.instance.m_LoadingWrapper);\n\t\t\t} else {\n\t\t\t\tLog.Warning(\"Could not get loading extensions field\");\n\t\t\t}\n\n\t\t\tif (loadingExtensions != null) {\n\t\t\t\tforeach (ILoadingExtension extension in loadingExtensions) {\n\t\t\t\t\tif (printAll)\n\t\t\t\t\t\tLog.Info($\"Detected extension: {extension.GetType().Name} in namespace {extension.GetType().Namespace}\");\n\t\t\t\t\tif (extension.GetType().Namespace == null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tvar nsStr = extension.GetType().Namespace.ToString();\n\t\t\t\t\tif (namespaceStr.Equals(nsStr)) {\n\t\t\t\t\t\tLog.Info($\"The mod '{namespaceStr}' has been detected.\");\n\t\t\t\t\t\tthirdPartyModLoaded = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog._Debug(\"Could not get loading extensions\");\n\t\t\t}\n\n\t\t\treturn thirdPartyModLoaded;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/AbstractCustomManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing GenericGameBridge.Factory;\nusing CSUtil.Commons;\n\nnamespace TrafficManager.Manager {\n\t/// <summary>\n\t/// Abstract manager class, supports events before/after loading/saving.\n\t/// \n\t/// Event sequences:\n\t/// \n\t/// Startup / Level loading:\n\t///\t\t1. OnInit (TODO) ->\n\t///\t\t2. {Flags|NodeGeometry|SegmentGeometry}.OnBeforeLoadData ->\n\t///\t\t3. OnBeforeLoadData ->\n\t///\t\t4. (SerializableDataExtension loads custom game data) ->\n\t///\t\t5. OnAfterLoadData ->\n\t///\t\t6. (LoadingManager sets up detours) ->\n\t///\t\t7. OnLevelLoading\n\t///\tSaving:\n\t///\t\t1. OnBeforeSaveData ->\n\t///\t\t2. (SerializableDataExtension saves custom game data) ->\n\t///\t\t3. OnAfterSaveData\n\t///\tLevel unloading:\n\t///\t\t1. (LoadingManager releases detours) ->\n\t///\t\t2. OnLevelUnloading\n\t/// </summary>\n\tpublic abstract class AbstractCustomManager : ICustomManager {\n\t\tpublic IServiceFactory Services {\n\t\t\tget {\n\t\t\t\treturn Constants.ServiceFactory;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Performs actions after game data has been loaded\n\t\t/// </summary>\n\t\tpublic virtual void OnAfterLoadData() {\n\t\t\t\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Performs actions after game data has been saved\n\t\t/// </summary>\n\t\tpublic virtual void OnAfterSaveData() {\n\t\t\t\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Performs actions before game data is going to be loaded\n\t\t/// </summary>\n\t\tpublic virtual void OnBeforeLoadData() {\n\t\t\t\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Performs actions before game data is going to be saved\n\t\t/// </summary>\n\t\tpublic virtual void OnBeforeSaveData() {\n\t\t\t\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Performs actions after a game has been loaded\n\t\t/// </summary>\n\t\tpublic virtual void OnLevelLoading() {\n\t\t\t\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Performs actions after a game has been unloaded\n\t\t/// </summary>\n\t\tpublic virtual void OnLevelUnloading() {\n\t\t\t\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Prints information for debugging purposes\n\t\t/// </summary>\n\t\tprotected virtual void InternalPrintDebugInfo() {\n\n\t\t}\n\n\t\tpublic void PrintDebugInfo() {\n\t\t\tLog._Debug($\"=== {this.GetType().Name}.PrintDebugInfo() *START* ===\");\n\t\t\tInternalPrintDebugInfo();\n\t\t\tLog._Debug($\"=== {this.GetType().Name}.PrintDebugInfo() *END* ===\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/AbstractFeatureManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\t/// <summary>\n\t/// Helper class to ensure that events are always handled in the simulation thread\n\t/// </summary>\n\tpublic abstract class AbstractFeatureManager : AbstractCustomManager, IFeatureManager {\n\t\tpublic void OnDisableFeature() {\n\t\t\tServices.SimulationService.AddAction(() => {\n\t\t\t\tOnDisableFeatureInternal();\n\t\t\t});\n\t\t}\n\n\t\tpublic void OnEnableFeature() {\n\t\t\tServices.SimulationService.AddAction(() => {\n\t\t\t\tOnEnableFeatureInternal();\n\t\t\t});\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Executes whenever the associated feature is disabled. Guaranteed to run in the simulation thread.\n\t\t/// </summary>\n\t\tprotected abstract void OnDisableFeatureInternal();\n\n\t\t/// <summary>\n\t\t/// Executes whenever the associated feature is enabled. Guaranteed to run in the simulation thread.\n\t\t/// </summary>\n\t\tprotected abstract void OnEnableFeatureInternal();\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/AbstractGeometryObservingManager.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing TrafficManager.Geometry;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Util;\nusing static TrafficManager.Geometry.Impl.NodeGeometry;\n\nnamespace TrafficManager.Manager {\n\tpublic abstract class AbstractGeometryObservingManager : AbstractCustomManager, IObserver<GeometryUpdate> {\n\t\tprivate IDisposable geoUpdateUnsubscriber = null;\n\n\t\tprivate object geoLock = new object();\n\n\t\t/// <summary>\n\t\t/// Handles an invalid segment\n\t\t/// </summary>\n\t\t/// <param name=\"geometry\">segment geometry</param>\n\t\tprotected virtual void HandleInvalidSegment(SegmentGeometry geometry) { }\n\n\t\t/// <summary>\n\t\t/// Handles a valid segment\n\t\t/// </summary>\n\t\t/// <param name=\"geometry\">segment geometry</param>\n\t\tprotected virtual void HandleValidSegment(SegmentGeometry geometry) { }\n\n\t\t/// <summary>\n\t\t/// Handles an invalid node\n\t\t/// </summary>\n\t\t/// <param name=\"geometry\">node geometry</param>\n\t\tprotected virtual void HandleInvalidNode(NodeGeometry geometry) { }\n\n\t\t/// <summary>\n\t\t/// Handles a valid node\n\t\t/// </summary>\n\t\t/// <param name=\"geometry\">node geometry</param>\n\t\tprotected virtual void HandleValidNode(NodeGeometry geometry) { }\n\n\t\t/// <summary>\n\t\t/// Handles a segment replacement\n\t\t/// </summary>\n\t\t/// <param name=\"replacement\">segment replacement</param>\n\t\t/// <param name=\"newEndGeo\">new segment end geometry</param>\n\t\tprotected virtual void HandleSegmentEndReplacement(SegmentEndReplacement replacement, SegmentEndGeometry newEndGeo) { }\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t}\n\n\t\tpublic override void OnLevelLoading() {\n\t\t\tbase.OnLevelLoading();\n\t\t\tgeoUpdateUnsubscriber = Constants.ManagerFactory.GeometryManager.Subscribe(this);\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tif (geoUpdateUnsubscriber != null) {\n\t\t\t\tgeoUpdateUnsubscriber.Dispose();\n\t\t\t}\n\t\t}\n\n\t\tpublic void OnUpdate(GeometryUpdate update) {\n\t\t\tif (update.segmentGeometry != null) {\n\t\t\t\t// Handle a segment update\n\t\t\t\tSegmentGeometry geometry = update.segmentGeometry;\n\t\t\t\tif (!geometry.IsValid()) {\n#if DEBUGGEO\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\t\tLog._Debug($\"{this.GetType().Name}.HandleInvalidSegment({geometry.SegmentId})\");\n#endif\n\t\t\t\t\tHandleInvalidSegment(geometry);\n\t\t\t\t} else {\n#if DEBUGGEO\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\t\tLog._Debug($\"{this.GetType().Name}.HandleValidSegment({geometry.SegmentId})\");\n#endif\n\t\t\t\t\tHandleValidSegment(geometry);\n\t\t\t\t}\n\t\t\t} else if (update.nodeGeometry != null) {\n\t\t\t\t// Handle a node update\n\t\t\t\tNodeGeometry geometry = update.nodeGeometry;\n\t\t\t\tif (!geometry.IsValid()) {\n#if DEBUGGEO\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\t\tLog._Debug($\"{this.GetType().Name}.HandleInvalidNode({geometry.NodeId})\");\n#endif\n\t\t\t\t\tHandleInvalidNode(geometry);\n\t\t\t\t} else {\n#if DEBUGGEO\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\t\tLog._Debug($\"{this.GetType().Name}.HandleValidNode({geometry.NodeId})\");\n#endif\n\t\t\t\t\tHandleValidNode(geometry);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Handle a segment end replacement\n\t\t\t\tSegmentEndGeometry endGeo = SegmentGeometry.Get(update.replacement.newSegmentEndId.SegmentId)?.GetEnd(update.replacement.newSegmentEndId.StartNode);\n\t\t\t\tif (endGeo != null) {\n#if DEBUGGEO\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\t\t\tLog._Debug($\"{this.GetType().Name}.HandleSegmentReplacement({update.replacement.oldSegmentEndId} -> {update.replacement.newSegmentEndId})\");\n#endif\n\t\t\t\t\tHandleSegmentEndReplacement(update.replacement, endGeo);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t~AbstractGeometryObservingManager() {\n\t\t\tif (geoUpdateUnsubscriber != null) {\n\t\t\t\tgeoUpdateUnsubscriber.Dispose();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IAdvancedParkingManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing UnityEngine;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\n\nnamespace TrafficManager.Manager {\n\t/// <summary>\n\t/// Indicates if a private car [may]/[shall]/[must not] be used\n\t/// </summary>\n\tpublic enum CarUsagePolicy {\n\t\t/// <summary>\n\t\t/// Citizens may use their own car\n\t\t/// </summary>\n\t\tAllowed,\n\t\t/// <summary>\n\t\t/// Citizens are forced to use their parked car\n\t\t/// </summary>\n\t\tForcedParked,\n\t\t/// <summary>\n\t\t/// Citizens are forced to use a pocket car\n\t\t/// </summary>\n\t\tForcedPocket,\n\t\t/// <summary>\n\t\t/// Citizens are forbidden to use their car\n\t\t/// </summary>\n\t\tForbidden\n\t}\n\n\t/// <summary>\n\t/// Indicates the current state while approaching a private car\n\t/// </summary>\n\tpublic enum ParkedCarApproachState {\n\t\t/// <summary>\n\t\t/// Citizen is not approaching their parked car\n\t\t/// </summary>\n\t\tNone,\n\t\t/// <summary>\n\t\t/// Citizen is currently approaching their parked car\n\t\t/// </summary>\n\t\tApproaching,\n\t\t/// <summary>\n\t\t/// Citizen has approaching their parked car\n\t\t/// </summary>\n\t\tApproached,\n\t\t/// <summary>\n\t\t/// Citizen failed to approach their parked car\n\t\t/// </summary>\n\t\tFailure\n\t}\n\n\t/// <summary>\n\t/// Represents the reason why a parked car could not be spawned\n\t/// </summary>\n\tpublic enum ParkingUnableReason {\n\t\t/// <summary>\n\t\t/// Parked car could be spawned\n\t\t/// </summary>\n\t\tNone,\n\t\t/// <summary>\n\t\t/// No free parking space was found\n\t\t/// </summary>\n\t\tNoSpaceFound,\n\t\t/// <summary>\n\t\t/// The maximum allowed number of parked vehicles has been reached\n\t\t/// </summary>\n\t\tLimitHit\n\t}\n\n\tpublic interface IAdvancedParkingManager : IFeatureManager {\n\t\t/// <summary>\n\t\t/// Determines the color the given building should be colorized with given the current info view mode.\n\t\t/// While the traffic view is active buildings with high parking space demand are colored red and\n\t\t/// buildings with low demand are colored green.\n\t\t/// </summary>\n\t\t/// <param name=\"buildingId\">building id</param>\n\t\t/// <param name=\"buildingData\">building data</param>\n\t\t/// <param name=\"infoMode\">current info view mode</param>\n\t\t/// <param name=\"color\">output color</param>\n\t\t/// <returns>true if a custom color should be displayed, false otherwise</returns>\n\t\tbool GetBuildingInfoViewColor(ushort buildingId, ref Building buildingData, ref ExtBuilding extBuilding, InfoManager.InfoMode infoMode, out Color? color);\n\n\t\t/// <summary>\n\t\t/// Adds Parking AI related information to the given citizen status text.\n\t\t/// </summary>\n\t\t/// <param name=\"ret\">status text to enrich</param>\n\t\t/// <param name=\"extInstance\">extended citizen instance data</param>\n\t\t/// <param name=\"extCitizen\">extended citizen data</param>\n\t\t/// <returns></returns>\n\t\tstring EnrichLocalizedCitizenStatus(string ret, ref ExtCitizenInstance extInstance, ref ExtCitizen extCitizen);\n\n\t\t/// <summary>\n\t\t/// Adds Parking AI related information to the given passenger car status text.\n\t\t/// </summary>\n\t\t/// <param name=\"ret\">status text to enrich</param>\n\t\t/// <param name=\"driverExtInstance\">extended citizen instance data</param>\n\t\t/// <returns></returns>\n\t\tstring EnrichLocalizedCarStatus(string ret, ref ExtCitizenInstance driverExtInstance);\n\n\t\t/// <summary>\n\t\t/// Merges the current calculation states of the citizen's main path and return path (while walking).\n\t\t/// If a definite calculation state can be determined path-find failure/success is handled appropriately.\n\t\t/// The returned (soft) path state indicates if further handling must be undertaken by the game.\n\t\t/// </summary>\n\t\t/// <param name=\"citizenInstanceId\">citizen instance that shall be processed</param>\n\t\t/// <param name=\"citizenInstance\">citizen instance data</param>\n\t\t/// <param name=\"extInstance\">extended citizen instance data</param>\n\t\t/// <param name=\"extCitizen\">extended citizen data</param>\n\t\t/// <param name=\"citizen\">citizen data</param>\n\t\t/// <param name=\"mainPathState\">current state of the citizen instance's main path</param>\n\t\t/// <returns>\n\t\t///\t\tIndication of how (external) game logic should treat this situation:\n\t\t///\t\t<code>Calculating</code>: Paths are still being calculated. Game must await completion.\n\t\t///\t\t<code>Ready</code>: All paths are ready and path-find success must be handled.\n\t\t///\t\t<code>FailedHard</code>: At least one path calculation failed and the failure must be handled.\n\t\t///\t\t<code>FailedSoft</code>: Path-finding must be repeated.\n\t\t///\t\t<code>Ignore</code>: Default citizen behavior must be skipped. \n\t\t///\t</returns>\n\t\tExtSoftPathState UpdateCitizenPathState(ushort citizenInstanceId, ref CitizenInstance citizenInstance, ref ExtCitizenInstance extInstance, ref ExtCitizen extCitizen, ref Citizen citizen, ExtPathState mainPathState);\n\n\t\t/// <summary>\n\t\t/// Merges the current calculation states of the citizen's main path and return path (while driving a passenger car).\n\t\t/// If a definite calculation state can be determined path-find failure/success is handled appropriately.\n\t\t/// The returned (soft) path state indicates if further handling must be undertaken by the game.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleId\">vehicle that shall be processed</param>\n\t\t/// <param name=\"vehicleData\">vehicle data</param>\n\t\t/// <param name=\"driverInstance\">driver citizen instance</param>\n\t\t/// <param name=\"driverExtInstance\">extended citizen instance data of the driving citizen</param>\n\t\t/// <param name=\"mainPathState\">current state of the citizen instance's main path</param>\n\t\t/// <returns>\n\t\t///\t\tIndication of how (external) game logic should treat this situation:\n\t\t///\t\t<code>Calculating</code>: Paths are still being calculated. Game must await completion.\n\t\t///\t\t<code>Ready</code>: All paths are ready and path-find success must be handled.\n\t\t///\t\t<code>FailedHard</code>: At least one path calculation failed and the failure must be handled.\n\t\t///\t\t<code>FailedSoft</code>: Path-finding must be repeated.\n\t\t///\t\t<code>Ignore</code>: Default citizen behavior must be skipped. \n\t\t/// </returns>\n\t\tExtSoftPathState UpdateCarPathState(ushort vehicleId, ref Vehicle vehicleData, ref CitizenInstance driverInstance, ref ExtCitizenInstance driverExtInstance, ExtPathState mainPathState);\n\n\t\t/// <summary>\n\t\t/// Processes a citizen that is approaching their private car.\n\t\t/// Internal state information is updated appropriately. The returned approach\n\t\t/// state indicates if the approach is finished.\n\t\t/// </summary>\n\t\t/// <param name=\"instanceId\">citizen instance that shall be processed</param>\n\t\t/// <param name=\"instanceData\">citizen instance data</param>\n\t\t/// <param name=\"extInstance\">extended citizen instance data</param>\n\t\t/// <param name=\"physicsLodRefPos\">simulation accuracy</param>\n\t\t/// <param name=\"parkedCar\">parked car data</param>\n\t\t/// <returns>\n\t\t///\t\tApproach state indication:\n\t\t///\t\t<code>Approaching</code>: The citizen is currently approaching the parked car.\n\t\t///\t\t<code>Approached</code>: The citizen has approached the car and is ready to enter it.\n\t\t///\t\t<code>Failure</code>: The approach procedure failed (currently not returned).\n\t\t/// </returns>\n\t\tParkedCarApproachState CitizenApproachingParkedCarSimulationStep(ushort instanceId, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance, Vector3 physicsLodRefPos, ref VehicleParked parkedCar);\n\n\t\t/// <summary>\n\t\t/// Processes a citizen that is approaching their target building.\n\t\t/// Internal state information is updated appropriately. The returned flag\n\t\t/// indicates if the approach is finished.\n\t\t/// </summary>\n\t\t/// <param name=\"instanceId\">citizen instance that shall be processed</param>\n\t\t/// <param name=\"instanceData\">citizen instance data</param>\n\t\t/// <param name=\"extInstance\">extended citizen instance</param>\n\t\t/// <returns>true if the citizen arrived at the target, false otherwise</returns>\n\t\tbool CitizenApproachingTargetSimulationStep(ushort instanceId, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance);\n\n\t\t/// <summary>\n\t\t/// Finds a free parking space in the vicinity of the given target position <paramref name=\"endPos\"/>\n\t\t/// for the given citizen instance <paramref name=\"extDriverInstance\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"endPos\">target position</param>\n\t\t/// <param name=\"vehicleInfo\">vehicle type that is being used</param>\n\t\t/// <param name=\"extDriverInstance\">cititzen instance that is driving the car</param>\n\t\t/// <param name=\"homeId\">Home building of the citizen (may be 0 for tourists/homeless cims)</param>\n\t\t/// <param name=\"goingHome\">Specifies if the citizen is going home</param>\n\t\t/// <param name=\"vehicleId\">Vehicle that is being used (used for logging)</param>\n\t\t/// <param name=\"allowTourists\">If true, method fails if given citizen is a tourist (TODO remove this parameter)</param>\n\t\t/// <param name=\"parkPos\">parking position (output)</param>\n\t\t/// <param name=\"endPathPos\">sidewalk path position near parking space (output). only valid if <paramref name=\"calculateEndPos\"/> yields false.</param>\n\t\t/// <param name=\"calculateEndPos\">if false, a parking space path position could be calculated (TODO negate & rename parameter)</param>\n\t\t/// <returns>true if a parking space could be found, false otherwise</returns>\n\t\tbool FindParkingSpaceForCitizen(Vector3 endPos, VehicleInfo vehicleInfo, ref ExtCitizenInstance extDriverInstance, ushort homeId, bool goingHome, ushort vehicleId, bool allowTourists, out Vector3 parkPos, ref PathUnit.Position endPathPos, out bool calculateEndPos);\n\n\t\t/// <summary>\n\t\t/// Tries to relocate the given parked car (<paramref name=\"parkedVehicleId\"/>, <paramref name=\"parkedVehicle\"/>)\n\t\t/// within the vicinity of the given reference position <paramref name=\"refPos\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"parkedVehicleId\">parked vehicle id</param>\n\t\t/// <param name=\"parkedVehicle\">parked vehicle data</param>\n\t\t/// <param name=\"refPos\">reference position</param>\n\t\t/// <param name=\"maxDistance\">maximum allowed distance between reference position and parking space location</param>\n\t\t/// <param name=\"homeId\">Home building id of the citizen (For residential buildings, parked cars may only spawn at the home building)</param>\n\t\t/// <returns><code>true</code> if the parked vehicle was relocated, <code>false</code> otherwise</returns>\n\t\tbool TryMoveParkedVehicle(ushort parkedVehicleId, ref VehicleParked parkedVehicle, Vector3 refPos, float maxDistance, ushort homeId);\n\n\t\t/// <summary>\n\t\t/// Tries to spawn a parked passenger car for the given citizen <paramref name=\"citizenId\"/>\n\t\t/// in the vicinity of the given position <paramref name=\"refPos\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"citizenId\">Citizen that requires a parked car</param>\n\t\t/// <param name=\"homeId\">Home building id of the citizen (For residential buildings, parked cars may only spawn at the home building)</param>\n\t\t/// <param name=\"refPos\">Reference position</param>\n\t\t/// <param name=\"vehicleInfo\">Vehicle type to spawn</param>\n\t\t/// <param name=\"parkPos\">Parked vehicle position (output)</param>\n\t\t/// <param name=\"reason\">Indicates the reason why no car could be spawned when the method returns false</param>\n\t\t/// <returns>true if a passenger car could be spawned, false otherwise</returns>\n\t\tbool TrySpawnParkedPassengerCar(uint citizenId, ushort homeId, Vector3 refPos, VehicleInfo vehicleInfo, out Vector3 parkPos, out ParkingUnableReason reason);\n\n\t\t/// <summary>\n\t\t/// Tries to spawn a parked passenger car for the given citizen <paramref name=\"citizenId\"/>\n\t\t/// at a road segment in the vicinity of the given position <paramref name=\"refPos\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"citizenId\">Citizen that requires a parked car</param>\n\t\t/// <param name=\"refPos\">Reference position</param>\n\t\t/// <param name=\"vehicleInfo\">Vehicle type to spawn</param>\n\t\t/// <param name=\"parkPos\">Parked vehicle position (output)</param>\n\t\t/// <param name=\"reason\">Indicates the reason why no car could be spawned when the method returns false</param>\n\t\t/// <returns>true if a passenger car could be spawned, false otherwise</returns>\n\t\tbool TrySpawnParkedPassengerCarRoadSide(uint citizenId, Vector3 refPos, VehicleInfo vehicleInfo, out Vector3 parkPos, out ParkingUnableReason reason);\n\n\t\t/// <summary>\n\t\t/// Tries to spawn a parked passenger car for the given citizen <paramref name=\"citizenId\"/>\n\t\t/// at a building in the vicinity of the given position <paramref name=\"refPos\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"citizenId\">Citizen that requires a parked car</param>\n\t\t/// <param name=\"homeId\">Home building id of the citizen (For residential buildings, parked cars may only spawn at the home building)</param>\n\t\t/// <param name=\"refPos\">Reference position</param>\n\t\t/// <param name=\"vehicleInfo\">Vehicle type to spawn</param>\n\t\t/// <param name=\"parkPos\">Parked vehicle position (output)</param>\n\t\t/// <param name=\"reason\">Indicates the reason why no car could be spawned when the method returns false</param>\n\t\t/// <returns>true if a passenger car could be spawned, false otherwise</returns>\n\t\tbool TrySpawnParkedPassengerCarBuilding(uint citizenId, ushort homeId, Vector3 refPos, VehicleInfo vehicleInfo, out Vector3 parkPos, out ParkingUnableReason reason);\n\n\t\t/// <summary>\n\t\t/// Tries to find a parking space in the broaded vicinity of the given position <paramref name=\"targetPos\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"targetPos\">Target position that is used as a center point for the search procedure</param>\n\t\t/// <param name=\"vehicleInfo\">Vehicle that shall be parked (used for gathering vehicle geometry information)</param>\n\t\t/// <param name=\"homeId\">Home building id of the citizen (citizens are not allowed to park their car on foreign residential premises)</param>\n\t\t/// <param name=\"vehicleId\">Vehicle that shall be parked</param>\n\t\t/// <param name=\"maxDist\">maximum allowed distance between target position and parking space location</param>\n\t\t/// <param name=\"parkingSpaceLocation\">identified parking space location type (only valid if method returns true)</param>\n\t\t/// <param name=\"parkingSpaceLocationId\">identified parking space location identifier (only valid if method returns true)</param>\n\t\t/// <param name=\"parkPos\">identified parking space position (only valid if method returns true)</param>\n\t\t/// <param name=\"parkRot\">identified parking space rotation (only valid if method returns true)</param>\n\t\t/// <param name=\"parkOffset\">identified parking space offset (only valid if method returns true)</param>\n\t\t/// <returns>true if a parking space could be found, false otherwise</returns>\n\t\tbool FindParkingSpaceInVicinity(Vector3 targetPos, VehicleInfo vehicleInfo, ushort homeId, ushort vehicleId, float maxDist, out ExtParkingSpaceLocation parkingSpaceLocation, out ushort parkingSpaceLocationId, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset);\n\n\t\t/// <summary>\n\t\t/// Tries to find a parking space for a moving vehicle at a given segment. The search\n\t\t/// is restricted to the given segment.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleInfo\">vehicle that shall be parked (used for gathering vehicle geometry information)</param>\n\t\t/// <param name=\"ignoreParked\">if true, already parked vehicles are ignored</param>\n\t\t/// <param name=\"segmentId\">segment to search on</param>\n\t\t/// <param name=\"refPos\">current vehicle position</param>\n\t\t/// <param name=\"parkPos\">identified parking space position (only valid if method returns true)</param>\n\t\t/// <param name=\"parkRot\">identified parking space rotation (only valid if method returns true)</param>\n\t\t/// <param name=\"parkOffset\">identified parking space offset (only valid if method returns true)</param>\n\t\t/// <param name=\"laneId\">identified parking space lane id (only valid if method returns true)</param>\n\t\t/// <param name=\"laneIndex\">identified parking space lane index (only valid if method returns true)</param>\n\t\t/// <returns>true if a parking space could be found, false otherwise</returns>\n\t\tbool FindParkingSpaceRoadSideForVehiclePos(VehicleInfo vehicleInfo, ushort ignoreParked, ushort segmentId, Vector3 refPos, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset, out uint laneId, out int laneIndex);\n\n\t\t/// <summary>\n\t\t/// Tries to find a road-side parking space in the vicinity of the given position <paramref name=\"refPos\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"ignoreParked\">if true, already parked vehicles are ignored</param>\n\t\t/// <param name=\"refPos\">Target position that is used as a center point for the search procedure</param>\n\t\t/// <param name=\"width\">vehicle width</param>\n\t\t/// <param name=\"length\">vehicle length</param>\n\t\t/// <param name=\"maxDistance\">Maximum allowed distance between the target position and the parking space</param>\n\t\t/// <param name=\"parkPos\">identified parking space position (only valid if method returns true)</param>\n\t\t/// <param name=\"parkRot\">identified parking space rotation (only valid if method returns true)</param>\n\t\t/// <param name=\"parkOffset\">identified parking space offset (only valid if method returns true)</param>\n\t\t/// <returns>true if a parking space could be found, false otherwise</returns>\n\t\tbool FindParkingSpaceRoadSide(ushort ignoreParked, Vector3 refPos, float width, float length, float maxDistance, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset);\n\n\t\t/// <summary>\n\t\t/// Tries to find a parking space at a building in the vicinity of the given position <paramref name=\"targetPos\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleInfo\">vehicle that shall be parked (used for gathering vehicle geometry information)</param>\n\t\t/// <param name=\"homeID\">Home building id of the citizen (citizens are not allowed to park their car on foreign residential premises)</param>\n\t\t/// <param name=\"ignoreParked\">if true, already parked vehicles are ignored</param>\n\t\t/// <param name=\"segmentId\">if != 0, the building is forced to be \"accessible\" from this segment (where accessible means \"close enough\")</param>\n\t\t/// <param name=\"refPos\">Target position that is used as a center point for the search procedure</param>\n\t\t/// <param name=\"maxBuildingDistance\">Maximum allowed distance between the target position and the parking building</param>\n\t\t/// <param name=\"maxParkingSpaceDistance\">Maximum allowed distance between the target position and the parking space</param>\n\t\t/// <param name=\"parkPos\">identified parking space position (only valid if method returns true)</param>\n\t\t/// <param name=\"parkRot\">identified parking space rotation (only valid if method returns true)</param>\n\t\t/// <param name=\"parkOffset\">identified parking space offset (only valid if method returns true and a segment id was given)</param>\n\t\t/// <returns>true if a parking space could be found, false otherwise</returns>\n\t\tbool FindParkingSpaceBuilding(VehicleInfo vehicleInfo, ushort homeID, ushort ignoreParked, ushort segmentId, Vector3 refPos, float maxBuildingDistance, float maxParkingSpaceDistance, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset);\n\n\t\t/// <summary>\n\t\t/// Tries to find a parking space prop that belongs to the given building <paramref name=\"buildingID\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleInfo\">vehicle that shall be parked (used for gathering vehicle geometry information)</param>\n\t\t/// <param name=\"homeID\">Home building id of the citizen (citizens are not allowed to park their car on foreign residential premises)</param>\n\t\t/// <param name=\"ignoreParked\">if true, already parked vehicles are ignored</param>\n\t\t/// <param name=\"buildingID\">Building that is queried</param>\n\t\t/// <param name=\"building\">Building data</param>\n\t\t/// <param name=\"segmentId\">if != 0, the building is forced to be \"accessible\" from this segment (where accessible means \"close enough\")</param>\n\t\t/// <param name=\"refPos\">Target position that is used as a center point for the search procedure</param>\n\t\t/// <param name=\"maxDistance\">Maximum allowed distance between the target position and the parking space</param>\n\t\t/// <param name=\"randomize\">If true, search is randomized such that not always only the closest parking space is selected.</param>\n\t\t/// <param name=\"parkPos\">identified parking space position (only valid if method returns true)</param>\n\t\t/// <param name=\"parkRot\">identified parking space rotation (only valid if method returns true)</param>\n\t\t/// <param name=\"parkOffset\">identified parking space offset (only valid if method returns true and a segment id was given)</param>\n\t\t/// <returns>true if a parking space could be found, false otherwise</returns>\n\t\tbool FindParkingSpacePropAtBuilding(VehicleInfo vehicleInfo, ushort homeID, ushort ignoreParked, ushort buildingID, ref Building building, ushort segmentId, Vector3 refPos, ref float maxDistance, bool randomize, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset);\n\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/ICustomDataManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic interface ICustomDataManager<T> {\n\t\t// TODO documentation\n\t\tbool LoadData(T data);\n\t\tT SaveData(ref bool success);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/ICustomManager.cs",
    "content": "﻿using GenericGameBridge.Factory;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic interface ICustomManager {\n\t\t// TODO documentation\n\t\tIServiceFactory Services { get; }\n\t\tvoid OnBeforeLoadData();\n\t\tvoid OnAfterLoadData();\n\t\tvoid OnBeforeSaveData();\n\t\tvoid OnAfterSaveData();\n\t\tvoid OnLevelLoading();\n\t\tvoid OnLevelUnloading();\n\t\tvoid PrintDebugInfo();\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/ICustomSegmentLightsManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Traffic;\nusing TrafficManager.TrafficLight;\n\nnamespace TrafficManager.Manager {\n\tpublic interface ICustomSegmentLightsManager {\n\t\t// TODO documentation\n\t\tICustomSegmentLights GetSegmentLights(ushort nodeId, ushort segmentId);\n\t\tICustomSegmentLights GetSegmentLights(ushort segmentId, bool startNode, bool add = true, RoadBaseAI.TrafficLightState lightState = RoadBaseAI.TrafficLightState.Red);\n\t\tICustomSegmentLights GetOrLiveSegmentLights(ushort segmentId, bool startNode);\n\t\tvoid AddNodeLights(ushort nodeId);\n\t\tvoid RemoveNodeLights(ushort nodeId);\n\t\tvoid RemoveSegmentLights(ushort segmentId);\n\t\tvoid RemoveSegmentLight(ushort segmentId, bool startNode);\n\t\tbool IsSegmentLight(ushort segmentId, bool startNode);\n\t\tvoid SetLightMode(ushort segmentId, bool startNode, ExtVehicleType vehicleType, LightMode mode);\n\t\tbool ApplyLightModes(ushort segmentId, bool startNode, ICustomSegmentLights otherLights);\n\t\tbool SetSegmentLights(ushort nodeId, ushort segmentId, ICustomSegmentLights lights);\n\t\tshort ClockwiseIndexOfSegmentEnd(ISegmentEndId endId);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IExtBuildingManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic interface IExtBuildingManager {\n\t\t// TODO define me!\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IExtCitizenInstanceManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Traffic.Data;\nusing UnityEngine;\n\nnamespace TrafficManager.Manager {\n\tpublic interface IExtCitizenInstanceManager {\n\t\t// TODO define me!\n\t\tvoid ResetInstance(ushort instanceId);\n\t\t/// <summary>\n\t\t/// Determines whether the given citizen instance is located at an outside connection based on the given start position.\n\t\t/// </summary>\n\t\t/// <param name=\"instanceId\">citizen instance id</param>\n\t\t/// <param name=\"instanceData\">citizen instance data</param>\n\t\t/// <param name=\"extInstance\">extended citizen instance data</param>\n\t\t/// <param name=\"startPos\">start position</param>\n\t\t/// <returns><code>true</code> if the citizen instance is located at an outside connection, <code>false</code> otherwise</returns>\n\t\tbool IsAtOutsideConnection(ushort instanceId, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance, Vector3 startPos);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IExtCitizenManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic interface IExtCitizenManager {\n\t\t// TODO define me!\n\t\tvoid ResetCitizen(uint citizenId);\n\n\t\t/// <summary>\n\t\t/// Called whenever a citizen reaches their destination building.\n\t\t/// </summary>\n\t\t/// <param name=\"citizenId\">citizen id</param>\n\t\t/// <param name=\"citizen\">citizen data</param>\n\t\tvoid OnArriveAtDestination(uint citizenId, ref Citizen citizen);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IFeatureManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\t/// <summary>\n\t/// Represents a manager that handles logic bound to a certain feature\n\t/// </summary>\n\tpublic interface IFeatureManager {\n\t\t/// <summary>\n\t\t/// Handles disabling the managed feature \n\t\t/// </summary>\n\t\tvoid OnDisableFeature();\n\n\t\t/// <summary>\n\t\t/// Handles enabling the managed feature\n\t\t/// </summary>\n\t\tvoid OnEnableFeature();\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IGeometryManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.Util;\nusing static TrafficManager.Geometry.Impl.NodeGeometry;\n\nnamespace TrafficManager.Manager {\n\tpublic struct GeometryUpdate {\n\t\tpublic SegmentGeometry segmentGeometry { get; private set; }\n\t\tpublic NodeGeometry nodeGeometry { get; private set; }\n\t\tpublic SegmentEndReplacement replacement { get; private set; }\n\n\t\tpublic GeometryUpdate(SegmentGeometry segmentGeometry) {\n\t\t\tthis.segmentGeometry = segmentGeometry;\n\t\t\tnodeGeometry = null;\n\t\t\treplacement = default(SegmentEndReplacement);\n\t\t}\n\n\t\tpublic GeometryUpdate(NodeGeometry nodeGeometry) {\n\t\t\tthis.nodeGeometry = nodeGeometry;\n\t\t\tsegmentGeometry = null;\n\t\t\treplacement = default(SegmentEndReplacement);\n\t\t}\n\n\t\tpublic GeometryUpdate(SegmentEndReplacement replacement) {\n\t\t\tthis.replacement = replacement;\n\t\t\tsegmentGeometry = null;\n\t\t\tnodeGeometry = null;\n\t\t}\n\t}\n\n\tpublic interface IGeometryManager {\n\t\t// TODO define me!\n\t\tvoid SimulationStep(bool onylFirstPass=false);\n\t\tvoid OnUpdateSegment(SegmentGeometry geo);\n\t\tvoid OnSegmentEndReplacement(SegmentEndReplacement replacement);\n\t\tIDisposable Subscribe(IObserver<GeometryUpdate> observer);\n\t\tvoid MarkAsUpdated(SegmentGeometry geometry, bool updateNodes = true);\n\t\tvoid MarkAsUpdated(NodeGeometry geometry, bool updateSegments = false);\n\t\tvoid MarkAllAsUpdated();\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IJunctionRestrictionsManager.cs",
    "content": "using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic interface IJunctionRestrictionsManager {\n\t\t/// <summary>\n\t\t/// Determines if u-turn behavior may be controlled at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if u-turns may be customized, <code>false</code> otherwise</returns>\n\t\tbool IsUturnAllowedConfigurable(ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines if turn-on-red behavior is enabled for near turns and may be controlled at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if turn-on-red may be customized for near turns, <code>false</code> otherwise</returns>\n\t\tbool IsNearTurnOnRedAllowedConfigurable(ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines if turn-on-red behavior is enabled for far turns and may be controlled at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if turn-on-red may be customized for far turns, <code>false</code> otherwise</returns>\n\t\tbool IsFarTurnOnRedAllowedConfigurable(ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines if turn-on-red behavior is enabled for the given turn type and that it may be controlled at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"near\">for near turns?</param>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if turn-on-red may be customized, <code>false</code> otherwise</returns>\n\t\tbool IsTurnOnRedAllowedConfigurable(bool near, ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines if lane changing behavior may be controlled at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if lane changing may be customized, <code>false</code> otherwise</returns>\n\t\tbool IsLaneChangingAllowedWhenGoingStraightConfigurable(ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines if entering blocked junctions may be controlled at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if entering blocked junctions may be customized, <code>false</code> otherwise</returns>\n\t\tbool IsEnteringBlockedJunctionAllowedConfigurable(ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines if pedestrian crossings may be controlled at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if pedestrian crossings may be customized, <code>false</code> otherwise</returns>\n\t\tbool IsPedestrianCrossingAllowedConfigurable(ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines the default setting for u-turns at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if u-turns are allowed by default, <code>false</code> otherwise</returns>\n\t\tbool GetDefaultUturnAllowed(ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines the default setting for near turn-on-red at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if turn-on-red is allowed for near turns by default, <code>false</code> otherwise</returns>\n\t\tbool GetDefaultNearTurnOnRedAllowed(ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines the default setting for far turn-on-red at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if turn-on-red is allowed for far turns by default, <code>false</code> otherwise</returns>\n\t\tbool GetDefaultFarTurnOnRedAllowed(ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines the default turn-on-red setting for the given turn type at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"near\">for near turns?</param>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if turn-on-red is allowed by default, <code>false</code> otherwise</returns>\n\t\tbool GetDefaultTurnOnRedAllowed(bool near, ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines the default setting for straight lane changes at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if straight lane changes are allowed by default, <code>false</code> otherwise</returns>\n\t\tbool GetDefaultLaneChangingAllowedWhenGoingStraight(ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines the default setting for entering a blocked junction at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if entering a blocked junction is allowed by default, <code>false</code> otherwise</returns>\n\t\tbool GetDefaultEnteringBlockedJunctionAllowed(ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines the default setting for pedestrian crossings at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"node\">node data</param>\n\t\t/// <returns><code>true</code> if crossing the road is allowed by default, <code>false</code> otherwise</returns>\n\t\tbool GetDefaultPedestrianCrossingAllowed(ushort segmentId, bool startNode, ref NetNode node);\n\n\t\t/// <summary>\n\t\t/// Determines whether u-turns are allowed at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> if u-turns are allowed, <code>false</code> otherwise</returns>\n\t\tbool IsUturnAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Determines whether turn-on-red is allowed for near turns at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> if turn-on-red is allowed for near turns, <code>false</code> otherwise</returns>\n\t\tbool IsNearTurnOnRedAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Determines whether turn-on-red is allowed for far turns at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> if turn-on-red is allowed for far turns, <code>false</code> otherwise</returns>\n\t\tbool IsFarTurnOnRedAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Determines whether turn-on-red is allowed for the given turn type at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"near\">for near turns?</param>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> if turn-on-red is allowed, <code>false</code> otherwise</returns>\n\t\tbool IsTurnOnRedAllowed(bool near, ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Determines whether lane changing when going straight is allowed at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> if lane changing when going straight is allowed, <code>false</code> otherwise</returns>\n\t\tbool IsLaneChangingAllowedWhenGoingStraight(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Determines whether entering a blocked junction is allowed at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> if entering a blocked junction is allowed, <code>false</code> otherwise</returns>\n\t\tbool IsEnteringBlockedJunctionAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Determines whether crossing the road is allowed at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> if crossing the road is allowed, <code>false</code> otherwise</returns>\n\t\tbool IsPedestrianCrossingAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Retrieves the u-turn setting for the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns>ternary u-turn flag</returns>\n\t\tTernaryBool GetUturnAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Retrieves the turn-on-red setting for near turns and the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns>ternary turn-on-red flag for near turns</returns>\n\t\tTernaryBool GetNearTurnOnRedAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Retrieves the turn-on-red setting for far turns and the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns>ternary turn-on-red flag for far turns</returns>\n\t\tTernaryBool GetFarTurnOnRedAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Retrieves the turn-on-red setting for the given turn type and segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"near\">for near turns?</param>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns>ternary turn-on-red flag</returns>\n\t\tTernaryBool GetTurnOnRedAllowed(bool near, ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Retrieves the lane changing setting for the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns>ternary lane changing flag</returns>\n\t\tTernaryBool GetLaneChangingAllowedWhenGoingStraight(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Retrieves the \"enter blocked junction\" setting for the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns>ternary \"enter blocked junction\" flag</returns>\n\t\tTernaryBool GetEnteringBlockedJunctionAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Retrieves the pedestrian crossing setting for the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns>ternary pedestrian crossing flag</returns>\n\t\tTernaryBool GetPedestrianCrossingAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Switches the u-turn flag for the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool ToggleUturnAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Switches the turn-on-red flag for near turns and given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool ToggleNearTurnOnRedAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Switches the turn-on-red flag for far turns and given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool ToggleFarTurnOnRedAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Switches the turn-on-red flag for the given turn type and segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"near\">for near turns?</param>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool ToggleTurnOnRedAllowed(bool near, ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Switches the lane changing flag for the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool ToggleLaneChangingAllowedWhenGoingStraight(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Switches the \"enter blocked junction\" flag for the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool ToggleEnteringBlockedJunctionAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Switches the pedestrian crossing flag for the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool TogglePedestrianCrossingAllowed(ushort segmentId, bool startNode);\n\n\t\t/// <summary>\n\t\t/// Sets the u-turn flag for the given segment end to the given value.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"value\">new value</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool SetUturnAllowed(ushort segmentId, bool startNode, bool value);\n\n\t\t/// <summary>\n\t\t/// Sets the turn-on-red flag for near turns at the given segment end to the given value.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"value\">new value</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool SetNearTurnOnRedAllowed(ushort segmentId, bool startNode, bool value);\n\n\t\t/// <summary>\n\t\t/// Sets the turn-on-red flag for far turns at the given segment end to the given value.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"value\">new value</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool SetFarTurnOnRedAllowed(ushort segmentId, bool startNode, bool value);\n\n\t\t/// <summary>\n\t\t/// Sets the turn-on-red flag for the given turn type and segment end to the given value.\n\t\t/// </summary>\n\t\t/// <param name=\"near\">for near turns?</param>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"value\">new value</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool SetTurnOnRedAllowed(bool near, ushort segmentId, bool startNode, bool value);\n\n\t\t/// <summary>\n\t\t/// Sets the lane changing flag for the given segment end to the given value.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"value\">new value</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool SetLaneChangingAllowedWhenGoingStraight(ushort segmentId, bool startNode, bool value);\n\n\t\t/// <summary>\n\t\t/// Sets the \"enter blocked junction\" flag for the given segment end to the given value.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"value\">new value</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool SetEnteringBlockedJunctionAllowed(ushort segmentId, bool startNode, bool value);\n\n\t\t/// <summary>\n\t\t/// Sets the pedestrian crossing flag for the given segment end to the given value.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <param name=\"value\">new value</param>\n\t\t/// <returns><code>true</code> on success, <code>false</code> otherwise</returns>\n\t\tbool SetPedestrianCrossingAllowed(ushort segmentId, bool startNode, bool value);\n\n\t\t/// <summary>\n\t\t/// Updates the default values for all junction restrictions and segments.\n\t\t/// </summary>\n\t\tvoid UpdateAllDefaults();\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/ILaneArrowManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic interface ILaneArrowManager {\n\t\t// TODO define me!\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/ILaneConnectionManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic interface ILaneConnectionManager {\n\t\t// TODO define me!\n\n\t\t/// <summary>\n\t\t/// Determines whether u-turn connections exist for the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">at start node?</param>\n\t\t/// <returns><code>true</code> if u-turn connections exist, <code>false</code> otherwise</returns>\n\t\tbool HasUturnConnections(ushort segmentId, bool startNode);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IManagerFactory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Manager;\n\nnamespace TrafficManager.Manager {\n\tpublic interface IManagerFactory {\n\t\tIAdvancedParkingManager AdvancedParkingManager { get; }\n\t\tICustomSegmentLightsManager CustomSegmentLightsManager { get; }\n\t\tIExtBuildingManager ExtBuildingManager { get; }\n\t\tIExtCitizenInstanceManager ExtCitizenInstanceManager { get; }\n\t\tIExtCitizenManager ExtCitizenManager { get; }\n\t\tIJunctionRestrictionsManager JunctionRestrictionsManager { get; }\n\t\tILaneArrowManager LaneArrowManager { get; }\n\t\tILaneConnectionManager LaneConnectionManager { get; }\n\t\tIGeometryManager GeometryManager { get; }\n\t\tIOptionsManager OptionsManager { get; }\n\t\tIParkingRestrictionsManager ParkingRestrictionsManager { get; }\n\t\tIRoutingManager RoutingManager { get; }\n\t\tISegmentEndManager SegmentEndManager { get; }\n\t\tISpeedLimitManager SpeedLimitManager { get; }\n\t\tITrafficLightManager TrafficLightManager { get; }\n\t\tITrafficLightSimulationManager TrafficLightSimulationManager { get; }\n\t\tITrafficMeasurementManager TrafficMeasurementManager { get; }\n\t\tITrafficPriorityManager TrafficPriorityManager { get; }\n\t\tITurnOnRedManager TurnOnRedManager { get; }\n\t\tIUtilityManager UtilityManager { get; }\n\t\tIVehicleBehaviorManager VehicleBehaviorManager { get; }\n\t\tIVehicleRestrictionsManager VehicleRestrictionsManager { get; }\n\t\tIVehicleStateManager VehicleStateManager { get; }\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IOptionsManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\t/// <summary>\n\t/// Manages mod options\n\t/// </summary>\n\tpublic interface IOptionsManager : ICustomDataManager<byte[]> {\n\t\t/// <summary>\n\t\t/// Determines if modifications to segments may be published in the current state.\n\t\t/// </summary>\n\t\t/// <returns>true if changes may be published, false otherwise</returns>\n\t\tbool MayPublishSegmentChanges();\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IParkingRestrictionsManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic interface IParkingRestrictionsManager {\n\t\t// TODO define me!\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IRoutingManager.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic enum LaneEndTransitionType {\n\t\t/// <summary>\n\t\t/// No connection\n\t\t/// </summary>\n\t\tInvalid,\n\t\t/// <summary>\n\t\t/// Lane arrow or regular lane connection\n\t\t/// </summary>\n\t\tDefault,\n\t\t/// <summary>\n\t\t/// Custom lane connection\n\t\t/// </summary>\n\t\tLaneConnection,\n\t\t/// <summary>\n\t\t/// Relaxed connection for road vehicles [!] that do not have to follow lane arrows\n\t\t/// </summary>\n\t\tRelaxed\n\t}\n\n\tpublic struct SegmentRoutingData {\n\t\tpublic bool startNodeOutgoingOneWay;\n\t\tpublic bool endNodeOutgoingOneWay;\n\t\tpublic bool highway;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[SegmentRoutingData\\n\" +\n\t\t\t\t\"\\t\" + $\"startNodeOutgoingOneWay = {startNodeOutgoingOneWay}\\n\" +\n\t\t\t\t\"\\t\" + $\"endNodeOutgoingOneWay = {endNodeOutgoingOneWay}\\n\" +\n\t\t\t\t\"\\t\" + $\"highway = {highway}\\n\" +\n\t\t\t\t\"SegmentRoutingData]\";\n\t\t}\n\n\t\tpublic void Reset() {\n\t\t\tstartNodeOutgoingOneWay = false;\n\t\t\tendNodeOutgoingOneWay = false;\n\t\t\thighway = false;\n\t\t}\n\t}\n\n\tpublic struct LaneEndRoutingData {\n\t\tpublic bool routed;\n\t\tpublic LaneTransitionData[] transitions;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[LaneEndRoutingData\\n\" +\n\t\t\t\t\"\\t\" + $\"routed = {routed}\\n\" +\n\t\t\t\t\"\\t\" + $\"transitions = {(transitions == null ? \"<null>\" : transitions.ArrayToString())}\\n\" +\n\t\t\t\t\"LaneEndRoutingData]\";\n\t\t}\n\n\t\tpublic void Reset() {\n\t\t\trouted = false;\n\t\t\ttransitions = null;\n\t\t}\n\n\t\tpublic void RemoveTransition(uint laneId) {\n\t\t\tif (transitions == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tint index = -1;\n\t\t\tfor (int i = 0; i < transitions.Length; ++i) {\n\t\t\t\tif (transitions[i].laneId == laneId) {\n\t\t\t\t\tindex = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (index < 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (transitions.Length == 1) {\n\t\t\t\tReset();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLaneTransitionData[] newTransitions = new LaneTransitionData[transitions.Length - 1];\n\t\t\tif (index > 0) {\n\t\t\t\tArray.Copy(transitions, 0, newTransitions, 0, index);\n\t\t\t}\n\t\t\tif (index < transitions.Length - 1) {\n\t\t\t\tArray.Copy(transitions, index + 1, newTransitions, index, transitions.Length - index - 1);\n\t\t\t}\n\t\t\ttransitions = newTransitions;\n\t\t}\n\n\t\tpublic void AddTransitions(LaneTransitionData[] transitionsToAdd) {\n\t\t\tif (transitions == null) {\n\t\t\t\ttransitions = transitionsToAdd;\n\t\t\t\trouted = true;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLaneTransitionData[] newTransitions = new LaneTransitionData[transitions.Length + transitionsToAdd.Length];\n\t\t\tArray.Copy(transitions, newTransitions, transitions.Length);\n\t\t\tArray.Copy(transitionsToAdd, 0, newTransitions, transitions.Length, transitionsToAdd.Length);\n\t\t\ttransitions = newTransitions;\n\n\t\t\trouted = true;\n\t\t}\n\n\t\tpublic void AddTransition(LaneTransitionData transition) {\n\t\t\tAddTransitions(new LaneTransitionData[1] { transition });\n\t\t}\n\t}\n\n\tpublic struct LaneTransitionData {\n\t\tpublic uint laneId;\n\t\tpublic byte laneIndex;\n\t\tpublic LaneEndTransitionType type;\n\t\tpublic byte distance;\n\t\tpublic ushort segmentId;\n\t\tpublic bool startNode;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[LaneTransitionData\\n\" +\n\t\t\t\t\"\\t\" + $\"laneId = {laneId}\\n\" +\n\t\t\t\t\"\\t\" + $\"laneIndex = {laneIndex}\\n\" +\n\t\t\t\t\"\\t\" + $\"segmentId = {segmentId}\\n\" +\n\t\t\t\t\"\\t\" + $\"startNode = {startNode}\\n\" +\n\t\t\t\t\"\\t\" + $\"type = {type}\\n\" +\n\t\t\t\t\"\\t\" + $\"distance = {distance}\\n\" +\n\t\t\t\t\"LaneTransitionData]\";\n\t\t}\n\n\t\tpublic void Set(uint laneId, byte laneIndex, LaneEndTransitionType type, ushort segmentId, bool startNode, byte distance) {\n\t\t\tthis.laneId = laneId;\n\t\t\tthis.laneIndex = laneIndex;\n\t\t\tthis.type = type;\n\t\t\tthis.distance = distance;\n\t\t\tthis.segmentId = segmentId;\n\t\t\tthis.startNode = startNode;\n\t\t}\n\n\t\tpublic void Set(uint laneId, byte laneIndex, LaneEndTransitionType type, ushort segmentId, bool startNode) {\n\t\t\tSet(laneId, laneIndex, type, segmentId, startNode, 0);\n\t\t}\n\t}\n\n\tpublic interface IRoutingManager {\n\t\t// TODO documentation\n\t\tvoid SimulationStep();\n\t\tvoid RequestFullRecalculation();\n\t\tvoid RequestRecalculation(ushort segmentId, bool propagate = true);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/ISegmentEndManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Traffic;\nusing TrafficManager.TrafficLight;\n\nnamespace TrafficManager.Manager {\n\tpublic interface ISegmentEndManager {\n\t\t// TODO documentation\n\t\tISegmentEnd GetOrAddSegmentEnd(ISegmentEndId endId);\n\t\tISegmentEnd GetOrAddSegmentEnd(ushort segmentId, bool startNode);\n\t\tISegmentEnd GetSegmentEnd(ISegmentEndId endId);\n\t\tISegmentEnd GetSegmentEnd(ushort segmentId, bool startNode);\n\t\tvoid RemoveSegmentEnd(ISegmentEndId endId);\n\t\tvoid RemoveSegmentEnd(ushort segmentId, bool startNode);\n\t\tvoid RemoveSegmentEnds(ushort segmentId);\n\t\tbool UpdateSegmentEnd(ISegmentEndId endId);\n\t\tbool UpdateSegmentEnd(ushort segmentId, bool startNode);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/ISpeedLimitManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic interface ISpeedLimitManager {\n\t\t// TODO define me!\n\t\t/// <summary>\n\t\t/// Retrieves the speed limit for the given lane without locking.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"laneIndex\">lane index</param>\n\t\t/// <param name=\"laneId\">lane id</param>\n\t\t/// <param name=\"laneInfo\">lane info</param>\n\t\t/// <returns>speed limit in game units</returns>\n\t\tfloat GetLockFreeGameSpeedLimit(ushort segmentId, byte laneIndex, uint laneId, NetInfo.Lane laneInfo);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/ITrafficLightManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic enum UnableReason {\n\t\tNone,\n\t\tNoJunction,\n\t\tHasTimedLight,\n\t\tIsLevelCrossing,\n\t\tInsufficientSegments\n\t}\n\n\tpublic interface ITrafficLightManager {\n\t\t// TODO documentation\n\t\tbool AddTrafficLight(ushort nodeId, ref NetNode node);\n\t\tbool AddTrafficLight(ushort nodeId, ref NetNode node, out UnableReason reason);\n\t\tbool HasTrafficLight(ushort nodeId, ref NetNode node);\n\t\tbool IsTrafficLightEnablable(ushort nodeId, ref NetNode node, out UnableReason reason);\n\t\tbool IsTrafficLightToggleable(ushort nodeId, bool flag, ref NetNode node, out UnableReason reason);\n\t\tbool RemoveTrafficLight(ushort nodeId, ref NetNode node);\n\t\tbool RemoveTrafficLight(ushort nodeId, ref NetNode node, out UnableReason reason);\n\t\tbool SetTrafficLight(ushort nodeId, bool flag, ref NetNode node);\n\t\tbool SetTrafficLight(ushort nodeId, bool flag, ref NetNode node, out UnableReason reason);\n\t\tbool ToggleTrafficLight(ushort nodeId, ref NetNode node);\n\t\tbool ToggleTrafficLight(ushort nodeId, ref NetNode node, out UnableReason reason);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/ITrafficLightSimulationManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.TrafficLight;\n\nnamespace TrafficManager.Manager {\n\tpublic interface ITrafficLightSimulationManager {\n\t\t// TODO documentation\n\t\tbool SetUpManualTrafficLight(ushort nodeId);\n\t\tbool SetUpTimedTrafficLight(ushort nodeId, IList<ushort> nodeGroup);\n\t\tbool HasActiveSimulation(ushort nodeId);\n\t\tbool HasActiveTimedSimulation(ushort nodeId);\n\t\tbool HasSimulation(ushort nodeId);\n\t\tbool HasManualSimulation(ushort nodeId);\n\t\tbool HasTimedSimulation(ushort nodeId);\n\t\tvoid RemoveNodeFromSimulation(ushort nodeId, bool destroyGroup, bool removeTrafficLight);\n\t\tvoid SimulationStep();\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/ITrafficMeasurementManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic interface ITrafficMeasurementManager {\n\t\t// TODO define me!\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/ITrafficPriorityManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing static TrafficManager.Traffic.Data.PrioritySegment;\n\nnamespace TrafficManager.Manager {\n\tpublic interface ITrafficPriorityManager {\n\t\t// TODO define me!\n\n\t\t/// <summary>\n\t\t/// Checks if a vehicle (the target vehicle) has to wait for other incoming vehicles at a junction with priority signs.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleId\">target vehicle</param>\n\t\t/// <param name=\"vehicle\">target vehicle data</param>\n\t\t/// <param name=\"curPos\">current path position</param>\n\t\t/// <param name=\"transitNodeId\">transit node</param>\n\t\t/// <param name=\"startNode\">true if the transit node is the start node of the current segment</param>\n\t\t/// <param name=\"nextPos\">next path position</param>\n\t\t/// <param name=\"transitNode\">transit node data</param>\n\t\t/// <returns>false if the target vehicle must wait for other vehicles, true otherwise</returns>\n\t\tbool HasPriority(ushort vehicleId, ref Vehicle vehicle, ref PathUnit.Position curPos, ushort transitNodeId, bool startNode, ref PathUnit.Position nextPos, ref NetNode transitNode);\n\n\t\tPriorityType GetPrioritySign(ushort segmentId, bool startNode);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/ITurnOnRedManager.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Traffic.Data;\n\nnamespace TrafficManager.Manager {\n\tpublic interface ITurnOnRedManager {\n\t\tTurnOnRedSegments[] TurnOnRedSegments { get; }\n\n\t\t/// <summary>\n\t\t/// Retrieves the array index for the given segment end id.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"startNode\">start node</param>\n\t\t/// <returns>array index for the segment end id</returns>\n\t\tint GetIndex(ushort segmentId, bool startNode);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IUtilityManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Manager {\n\tpublic interface IUtilityManager {\n\t\t// TODO define me!\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IVehicleBehaviorManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Traffic.Data;\nusing UnityEngine;\n\nnamespace TrafficManager.Manager {\n\tpublic interface IVehicleBehaviorManager {\n\t\t// TODO define me!\n\t\t// TODO documentation\n\n\t\t/// <summary>\n\t\t/// Checks if space reservation at <paramref name=\"targetPos\"/> is allowed. When a custom traffic light is active at the transit node\n\t\t/// space reservation is only allowed if the light is not red.\n\t\t/// </summary>\n\t\t/// <param name=\"transitNodeId\">transition node id</param>\n\t\t/// <param name=\"sourcePos\">source path position</param>\n\t\t/// <param name=\"targetPos\">target path position</param>\n\t\t/// <returns></returns>\n\t\tbool IsSpaceReservationAllowed(ushort transitNodeId, PathUnit.Position sourcePos, PathUnit.Position targetPos);\n\n\t\t/// <summary>\n\t\t/// Determines if the given vehicle is driven by a reckless driver.\n\t\t/// Note that the result is cached in VehicleState for individual vehicles.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleId\"></param>\n\t\t/// <param name=\"vehicleData\"></param>\n\t\t/// <returns></returns>\n\t\tbool IsRecklessDriver(ushort vehicleId, ref Vehicle vehicleData);\n\n\t\t/// <summary>\n\t\t/// Identifies the best lane on the next segment.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleId\">queried vehicle</param>\n\t\t/// <param name=\"vehicleData\">vehicle data</param>\n\t\t/// <param name=\"vehicleState\">vehicle state</param>\n\t\t/// <param name=\"currentLaneId\">current lane id</param>\n\t\t/// <param name=\"currentPathPos\">current path position</param>\n\t\t/// <param name=\"currentSegInfo\">current segment info</param>\n\t\t/// <param name=\"next1PathPos\">1st next path position</param>\n\t\t/// <param name=\"next1SegInfo\">1st next segment info</param>\n\t\t/// <param name=\"next2PathPos\">2nd next path position</param>\n\t\t/// <param name=\"next3PathPos\">3rd next path position</param>\n\t\t/// <param name=\"next4PathPos\">4th next path position</param>\n\t\t/// <returns>target position lane index</returns>\n\t\tint FindBestLane(ushort vehicleId, ref Vehicle vehicleData, ref VehicleState vehicleState, uint currentLaneId, PathUnit.Position currentPathPos, NetInfo currentSegInfo, PathUnit.Position next1PathPos, NetInfo next1SegInfo, PathUnit.Position next2PathPos, NetInfo next2SegInfo, PathUnit.Position next3PathPos, NetInfo next3SegInfo, PathUnit.Position next4PathPos);\n\n\t\t/// <summary>\n\t\t/// Determines if the given vehicle is allowed to find an alternative lane.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleId\">queried vehicle</param>\n\t\t/// <param name=\"vehicleData\">vehicle data</param>\n\t\t/// <param name=\"vehicleState\">vehicle state</param>\n\t\t/// <returns></returns>\n\t\tbool MayFindBestLane(ushort vehicleId, ref Vehicle vehicleData, ref VehicleState vehicleState);\n\n\t\t/// <summary>\n\t\t/// Calculates the current randomization value for a vehicle.\n\t\t/// The value changes over time.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleId\">vehicle id</param>\n\t\t/// <returns>a number between 0 and 99</returns>\n\t\tuint GetTimedVehicleRand(ushort vehicleId);\n\n\t\t/// <summary>\n\t\t/// Calculates the randomization value for a vehicle.\n\t\t/// The value is static throughout the vehicle's lifetime.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleId\">vehicle id</param>\n\t\t/// <returns>a number between 0 and 99</returns>\n\t\tuint GetStaticVehicleRand(ushort vehicleId);\n\n\t\t/// <summary>\n\t\t/// Applies realistic speed multipliers to the given velocity.\n\t\t/// </summary>\n\t\t/// <param name=\"speed\">vehicle target velocity</param>\n\t\t/// <param name=\"vehicleId\">vehicle id</param>\n\t\t/// <param name=\"vehicleState\">vehicle state</param>\n\t\t/// <param name=\"vehicleInfo\">vehicle info</param>\n\t\t/// <returns>modified target velocity</returns>\n\t\tfloat ApplyRealisticSpeeds(float speed, ushort vehicleId, ref VehicleState vehicleState, VehicleInfo vehicleInfo);\n\n\t\t/// <summary>\n\t\t/// Calculates the target velocity for the given vehicle.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleId\">vehicle id</param>\n\t\t/// <param name=\"state\">vehicle state</param>\n\t\t/// <param name=\"vehicleInfo\">vehicle info</param>\n\t\t/// <param name=\"position\">current path position</param>\n\t\t/// <param name=\"segment\">segment data</param>\n\t\t/// <param name=\"pos\">current world position</param>\n\t\t/// <param name=\"maxSpeed\">vehicle target velocity</param>\n\t\t/// <returns>modified target velocity</returns>\n\t\tfloat CalcMaxSpeed(ushort vehicleId, ref VehicleState state, VehicleInfo vehicleInfo, PathUnit.Position position, ref NetSegment segment, Vector3 pos, float maxSpeed);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IVehicleRestrictionsManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Traffic;\n\nnamespace TrafficManager.Manager {\n\tpublic enum VehicleRestrictionsMode {\n\t\t/// <summary>\n\t\t/// Interpret bus lanes as \"free for all\"\n\t\t/// </summary>\n\t\tUnrestricted,\n\t\t/// <summary>\n\t\t/// Interpret bus lanes according to the configuration\n\t\t/// </summary>\n\t\tConfigured,\n\t\t/// <summary>\n\t\t/// Interpret bus lanes as restricted\n\t\t/// </summary>\n\t\tRestricted\n\t}\n\n\t/// <summary>\n\t/// Represents vehicle restrictions effect strength\n\t/// </summary>\n\tpublic enum VehicleRestrictionsAggression {\n\t\t/// <summary>\n\t\t/// Low aggression\n\t\t/// </summary>\n\t\tLow = 0,\n\t\t/// <summary>\n\t\t/// Medium aggression\n\t\t/// </summary>\n\t\tMedium = 1,\n\t\t/// <summary>\n\t\t/// High aggression\n\t\t/// </summary>\n\t\tHigh = 2,\n\t\t/// <summary>\n\t\t/// Strict aggression\n\t\t/// </summary>\n\t\tStrict = 3\n\t}\n\n\tpublic interface IVehicleRestrictionsManager {\n\t\t// TODO documentation\n\t\tvoid AddAllowedType(ushort segmentId, NetInfo segmentInfo, uint laneIndex, uint laneId, NetInfo.Lane laneInfo, ExtVehicleType vehicleType);\n\t\tExtVehicleType GetAllowedVehicleTypes(ushort segmentId, ushort nodeId, VehicleRestrictionsMode busLaneMode);\n\t\tExtVehicleType GetAllowedVehicleTypes(ushort segmentId, NetInfo segmentInfo, uint laneIndex, NetInfo.Lane laneInfo, VehicleRestrictionsMode busLaneMode);\n\t\tIDictionary<byte, ExtVehicleType> GetAllowedVehicleTypesAsDict(ushort segmentId, ushort nodeId, VehicleRestrictionsMode busLaneMode);\n\t\tHashSet<ExtVehicleType> GetAllowedVehicleTypesAsSet(ushort segmentId, ushort nodeId, VehicleRestrictionsMode busLaneMode);\n\t\tExtVehicleType GetBaseMask(uint laneId, VehicleRestrictionsMode includeBusLanes);\n\t\tExtVehicleType GetBaseMask(NetInfo.Lane laneInfo, VehicleRestrictionsMode includeBusLanes);\n\t\tExtVehicleType GetDefaultAllowedVehicleTypes(NetInfo.Lane laneInfo, VehicleRestrictionsMode busLaneMode);\n\t\tExtVehicleType GetDefaultAllowedVehicleTypes(ushort segmentId, NetInfo segmentInfo, uint laneIndex, NetInfo.Lane laneInfo, VehicleRestrictionsMode busLaneMode);\n\t\tbool IsAllowed(ExtVehicleType? allowedTypes, ExtVehicleType vehicleType);\n\t\tbool IsBicycleAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsBlimpAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsBusAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsCableCarAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsCargoTrainAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsCargoTruckAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsEmergencyAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsFerryAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsMonorailSegment(NetInfo segmentInfo);\n\t\tbool IsPassengerCarAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsPassengerTrainAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsRailLane(NetInfo.Lane laneInfo);\n\t\tbool IsRailSegment(NetInfo segmentInfo);\n\t\tbool IsRailVehicleAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsRoadLane(NetInfo.Lane laneInfo);\n\t\tbool IsRoadSegment(NetInfo segmentInfo);\n\t\tbool IsRoadVehicleAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsServiceAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsTaxiAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsTramAllowed(ExtVehicleType? allowedTypes);\n\t\tbool IsTramLane(NetInfo.Lane laneInfo);\n\t\tbool LoadData(List<Configuration.LaneVehicleTypes> data);\n\t\tvoid NotifyStartEndNode(ushort segmentId);\n\t\tvoid OnLevelUnloading();\n\t\tvoid RemoveAllowedType(ushort segmentId, NetInfo segmentInfo, uint laneIndex, uint laneId, NetInfo.Lane laneInfo, ExtVehicleType vehicleType);\n\t\tList<Configuration.LaneVehicleTypes> SaveData(ref bool success);\n\t\tvoid ToggleAllowedType(ushort segmentId, NetInfo segmentInfo, uint laneIndex, uint laneId, NetInfo.Lane laneInfo, ExtVehicleType vehicleType, bool add);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/IVehicleStateManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Traffic.Data;\n\nnamespace TrafficManager.Manager {\n\tpublic interface IVehicleStateManager {\n\t\t// TODO define me!\n\t\t// TODO documentation\n\t\tVehicleState[] VehicleStates { get; }\n\t\tvoid SetNextVehicleIdOnSegment(ushort vehicleId, ushort nextVehicleId);\n\t\tvoid SetPreviousVehicleIdOnSegment(ushort vehicleId, ushort previousVehicleId);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/AdvancedParkingManager.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Globalization;\nusing ColossalFramework.Math;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing TrafficManager.UI;\nusing TrafficManager.Util;\nusing UnityEngine;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class AdvancedParkingManager : AbstractFeatureManager, IAdvancedParkingManager {\n\t\tpublic static AdvancedParkingManager Instance { get; private set; } = null;\n\n\t\tstatic AdvancedParkingManager() {\n\t\t\tInstance = new AdvancedParkingManager();\n\t\t}\n\n\t\tprotected override void OnDisableFeatureInternal() {\n\t\t\tfor (int citizenInstanceId = 0; citizenInstanceId < ExtCitizenInstanceManager.Instance.ExtInstances.Length; ++citizenInstanceId) {\n\t\t\t\tExtPathMode pathMode = ExtCitizenInstanceManager.Instance.ExtInstances[citizenInstanceId].pathMode;\n\t\t\t\tswitch (pathMode) {\n\t\t\t\t\tcase ExtPathMode.RequiresWalkingPathToParkedCar:\n\t\t\t\t\tcase ExtPathMode.CalculatingWalkingPathToParkedCar:\n\t\t\t\t\tcase ExtPathMode.WalkingToParkedCar:\n\t\t\t\t\tcase ExtPathMode.ApproachingParkedCar:\n\t\t\t\t\t\t// citizen requires a path to their parked car: release instance to prevent it from floating\n\t\t\t\t\t\tServices.CitizenService.ReleaseCitizenInstance((ushort)citizenInstanceId);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtPathMode.RequiresCarPath:\n\t\t\t\t\tcase ExtPathMode.RequiresMixedCarPathToTarget:\n\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToKnownParkPos:\n\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToTarget:\n\t\t\t\t\tcase ExtPathMode.DrivingToKnownParkPos:\n\t\t\t\t\tcase ExtPathMode.DrivingToTarget:\n\t\t\t\t\t\tif (Services.CitizenService.CheckCitizenInstanceFlags((ushort)citizenInstanceId, CitizenInstance.Flags.Character)) {\n\t\t\t\t\t\t\t// citizen instance requires a car but is walking: release instance to prevent it from floating\n\t\t\t\t\t\t\tServices.CitizenService.ReleaseCitizenInstance((ushort)citizenInstanceId);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tExtCitizenManager.Instance.Reset();\n\t\t\tExtCitizenInstanceManager.Instance.Reset();\n\t\t}\n\n\t\tprotected override void OnEnableFeatureInternal() {\n\t\t\t\n\t\t}\n\n\t\tpublic ExtSoftPathState UpdateCitizenPathState(ushort citizenInstanceId, ref CitizenInstance citizenInstance, ref ExtCitizenInstance extInstance, ref ExtCitizen extCitizen, ref Citizen citizen, ExtPathState mainPathState) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == citizenInstanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == citizenInstance.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == citizenInstance.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == citizenInstance.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\t\t\tif (fineDebug)\n\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCitizenPathState({citizenInstanceId}, ..., {mainPathState}) called.\");\n#endif\n\t\t\tif (mainPathState == ExtPathState.Calculating) {\n\t\t\t\t// main path is still calculating, do not check return path\n#if DEBUG\n\t\t\t\tif (fineDebug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCitizenPathState({citizenInstanceId}, ..., {mainPathState}): still calculating main path. returning CALCULATING.\");\n#endif\n\t\t\t\treturn ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);\n\t\t\t}\n\n\t\t\t//ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(citizenInstanceId);\n\n\t\t\tif (!extInstance.IsValid()) {\n\t\t\t\t// no citizen\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCitizenPathState({citizenInstanceId}, ..., {mainPathState}): no citizen found!\");\n#endif\n\t\t\t\treturn ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);\n\t\t\t}\n\n\t\t\tif (mainPathState == ExtPathState.None || mainPathState == ExtPathState.Failed) {\n\t\t\t\t// main path failed or non-existing\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCitizenPathState({citizenInstanceId}, ..., {mainPathState}): mainPathSate is {mainPathState}.\");\n#endif\n\n\t\t\t\tif (mainPathState == ExtPathState.Failed) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCitizenPathState({citizenInstanceId}, ..., {mainPathState}): Checking if path-finding may be repeated.\");\n#endif\n\t\t\t\t\treturn OnCitizenPathFindFailure(citizenInstanceId, ref citizenInstance, ref extInstance, ref extCitizen);\n\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCitizenPathState({citizenInstanceId}, ..., {mainPathState}): Resetting instance and returning FAILED.\");\n#endif\n\n\t\t\t\t\textInstance.Reset();\n\t\t\t\t\treturn ExtSoftPathState.FailedHard;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// main path state is READY\n\n\t\t\t// main path calculation succeeded: update return path state and check its state if necessary\n\t\t\textInstance.UpdateReturnPathState();\n\n\t\t\tbool success = true;\n\t\t\tswitch (extInstance.returnPathState) {\n\t\t\t\tcase ExtPathState.None:\n\t\t\t\tdefault:\n\t\t\t\t\t// no return path calculated: ignore\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCitizenPathState({citizenInstanceId}, ..., {mainPathState}): return path state is None. Ignoring and returning main path state.\");\n#endif\n\t\t\t\t\tbreak;\n\t\t\t\tcase ExtPathState.Calculating: // OK\n\t\t\t\t\t\t\t\t\t\t\t   // return path not read yet: wait for it\n#if DEBUG\n\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCitizenPathState({citizenInstanceId}, ..., {mainPathState}): return path state is still calculating.\");\n#endif\n\t\t\t\t\treturn ExtSoftPathState.Calculating;\n\t\t\t\tcase ExtPathState.Failed: // OK\n\t\t\t\t\t\t\t\t\t\t  // no walking path from parking position to target found. flag main path as 'failed'.\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCitizenPathState({citizenInstanceId}, ..., {mainPathState}): Return path FAILED.\");\n#endif\n\t\t\t\t\tsuccess = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ExtPathState.Ready:\n\t\t\t\t\t// handle valid return path\n#if DEBUG\n\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCitizenPathState({citizenInstanceId}, ..., {mainPathState}): Path is READY.\");\n#endif\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\textInstance.ReleaseReturnPath();\n\n\t\t\tif (success) {\n\t\t\t\t// handle path find success\n\t\t\t\treturn OnCitizenPathFindSuccess(citizenInstanceId, ref citizenInstance, ref extInstance, ref extCitizen, ref citizen);\n\t\t\t} else {\n\t\t\t\t// handle path find failure\n\t\t\t\treturn OnCitizenPathFindFailure(citizenInstanceId, ref citizenInstance, ref extInstance, ref extCitizen);\n\t\t\t}\n\t\t}\n\n\t\tpublic ExtSoftPathState UpdateCarPathState(ushort vehicleId, ref Vehicle vehicleData, ref CitizenInstance driverInstance, ref ExtCitizenInstance driverExtInstance, ExtPathState mainPathState) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == driverExtInstance.instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == driverInstance.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == driverInstance.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == driverInstance.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\t\t\tif (fineDebug)\n\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}) called.\");\n#endif\n\t\t\tif (mainPathState == ExtPathState.Calculating) {\n\t\t\t\t// main path is still calculating, do not check return path\n#if DEBUG\n\t\t\t\tif (fineDebug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): still calculating main path. returning CALCULATING.\");\n#endif\n\t\t\t\treturn ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);\n\t\t\t}\n\n\t\t\t//ExtCitizenInstance driverExtInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(CustomPassengerCarAI.GetDriverInstance(vehicleId, ref vehicleData));\n\n\t\t\tif (!driverExtInstance.IsValid()) {\n\t\t\t\t// no driver\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): no driver found!\");\n#endif\n\t\t\t\treturn ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);\n\t\t\t}\n\n\t\t\tif (VehicleStateManager.Instance.VehicleStates[vehicleId].vehicleType != ExtVehicleType.PassengerCar) {\n\t\t\t\t// non-passenger cars are not handled\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): not a passenger car!\");\n#endif\n\t\t\t\tdriverExtInstance.Reset();\n\t\t\t\treturn ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);\n\t\t\t}\n\n\t\t\tif (mainPathState == ExtPathState.None || mainPathState == ExtPathState.Failed) {\n\t\t\t\t// main path failed or non-existing: reset return path\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): mainPathSate is {mainPathState}.\");\n#endif\n\n\t\t\t\tif (mainPathState == ExtPathState.Failed) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): Checking if path-finding may be repeated.\");\n#endif\n\t\t\t\t\tdriverExtInstance.ReleaseReturnPath();\n\t\t\t\t\treturn OnCarPathFindFailure(vehicleId, ref vehicleData, ref driverInstance, ref driverExtInstance);\n\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): Resetting instance and returning FAILED.\");\n#endif\n\n\t\t\t\t\tdriverExtInstance.Reset();\n\t\t\t\t\treturn ExtSoftPathState.FailedHard;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// main path state is READY\n\n\t\t\t// main path calculation succeeded: update return path state and check its state\n\t\t\tdriverExtInstance.UpdateReturnPathState();\n\n\t\t\tswitch (driverExtInstance.returnPathState) {\n\t\t\t\tcase ExtPathState.None:\n\t\t\t\tdefault:\n\t\t\t\t\t// no return path calculated: ignore\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): return path state is None. Setting pathMode=DrivingToTarget and returning main path state.\");\n#endif\n\t\t\t\t\tdriverExtInstance.pathMode = ExtPathMode.DrivingToTarget;\n\t\t\t\t\treturn ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState);\n\t\t\t\tcase ExtPathState.Calculating:\n\t\t\t\t\t// return path not read yet: wait for it\n#if DEBUG\n\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): return path state is still calculating.\");\n#endif\n\t\t\t\t\treturn ExtSoftPathState.Calculating;\n\t\t\t\tcase ExtPathState.Failed:\n\t\t\t\t\t// no walking path from parking position to target found. flag main path as 'failed'.\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): Return path {driverExtInstance.returnPathId} FAILED. Forcing path-finding to fail.\");\n#endif\n\t\t\t\t\tdriverExtInstance.Reset();\n\t\t\t\t\treturn ExtSoftPathState.FailedHard;\n\t\t\t\tcase ExtPathState.Ready:\n\t\t\t\t\t// handle valid return path\n\t\t\t\t\tdriverExtInstance.ReleaseReturnPath();\n#if DEBUG\n\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): Path is ready for vehicle {vehicleId}, citizen instance {driverExtInstance.instanceId}! CurrentPathMode={driverExtInstance.pathMode}\");\n#endif\n\t\t\t\t\tbyte laneTypes = CustomPathManager._instance.m_pathUnits.m_buffer[vehicleData.m_path].m_laneTypes;\n\t\t\t\t\tbool usesPublicTransport = (laneTypes & (byte)(NetInfo.LaneType.PublicTransport)) != 0;\n\n\t\t\t\t\tif (usesPublicTransport && (driverExtInstance.pathMode == ExtPathMode.CalculatingCarPathToKnownParkPos || driverExtInstance.pathMode == ExtPathMode.CalculatingCarPathToAltParkPos)) {\n\t\t\t\t\t\tdriverExtInstance.pathMode = ExtPathMode.CalculatingCarPathToTarget;\n\t\t\t\t\t\tdriverExtInstance.parkingSpaceLocation = ExtParkingSpaceLocation.None;\n\t\t\t\t\t\tdriverExtInstance.parkingSpaceLocationId = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (driverExtInstance.pathMode == ExtPathMode.CalculatingCarPathToAltParkPos) {\n\t\t\t\t\t\tdriverExtInstance.pathMode = ExtPathMode.DrivingToAltParkPos;\n\t\t\t\t\t\tdriverExtInstance.parkingPathStartPosition = null;\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): Path to an alternative parking position is READY for vehicle {vehicleId}! CurrentPathMode={driverExtInstance.pathMode}\");\n#endif\n\t\t\t\t\t} else if (driverExtInstance.pathMode == ExtPathMode.CalculatingCarPathToTarget) {\n\t\t\t\t\t\tdriverExtInstance.pathMode = ExtPathMode.DrivingToTarget;\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): Car path is READY for vehicle {vehicleId}! CurrentPathMode={driverExtInstance.pathMode}\");\n#endif\n\t\t\t\t\t} else if (driverExtInstance.pathMode == ExtPathMode.CalculatingCarPathToKnownParkPos) {\n\t\t\t\t\t\tdriverExtInstance.pathMode = ExtPathMode.DrivingToKnownParkPos;\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.UpdateCarPathState({vehicleId}, ..., {mainPathState}): Car path to known parking position is READY for vehicle {vehicleId}! CurrentPathMode={driverExtInstance.pathMode}\");\n#endif\n\t\t\t\t\t}\n\t\t\t\t\treturn ExtSoftPathState.Ready;\n\t\t\t}\n\t\t}\n\n\t\tpublic ParkedCarApproachState CitizenApproachingParkedCarSimulationStep(ushort instanceId, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance, Vector3 physicsLodRefPos, ref VehicleParked parkedCar) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == instanceData.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == instanceData.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == instanceData.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n#endif\n\n\t\t\tif ((instanceData.m_flags & CitizenInstance.Flags.WaitingPath) != CitizenInstance.Flags.None) {\n#if DEBUG\n\t\t\t\tif (fineDebug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.CheckCitizenReachedParkedCar({instanceId}): citizen instance {instanceId} is waiting for path-finding to complete.\");\n#endif\n\t\t\t\treturn ParkedCarApproachState.None;\n\t\t\t}\n\n\t\t\t//ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceId);\n\n\t\t\tif (extInstance.pathMode != ExtPathMode.ApproachingParkedCar && extInstance.pathMode != ExtPathMode.WalkingToParkedCar) {\n#if DEBUG\n\t\t\t\tif (fineDebug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.CitizenApproachingParkedCarSimulationStep({instanceId}): citizen instance {instanceId} is not reaching a parked car ({extInstance.pathMode})\");\n#endif\n\t\t\t\treturn ParkedCarApproachState.None;\n\t\t\t}\n\n\t\t\tif ((instanceData.m_flags & CitizenInstance.Flags.Character) == CitizenInstance.Flags.None) {\n#if DEBUG\n\t\t\t\t/*if (fineDebug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.CitizenApproachingParkedCarSimulationStep({instanceId}): citizen instance {instanceId} is not spawned!\");*/\n#endif\n\t\t\t\treturn ParkedCarApproachState.None;\n\t\t\t}\n\n\t\t\tVector3 lastFramePos = instanceData.GetLastFramePosition();\n\t\t\tVector3 doorPosition = parkedCar.GetClosestDoorPosition(parkedCar.m_position, VehicleInfo.DoorType.Enter);\n\n\t\t\tif (extInstance.pathMode == ExtPathMode.WalkingToParkedCar) {\n\t\t\t\t// check if path is complete\n\t\t\t\tPathUnit.Position pos;\n\t\t\t\tif (instanceData.m_pathPositionIndex != 255 && (instanceData.m_path == 0 || !CustomPathManager._instance.m_pathUnits.m_buffer[instanceData.m_path].GetPosition(instanceData.m_pathPositionIndex >> 1, out pos))) {\n\t\t\t\t\textInstance.pathMode = ExtPathMode.ApproachingParkedCar;\n\t\t\t\t\textInstance.lastDistanceToParkedCar = (instanceData.GetLastFramePosition() - doorPosition).sqrMagnitude;\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.CitizenApproachingParkedCarSimulationStep({instanceId}): citizen instance {instanceId} was walking to parked car and reached final path position. Switched PathMode to {extInstance.pathMode}.\");\n#endif\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (extInstance.pathMode == ExtPathMode.ApproachingParkedCar) {\n\t\t\t\tVector3 doorTargetDir = doorPosition - lastFramePos;\n\t\t\t\tVector3 doorWalkVector = doorPosition;\n\t\t\t\tfloat doorTargetDirMagnitude = doorTargetDir.magnitude;\n\t\t\t\tif (doorTargetDirMagnitude > 1f) {\n\t\t\t\t\tfloat speed = Mathf.Max(doorTargetDirMagnitude - 5f, doorTargetDirMagnitude * 0.5f);\n\t\t\t\t\tdoorWalkVector = lastFramePos + (doorTargetDir * (speed / doorTargetDirMagnitude));\n\t\t\t\t}\n\t\t\t\tinstanceData.m_targetPos = new Vector4(doorWalkVector.x, doorWalkVector.y, doorWalkVector.z, 0.5f);\n\t\t\t\tinstanceData.m_targetDir = VectorUtils.XZ(doorTargetDir);\n\n\t\t\t\tCitizenApproachingParkedCarSimulationStep(instanceId, ref instanceData, physicsLodRefPos);\n\n\t\t\t\tfloat doorSqrDist = (instanceData.GetLastFramePosition() - doorPosition).sqrMagnitude;\n\t\t\t\tif (doorSqrDist > GlobalConfig.Instance.ParkingAI.MaxParkedCarInstanceSwitchSqrDistance) {\n\t\t\t\t\t// citizen is still too far away from the parked car\n\t\t\t\t\tExtPathMode oldPathMode = extInstance.pathMode;\n\t\t\t\t\tif (doorSqrDist > extInstance.lastDistanceToParkedCar + 1024f) {\n\t\t\t\t\t\t// distance has increased dramatically since the last time\n\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog.Warning($\"AdvancedParkingManager.CitizenApproachingParkedCarSimulationStep({instanceId}): Citizen instance {instanceId} is currently reaching their parked car but distance increased! dist={doorSqrDist}, LastDistanceToParkedCar={extInstance.lastDistanceToParkedCar}.\");\n\n\t\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[6]) {\n\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.CitizenApproachingParkedCarSimulationStep({instanceId}): FORCED PAUSE. Distance increased! Citizen instance {instanceId}. dist={doorSqrDist}\");\n\t\t\t\t\t\t\tSingleton<SimulationManager>.instance.SimulationPaused = true;\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tCitizenInstance.Frame frameData = instanceData.GetLastFrameData();\n\t\t\t\t\t\tframeData.m_position = doorPosition;\n\t\t\t\t\t\tinstanceData.SetLastFrameData(frameData);\n\n\t\t\t\t\t\textInstance.pathMode = ExtCitizenInstance.ExtPathMode.RequiresCarPath;\n\n\t\t\t\t\t\treturn ParkedCarApproachState.Approached;\n\t\t\t\t\t} else if (doorSqrDist < extInstance.lastDistanceToParkedCar) {\n\t\t\t\t\t\textInstance.lastDistanceToParkedCar = doorSqrDist;\n\t\t\t\t\t}\n#if DEBUG\n\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.CitizenApproachingParkedCarSimulationStep({instanceId}): Citizen instance {instanceId} is currently reaching their parked car (dist={doorSqrDist}, LastDistanceToParkedCar={extInstance.lastDistanceToParkedCar}). CurrentDepartureMode={extInstance.pathMode}\");\n#endif\n\n\t\t\t\t\treturn ParkedCarApproachState.Approaching;\n\t\t\t\t} else {\n\t\t\t\t\textInstance.pathMode = ExtCitizenInstance.ExtPathMode.RequiresCarPath;\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.CitizenApproachingParkedCarSimulationStep({instanceId}): Citizen instance {instanceId} reached parking position (dist={doorSqrDist}). Calculating remaining path now. CurrentDepartureMode={extInstance.pathMode}\");\n#endif\n\t\t\t\t\treturn ParkedCarApproachState.Approached;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ParkedCarApproachState.None;\n\t\t}\n\n\t\tprotected void CitizenApproachingParkedCarSimulationStep(ushort instanceID, ref CitizenInstance instanceData, Vector3 physicsLodRefPos) {\n\t\t\tif ((instanceData.m_flags & CitizenInstance.Flags.Character) != CitizenInstance.Flags.None) {\n\t\t\t\tCitizenInstance.Frame lastFrameData = instanceData.GetLastFrameData();\n\t\t\t\tint oldGridX = Mathf.Clamp((int)(lastFrameData.m_position.x / (float)CitizenManager.CITIZENGRID_CELL_SIZE + (float)CitizenManager.CITIZENGRID_RESOLUTION / 2f), 0, CitizenManager.CITIZENGRID_RESOLUTION - 1);\n\t\t\t\tint oldGridY = Mathf.Clamp((int)(lastFrameData.m_position.z / (float)CitizenManager.CITIZENGRID_CELL_SIZE + (float)CitizenManager.CITIZENGRID_RESOLUTION / 2f), 0, CitizenManager.CITIZENGRID_RESOLUTION - 1);\n\t\t\t\tbool lodPhysics = Vector3.SqrMagnitude(physicsLodRefPos - lastFrameData.m_position) >= 62500f;\n\t\t\t\tCitizenApproachingParkedCarSimulationStep(instanceID, ref instanceData, ref lastFrameData, lodPhysics);\n\t\t\t\tint newGridX = Mathf.Clamp((int)(lastFrameData.m_position.x / (float)CitizenManager.CITIZENGRID_CELL_SIZE + (float)CitizenManager.CITIZENGRID_RESOLUTION / 2f), 0, CitizenManager.CITIZENGRID_RESOLUTION - 1);\n\t\t\t\tint newGridY = Mathf.Clamp((int)(lastFrameData.m_position.z / (float)CitizenManager.CITIZENGRID_CELL_SIZE + (float)CitizenManager.CITIZENGRID_RESOLUTION / 2f), 0, CitizenManager.CITIZENGRID_RESOLUTION - 1);\n\t\t\t\tif ((newGridX != oldGridX || newGridY != oldGridY) && (instanceData.m_flags & CitizenInstance.Flags.Character) != CitizenInstance.Flags.None) {\n\t\t\t\t\tSingleton<CitizenManager>.instance.RemoveFromGrid(instanceID, ref instanceData, oldGridX, oldGridY);\n\t\t\t\t\tSingleton<CitizenManager>.instance.AddToGrid(instanceID, ref instanceData, newGridX, newGridY);\n\t\t\t\t}\n\t\t\t\tif (instanceData.m_flags != CitizenInstance.Flags.None) {\n\t\t\t\t\tinstanceData.SetFrameData(Singleton<SimulationManager>.instance.m_currentFrameIndex, lastFrameData);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprotected void CitizenApproachingParkedCarSimulationStep(ushort instanceID, ref CitizenInstance instanceData, ref CitizenInstance.Frame frameData, bool lodPhysics) {\n\t\t\tframeData.m_position = frameData.m_position + (frameData.m_velocity * 0.5f);\n\n\t\t\tVector3 targetDiff = (Vector3)instanceData.m_targetPos - frameData.m_position;\n\t\t\tVector3 targetVelDiff = targetDiff - frameData.m_velocity;\n\t\t\tfloat targetVelDiffMag = targetVelDiff.magnitude;\n\n\t\t\ttargetVelDiff = targetVelDiff * (2f / Mathf.Max(targetVelDiffMag, 2f));\n\t\t\tframeData.m_velocity = frameData.m_velocity + targetVelDiff;\n\t\t\tframeData.m_velocity = frameData.m_velocity - (Mathf.Max(0f, Vector3.Dot((frameData.m_position + frameData.m_velocity) - (Vector3)instanceData.m_targetPos, frameData.m_velocity)) / Mathf.Max(0.01f, frameData.m_velocity.sqrMagnitude) * frameData.m_velocity);\n\t\t\tif (frameData.m_velocity.sqrMagnitude > 0.01f) {\n\t\t\t\tframeData.m_rotation = Quaternion.LookRotation(frameData.m_velocity);\n\t\t\t}\n\t\t}\n\n\t\tpublic bool CitizenApproachingTargetSimulationStep(ushort instanceId, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == instanceData.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == instanceData.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == instanceData.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n#endif\n\n\t\t\tif ((instanceData.m_flags & CitizenInstance.Flags.WaitingPath) != CitizenInstance.Flags.None) {\n#if DEBUG\n\t\t\t\tif (fineDebug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.CitizenApproachingTargetSimulationStep({instanceId}): citizen instance {instanceId} is waiting for path-finding to complete.\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t//ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceId);\n\n\t\t\tif (extInstance.pathMode != ExtCitizenInstance.ExtPathMode.WalkingToTarget &&\n\t\t\t\textInstance.pathMode != ExtCitizenInstance.ExtPathMode.TaxiToTarget) {\n#if DEBUG\n\t\t\t\tif (fineDebug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.CitizenApproachingTargetSimulationStep({instanceId}): citizen instance {instanceId} is not reaching target ({extInstance.pathMode})\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif ((instanceData.m_flags & CitizenInstance.Flags.Character) == CitizenInstance.Flags.None) {\n#if DEBUG\n\t\t\t\t/*if (fineDebug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.CitizenApproachingTargetSimulationStep({instanceId}): citizen instance {instanceId} is not spawned!\");*/\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\n\t\t\t\n\t\t\t// check if path is complete\n\t\t\tPathUnit.Position pos;\n\t\t\tif (instanceData.m_pathPositionIndex != 255 && (instanceData.m_path == 0 || !CustomPathManager._instance.m_pathUnits.m_buffer[instanceData.m_path].GetPosition(instanceData.m_pathPositionIndex >> 1, out pos))) {\n\t\t\t\textInstance.Reset();\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.CitizenApproachingTargetSimulationStep({instanceId}): Citizen instance {instanceId} reached target. CurrentDepartureMode={extInstance.pathMode}\");\n#endif\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles a path-finding success for activated Parking AI.\n\t\t/// </summary>\n\t\t/// <param name=\"instanceId\">Citizen instance id</param>\n\t\t/// <param name=\"instanceData\">Citizen instance data</param>\n\t\t/// <param name=\"extInstance\">Extended citizen instance data</param>\n\t\t/// <param name=\"extCitizen\">Extended citizen data</param>\n\t\t/// <param name=\"citizenData\">Citizen data</param>\n\t\t/// <returns>soft path state</returns>\n\t\tprotected ExtSoftPathState OnCitizenPathFindSuccess(ushort instanceId, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance, ref ExtCitizen extCitizen, ref Citizen citizenData) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == instanceData.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == instanceData.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == instanceData.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Path-finding succeeded for citizen instance {instanceId}. Path: {instanceData.m_path} vehicle={citizenData.m_vehicle}\");\n#endif\n\n\t\t\t//ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceId);\n\n\t\t\tif (!extInstance.IsValid()) {\n#if DEBUG\n\t\t\t\tif (fineDebug)\n\t\t\t\t\tLog.Warning($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Ext. citizen instance not found.\");\n#endif\n\t\t\t\treturn ExtSoftPathState.FailedHard;\n\t\t\t}\n\n\t\t\tif (citizenData.m_vehicle == 0) {\n\t\t\t\t// citizen does not already have a vehicle assigned\n\n\t\t\t\tif (extInstance.pathMode == ExtPathMode.TaxiToTarget) {\n#if DEBUG\n\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen uses a taxi. Decreasing public transport demand and returning READY.\");\n#endif\n\n\t\t\t\t\t// cim uses taxi\n\t\t\t\t\tif (instanceData.m_sourceBuilding != 0) {\n\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[instanceData.m_sourceBuilding].RemovePublicTransportDemand((uint)GlobalConfig.Instance.ParkingAI.PublicTransportDemandUsageDecrement, true);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (instanceData.m_targetBuilding != 0) {\n\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[instanceData.m_targetBuilding].RemovePublicTransportDemand((uint)GlobalConfig.Instance.ParkingAI.PublicTransportDemandUsageDecrement, false);\n\t\t\t\t\t}\n\n\t\t\t\t\textCitizen.transportMode |= ExtCitizen.ExtTransportMode.PublicTransport;\n\t\t\t\t\treturn ExtSoftPathState.Ready;\n\t\t\t\t}\n\n\t\t\t\tushort parkedVehicleId = citizenData.m_parkedVehicle;\n\t\t\t\tfloat sqrDistToParkedVehicle = 0f;\n\t\t\t\tif (parkedVehicleId != 0) {\n\t\t\t\t\t// calculate distance to parked vehicle\n\t\t\t\t\tVehicleManager vehicleManager = Singleton<VehicleManager>.instance;\n\t\t\t\t\tVector3 doorPosition = vehicleManager.m_parkedVehicles.m_buffer[parkedVehicleId].GetClosestDoorPosition(vehicleManager.m_parkedVehicles.m_buffer[parkedVehicleId].m_position, VehicleInfo.DoorType.Enter);\n\t\t\t\t\tsqrDistToParkedVehicle = (instanceData.GetLastFramePosition() - doorPosition).sqrMagnitude;\n\t\t\t\t}\n\n\t\t\t\tbyte laneTypes = CustomPathManager._instance.m_pathUnits.m_buffer[instanceData.m_path].m_laneTypes;\n\t\t\t\tushort vehicleTypes = CustomPathManager._instance.m_pathUnits.m_buffer[instanceData.m_path].m_vehicleTypes;\n\t\t\t\tbool usesPublicTransport = (laneTypes & (byte)(NetInfo.LaneType.PublicTransport)) != 0;\n\t\t\t\tbool usesCar = (laneTypes & (byte)(NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != 0 && (vehicleTypes & (ushort)(VehicleInfo.VehicleType.Car)) != 0;\n\n\t\t\t\tif (usesPublicTransport && usesCar && (extInstance.pathMode == ExtPathMode.CalculatingCarPathToKnownParkPos || extInstance.pathMode == ExtPathMode.CalculatingCarPathToAltParkPos)) {\n\t\t\t\t\t/*\n\t\t\t\t\t * when using public transport together with a car (assuming a \"source -> walk -> drive -> walk -> use public transport -> walk -> target\" path)\n\t\t\t\t\t * discard parking space information since the cim has to park near the public transport stop\n\t\t\t\t\t * (instead of parking in the vicinity of the target building).\n\t\t\t\t\t * \n\t\t\t\t\t * TODO we could check if the path looks like \"source -> walk -> use public transport -> walk -> drive -> [walk ->] target\" (in this case parking space information would still be valid)\n\t\t\t\t\t*/\n#if DEBUG\n\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen uses their car together with public transport. Discarding parking space information and setting path mode to CalculatingCarPathToTarget.\");\n#endif\n\t\t\t\t\textInstance.pathMode = ExtPathMode.CalculatingCarPathToTarget;\n\t\t\t\t\textInstance.parkingSpaceLocation = ExtParkingSpaceLocation.None;\n\t\t\t\t\textInstance.parkingSpaceLocationId = 0;\n\t\t\t\t}\n\n\t\t\t\tswitch (extInstance.pathMode) {\n\t\t\t\t\tcase ExtPathMode.None: // citizen starts at source building\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (extInstance.pathMode != ExtPathMode.None) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog.Warning($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Unexpected path mode {extInstance.pathMode}! {extInstance}\");\n#endif\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (usesCar) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Path for citizen instance {instanceId} contains passenger car section. Ensuring that citizen is allowed to use their car.\");\n#endif\n\n\t\t\t\t\t\t\tushort sourceBuildingId = instanceData.m_sourceBuilding;\n\t\t\t\t\t\t\tushort homeId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[instanceData.m_citizen].m_homeBuilding;\n\n\t\t\t\t\t\t\tif (parkedVehicleId == 0) {\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen {instanceData.m_citizen} (citizen instance {instanceId}), source building {sourceBuildingId} does not have a parked vehicle! CurrentPathMode={extInstance.pathMode}\");\n#endif\n\n\t\t\t\t\t\t\t\t// try to spawn parked vehicle in the vicinity of the starting point.\n\t\t\t\t\t\t\t\tVehicleInfo vehicleInfo = null;\n\t\t\t\t\t\t\t\tif (instanceData.Info.m_agePhase > Citizen.AgePhase.Child) {\n\t\t\t\t\t\t\t\t\t// get a random car info (due to the fact we are using a different randomizer, car assignment differs from the selection in ResidentAI.GetVehicleInfo/TouristAI.GetVehicleInfo method, but this should not matter since we are reusing parked vehicle infos there)\n\t\t\t\t\t\t\t\t\tvehicleInfo = Singleton<VehicleManager>.instance.GetRandomVehicleInfo(ref Singleton<SimulationManager>.instance.m_randomizer, ItemClass.Service.Residential, ItemClass.SubService.ResidentialLow, ItemClass.Level.Level1);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (vehicleInfo != null) {\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen {instanceData.m_citizen} (citizen instance {instanceId}), source building {sourceBuildingId} is using their own passenger car. CurrentPathMode={extInstance.pathMode}\");\n#endif\n\n\t\t\t\t\t\t\t\t\t// determine current position vector\n\t\t\t\t\t\t\t\t\tVector3 currentPos;\n\t\t\t\t\t\t\t\t\tushort currentBuildingId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[instanceData.m_citizen].GetBuildingByLocation();\n\t\t\t\t\t\t\t\t\tif (currentBuildingId != 0) {\n\t\t\t\t\t\t\t\t\t\tcurrentPos = Singleton<BuildingManager>.instance.m_buildings.m_buffer[currentBuildingId].m_position;\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Taking current position from current building {currentBuildingId} for citizen {instanceData.m_citizen} (citizen instance {instanceId}): {currentPos} CurrentPathMode={extInstance.pathMode}\");\n#endif\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tcurrentBuildingId = sourceBuildingId;\n\t\t\t\t\t\t\t\t\t\tcurrentPos = instanceData.GetLastFramePosition();\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Taking current position from last frame position for citizen {instanceData.m_citizen} (citizen instance {instanceId}): {currentPos}. Home {homeId} pos: {Singleton<BuildingManager>.instance.m_buildings.m_buffer[homeId].m_position} CurrentPathMode={extInstance.pathMode}\");\n#endif\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// spawn a passenger car near the current position\n\t\t\t\t\t\t\t\t\tVector3 parkPos;\n\t\t\t\t\t\t\t\t\tParkingUnableReason parkReason;\n\t\t\t\t\t\t\t\t\tif (AdvancedParkingManager.Instance.TrySpawnParkedPassengerCar(instanceData.m_citizen, homeId, currentPos, vehicleInfo, out parkPos, out parkReason)) {\n\t\t\t\t\t\t\t\t\t\tparkedVehicleId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[instanceData.m_citizen].m_parkedVehicle;\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Parked vehicle for citizen {instanceData.m_citizen} (instance {instanceId}) is {parkedVehicleId} now (parkPos={parkPos}).\");\n#endif\n\n\t\t\t\t\t\t\t\t\t\tif (currentBuildingId != 0) {\n\t\t\t\t\t\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[currentBuildingId].ModifyParkingSpaceDemand(parkPos, GlobalConfig.Instance.ParkingAI.MinSpawnedCarParkingSpaceDemandDelta, GlobalConfig.Instance.ParkingAI.MaxSpawnedCarParkingSpaceDemandDelta);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): >> Failed to spawn parked vehicle for citizen {instanceData.m_citizen} (citizen instance {instanceId}). reason={parkReason}. homePos: {Singleton<BuildingManager>.instance.m_buildings.m_buffer[homeId].m_position}\");\n\t\t\t\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t\t\t\tif (parkReason == ParkingUnableReason.NoSpaceFound && currentBuildingId != 0) {\n\t\t\t\t\t\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[currentBuildingId].AddParkingSpaceDemand(GlobalConfig.Instance.ParkingAI.FailedSpawnParkingSpaceDemandIncrement);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen {instanceData.m_citizen} (citizen instance {instanceId}), source building {sourceBuildingId}, home {homeId} does not own a vehicle.\");\n\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (parkedVehicleId != 0) {\n\t\t\t\t\t\t\t\t// citizen has to reach their parked vehicle first\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Calculating path to reach parked vehicle {parkedVehicleId} for citizen instance {instanceId}. targetPos={instanceData.m_targetPos} lastFramePos={instanceData.GetLastFramePosition()}\");\n#endif\n\n\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.RequiresWalkingPathToParkedCar;\n\t\t\t\t\t\t\t\treturn ExtSoftPathState.FailedSoft;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// error! could not find/spawn parked car\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen instance {instanceId} still does not have a parked vehicle! Retrying: Cim should walk to target\");\n#endif\n\n\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.RequiresWalkingPathToTarget;\n\t\t\t\t\t\t\t\treturn ExtSoftPathState.FailedSoft;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// path does not contain a car section: path can be reused for walking\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): A direct car path OR initial path was queried that does not contain a car section. Switching path mode to walking.\");\n#endif\n\n\t\t\t\t\t\t\tif (usesPublicTransport) {\n\t\t\t\t\t\t\t\t// decrease public tranport demand\n\t\t\t\t\t\t\t\tif (instanceData.m_sourceBuilding != 0) {\n\t\t\t\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[instanceData.m_sourceBuilding].RemovePublicTransportDemand((uint)GlobalConfig.Instance.ParkingAI.PublicTransportDemandUsageDecrement, true);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (instanceData.m_targetBuilding != 0) {\n\t\t\t\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[instanceData.m_targetBuilding].RemovePublicTransportDemand((uint)GlobalConfig.Instance.ParkingAI.PublicTransportDemandUsageDecrement, false);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\textCitizen.transportMode |= ExtCitizen.ExtTransportMode.PublicTransport;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.WalkingToTarget;\n\t\t\t\t\t\t\treturn ExtSoftPathState.Ready;\n\t\t\t\t\t\t}\n\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToTarget: // citizen has not yet entered their car (but is close to do so) and tries to reach the target directly\n\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToKnownParkPos: // citizen has not yet entered their (but is close to do so) car and tries to reach a parking space in the vicinity of the target\n\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToAltParkPos: // citizen has not yet entered their car (but is close to do so) and tries to reach an alternative parking space in the vicinity of the target\n\t\t\t\t\t\tif (usesCar) {\n\t\t\t\t\t\t\t// parked car should be reached now\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Path for citizen instance {instanceId} contains passenger car section and citizen should stand in front of their car.\");\n#endif\n\t\t\t\t\t\t\tif (extInstance.atOutsideConnection) {\n\t\t\t\t\t\t\t\t// car path calculated starting at road outside connection: success\n\t\t\t\t\t\t\t\tif (extInstance.pathMode == ExtPathMode.CalculatingCarPathToAltParkPos) {\n\t\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.DrivingToAltParkPos;\n\t\t\t\t\t\t\t\t\textInstance.parkingPathStartPosition = null;\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Path to an alternative parking position is READY! CurrentPathMode={extInstance.pathMode}\");\n#endif\n\t\t\t\t\t\t\t\t} else if (extInstance.pathMode == ExtPathMode.CalculatingCarPathToTarget) {\n\t\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.DrivingToTarget;\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Car path is READY! CurrentPathMode={extInstance.pathMode}\");\n#endif\n\t\t\t\t\t\t\t\t} else if (extInstance.pathMode == ExtPathMode.CalculatingCarPathToKnownParkPos) {\n\t\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.DrivingToKnownParkPos;\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Car path to known parking position is READY! CurrentPathMode={extInstance.pathMode}\");\n#endif\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\textInstance.atOutsideConnection = false; // citizen leaves outside connection\n\t\t\t\t\t\t\t\treturn ExtSoftPathState.Ready;\n\t\t\t\t\t\t\t} else if (parkedVehicleId == 0) {\n\t\t\t\t\t\t\t\t// error! could not find/spawn parked car\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen instance {instanceId} still does not have a parked vehicle! Retrying: Cim should walk to target\");\n#endif\n\n\t\t\t\t\t\t\t\textInstance.Reset();\n\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.RequiresWalkingPathToTarget;\n\t\t\t\t\t\t\t\treturn ExtSoftPathState.FailedSoft;\n\t\t\t\t\t\t\t} else if (sqrDistToParkedVehicle > 4f * GlobalConfig.Instance.ParkingAI.MaxParkedCarInstanceSwitchSqrDistance) {\n\t\t\t\t\t\t\t\t// error! parked car is too far away\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen instance {instanceId} cannot enter parked vehicle because it is too far away (sqrDistToParkedVehicle={sqrDistToParkedVehicle})! Retrying: Cim should walk to parked car\");\n#endif\n\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.RequiresWalkingPathToParkedCar;\n\t\t\t\t\t\t\t\treturn ExtSoftPathState.FailedSoft;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// path using passenger car has been calculated\n\t\t\t\t\t\t\t\tushort vehicleId;\n\t\t\t\t\t\t\t\tif (CustomHumanAI.EnterParkedCar(instanceId, ref instanceData, parkedVehicleId, out vehicleId)) { // TODO move method body here\n\t\t\t\t\t\t\t\t\textInstance.pathMode = extInstance.pathMode == ExtPathMode.CalculatingCarPathToTarget ? ExtPathMode.DrivingToTarget : ExtPathMode.DrivingToKnownParkPos;\n\t\t\t\t\t\t\t\t\textCitizen.transportMode |= ExtCitizen.ExtTransportMode.Car;\n\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen instance {instanceId} has entered their car and is now travelling by car (vehicleId={vehicleId}). CurrentDepartureMode={extInstance.pathMode}, targetPos={instanceData.m_targetPos} lastFramePos={instanceData.GetLastFramePosition()}\");\n#endif\n\t\t\t\t\t\t\t\t\treturn ExtSoftPathState.Ignore;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// error! parked car could not be entered (reached vehicle limit?): try to walk to target\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Entering parked vehicle {parkedVehicleId} failed for citizen instance {instanceId}. Trying to walk to target. CurrentDepartureMode={extInstance.pathMode}\");\n#endif\n\n\t\t\t\t\t\t\t\t\textInstance.Reset();\n\t\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.RequiresWalkingPathToTarget;\n\t\t\t\t\t\t\t\t\treturn ExtSoftPathState.FailedSoft;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// citizen does not need a car for the calculated path...\n\t\t\t\t\t\t\tswitch (extInstance.pathMode) {\n\t\t\t\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToTarget:\n\t\t\t\t\t\t\t\t\t// ... and the path can be reused for walking\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): A direct car path was queried that does not contain a car section. Switching path mode to walking.\");\n#endif\n\t\t\t\t\t\t\t\t\textInstance.Reset();\n\n\t\t\t\t\t\t\t\t\tif (usesPublicTransport) {\n\t\t\t\t\t\t\t\t\t\t// decrease public tranport demand\n\t\t\t\t\t\t\t\t\t\tif (instanceData.m_sourceBuilding != 0) {\n\t\t\t\t\t\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[instanceData.m_sourceBuilding].RemovePublicTransportDemand((uint)GlobalConfig.Instance.ParkingAI.PublicTransportDemandUsageDecrement, true);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tif (instanceData.m_targetBuilding != 0) {\n\t\t\t\t\t\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[instanceData.m_targetBuilding].RemovePublicTransportDemand((uint)GlobalConfig.Instance.ParkingAI.PublicTransportDemandUsageDecrement, false);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\textCitizen.transportMode |= ExtCitizen.ExtTransportMode.PublicTransport;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.WalkingToTarget;\n\t\t\t\t\t\t\t\t\treturn ExtSoftPathState.Ready;\n\t\t\t\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToKnownParkPos:\n\t\t\t\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToAltParkPos:\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t// ... and a path to a parking spot was calculated: dismiss path and restart path-finding for walking\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): A parking space car path was queried but it turned out that no car is needed. Retrying path-finding for walking.\");\n#endif\n\t\t\t\t\t\t\t\t\textInstance.Reset();\n\t\t\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.RequiresWalkingPathToTarget;\n\t\t\t\t\t\t\t\t\treturn ExtSoftPathState.FailedSoft;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\tcase ExtPathMode.CalculatingWalkingPathToParkedCar:\n\t\t\t\t\t\t// path to parked vehicle has been calculated...\n\t\t\t\t\t\tif (parkedVehicleId == 0) {\n\t\t\t\t\t\t\t// ... but the parked vehicle has vanished\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen instance {instanceId} shall walk to their parked vehicle but it disappeared. Retrying path-find for walking.\");\n#endif\n\t\t\t\t\t\t\textInstance.Reset();\n\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.RequiresWalkingPathToTarget;\n\t\t\t\t\t\t\treturn ExtSoftPathState.FailedSoft;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\textInstance.pathMode = ExtPathMode.WalkingToParkedCar;\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen instance {instanceId} is now on their way to its parked vehicle. CurrentDepartureMode={extInstance.pathMode}, targetPos={instanceData.m_targetPos} lastFramePos={instanceData.GetLastFramePosition()}\");\n#endif\n\t\t\t\t\t\t\treturn ExtSoftPathState.Ready;\n\t\t\t\t\t\t}\n\t\t\t\t\tcase ExtPathMode.CalculatingWalkingPathToTarget:\n\t\t\t\t\t\t// final walking path to target has been calculated\n\t\t\t\t\t\textInstance.pathMode = ExtPathMode.WalkingToTarget;\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen instance {instanceId} is now travelling by foot to their final target. CurrentDepartureMode={extInstance.pathMode}, targetPos={instanceData.m_targetPos} lastFramePos={instanceData.GetLastFramePosition()}\");\n#endif\n\t\t\t\t\t\treturn ExtSoftPathState.Ready;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// citizen has a vehicle assigned\n\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog.Warning($\"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): Citizen has a vehicle assigned but this method does not handle this situation. Forcing path-find to fail.\");\n#endif\n\t\t\t\textInstance.Reset();\n\t\t\t\treturn ExtSoftPathState.FailedHard;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles a path-finding failure for citizen instances and activated Parking AI.\n\t\t/// </summary>\n\t\t/// <param name=\"instanceId\">Citizen instance id</param>\n\t\t/// <param name=\"instanceData\">Citizen instance data</param>\n\t\t/// <param name=\"extInstance\">extended citizen instance information</param>\n\t\t/// <param name=\"extCitizen\">extended citizen information</param>\n\t\t/// <returns>if true path-finding may be repeated (path mode has been updated), false otherwise</returns>\n\t\tprotected ExtSoftPathState OnCitizenPathFindFailure(ushort instanceId, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance, ref ExtCitizen extCitizen) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == instanceData.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == instanceData.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == instanceData.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindFailure({instanceId}): Path-finding failed for citizen instance {extInstance.instanceId}. CurrentPathMode={extInstance.pathMode}\");\n#endif\n\n\t\t\t// update public transport demands\n\t\t\tswitch (extInstance.pathMode) {\n\t\t\t\tcase ExtPathMode.None:\n\t\t\t\tcase ExtPathMode.CalculatingWalkingPathToTarget:\n\t\t\t\tcase ExtPathMode.CalculatingWalkingPathToParkedCar:\n\t\t\t\tcase ExtPathMode.TaxiToTarget:\n\t\t\t\t\t// could not reach target building by walking/driving/public transport: increase public transport demand\n\t\t\t\t\tif ((instanceData.m_flags & CitizenInstance.Flags.CannotUseTransport) == CitizenInstance.Flags.None) {\n#if DEBUG\n\t\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindFailure({instanceId}): Increasing public transport demand of target building {instanceData.m_targetBuilding} and source building {instanceData.m_sourceBuilding}\");\n#endif\n\n\t\t\t\t\t\tif (instanceData.m_targetBuilding != 0) {\n\t\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[instanceData.m_targetBuilding].AddPublicTransportDemand((uint)GlobalConfig.Instance.ParkingAI.PublicTransportDemandIncrement, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (instanceData.m_sourceBuilding != 0) {\n\t\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[instanceData.m_sourceBuilding].AddPublicTransportDemand((uint)GlobalConfig.Instance.ParkingAI.PublicTransportDemandIncrement, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * relocate parked car if abandoned\n\t\t\t */\n\t\t\tif (extInstance.pathMode == ExtPathMode.CalculatingWalkingPathToParkedCar) {\n\t\t\t\t/*\n\t\t\t\t * parked car is unreachable\n\t\t\t\t */\n\t\t\t\tushort parkedVehicleId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[instanceData.m_citizen].m_parkedVehicle;\n\t\t\t\tif (parkedVehicleId != 0) {\n\t\t\t\t\t/*\n\t\t\t\t\t * parked car is present\n\t\t\t\t\t */\n\n\t\t\t\t\tushort homeId = 0;\n\t\t\t\t\tServices.CitizenService.ProcessCitizen(extCitizen.citizenId, delegate (uint citId, ref Citizen cit) {\n\t\t\t\t\t\thomeId = cit.m_homeBuilding;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\n\t\t\t\t\t// calculate distance between citizen and parked car\n\t\t\t\t\tbool movedCar = false;\n\t\t\t\t\tVector3 citizenPos = instanceData.GetLastFramePosition();\n\t\t\t\t\tfloat parkedToCitizen = 0f;\n\t\t\t\t\tVector3 oldParkedVehiclePos = default(Vector3);\n\t\t\t\t\tServices.VehicleService.ProcessParkedVehicle(parkedVehicleId, delegate (ushort parkedVehId, ref VehicleParked parkedVehicle) {\n\t\t\t\t\t\toldParkedVehiclePos = parkedVehicle.m_position;\n\t\t\t\t\t\tparkedToCitizen = (parkedVehicle.m_position - citizenPos).magnitude;\n\t\t\t\t\t\tif (parkedToCitizen > GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToHome) {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * parked car is far away from current location\n\t\t\t\t\t\t\t * -> relocate parked car and try again\n\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\tmovedCar = TryMoveParkedVehicle(parkedVehicleId, ref parkedVehicle, citizenPos, GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToHome, homeId);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\n\t\t\t\t\tif (movedCar) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * successfully moved the parked car to a closer location\n\t\t\t\t\t\t * -> retry path-finding\n\t\t\t\t\t\t */\n\n\t\t\t\t\t\textInstance.pathMode = ExtPathMode.RequiresWalkingPathToParkedCar;\n#if DEBUG\n\t\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindFailure({instanceId}): Relocated parked car {parkedVehicleId} to a closer location (old pos/distance: {oldParkedVehiclePos}/{parkedToCitizen}, new pos/distance: {Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position}/{(Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_position - citizenPos).magnitude}) for citizen @ {citizenPos}. Retrying path-finding. CurrentPathMode={extInstance.pathMode}\");\n#endif\n\n\t\t\t\t\t\treturn ExtSoftPathState.FailedSoft;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * could not move car\n\t\t\t\t\t\t * -> despawn parked car, walk to target or use public transport\n\t\t\t\t\t\t */\n#if DEBUG\n\t\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindFailure({instanceId}): Releasing unreachable parked vehicle {parkedVehicleId} for citizen instance {extInstance.instanceId}. CurrentPathMode={extInstance.pathMode}\");\n#endif\n\t\t\t\t\t\tSingleton<VehicleManager>.instance.ReleaseParkedVehicle(parkedVehicleId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// check if path-finding may be repeated\n\t\t\tExtSoftPathState ret = ExtSoftPathState.FailedHard;\n\t\t\tswitch (extInstance.pathMode) {\n\t\t\t\tcase ExtPathMode.CalculatingCarPathToTarget:\n\t\t\t\tcase ExtPathMode.CalculatingCarPathToKnownParkPos:\n\t\t\t\tcase ExtPathMode.CalculatingWalkingPathToParkedCar:\n\t\t\t\t\t// try to walk to target\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindFailure({instanceId}): Path failed but it may be retried to walk to the target.\");\n#endif\n\t\t\t\t\textInstance.pathMode = ExtPathMode.RequiresWalkingPathToTarget;\n\t\t\t\t\tret = ExtSoftPathState.FailedSoft;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindFailure({instanceId}): Path failed and walking to target is not an option. Resetting ext. instance.\");\n#endif\n\t\t\t\t\textInstance.Reset();\n\t\t\t\t\tbreak;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCitizenPathFindFailure({instanceId}): Setting CurrentPathMode for citizen instance {extInstance.instanceId} to {extInstance.pathMode}, ret={ret}\");\n#endif\n\n\t\t\t// reset current transport mode for hard failures\n\t\t\tif (ret == ExtSoftPathState.FailedHard) {\n\t\t\t\textCitizen.transportMode = ExtCitizen.ExtTransportMode.None;\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Handles a path-finding failure for citizen instances and activated Parking AI.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleId\">Vehicle id</param>\n\t\t/// <param name=\"vehicleData\">Vehicle data</param>\n\t\t/// <param name=\"driverInstanceData\">Driver citizen instance data</param>\n\t\t/// <param name=\"driverExtInstance\">extended citizen instance information of driver</param>\n\t\t/// <returns>if true path-finding may be repeated (path mode has been updated), false otherwise</returns>\n\t\tprotected ExtSoftPathState OnCarPathFindFailure(ushort vehicleId, ref Vehicle vehicleData, ref CitizenInstance driverInstanceData, ref ExtCitizenInstance driverExtInstance) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == driverExtInstance.instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == driverInstanceData.m_citizen) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == driverInstanceData.m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == driverInstanceData.m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCarPathFindFailure({vehicleId}): Path-finding failed for driver citizen instance {driverExtInstance.instanceId}. CurrentPathMode={driverExtInstance.pathMode}\");\n#endif\n\n\t\t\t// update parking demands\n\t\t\tswitch (driverExtInstance.pathMode) {\n\t\t\t\tcase ExtPathMode.None:\n\t\t\t\tcase ExtPathMode.CalculatingCarPathToAltParkPos:\n\t\t\t\tcase ExtPathMode.CalculatingCarPathToKnownParkPos:\n\t\t\t\t\t// could not reach target building by driving: increase parking space demand\n#if DEBUG\n\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCarPathFindFailure({vehicleId}): Increasing parking space demand of target building {driverInstanceData.m_targetBuilding}\");\n#endif\n\t\t\t\t\tif (driverInstanceData.m_targetBuilding != 0) {\n\t\t\t\t\t\tExtBuildingManager.Instance.ExtBuildings[driverInstanceData.m_targetBuilding].AddParkingSpaceDemand((uint)GlobalConfig.Instance.ParkingAI.FailedParkingSpaceDemandIncrement);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// check if path-finding may be repeated\n\t\t\tExtSoftPathState ret = ExtSoftPathState.FailedHard;\n\t\t\tswitch (driverExtInstance.pathMode) {\n\t\t\t\tcase ExtPathMode.CalculatingCarPathToAltParkPos:\n\t\t\t\tcase ExtPathMode.CalculatingCarPathToKnownParkPos:\n\t\t\t\t\t// try to drive directly to the target if public transport is allowed\n\t\t\t\t\tif ((driverInstanceData.m_flags & CitizenInstance.Flags.CannotUseTransport) == CitizenInstance.Flags.None) {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCarPathFindFailure({vehicleId}): Path failed but it may be retried to drive directly to the target / using public transport.\");\n#endif\n\t\t\t\t\t\tdriverExtInstance.pathMode = ExtPathMode.RequiresMixedCarPathToTarget;\n\t\t\t\t\t\tret = ExtSoftPathState.FailedSoft;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCarPathFindFailure({vehicleId}): Path failed and a direct target is not an option. Resetting driver ext. instance.\");\n#endif\n\t\t\t\t\tdriverExtInstance.Reset();\n\t\t\t\t\tbreak;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"AdvancedParkingManager.OnCarPathFindFailure({vehicleId}): Setting CurrentPathMode for driver citizen instance {driverExtInstance.instanceId} to {driverExtInstance.pathMode}, ret={ret}\");\n#endif\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool TryMoveParkedVehicle(ushort parkedVehicleId, ref VehicleParked parkedVehicle, Vector3 refPos, float maxDistance, ushort homeId) {\n\t\t\tExtParkingSpaceLocation parkingSpaceLocation;\n\t\t\tushort parkingSpaceLocationId;\n\t\t\tVector3 parkPos;\n\t\t\tQuaternion parkRot;\n\t\t\tfloat parkOffset;\n\n\t\t\tbool found = false;\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark(null, \"FindParkingSpaceInVicinity\")) {\n#endif\n\t\t\tfound = AdvancedParkingManager.Instance.FindParkingSpaceInVicinity(refPos, parkedVehicle.Info, homeId, 0, maxDistance, out parkingSpaceLocation, out parkingSpaceLocationId, out parkPos, out parkRot, out parkOffset);\n#if BENCHMARK\n\t\t\t}\n#endif\n\t\t\tif (found) {\n\t\t\t\tSingleton<VehicleManager>.instance.RemoveFromGrid(parkedVehicleId, ref parkedVehicle);\n\t\t\t\tparkedVehicle.m_position = parkPos;\n\t\t\t\tparkedVehicle.m_rotation = parkRot;\n\t\t\t\tSingleton<VehicleManager>.instance.AddToGrid(parkedVehicleId, ref parkedVehicle);\n\t\t\t}\n\n\t\t\treturn found;\n\t\t}\n\n\t\tpublic bool FindParkingSpaceForCitizen(Vector3 endPos, VehicleInfo vehicleInfo, ref ExtCitizenInstance extDriverInstance, ushort homeId, bool goingHome, ushort vehicleId, bool allowTourists, out Vector3 parkPos, ref PathUnit.Position endPathPos, out bool calculateEndPos) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == extDriverInstance.instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == extDriverInstance.GetCitizenId()) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == Singleton<CitizenManager>.instance.m_instances.m_buffer[extDriverInstance.instanceId].m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == Singleton<CitizenManager>.instance.m_instances.m_buffer[extDriverInstance.instanceId].m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n#endif\n\n\t\t\tcalculateEndPos = true;\n\t\t\tparkPos = default(Vector3);\n\n\t\t\tif (!allowTourists) {\n\t\t\t\t// TODO remove this from this method\n\t\t\t\tuint citizenId = extDriverInstance.GetCitizenId();\n\t\t\t\tif (citizenId == 0 ||\n\t\t\t\t\t(Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenId].m_flags & Citizen.Flags.Tourist) != Citizen.Flags.None)\n\t\t\t\t\treturn false;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (fineDebug)\n\t\t\t\tLog._Debug($\"Citizen instance {extDriverInstance.instanceId} (CurrentPathMode={extDriverInstance.pathMode}) can still use their passenger car and is either not a tourist or wants to find an alternative parking spot. Finding a parking space before starting path-finding.\");\n#endif\n\n\t\t\tExtParkingSpaceLocation knownParkingSpaceLocation;\n\t\t\tushort knownParkingSpaceLocationId;\n\t\t\tQuaternion parkRot;\n\t\t\tfloat parkOffset;\n\n\t\t\t// find a free parking space\n\t\t\tbool success = FindParkingSpaceInVicinity(endPos, vehicleInfo, homeId, vehicleId, goingHome ? GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToHome : GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToBuilding, out knownParkingSpaceLocation, out knownParkingSpaceLocationId, out parkPos, out parkRot, out parkOffset);\n\n\t\t\textDriverInstance.parkingSpaceLocation = knownParkingSpaceLocation;\n\t\t\textDriverInstance.parkingSpaceLocationId = knownParkingSpaceLocationId;\n\n\t\t\tif (success) {\n#if DEBUG\n\t\t\t\tif (fineDebug)\n\t\t\t\t\tLog._Debug($\"Found a parking spot for citizen instance {extDriverInstance.instanceId} (CurrentPathMode={extDriverInstance.pathMode}) before starting car path: {knownParkingSpaceLocation} @ {knownParkingSpaceLocationId}\");\n#endif\n\n\t\t\t\tif (knownParkingSpaceLocation == ExtParkingSpaceLocation.RoadSide) {\n\t\t\t\t\t// found segment with parking space\n\t\t\t\t\tVector3 pedPos;\n\t\t\t\t\tuint laneId;\n\t\t\t\t\tint laneIndex;\n\t\t\t\t\tfloat laneOffset;\n\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"Found segment {knownParkingSpaceLocationId} for road-side parking position for citizen instance {extDriverInstance.instanceId}!\");\n#endif\n\n\t\t\t\t\t// determine nearest sidewalk position for parking position at segment\n\t\t\t\t\tif (Singleton<NetManager>.instance.m_segments.m_buffer[knownParkingSpaceLocationId].GetClosestLanePosition(parkPos, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, out pedPos, out laneId, out laneIndex, out laneOffset)) {\n\t\t\t\t\t\tendPathPos.m_segment = knownParkingSpaceLocationId;\n\t\t\t\t\t\tendPathPos.m_lane = (byte)laneIndex;\n\t\t\t\t\t\tendPathPos.m_offset = (byte)(parkOffset * 255f);\n\t\t\t\t\t\tcalculateEndPos = false;\n\n\t\t\t\t\t\t//extDriverInstance.CurrentPathMode = successMode;// ExtCitizenInstance.PathMode.CalculatingKnownCarPath;\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"Found an parking spot sidewalk position for citizen instance {extDriverInstance.instanceId} @ segment {knownParkingSpaceLocationId}, laneId {laneId}, laneIndex {laneIndex}, offset={endPathPos.m_offset}! CurrentPathMode={extDriverInstance.pathMode}\");\n#endif\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"Could not find an alternative parking spot sidewalk position for citizen instance {extDriverInstance.instanceId}! CurrentPathMode={extDriverInstance.pathMode}\");\n#endif\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t} else if (knownParkingSpaceLocation == ExtParkingSpaceLocation.Building) {\n\t\t\t\t\t// found a building with parking space\n\t\t\t\t\tif (CustomPathManager.FindPathPositionWithSpiralLoop(parkPos, endPos, ItemClass.Service.Road, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, NetInfo.LaneType.None, VehicleInfo.VehicleType.None, false, false, GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance, out endPathPos)) {\n\t\t\t\t\t\tcalculateEndPos = false;\n\t\t\t\t\t}\n\n\t\t\t\t\t//endPos = parkPos;\n\n\t\t\t\t\t//extDriverInstance.CurrentPathMode = successMode;// ExtCitizenInstance.PathMode.CalculatingKnownCarPath;\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"Navigating citizen instance {extDriverInstance.instanceId} to parking building {knownParkingSpaceLocationId}! segment={endPathPos.m_segment}, laneIndex={endPathPos.m_lane}, offset={endPathPos.m_offset}. CurrentPathMode={extDriverInstance.pathMode} calculateEndPos={calculateEndPos}\");\n#endif\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic bool TrySpawnParkedPassengerCar(uint citizenId, ushort homeId, Vector3 refPos, VehicleInfo vehicleInfo, out Vector3 parkPos, out ParkingUnableReason reason) {\n#if DEBUG\n\t\t\tbool citDebug = GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == citizenId;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (fineDebug && homeId != 0)\n\t\t\t\tLog._Debug($\"Trying to spawn parked passenger car for citizen {citizenId}, home {homeId} @ {refPos}\");\n#endif\n\n\t\t\tVector3 roadParkPos;\n\t\t\tParkingUnableReason roadParkReason;\n\t\t\tbool roadParkSuccess = TrySpawnParkedPassengerCarRoadSide(citizenId, refPos, vehicleInfo, out roadParkPos, out roadParkReason);\n\n\t\t\tVector3 buildingParkPos;\n\t\t\tParkingUnableReason buildingParkReason;\n\t\t\tbool buildingParkSuccess = TrySpawnParkedPassengerCarBuilding(citizenId, homeId, refPos, vehicleInfo, out buildingParkPos, out buildingParkReason);\n\n\t\t\tif ((!roadParkSuccess && !buildingParkSuccess) || (roadParkSuccess && !buildingParkSuccess)) {\n\t\t\t\tparkPos = roadParkPos;\n\t\t\t\treason = roadParkReason;\n\t\t\t\treturn roadParkSuccess;\n\t\t\t} else if (buildingParkSuccess && !roadParkSuccess) {\n\t\t\t\tparkPos = buildingParkPos;\n\t\t\t\treason = buildingParkReason;\n\t\t\t\treturn buildingParkSuccess;\n\t\t\t} else if ((roadParkPos - refPos).sqrMagnitude < (buildingParkPos - refPos).sqrMagnitude) {\n\t\t\t\tparkPos = roadParkPos;\n\t\t\t\treason = roadParkReason;\n\t\t\t\treturn roadParkSuccess;\n\t\t\t} else {\n\t\t\t\tparkPos = buildingParkPos;\n\t\t\t\treason = buildingParkReason;\n\t\t\t\treturn buildingParkSuccess;\n\t\t\t}\n\t\t}\n\n\t\tpublic bool TrySpawnParkedPassengerCarRoadSide(uint citizenId, Vector3 refPos, VehicleInfo vehicleInfo, out Vector3 parkPos, out ParkingUnableReason reason) {\n#if DEBUG\n\t\t\tbool citDebug = GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == citizenId;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"Trying to spawn parked passenger car at road side for citizen {citizenId} @ {refPos}\");\n#endif\n\t\t\tparkPos = Vector3.zero;\n\t\t\tQuaternion parkRot = Quaternion.identity;\n\t\t\tfloat parkOffset = 0f;\n\n\t\t\tif (FindParkingSpaceRoadSide(0, refPos, vehicleInfo.m_generatedInfo.m_size.x, vehicleInfo.m_generatedInfo.m_size.z, GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToBuilding, out parkPos, out parkRot, out parkOffset)) {\n\t\t\t\t// position found, spawn a parked vehicle\n\t\t\t\tushort parkedVehicleId;\n\t\t\t\tif (Singleton<VehicleManager>.instance.CreateParkedVehicle(out parkedVehicleId, ref Singleton<SimulationManager>.instance.m_randomizer, vehicleInfo, parkPos, parkRot, citizenId)) {\n\t\t\t\t\tSingleton<CitizenManager>.instance.m_citizens.m_buffer[citizenId].SetParkedVehicle(citizenId, parkedVehicleId);\n\t\t\t\t\tSingleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_flags &= (ushort)(VehicleParked.Flags.All & ~VehicleParked.Flags.Parking);\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"[SUCCESS] Spawned parked passenger car at road side for citizen {citizenId}: {parkedVehicleId} @ {parkPos}\");\n#endif\n\t\t\t\t\treason = ParkingUnableReason.None;\n\t\t\t\t\treturn true;\n\t\t\t\t} else {\n\t\t\t\t\treason = ParkingUnableReason.LimitHit;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treason = ParkingUnableReason.NoSpaceFound;\n\t\t\t}\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"[FAIL] Failed to spawn parked passenger car at road side for citizen {citizenId}\");\n#endif\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic bool TrySpawnParkedPassengerCarBuilding(uint citizenId, ushort homeId, Vector3 refPos, VehicleInfo vehicleInfo, out Vector3 parkPos, out ParkingUnableReason reason) {\n#if DEBUG\n\t\t\tbool citDebug = GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == citizenId;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (fineDebug && homeId != 0)\n\t\t\t\tLog._Debug($\"Trying to spawn parked passenger car next to building for citizen {citizenId} @ {refPos}\");\n#endif\n\t\t\tparkPos = Vector3.zero;\n\t\t\tQuaternion parkRot = Quaternion.identity;\n\t\t\tfloat parkOffset;\n\n\t\t\tif (FindParkingSpaceBuilding(vehicleInfo, homeId, 0, 0, refPos, GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToBuilding, GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToBuilding, out parkPos, out parkRot, out parkOffset)) {\n\t\t\t\t// position found, spawn a parked vehicle\n\t\t\t\tushort parkedVehicleId;\n\t\t\t\tif (Singleton<VehicleManager>.instance.CreateParkedVehicle(out parkedVehicleId, ref Singleton<SimulationManager>.instance.m_randomizer, vehicleInfo, parkPos, parkRot, citizenId)) {\n\t\t\t\t\tSingleton<CitizenManager>.instance.m_citizens.m_buffer[citizenId].SetParkedVehicle(citizenId, parkedVehicleId);\n\t\t\t\t\tSingleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].m_flags &= (ushort)(VehicleParked.Flags.All & ~VehicleParked.Flags.Parking);\n#if DEBUG\n\t\t\t\t\tif (fineDebug && homeId != 0)\n\t\t\t\t\t\tLog._Debug($\"[SUCCESS] Spawned parked passenger car next to building for citizen {citizenId}: {parkedVehicleId} @ {parkPos}\");\n#endif\n\t\t\t\t\treason = ParkingUnableReason.None;\n\t\t\t\t\treturn true;\n\t\t\t\t} else {\n\t\t\t\t\treason = ParkingUnableReason.LimitHit;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treason = ParkingUnableReason.NoSpaceFound;\n\t\t\t}\n#if DEBUG\n\t\t\tif (debug && homeId != 0)\n\t\t\t\tLog._Debug($\"[FAIL] Failed to spawn parked passenger car next to building for citizen {citizenId}\");\n#endif\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic bool FindParkingSpaceInVicinity(Vector3 targetPos, VehicleInfo vehicleInfo, ushort homeId, ushort vehicleId, float maxDist, out ExtParkingSpaceLocation parkingSpaceLocation, out ushort parkingSpaceLocationId, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset) {\n#if DEBUG\n\t\t\tbool vehDebug = GlobalConfig.Instance.Debug.VehicleId == 0 || GlobalConfig.Instance.Debug.VehicleId == vehicleId;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[22] && vehDebug;\n#endif\n\n\t\t\t// TODO check isElectric\n\t\t\tVector3 roadParkPos;\n\t\t\tQuaternion roadParkRot;\n\t\t\tfloat roadParkOffset;\n\t\t\tVector3 buildingParkPos;\n\t\t\tQuaternion buildingParkRot;\n\t\t\tfloat buildingParkOffset;\n\n\t\t\tushort parkingSpaceSegmentId = FindParkingSpaceAtRoadSide(0, targetPos, vehicleInfo.m_generatedInfo.m_size.x, vehicleInfo.m_generatedInfo.m_size.z, maxDist, true, out roadParkPos, out roadParkRot, out roadParkOffset);\n\t\t\tushort parkingBuildingId = FindParkingSpaceBuilding(vehicleInfo, homeId, 0, 0, targetPos, maxDist, maxDist, true, out buildingParkPos, out buildingParkRot, out buildingParkOffset);\n\n\t\t\tif (parkingSpaceSegmentId != 0) {\n\t\t\t\tif (parkingBuildingId != 0) {\n\t\t\t\t\tRandomizer rng = Singleton<SimulationManager>.instance.m_randomizer;\n\n\t\t\t\t\t// choose nearest parking position, after a bit of randomization\n\t\t\t\t\tif ((roadParkPos - targetPos).magnitude < (buildingParkPos - targetPos).magnitude\n\t\t\t\t\t\t&& rng.Int32(GlobalConfig.Instance.ParkingAI.VicinityParkingSpaceSelectionRand) != 0) {\n\t\t\t\t\t\t// road parking space is closer\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"Found an (alternative) road-side parking position for vehicle {vehicleId} @ segment {parkingSpaceSegmentId} after comparing distance with a bulding parking position @ {parkingBuildingId}!\");\n#endif\n\t\t\t\t\t\tparkPos = roadParkPos;\n\t\t\t\t\t\tparkRot = roadParkRot;\n\t\t\t\t\t\tparkOffset = roadParkOffset;\n\t\t\t\t\t\tparkingSpaceLocation = ExtCitizenInstance.ExtParkingSpaceLocation.RoadSide;\n\t\t\t\t\t\tparkingSpaceLocationId = parkingSpaceSegmentId;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// building parking space is closer\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"Found an alternative building parking position for vehicle {vehicleId} at building {parkingBuildingId} after comparing distance with a road-side parking position @ {parkingSpaceSegmentId}!\");\n#endif\n\t\t\t\t\t\tparkPos = buildingParkPos;\n\t\t\t\t\t\tparkRot = buildingParkRot;\n\t\t\t\t\t\tparkOffset = buildingParkOffset;\n\t\t\t\t\t\tparkingSpaceLocation = ExtCitizenInstance.ExtParkingSpaceLocation.Building;\n\t\t\t\t\t\tparkingSpaceLocationId = parkingBuildingId;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// road-side but no building parking space found\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"Found an alternative road-side parking position for vehicle {vehicleId} @ segment {parkingSpaceSegmentId}!\");\n#endif\n\t\t\t\t\tparkPos = roadParkPos;\n\t\t\t\t\tparkRot = roadParkRot;\n\t\t\t\t\tparkOffset = roadParkOffset;\n\t\t\t\t\tparkingSpaceLocation = ExtCitizenInstance.ExtParkingSpaceLocation.RoadSide;\n\t\t\t\t\tparkingSpaceLocationId = parkingSpaceSegmentId;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else if (parkingBuildingId != 0) {\n\t\t\t\t// building but no road-side parking space found\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"Found an alternative building parking position for vehicle {vehicleId} at building {parkingBuildingId}!\");\n#endif\n\t\t\t\tparkPos = buildingParkPos;\n\t\t\t\tparkRot = buildingParkRot;\n\t\t\t\tparkOffset = buildingParkOffset;\n\t\t\t\tparkingSpaceLocation = ExtCitizenInstance.ExtParkingSpaceLocation.Building;\n\t\t\t\tparkingSpaceLocationId = parkingBuildingId;\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\t//driverExtInstance.CurrentPathMode = ExtCitizenInstance.PathMode.AltParkFailed;\n\t\t\t\tparkingSpaceLocation = ExtCitizenInstance.ExtParkingSpaceLocation.None;\n\t\t\t\tparkingSpaceLocationId = 0;\n\t\t\t\tparkPos = default(Vector3);\n\t\t\t\tparkRot = default(Quaternion);\n\t\t\t\tparkOffset = -1f;\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"Could not find a road-side or building parking position for vehicle {vehicleId}!\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tprotected ushort FindParkingSpaceAtRoadSide(ushort ignoreParked, Vector3 refPos, float width, float length, float maxDistance, bool randomize, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[22];\n#endif\n\n\t\t\tparkPos = Vector3.zero;\n\t\t\tparkRot = Quaternion.identity;\n\t\t\tparkOffset = 0f;\n\n\t\t\tint centerI = (int)(refPos.z / (float)BuildingManager.BUILDINGGRID_CELL_SIZE + (float)BuildingManager.BUILDINGGRID_RESOLUTION / 2f);\n\t\t\tint centerJ = (int)(refPos.x / (float)BuildingManager.BUILDINGGRID_CELL_SIZE + (float)BuildingManager.BUILDINGGRID_RESOLUTION / 2f);\n\t\t\tint radius = Math.Max(1, (int)(maxDistance / ((float)BuildingManager.BUILDINGGRID_CELL_SIZE / 2f)) + 1);\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tRandomizer rng = Singleton<SimulationManager>.instance.m_randomizer;\n\n\t\t\tushort foundSegmentId = 0;\n\t\t\tVector3 myParkPos = parkPos;\n\t\t\tQuaternion myParkRot = parkRot;\n\t\t\tfloat myParkOffset = parkOffset;\n\n\t\t\tLoopUtil.SpiralLoop(centerI, centerJ, radius, radius, delegate (int i, int j) {\n\t\t\t\tif (i < 0 || i >= BuildingManager.BUILDINGGRID_RESOLUTION || j < 0 || j >= BuildingManager.BUILDINGGRID_RESOLUTION)\n\t\t\t\t\treturn true;\n\n\t\t\t\tushort segmentId = netManager.m_segmentGrid[i * BuildingManager.BUILDINGGRID_RESOLUTION + j];\n\t\t\t\tint iterations = 0;\n\t\t\t\twhile (segmentId != 0) {\n\t\t\t\t\tuint laneId;\n\t\t\t\t\tint laneIndex;\n\t\t\t\t\tfloat laneOffset;\n\t\t\t\t\tVector3 innerParkPos;\n\t\t\t\t\tQuaternion innerParkRot;\n\t\t\t\t\tfloat innerParkOffset;\n\n\t\t\t\t\tNetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info;\n\t\t\t\t\tVector3 segCenter = netManager.m_segments.m_buffer[segmentId].m_bounds.center;\n\n\t\t\t\t\t// randomize target position to allow for opposite road-side parking\n\t\t\t\t\tsegCenter.x += Singleton<SimulationManager>.instance.m_randomizer.Int32(GlobalConfig.Instance.ParkingAI.ParkingSpacePositionRand) - GlobalConfig.Instance.ParkingAI.ParkingSpacePositionRand / 2u;\n\t\t\t\t\tsegCenter.z += Singleton<SimulationManager>.instance.m_randomizer.Int32(GlobalConfig.Instance.ParkingAI.ParkingSpacePositionRand) - GlobalConfig.Instance.ParkingAI.ParkingSpacePositionRand / 2u;\n\n\t\t\t\t\tif (netManager.m_segments.m_buffer[segmentId].GetClosestLanePosition(segCenter, NetInfo.LaneType.Parking, VehicleInfo.VehicleType.Car, out innerParkPos, out laneId, out laneIndex, out laneOffset)) {\n\t\t\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\t\t\tif (!Options.parkingRestrictionsEnabled || ParkingRestrictionsManager.Instance.IsParkingAllowed(segmentId, laneInfo.m_finalDirection)) {\n\t\t\t\t\t\t\tif (!Options.vehicleRestrictionsEnabled || (VehicleRestrictionsManager.Instance.GetAllowedVehicleTypes(segmentId, segmentInfo, (uint)laneIndex, laneInfo, VehicleRestrictionsMode.Configured) & ExtVehicleType.PassengerCar) != ExtVehicleType.None) {\n\n\t\t\t\t\t\t\t\tif (CustomPassengerCarAI.FindParkingSpaceRoadSide(ignoreParked, segmentId, innerParkPos, width, length, out innerParkPos, out innerParkRot, out innerParkOffset)) {\n\t#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"FindParkingSpaceRoadSide: Found a parking space for refPos {refPos}, segment center {segCenter} @ {innerParkPos}, laneId {laneId}, laneIndex {laneIndex}!\");\n\t#endif\n\t\t\t\t\t\t\t\t\tfoundSegmentId = segmentId;\n\t\t\t\t\t\t\t\t\tmyParkPos = innerParkPos;\n\t\t\t\t\t\t\t\t\tmyParkRot = innerParkRot;\n\t\t\t\t\t\t\t\t\tmyParkOffset = innerParkOffset;\n\t\t\t\t\t\t\t\t\tif (!randomize || rng.Int32(GlobalConfig.Instance.ParkingAI.VicinityParkingSpaceSelectionRand) != 0)\n\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t/*if (debug)\n\t\t\t\t\t\t\tLog._Debug($\"FindParkingSpaceRoadSide: Could not find closest lane position for parking @ {segmentId}!\");*/\n\t\t\t\t\t}\n\n\t\t\t\t\tsegmentId = netManager.m_segments.m_buffer[segmentId].m_nextGridSegment;\n\t\t\t\t\tif (++iterations >= NetManager.MAX_SEGMENT_COUNT) {\n\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tif (foundSegmentId == 0) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"FindParkingSpaceRoadSide: Could not find a parking space for refPos {refPos}!\");\n#endif\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tparkPos = myParkPos;\n\t\t\tparkRot = myParkRot;\n\t\t\tparkOffset = myParkOffset;\n\n\t\t\treturn foundSegmentId;\n\t\t}\n\n\t\tprotected ushort FindParkingSpaceBuilding(VehicleInfo vehicleInfo, ushort homeID, ushort ignoreParked, ushort segmentId, Vector3 refPos, float maxBuildingDistance, float maxParkingSpaceDistance, bool randomize, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[22];\n#endif\n\n\t\t\tparkPos = Vector3.zero;\n\t\t\tparkRot = Quaternion.identity;\n\t\t\tparkOffset = -1f;\n\n\t\t\tint centerI = (int)(refPos.z / (float)BuildingManager.BUILDINGGRID_CELL_SIZE + (float)BuildingManager.BUILDINGGRID_RESOLUTION / 2f);\n\t\t\tint centerJ = (int)(refPos.x / (float)BuildingManager.BUILDINGGRID_CELL_SIZE + (float)BuildingManager.BUILDINGGRID_RESOLUTION / 2f);\n\t\t\tint radius = Math.Max(1, (int)(maxBuildingDistance / ((float)BuildingManager.BUILDINGGRID_CELL_SIZE / 2f)) + 1);\n\n\t\t\tBuildingManager buildingMan = Singleton<BuildingManager>.instance;\n\t\t\tRandomizer rng = Singleton<SimulationManager>.instance.m_randomizer;\n\n\t\t\tushort foundBuildingId = 0;\n\t\t\tVector3 myParkPos = parkPos;\n\t\t\tQuaternion myParkRot = parkRot;\n\t\t\tfloat myParkOffset = parkOffset;\n\n\t\t\tLoopUtil.SpiralLoop(centerI, centerJ, radius, radius, delegate (int i, int j) {\n\t\t\t\tif (i < 0 || i >= BuildingManager.BUILDINGGRID_RESOLUTION || j < 0 || j >= BuildingManager.BUILDINGGRID_RESOLUTION)\n\t\t\t\t\treturn true;\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\t//Log._Debug($\"FindParkingSpaceBuilding: Checking building grid @ i={i}, j={j}, index={i * BuildingManager.BUILDINGGRID_RESOLUTION + j} for {refPos}, homeID {homeID}, segment {segmentId}, maxDistance {maxDistance}\");\n\t\t\t\t}\n#endif\n\n\t\t\t\tushort buildingId = buildingMan.m_buildingGrid[i * BuildingManager.BUILDINGGRID_RESOLUTION + j];\n\t\t\t\tint numIterations = 0;\n\t\t\t\twhile (buildingId != 0) {\n\t\t\t\t\tVector3 innerParkPos; Quaternion innerParkRot; float innerParkOffset;\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t//Log._Debug($\"FindParkingSpaceBuilding: Checking building {buildingId} @ i={i}, j={j}, index={i * BuildingManager.BUILDINGGRID_RESOLUTION + j}, for {refPos}, homeID {homeID}, segment {segmentId}, maxDistance {maxDistance}.\");\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tif (FindParkingSpacePropAtBuilding(vehicleInfo, homeID, ignoreParked, buildingId, ref buildingMan.m_buildings.m_buffer[(int)buildingId], segmentId, refPos, ref maxParkingSpaceDistance, randomize, out innerParkPos, out innerParkRot, out innerParkOffset)) {\n#if DEBUG\n\t\t\t\t\t\t/*/if (fineDebug && homeID != 0)\n\t\t\t\t\t\t\tLog._Debug($\"FindParkingSpaceBuilding: Found a parking space for {refPos}, homeID {homeID} @ building {buildingId}, {myParkPos}, offset {myParkOffset}!\");\n\t\t\t\t\t\t*/\n#endif\n\t\t\t\t\t\tfoundBuildingId = buildingId;\n\t\t\t\t\t\tmyParkPos = innerParkPos;\n\t\t\t\t\t\tmyParkRot = innerParkRot;\n\t\t\t\t\t\tmyParkOffset = innerParkOffset;\n\n\t\t\t\t\t\tif (!randomize || rng.Int32(GlobalConfig.Instance.ParkingAI.VicinityParkingSpaceSelectionRand) != 0)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tbuildingId = buildingMan.m_buildings.m_buffer[(int)buildingId].m_nextGridBuilding;\n\t\t\t\t\tif (++numIterations >= 49152) {\n\t\t\t\t\t\tCODebugBase<LogChannel>.Error(LogChannel.Core, \"Invalid list detected!\\n\" + Environment.StackTrace);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tif (foundBuildingId == 0) {\n#if DEBUG\n\t\t\t\tif (debug && homeID != 0)\n\t\t\t\t\tLog._Debug($\"FindParkingSpaceBuilding: Could not find a parking space for homeID {homeID}!\");\n#endif\n\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tparkPos = myParkPos;\n\t\t\tparkRot = myParkRot;\n\t\t\tparkOffset = myParkOffset;\n\n\t\t\treturn foundBuildingId;\n\t\t}\n\n\t\tpublic bool FindParkingSpacePropAtBuilding(VehicleInfo vehicleInfo, ushort homeID, ushort ignoreParked, ushort buildingID, ref Building building, ushort segmentId, Vector3 refPos, ref float maxDistance, bool randomize, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[22];\n#endif\n\n\t\t\tint buildingWidth = building.Width;\n\t\t\tint buildingLength = building.Length;\n\n\t\t\t// NON-STOCK CODE START\n\t\t\tparkOffset = -1f; // only set if segmentId != 0\n\t\t\tparkPos = default(Vector3);\n\t\t\tparkRot = default(Quaternion);\n\n\t\t\tif ((building.m_flags & Building.Flags.Created) == Building.Flags.None) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"Refusing to find parking space at building {buildingID}! Building is not created.\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif ((building.m_problems & Notification.Problem.TurnedOff) != Notification.Problem.None) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"Refusing to find parking space at building {buildingID}! Building is not active.\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif ((building.m_flags & Building.Flags.Collapsed) != Building.Flags.None) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"Refusing to find parking space at building {buildingID}! Building is collapsed.\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t/*else {\n\t\t\t// NON-STOCK CODE END\n\t\t\t\tfloat diagWidth = Mathf.Sqrt((float)(buildingWidth * buildingWidth + buildingLength * buildingLength)) * 8f;\n\t\t\t\tif (VectorUtils.LengthXZ(building.m_position - refPos) >= maxDistance + diagWidth) {*/\n#if DEBUG\n\t\t\t/*if (fineDebug)\n\t\t\t\tLog._Debug($\"Refusing to find parking space at building {buildingID}! {VectorUtils.LengthXZ(building.m_position - refPos)} >= {maxDistance + diagWidth} (maxDistance={maxDistance})\");*/\n#endif\n\t\t\t/*return false;\n\t\t}\n\t}*/ // NON-STOCK CODE\n\n\t\t\tRandomizer rng = Singleton<SimulationManager>.instance.m_randomizer; // NON-STOCK CODE\n\n\t\t\tbool isElectric = vehicleInfo.m_class.m_subService != ItemClass.SubService.ResidentialLow;\n\t\t\tBuildingInfo buildingInfo = building.Info;\n\t\t\tMatrix4x4 transformMatrix = default(Matrix4x4);\n\t\t\tbool transformMatrixCalculated = false;\n\t\t\tbool result = false;\n\t\t\tif (buildingInfo.m_class.m_service == ItemClass.Service.Residential && buildingID != homeID && rng.Int32((uint)Options.getRecklessDriverModulo()) != 0) { // NON-STOCK CODE\n#if DEBUG\n\t\t\t\t/*if (fineDebug)\n\t\t\t\t\tLog._Debug($\"Refusing to find parking space at building {buildingID}! Building is a residential building which does not match home id {homeID}.\");*/\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfloat propMinDistance = 9999f; // NON-STOCK CODE\n\t\t\tif (buildingInfo.m_props != null && (buildingInfo.m_hasParkingSpaces & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None) {\n\t\t\t\tfor (int i = 0; i < buildingInfo.m_props.Length; i++) {\n\t\t\t\t\tBuildingInfo.Prop prop = buildingInfo.m_props[i];\n\t\t\t\t\tRandomizer randomizer = new Randomizer((int)buildingID << 6 | prop.m_index);\n\t\t\t\t\tif (randomizer.Int32(100u) < prop.m_probability && buildingLength >= prop.m_requiredLength) {\n\t\t\t\t\t\tPropInfo propInfo = prop.m_finalProp;\n\t\t\t\t\t\tif (propInfo != null) {\n\t\t\t\t\t\t\tpropInfo = propInfo.GetVariation(ref randomizer);\n\t\t\t\t\t\t\tif (propInfo.m_parkingSpaces != null && propInfo.m_parkingSpaces.Length != 0) {\n\t\t\t\t\t\t\t\tif (!transformMatrixCalculated) {\n\t\t\t\t\t\t\t\t\ttransformMatrixCalculated = true;\n\t\t\t\t\t\t\t\t\tVector3 pos = Building.CalculateMeshPosition(buildingInfo, building.m_position, building.m_angle, building.Length);\n\t\t\t\t\t\t\t\t\tQuaternion q = Quaternion.AngleAxis(building.m_angle * 57.29578f, Vector3.down);\n\t\t\t\t\t\t\t\t\ttransformMatrix.SetTRS(pos, q, Vector3.one);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tVector3 position = transformMatrix.MultiplyPoint(prop.m_position);\n\t\t\t\t\t\t\t\tif (CustomPassengerCarAI.FindParkingSpaceProp(isElectric, ignoreParked, propInfo, position, building.m_angle + prop.m_radAngle, prop.m_fixedHeight, refPos, vehicleInfo.m_generatedInfo.m_size.x, vehicleInfo.m_generatedInfo.m_size.z, ref propMinDistance, ref parkPos, ref parkRot)) { // NON-STOCK CODE\n\t\t\t\t\t\t\t\t\tresult = true;\n\t\t\t\t\t\t\t\t\tif (randomize && propMinDistance <= maxDistance && rng.Int32(GlobalConfig.Instance.ParkingAI.VicinityParkingSpaceSelectionRand) == 0)\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (result && propMinDistance <= maxDistance) {\n\t\t\t\tmaxDistance = propMinDistance; // NON-STOCK CODE\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"Found parking space prop in range ({maxDistance}) at building {buildingID}.\");\n#endif\n\t\t\t\tif (segmentId != 0) {\n\t\t\t\t\t// check if building is accessible from the given segment\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"Calculating unspawn position of building {buildingID} for segment {segmentId}.\");\n#endif\n\n\t\t\t\t\tVector3 unspawnPos;\n\t\t\t\t\tVector3 unspawnTargetPos;\n\t\t\t\t\tbuilding.Info.m_buildingAI.CalculateUnspawnPosition(buildingID, ref building, ref Singleton<SimulationManager>.instance.m_randomizer, vehicleInfo, out unspawnPos, out unspawnTargetPos);\n\n\t\t\t\t\tVector3 lanePos;\n\t\t\t\t\tuint laneId;\n\t\t\t\t\tint laneIndex;\n\t\t\t\t\tfloat laneOffset;\n\t\t\t\t\t// calculate segment offset\n\t\t\t\t\tif (Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].GetClosestLanePosition(unspawnPos, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, out lanePos, out laneId, out laneIndex, out laneOffset)) {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"Succeeded in finding unspawn position lane offset for building {buildingID}, segment {segmentId}, unspawnPos={unspawnPos}! lanePos={lanePos}, dist={(lanePos - unspawnPos).magnitude}, laneId={laneId}, laneIndex={laneIndex}, laneOffset={laneOffset}\");\n#endif\n\n\t\t\t\t\t\t/*if (dist > 16f) {\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"Distance between unspawn position and lane position is too big! {dist} unspawnPos={unspawnPos} lanePos={lanePos}\");\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}*/\n\n\t\t\t\t\t\tparkOffset = laneOffset;\n\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog.Warning($\"Could not find unspawn position lane offset for building {buildingID}, segment {segmentId}, unspawnPos={unspawnPos}!\");\n#endif\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t} else {\n#if DEBUG\n\t\t\t\tif (result && debug)\n\t\t\t\t\tLog._Debug($\"Could not find parking space prop in range ({maxDistance}) at building {buildingID}.\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tpublic bool FindParkingSpaceRoadSideForVehiclePos(VehicleInfo vehicleInfo, ushort ignoreParked, ushort segmentId, Vector3 refPos, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset, out uint laneId, out int laneIndex) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[22];\n#endif\n\n\t\t\tfloat width = vehicleInfo.m_generatedInfo.m_size.x;\n\t\t\tfloat length = vehicleInfo.m_generatedInfo.m_size.z;\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tif ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Created) != NetSegment.Flags.None) {\n\t\t\t\tif (netManager.m_segments.m_buffer[segmentId].GetClosestLanePosition(refPos, NetInfo.LaneType.Parking, VehicleInfo.VehicleType.Car, out parkPos, out laneId, out laneIndex, out parkOffset)) {\n\t\t\t\t\tif (!Options.parkingRestrictionsEnabled || ParkingRestrictionsManager.Instance.IsParkingAllowed(segmentId, netManager.m_segments.m_buffer[segmentId].Info.m_lanes[laneIndex].m_finalDirection)) {\n\t\t\t\t\t\tif (CustomPassengerCarAI.FindParkingSpaceRoadSide(ignoreParked, segmentId, parkPos, width, length, out parkPos, out parkRot, out parkOffset)) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"FindParkingSpaceRoadSideForVehiclePos: Found a parking space for refPos {refPos} @ {parkPos}, laneId {laneId}, laneIndex {laneIndex}!\");\n#endif\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//\n\n\t\t\tparkPos = default(Vector3);\n\t\t\tparkRot = default(Quaternion);\n\t\t\tlaneId = 0;\n\t\t\tlaneIndex = -1;\n\t\t\tparkOffset = -1f;\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic bool FindParkingSpaceRoadSide(ushort ignoreParked, Vector3 refPos, float width, float length, float maxDistance, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset) {\n\t\t\treturn FindParkingSpaceAtRoadSide(ignoreParked, refPos, width, length, maxDistance, false, out parkPos, out parkRot, out parkOffset) != 0;\n\t\t}\n\n\t\tpublic bool FindParkingSpaceBuilding(VehicleInfo vehicleInfo, ushort homeID, ushort ignoreParked, ushort segmentId, Vector3 refPos, float maxBuildingDistance, float maxParkingSpaceDistance, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset) {\n\t\t\treturn FindParkingSpaceBuilding(vehicleInfo, homeID, ignoreParked, segmentId, refPos, maxBuildingDistance, maxParkingSpaceDistance, false, out parkPos, out parkRot, out parkOffset) != 0;\n\t\t}\n\n\t\tpublic bool GetBuildingInfoViewColor(ushort buildingId, ref Building buildingData, ref ExtBuilding extBuilding, InfoManager.InfoMode infoMode, out Color? color) {\n\t\t\tcolor = null;\n\n\t\t\tif (infoMode == InfoManager.InfoMode.Traffic) {\n\t\t\t\t// parking space demand info view\n\t\t\t\tcolor = Color.Lerp(Singleton<InfoManager>.instance.m_properties.m_modeProperties[(int)infoMode].m_targetColor, Singleton<InfoManager>.instance.m_properties.m_modeProperties[(int)infoMode].m_negativeColor, Mathf.Clamp01((float)extBuilding.parkingSpaceDemand * 0.01f));\n\t\t\t\treturn true;\n\t\t\t} else if (infoMode == InfoManager.InfoMode.Transport && !(buildingData.Info.m_buildingAI is DepotAI)) {\n\t\t\t\t// public transport demand info view\n\t\t\t\t// TODO should not depend on UI class \"TrafficManagerTool\"\n\t\t\t\tcolor = Color.Lerp(Singleton<InfoManager>.instance.m_properties.m_modeProperties[(int)InfoManager.InfoMode.Traffic].m_targetColor, Singleton<InfoManager>.instance.m_properties.m_modeProperties[(int)InfoManager.InfoMode.Traffic].m_negativeColor, Mathf.Clamp01((float)(TrafficManagerTool.CurrentTransportDemandViewMode == TransportDemandViewMode.Outgoing ? extBuilding.outgoingPublicTransportDemand : extBuilding.incomingPublicTransportDemand) * 0.01f));\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic string EnrichLocalizedCitizenStatus(string ret, ref ExtCitizenInstance extInstance, ref ExtCitizen extCitizen) {\n\t\t\tif (extInstance.IsValid()) {\n\t\t\t\tswitch (extInstance.pathMode) {\n\t\t\t\t\tcase ExtPathMode.ApproachingParkedCar:\n\t\t\t\t\tcase ExtPathMode.RequiresCarPath:\n\t\t\t\t\tcase ExtPathMode.RequiresMixedCarPathToTarget:\n\t\t\t\t\t\tret = Translation.GetString(\"Entering_vehicle\") + \", \" + ret;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtPathMode.RequiresWalkingPathToParkedCar:\n\t\t\t\t\tcase ExtPathMode.CalculatingWalkingPathToParkedCar:\n\t\t\t\t\tcase ExtPathMode.WalkingToParkedCar:\n\t\t\t\t\t\tret = Translation.GetString(\"Walking_to_car\") + \", \" + ret;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtPathMode.CalculatingWalkingPathToTarget:\n\t\t\t\t\tcase ExtPathMode.TaxiToTarget:\n\t\t\t\t\tcase ExtPathMode.WalkingToTarget:\n\t\t\t\t\t\tif ((extCitizen.transportMode & ExtCitizen.ExtTransportMode.PublicTransport) != ExtCitizen.ExtTransportMode.None) {\n\t\t\t\t\t\t\tret = Translation.GetString(\"Using_public_transport\") + \", \" + ret;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tret = Translation.GetString(\"Walking\") + \", \" + ret;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToTarget:\n\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToKnownParkPos:\n\t\t\t\t\t\tret = Translation.GetString(\"Thinking_of_a_good_parking_spot\") + \", \" + ret;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic string EnrichLocalizedCarStatus(string ret, ref ExtCitizenInstance driverExtInstance) {\n\t\t\tif (driverExtInstance.IsValid()) {\n\t\t\t\tswitch (driverExtInstance.pathMode) {\n\t\t\t\t\tcase ExtPathMode.DrivingToAltParkPos:\n\t\t\t\t\t\tif (driverExtInstance.failedParkingAttempts <= 1) {\n\t\t\t\t\t\t\tret = Translation.GetString(\"Driving_to_a_parking_spot\") + \", \" + ret;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tret = Translation.GetString(\"Driving_to_another_parking_spot\") + \" (#\" + driverExtInstance.failedParkingAttempts + \"), \" + ret;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToKnownParkPos:\n\t\t\t\t\tcase ExtPathMode.DrivingToKnownParkPos:\n\t\t\t\t\t\tret = Translation.GetString(\"Driving_to_a_parking_spot\") + \", \" + ret;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtPathMode.ParkingFailed:\n\t\t\t\t\tcase ExtPathMode.CalculatingCarPathToAltParkPos:\n\t\t\t\t\t\tret = Translation.GetString(\"Looking_for_a_parking_spot\") + \", \" + ret;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ExtPathMode.RequiresWalkingPathToTarget:\n\t\t\t\t\t\tret = Locale.Get(\"VEHICLE_STATUS_PARKING\") + \", \" + ret;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/CustomSegmentLightsManager.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing ColossalFramework;\nusing TrafficManager.Geometry;\nusing TrafficManager.Util;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.State;\nusing System.Linq;\nusing TrafficManager.Traffic;\nusing CSUtil.Commons;\nusing TrafficManager.TrafficLight.Impl;\nusing TrafficManager.Geometry.Impl;\n\nnamespace TrafficManager.Manager.Impl {\n\t/// <summary>\n\t/// Manages the states of all custom traffic lights on the map\n\t/// </summary>\n\tpublic class CustomSegmentLightsManager : AbstractGeometryObservingManager, ICustomSegmentLightsManager {\n\t\tpublic static CustomSegmentLightsManager Instance { get; private set; } = null;\n\n\t\tstatic CustomSegmentLightsManager() {\n\t\t\tInstance = new CustomSegmentLightsManager();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// custom traffic lights by segment id\n\t\t/// </summary>\n\t\tprivate CustomSegment[] CustomSegments = new CustomSegment[NetManager.MAX_SEGMENT_COUNT];\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"Custom segments:\");\n\t\t\tfor (int i = 0; i < CustomSegments.Length; ++i) {\n\t\t\t\tif (CustomSegments[i] == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLog._Debug($\"Segment {i}: {CustomSegments[i]}\");\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds custom traffic lights at the specified node and segment.\n\t\t/// Light states (red, yellow, green) are taken from the \"live\" state, that is the traffic light's light state right before the custom light takes control.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"startNode\"></param>\n\t\tpublic ICustomSegmentLights AddLiveSegmentLights(ushort segmentId, bool startNode) {\n\t\t\tSegmentGeometry segGeometry = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeometry == null) {\n\t\t\t\tLog.Error($\"CustomTrafficLightsManager.AddLiveSegmentLights: Segment {segmentId} is invalid.\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tSegmentEndGeometry endGeometry = segGeometry.GetEnd(startNode);\n\t\t\tif (endGeometry == null) {\n\t\t\t\tLog.Error($\"CustomTrafficLightsManager.AddLiveSegmentLights: Segment {segmentId} is not connected to a node @ start {startNode}\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tvar currentFrameIndex = Singleton<SimulationManager>.instance.m_currentFrameIndex;\n\n\t\t\tRoadBaseAI.TrafficLightState vehicleLightState;\n\t\t\tRoadBaseAI.TrafficLightState pedestrianLightState;\n\t\t\tbool vehicles;\n\t\t\tbool pedestrians;\n\n\t\t\tRoadBaseAI.GetTrafficLightState(endGeometry.NodeId(), ref Singleton<NetManager>.instance.m_segments.m_buffer[segmentId],\n\t\t\t\tcurrentFrameIndex - 256u, out vehicleLightState, out pedestrianLightState, out vehicles,\n\t\t\t\tout pedestrians);\n\n\t\t\treturn AddSegmentLights(segmentId, startNode,\n\t\t\t\tvehicleLightState == RoadBaseAI.TrafficLightState.Green\n\t\t\t\t\t? RoadBaseAI.TrafficLightState.Green\n\t\t\t\t\t: RoadBaseAI.TrafficLightState.Red);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds custom traffic lights at the specified node and segment.\n\t\t/// Light stats are set to the given light state, or to \"Red\" if no light state is given.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"startNode\"></param>\n\t\t/// <param name=\"lightState\">(optional) light state to set</param>\n\t\tpublic ICustomSegmentLights AddSegmentLights(ushort segmentId, bool startNode, RoadBaseAI.TrafficLightState lightState=RoadBaseAI.TrafficLightState.Red) {\n#if DEBUG\n\t\t\tLog._Debug($\"CustomTrafficLights.AddSegmentLights: Adding segment light: {segmentId} @ startNode={startNode}\");\n#endif\n\n\t\t\tSegmentGeometry segGeometry = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeometry == null) {\n\t\t\t\tLog.Error($\"CustomTrafficLightsManager.AddSegmentLights: Segment {segmentId} is invalid.\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tSegmentEndGeometry endGeometry = segGeometry.GetEnd(startNode);\n\t\t\tif (endGeometry == null) {\n\t\t\t\tLog.Error($\"CustomTrafficLightsManager.AddSegmentLights: Segment {segmentId} is not connected to a node @ start {startNode}\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tCustomSegment customSegment = CustomSegments[segmentId];\n\t\t\tif (customSegment == null) {\n\t\t\t\tcustomSegment = new CustomSegment();\n\t\t\t\tCustomSegments[segmentId] = customSegment;\n\t\t\t} else {\n\t\t\t\tICustomSegmentLights existingLights = startNode ? customSegment.StartNodeLights : customSegment.EndNodeLights;\n\n\t\t\t\tif (existingLights != null) {\n\t\t\t\t\texistingLights.SetLights(lightState);\n\t\t\t\t\treturn existingLights;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (startNode) {\n\t\t\t\tcustomSegment.StartNodeLights = new CustomSegmentLights(this, segmentId, startNode, false);\n\t\t\t\tcustomSegment.StartNodeLights.SetLights(lightState);\n\t\t\t\treturn customSegment.StartNodeLights;\n\t\t\t} else {\n\t\t\t\tcustomSegment.EndNodeLights = new CustomSegmentLights(this, segmentId, startNode, false);\n\t\t\t\tcustomSegment.EndNodeLights.SetLights(lightState);\n\t\t\t\treturn customSegment.EndNodeLights;\n\t\t\t}\n\t\t}\n\n\t\tpublic bool SetSegmentLights(ushort nodeId, ushort segmentId, ICustomSegmentLights lights) {\n\t\t\tSegmentEndGeometry endGeo = SegmentGeometry.Get(segmentId)?.GetEnd(nodeId);\n\t\t\tif (endGeo == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tCustomSegment customSegment = CustomSegments[segmentId];\n\t\t\tif (customSegment == null) {\n\t\t\t\tcustomSegment = new CustomSegment();\n\t\t\t\tCustomSegments[segmentId] = customSegment;\n\t\t\t}\n\n\t\t\tif (endGeo.StartNode) {\n\t\t\t\tcustomSegment.StartNodeLights = lights;\n\t\t\t} else {\n\t\t\t\tcustomSegment.EndNodeLights = lights;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Add custom traffic lights at the given node\n\t\t/// </summary>\n\t\t/// <param name=\"nodeId\"></param>\n\t\tpublic void AddNodeLights(ushort nodeId) {\n\t\t\tNodeGeometry nodeGeo = NodeGeometry.Get(nodeId);\n\t\t\tif (!nodeGeo.IsValid())\n\t\t\t\treturn;\n\n\t\t\tforeach (SegmentEndGeometry endGeo in nodeGeo.SegmentEndGeometries) {\n\t\t\t\tif (endGeo == null)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tAddSegmentLights(endGeo.SegmentId, endGeo.StartNode);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes custom traffic lights at the given node\n\t\t/// </summary>\n\t\t/// <param name=\"nodeId\"></param>\n\t\tpublic void RemoveNodeLights(ushort nodeId) {\n\t\t\tNodeGeometry nodeGeo = NodeGeometry.Get(nodeId);\n\t\t\t/*if (!nodeGeo.IsValid())\n\t\t\t\treturn;*/\n\n\t\t\tforeach (SegmentEndGeometry endGeo in nodeGeo.SegmentEndGeometries) {\n\t\t\t\tif (endGeo == null)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tRemoveSegmentLight(endGeo.SegmentId, endGeo.StartNode);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes all custom traffic lights at both ends of the given segment.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\tpublic void RemoveSegmentLights(ushort segmentId) {\n\t\t\tCustomSegments[segmentId] = null;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes the custom traffic light at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"startNode\"></param>\n\t\tpublic void RemoveSegmentLight(ushort segmentId, bool startNode) {\n#if DEBUG\n\t\t\tLog.Warning($\"Removing segment light: {segmentId} @ startNode={startNode}\");\n#endif\n\n\t\t\tCustomSegment customSegment = CustomSegments[segmentId];\n\t\t\tif (customSegment == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (startNode) {\n\t\t\t\tcustomSegment.StartNodeLights = null;\n\t\t\t} else {\n\t\t\t\tcustomSegment.EndNodeLights = null;\n\t\t\t}\n\n\t\t\tif (customSegment.StartNodeLights == null && customSegment.EndNodeLights == null) {\n\t\t\t\tCustomSegments[segmentId] = null;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Checks if a custom traffic light is present at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"startNode\"></param>\n\t\t/// <returns></returns>\n\t\tpublic bool IsSegmentLight(ushort segmentId, bool startNode) {\n\t\t\tCustomSegment customSegment = CustomSegments[segmentId];\n\t\t\tif (customSegment == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn (startNode && customSegment.StartNodeLights != null) || (!startNode && customSegment.EndNodeLights != null);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Retrieves the custom traffic light at the given segment end. If none exists, a new custom traffic light is created and returned.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"startNode\"></param>\n\t\t/// <returns>existing or new custom traffic light at segment end</returns>\n\t\tpublic ICustomSegmentLights GetOrLiveSegmentLights(ushort segmentId, bool startNode) {\n\t\t\tif (! IsSegmentLight(segmentId, startNode))\n\t\t\t\treturn AddLiveSegmentLights(segmentId, startNode);\n\n\t\t\treturn GetSegmentLights(segmentId, startNode);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Retrieves the custom traffic light at the given segment end.\n\t\t/// </summary>\n\t\t/// <param name=\"nodeId\"></param>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <returns>existing custom traffic light at segment end, <code>null</code> if none exists</returns>\n\t\tpublic ICustomSegmentLights GetSegmentLights(ushort segmentId, bool startNode, bool add=true, RoadBaseAI.TrafficLightState lightState = RoadBaseAI.TrafficLightState.Red) {\n\t\t\tif (!IsSegmentLight(segmentId, startNode)) {\n\t\t\t\tif (add)\n\t\t\t\t\treturn AddSegmentLights(segmentId, startNode, lightState);\n\t\t\t\telse\n\t\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tCustomSegment customSegment = CustomSegments[segmentId];\n\t\t\t\n\t\t\tif (startNode) {\n\t\t\t\treturn customSegment.StartNodeLights;\n\t\t\t} else {\n\t\t\t\treturn customSegment.EndNodeLights;\n\t\t\t}\n\t\t}\n\n\t\tpublic void SetLightMode(ushort segmentId, bool startNode, ExtVehicleType vehicleType, LightMode mode) {\n\t\t\tICustomSegmentLights liveLights = GetSegmentLights(segmentId, startNode);\n\t\t\tif (liveLights == null) {\n\t\t\t\tLog.Warning($\"CustomSegmentLightsManager.SetLightMode({segmentId}, {startNode}, {vehicleType}, {mode}): Could not retrieve segment lights.\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tICustomSegmentLight liveLight = liveLights.GetCustomLight(vehicleType);\n\t\t\tif (liveLight == null) {\n\t\t\t\tLog.Error($\"CustomSegmentLightsManager.SetLightMode: Cannot change light mode on seg. {segmentId} @ {startNode} for vehicle type {vehicleType} to {mode}: Vehicle light not found\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tliveLight.CurrentMode = mode;\n\t\t}\n\n\t\tpublic bool ApplyLightModes(ushort segmentId, bool startNode, ICustomSegmentLights otherLights) {\n\t\t\tICustomSegmentLights sourceLights = GetSegmentLights(segmentId, startNode);\n\t\t\tif (sourceLights == null) {\n\t\t\t\tLog.Warning($\"CustomSegmentLightsManager.ApplyLightModes({segmentId}, {startNode}, {otherLights}): Could not retrieve segment lights.\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in sourceLights.CustomLights) {\n\t\t\t\tExtVehicleType vehicleType = e.Key;\n\t\t\t\tICustomSegmentLight targetLight = e.Value;\n\n\t\t\t\tICustomSegmentLight sourceLight;\n\t\t\t\tif (otherLights.CustomLights.TryGetValue(vehicleType, out sourceLight)) {\n\t\t\t\t\ttargetLight.CurrentMode = sourceLight.CurrentMode;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic ICustomSegmentLights GetSegmentLights(ushort nodeId, ushort segmentId) {\n\t\t\tSegmentEndGeometry endGeometry = SegmentGeometry.Get(segmentId)?.GetEnd(nodeId);\n\t\t\tif (endGeometry == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn GetSegmentLights(segmentId, endGeometry.StartNode, false);\n\t\t}\n\n\t\tprotected override void HandleInvalidSegment(SegmentGeometry geometry) {\n\t\t\tRemoveSegmentLights(geometry.SegmentId);\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tCustomSegments = new CustomSegment[NetManager.MAX_SEGMENT_COUNT];\n\t\t}\n\n\t\tpublic short ClockwiseIndexOfSegmentEnd(ISegmentEndId endId) {\n\t\t\tSegmentEndGeometry endGeo = SegmentGeometry.Get(endId.SegmentId)?.GetEnd(endId.StartNode);\n\t\t\tif (endGeo == null) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn endGeo.GetClockwiseIndex();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/ExtBuildingManager.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Threading;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing TrafficManager.Util;\nusing UnityEngine;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class ExtBuildingManager : AbstractCustomManager, IExtBuildingManager {\n\t\tpublic static ExtBuildingManager Instance { get; private set; } = null;\n\n\t\tstatic ExtBuildingManager() {\n\t\t\tInstance = new ExtBuildingManager();\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// All additional data for buildings\n\t\t/// </summary>\n\t\tpublic ExtBuilding[] ExtBuildings = null;\n\n\t\tprivate ExtBuildingManager() {\n\t\t\tExtBuildings = new ExtBuilding[BuildingManager.MAX_BUILDING_COUNT];\n\t\t\tfor (uint i = 0; i < BuildingManager.MAX_BUILDING_COUNT; ++i) {\n\t\t\t\tExtBuildings[i] = new ExtBuilding((ushort)i);\n\t\t\t}\n\t\t}\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"Extended building data:\");\n\t\t\tfor (int i = 0; i < ExtBuildings.Length; ++i) {\n\t\t\t\tif (! ExtBuildings[i].IsValid()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLog._Debug($\"Building {i}: {ExtBuildings[i]}\");\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tfor (int i = 0; i < ExtBuildings.Length; ++i)\n\t\t\t\tExtBuildings[i].Reset();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/ExtCitizenInstanceManager.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Threading;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing TrafficManager.Util;\nusing UnityEngine;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class ExtCitizenInstanceManager : AbstractCustomManager, ICustomDataManager<List<Configuration.ExtCitizenInstanceData>>, IExtCitizenInstanceManager {\n\t\tpublic static ExtCitizenInstanceManager Instance = new ExtCitizenInstanceManager();\n\n\t\t/// <summary>\n\t\t/// All additional data for citizen instance. Index: citizen instance id\n\t\t/// </summary>\n\t\tpublic ExtCitizenInstance[] ExtInstances = null;\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"Extended citizen instance data:\");\n\t\t\tfor (int i = 0; i < ExtInstances.Length; ++i) {\n\t\t\t\tif (!ExtInstances[i].IsValid()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLog._Debug($\"Citizen instance {i}: {ExtInstances[i]}\");\n\t\t\t}\n\t\t}\n\n\t\tinternal void OnReleaseInstance(ushort instanceId) {\n\t\t\tExtInstances[instanceId].Reset();\n\t\t}\n\n\t\tpublic void ResetInstance(ushort instanceId) {\n\t\t\tExtInstances[instanceId].Reset();\n\t\t}\n\n\t\tprivate ExtCitizenInstanceManager() {\n\t\t\tExtInstances = new ExtCitizenInstance[CitizenManager.MAX_INSTANCE_COUNT];\n\t\t\tfor (uint i = 0; i < CitizenManager.MAX_INSTANCE_COUNT; ++i) {\n\t\t\t\tExtInstances[i] = new ExtCitizenInstance((ushort)i);\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tReset();\n\t\t}\n\n\t\tinternal void Reset() {\n\t\t\tfor (int i = 0; i < ExtInstances.Length; ++i) {\n\t\t\t\tExtInstances[i].Reset();\n\t\t\t}\n\t\t}\n\n\t\tpublic bool LoadData(List<Configuration.ExtCitizenInstanceData> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading {data.Count} extended citizen instances\");\n\n\t\t\tforeach (Configuration.ExtCitizenInstanceData item in data) {\n\t\t\t\ttry {\n\t\t\t\t\tuint instanceId = item.instanceId;\n\t\t\t\t\tExtInstances[instanceId].pathMode = (ExtPathMode)item.pathMode;\n\t\t\t\t\tExtInstances[instanceId].failedParkingAttempts = item.failedParkingAttempts;\n\t\t\t\t\tExtInstances[instanceId].parkingSpaceLocationId = item.parkingSpaceLocationId;\n\t\t\t\t\tExtInstances[instanceId].parkingSpaceLocation = (ExtParkingSpaceLocation)item.parkingSpaceLocation;\n\t\t\t\t\tif (item.parkingPathStartPositionSegment != 0) {\n\t\t\t\t\t\tPathUnit.Position pos = new PathUnit.Position();\n\t\t\t\t\t\tpos.m_segment = item.parkingPathStartPositionSegment;\n\t\t\t\t\t\tpos.m_lane = item.parkingPathStartPositionLane;\n\t\t\t\t\t\tpos.m_offset = item.parkingPathStartPositionOffset;\n\t\t\t\t\t\tExtInstances[instanceId].parkingPathStartPosition = pos;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tExtInstances[instanceId].parkingPathStartPosition = null;\n\t\t\t\t\t}\n\t\t\t\t\tExtInstances[instanceId].returnPathId = item.returnPathId;\n\t\t\t\t\tExtInstances[instanceId].returnPathState = (ExtPathState)item.returnPathState;\n\t\t\t\t\tExtInstances[instanceId].lastDistanceToParkedCar = item.lastDistanceToParkedCar;\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore, as it's probably corrupt save data. it'll be culled on next save\n\t\t\t\t\tLog.Warning(\"Error loading ext. citizen instance: \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn success;\n\t\t}\n\n\t\tpublic List<Configuration.ExtCitizenInstanceData> SaveData(ref bool success) {\n\t\t\tList<Configuration.ExtCitizenInstanceData> ret = new List<Configuration.ExtCitizenInstanceData>();\n\t\t\tfor (uint instanceId = 0; instanceId < CitizenManager.MAX_INSTANCE_COUNT; ++instanceId) {\n\t\t\t\ttry {\n\t\t\t\t\tif ((Singleton<CitizenManager>.instance.m_instances.m_buffer[instanceId].m_flags & CitizenInstance.Flags.Created) == CitizenInstance.Flags.None) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (ExtInstances[instanceId].pathMode == ExtPathMode.None && ExtInstances[instanceId].returnPathId == 0) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tConfiguration.ExtCitizenInstanceData item = new Configuration.ExtCitizenInstanceData(instanceId);\n\t\t\t\t\titem.pathMode = (int)ExtInstances[instanceId].pathMode;\n\t\t\t\t\titem.failedParkingAttempts = ExtInstances[instanceId].failedParkingAttempts;\n\t\t\t\t\titem.parkingSpaceLocationId = ExtInstances[instanceId].parkingSpaceLocationId;\n\t\t\t\t\titem.parkingSpaceLocation = (int)ExtInstances[instanceId].parkingSpaceLocation;\n\t\t\t\t\tif (ExtInstances[instanceId].parkingPathStartPosition != null) {\n\t\t\t\t\t\tPathUnit.Position pos = (PathUnit.Position)ExtInstances[instanceId].parkingPathStartPosition;\n\t\t\t\t\t\titem.parkingPathStartPositionSegment = pos.m_segment;\n\t\t\t\t\t\titem.parkingPathStartPositionLane = pos.m_lane;\n\t\t\t\t\t\titem.parkingPathStartPositionOffset = pos.m_offset;\n\t\t\t\t\t} else {\n\t\t\t\t\t\titem.parkingPathStartPositionSegment = 0;\n\t\t\t\t\t\titem.parkingPathStartPositionLane = 0;\n\t\t\t\t\t\titem.parkingPathStartPositionOffset = 0;\n\t\t\t\t\t}\n\t\t\t\t\titem.returnPathId = ExtInstances[instanceId].returnPathId;\n\t\t\t\t\titem.returnPathState = (int)ExtInstances[instanceId].returnPathState;\n\t\t\t\t\titem.lastDistanceToParkedCar = ExtInstances[instanceId].lastDistanceToParkedCar;\n\t\t\t\t\tret.Add(item);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tLog.Error($\"Exception occurred while saving ext. citizen instances @ {instanceId}: {ex.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool IsAtOutsideConnection(ushort instanceId, ref CitizenInstance instanceData, ref ExtCitizenInstance extInstance, Vector3 startPos) {\n#if DEBUG\n\t\t\tbool citDebug =\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == extInstance.GetCitizenId()) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == Singleton<CitizenManager>.instance.m_instances.m_buffer[extInstance.instanceId].m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == Singleton<CitizenManager>.instance.m_instances.m_buffer[extInstance.instanceId].m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"ExtCitizenInstanceManager.IsAtOutsideConnection({extInstance.instanceId}): called. Path: {instanceData.m_path} sourceBuilding={instanceData.m_sourceBuilding}\");\n#endif\n\n\t\t\tbool ret =\n\t\t\t\t(Singleton<BuildingManager>.instance.m_buildings.m_buffer[instanceData.m_sourceBuilding].m_flags & Building.Flags.IncomingOutgoing) != Building.Flags.None &&\n\t\t\t\t(startPos - Singleton<BuildingManager>.instance.m_buildings.m_buffer[instanceData.m_sourceBuilding].m_position).magnitude <= GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance;\n\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"ExtCitizenInstanceManager.IsAtOutsideConnection({instanceId}): ret={ret}\");\n#endif\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/ExtCitizenManager.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Threading;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing TrafficManager.Util;\nusing UnityEngine;\nusing static TrafficManager.Traffic.Data.ExtCitizen;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class ExtCitizenManager : AbstractCustomManager, ICustomDataManager<List<Configuration.ExtCitizenData>>, IExtCitizenManager {\n\t\tpublic static ExtCitizenManager Instance = new ExtCitizenManager();\n\n\t\t/// <summary>\n\t\t/// All additional data for citizens. Index: citizen id\n\t\t/// </summary>\n\t\tpublic ExtCitizen[] ExtCitizens = null;\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"Extended citizen data:\");\n\t\t\tfor (int i = 0; i < ExtCitizens.Length; ++i) {\n\t\t\t\tif (!ExtCitizens[i].IsValid()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLog._Debug($\"Citizen {i}: {ExtCitizens[i]}\");\n\t\t\t}\n\t\t}\n\n\t\tinternal void OnReleaseCitizen(uint citizenId) {\n\t\t\tExtCitizens[citizenId].Reset();\n\t\t}\n\n\t\tpublic void OnArriveAtDestination(uint citizenId, ref Citizen citizen) {\n#if DEBUG\n\t\t\tbool citDebug = GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == citizenId;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (fineDebug)\n\t\t\t\tLog._Debug($\"ExtCitizenManager.OnArriveAtDestination({citizenId}) called\");\n#endif\n\t\t\tif (ExtCitizens[citizenId].lastLocation == Citizen.Location.Home) {\n#if DEBUG\n\t\t\t\tif (fineDebug)\n\t\t\t\t\tLog._Debug($\"ExtCitizenManager.OnArriveAtDestination({citizenId}): setting lastTransportMode=transportMode={ExtCitizens[citizenId].transportMode}\");\n#endif\n\t\t\t\tExtCitizens[citizenId].lastTransportMode = ExtCitizens[citizenId].transportMode;\n\t\t\t}\n\t\t\tExtCitizens[citizenId].transportMode = ExtTransportMode.None;\n\n\t\t\tif (citizen.CurrentLocation != Citizen.Location.Moving) {\n\t\t\t\tExtCitizens[citizenId].lastLocation = citizen.CurrentLocation;\n#if DEBUG\n\t\t\t\tif (fineDebug)\n\t\t\t\t\tLog._Debug($\"ExtCitizenManager.OnArriveAtDestination({citizenId}): setting lastLocation={ExtCitizens[citizenId].lastLocation}\");\n#endif\n\t\t\t}\n\t\t}\n\n\t\tpublic void ResetCitizen(uint citizenId) {\n\t\t\tExtCitizens[citizenId].Reset();\n\t\t}\n\n\t\tprivate ExtCitizenManager() {\n\t\t\tExtCitizens = new ExtCitizen[CitizenManager.MAX_CITIZEN_COUNT];\n\t\t\tfor (uint i = 0; i < CitizenManager.MAX_CITIZEN_COUNT; ++i) {\n\t\t\t\tExtCitizens[i] = new ExtCitizen(i);\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tReset();\n\t\t}\n\n\t\tinternal void Reset() {\n\t\t\tfor (int i = 0; i < ExtCitizens.Length; ++i) {\n\t\t\t\tExtCitizens[i].Reset();\n\t\t\t}\n\t\t}\n\n\t\tpublic bool LoadData(List<Configuration.ExtCitizenData> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading {data.Count} extended citizens\");\n\n\t\t\tforeach (Configuration.ExtCitizenData item in data) {\n\t\t\t\ttry {\n\t\t\t\t\tuint citizenId = item.citizenId;\n\t\t\t\t\tExtCitizens[citizenId].transportMode = (ExtTransportMode)item.lastTransportMode;\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore, as it's probably corrupt save data. it'll be culled on next save\n\t\t\t\t\tLog.Warning(\"Error loading ext. citizen: \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn success;\n\t\t}\n\n\t\tpublic List<Configuration.ExtCitizenData> SaveData(ref bool success) {\n\t\t\tList<Configuration.ExtCitizenData> ret = new List<Configuration.ExtCitizenData>();\n\t\t\tfor (uint citizenId = 0; citizenId < CitizenManager.MAX_CITIZEN_COUNT; ++citizenId) {\n\t\t\t\ttry {\n\t\t\t\t\tif ((Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenId].m_flags & Citizen.Flags.Created) == Citizen.Flags.None) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (ExtCitizens[citizenId].transportMode == ExtTransportMode.None) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tConfiguration.ExtCitizenData item = new Configuration.ExtCitizenData(citizenId);\n\t\t\t\t\titem.lastTransportMode = (int)ExtCitizens[citizenId].transportMode;\n\t\t\t\t\tret.Add(item);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tLog.Error($\"Exception occurred while saving ext. citizen @ {citizenId}: {ex.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/GeometryManager.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Util;\nusing static TrafficManager.Geometry.Impl.NodeGeometry;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class GeometryManager : AbstractCustomManager, IGeometryManager {\n\t\tpublic static GeometryManager Instance { get; private set; } = new GeometryManager();\n\n\t\tpublic class GeometryUpdateObservable : GenericObservable<GeometryUpdate> {\n\n\t\t}\n\n\t\tprivate bool stateUpdated;\n\t\tprivate ulong[] updatedSegmentBuckets;\n\t\tprivate ulong[] updatedNodeBuckets;\n\t\tprivate object updateLock;\n\t\tprivate Queue<SegmentEndReplacement> segmentReplacements;\n\t\tprivate GeometryUpdateObservable geometryUpdateObservable;\n\n\t\tprivate GeometryManager() {\n\t\t\tstateUpdated = false;\n\t\t\tupdatedSegmentBuckets = new ulong[576];\n\t\t\tupdatedNodeBuckets = new ulong[512];\n\t\t\tupdateLock = new object();\n\t\t\tsegmentReplacements = new Queue<SegmentEndReplacement>();\n\t\t\tgeometryUpdateObservable = new GeometryUpdateObservable();\n\t\t}\n\n\t\tpublic override void OnBeforeLoadData() {\n\t\t\tbase.OnBeforeLoadData();\n\t\t\tsegmentReplacements.Clear();\n\t\t\tSimulationStep();\n\t\t}\n\n\t\tpublic void OnUpdateSegment(SegmentGeometry geo) {\n\t\t\tMarkAsUpdated(geo);\n\t\t}\n\n\t\tpublic void SimulationStep(bool onlyFirstPass=false) {\n#if DEBUGGEO\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[5];\n#endif\n\t\t\tif (!stateUpdated) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tif (!onlyFirstPass && (netManager.m_segmentsUpdated || netManager.m_nodesUpdated)) { // TODO maybe refactor NetManager use (however this could influence performance)\n#if DEBUGGEO\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"GeometryManager.SimulationStep(): Skipping! stateUpdated={stateUpdated}, m_segmentsUpdated={netManager.m_segmentsUpdated}, m_nodesUpdated={netManager.m_nodesUpdated}\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(updateLock);\n\n\t\t\t\tbool updatesMissing = onlyFirstPass;\n\t\t\t\tfor (int pass = 0; pass < (onlyFirstPass ? 1 : 2); ++pass) {\n\t\t\t\t\tbool firstPass = pass == 0;\n\n\t\t\t\t\tint len = updatedSegmentBuckets.Length;\n\t\t\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\t\t\tulong segMask = updatedSegmentBuckets[i];\n\t\t\t\t\t\tif (segMask != 0uL) {\n\t\t\t\t\t\t\tfor (int m = 0; m < 64; m++) {\n\t\t\t\t\t\t\t\tif ((segMask & 1uL << m) != 0uL) {\n\t\t\t\t\t\t\t\t\tushort segmentId = (ushort)(i << 6 | m);\n\t\t\t\t\t\t\t\t\tSegmentGeometry segmentGeometry = SegmentGeometry.Get(segmentId, true);\n\t\t\t\t\t\t\t\t\tif (firstPass ^ !segmentGeometry.IsValid()) {\n\t\t\t\t\t\t\t\t\t\tif (! firstPass) {\n\t\t\t\t\t\t\t\t\t\t\tupdatesMissing = true;\n#if DEBUGGEO\n\t\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\t\tLog.Warning($\"GeometryManager.SimulationStep(): Detected invalid segment {segmentGeometry.SegmentId} in second pass\");\n#endif\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n#if DEBUGGEO\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"GeometryManager.SimulationStep(): Notifying observers about segment {segmentGeometry.SegmentId}. Valid? {segmentGeometry.IsValid()} First pass? {firstPass}\");\n#endif\n\t\t\t\t\t\t\t\t\tNotifyObservers(new GeometryUpdate(segmentGeometry));\n\t\t\t\t\t\t\t\t\tupdatedSegmentBuckets[i] &= ~(1uL << m);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tlen = updatedNodeBuckets.Length;\n\t\t\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\t\t\tulong nodeMask = updatedNodeBuckets[i];\n\t\t\t\t\t\tif (nodeMask != 0uL) {\n\t\t\t\t\t\t\tfor (int m = 0; m < 64; m++) {\n\t\t\t\t\t\t\t\tif ((nodeMask & 1uL << m) != 0uL) {\n\t\t\t\t\t\t\t\t\tushort nodeId = (ushort)(i << 6 | m);\n\t\t\t\t\t\t\t\t\tNodeGeometry nodeGeometry = NodeGeometry.Get(nodeId);\n\t\t\t\t\t\t\t\t\tif (firstPass ^ !nodeGeometry.IsValid()) {\n\t\t\t\t\t\t\t\t\t\tif (!firstPass) {\n\t\t\t\t\t\t\t\t\t\t\tupdatesMissing = true;\n#if DEBUGGEO\n\t\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\t\tLog.Warning($\"GeometryManager.SimulationStep(): Detected invalid node {nodeGeometry.NodeId} in second pass\");\n#endif\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n#if DEBUGGEO\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"GeometryManager.SimulationStep(): Notifying observers about node {nodeGeometry.NodeId}. Valid? {nodeGeometry.IsValid()} First pass? {firstPass}\");\n#endif\n\t\t\t\t\t\t\t\t\tNotifyObservers(new GeometryUpdate(nodeGeometry));\n\t\t\t\t\t\t\t\t\tupdatedNodeBuckets[i] &= ~(1uL << m);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (! updatesMissing) {\n\t\t\t\t\twhile (segmentReplacements.Count > 0) {\n\t\t\t\t\t\tSegmentEndReplacement replacement = segmentReplacements.Dequeue();\n#if DEBUGGEO\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"GeometryManager.SimulationStep(): Notifying observers about segment end replacement {replacement}\");\n#endif\n\t\t\t\t\t\tNotifyObservers(new GeometryUpdate(replacement));\n\t\t\t\t\t}\n\n\t\t\t\t\tstateUpdated = false;\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(updateLock);\n\t\t\t}\n\t\t}\n\n\t\tpublic void MarkAllAsUpdated() {\n\t\t\tfor (uint segmentId = 0; segmentId < NetManager.MAX_SEGMENT_COUNT; ++segmentId) {\n\t\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get((ushort)segmentId);\n\t\t\t\tif (segGeo != null) {\n\t\t\t\t\tMarkAsUpdated(segGeo, true);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void MarkAsUpdated(SegmentGeometry geometry, bool updateNodes = true) {\n#if DEBUGGEO\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\tLog._Debug($\"GeometryManager.MarkAsUpdated(segment {geometry.SegmentId}): Marking segment as updated\");\n#endif\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(updateLock);\n\n\t\t\t\tushort segmentId = geometry.SegmentId;\n\n\t\t\t\tupdatedSegmentBuckets[segmentId >> 6] |= 1uL << (int)(segmentId & 63);\n\t\t\t\tstateUpdated = true;\n\n\t\t\t\tif (updateNodes) {\n\t\t\t\t\tMarkAsUpdated(NodeGeometry.Get(geometry.StartNodeId()));\n\t\t\t\t\tMarkAsUpdated(NodeGeometry.Get(geometry.EndNodeId()));\n\t\t\t\t}\n\n\t\t\t\tif (! geometry.IsValid()) {\n\t\t\t\t\tSimulationStep(true);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(updateLock);\n\t\t\t}\n\t\t}\n\n\t\tpublic void MarkAsUpdated(NodeGeometry geometry, bool updateSegments = false) {\n#if DEBUGGEO\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\tLog._Debug($\"GeometryManager.MarkAsUpdated(node {geometry.NodeId}): Marking node as updated\");\n#endif\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(updateLock);\n\n\t\t\t\tushort nodeId = geometry.NodeId;\n\t\t\t\tif (nodeId != 0) {\n\t\t\t\t\tupdatedNodeBuckets[nodeId >> 6] |= 1uL << (int)(nodeId & 63);\n\t\t\t\t\tstateUpdated = true;\n\n\t\t\t\t\tif (updateSegments) {\n\t\t\t\t\t\tforeach (SegmentEndGeometry segEndGeo in geometry.SegmentEndGeometries) {\n\t\t\t\t\t\t\tif (segEndGeo != null) {\n\t\t\t\t\t\t\t\tMarkAsUpdated(segEndGeo.GetSegmentGeometry(), false);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!geometry.IsValid()) {\n\t\t\t\t\t\tSimulationStep(true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(updateLock);\n\t\t\t}\n\t\t}\n\n\t\tpublic void OnSegmentEndReplacement(NodeGeometry.SegmentEndReplacement replacement) {\n#if DEBUGGEO\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\tLog._Debug($\"GeometryManager.OnSegmentEndReplacement(): Detected segment replacement: {replacement.oldSegmentEndId.SegmentId} -> {replacement.newSegmentEndId.SegmentId}\");\n#endif\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(updateLock);\n\n\t\t\t\tsegmentReplacements.Enqueue(replacement);\n\t\t\t\tstateUpdated = true;\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(updateLock);\n\t\t\t}\n\t\t}\n\n\t\tpublic IDisposable Subscribe(IObserver<GeometryUpdate> observer) {\n#if DEBUGGEO\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[5])\n\t\t\t\tLog._Debug($\"GeometryManager.Subscribe(): Subscribing observer {observer.GetType().Name}\");\n#endif\n\t\t\treturn geometryUpdateObservable.Subscribe(observer);\n\t\t}\n\n\t\tprotected void NotifyObservers(GeometryUpdate geometryUpdate) {\n\t\t\tgeometryUpdateObservable.NotifyObservers(geometryUpdate);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/JunctionRestrictionsManager.cs",
    "content": "using ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing TrafficManager.Traffic.Impl;\nusing TrafficManager.Util;\nusing static TrafficManager.Geometry.Impl.NodeGeometry;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class JunctionRestrictionsManager : AbstractGeometryObservingManager, ICustomDataManager<List<Configuration.SegmentNodeConf>>, IJunctionRestrictionsManager {\n\t\tpublic static JunctionRestrictionsManager Instance { get; private set; } = new JunctionRestrictionsManager();\n\n\t\tprivate SegmentFlags[] invalidSegmentFlags = null;\n\n\t\t/// <summary>\n\t\t/// Holds junction restrictions for each segment end\n\t\t/// </summary>\n\t\tprivate SegmentFlags[] SegmentFlags = null;\n\n\t\tprivate JunctionRestrictionsManager() {\n\t\t\tSegmentFlags = new Traffic.Data.SegmentFlags[NetManager.MAX_SEGMENT_COUNT];\n\t\t\tinvalidSegmentFlags = new Traffic.Data.SegmentFlags[NetManager.MAX_SEGMENT_COUNT];\n\t\t}\n\n\t\tprotected void AddInvalidSegmentEndFlags(ushort segmentId, bool startNode, ref SegmentEndFlags endFlags) {\n\t\t\tif (startNode) {\n\t\t\t\tinvalidSegmentFlags[segmentId].startNodeFlags = endFlags;\n\t\t\t} else {\n\t\t\t\tinvalidSegmentFlags[segmentId].endNodeFlags = endFlags;\n\t\t\t}\n\t\t}\n\n\t\tprotected override void HandleSegmentEndReplacement(SegmentEndReplacement replacement, SegmentEndGeometry endGeo) {\n\t\t\tISegmentEndId oldSegmentEndId = replacement.oldSegmentEndId;\n\t\t\tISegmentEndId newSegmentEndId = replacement.newSegmentEndId;\n\n\t\t\tSegmentEndFlags flags;\n\t\t\tif (oldSegmentEndId.StartNode) {\n\t\t\t\tflags = invalidSegmentFlags[oldSegmentEndId.SegmentId].startNodeFlags;\n\t\t\t\tinvalidSegmentFlags[oldSegmentEndId.SegmentId].startNodeFlags.Reset();\n\t\t\t} else {\n\t\t\t\tflags = invalidSegmentFlags[oldSegmentEndId.SegmentId].endNodeFlags;\n\t\t\t\tinvalidSegmentFlags[oldSegmentEndId.SegmentId].endNodeFlags.Reset();\n\t\t\t}\n\n\t\t\tServices.NetService.ProcessNode(endGeo.NodeId(), delegate (ushort nId, ref NetNode node) {\n\t\t\t\tflags.UpdateDefaults(newSegmentEndId.SegmentId, newSegmentEndId.StartNode, ref node);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\tLog._Debug($\"JunctionRestrictionsManager.HandleSegmentEndReplacement({replacement}): Segment replacement detected: {oldSegmentEndId.SegmentId} -> {newSegmentEndId.SegmentId} @ {newSegmentEndId.StartNode}\");\n\t\t\tSetSegmentEndFlags(newSegmentEndId.SegmentId, newSegmentEndId.StartNode, flags);\n\t\t}\n\n\t\tpublic override void OnLevelLoading() {\n\t\t\tbase.OnLevelLoading();\n\t\t\tfor (uint i = 0; i < NetManager.MAX_SEGMENT_COUNT; ++i) {\n\t\t\t\tSegmentGeometry geo = SegmentGeometry.Get((ushort)i);\n\t\t\t\tif (geo != null && geo.IsValid()) {\n\t\t\t\t\t//Log._Debug($\"JunctionRestrictionsManager.OnLevelLoading: Handling valid segment {geo.SegmentId}\");\n\t\t\t\t\tHandleValidSegment(geo);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\n\t\t\tLog._Debug($\"Junction restrictions:\");\n\t\t\tfor (int i = 0; i < SegmentFlags.Length; ++i) {\n\t\t\t\tif (SegmentFlags[i].IsDefault()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLog._Debug($\"Segment {i}: {SegmentFlags[i]}\");\n\t\t\t}\n\t\t}\n\n\t\tpublic bool MayHaveJunctionRestrictions(ushort nodeId) {\n\t\t\tNetNode.Flags flags = NetNode.Flags.None;\n\t\t\tServices.NetService.ProcessNode(nodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\tflags = node.m_flags;\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tLog._Debug($\"JunctionRestrictionsManager.MayHaveJunctionRestrictions({nodeId}): flags={(NetNode.Flags)flags}\");\n\n\t\t\tif (!LogicUtil.CheckFlags((uint)flags, (uint)(NetNode.Flags.Created | NetNode.Flags.Deleted), (uint)NetNode.Flags.Created)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn LogicUtil.CheckFlags((uint)flags, (uint)(NetNode.Flags.Junction | NetNode.Flags.Bend));\n\t\t}\n\n\t\tpublic bool HasJunctionRestrictions(ushort nodeId) {\n\t\t\tif (!Services.NetService.IsNodeValid(nodeId)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool ret = false;\n\t\t\tServices.NetService.IterateNodeSegments(nodeId, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\tif (segmentId == 0) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tbool startNode = segment.m_startNode == nodeId;\n\t\t\t\tbool isDefault = startNode\n\t\t\t\t\t? SegmentFlags[segmentId].startNodeFlags.IsDefault()\n\t\t\t\t\t: SegmentFlags[segmentId].endNodeFlags.IsDefault();\n\n\t\t\t\tif (!isDefault) {\n\t\t\t\t\tret = true;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic void RemoveJunctionRestrictions(ushort nodeId) {\n\t\t\tLog._Debug($\"JunctionRestrictionsManager.RemoveJunctionRestrictions({nodeId}) called.\");\n\t\t\tServices.NetService.IterateNodeSegments(nodeId, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\tif (segmentId == 0) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tif (segment.m_startNode == nodeId) {\n\t\t\t\t\tSegmentFlags[segmentId].startNodeFlags.Reset(false);\n\t\t\t\t} else {\n\t\t\t\t\tSegmentFlags[segmentId].endNodeFlags.Reset(false);\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\tpublic void RemoveJunctionRestrictionsIfNecessary() {\n\t\t\tfor (uint nodeId = 0; nodeId < NetManager.MAX_NODE_COUNT; ++nodeId) {\n\t\t\t\tRemoveJunctionRestrictionsIfNecessary((ushort)nodeId);\n\t\t\t}\n\t\t}\n\n\t\tpublic void RemoveJunctionRestrictionsIfNecessary(ushort nodeId) {\n\t\t\tif (!MayHaveJunctionRestrictions(nodeId)) {\n\t\t\t\tRemoveJunctionRestrictions(nodeId);\n\t\t\t}\n\t\t}\n\n\t\tprotected override void HandleInvalidSegment(SegmentGeometry geometry) {\n\t\t\tforeach (bool startNode in Constants.ALL_BOOL) {\n\t\t\t\tSegmentEndFlags flags = startNode\n\t\t\t\t\t\t? SegmentFlags[geometry.SegmentId].startNodeFlags\n\t\t\t\t\t\t: SegmentFlags[geometry.SegmentId].endNodeFlags;\n\n\t\t\t\tif (!flags.IsDefault()) {\n\t\t\t\t\tAddInvalidSegmentEndFlags(geometry.SegmentId, startNode, ref flags);\n\t\t\t\t}\n\n\t\t\t\tSegmentFlags[geometry.SegmentId].Reset(startNode, true);\n\t\t\t}\n\t\t}\n\n\t\tprotected override void HandleValidSegment(SegmentGeometry geometry) {\n\t\t\tUpdateDefaults(geometry);\n\t\t}\n\n\t\tpublic void UpdateAllDefaults() {\n\t\t\tfor (uint segmentId = 0; segmentId < NetManager.MAX_SEGMENT_COUNT; ++segmentId) {\n\t\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get((ushort)segmentId);\n\t\t\t\tif (segGeo != null) {\n\t\t\t\t\tUpdateDefaults(segGeo);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprotected void UpdateDefaults(SegmentGeometry geometry) {\n\t\t\t//Log.Warning($\"JunctionRestrictionsManager.HandleValidSegment({geometry.SegmentId}) called.\");\n\t\t\tushort startNodeId = geometry.StartNodeId();\n\t\t\tServices.NetService.ProcessNode(startNodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\tSegmentFlags[geometry.SegmentId].startNodeFlags.UpdateDefaults(geometry.SegmentId, true, ref node);\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tushort endNodeId = geometry.EndNodeId();\n\t\t\tServices.NetService.ProcessNode(endNodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\tSegmentFlags[geometry.SegmentId].endNodeFlags.UpdateDefaults(geometry.SegmentId, false, ref node);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\tpublic bool IsUturnAllowedConfigurable(ushort segmentId, bool startNode, ref NetNode node) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\n\t\t\tSegmentEndGeometry endGeo = SegmentGeometry.Get(segmentId)?.GetEnd(startNode);\n\n\t\t\tif (endGeo == null) {\n\t\t\t\tLog.Warning($\"JunctionRestrictionsManager.IsUturnAllowedConfigurable({segmentId}, {startNode}): Could not get segment end geometry\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool ret =\n\t\t\t\t(node.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition | NetNode.Flags.End | NetNode.Flags.Bend | NetNode.Flags.OneWayOut)) != NetNode.Flags.None &&\n\t\t\t\tnode.Info?.m_class?.m_service != ItemClass.Service.Beautification &&\n\t\t\t\t!endGeo.IncomingOneWay && !endGeo.OutgoingOneWay\n\t\t\t;\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.IsUturnAllowedConfigurable({segmentId}, {startNode}): ret={ret}, flags={node.m_flags}, service={node.Info?.m_class?.m_service}, incomingOneWay={endGeo.IncomingOneWay}, outgoingOneWay={endGeo.OutgoingOneWay}\");\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool GetDefaultUturnAllowed(ushort segmentId, bool startNode, ref NetNode node) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\n\t\t\tif (!Constants.ManagerFactory.JunctionRestrictionsManager.IsUturnAllowedConfigurable(segmentId, startNode, ref node)) {\n\t\t\t\tbool res = (node.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.GetDefaultUturnAllowed({segmentId}, {startNode}): Setting is not configurable. res={res}, flags={node.m_flags}\");\n#endif\n\t\t\t\treturn res;\n\t\t\t}\n\n\t\t\tbool ret = (node.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;\n\n\t\t\tif (!ret && Options.allowUTurns) {\n\t\t\t\tret = (node.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) != NetNode.Flags.None;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.GetDefaultUturnAllowed({segmentId}, {startNode}): Setting is configurable. ret={ret}, flags={node.m_flags}\");\n#endif\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool IsUturnAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentFlags[segmentId].IsUturnAllowed(startNode);\n\t\t}\n\n\t\tpublic bool IsNearTurnOnRedAllowedConfigurable(ushort segmentId, bool startNode, ref NetNode node) {\n\t\t\treturn IsTurnOnRedAllowedConfigurable(true, segmentId, startNode, ref node);\n\t\t}\n\n\t\tpublic bool IsFarTurnOnRedAllowedConfigurable(ushort segmentId, bool startNode, ref NetNode node) {\n\t\t\treturn IsTurnOnRedAllowedConfigurable(false, segmentId, startNode, ref node);\n\t\t}\n\n\t\tpublic bool IsTurnOnRedAllowedConfigurable(bool near, ushort segmentId, bool startNode, ref NetNode node) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\n\t\t\tITurnOnRedManager turnOnRedMan = Constants.ManagerFactory.TurnOnRedManager;\n\t\t\tint index = turnOnRedMan.GetIndex(segmentId, startNode);\n\t\t\tbool lhd = Services.SimulationService.LeftHandDrive;\n\t\t\tbool ret =\n\t\t\t\t(node.m_flags & NetNode.Flags.TrafficLights) != NetNode.Flags.None &&\n\t\t\t\t(((lhd == near) && turnOnRedMan.TurnOnRedSegments[index].leftSegmentId != 0) ||\n\t\t\t\t((!lhd == near) && turnOnRedMan.TurnOnRedSegments[index].rightSegmentId != 0));\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog.Warning($\"JunctionRestrictionsManager.IsTurnOnRedAllowedConfigurable({near}, {segmentId}, {startNode}): ret={ret}\");\n#endif\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool GetDefaultNearTurnOnRedAllowed(ushort segmentId, bool startNode, ref NetNode node) {\n\t\t\treturn GetDefaultTurnOnRedAllowed(true, segmentId, startNode, ref node);\n\t\t}\n\n\t\tpublic bool GetDefaultFarTurnOnRedAllowed(ushort segmentId, bool startNode, ref NetNode node) {\n\t\t\treturn GetDefaultTurnOnRedAllowed(false, segmentId, startNode, ref node);\n\t\t}\n\n\t\tpublic bool GetDefaultTurnOnRedAllowed(bool near, ushort segmentId, bool startNode, ref NetNode node) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\n\t\t\tif (!IsTurnOnRedAllowedConfigurable(near, segmentId, startNode, ref node)) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.IsTurnOnRedAllowedConfigurable({near}, {segmentId}, {startNode}): Setting is not configurable. res=false\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\tbool ret = near ? Options.allowNearTurnOnRed : Options.allowFarTurnOnRed;\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.GetTurnOnRedAllowed({near}, {segmentId}, {startNode}): Setting is configurable. ret={ret}, flags={node.m_flags}\");\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool IsTurnOnRedAllowed(bool near, ushort segmentId, bool startNode) {\n\t\t\treturn near\n\t\t\t\t? IsNearTurnOnRedAllowed(segmentId, startNode)\n\t\t\t\t: IsFarTurnOnRedAllowed(segmentId, startNode);\n\t\t}\n\n\t\tpublic bool IsNearTurnOnRedAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentFlags[segmentId].IsNearTurnOnRedAllowed(startNode);\n\t\t}\n\n\t\tpublic bool IsFarTurnOnRedAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentFlags[segmentId].IsFarTurnOnRedAllowed(startNode);\n\t\t}\n\n\t\tpublic bool IsLaneChangingAllowedWhenGoingStraightConfigurable(ushort segmentId, bool startNode, ref NetNode node) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\n\t\t\tSegmentEndGeometry endGeo = SegmentGeometry.Get(segmentId)?.GetEnd(startNode);\n\n\t\t\tif (endGeo == null) {\n\t\t\t\tLog.Warning($\"JunctionRestrictionsManager.IsLaneChangingAllowedWhenGoingStraightConfigurable({segmentId}, {startNode}): Could not get segment end geometry\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool ret =\n\t\t\t\t(node.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Transition)) != NetNode.Flags.None &&\n\t\t\t\tnode.Info?.m_class?.m_service != ItemClass.Service.Beautification &&\n\t\t\t\t!endGeo.OutgoingOneWay &&\n\t\t\t\tnode.CountSegments() > 2\n\t\t\t;\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.IsLaneChangingAllowedWhenGoingStraightConfigurable({segmentId}, {startNode}): ret={ret}, flags={node.m_flags}, service={node.Info?.m_class?.m_service}, incomingOneWay={endGeo.IncomingOneWay}, outgoingOneWay={endGeo.OutgoingOneWay}, node.CountSegments()={node.CountSegments()}\");\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool GetDefaultLaneChangingAllowedWhenGoingStraight(ushort segmentId, bool startNode, ref NetNode node) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\n\t\t\tif (!Constants.ManagerFactory.JunctionRestrictionsManager.IsLaneChangingAllowedWhenGoingStraightConfigurable(segmentId, startNode, ref node)) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.GetDefaultLaneChangingAllowedWhenGoingStraight({segmentId}, {startNode}): Setting is not configurable. res=false\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool ret = Options.allowLaneChangesWhileGoingStraight;\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.GetDefaultLaneChangingAllowedWhenGoingStraight({segmentId}, {startNode}): Setting is configurable. ret={ret}\");\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool IsLaneChangingAllowedWhenGoingStraight(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentFlags[segmentId].IsLaneChangingAllowedWhenGoingStraight(startNode);\n\t\t}\n\n\t\tpublic bool IsEnteringBlockedJunctionAllowedConfigurable(ushort segmentId, bool startNode, ref NetNode node) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\n\t\t\tSegmentEndGeometry endGeo = SegmentGeometry.Get(segmentId)?.GetEnd(startNode);\n\n\t\t\tif (endGeo == null) {\n\t\t\t\tLog.Warning($\"JunctionRestrictionsManager.IsEnteringBlockedJunctionAllowedConfigurable({segmentId}, {startNode}): Could not get segment end geometry\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool ret =\n\t\t\t\t(node.m_flags & NetNode.Flags.Junction) != NetNode.Flags.None &&\n\t\t\t\tnode.Info?.m_class?.m_service != ItemClass.Service.Beautification &&\n\t\t\t\t!endGeo.OutgoingOneWay;\n\t\t\t;\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.IsEnteringBlockedJunctionAllowedConfigurable({segmentId}, {startNode}): ret={ret}, flags={node.m_flags}, service={node.Info?.m_class?.m_service}, outgoingOneWay={endGeo.OutgoingOneWay}\");\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool GetDefaultEnteringBlockedJunctionAllowed(ushort segmentId, bool startNode, ref NetNode node) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\n\t\t\tSegmentEndGeometry endGeo = SegmentGeometry.Get(segmentId)?.GetEnd(startNode);\n\n\t\t\tif (endGeo == null) {\n\t\t\t\tLog.Warning($\"JunctionRestrictionsManager.GetDefaultEnteringBlockedJunctionAllowed({segmentId}, {startNode}): Could not get segment end geometry\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (!IsEnteringBlockedJunctionAllowedConfigurable(segmentId, startNode, ref node)) {\n\t\t\t\tbool res = (node.m_flags & (NetNode.Flags.Junction | NetNode.Flags.OneWayOut | NetNode.Flags.OneWayIn)) != NetNode.Flags.Junction || node.CountSegments() == 2;\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.GetDefaultEnteringBlockedJunctionAllowed({segmentId}, {startNode}): Setting is not configurable. res={res}, flags={node.m_flags}, node.CountSegments()={node.CountSegments()}\");\n#endif\n\t\t\t\treturn res;\n\t\t\t}\n\n\t\t\tbool ret;\n\t\t\tif (Options.allowEnterBlockedJunctions) {\n\t\t\t\tret = true;\n\t\t\t} else {\n\t\t\t\tint numOutgoing = 0;\n\t\t\t\tint numIncoming = 0;\n\t\t\t\tnode.CountLanes(endGeo.NodeId(), 0, NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle, VehicleInfo.VehicleType.Car, true, ref numOutgoing, ref numIncoming);\n\t\t\t\tret = numOutgoing == 1 || numIncoming == 1;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.GetDefaultEnteringBlockedJunctionAllowed({segmentId}, {startNode}): Setting is configurable. ret={ret}\");\n#endif\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool IsEnteringBlockedJunctionAllowed(ushort segmentId, bool startNode) {\n\t\t\t//Log.Warning($\"JunctionRestrictionsManager.IsEnteringBlockedJunctionAllowed({segmentId}, {startNode}) called.\");\n\t\t\treturn SegmentFlags[segmentId].IsEnteringBlockedJunctionAllowed(startNode);\n\t\t}\n\n\t\tpublic bool IsPedestrianCrossingAllowedConfigurable(ushort segmentId, bool startNode, ref NetNode node) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\n\t\t\tbool ret = (node.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Bend)) != NetNode.Flags.None &&\n\t\t\t\t\tnode.Info?.m_class?.m_service != ItemClass.Service.Beautification;\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.IsPedestrianCrossingAllowedConfigurable({segmentId}, {startNode}): ret={ret}, flags={node.m_flags}, service={node.Info?.m_class?.m_service}\");\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool GetDefaultPedestrianCrossingAllowed(ushort segmentId, bool startNode, ref NetNode node) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\n\t\t\tif (!IsPedestrianCrossingAllowedConfigurable(segmentId, startNode, ref node)) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.GetDefaultPedestrianCrossingAllowed({segmentId}, {startNode}): Setting is not configurable. res=true\");\n#endif\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tbool ret = (node.m_flags & NetNode.Flags.Junction) != NetNode.Flags.None;\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.GetDefaultPedestrianCrossingAllowed({segmentId}, {startNode}): Setting is configurable. ret={ret}, flags={node.m_flags}\");\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool IsPedestrianCrossingAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentFlags[segmentId].IsPedestrianCrossingAllowed(startNode);\n\t\t}\n\n\t\tpublic TernaryBool GetUturnAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentFlags[segmentId].GetUturnAllowed(startNode);\n\t\t}\n\n\t\tpublic TernaryBool GetNearTurnOnRedAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentFlags[segmentId].GetNearTurnOnRedAllowed(startNode);\n\t\t}\n\n\t\tpublic TernaryBool GetFarTurnOnRedAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentFlags[segmentId].GetFarTurnOnRedAllowed(startNode);\n\t\t}\n\n\t\tpublic TernaryBool GetTurnOnRedAllowed(bool near, ushort segmentId, bool startNode) {\n\t\t\treturn near\n\t\t\t\t? GetNearTurnOnRedAllowed(segmentId, startNode)\n\t\t\t\t: GetFarTurnOnRedAllowed(segmentId, startNode);\n\t\t}\n\n\t\tpublic TernaryBool GetLaneChangingAllowedWhenGoingStraight(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentFlags[segmentId].GetLaneChangingAllowedWhenGoingStraight(startNode);\n\t\t}\n\n\t\tpublic TernaryBool GetEnteringBlockedJunctionAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentFlags[segmentId].GetEnteringBlockedJunctionAllowed(startNode);\n\t\t}\n\n\t\tpublic TernaryBool GetPedestrianCrossingAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentFlags[segmentId].GetPedestrianCrossingAllowed(startNode);\n\t\t}\n\n\t\tpublic bool ToggleUturnAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn SetUturnAllowed(segmentId, startNode, !IsUturnAllowed(segmentId, startNode));\n\t\t}\n\n\t\tpublic bool ToggleNearTurnOnRedAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn ToggleTurnOnRedAllowed(true, segmentId, startNode);\n\t\t}\n\n\t\tpublic bool ToggleFarTurnOnRedAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn ToggleTurnOnRedAllowed(false, segmentId, startNode);\n\t\t}\n\n\t\tpublic bool ToggleTurnOnRedAllowed(bool near, ushort segmentId, bool startNode) {\n\t\t\treturn SetTurnOnRedAllowed(near, segmentId, startNode, !IsTurnOnRedAllowed(near, segmentId, startNode));\n\t\t}\n\n\t\tpublic bool ToggleLaneChangingAllowedWhenGoingStraight(ushort segmentId, bool startNode) {\n\t\t\treturn SetLaneChangingAllowedWhenGoingStraight(segmentId, startNode, !IsLaneChangingAllowedWhenGoingStraight(segmentId, startNode));\n\t\t}\n\n\t\tpublic bool ToggleEnteringBlockedJunctionAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn SetEnteringBlockedJunctionAllowed(segmentId, startNode, !IsEnteringBlockedJunctionAllowed(segmentId, startNode));\n\t\t}\n\n\t\tpublic bool TogglePedestrianCrossingAllowed(ushort segmentId, bool startNode) {\n\t\t\treturn SetPedestrianCrossingAllowed(segmentId, startNode, !IsPedestrianCrossingAllowed(segmentId, startNode));\n\t\t}\n\n\t\tprivate void SetSegmentEndFlags(ushort segmentId, bool startNode, SegmentEndFlags flags) {\n\t\t\tif (flags.uturnAllowed != TernaryBool.Undefined) {\n\t\t\t\tSetUturnAllowed(segmentId, startNode, flags.IsUturnAllowed());\n\t\t\t}\n\n\t\t\tif (flags.nearTurnOnRedAllowed != TernaryBool.Undefined) {\n\t\t\t\tSetNearTurnOnRedAllowed(segmentId, startNode, flags.IsNearTurnOnRedAllowed());\n\t\t\t}\n\n\t\t\tif (flags.nearTurnOnRedAllowed != TernaryBool.Undefined) {\n\t\t\t\tSetFarTurnOnRedAllowed(segmentId, startNode, flags.IsFarTurnOnRedAllowed());\n\t\t\t}\n\n\t\t\tif (flags.straightLaneChangingAllowed != TernaryBool.Undefined) {\n\t\t\t\tSetLaneChangingAllowedWhenGoingStraight(segmentId, startNode, flags.IsLaneChangingAllowedWhenGoingStraight());\n\t\t\t}\n\n\t\t\tif (flags.enterWhenBlockedAllowed != TernaryBool.Undefined) {\n\t\t\t\t//Log.Warning($\"JunctionRestrictionsManager.SetSegmentEndFlags({segmentId}, {startNode}, {flags}): flags.enterWhenBlockedAllowed is defined\");\n\t\t\t\tSetEnteringBlockedJunctionAllowed(segmentId, startNode, flags.IsEnteringBlockedJunctionAllowed());\n\t\t\t}\n\n\t\t\tif (flags.pedestrianCrossingAllowed != TernaryBool.Undefined) {\n\t\t\t\tSetPedestrianCrossingAllowed(segmentId, startNode, flags.IsPedestrianCrossingAllowed());\n\t\t\t}\n\t\t}\n\n\t\tpublic bool SetUturnAllowed(ushort segmentId, bool startNode, bool value) {\n\t\t\tif (!Services.NetService.IsSegmentValid(segmentId)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!value && Constants.ManagerFactory.LaneConnectionManager.HasUturnConnections(segmentId, startNode)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeo == null) {\n\t\t\t\tLog.Error($\"JunctionRestrictionsManager.SetUturnAllowed: No geometry information available for segment {segmentId}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentFlags[segmentId].SetUturnAllowed(startNode, value);\n\t\t\tOnSegmentChange(segmentId, startNode, segGeo, true);\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool SetNearTurnOnRedAllowed(ushort segmentId, bool startNode, bool value) {\n\t\t\treturn SetTurnOnRedAllowed(true, segmentId, startNode, value);\n\t\t}\n\n\t\tpublic bool SetFarTurnOnRedAllowed(ushort segmentId, bool startNode, bool value) {\n\t\t\treturn SetTurnOnRedAllowed(false, segmentId, startNode, value);\n\t\t}\n\n\t\tpublic bool SetTurnOnRedAllowed(bool near, ushort segmentId, bool startNode, bool value) {\n\t\t\tif (!Services.NetService.IsSegmentValid(segmentId)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeo == null) {\n\t\t\t\tLog.Error($\"JunctionRestrictionsManager.SetTurnOnRedAllowed: No geometry information available for segment {segmentId}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (near) {\n\t\t\t\tSegmentFlags[segmentId].SetNearTurnOnRedAllowed(startNode, value);\n\t\t\t} else {\n\t\t\t\tSegmentFlags[segmentId].SetFarTurnOnRedAllowed(startNode, value);\n\t\t\t}\n\t\t\tOnSegmentChange(segmentId, startNode, segGeo, true);\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool SetLaneChangingAllowedWhenGoingStraight(ushort segmentId, bool startNode, bool value) {\n\t\t\tif (!Services.NetService.IsSegmentValid(segmentId)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeo == null) {\n\t\t\t\tLog.Error($\"JunctionRestrictionsManager.SetUturnAllowed: No geometry information available for segment {segmentId}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentFlags[segmentId].SetLaneChangingAllowedWhenGoingStraight(startNode, value);\n\t\t\tOnSegmentChange(segmentId, startNode, segGeo, true);\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool SetEnteringBlockedJunctionAllowed(ushort segmentId, bool startNode, bool value) {\n\t\t\tif (!Services.NetService.IsSegmentValid(segmentId)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeo == null) {\n\t\t\t\tLog.Error($\"JunctionRestrictionsManager.SetUturnAllowed: No geometry information available for segment {segmentId}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentFlags[segmentId].SetEnteringBlockedJunctionAllowed(startNode, value);\n\t\t\t// recalculation not needed here because this is a simulation-time feature\n\t\t\tOnSegmentChange(segmentId, startNode, segGeo, false);\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool SetPedestrianCrossingAllowed(ushort segmentId, bool startNode, bool value) {\n\t\t\tif (!Services.NetService.IsSegmentValid(segmentId)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeo == null) {\n\t\t\t\tLog.Error($\"JunctionRestrictionsManager.SetPedestrianCrossingAllowed: No geometry information available for segment {segmentId}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentFlags[segmentId].SetPedestrianCrossingAllowed(startNode, value);\n\t\t\tOnSegmentChange(segmentId, startNode, segGeo, true);\n\t\t\treturn true;\n\t\t}\n\n\t\tprotected void OnSegmentChange(ushort segmentId, bool startNode, SegmentGeometry segGeo, bool requireRecalc) {\n\t\t\tushort nodeId = segGeo.GetNodeId(startNode);\n\n\t\t\tHandleValidSegment(segGeo);\n\n\t\t\tif (requireRecalc) {\n\t\t\t\tConstants.ManagerFactory.RoutingManager.RequestRecalculation(segmentId);\n\t\t\t\tif (Constants.ManagerFactory.OptionsManager.MayPublishSegmentChanges()) {\n\t\t\t\t\tServices.NetService.PublishSegmentChanges(segmentId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tfor (int i = 0; i < SegmentFlags.Length; ++i) {\n\t\t\t\tSegmentFlags[i].Reset(true);\n\t\t\t}\n\t\t\tfor (int i = 0; i < invalidSegmentFlags.Length; ++i) {\n\t\t\t\tinvalidSegmentFlags[i].Reset(true);\n\t\t\t}\n\t\t}\n\n\t\tpublic bool LoadData(List<Configuration.SegmentNodeConf> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading junction restrictions. {data.Count} elements\");\n\t\t\tforeach (Configuration.SegmentNodeConf segNodeConf in data) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!Services.NetService.IsSegmentValid(segNodeConf.segmentId)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.LoadData: Loading junction restrictions for segment {segNodeConf.segmentId}: startNodeFlags={segNodeConf.startNodeFlags} endNodeFlags={segNodeConf.endNodeFlags}\");\n\n\t\t\t\t\tif (segNodeConf.startNodeFlags != null) {\n\t\t\t\t\t\tSegmentEndGeometry startNodeSegGeo = SegmentGeometry.Get(segNodeConf.segmentId)?.GetEnd(true);\n\t\t\t\t\t\tif (startNodeSegGeo != null) {\n\t\t\t\t\t\t\tConfiguration.SegmentNodeFlags flags = segNodeConf.startNodeFlags;\n\n\t\t\t\t\t\t\tServices.NetService.ProcessNode(startNodeSegGeo.NodeId(), delegate (ushort nId, ref NetNode node) {\n\t\t\t\t\t\t\t\tif (flags.uturnAllowed != null && IsUturnAllowedConfigurable(segNodeConf.segmentId, true, ref node)) {\n\t\t\t\t\t\t\t\t\tSetUturnAllowed(segNodeConf.segmentId, true, (bool)flags.uturnAllowed);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (flags.turnOnRedAllowed != null && IsNearTurnOnRedAllowedConfigurable(segNodeConf.segmentId, true, ref node)) {\n\t\t\t\t\t\t\t\t\tSetNearTurnOnRedAllowed(segNodeConf.segmentId, true, (bool)flags.turnOnRedAllowed);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (flags.farTurnOnRedAllowed != null && IsNearTurnOnRedAllowedConfigurable(segNodeConf.segmentId, true, ref node)) {\n\t\t\t\t\t\t\t\t\tSetFarTurnOnRedAllowed(segNodeConf.segmentId, true, (bool)flags.farTurnOnRedAllowed);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (flags.straightLaneChangingAllowed != null && IsLaneChangingAllowedWhenGoingStraightConfigurable(segNodeConf.segmentId, true, ref node)) {\n\t\t\t\t\t\t\t\t\tSetLaneChangingAllowedWhenGoingStraight(segNodeConf.segmentId, true, (bool)flags.straightLaneChangingAllowed);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (flags.enterWhenBlockedAllowed != null && IsEnteringBlockedJunctionAllowedConfigurable(segNodeConf.segmentId, true, ref node)) {\n\t\t\t\t\t\t\t\t\tSetEnteringBlockedJunctionAllowed(segNodeConf.segmentId, true, (bool)flags.enterWhenBlockedAllowed);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (flags.pedestrianCrossingAllowed != null && IsPedestrianCrossingAllowedConfigurable(segNodeConf.segmentId, true, ref node)) {\n\t\t\t\t\t\t\t\t\tSetPedestrianCrossingAllowed(segNodeConf.segmentId, true, (bool)flags.pedestrianCrossingAllowed);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tLog.Warning($\"JunctionRestrictionsManager.LoadData(): Could not get segment end geometry for segment {segNodeConf.segmentId} @ start node\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (segNodeConf.endNodeFlags != null) {\n\t\t\t\t\t\tSegmentEndGeometry endNodeSegGeo = SegmentGeometry.Get(segNodeConf.segmentId)?.GetEnd(false);\n\t\t\t\t\t\tif (endNodeSegGeo != null) {\n\t\t\t\t\t\t\tConfiguration.SegmentNodeFlags flags = segNodeConf.endNodeFlags;\n\n\t\t\t\t\t\t\tServices.NetService.ProcessNode(endNodeSegGeo.NodeId(), delegate (ushort nId, ref NetNode node) {\n\t\t\t\t\t\t\t\tif (flags.uturnAllowed != null && IsUturnAllowedConfigurable(segNodeConf.segmentId, false, ref node)) {\n\t\t\t\t\t\t\t\t\tSetUturnAllowed(segNodeConf.segmentId, false, (bool)flags.uturnAllowed);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (flags.straightLaneChangingAllowed != null && IsLaneChangingAllowedWhenGoingStraightConfigurable(segNodeConf.segmentId, false, ref node)) {\n\t\t\t\t\t\t\t\t\tSetLaneChangingAllowedWhenGoingStraight(segNodeConf.segmentId, false, (bool)flags.straightLaneChangingAllowed);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (flags.enterWhenBlockedAllowed != null && IsEnteringBlockedJunctionAllowedConfigurable(segNodeConf.segmentId, false, ref node)) {\n\t\t\t\t\t\t\t\t\tSetEnteringBlockedJunctionAllowed(segNodeConf.segmentId, false, (bool)flags.enterWhenBlockedAllowed);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (flags.pedestrianCrossingAllowed != null && IsPedestrianCrossingAllowedConfigurable(segNodeConf.segmentId, false, ref node)) {\n\t\t\t\t\t\t\t\t\tSetPedestrianCrossingAllowed(segNodeConf.segmentId, false, (bool)flags.pedestrianCrossingAllowed);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (flags.turnOnRedAllowed != null) {\n\t\t\t\t\t\t\t\t\tSetNearTurnOnRedAllowed(segNodeConf.segmentId, false, (bool)flags.turnOnRedAllowed);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (flags.farTurnOnRedAllowed != null) {\n\t\t\t\t\t\t\t\t\tSetFarTurnOnRedAllowed(segNodeConf.segmentId, false, (bool)flags.farTurnOnRedAllowed);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tLog.Warning($\"JunctionRestrictionsManager.LoadData(): Could not get segment end geometry for segment {segNodeConf.segmentId} @ end node\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore, as it's probably corrupt save data. it'll be culled on next save\n\t\t\t\t\tLog.Warning($\"Error loading junction restrictions @ segment {segNodeConf.segmentId}: \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\tpublic List<Configuration.SegmentNodeConf> SaveData(ref bool success) {\n\t\t\tList<Configuration.SegmentNodeConf> ret = new List<Configuration.SegmentNodeConf>();\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tfor (uint segmentId = 0; segmentId < NetManager.MAX_SEGMENT_COUNT; segmentId++) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!Services.NetService.IsSegmentValid((ushort)segmentId)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tConfiguration.SegmentNodeFlags startNodeFlags = null;\n\t\t\t\t\tConfiguration.SegmentNodeFlags endNodeFlags = null;\n\n\t\t\t\t\tushort startNodeId = netManager.m_segments.m_buffer[segmentId].m_startNode;\n\t\t\t\t\tif (Services.NetService.IsNodeValid(startNodeId)) {\n\t\t\t\t\t\tSegmentEndFlags endFlags = SegmentFlags[segmentId].startNodeFlags;\n\n\t\t\t\t\t\tif (!endFlags.IsDefault()) {\n\t\t\t\t\t\t\tstartNodeFlags = new Configuration.SegmentNodeFlags();\n\n\t\t\t\t\t\t\tstartNodeFlags.uturnAllowed = TernaryBoolUtil.ToOptBool(GetUturnAllowed((ushort)segmentId, true));\n\t\t\t\t\t\t\tstartNodeFlags.turnOnRedAllowed = TernaryBoolUtil.ToOptBool(GetNearTurnOnRedAllowed((ushort)segmentId, true));\n\t\t\t\t\t\t\tstartNodeFlags.farTurnOnRedAllowed = TernaryBoolUtil.ToOptBool(GetFarTurnOnRedAllowed((ushort)segmentId, true));\n\t\t\t\t\t\t\tstartNodeFlags.straightLaneChangingAllowed = TernaryBoolUtil.ToOptBool(GetLaneChangingAllowedWhenGoingStraight((ushort)segmentId, true));\n\t\t\t\t\t\t\tstartNodeFlags.enterWhenBlockedAllowed = TernaryBoolUtil.ToOptBool(GetEnteringBlockedJunctionAllowed((ushort)segmentId, true));\n\t\t\t\t\t\t\tstartNodeFlags.pedestrianCrossingAllowed = TernaryBoolUtil.ToOptBool(GetPedestrianCrossingAllowed((ushort)segmentId, true));\n\n\t\t\t\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.SaveData: Saving start node junction restrictions for segment {segmentId}: {startNodeFlags}\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tushort endNodeId = netManager.m_segments.m_buffer[segmentId].m_endNode;\n\t\t\t\t\tif (Services.NetService.IsNodeValid(endNodeId)) {\n\t\t\t\t\t\tSegmentEndFlags endFlags = SegmentFlags[segmentId].endNodeFlags;\n\n\t\t\t\t\t\tif (!endFlags.IsDefault()) {\n\t\t\t\t\t\t\tendNodeFlags = new Configuration.SegmentNodeFlags();\n\n\t\t\t\t\t\t\tendNodeFlags.uturnAllowed = TernaryBoolUtil.ToOptBool(GetUturnAllowed((ushort)segmentId, false));\n\t\t\t\t\t\t\tendNodeFlags.turnOnRedAllowed = TernaryBoolUtil.ToOptBool(GetNearTurnOnRedAllowed((ushort)segmentId, false));\n\t\t\t\t\t\t\tendNodeFlags.farTurnOnRedAllowed = TernaryBoolUtil.ToOptBool(GetFarTurnOnRedAllowed((ushort)segmentId, false));\n\t\t\t\t\t\t\tendNodeFlags.straightLaneChangingAllowed = TernaryBoolUtil.ToOptBool(GetLaneChangingAllowedWhenGoingStraight((ushort)segmentId, false));\n\t\t\t\t\t\t\tendNodeFlags.enterWhenBlockedAllowed = TernaryBoolUtil.ToOptBool(GetEnteringBlockedJunctionAllowed((ushort)segmentId, false));\n\t\t\t\t\t\t\tendNodeFlags.pedestrianCrossingAllowed = TernaryBoolUtil.ToOptBool(GetPedestrianCrossingAllowed((ushort)segmentId, false));\n\n\t\t\t\t\t\t\tLog._Debug($\"JunctionRestrictionsManager.SaveData: Saving end node junction restrictions for segment {segmentId}: {endNodeFlags}\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (startNodeFlags == null && endNodeFlags == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tConfiguration.SegmentNodeConf conf = new Configuration.SegmentNodeConf((ushort)segmentId);\n\n\t\t\t\t\tconf.startNodeFlags = startNodeFlags;\n\t\t\t\t\tconf.endNodeFlags = endNodeFlags;\n\n\t\t\t\t\tLog._Debug($\"Saving segment-at-node flags for seg. {segmentId}\");\n\t\t\t\t\tret.Add(conf);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Exception occurred while saving segment node flags @ {segmentId}: {e.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/LaneArrowManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Util;\nusing TrafficManager.State;\nusing ColossalFramework;\nusing TrafficManager.Geometry;\nusing static TrafficManager.State.Flags;\nusing TrafficManager.Traffic;\nusing CSUtil.Commons;\nusing TrafficManager.Geometry.Impl;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class LaneArrowManager : AbstractGeometryObservingManager, ICustomDataManager<List<Configuration.LaneArrowData>>, ICustomDataManager<string>, ILaneArrowManager {\n\t\tpublic const NetInfo.LaneType LANE_TYPES = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;\n\t\tpublic const VehicleInfo.VehicleType VEHICLE_TYPES = VehicleInfo.VehicleType.Car;\n\t\tpublic const ExtVehicleType EXT_VEHICLE_TYPES = ExtVehicleType.RoadVehicle &~ ExtVehicleType.Emergency;\n\n\t\tpublic static readonly LaneArrowManager Instance = new LaneArrowManager();\n\t\t\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"- Not implemented -\");\n\t\t\t// TODO implement\n\t\t}\n\n\t\tpublic LaneArrows GetFinalLaneArrows(uint laneId) {\n\t\t\treturn Flags.getFinalLaneArrowFlags(laneId, true);\n\t\t}\n\n\t\tpublic bool SetLaneArrows(uint laneId, LaneArrows flags, bool overrideHighwayArrows = false) {\n\t\t\tif (Flags.setLaneArrowFlags(laneId, flags, overrideHighwayArrows)) {\n\t\t\t\tOnLaneChange(laneId);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic bool ToggleLaneArrows(uint laneId, bool startNode, LaneArrows flags, out LaneArrowChangeResult res) {\n\t\t\tif (Flags.toggleLaneArrowFlags(laneId, startNode, flags, out res)) {\n\t\t\t\tOnLaneChange(laneId);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tprotected void OnLaneChange(uint laneId) {\n\t\t\tServices.NetService.ProcessLane(laneId, delegate (uint lId, ref NetLane lane) {\n\t\t\t\tRoutingManager.Instance.RequestRecalculation(lane.m_segment);\n\n\t\t\t\tif (OptionsManager.Instance.MayPublishSegmentChanges()) {\n\t\t\t\t\tServices.NetService.PublishSegmentChanges(lane.m_segment);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\tprotected override void HandleInvalidSegment(SegmentGeometry geometry) {\n\t\t\tFlags.resetSegmentArrowFlags(geometry.SegmentId);\n\t\t}\n\n\t\tprotected override void HandleValidSegment(SegmentGeometry geometry) {\n\t\t\t\n\t\t}\n\n\t\tpublic void ApplyFlags() {\n\t\t\tfor (uint laneId = 0; laneId < NetManager.MAX_LANE_COUNT; ++laneId) {\n\t\t\t\tFlags.applyLaneArrowFlags(laneId);\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnBeforeSaveData() {\n\t\t\tbase.OnBeforeSaveData();\n\t\t\tApplyFlags();\n\t\t}\n\n\t\tpublic override void OnAfterLoadData() {\n\t\t\tbase.OnAfterLoadData();\n\t\t\tFlags.clearHighwayLaneArrows();\n\t\t\tApplyFlags();\n\t\t}\n\n\t\t[Obsolete]\n\t\tpublic bool LoadData(string data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading lane arrow data (old method)\");\n#if DEBUG\n\t\t\tLog._Debug($\"LaneFlags: {data}\");\n#endif\n\t\t\tvar lanes = data.Split(',');\n\n\t\t\tif (lanes.Length > 1) {\n\t\t\t\tforeach (var split in lanes.Select(lane => lane.Split(':')).Where(split => split.Length > 1)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tLog._Debug($\"Split Data: {split[0]} , {split[1]}\");\n\t\t\t\t\t\tvar laneId = Convert.ToUInt32(split[0]);\n\t\t\t\t\t\tuint flags = Convert.ToUInt32(split[1]);\n\n\t\t\t\t\t\tif (!Services.NetService.IsLaneValid(laneId))\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\tif (flags > ushort.MaxValue)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\n\t\t\t\t\t\tuint laneArrowFlags = flags & Flags.lfr;\n\t\t\t\t\t\tuint origFlags = (Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags & Flags.lfr);\n#if DEBUG\n\t\t\t\t\t\tLog._Debug(\"Setting flags for lane \" + laneId + \" to \" + flags + \" (\" + ((Flags.LaneArrows)(laneArrowFlags)).ToString() + \")\");\n\t\t\t\t\t\tif ((origFlags | laneArrowFlags) == origFlags) { // only load if setting differs from default\n\t\t\t\t\t\t\tLog._Debug(\"Flags for lane \" + laneId + \" are original (\" + ((NetLane.Flags)(origFlags)).ToString() + \")\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tSetLaneArrows(laneId, (Flags.LaneArrows)laneArrowFlags);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLog.Error($\"Error loading Lane Split data. Length: {split.Length} value: {split}\\nError: {e.ToString()}\");\n\t\t\t\t\t\tsuccess = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\t[Obsolete]\n\t\tstring ICustomDataManager<string>.SaveData(ref bool success) {\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic bool LoadData(List<Configuration.LaneArrowData> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading lane arrow data (new method)\");\n\n\t\t\tforeach (var laneArrowData in data) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!Services.NetService.IsLaneValid(laneArrowData.laneId))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tuint laneArrowFlags = laneArrowData.arrows & Flags.lfr;\n\t\t\t\t\tSetLaneArrows(laneArrowData.laneId, (Flags.LaneArrows)laneArrowFlags);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Error loading lane arrow data for lane {laneArrowData.laneId}, arrows={laneArrowData.arrows}: {e.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\tpublic List<Configuration.LaneArrowData> SaveData(ref bool success) {\n\t\t\tList<Configuration.LaneArrowData> ret = new List<Configuration.LaneArrowData>();\n\t\t\tfor (uint i = 0; i < Singleton<NetManager>.instance.m_lanes.m_buffer.Length; i++) {\n\t\t\t\ttry {\n\t\t\t\t\tFlags.LaneArrows? laneArrows = Flags.getLaneArrowFlags(i);\n\n\t\t\t\t\tif (laneArrows == null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tuint laneArrowInt = (uint)laneArrows;\n\t\t\t\t\tLog._Debug($\"Saving lane arrows for lane {i}, setting to {laneArrows.ToString()} ({laneArrowInt})\");\n\t\t\t\t\tret.Add(new Configuration.LaneArrowData(i, laneArrowInt));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Exception occurred while saving lane arrows @ {i}: {e.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/LaneConnectionManager.cs",
    "content": "﻿using ColossalFramework;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Threading;\nusing TrafficManager.Geometry;\nusing TrafficManager.Traffic;\nusing TrafficManager.State;\nusing TrafficManager.Util;\nusing UnityEngine;\nusing CSUtil.Commons;\nusing TrafficManager.Geometry.Impl;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class LaneConnectionManager : AbstractGeometryObservingManager, ICustomDataManager<List<Configuration.LaneConnection>>, ILaneConnectionManager {\n\t\tpublic const NetInfo.LaneType LANE_TYPES = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;\n\t\tpublic const VehicleInfo.VehicleType VEHICLE_TYPES = VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Monorail;\n\t\tpublic const ExtVehicleType EXT_VEHICLE_TYPES = ExtVehicleType.RoadVehicle | ExtVehicleType.Tram | ExtVehicleType.RailVehicle;\n\n\t\tpublic static LaneConnectionManager Instance { get; private set; } = null;\n\t\t\n\t\tstatic LaneConnectionManager() {\n\t\t\tInstance = new LaneConnectionManager();\n\t\t}\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"- Not implemented -\");\n\t\t\t// TODO implement\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Checks if traffic may flow from source lane to target lane according to setup lane connections\n\t\t/// </summary>\n\t\t/// <param name=\"sourceLaneId\"></param>\n\t\t/// <param name=\"targetLaneId\"></param>\n\t\t/// <param name=\"sourceStartNode\">(optional) check at start node of source lane?</param>\n\t\t/// <returns></returns>\n\t\tpublic bool AreLanesConnected(uint sourceLaneId, uint targetLaneId, bool sourceStartNode) {\n\t\t\tif (! Options.laneConnectorEnabled) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (targetLaneId == 0 || Flags.laneConnections[sourceLaneId] == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\tint nodeArrayIndex = sourceStartNode ? 0 : 1;\n\n\t\t\tuint[] connectedLanes = Flags.laneConnections[sourceLaneId][nodeArrayIndex];\n\t\t\tif (connectedLanes == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool ret = false;\n\t\t\tforeach (uint laneId in connectedLanes)\n\t\t\t\tif (laneId == targetLaneId) {\n\t\t\t\t\tret = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if the given lane has outgoing connections\n\t\t/// </summary>\n\t\t/// <param name=\"sourceLaneId\"></param>\n\t\t/// <returns></returns>\n\t\tpublic bool HasConnections(uint sourceLaneId, bool startNode) {\n\t\t\tif (!Options.laneConnectorEnabled) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tint nodeArrayIndex = startNode ? 0 : 1;\n\n\t\t\tbool ret = Flags.laneConnections[sourceLaneId] != null && Flags.laneConnections[sourceLaneId][nodeArrayIndex] != null;\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if there exist custom lane connections at the specified node\n\t\t/// </summary>\n\t\t/// <param name=\"nodeId\"></param>\n\t\tpublic bool HasNodeConnections(ushort nodeId) {\n\t\t\tif (!Options.laneConnectorEnabled) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool ret = false;\n\t\t\tServices.NetService.IterateNodeSegments(nodeId, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\tServices.NetService.IterateSegmentLanes(segmentId, delegate (uint laneId, ref NetLane lane, NetInfo.Lane laneInfo, ushort segId, ref NetSegment seg, byte laneIndex) {\n\t\t\t\t\tif (HasConnections(laneId, seg.m_startNode == nodeId)) {\n\t\t\t\t\t\tret = true;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t\treturn !ret;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool HasUturnConnections(ushort segmentId, bool startNode) {\n\t\t\tif (!Options.laneConnectorEnabled) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tint nodeArrayIndex = startNode ? 0 : 1;\n\n\t\t\tuint sourceLaneId = netManager.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\twhile (sourceLaneId != 0) {\n\t\t\t\tuint[] targetLaneIds = GetLaneConnections(sourceLaneId, startNode);\n\n\t\t\t\tif (targetLaneIds != null) {\n\t\t\t\t\tforeach (uint targetLaneId in targetLaneIds) {\n\t\t\t\t\t\tif (netManager.m_lanes.m_buffer[targetLaneId].m_segment == segmentId) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsourceLaneId = netManager.m_lanes.m_buffer[sourceLaneId].m_nextLane;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tinternal int CountConnections(uint sourceLaneId, bool startNode) {\n\t\t\tif (!Options.laneConnectorEnabled) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tif (Flags.laneConnections[sourceLaneId] == null)\n\t\t\t\treturn 0;\n\t\t\tint nodeArrayIndex = startNode ? 0 : 1;\n\t\t\tif (Flags.laneConnections[sourceLaneId][nodeArrayIndex] == null)\n\t\t\t\treturn 0;\n\t\t\t\n\t\t\treturn Flags.laneConnections[sourceLaneId][nodeArrayIndex].Length;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Gets all lane connections for the given lane\n\t\t/// </summary>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <returns></returns>\n\t\tinternal uint[] GetLaneConnections(uint laneId, bool startNode) {\n\t\t\tif (!Options.laneConnectorEnabled) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif (Flags.laneConnections[laneId] == null)\n\t\t\t\treturn null;\n\n\t\t\tint nodeArrayIndex = startNode ? 0 : 1;\n\t\t\treturn Flags.laneConnections[laneId][nodeArrayIndex];\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes a lane connection between two lanes\n\t\t/// </summary>\n\t\t/// <param name=\"laneId1\"></param>\n\t\t/// <param name=\"laneId2\"></param>\n\t\t/// <param name=\"startNode1\"></param>\n\t\t/// <returns></returns>\n\t\tinternal bool RemoveLaneConnection(uint laneId1, uint laneId2, bool startNode1) {\n#if DEBUGCONN\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[23];\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"LaneConnectionManager.RemoveLaneConnection({laneId1}, {laneId2}, {startNode1}) called.\");\n#endif\n\t\t\tbool ret = Flags.RemoveLaneConnection(laneId1, laneId2, startNode1);\n\n#if DEBUGCONN\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"LaneConnectionManager.RemoveLaneConnection({laneId1}, {laneId2}, {startNode1}): ret={ret}\");\n#endif\n\t\t\tif (ret) {\n\t\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\t\tushort segmentId1 = netManager.m_lanes.m_buffer[laneId1].m_segment;\n\t\t\t\tushort segmentId2 = netManager.m_lanes.m_buffer[laneId2].m_segment;\n\n\t\t\t\tushort commonNodeId;\n\t\t\t\tbool startNode2;\n\t\t\t\tGetCommonNodeId(laneId1, laneId2, startNode1, out commonNodeId, out startNode2);\n\n\t\t\t\tRecalculateLaneArrows(laneId1, commonNodeId, startNode1);\n\t\t\t\tRecalculateLaneArrows(laneId2, commonNodeId, startNode2);\n\n\t\t\t\tRoutingManager.Instance.RequestRecalculation(segmentId1, false);\n\t\t\t\tRoutingManager.Instance.RequestRecalculation(segmentId2, false);\n\n\t\t\t\tif (OptionsManager.Instance.MayPublishSegmentChanges()) {\n\t\t\t\t\tServices.NetService.PublishSegmentChanges(segmentId1);\n\t\t\t\t\tServices.NetService.PublishSegmentChanges(segmentId2);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes all lane connections at the specified node\n\t\t/// </summary>\n\t\t/// <param name=\"nodeId\"></param>\n\t\tinternal void RemoveLaneConnectionsFromNode(ushort nodeId) {\n#if DEBUGCONN\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[23];\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"LaneConnectionManager.RemoveLaneConnectionsFromNode({nodeId}) called.\");\n#endif\n\t\t\tServices.NetService.IterateNodeSegments(nodeId, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\tRemoveLaneConnectionsFromSegment(segmentId, segment.m_startNode == nodeId);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes all lane connections at the specified segment end\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"startNode\"></param>\n\t\tinternal void RemoveLaneConnectionsFromSegment(ushort segmentId, bool startNode, bool recalcAndPublish=true) {\n#if DEBUGCONN\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[23];\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"LaneConnectionManager.RemoveLaneConnectionsFromSegment({segmentId}, {startNode}) called.\");\n#endif\n\n\t\t\tServices.NetService.IterateSegmentLanes(segmentId, delegate (uint laneId, ref NetLane lane, NetInfo.Lane laneInfo, ushort segId, ref NetSegment segment, byte laneIndex) {\n#if DEBUGCONN\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RemoveLaneConnectionsFromSegment: Removing lane connections from segment {segmentId}, lane {laneId}.\");\n#endif\n\t\t\t\tRemoveLaneConnections(laneId, startNode, false);\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tif (recalcAndPublish) {\n\t\t\t\tRoutingManager.Instance.RequestRecalculation(segmentId);\n\n\t\t\t\tif (OptionsManager.Instance.MayPublishSegmentChanges()) {\n\t\t\t\t\tServices.NetService.PublishSegmentChanges(segmentId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes all lane connections from the specified lane\n\t\t/// </summary>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <param name=\"startNode\"></param>\n\t\tinternal void RemoveLaneConnections(uint laneId, bool startNode, bool recalcAndPublish=true) {\n#if DEBUGCONN\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[23];\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"LaneConnectionManager.RemoveLaneConnections({laneId}, {startNode}) called.\");\n#endif\n\n\t\t\tif (Flags.laneConnections[laneId] == null)\n\t\t\t\treturn;\n\n\t\t\tint nodeArrayIndex = startNode ? 0 : 1;\n\n\t\t\tif (Flags.laneConnections[laneId][nodeArrayIndex] == null)\n\t\t\t\treturn;\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\t/*for (int i = 0; i < Flags.laneConnections[laneId][nodeArrayIndex].Length; ++i) {\n\t\t\t\tuint otherLaneId = Flags.laneConnections[laneId][nodeArrayIndex][i];\n\t\t\t\tif (Flags.laneConnections[otherLaneId] != null) {\n\t\t\t\t\tif ((Flags.laneConnections[otherLaneId][0] != null && Flags.laneConnections[otherLaneId][0].Length == 1 && Flags.laneConnections[otherLaneId][0][0] == laneId && Flags.laneConnections[otherLaneId][1] == null) ||\n\t\t\t\t\t\tFlags.laneConnections[otherLaneId][1] != null && Flags.laneConnections[otherLaneId][1].Length == 1 && Flags.laneConnections[otherLaneId][1][0] == laneId && Flags.laneConnections[otherLaneId][0] == null) {\n\n\t\t\t\t\t\tushort otherSegmentId = netManager.m_lanes.m_buffer[otherLaneId].m_segment;\n\t\t\t\t\t\tUnsubscribeFromSegmentGeometry(otherSegmentId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}*/\n\n\t\t\tFlags.RemoveLaneConnections(laneId, startNode);\n\n\t\t\tServices.NetService.ProcessLane(laneId, delegate (uint lId, ref NetLane lane) {\n\t\t\t\tif (recalcAndPublish) {\n\t\t\t\t\tRoutingManager.Instance.RequestRecalculation(lane.m_segment);\n\n\t\t\t\t\tif (OptionsManager.Instance.MayPublishSegmentChanges()) {\n\t\t\t\t\t\tServices.NetService.PublishSegmentChanges(lane.m_segment);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds a lane connection between two lanes\n\t\t/// </summary>\n\t\t/// <param name=\"sourceLaneId\"></param>\n\t\t/// <param name=\"targetLaneId\"></param>\n\t\t/// <param name=\"sourceStartNode\"></param>\n\t\t/// <returns></returns>\n\t\tinternal bool AddLaneConnection(uint sourceLaneId, uint targetLaneId, bool sourceStartNode) {\n\t\t\tif (sourceLaneId == targetLaneId) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool ret = Flags.AddLaneConnection(sourceLaneId, targetLaneId, sourceStartNode);\n\n#if DEBUGCONN\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[23];\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"LaneConnectionManager.AddLaneConnection({sourceLaneId}, {targetLaneId}, {sourceStartNode}): ret={ret}\");\n#endif\n\t\t\tif (ret) {\n\t\t\t\tushort commonNodeId;\n\t\t\t\tbool targetStartNode;\n\t\t\t\tGetCommonNodeId(sourceLaneId, targetLaneId, sourceStartNode, out commonNodeId, out targetStartNode);\n\t\t\t\tRecalculateLaneArrows(sourceLaneId, commonNodeId, sourceStartNode);\n\t\t\t\tRecalculateLaneArrows(targetLaneId, commonNodeId, targetStartNode);\n\n\t\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\t\tushort sourceSegmentId = netManager.m_lanes.m_buffer[sourceLaneId].m_segment;\n\t\t\t\tushort targetSegmentId = netManager.m_lanes.m_buffer[targetLaneId].m_segment;\n\n\t\t\t\tif (sourceSegmentId == targetSegmentId) {\n\t\t\t\t\tJunctionRestrictionsManager.Instance.SetUturnAllowed(sourceSegmentId, sourceStartNode, true);\n\t\t\t\t}\n\n\t\t\t\tRoutingManager.Instance.RequestRecalculation(sourceSegmentId, false);\n\t\t\t\tRoutingManager.Instance.RequestRecalculation(targetSegmentId, false);\n\n\t\t\t\tif (OptionsManager.Instance.MayPublishSegmentChanges()) {\n\t\t\t\t\tServices.NetService.PublishSegmentChanges(sourceSegmentId);\n\t\t\t\t\tServices.NetService.PublishSegmentChanges(targetSegmentId);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tprotected override void HandleInvalidSegment(SegmentGeometry geometry) {\n#if DEBUGCONN\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[23];\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"LaneConnectionManager.HandleInvalidSegment({geometry.SegmentId}): Segment has become invalid. Removing lane connections.\");\n#endif\n\t\t\tRemoveLaneConnectionsFromSegment(geometry.SegmentId, false, false);\n\t\t\tRemoveLaneConnectionsFromSegment(geometry.SegmentId, true);\n\t\t}\n\n\t\tprotected override void HandleValidSegment(SegmentGeometry geometry) {\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Given two lane ids and node of the first lane, determines the node id to which both lanes are connected to\n\t\t/// </summary>\n\t\t/// <param name=\"laneId1\"></param>\n\t\t/// <param name=\"laneId2\"></param>\n\t\t/// <returns></returns>\n\t\tinternal void GetCommonNodeId(uint laneId1, uint laneId2, bool startNode1, out ushort commonNodeId, out bool startNode2) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tushort segmentId1 = netManager.m_lanes.m_buffer[laneId1].m_segment;\n\t\t\tushort segmentId2 = netManager.m_lanes.m_buffer[laneId2].m_segment;\n\n\t\t\tushort nodeId2Start = netManager.m_segments.m_buffer[segmentId2].m_startNode;\n\t\t\tushort nodeId2End = netManager.m_segments.m_buffer[segmentId2].m_endNode;\n\n\t\t\tushort nodeId1 = startNode1 ? netManager.m_segments.m_buffer[segmentId1].m_startNode : netManager.m_segments.m_buffer[segmentId1].m_endNode;\n\n\t\t\tstartNode2 = (nodeId1 == nodeId2Start);\n\t\t\tif (!startNode2 && nodeId1 != nodeId2End)\n\t\t\t\tcommonNodeId = 0;\n\t\t\telse\n\t\t\t\tcommonNodeId = nodeId1;\n\t\t}\n\n\t\tinternal bool GetLaneEndPoint(ushort segmentId, bool startNode, byte laneIndex, uint? laneId, NetInfo.Lane laneInfo, out bool outgoing, out bool incoming, out Vector3? pos) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tpos = null;\n\t\t\toutgoing = false;\n\t\t\tincoming = false;\n\n\t\t\tif ((netManager.m_segments.m_buffer[segmentId].m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created)\n\t\t\t\treturn false;\n\n\t\t\tif (laneId == null) {\n\t\t\t\tlaneId = FindLaneId(segmentId, laneIndex);\n\t\t\t\tif (laneId == null)\n\t\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif ((netManager.m_lanes.m_buffer[(uint)laneId].m_flags & ((ushort)NetLane.Flags.Created | (ushort)NetLane.Flags.Deleted)) != (ushort)NetLane.Flags.Created)\n\t\t\t\treturn false;\n\n\t\t\tif (laneInfo == null) {\n\t\t\t\tif (laneIndex < netManager.m_segments.m_buffer[segmentId].Info.m_lanes.Length)\n\t\t\t\t\tlaneInfo = netManager.m_segments.m_buffer[segmentId].Info.m_lanes[laneIndex];\n\t\t\t\telse\n\t\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tNetInfo.Direction laneDir = ((NetManager.instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? laneInfo.m_finalDirection : NetInfo.InvertDirection(laneInfo.m_finalDirection);\n\n\t\t\tif (startNode) {\n\t\t\t\tif ((laneDir & NetInfo.Direction.Backward) != NetInfo.Direction.None)\n\t\t\t\t\toutgoing = true;\n\t\t\t\tif ((laneDir & NetInfo.Direction.Forward) != NetInfo.Direction.None)\n\t\t\t\t\tincoming = true;\n\t\t\t\tpos = NetManager.instance.m_lanes.m_buffer[(uint)laneId].m_bezier.a;\n\t\t\t} else {\n\t\t\t\tif ((laneDir & NetInfo.Direction.Forward) != NetInfo.Direction.None)\n\t\t\t\t\toutgoing = true;\n\t\t\t\tif ((laneDir & NetInfo.Direction.Backward) != NetInfo.Direction.None)\n\t\t\t\t\tincoming = true;\n\t\t\t\tpos = NetManager.instance.m_lanes.m_buffer[(uint)laneId].m_bezier.d;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate uint? FindLaneId(ushort segmentId, byte laneIndex) {\n\t\t\tNetInfo.Lane[] lanes = NetManager.instance.m_segments.m_buffer[segmentId].Info.m_lanes;\n\t\t\tuint laneId = NetManager.instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tfor (byte i = 0; i < lanes.Length && laneId != 0; i++) {\n\t\t\t\tif (i == laneIndex)\n\t\t\t\t\treturn laneId;\n\n\t\t\t\tlaneId = NetManager.instance.m_lanes.m_buffer[laneId].m_nextLane;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Recalculates lane arrows based on present lane connections.\n\t\t/// </summary>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <param name=\"nodeId\"></param>\n\t\tprivate void RecalculateLaneArrows(uint laneId, ushort nodeId, bool startNode) {\n#if DEBUGCONN\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[23];\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}) called\");\n#endif\n\t\t\tif (!Options.laneConnectorEnabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!Flags.mayHaveLaneArrows(laneId, startNode)) {\n#if DEBUGCONN\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): lane {laneId}, startNode? {startNode} must not have lane arrows\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!HasConnections(laneId, startNode)) {\n#if DEBUGCONN\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): lane {laneId} does not have outgoing connections\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (nodeId == 0) {\n#if DEBUGCONN\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): invalid node\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tFlags.LaneArrows arrows = Flags.LaneArrows.None;\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tushort segmentId = netManager.m_lanes.m_buffer[laneId].m_segment;\n\n\t\t\tif (segmentId == 0) {\n#if DEBUGCONN\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): invalid segment\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n#if DEBUGCONN\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): startNode? {startNode}\");\n#endif\n\n\t\t\tNodeGeometry nodeGeo = NodeGeometry.Get(nodeId);\n\t\t\tif (!nodeGeo.IsValid()) {\n#if DEBUGCONN\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): invalid node geometry\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tSegmentGeometry segmentGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segmentGeo == null) {\n#if DEBUGCONN\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): invalid segment geometry\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tushort[] connectedSegmentIds = segmentGeo.GetConnectedSegments(startNode);\n\t\t\tif (connectedSegmentIds == null) {\n#if DEBUGCONN\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): connectedSegmentIds is null\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tushort[] allSegmentIds = new ushort[connectedSegmentIds.Length + 1];\n\t\t\tallSegmentIds[0] = segmentId;\n\t\t\tArray.Copy(connectedSegmentIds, 0, allSegmentIds, 1, connectedSegmentIds.Length);\n\n\t\t\tforeach (ushort otherSegmentId in allSegmentIds) {\n\t\t\t\tif (otherSegmentId == 0)\n\t\t\t\t\tcontinue;\n\t\t\t\tArrowDirection dir = segmentGeo.GetDirection(otherSegmentId, startNode);\n\n#if DEBUGCONN\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {otherSegmentId}. dir={dir}\");\n#endif\n\n\t\t\t\t// check if arrow has already been set for this direction\n\t\t\t\tswitch (dir) {\n\t\t\t\t\tcase ArrowDirection.Turn:\n\t\t\t\t\t\tif (Constants.ServiceFactory.SimulationService.LeftHandDrive) {\n\t\t\t\t\t\t\tif ((arrows & Flags.LaneArrows.Right) != Flags.LaneArrows.None)\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif ((arrows & Flags.LaneArrows.Left) != Flags.LaneArrows.None)\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ArrowDirection.Forward:\n\t\t\t\t\t\tif ((arrows & Flags.LaneArrows.Forward) != Flags.LaneArrows.None)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ArrowDirection.Left:\n\t\t\t\t\t\tif ((arrows & Flags.LaneArrows.Left) != Flags.LaneArrows.None)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ArrowDirection.Right:\n\t\t\t\t\t\tif ((arrows & Flags.LaneArrows.Right) != Flags.LaneArrows.None)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n#if DEBUGCONN\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {otherSegmentId}: need to determine arrows\");\n#endif\n\n\t\t\t\tbool addArrow = false;\n\n\t\t\t\tuint curLaneId = netManager.m_segments.m_buffer[otherSegmentId].m_lanes;\n\t\t\t\twhile (curLaneId != 0) {\n#if DEBUGCONN\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {otherSegmentId}: checking lane {curLaneId}\");\n#endif\n\t\t\t\t\tif (AreLanesConnected(laneId, curLaneId, startNode)) {\n#if DEBUGCONN\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {otherSegmentId}: checking lane {curLaneId}: lanes are connected\");\n#endif\n\t\t\t\t\t\taddArrow = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t}\n\n#if DEBUGCONN\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {otherSegmentId}: finished processing lanes. addArrow={addArrow} arrows (before)={arrows}\");\n#endif\n\t\t\t\tif (addArrow) {\n\t\t\t\t\tswitch (dir) {\n\t\t\t\t\t\tcase ArrowDirection.Turn:\n\t\t\t\t\t\t\tif (Constants.ServiceFactory.SimulationService.LeftHandDrive) {\n\t\t\t\t\t\t\t\tarrows |= Flags.LaneArrows.Right;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tarrows |= Flags.LaneArrows.Left;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase ArrowDirection.Forward:\n\t\t\t\t\t\t\tarrows |= Flags.LaneArrows.Forward;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase ArrowDirection.Left:\n\t\t\t\t\t\t\tarrows |= Flags.LaneArrows.Left;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase ArrowDirection.Right:\n\t\t\t\t\t\t\tarrows |= Flags.LaneArrows.Right;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n#if DEBUGCONN\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {otherSegmentId}: arrows={arrows}\");\n#endif\n\t\t\t\t}\n\t\t\t}\n\n#if DEBUGCONN\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): setting lane arrows to {arrows}\");\n#endif\n\n\t\t\tLaneArrowManager.Instance.SetLaneArrows(laneId, arrows, true);\n\t\t}\n\n\t\tpublic bool LoadData(List<Configuration.LaneConnection> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading {data.Count} lane connections\");\n\t\t\tforeach (Configuration.LaneConnection conn in data) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!Services.NetService.IsLaneValid(conn.lowerLaneId))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (!Services.NetService.IsLaneValid(conn.higherLaneId))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (conn.lowerLaneId == conn.higherLaneId) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tLog._Debug($\"Loading lane connection: lane {conn.lowerLaneId} -> {conn.higherLaneId}\");\n\t\t\t\t\tAddLaneConnection(conn.lowerLaneId, conn.higherLaneId, conn.lowerStartNode);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore, as it's probably corrupt save data. it'll be culled on next save\n\t\t\t\t\tLog.Error(\"Error loading data from lane connection: \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\tpublic List<Configuration.LaneConnection> SaveData(ref bool success) {\n\t\t\tList<Configuration.LaneConnection> ret = new List<Configuration.LaneConnection>();\n\t\t\tfor (uint i = 0; i < Singleton<NetManager>.instance.m_lanes.m_buffer.Length; i++) {\n\t\t\t\ttry {\n\t\t\t\t\tif (Flags.laneConnections[i] == null)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\n\t\t\t\t\tfor (int nodeArrayIndex = 0; nodeArrayIndex <= 1; ++nodeArrayIndex) {\n\t\t\t\t\t\tuint[] connectedLaneIds = Flags.laneConnections[i][nodeArrayIndex];\n\t\t\t\t\t\tbool startNode = nodeArrayIndex == 0;\n\t\t\t\t\t\tif (connectedLaneIds != null) {\n\t\t\t\t\t\t\tforeach (uint otherHigherLaneId in connectedLaneIds) {\n\t\t\t\t\t\t\t\tif (otherHigherLaneId <= i)\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\tif (!Services.NetService.IsLaneValid(otherHigherLaneId))\n\t\t\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\t\t\tLog._Debug($\"Saving lane connection: lane {i} -> {otherHigherLaneId}\");\n\t\t\t\t\t\t\t\tret.Add(new Configuration.LaneConnection(i, (uint)otherHigherLaneId, startNode));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Exception occurred while saving lane data @ {i}: {e.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/ManagerFactory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Manager;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class ManagerFactory : IManagerFactory {\n\t\tpublic static IManagerFactory Instance = new ManagerFactory();\n\n\t\tpublic IAdvancedParkingManager AdvancedParkingManager {\n\t\t\tget {\n\t\t\t\treturn Impl.AdvancedParkingManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic ICustomSegmentLightsManager CustomSegmentLightsManager {\n\t\t\tget {\n\t\t\t\treturn Impl.CustomSegmentLightsManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IExtBuildingManager ExtBuildingManager {\n\t\t\tget {\n\t\t\t\treturn Impl.ExtBuildingManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IExtCitizenInstanceManager ExtCitizenInstanceManager {\n\t\t\tget {\n\t\t\t\treturn Impl.ExtCitizenInstanceManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IExtCitizenManager ExtCitizenManager {\n\t\t\tget {\n\t\t\t\treturn Impl.ExtCitizenManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IJunctionRestrictionsManager JunctionRestrictionsManager {\n\t\t\tget {\n\t\t\t\treturn Impl.JunctionRestrictionsManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic ILaneArrowManager LaneArrowManager {\n\t\t\tget {\n\t\t\t\treturn Impl.LaneArrowManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic ILaneConnectionManager LaneConnectionManager {\n\t\t\tget {\n\t\t\t\treturn Impl.LaneConnectionManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IGeometryManager GeometryManager {\n\t\t\tget {\n\t\t\t\treturn Impl.GeometryManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IOptionsManager OptionsManager {\n\t\t\tget {\n\t\t\t\treturn Impl.OptionsManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IParkingRestrictionsManager ParkingRestrictionsManager {\n\t\t\tget {\n\t\t\t\treturn Impl.ParkingRestrictionsManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IRoutingManager RoutingManager {\n\t\t\tget {\n\t\t\t\treturn Impl.RoutingManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic ISegmentEndManager SegmentEndManager {\n\t\t\tget {\n\t\t\t\treturn Impl.SegmentEndManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic ISpeedLimitManager SpeedLimitManager {\n\t\t\tget {\n\t\t\t\treturn Impl.SpeedLimitManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic ITrafficLightManager TrafficLightManager {\n\t\t\tget {\n\t\t\t\treturn Impl.TrafficLightManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic ITrafficLightSimulationManager TrafficLightSimulationManager {\n\t\t\tget {\n\t\t\t\treturn Impl.TrafficLightSimulationManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic ITrafficMeasurementManager TrafficMeasurementManager {\n\t\t\tget {\n\t\t\t\treturn Impl.TrafficMeasurementManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic ITrafficPriorityManager TrafficPriorityManager {\n\t\t\tget {\n\t\t\t\treturn Impl.TrafficPriorityManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic ITurnOnRedManager TurnOnRedManager {\n\t\t\tget {\n\t\t\t\treturn Impl.TurnOnRedManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IUtilityManager UtilityManager {\n\t\t\tget {\n\t\t\t\treturn Impl.UtilityManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IVehicleBehaviorManager VehicleBehaviorManager {\n\t\t\tget {\n\t\t\t\treturn Impl.VehicleBehaviorManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IVehicleRestrictionsManager VehicleRestrictionsManager {\n\t\t\tget {\n\t\t\t\treturn Impl.VehicleRestrictionsManager.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IVehicleStateManager VehicleStateManager {\n\t\t\tget {\n\t\t\t\treturn Impl.VehicleStateManager.Instance;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/OptionsManager.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.State;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class OptionsManager : AbstractCustomManager, IOptionsManager {\n\t\t// TODO I contain ugly code\n\t\tpublic static OptionsManager Instance = new OptionsManager();\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"- Not implemented -\");\n\t\t\t// TODO implement\n\t\t}\n\n\t\tpublic bool MayPublishSegmentChanges() {\n\t\t\treturn Options.instantEffects && !SerializableDataExtension.StateLoading;\n\t\t}\n\n\t\tpublic bool LoadData(byte[] data) {\n\t\t\tif (data.Length >= 1) {\n\t\t\t\tOptions.setSimAccuracy(data[0]);\n\t\t\t}\n\n\t\t\tif (data.Length >= 2) {\n\t\t\t\t//Options.setLaneChangingRandomization(options[1]);\n\t\t\t}\n\n\t\t\tif (data.Length >= 3) {\n\t\t\t\tOptions.setRecklessDrivers(data[2]);\n\t\t\t}\n\n\t\t\tif (data.Length >= 4) {\n\t\t\t\tOptions.setRelaxedBusses(data[3] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 5) {\n\t\t\t\tOptions.setNodesOverlay(data[4] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 6) {\n\t\t\t\tOptions.setMayEnterBlockedJunctions(data[5] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 7) {\n\t\t\t\tOptions.setAdvancedAI(data[6] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 8) {\n\t\t\t\tOptions.setHighwayRules(data[7] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 9) {\n\t\t\t\tOptions.setPrioritySignsOverlay(data[8] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 10) {\n\t\t\t\tOptions.setTimedLightsOverlay(data[9] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 11) {\n\t\t\t\tOptions.setSpeedLimitsOverlay(data[10] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 12) {\n\t\t\t\tOptions.setVehicleRestrictionsOverlay(data[11] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 13) {\n\t\t\t\tOptions.setStrongerRoadConditionEffects(data[12] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 14) {\n\t\t\t\tOptions.setAllowUTurns(data[13] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 15) {\n\t\t\t\tOptions.setAllowLaneChangesWhileGoingStraight(data[14] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 16) {\n\t\t\t\tOptions.setDisableDespawning(data[15] != (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 17) {\n\t\t\t\t//Options.setDynamicPathRecalculation(data[16] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 18) {\n\t\t\t\tOptions.setConnectedLanesOverlay(data[17] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 19) {\n\t\t\t\tOptions.setPrioritySignsEnabled(data[18] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 20) {\n\t\t\t\tOptions.setTimedLightsEnabled(data[19] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 21) {\n\t\t\t\tOptions.setCustomSpeedLimitsEnabled(data[20] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 22) {\n\t\t\t\tOptions.setVehicleRestrictionsEnabled(data[21] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 23) {\n\t\t\t\tOptions.setLaneConnectorEnabled(data[22] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 24) {\n\t\t\t\tOptions.setJunctionRestrictionsOverlay(data[23] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 25) {\n\t\t\t\tOptions.setJunctionRestrictionsEnabled(data[24] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 26) {\n\t\t\t\tOptions.setProhibitPocketCars(data[25] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 27) {\n\t\t\t\tOptions.setPreferOuterLane(data[26] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 28) {\n\t\t\t\tOptions.setRealisticSpeeds(data[27] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 29) {\n\t\t\t\tOptions.setEvacBussesMayIgnoreRules(data[28] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 30) {\n\t\t\t\tOptions.setInstantEffects(data[29] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 31) {\n\t\t\t\tOptions.setParkingRestrictionsEnabled(data[30] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 32) {\n\t\t\t\tOptions.setParkingRestrictionsOverlay(data[31] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 33) {\n\t\t\t\tOptions.setBanRegularTrafficOnBusLanes(data[32] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 34) {\n\t\t\t\tOptions.setShowPathFindStats(data[33] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 35) {\n\t\t\t\tOptions.setAltLaneSelectionRatio(data[34]);\n\t\t\t}\n\n\t\t\tif (data.Length >= 36) {\n\t\t\t\ttry {\n\t\t\t\t\tOptions.setVehicleRestrictionsAggression((VehicleRestrictionsAggression)data[35]);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Warning($\"Skipping invalid value {data[35]} for vehicle restrictions aggression\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (data.Length >= 37) {\n\t\t\t\tOptions.setTrafficLightPriorityRules(data[36] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 38) {\n\t\t\t\tOptions.setRealisticPublicTransport(data[37] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 39) {\n\t\t\t\tOptions.setTurnOnRedEnabled(data[38] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 40) {\n\t\t\t\tOptions.setAllowNearTurnOnRed(data[39] == (byte)1);\n\t\t\t}\n\n\t\t\tif (data.Length >= 41) {\n\t\t\t\tOptions.setAllowFarTurnOnRed(data[40] == (byte)1);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic byte[] SaveData(ref bool success) {\n\t\t\treturn new byte[] {\n\t\t\t\t\t\t(byte)Options.simAccuracy,\n\t\t\t\t\t\t(byte)0,//Options.laneChangingRandomization,\n\t\t\t\t\t\t(byte)Options.recklessDrivers,\n\t\t\t\t\t\t(byte)(Options.relaxedBusses ? 1 : 0),\n\t\t\t\t\t\t(byte) (Options.nodesOverlay ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.allowEnterBlockedJunctions ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.advancedAI ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.highwayRules ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.prioritySignsOverlay ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.timedLightsOverlay ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.speedLimitsOverlay ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.vehicleRestrictionsOverlay ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.strongerRoadConditionEffects ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.allowUTurns ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.allowLaneChangesWhileGoingStraight ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.disableDespawning ? 0 : 1),\n\t\t\t\t\t\t(byte)0,//Options.IsDynamicPathRecalculationActive()\n\t\t\t\t\t\t(byte)(Options.connectedLanesOverlay ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.prioritySignsEnabled ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.timedLightsEnabled ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.customSpeedLimitsEnabled ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.vehicleRestrictionsEnabled ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.laneConnectorEnabled ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.junctionRestrictionsOverlay ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.junctionRestrictionsEnabled ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.prohibitPocketCars ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.preferOuterLane ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.realisticSpeeds ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.evacBussesMayIgnoreRules ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.instantEffects ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.parkingRestrictionsEnabled ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.parkingRestrictionsOverlay ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.banRegularTrafficOnBusLanes ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.showPathFindStats ? 1 : 0),\n\t\t\t\t\t\t(byte)Options.altLaneSelectionRatio,\n\t\t\t\t\t\t(byte)Options.vehicleRestrictionsAggression,\n\t\t\t\t\t\t(byte)(Options.trafficLightPriorityRules ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.realisticPublicTransport ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.turnOnRedEnabled ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.allowNearTurnOnRed ? 1 : 0),\n\t\t\t\t\t\t(byte)(Options.allowFarTurnOnRed ? 1 : 0)\n\t\t\t\t};\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/ParkingRestrictionsManager.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Geometry.Impl;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class ParkingRestrictionsManager : AbstractGeometryObservingManager, ICustomDataManager<List<Configuration.ParkingRestriction>>, IParkingRestrictionsManager {\n\t\tpublic const NetInfo.LaneType LANE_TYPES = NetInfo.LaneType.Parking;\n\t\tpublic const VehicleInfo.VehicleType VEHICLE_TYPES = VehicleInfo.VehicleType.Car;\n\n\t\tpublic static readonly ParkingRestrictionsManager Instance = new ParkingRestrictionsManager();\n\n\t\tprivate bool[][] parkingAllowed;\n\n\t\tprivate ParkingRestrictionsManager() {\n\t\t\t\n\t\t}\n\n\t\tpublic bool MayHaveParkingRestriction(ushort segmentId) {\n\t\t\tbool ret = false;\n\t\t\tServices.NetService.ProcessSegment(segmentId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\tif ((segment.m_flags & NetSegment.Flags.Created) == NetSegment.Flags.None) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tItemClass connectionClass = segment.Info.GetConnectionClass();\n\t\t\t\tret = connectionClass.m_service == ItemClass.Service.Road && segment.Info.m_hasParkingSpaces;\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool IsParkingAllowed(ushort segmentId, NetInfo.Direction finalDir) {\n\t\t\treturn parkingAllowed[segmentId][GetDirIndex(finalDir)];\n\t\t}\n\n\t\tpublic bool ToggleParkingAllowed(ushort segmentId, NetInfo.Direction finalDir) {\n\t\t\treturn SetParkingAllowed(segmentId, finalDir, !IsParkingAllowed(segmentId, finalDir));\n\t\t}\n\n\t\tpublic bool SetParkingAllowed(ushort segmentId, NetInfo.Direction finalDir, bool flag) {\n\t\t\tif (!MayHaveParkingRestriction(segmentId)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tint dirIndex = GetDirIndex(finalDir);\n\t\t\tparkingAllowed[segmentId][dirIndex] = flag;\n\t\t\tif (!flag || !parkingAllowed[segmentId][1 - dirIndex]) {\n\t\t\t\tServices.SimulationService.AddAction(() => {\n\t\t\t\t\t// force relocation of illegaly parked vehicles\n\t\t\t\t\tServices.NetService.ProcessSegment(segmentId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\t\t\tsegment.UpdateSegment(segmentId);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tprotected override void HandleInvalidSegment(SegmentGeometry geometry) {\n\t\t\tparkingAllowed[geometry.SegmentId][0] = true;\n\t\t\tparkingAllowed[geometry.SegmentId][1] = true;\n\t\t}\n\n\t\tprotected override void HandleValidSegment(SegmentGeometry geometry) {\n\t\t\tif (! MayHaveParkingRestriction(geometry.SegmentId)) {\n\t\t\t\tparkingAllowed[geometry.SegmentId][0] = true;\n\t\t\t\tparkingAllowed[geometry.SegmentId][1] = true;\n\t\t\t}\n\t\t}\n\n\t\tprotected int GetDirIndex(NetInfo.Direction dir) {\n\t\t\treturn dir == NetInfo.Direction.Backward ? 1 : 0;\n\t\t}\n\n\t\tpublic override void OnBeforeLoadData() {\n\t\t\tbase.OnBeforeLoadData();\n\n\t\t\tparkingAllowed = new bool[NetManager.MAX_SEGMENT_COUNT][];\n\t\t\tfor (uint segmentId = 0; segmentId < parkingAllowed.Length; ++segmentId) {\n\t\t\t\tparkingAllowed[segmentId] = new bool[2];\n\t\t\t\tfor (int i = 0; i < 2; ++i) {\n\t\t\t\t\tparkingAllowed[segmentId][i] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic bool LoadData(List<Configuration.ParkingRestriction> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading parking restrictions data. {data.Count} elements\");\n\t\t\tforeach (Configuration.ParkingRestriction restr in data) {\n\t\t\t\ttry {\n\t\t\t\t\tSetParkingAllowed(restr.segmentId, NetInfo.Direction.Forward, restr.forwardParkingAllowed);\n\t\t\t\t\tSetParkingAllowed(restr.segmentId, NetInfo.Direction.Backward, restr.backwardParkingAllowed);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore, as it's probably corrupt save data. it'll be culled on next save\n\t\t\t\t\tLog.Warning(\"Error loading data from parking restrictions: \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\tpublic List<Configuration.ParkingRestriction> SaveData(ref bool success) {\n\t\t\tList<Configuration.ParkingRestriction> ret = new List<Configuration.ParkingRestriction>();\n\t\t\tfor (uint segmentId = 0; segmentId < parkingAllowed.Length; ++segmentId) {\n\t\t\t\ttry {\n\t\t\t\t\tif (parkingAllowed[segmentId][0] && parkingAllowed[segmentId][1]) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tConfiguration.ParkingRestriction restr = new Configuration.ParkingRestriction((ushort)segmentId);\n\t\t\t\t\trestr.forwardParkingAllowed = parkingAllowed[segmentId][0];\n\t\t\t\t\trestr.backwardParkingAllowed = parkingAllowed[segmentId][1];\n\t\t\t\t\tret.Add(restr);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tLog.Error($\"Exception occurred while saving parking restrictions @ {segmentId}: {ex.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/RoutingManager.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing ColossalFramework.UI;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing TrafficManager.Geometry;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.UI;\nusing TrafficManager.Util;\nusing static TrafficManager.State.Flags;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class RoutingManager : AbstractGeometryObservingManager, IRoutingManager {\n\t\tpublic static readonly RoutingManager Instance = new RoutingManager();\n\n\t\tpublic const NetInfo.LaneType ROUTED_LANE_TYPES = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;\n\t\tpublic const VehicleInfo.VehicleType ROUTED_VEHICLE_TYPES = VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Monorail;\n\t\tpublic const VehicleInfo.VehicleType ARROW_VEHICLE_TYPES = VehicleInfo.VehicleType.Car;\n\n\t\tprivate const byte MAX_NUM_TRANSITIONS = 64;\n\n\t\t/// <summary>\n\t\t/// Structs for path-finding that contain required segment-related routing data\n\t\t/// </summary>\n\t\tpublic SegmentRoutingData[] segmentRoutings = new SegmentRoutingData[NetManager.MAX_SEGMENT_COUNT];\n\n\t\t/// <summary>\n\t\t/// Structs for path-finding that contain required lane-end-related backward routing data.\n\t\t/// Index:\n\t\t///\t\t[0 .. NetManager.MAX_LANE_COUNT-1]: lane ends at start node\n\t\t///\t\t[NetManager.MAX_LANE_COUNT .. 2*NetManger.MAX_LANE_COUNT-1]: lane ends at end node\n\t\t/// </summary>\n\t\tpublic LaneEndRoutingData[] laneEndBackwardRoutings = new LaneEndRoutingData[(uint)NetManager.MAX_LANE_COUNT * 2u];\n\n\t\t/// <summary>\n\t\t/// Structs for path-finding that contain required lane-end-related forward routing data.\n\t\t/// Index:\n\t\t///\t\t[0 .. NetManager.MAX_LANE_COUNT-1]: lane ends at start node\n\t\t///\t\t[NetManager.MAX_LANE_COUNT .. 2*NetManger.MAX_LANE_COUNT-1]: lane ends at end node\n\t\t/// </summary>\n\t\tpublic LaneEndRoutingData[] laneEndForwardRoutings = new LaneEndRoutingData[(uint)NetManager.MAX_LANE_COUNT * 2u];\n\n\t\tprotected bool segmentsUpdated = false;\n\t\tprotected ulong[] updatedSegmentBuckets = new ulong[576];\n\t\tprotected object updateLock = new object();\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tString buf = $\"Segment routings:\\n\";\n\t\t\tfor (int i = 0; i < segmentRoutings.Length; ++i) {\n\t\t\t\tif (!Services.NetService.IsSegmentValid((ushort)i)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbuf += $\"Segment {i}: {segmentRoutings[i]}\\n\";\n\t\t\t}\n\t\t\tbuf += $\"\\nLane end backward routings:\\n\";\n\t\t\tfor (uint laneId = 0; laneId < NetManager.MAX_LANE_COUNT; ++laneId) {\n\t\t\t\tif (!Services.NetService.IsLaneValid(laneId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbuf += $\"Lane {laneId} @ start: {laneEndBackwardRoutings[GetLaneEndRoutingIndex(laneId, true)]}\\n\";\n\t\t\t\tbuf += $\"Lane {laneId} @ end: {laneEndBackwardRoutings[GetLaneEndRoutingIndex(laneId, false)]}\\n\";\n\t\t\t}\n\t\t\tbuf += $\"\\nLane end forward routings:\\n\";\n\t\t\tfor (uint laneId = 0; laneId < NetManager.MAX_LANE_COUNT; ++laneId) {\n\t\t\t\tif (!Services.NetService.IsLaneValid(laneId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbuf += $\"Lane {laneId} @ start: {laneEndForwardRoutings[GetLaneEndRoutingIndex(laneId, true)]}\\n\";\n\t\t\t\tbuf += $\"Lane {laneId} @ end: {laneEndForwardRoutings[GetLaneEndRoutingIndex(laneId, false)]}\\n\";\n\t\t\t}\n\t\t\tLog._Debug(buf);\n\t\t}\n\n\t\tprivate RoutingManager() {\n\n\t\t}\n\n\t\tpublic void SimulationStep() {\n\t\t\tif (!segmentsUpdated || Singleton<NetManager>.instance.m_segmentsUpdated || Singleton<NetManager>.instance.m_nodesUpdated) { // TODO maybe refactor NetManager use (however this could influence performance)\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(updateLock);\n\t\t\t\tsegmentsUpdated = false;\n\n\t\t\t\tint len = updatedSegmentBuckets.Length;\n\t\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\t\tulong segMask = updatedSegmentBuckets[i];\n\t\t\t\t\tif (segMask != 0uL) {\n\t\t\t\t\t\tfor (int m = 0; m < 64; m++) {\n\t\t\t\t\t\t\tif ((segMask & 1uL << m) != 0uL) {\n\t\t\t\t\t\t\t\tushort segmentId = (ushort)(i << 6 | m);\n\t\t\t\t\t\t\t\tRecalculateSegment(segmentId);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tupdatedSegmentBuckets[i] = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(updateLock);\n\t\t\t}\n\t\t}\n\n\t\tpublic void RequestFullRecalculation() {\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(updateLock);\n\n\t\t\t\tfor (uint segmentId = 0; segmentId < NetManager.MAX_SEGMENT_COUNT; ++segmentId) {\n\t\t\t\t\tupdatedSegmentBuckets[segmentId >> 6] |= 1uL << (int)(segmentId & 63);\n\t\t\t\t}\n\t\t\t\tFlags.clearHighwayLaneArrows();\n\t\t\t\tsegmentsUpdated = true;\n\n\t\t\t\tif (Services.SimulationService.SimulationPaused || Services.SimulationService.ForcedSimulationPaused) {\n\t\t\t\t\tSimulationStep();\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(updateLock);\n\t\t\t}\n\t\t}\n\n\t\tpublic void RequestRecalculation(ushort segmentId, bool propagate = true) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[1] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || GlobalConfig.Instance.Debug.SegmentId == segmentId);\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"RoutingManager.RequestRecalculation({segmentId}, {propagate}) called.\");\n\t\t\t}\n#endif\n\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(updateLock);\n\n\t\t\t\tupdatedSegmentBuckets[segmentId >> 6] |= 1uL << (int)(segmentId & 63);\n\t\t\t\tResetIncomingHighwayLaneArrows(segmentId);\n\t\t\t\tsegmentsUpdated = true;\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(updateLock);\n\t\t\t}\n\n\t\t\tif (propagate) {\n\t\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\t\tif (segGeo == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tforeach (ushort otherSegmentId in segGeo.GetConnectedSegments(true)) {\n\t\t\t\t\tif (otherSegmentId == 0) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tRequestRecalculation(otherSegmentId, false);\n\t\t\t\t}\n\n\t\t\t\tforeach (ushort otherSegmentId in segGeo.GetConnectedSegments(false)) {\n\t\t\t\t\tif (otherSegmentId == 0) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tRequestRecalculation(otherSegmentId, false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprotected void RecalculateAll() {\n#if DEBUGROUTING\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[1];\n\t\t\tLog._Debug($\"RoutingManager.RecalculateAll: called\");\n#endif\n\t\t\tFlags.clearHighwayLaneArrows();\n\t\t\tfor (uint segmentId = 0; segmentId < NetManager.MAX_SEGMENT_COUNT; ++segmentId) {\n\t\t\t\ttry {\n\t\t\t\t\tRecalculateSegment((ushort)segmentId);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"An error occurred while calculating routes for segment {segmentId}: {e}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprotected void RecalculateSegment(ushort segmentId) {\n#if DEBUGROUTING\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[1] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || GlobalConfig.Instance.Debug.SegmentId == segmentId);\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"RoutingManager.RecalculateSegment({segmentId}) called.\");\n#endif\n\t\t\tif (!Services.NetService.IsSegmentValid(segmentId)) {\n#if DEBUGROUTING\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateSegment({segmentId}): Segment is invalid. Skipping recalculation\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tRecalculateSegmentRoutingData(segmentId);\n\n\t\t\tServices.NetService.IterateSegmentLanes(segmentId, delegate (uint laneId, ref NetLane lane, NetInfo.Lane laneInfo, ushort segId, ref NetSegment segment, byte laneIndex) {\n\t\t\t\tRecalculateLaneEndRoutingData(segmentId, laneIndex, laneId, true);\n\t\t\t\tRecalculateLaneEndRoutingData(segmentId, laneIndex, laneId, false);\n\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\tprotected void ResetIncomingHighwayLaneArrows(ushort segmentId) {\n\t\t\tushort[] nodeIds = new ushort[2];\n\t\t\tServices.NetService.ProcessSegment(segmentId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\tnodeIds[0] = segment.m_startNode;\n\t\t\t\tnodeIds[1] = segment.m_endNode;\n\t\t\t\treturn true;\n\t\t\t});\n\n#if DEBUGROUTING\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[1] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || GlobalConfig.Instance.Debug.SegmentId == segmentId);\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"RoutingManager.ResetRoutingData: Identify nodes connected to {segmentId}: nodeIds={nodeIds.ArrayToString()}\");\n#endif\n\n\t\t\t// reset highway lane arrows on all incoming lanes\n\t\t\tforeach (ushort nodeId in nodeIds) {\n\t\t\t\tif (nodeId == 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tServices.NetService.IterateNodeSegments(nodeId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\t\tif (segId == segmentId) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tServices.NetService.IterateSegmentLanes(segId, delegate (uint laneId, ref NetLane lane, NetInfo.Lane laneInfo, ushort sId, ref NetSegment seg, byte laneIndex) {\n\t\t\t\t\t\tif (IsIncomingLane(segId, seg.m_startNode == nodeId, laneIndex)) {\n\t\t\t\t\t\t\tFlags.removeHighwayLaneArrowFlags(laneId);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tprotected void ResetRoutingData(ushort segmentId) {\n#if DEBUGROUTING\n\t\t\tbool debugBasic = GlobalConfig.Instance.Debug.Switches[1] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || GlobalConfig.Instance.Debug.SegmentId == segmentId);\n\t\t\tbool debugFine = GlobalConfig.Instance.Debug.Switches[8] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || GlobalConfig.Instance.Debug.SegmentId == segmentId);\n\t\t\tif (debugBasic)\n\t\t\t\tLog._Debug($\"RoutingManager.ResetRoutingData: called for segment {segmentId}\");\n#endif\n\t\t\tsegmentRoutings[segmentId].Reset();\n\n\t\t\tResetIncomingHighwayLaneArrows(segmentId);\n\n\t\t\tServices.NetService.IterateSegmentLanes(segmentId, delegate (uint laneId, ref NetLane lane, NetInfo.Lane laneInfo, ushort segId, ref NetSegment segment, byte laneIndex) {\n#if DEBUGROUTING\n\t\t\t\tif (debugFine)\n\t\t\t\t\tLog._Debug($\"RoutingManager.ResetRoutingData: Resetting lane {laneId}, idx {laneIndex} @ seg. {segmentId}\");\n#endif\n\t\t\t\tResetLaneRoutings(laneId, true);\n\t\t\t\tResetLaneRoutings(laneId, false);\n\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\tprotected void RecalculateSegmentRoutingData(ushort segmentId) {\n#if DEBUGROUTING\n\t\t\tbool debugBasic = GlobalConfig.Instance.Debug.Switches[1] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || GlobalConfig.Instance.Debug.SegmentId == segmentId);\n\t\t\tbool debugFine = GlobalConfig.Instance.Debug.Switches[8] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || GlobalConfig.Instance.Debug.SegmentId == segmentId);\n\t\t\tif (debugBasic)\n\t\t\t\tLog._Debug($\"RoutingManager.RecalculateSegmentRoutingData: called for seg. {segmentId}\");\n#endif\n\n\t\t\tsegmentRoutings[segmentId].Reset();\n\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeo == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsegmentRoutings[segmentId].highway = segGeo.IsHighway();\n\t\t\tsegmentRoutings[segmentId].startNodeOutgoingOneWay = segGeo.IsOutgoingOneWay(true);\n\t\t\tsegmentRoutings[segmentId].endNodeOutgoingOneWay = segGeo.IsOutgoingOneWay(false);\n\n#if DEBUGROUTING\n\t\t\tif (debugBasic)\n\t\t\t\tLog._Debug($\"RoutingManager.RecalculateSegmentRoutingData: Calculated routing data for segment {segmentId}: {segmentRoutings[segmentId]}\");\n#endif\n\t\t}\n\n\t\tprotected void RecalculateLaneEndRoutingData(ushort segmentId, int laneIndex, uint laneId, bool startNode) {\n#if DEBUGROUTING\n\t\t\tbool debugBasic = GlobalConfig.Instance.Debug.Switches[1] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || GlobalConfig.Instance.Debug.SegmentId == segmentId);\n\t\t\tbool debugFine = GlobalConfig.Instance.Debug.Switches[8] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || GlobalConfig.Instance.Debug.SegmentId == segmentId);\n\t\t\tif (debugBasic)\n\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}) called\");\n#endif\n\n\t\t\tResetLaneRoutings(laneId, startNode);\n\n\t\t\tif (!IsOutgoingLane(segmentId, startNode, laneIndex)) {\n#if DEBUGROUTING\n\t\t\t\tif (debugFine)\n\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): Lane is not an outgoing lane\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbool prevIsMergeLane = Constants.ServiceFactory.NetService.CheckLaneFlags(laneId, NetLane.Flags.Merge);\n\n\t\t\tNetInfo prevSegmentInfo = null;\n\t\t\tbool prevSegIsInverted = false;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(segmentId, delegate (ushort prevSegId, ref NetSegment segment) {\n\t\t\t\tprevSegmentInfo = segment.Info;\n\t\t\t\tprevSegIsInverted = (segment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None;\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tbool leftHandDrive = Constants.ServiceFactory.SimulationService.LeftHandDrive;\n\n\t\t\tSegmentGeometry prevSegGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (prevSegGeo == null) {\n#if DEBUGROUTING\n\t\t\t\tif (debugFine)\n\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): No segment geometry found\");\n#endif\n\t\t\t\t//Log.Warning($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevSegGeo for segment {segmentId} is null\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tSegmentEndGeometry prevEndGeo = prevSegGeo.GetEnd(startNode);\n\t\t\tif (prevEndGeo == null) {\n#if DEBUGROUTING\n\t\t\t\tif (debugFine)\n\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): No segment end geometry found\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tushort prevSegmentId = segmentId;\n\t\t\tint prevLaneIndex = laneIndex;\n\t\t\tuint prevLaneId = laneId;\n\t\t\tushort nextNodeId = prevEndGeo.NodeId();\n\n\t\t\tNetInfo.Lane prevLaneInfo = prevSegmentInfo.m_lanes[prevLaneIndex];\n\t\t\tif (!prevLaneInfo.CheckType(ROUTED_LANE_TYPES, ROUTED_VEHICLE_TYPES)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tLaneEndRoutingData backwardRouting = new LaneEndRoutingData();\n\t\t\tbackwardRouting.routed = true;\n\n\t\t\tint prevSimilarLaneCount = prevLaneInfo.m_similarLaneCount;\n\t\t\tint prevInnerSimilarLaneIndex = CalcInnerSimilarLaneIndex(prevSegmentId, prevLaneIndex);\n\t\t\tint prevOuterSimilarLaneIndex = CalcOuterSimilarLaneIndex(prevSegmentId, prevLaneIndex);\n\t\t\tbool prevHasBusLane = prevSegGeo.HasBusLane();\n\n\t\t\tbool nextIsJunction = false;\n\t\t\tbool nextIsTransition = false;\n\t\t\tbool nextIsEndOrOneWayOut = false;\n\t\t\tbool nextHasTrafficLights = false;\n\t\t\tConstants.ServiceFactory.NetService.ProcessNode(nextNodeId, delegate (ushort nodeId, ref NetNode node) {\n\t\t\t\tnextIsJunction = (node.m_flags & NetNode.Flags.Junction) != NetNode.Flags.None;\n\t\t\t\tnextIsTransition = (node.m_flags & NetNode.Flags.Transition) != NetNode.Flags.None;\n\t\t\t\tnextHasTrafficLights = (node.m_flags & NetNode.Flags.TrafficLights) != NetNode.Flags.None;\n\t\t\t\tnextIsEndOrOneWayOut = (node.m_flags & (NetNode.Flags.End | NetNode.Flags.OneWayOut)) != NetNode.Flags.None;\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tbool nextIsSimpleJunction = false;\n\t\t\tbool nextIsSplitJunction = false;\n\t\t\tif (Options.highwayRules && !nextHasTrafficLights) {\n\t\t\t\t// determine if junction is a simple junction (highway rules only apply to simple junctions)\n\t\t\t\tNodeGeometry nodeGeo = NodeGeometry.Get(nextNodeId);\n\t\t\t\tnextIsSimpleJunction = nodeGeo.IsSimpleJunction;\n\t\t\t\tnextIsSplitJunction = nodeGeo.OutgoingSegments > 1;\n\t\t\t}\n\t\t\tbool isNextRealJunction = prevSegGeo.CountOtherSegments(startNode) > 1;\n\t\t\tbool nextAreOnlyOneWayHighways = prevEndGeo.OnlyHighways;\n\n\t\t\t// determine if highway rules should be applied\n\t\t\tbool onHighway = Options.highwayRules && nextAreOnlyOneWayHighways && prevEndGeo.OutgoingOneWay && prevSegGeo.IsHighway();\n\t\t\tbool applyHighwayRules = onHighway && nextIsSimpleJunction;\n\t\t\tbool applyHighwayRulesAtJunction = applyHighwayRules && isNextRealJunction;\n\t\t\tbool iterateViaGeometry = applyHighwayRulesAtJunction && prevLaneInfo.CheckType(ROUTED_LANE_TYPES, ARROW_VEHICLE_TYPES);\n\t\t\tushort nextSegmentId = iterateViaGeometry ? segmentId : (ushort)0; // start with u-turns at highway junctions\n\n#if DEBUGROUTING\n\t\t\tif (debugFine) {\n\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevSegment={segmentId}. Starting exploration with nextSegment={nextSegmentId} @ nextNodeId={nextNodeId} -- onHighway={onHighway} applyHighwayRules={applyHighwayRules} applyHighwayRulesAtJunction={applyHighwayRulesAtJunction} Options.highwayRules={Options.highwayRules} nextIsSimpleJunction={nextIsSimpleJunction} nextAreOnlyOneWayHighways={nextAreOnlyOneWayHighways} prevEndGeo.OutgoingOneWay={prevEndGeo.OutgoingOneWay} prevSegGeo.IsHighway()={prevSegGeo.IsHighway()} iterateViaGeometry={iterateViaGeometry}\");\n\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevSegIsInverted={prevSegIsInverted} leftHandDrive={leftHandDrive}\");\n\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevSimilarLaneCount={prevSimilarLaneCount} prevInnerSimilarLaneIndex={prevInnerSimilarLaneIndex} prevOuterSimilarLaneIndex={prevOuterSimilarLaneIndex} prevHasBusLane={prevHasBusLane}\");\n\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): nextIsJunction={nextIsJunction} nextIsEndOrOneWayOut={nextIsEndOrOneWayOut} nextHasTrafficLights={nextHasTrafficLights} nextIsSimpleJunction={nextIsSimpleJunction} nextIsSplitJunction={nextIsSplitJunction} isNextRealJunction={isNextRealJunction}\");\n\t\t\t}\n#endif\n\n\t\t\tint totalIncomingLanes = 0; // running number of next incoming lanes (number is updated at each segment iteration)\n\t\t\tint totalOutgoingLanes = 0; // running number of next outgoing lanes (number is updated at each segment iteration)\n\n\t\t\tfor (int k = 0; k < 8; ++k) {\n\t\t\t\tif (!iterateViaGeometry) {\n\t\t\t\t\tConstants.ServiceFactory.NetService.ProcessNode(nextNodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\t\t\tnextSegmentId = node.GetSegment(k);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\n\t\t\t\t\tif (nextSegmentId == 0) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tint outgoingVehicleLanes = 0;\n\t\t\t\tint incomingVehicleLanes = 0;\n\n\t\t\t\tbool isNextStartNodeOfNextSegment = false;\n\t\t\t\tbool nextSegIsInverted = false;\n\t\t\t\tNetInfo nextSegmentInfo = null;\n\t\t\t\tuint nextFirstLaneId = 0;\n\t\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(nextSegmentId, delegate (ushort nextSegId, ref NetSegment segment) {\n\t\t\t\t\tisNextStartNodeOfNextSegment = segment.m_startNode == nextNodeId;\n\t\t\t\t\t/*segment.UpdateLanes(nextSegmentId, true);\n\t\t\t\t\tif (isNextStartNodeOfNextSegment) {\n\t\t\t\t\t\tsegment.UpdateStartSegments(nextSegmentId);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsegment.UpdateEndSegments(nextSegmentId);\n\t\t\t\t\t}*/\n\t\t\t\t\tnextSegmentInfo = segment.Info;\n\t\t\t\t\tnextSegIsInverted = (segment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None;\n\t\t\t\t\tnextFirstLaneId = segment.m_lanes;\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t\tbool nextIsHighway = SegmentGeometry.calculateIsHighway(nextSegmentInfo);\n\t\t\t\tbool nextHasBusLane = SegmentGeometry.calculateHasBusLane(nextSegmentInfo);\n\n#if DEBUGROUTING\n\t\t\t\tif (debugFine) {\n\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): Exploring nextSegmentId={nextSegmentId}\");\n\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): isNextStartNodeOfNextSegment={isNextStartNodeOfNextSegment} nextSegIsInverted={nextSegIsInverted} nextFirstLaneId={nextFirstLaneId} nextIsHighway={nextIsHighway} nextHasBusLane={nextHasBusLane} totalOutgoingLanes={totalOutgoingLanes} totalIncomingLanes={totalIncomingLanes}\");\n\t\t\t\t}\n#endif\n\n\t\t\t\t// determine next segment direction by evaluating the geometry information\n\t\t\t\tArrowDirection nextIncomingDir = prevEndGeo.GetDirection(nextSegmentId);\n\t\t\t\tbool isNextSegmentValid = nextIncomingDir != ArrowDirection.None;\n\n#if DEBUGROUTING\n\t\t\t\tif (debugFine)\n\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevSegment={segmentId}. Exploring nextSegment={nextSegmentId} -- nextFirstLaneId={nextFirstLaneId} -- nextIncomingDir={nextIncomingDir} valid={isNextSegmentValid}\");\n#endif\n\n\n\t\t\t\tNetInfo.Direction nextDir = isNextStartNodeOfNextSegment ? NetInfo.Direction.Backward : NetInfo.Direction.Forward;\n\t\t\t\tNetInfo.Direction nextDir2 = !nextSegIsInverted ? nextDir : NetInfo.InvertDirection(nextDir);\n\n\t\t\t\tLaneTransitionData[] nextRelaxedTransitionDatas = null;\n\t\t\t\tbyte numNextRelaxedTransitionDatas = 0;\n\t\t\t\tLaneTransitionData[] nextCompatibleTransitionDatas = null;\n\t\t\t\tint[] nextCompatibleOuterSimilarIndices = null;\n\t\t\t\tbyte numNextCompatibleTransitionDatas = 0;\n\t\t\t\tLaneTransitionData[] nextLaneConnectionTransitionDatas = null;\n\t\t\t\tbyte numNextLaneConnectionTransitionDatas = 0;\n\t\t\t\tLaneTransitionData[] nextForcedTransitionDatas = null;\n\t\t\t\tbyte numNextForcedTransitionDatas = 0;\n\t\t\t\tint[] nextCompatibleTransitionDataIndices = null;\n\t\t\t\tbyte numNextCompatibleTransitionDataIndices = 0;\n\t\t\t\tint[] compatibleLaneIndexToLaneConnectionIndex = null;\n\n\t\t\t\tif (isNextSegmentValid) {\n\t\t\t\t\tnextRelaxedTransitionDatas = new LaneTransitionData[MAX_NUM_TRANSITIONS];\n\t\t\t\t\tnextCompatibleTransitionDatas = new LaneTransitionData[MAX_NUM_TRANSITIONS];\n\t\t\t\t\tnextLaneConnectionTransitionDatas = new LaneTransitionData[MAX_NUM_TRANSITIONS];\n\t\t\t\t\tnextForcedTransitionDatas = new LaneTransitionData[MAX_NUM_TRANSITIONS];\n\t\t\t\t\tnextCompatibleOuterSimilarIndices = new int[MAX_NUM_TRANSITIONS];\n\t\t\t\t\tnextCompatibleTransitionDataIndices = new int[MAX_NUM_TRANSITIONS];\n\t\t\t\t\tcompatibleLaneIndexToLaneConnectionIndex = new int[MAX_NUM_TRANSITIONS];\n\t\t\t\t}\n\n\n\t\t\t\tuint nextLaneId = nextFirstLaneId;\n\t\t\t\tbyte nextLaneIndex = 0;\n\t\t\t\t//ushort compatibleLaneIndicesMask = 0;\n\n\t\t\t\twhile (nextLaneIndex < nextSegmentInfo.m_lanes.Length && nextLaneId != 0u) {\n\t\t\t\t\t// determine valid lanes based on lane arrows\n\t\t\t\t\tNetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];\n\n#if DEBUGROUTING\n\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevSegment={segmentId}. Exploring nextSegment={nextSegmentId}, lane {nextLaneId}, idx {nextLaneIndex}\");\n#endif\n\n\t\t\t\t\tif (nextLaneInfo.CheckType(ROUTED_LANE_TYPES, ROUTED_VEHICLE_TYPES) &&\n\t\t\t\t\t\t(prevLaneInfo.m_vehicleType & nextLaneInfo.m_vehicleType) != VehicleInfo.VehicleType.None\n\t\t\t\t\t\t/*(nextLaneInfo.m_vehicleType & prevLaneInfo.m_vehicleType) != VehicleInfo.VehicleType.None &&\n\t\t\t\t\t\t(nextLaneInfo.m_laneType & prevLaneInfo.m_laneType) != NetInfo.LaneType.None*/) { // next is compatible lane\n#if DEBUGROUTING\n\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): vehicle type check passed for nextLaneId={nextLaneId}, idx={nextLaneIndex}\");\n#endif\n\t\t\t\t\t\tif ((nextLaneInfo.m_finalDirection & nextDir2) != NetInfo.Direction.None) { // next is incoming lane\n#if DEBUGROUTING\n\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): lane direction check passed for nextLaneId={nextLaneId}, idx={nextLaneIndex}\");\n#endif\n\t\t\t\t\t\t\t++incomingVehicleLanes;\n\n#if DEBUGROUTING\n\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): increasing number of incoming lanes at nextLaneId={nextLaneId}, idx={nextLaneIndex}: isNextValid={isNextSegmentValid}, nextLaneInfo.m_finalDirection={nextLaneInfo.m_finalDirection}, nextDir2={nextDir2}: incomingVehicleLanes={incomingVehicleLanes}, outgoingVehicleLanes={outgoingVehicleLanes} \");\n#endif\n\n\t\t\t\t\t\t\tif (isNextSegmentValid) {\n\t\t\t\t\t\t\t\t// calculate current similar lane index starting from outer lane\n\t\t\t\t\t\t\t\tint nextOuterSimilarLaneIndex = CalcOuterSimilarLaneIndex(nextSegmentId, nextLaneIndex);\n\t\t\t\t\t\t\t\t//int nextInnerSimilarLaneIndex = CalcInnerSimilarLaneIndex(nextSegmentId, nextLaneIndex);\n\t\t\t\t\t\t\t\tbool isCompatibleLane = false;\n\t\t\t\t\t\t\t\tLaneEndTransitionType transitionType = LaneEndTransitionType.Invalid;\n\n\t\t\t\t\t\t\t\t// check for lane connections\n\t\t\t\t\t\t\t\tbool nextHasOutgoingConnections = LaneConnectionManager.Instance.HasConnections(nextLaneId, isNextStartNodeOfNextSegment);\n\t\t\t\t\t\t\t\tbool nextIsConnectedWithPrev = true;\n\t\t\t\t\t\t\t\tif (nextHasOutgoingConnections) {\n\t\t\t\t\t\t\t\t\tnextIsConnectedWithPrev = LaneConnectionManager.Instance.AreLanesConnected(prevLaneId, nextLaneId, startNode);\n\t\t\t\t\t\t\t\t}\n\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): checking lane connections of nextLaneId={nextLaneId}, idx={nextLaneIndex}: isNextStartNodeOfNextSegment={isNextStartNodeOfNextSegment}, nextSegmentId={nextSegmentId}, nextHasOutgoingConnections={nextHasOutgoingConnections}, nextIsConnectedWithPrev={nextIsConnectedWithPrev}\");\n#endif\n\n\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): connection information for nextLaneId={nextLaneId}, idx={nextLaneIndex}: nextOuterSimilarLaneIndex={nextOuterSimilarLaneIndex}, nextHasOutgoingConnections={nextHasOutgoingConnections}, nextIsConnectedWithPrev={nextIsConnectedWithPrev}\");\n#endif\n\n\t\t\t\t\t\t\t\tint currentLaneConnectionTransIndex = -1;\n\t\t\t\t\t\t\t\tif (nextHasOutgoingConnections) {\n\t\t\t\t\t\t\t\t\t// check for lane connections\n\t\t\t\t\t\t\t\t\tif (nextIsConnectedWithPrev) {\n\t\t\t\t\t\t\t\t\t\t// lane is connected with previous lane\n\t\t\t\t\t\t\t\t\t\tif (numNextLaneConnectionTransitionDatas < MAX_NUM_TRANSITIONS) {\n\t\t\t\t\t\t\t\t\t\t\tcurrentLaneConnectionTransIndex = numNextLaneConnectionTransitionDatas;\n\t\t\t\t\t\t\t\t\t\t\tnextLaneConnectionTransitionDatas[numNextLaneConnectionTransitionDatas++].Set(nextLaneId, nextLaneIndex, LaneEndTransitionType.LaneConnection, nextSegmentId, isNextStartNodeOfNextSegment);\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tLog.Warning($\"nextTransitionDatas overflow @ source lane {prevLaneId}, idx {prevLaneIndex} @ seg. {prevSegmentId}\");\n\t\t\t\t\t\t\t\t\t\t}\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): nextLaneId={nextLaneId}, idx={nextLaneIndex} has outgoing connections and is connected with previous lane. adding as lane connection lane.\");\n#endif\n\t\t\t\t\t\t\t\t\t} else {\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): nextLaneId={nextLaneId}, idx={nextLaneIndex} has outgoing connections but is NOT connected with previous lane\");\n#endif\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (prevIsMergeLane && Constants.ServiceFactory.NetService.CheckLaneFlags(nextLaneId, NetLane.Flags.Merge)) {\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): nextLaneId={nextLaneId}, idx={nextLaneIndex} is a merge lane, as the previous lane. adding as Default.\");\n#endif\n\t\t\t\t\t\t\t\t\tif (nextOuterSimilarLaneIndex == prevOuterSimilarLaneIndex) {\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): nextLaneId={nextLaneId}, idx={nextLaneIndex} is a continuous merge lane. adding as Default.\");\n#endif\n\t\t\t\t\t\t\t\t\t\tisCompatibleLane = true;\n\t\t\t\t\t\t\t\t\t\ttransitionType = LaneEndTransitionType.Default;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else if (!nextIsJunction) {\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): nextLaneId={nextLaneId}, idx={nextLaneIndex} is not a junction. adding as Default.\");\n#endif\n\t\t\t\t\t\t\t\t\tisCompatibleLane = true;\n\t\t\t\t\t\t\t\t\ttransitionType = LaneEndTransitionType.Default;\n\t\t\t\t\t\t\t\t} else if (nextLaneInfo.CheckType(ROUTED_LANE_TYPES, ARROW_VEHICLE_TYPES)) {\n\t\t\t\t\t\t\t\t\t// check for lane arrows\n\t\t\t\t\t\t\t\t\tLaneArrows nextLaneArrows = LaneArrowManager.Instance.GetFinalLaneArrows(nextLaneId);\n\t\t\t\t\t\t\t\t\tbool hasLeftArrow = (nextLaneArrows & LaneArrows.Left) != LaneArrows.None;\n\t\t\t\t\t\t\t\t\tbool hasRightArrow = (nextLaneArrows & LaneArrows.Right) != LaneArrows.None;\n\t\t\t\t\t\t\t\t\tbool hasForwardArrow = (nextLaneArrows & LaneArrows.Forward) != LaneArrows.None || (nextLaneArrows & LaneArrows.LeftForwardRight) == LaneArrows.None;\n\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): start lane arrow check for nextLaneId={nextLaneId}, idx={nextLaneIndex}: hasLeftArrow={hasLeftArrow}, hasForwardArrow={hasForwardArrow}, hasRightArrow={hasRightArrow}\");\n#endif\n\n\t\t\t\t\t\t\t\t\tif (applyHighwayRules || // highway rules enabled\n\t\t\t\t\t\t\t\t\t\t\t(nextIncomingDir == ArrowDirection.Right && hasLeftArrow) || // valid incoming right\n\t\t\t\t\t\t\t\t\t\t\t(nextIncomingDir == ArrowDirection.Left && hasRightArrow) || // valid incoming left\n\t\t\t\t\t\t\t\t\t\t\t(nextIncomingDir == ArrowDirection.Forward && hasForwardArrow) || // valid incoming straight\n\t\t\t\t\t\t\t\t\t\t\t(nextIncomingDir == ArrowDirection.Turn && (nextIsEndOrOneWayOut || ((leftHandDrive && hasRightArrow) || (!leftHandDrive && hasLeftArrow))))) { // valid turning lane\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): lane arrow check passed for nextLaneId={nextLaneId}, idx={nextLaneIndex}. adding as default lane.\");\n#endif\n\t\t\t\t\t\t\t\t\t\tisCompatibleLane = true;\n\t\t\t\t\t\t\t\t\t\ttransitionType = LaneEndTransitionType.Default;\n\t\t\t\t\t\t\t\t\t} else if (nextIsConnectedWithPrev) {\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): lane arrow check FAILED for nextLaneId={nextLaneId}, idx={nextLaneIndex}. adding as relaxed lane.\");\n#endif\n\n\t\t\t\t\t\t\t\t\t\t// lane can be used by all vehicles that may disregard lane arrows\n\t\t\t\t\t\t\t\t\t\ttransitionType = LaneEndTransitionType.Relaxed;\n\t\t\t\t\t\t\t\t\t\tif (numNextRelaxedTransitionDatas < MAX_NUM_TRANSITIONS) {\n\t\t\t\t\t\t\t\t\t\t\tnextRelaxedTransitionDatas[numNextRelaxedTransitionDatas++].Set(nextLaneId, nextLaneIndex, transitionType, nextSegmentId, isNextStartNodeOfNextSegment, GlobalConfig.Instance.PathFinding.IncompatibleLaneDistance);\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tLog.Warning($\"nextTransitionDatas overflow @ source lane {prevLaneId}, idx {prevLaneIndex} @ seg. {prevSegmentId}\");\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else if (!nextHasOutgoingConnections) {\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): nextLaneId={nextLaneId}, idx={nextLaneIndex} is used by vehicles that do not follow lane arrows. adding as default.\");\n#endif\n\n\t\t\t\t\t\t\t\t\t// routed vehicle that does not follow lane arrows (trains, trams, metros, monorails)\n\t\t\t\t\t\t\t\t\ttransitionType = LaneEndTransitionType.Default;\n\n\t\t\t\t\t\t\t\t\tif (numNextForcedTransitionDatas < MAX_NUM_TRANSITIONS) {\n\t\t\t\t\t\t\t\t\t\tnextForcedTransitionDatas[numNextForcedTransitionDatas].Set(nextLaneId, nextLaneIndex, transitionType, nextSegmentId, isNextStartNodeOfNextSegment);\n\n\t\t\t\t\t\t\t\t\t\tif (! isNextRealJunction) {\n\t\t\t\t\t\t\t\t\t\t\t// simple forced lane transition: set lane distance\n\t\t\t\t\t\t\t\t\t\t\tnextForcedTransitionDatas[numNextForcedTransitionDatas].distance = (byte)Math.Abs(prevOuterSimilarLaneIndex - nextOuterSimilarLaneIndex);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t++numNextForcedTransitionDatas;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tLog.Warning($\"nextForcedTransitionDatas overflow @ source lane {prevLaneId}, idx {prevLaneIndex} @ seg. {prevSegmentId}\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (isCompatibleLane) {\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): adding nextLaneId={nextLaneId}, idx={nextLaneIndex} as compatible lane now.\");\n#endif\n\n\t\t\t\t\t\t\t\t\tif (numNextCompatibleTransitionDatas < MAX_NUM_TRANSITIONS) {\n\t\t\t\t\t\t\t\t\t\tnextCompatibleOuterSimilarIndices[numNextCompatibleTransitionDatas] = nextOuterSimilarLaneIndex;\n\t\t\t\t\t\t\t\t\t\tcompatibleLaneIndexToLaneConnectionIndex[numNextCompatibleTransitionDatas] = currentLaneConnectionTransIndex;\n\t\t\t\t\t\t\t\t\t\t//compatibleLaneIndicesMask |= POW2MASKS[numNextCompatibleTransitionDatas];\n\t\t\t\t\t\t\t\t\t\tnextCompatibleTransitionDatas[numNextCompatibleTransitionDatas++].Set(nextLaneId, nextLaneIndex, transitionType, nextSegmentId, isNextStartNodeOfNextSegment);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tLog.Warning($\"nextCompatibleTransitionDatas overflow @ source lane {prevLaneId}, idx {prevLaneIndex} @ seg. {prevSegmentId}\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): nextLaneId={nextLaneId}, idx={nextLaneIndex} is NOT compatible.\");\n#endif\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n#if DEBUGROUTING\n\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): lane direction check NOT passed for nextLaneId={nextLaneId}, idx={nextLaneIndex}: isNextValid={isNextSegmentValid}, nextLaneInfo.m_finalDirection={nextLaneInfo.m_finalDirection}, nextDir2={nextDir2}\");\n#endif\n\t\t\t\t\t\t\tif ((nextLaneInfo.m_finalDirection & NetInfo.InvertDirection(nextDir2)) != NetInfo.Direction.None) {\n\t\t\t\t\t\t\t\t++outgoingVehicleLanes;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): increasing number of outgoing lanes at nextLaneId={nextLaneId}, idx={nextLaneIndex}: isNextValid={isNextSegmentValid}, nextLaneInfo.m_finalDirection={nextLaneInfo.m_finalDirection}, nextDir2={nextDir2}: incomingVehicleLanes={incomingVehicleLanes}, outgoingVehicleLanes={outgoingVehicleLanes}\");\n#endif\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n#if DEBUGROUTING\n\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): vehicle type check NOT passed for nextLaneId={nextLaneId}, idx={nextLaneIndex}: prevLaneInfo.m_vehicleType={prevLaneInfo.m_vehicleType}, nextLaneInfo.m_vehicleType={nextLaneInfo.m_vehicleType}, prevLaneInfo.m_laneType={prevLaneInfo.m_laneType}, nextLaneInfo.m_laneType={nextLaneInfo.m_laneType}\");\n#endif\n\t\t\t\t\t}\n\n\t\t\t\t\tConstants.ServiceFactory.NetService.ProcessLane(nextLaneId, delegate (uint lId, ref NetLane lane) {\n\t\t\t\t\t\tnextLaneId = lane.m_nextLane;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\t\t\t\t\t++nextLaneIndex;\n\t\t\t\t} // foreach lane\n\n\n#if DEBUGROUTING\n\t\t\t\tif (debugFine)\n\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): isNextValid={isNextSegmentValid} Compatible lanes: \" + nextCompatibleTransitionDatas?.ArrayToString());\n#endif\n\t\t\t\tif (isNextSegmentValid) {\n\t\t\t\t\tbool laneChangesAllowed = Options.junctionRestrictionsEnabled && JunctionRestrictionsManager.Instance.IsLaneChangingAllowedWhenGoingStraight(nextSegmentId, isNextStartNodeOfNextSegment);\n\t\t\t\t\tint nextCompatibleLaneCount = numNextCompatibleTransitionDatas;\n\t\t\t\t\tif (nextCompatibleLaneCount > 0) {\n\t\t\t\t\t\t// we found compatible lanes\n\n\t\t\t\t\t\tint[] tmp = new int[nextCompatibleLaneCount];\n\t\t\t\t\t\tArray.Copy(nextCompatibleOuterSimilarIndices, tmp, nextCompatibleLaneCount);\n\t\t\t\t\t\tnextCompatibleOuterSimilarIndices = tmp;\n\n\t\t\t\t\t\tint[] compatibleLaneIndicesSortedByOuterSimilarIndex = nextCompatibleOuterSimilarIndices.Select((x, i) => new KeyValuePair<int, int>(x, i)).OrderBy(p => p.Key).Select(p => p.Value).ToArray();\n\n\t\t\t\t\t\t// enable highway rules only at junctions or at simple lane merging/splitting points\n\t\t\t\t\t\tint laneDiff = nextCompatibleLaneCount - prevSimilarLaneCount;\n\t\t\t\t\t\tbool applyHighwayRulesAtSegment = applyHighwayRules && (applyHighwayRulesAtJunction || Math.Abs(laneDiff) == 1);\n\n#if DEBUGROUTING\n\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): found compatible lanes! compatibleLaneIndicesSortedByOuterSimilarIndex={compatibleLaneIndicesSortedByOuterSimilarIndex.ArrayToString()}, laneDiff={laneDiff}, applyHighwayRulesAtSegment={applyHighwayRulesAtSegment}\");\n#endif\n\n\t\t\t\t\t\tif (applyHighwayRulesAtJunction) {\n\t\t\t\t\t\t\t// we reached a highway junction where more than two segments are connected to each other\n#if DEBUGROUTING\n\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): applying highway rules at junction\");\n#endif\n\n\t\t\t\t\t\t\t// number of lanes that were processed in earlier segment iterations (either all incoming or all outgoing)\n\t\t\t\t\t\t\tint numLanesSeen = Math.Max(totalIncomingLanes, totalOutgoingLanes);\n\n\t\t\t\t\t\t\tint minNextInnerSimilarIndex = -1;\n\t\t\t\t\t\t\tint maxNextInnerSimilarIndex = -1;\n\t\t\t\t\t\t\tint refNextInnerSimilarIndex = -1; // this lane will be referred as the \"stay\" lane with zero distance\n\n#if DEBUGHWJUNCTIONROUTING\n\t\t\t\t\t\t\tif (debugFine) {\n\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): applying highway rules at junction\");\n\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): totalIncomingLanes={totalIncomingLanes}, totalOutgoingLanes={totalOutgoingLanes}, numLanesSeen={numLanesSeen} laneChangesAllowed={laneChangesAllowed}\");\n\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevInnerSimilarLaneIndex={prevInnerSimilarLaneIndex}, prevSimilarLaneCount={prevSimilarLaneCount}, nextCompatibleLaneCount={nextCompatibleLaneCount}\");\n\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\tif (nextIsSplitJunction) {\n\t\t\t\t\t\t\t\t// lane splitting at junction\n\t\t\t\t\t\t\t\tminNextInnerSimilarIndex = prevInnerSimilarLaneIndex + numLanesSeen;\n\n\t\t\t\t\t\t\t\tif (minNextInnerSimilarIndex >= nextCompatibleLaneCount) {\n\t\t\t\t\t\t\t\t\t// there have already been explored more outgoing lanes than incoming lanes on the previous segment. Also allow vehicles to go to the current segment.\n\t\t\t\t\t\t\t\t\tminNextInnerSimilarIndex = maxNextInnerSimilarIndex = refNextInnerSimilarIndex = nextCompatibleLaneCount - 1;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tmaxNextInnerSimilarIndex = refNextInnerSimilarIndex = minNextInnerSimilarIndex;\n\t\t\t\t\t\t\t\t\tif (laneChangesAllowed) {\n\t\t\t\t\t\t\t\t\t\t// allow lane changes at highway junctions\n\t\t\t\t\t\t\t\t\t\tif (minNextInnerSimilarIndex > 0 && prevInnerSimilarLaneIndex > 0) {\n\t\t\t\t\t\t\t\t\t\t\t--minNextInnerSimilarIndex;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n#if DEBUGHWJUNCTIONROUTING\n\t\t\t\t\t\t\t\tif (debugFine) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): highway rules at junction: lane splitting junction. minNextInnerSimilarIndex={minNextInnerSimilarIndex}, maxNextInnerSimilarIndex={maxNextInnerSimilarIndex}\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// lane merging at junction\n\t\t\t\t\t\t\t\tminNextInnerSimilarIndex = prevInnerSimilarLaneIndex - numLanesSeen;\n\n\t\t\t\t\t\t\t\tif (minNextInnerSimilarIndex < 0) {\n\t\t\t\t\t\t\t\t\tif (prevInnerSimilarLaneIndex == prevSimilarLaneCount - 1) {\n\t\t\t\t\t\t\t\t\t\t// there have already been explored more incoming lanes than outgoing lanes on the previous segment. Allow the current segment to also join the big merging party. What a fun!\n\t\t\t\t\t\t\t\t\t\tminNextInnerSimilarIndex = 0;\n\t\t\t\t\t\t\t\t\t\tmaxNextInnerSimilarIndex = nextCompatibleLaneCount - 1;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// lanes do not connect (min/max = -1)\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// allow lane changes at highway junctions\n\t\t\t\t\t\t\t\t\trefNextInnerSimilarIndex = minNextInnerSimilarIndex;\n\t\t\t\t\t\t\t\t\tif (laneChangesAllowed) {\n\t\t\t\t\t\t\t\t\t\tmaxNextInnerSimilarIndex = Math.Min(nextCompatibleLaneCount - 1, minNextInnerSimilarIndex + 1);\n\t\t\t\t\t\t\t\t\t\tif (minNextInnerSimilarIndex > 0) {\n\t\t\t\t\t\t\t\t\t\t\t--minNextInnerSimilarIndex;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tmaxNextInnerSimilarIndex = minNextInnerSimilarIndex;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (totalIncomingLanes > 0 && prevInnerSimilarLaneIndex == prevSimilarLaneCount - 1 && maxNextInnerSimilarIndex < nextCompatibleLaneCount - 1) {\n\t\t\t\t\t\t\t\t\t\t// we reached the outermost lane on the previous segment but there are still lanes to go on the next segment: allow merging\n\t\t\t\t\t\t\t\t\t\tmaxNextInnerSimilarIndex = nextCompatibleLaneCount - 1;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n#if DEBUGHWJUNCTIONROUTING\n\t\t\t\t\t\t\t\tif (debugFine) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): highway rules at junction: lane merging/unknown junction. minNextInnerSimilarIndex={minNextInnerSimilarIndex}, maxNextInnerSimilarIndex={maxNextInnerSimilarIndex}\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (minNextInnerSimilarIndex >= 0) {\n#if DEBUGHWJUNCTIONROUTING\n\t\t\t\t\t\t\t\tif (debugFine) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): minNextInnerSimilarIndex >= 0. nextCompatibleTransitionDatas={nextCompatibleTransitionDatas.ArrayToString()}\");\n\t\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t\t// explore lanes\n\t\t\t\t\t\t\t\tfor (int nextInnerSimilarIndex = minNextInnerSimilarIndex; nextInnerSimilarIndex <= maxNextInnerSimilarIndex; ++nextInnerSimilarIndex) {\n\t\t\t\t\t\t\t\t\tint nextTransitionIndex = FindLaneByInnerIndex(nextCompatibleTransitionDatas, numNextCompatibleTransitionDatas, nextSegmentId, nextInnerSimilarIndex);\n\n#if DEBUGHWJUNCTIONROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine) {\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): highway junction iteration: nextInnerSimilarIndex={nextInnerSimilarIndex}, nextTransitionIndex={nextTransitionIndex}\");\n\t\t\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t\t\tif (nextTransitionIndex < 0) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// calculate lane distance\n\t\t\t\t\t\t\t\t\tbyte compatibleLaneDist = 0;\n\t\t\t\t\t\t\t\t\tif (refNextInnerSimilarIndex >= 0) {\n\t\t\t\t\t\t\t\t\t\tcompatibleLaneDist = (byte)Math.Abs(refNextInnerSimilarIndex - nextInnerSimilarIndex);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// skip lanes having lane connections\n\t\t\t\t\t\t\t\t\tif (LaneConnectionManager.Instance.HasConnections(nextCompatibleTransitionDatas[nextTransitionIndex].laneId, isNextStartNodeOfNextSegment)) {\n\t\t\t\t\t\t\t\t\t\tint laneConnectionTransIndex = compatibleLaneIndexToLaneConnectionIndex[nextTransitionIndex];\n\t\t\t\t\t\t\t\t\t\tif (laneConnectionTransIndex >= 0) {\n\t\t\t\t\t\t\t\t\t\t\tnextLaneConnectionTransitionDatas[laneConnectionTransIndex].distance = compatibleLaneDist;\n\t\t\t\t\t\t\t\t\t\t}\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): Next lane ({nextCompatibleTransitionDatas[nextTransitionIndex].laneId}) has outgoing lane connections. Skip for now but set compatibleLaneDist={compatibleLaneDist} if laneConnectionTransIndex={laneConnectionTransIndex} >= 0.\");\n#endif\n\t\t\t\t\t\t\t\t\t\tcontinue; // disregard lane since it has outgoing connections\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tnextCompatibleTransitionDatas[nextTransitionIndex].distance = compatibleLaneDist;\n#if DEBUGHWJUNCTIONROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine) {\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): highway junction iteration: compatibleLaneDist={compatibleLaneDist}\");\n\t\t\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t\t\tUpdateHighwayLaneArrows(nextCompatibleTransitionDatas[nextTransitionIndex].laneId, isNextStartNodeOfNextSegment, nextIncomingDir);\n\n\t\t\t\t\t\t\t\t\tif (numNextCompatibleTransitionDataIndices < MAX_NUM_TRANSITIONS) {\n\t\t\t\t\t\t\t\t\t\tnextCompatibleTransitionDataIndices[numNextCompatibleTransitionDataIndices++] = nextTransitionIndex;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tLog.Warning($\"nextCompatibleTransitionDataIndices overflow @ source lane {prevLaneId}, idx {prevLaneIndex} @ seg. {prevSegmentId}\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n#if DEBUGHWJUNCTIONROUTING\n\t\t\t\t\t\t\t\tif (debugFine) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): highway junction iterations finished: nextCompatibleTransitionDataIndices={nextCompatibleTransitionDataIndices.ArrayToString()}\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * This is\n\t\t\t\t\t\t\t *    1. a highway lane splitting/merging point,\n\t\t\t\t\t\t\t *    2. a city or highway lane continuation point (simple transition with equal number of lanes or flagged city transition), or\n\t\t\t\t\t\t\t *    3. a city junction\n\t\t\t\t\t\t\t *  with multiple or a single target lane: Perform lane matching\n\t\t\t\t\t\t\t */\n\n#if DEBUGROUTING\n\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): regular node\");\n#endif\n\n\t\t\t\t\t\t\t// min/max compatible outer similar lane indices\n\t\t\t\t\t\t\tint minNextCompatibleOuterSimilarIndex = -1;\n\t\t\t\t\t\t\tint maxNextCompatibleOuterSimilarIndex = -1;\n\t\t\t\t\t\t\tif (nextIncomingDir == ArrowDirection.Turn) {\n\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = 0;\n\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = nextCompatibleLaneCount - 1;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): u-turn: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t} else if (isNextRealJunction) {\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): next is real junction\");\n#endif\n\n\t\t\t\t\t\t\t\t// at junctions: try to match distinct lanes\n\t\t\t\t\t\t\t\tif (nextCompatibleLaneCount > prevSimilarLaneCount && prevOuterSimilarLaneIndex == prevSimilarLaneCount - 1) {\n\t\t\t\t\t\t\t\t\t// merge inner lanes\n\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = prevOuterSimilarLaneIndex;\n\t\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = nextCompatibleLaneCount - 1;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): merge inner lanes: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t} else if (nextCompatibleLaneCount < prevSimilarLaneCount && prevSimilarLaneCount % nextCompatibleLaneCount == 0) {\n\t\t\t\t\t\t\t\t\t// symmetric split\n\t\t\t\t\t\t\t\t\tint splitFactor = prevSimilarLaneCount / nextCompatibleLaneCount;\n\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = maxNextCompatibleOuterSimilarIndex = prevOuterSimilarLaneIndex / splitFactor;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): symmetric split: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// 1-to-n (split inner lane) or 1-to-1 (direct lane matching)\n\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = prevOuterSimilarLaneIndex;\n\t\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = prevOuterSimilarLaneIndex;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): 1-to-n (split inner lane) or 1-to-1 (direct lane matching): minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tbool straightLaneChangesAllowed = nextIncomingDir == ArrowDirection.Forward && laneChangesAllowed;\n\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): laneChangesAllowed={laneChangesAllowed} straightLaneChangesAllowed={straightLaneChangesAllowed}\");\n#endif\n\n\t\t\t\t\t\t\t\tif (!straightLaneChangesAllowed) {\n\t\t\t\t\t\t\t\t\tif (nextHasBusLane && !prevHasBusLane) {\n\t\t\t\t\t\t\t\t\t\t// allow vehicles on the bus lane AND on the next lane to merge on this lane\n\t\t\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = Math.Min(nextCompatibleLaneCount - 1, maxNextCompatibleOuterSimilarIndex + 1);\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): allow vehicles on the bus lane AND on the next lane to merge on this lane: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t\t} else if (!nextHasBusLane && prevHasBusLane) {\n\t\t\t\t\t\t\t\t\t\t// allow vehicles to enter the bus lane\n\t\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = Math.Max(0, minNextCompatibleOuterSimilarIndex - 1);\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): allow vehicles to enter the bus lane: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// vehicles may change lanes when going straight\n\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = minNextCompatibleOuterSimilarIndex - 1;\n\t\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = maxNextCompatibleOuterSimilarIndex + 1;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): vehicles may change lanes when going straight: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (prevSimilarLaneCount == nextCompatibleLaneCount) {\n\t\t\t\t\t\t\t\t// equal lane count: consider all available lanes\n\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = 0;\n\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = nextCompatibleLaneCount - 1;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): equal lane count: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// lane continuation point: lane merging/splitting\n\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): lane continuation point: lane merging/splitting\");\n#endif\n\n\t\t\t\t\t\t\t\tbool sym1 = (prevSimilarLaneCount & 1) == 0; // mod 2 == 0\n\t\t\t\t\t\t\t\tbool sym2 = (nextCompatibleLaneCount & 1) == 0; // mod 2 == 0\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): sym1={sym1}, sym2={sym2}\");\n#endif\n\t\t\t\t\t\t\t\tif (prevSimilarLaneCount < nextCompatibleLaneCount) {\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): lane merging (prevSimilarLaneCount={prevSimilarLaneCount} < nextCompatibleLaneCount={nextCompatibleLaneCount})\");\n#endif\n\n\t\t\t\t\t\t\t\t\t// lane merging\n\t\t\t\t\t\t\t\t\tif (sym1 == sym2) {\n\t\t\t\t\t\t\t\t\t\t// merge outer lanes\n\t\t\t\t\t\t\t\t\t\tint a = (nextCompatibleLaneCount - prevSimilarLaneCount) >> 1; // nextCompatibleLaneCount - prevSimilarLaneCount is always > 0\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): merge outer lanes. a={a}\");\n#endif\n\t\t\t\t\t\t\t\t\t\tif (prevSimilarLaneCount == 1) {\n\t\t\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = 0;\n\t\t\t\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = nextCompatibleLaneCount - 1; // always >=0\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevSimilarLaneCount == 1: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t\t\t} else if (prevOuterSimilarLaneIndex == 0) {\n\t\t\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = 0;\n\t\t\t\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = a;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevOuterSimilarLaneIndex == 0: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t\t\t} else if (prevOuterSimilarLaneIndex == prevSimilarLaneCount - 1) {\n\t\t\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = prevOuterSimilarLaneIndex + a;\n\t\t\t\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = nextCompatibleLaneCount - 1; // always >=0\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevOuterSimilarLaneIndex == prevSimilarLaneCount - 1: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = maxNextCompatibleOuterSimilarIndex = prevOuterSimilarLaneIndex + a;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): default case: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// criss-cross merge\n\t\t\t\t\t\t\t\t\t\tint a = (nextCompatibleLaneCount - prevSimilarLaneCount - 1) >> 1; // nextCompatibleLaneCount - prevSimilarLaneCount - 1 is always >= 0\n\t\t\t\t\t\t\t\t\t\tint b = (nextCompatibleLaneCount - prevSimilarLaneCount + 1) >> 1; // nextCompatibleLaneCount - prevSimilarLaneCount + 1 is always >= 2\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): criss-cross merge: a={a}, b={b}\");\n#endif\n\t\t\t\t\t\t\t\t\t\tif (prevSimilarLaneCount == 1) {\n\t\t\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = 0;\n\t\t\t\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = nextCompatibleLaneCount - 1; // always >=0\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevSimilarLaneCount == 1: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t\t\t} else if (prevOuterSimilarLaneIndex == 0) {\n\t\t\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = 0;\n\t\t\t\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = b;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevOuterSimilarLaneIndex == 0: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t\t\t} else if (prevOuterSimilarLaneIndex == prevSimilarLaneCount - 1) {\n\t\t\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = prevOuterSimilarLaneIndex + a;\n\t\t\t\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = nextCompatibleLaneCount - 1; // always >=0\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): prevOuterSimilarLaneIndex == prevSimilarLaneCount - 1: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = prevOuterSimilarLaneIndex + a;\n\t\t\t\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = prevOuterSimilarLaneIndex + b;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): default criss-cross case: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// at lane splits: distribute traffic evenly (1-to-n, n-to-n)\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t// prevOuterSimilarIndex is always > nextCompatibleLaneCount\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): at lane splits: distribute traffic evenly (1-to-n, n-to-n)\");\n#endif\n\t\t\t\t\t\t\t\t\tif (sym1 == sym2) {\n\t\t\t\t\t\t\t\t\t\t// split outer lanes\n\t\t\t\t\t\t\t\t\t\tint a = (prevSimilarLaneCount - nextCompatibleLaneCount) >> 1; // prevSimilarLaneCount - nextCompatibleLaneCount is always > 0\n\t\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = maxNextCompatibleOuterSimilarIndex = prevOuterSimilarLaneIndex - a; // a is always <= prevSimilarLaneCount\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): split outer lanes: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// split outer lanes, criss-cross inner lanes \n\t\t\t\t\t\t\t\t\t\tint a = (prevSimilarLaneCount - nextCompatibleLaneCount - 1) >> 1; // prevSimilarLaneCount - nextCompatibleLaneCount - 1 is always >= 0\n\n\t\t\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = (a - 1 >= prevOuterSimilarLaneIndex) ? 0 : prevOuterSimilarLaneIndex - a - 1;\n\t\t\t\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = (a >= prevOuterSimilarLaneIndex) ? 0 : prevOuterSimilarLaneIndex - a;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): split outer lanes, criss-cross inner lanes: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n#if DEBUGROUTING\n\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): pre-final bounds: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\n\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = Math.Max(0, Math.Min(minNextCompatibleOuterSimilarIndex, nextCompatibleLaneCount - 1));\n\t\t\t\t\t\t\tmaxNextCompatibleOuterSimilarIndex = Math.Max(0, Math.Min(maxNextCompatibleOuterSimilarIndex, nextCompatibleLaneCount - 1));\n\n\t\t\t\t\t\t\tif (minNextCompatibleOuterSimilarIndex > maxNextCompatibleOuterSimilarIndex) {\n\t\t\t\t\t\t\t\tminNextCompatibleOuterSimilarIndex = maxNextCompatibleOuterSimilarIndex;\n\t\t\t\t\t\t\t}\n\n#if DEBUGROUTING\n\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): final bounds: minNextCompatibleOuterSimilarIndex={minNextCompatibleOuterSimilarIndex}, maxNextCompatibleOuterSimilarIndex={maxNextCompatibleOuterSimilarIndex}\");\n#endif\n\n\t\t\t\t\t\t\t// find best matching lane(s)\n\t\t\t\t\t\t\tfor (int nextCompatibleOuterSimilarIndex = minNextCompatibleOuterSimilarIndex; nextCompatibleOuterSimilarIndex <= maxNextCompatibleOuterSimilarIndex; ++nextCompatibleOuterSimilarIndex) {\n\t\t\t\t\t\t\t\tint nextTransitionIndex = FindLaneWithMaxOuterIndex(compatibleLaneIndicesSortedByOuterSimilarIndex, nextCompatibleOuterSimilarIndex);\n\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): best matching lane iteration -- nextCompatibleOuterSimilarIndex={nextCompatibleOuterSimilarIndex} => nextTransitionIndex={nextTransitionIndex}\");\n#endif\n\n\t\t\t\t\t\t\t\tif (nextTransitionIndex < 0) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// calculate lane distance\n\t\t\t\t\t\t\t\tbyte compatibleLaneDist = 0;\n\t\t\t\t\t\t\t\tif (nextIncomingDir == ArrowDirection.Turn) {\n\t\t\t\t\t\t\t\t\tcompatibleLaneDist = (byte)GlobalConfig.Instance.PathFinding.UturnLaneDistance;\n\t\t\t\t\t\t\t\t} else if (!isNextRealJunction && ((!nextIsJunction && !nextIsTransition) || nextCompatibleLaneCount == prevSimilarLaneCount)) {\n\t\t\t\t\t\t\t\t\tint relLaneDist = nextCompatibleOuterSimilarIndices[nextTransitionIndex] - prevOuterSimilarLaneIndex; // relative lane distance (positive: change to more outer lane, negative: change to more inner lane)\n\t\t\t\t\t\t\t\t\tcompatibleLaneDist = (byte)Math.Abs(relLaneDist);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// skip lanes having lane connections\n\t\t\t\t\t\t\t\tif (LaneConnectionManager.Instance.HasConnections(nextCompatibleTransitionDatas[nextTransitionIndex].laneId, isNextStartNodeOfNextSegment)) {\n\t\t\t\t\t\t\t\t\tint laneConnectionTransIndex = compatibleLaneIndexToLaneConnectionIndex[nextTransitionIndex];\n\t\t\t\t\t\t\t\t\tif (laneConnectionTransIndex >= 0) {\n\t\t\t\t\t\t\t\t\t\tnextLaneConnectionTransitionDatas[laneConnectionTransIndex].distance = compatibleLaneDist;\n\t\t\t\t\t\t\t\t\t}\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): Next lane ({nextCompatibleTransitionDatas[nextTransitionIndex].laneId}) has outgoing lane connections. Skip for now but set compatibleLaneDist={compatibleLaneDist} if laneConnectionTransIndex={laneConnectionTransIndex} >= 0.\");\n#endif\n\t\t\t\t\t\t\t\t\tcontinue; // disregard lane since it has outgoing connections\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tnextIncomingDir == ArrowDirection.Turn && // u-turn\n\t\t\t\t\t\t\t\t\t!nextIsEndOrOneWayOut && // not a dead end\n\t\t\t\t\t\t\t\t\tnextCompatibleOuterSimilarIndex != maxNextCompatibleOuterSimilarIndex // incoming lane is not innermost lane\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t// force u-turns to happen on the innermost lane\n\t\t\t\t\t\t\t\t\t++compatibleLaneDist;\n\t\t\t\t\t\t\t\t\tnextCompatibleTransitionDatas[nextTransitionIndex].type = LaneEndTransitionType.Relaxed;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): Next lane ({nextCompatibleTransitionDatas[nextTransitionIndex].laneId}) is avoided u-turn. Incrementing compatible lane distance to {compatibleLaneDist}\");\n#endif\n\t\t\t\t\t\t\t\t}\n\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): -> compatibleLaneDist={compatibleLaneDist}\");\n#endif\n\n\t\t\t\t\t\t\t\tnextCompatibleTransitionDatas[nextTransitionIndex].distance = compatibleLaneDist;\n\t\t\t\t\t\t\t\tif (onHighway && !isNextRealJunction && compatibleLaneDist > 1) {\n\t\t\t\t\t\t\t\t\t// under normal circumstances vehicles should not change more than one lane on highways at one time\n\t\t\t\t\t\t\t\t\tnextCompatibleTransitionDatas[nextTransitionIndex].type = LaneEndTransitionType.Relaxed;\n#if DEBUGROUTING\n\t\t\t\t\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): -> under normal circumstances vehicles should not change more than one lane on highways at one time: setting type to Relaxed\");\n#endif\n\t\t\t\t\t\t\t\t} else if (applyHighwayRulesAtSegment) {\n\t\t\t\t\t\t\t\t\tUpdateHighwayLaneArrows(nextCompatibleTransitionDatas[nextTransitionIndex].laneId, isNextStartNodeOfNextSegment, nextIncomingDir);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (numNextCompatibleTransitionDataIndices < MAX_NUM_TRANSITIONS) {\n\t\t\t\t\t\t\t\t\tnextCompatibleTransitionDataIndices[numNextCompatibleTransitionDataIndices++] = nextTransitionIndex;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tLog.Warning($\"nextCompatibleTransitionDataIndices overflow @ source lane {prevLaneId}, idx {prevLaneIndex} @ seg. {prevSegmentId}\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} // foreach lane\n\t\t\t\t\t\t} // highway/city rules if/else\n\t\t\t\t\t} // compatible lanes found\n\n\t\t\t\t\t// build final array\n\t\t\t\t\tLaneTransitionData[] nextTransitionDatas = new LaneTransitionData[numNextRelaxedTransitionDatas + numNextCompatibleTransitionDataIndices + numNextLaneConnectionTransitionDatas + numNextForcedTransitionDatas];\n\t\t\t\t\tint j = 0;\n\t\t\t\t\tfor (int i = 0; i < numNextCompatibleTransitionDataIndices; ++i) {\n\t\t\t\t\t\tnextTransitionDatas[j++] = nextCompatibleTransitionDatas[nextCompatibleTransitionDataIndices[i]];\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (int i = 0; i < numNextLaneConnectionTransitionDatas; ++i) {\n\t\t\t\t\t\tnextTransitionDatas[j++] = nextLaneConnectionTransitionDatas[i];\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (int i = 0; i < numNextRelaxedTransitionDatas; ++i) {\n\t\t\t\t\t\tnextTransitionDatas[j++] = nextRelaxedTransitionDatas[i];\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (int i = 0; i < numNextForcedTransitionDatas; ++i) {\n\t\t\t\t\t\tnextTransitionDatas[j++] = nextForcedTransitionDatas[i];\n\t\t\t\t\t}\n\n#if DEBUGROUTING\n\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): build array for nextSegment={nextSegmentId}: nextTransitionDatas={nextTransitionDatas.ArrayToString()}\");\n#endif\n\n\t\t\t\t\tbackwardRouting.AddTransitions(nextTransitionDatas);\n\n#if DEBUGROUTING\n\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): updated incoming/outgoing lanes for next segment iteration: totalIncomingLanes={totalIncomingLanes}, totalOutgoingLanes={totalOutgoingLanes}\");\n#endif\n\t\t\t\t} // valid segment\n\n\t\t\t\tif (nextSegmentId != prevSegmentId) {\n\t\t\t\t\ttotalIncomingLanes += incomingVehicleLanes;\n\t\t\t\t\ttotalOutgoingLanes += outgoingVehicleLanes;\n\t\t\t\t}\n\n\t\t\t\tif (iterateViaGeometry) {\n\t\t\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(nextSegmentId, delegate (ushort nextSegId, ref NetSegment segment) {\n\t\t\t\t\t\tif (Constants.ServiceFactory.SimulationService.LeftHandDrive) {\n\t\t\t\t\t\t\tnextSegmentId = segment.GetLeftSegment(nextNodeId);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tnextSegmentId = segment.GetRightSegment(nextNodeId);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\n\t\t\t\t\tif (nextSegmentId == prevSegmentId || nextSegmentId == 0) {\n\t\t\t\t\t\t// we reached the first segment again\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} // foreach segment\n\n\t\t\t// update backward routing\n\t\t\tlaneEndBackwardRoutings[GetLaneEndRoutingIndex(laneId, startNode)] = backwardRouting;\n\n\t\t\t// update forward routing\n\t\t\tLaneTransitionData[] newTransitions = backwardRouting.transitions;\n\t\t\tif (newTransitions != null) {\n\t\t\t\tfor (int i = 0; i < newTransitions.Length; ++i) {\n\t\t\t\t\tuint sourceIndex = GetLaneEndRoutingIndex(newTransitions[i].laneId, newTransitions[i].startNode);\n\n\t\t\t\t\tLaneTransitionData forwardTransition = new LaneTransitionData();\n\t\t\t\t\tforwardTransition.laneId = laneId;\n\t\t\t\t\tforwardTransition.laneIndex = (byte)laneIndex;\n\t\t\t\t\tforwardTransition.type = newTransitions[i].type;\n\t\t\t\t\tforwardTransition.distance = newTransitions[i].distance;\n\t\t\t\t\tforwardTransition.segmentId = segmentId;\n\t\t\t\t\tforwardTransition.startNode = startNode;\n\n\t\t\t\t\tlaneEndForwardRoutings[sourceIndex].AddTransition(forwardTransition);\n\n#if DEBUGROUTING\n\t\t\t\t\tif (debugFine)\n\t\t\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): adding transition to forward routing of laneId={laneId}, idx={laneIndex} @ seg. {newTransitions[i].segmentId} @ node {newTransitions[i].startNode} (sourceIndex={sourceIndex}): {forwardTransition.ToString()}\\n\\nNew forward routing:\\n{laneEndForwardRoutings[sourceIndex].ToString()}\");\n#endif\n\t\t\t\t}\n\t\t\t}\n\n#if DEBUGROUTING\n\t\t\tif (debugBasic)\n\t\t\t\tLog._Debug($\"RoutingManager.RecalculateLaneEndRoutingData({segmentId}, {laneIndex}, {laneId}, {startNode}): FINISHED calculating routing data for array index {GetLaneEndRoutingIndex(laneId, startNode)}: {backwardRouting}\");\n#endif\n\t\t}\n\n\t\t/// <summary>\n\t\t/// remove all backward routings from this lane and forward routings pointing to this lane\n\t\t/// </summary>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <param name=\"startNode\"></param>\n\t\tprotected void ResetLaneRoutings(uint laneId, bool startNode) {\n\t\t\tuint index = GetLaneEndRoutingIndex(laneId, startNode);\n\n\t\t\tLaneTransitionData[] oldBackwardTransitions = laneEndBackwardRoutings[index].transitions;\n\t\t\tif (oldBackwardTransitions != null) {\n\t\t\t\tfor (int i = 0; i < oldBackwardTransitions.Length; ++i) {\n\t\t\t\t\tuint sourceIndex = GetLaneEndRoutingIndex(oldBackwardTransitions[i].laneId, oldBackwardTransitions[i].startNode);\n\t\t\t\t\tlaneEndForwardRoutings[sourceIndex].RemoveTransition(laneId);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlaneEndBackwardRoutings[index].Reset();\n\t\t}\n\n\t\tprivate void UpdateHighwayLaneArrows(uint laneId, bool startNode, ArrowDirection dir) {\n\n\t\t\tFlags.LaneArrows? prevHighwayArrows = Flags.getHighwayLaneArrowFlags(laneId);\n\t\t\tFlags.LaneArrows newHighwayArrows = Flags.LaneArrows.None;\n\t\t\tif (prevHighwayArrows != null)\n\t\t\t\tnewHighwayArrows = (Flags.LaneArrows)prevHighwayArrows;\n\t\t\tif (dir == ArrowDirection.Right)\n\t\t\t\tnewHighwayArrows |= Flags.LaneArrows.Left;\n\t\t\telse if (dir == ArrowDirection.Left)\n\t\t\t\tnewHighwayArrows |= Flags.LaneArrows.Right;\n\t\t\telse if (dir == ArrowDirection.Forward)\n\t\t\t\tnewHighwayArrows |= Flags.LaneArrows.Forward;\n\n#if DEBUGROUTING\n\t\t\t//Log._Debug($\"RoutingManager.RecalculateLaneEndRoutingData: highway rules -- next lane {laneId} obeys highway rules. Setting highway lane arrows to {newHighwayArrows}. prevHighwayArrows={prevHighwayArrows}\");\n#endif\n\n\t\t\tif (newHighwayArrows != prevHighwayArrows && newHighwayArrows != Flags.LaneArrows.None) {\n\t\t\t\tFlags.setHighwayLaneArrowFlags(laneId, newHighwayArrows, false);\n\t\t\t}\n\t\t}\n\n\t\t/*private int GetSegmentNodeIndex(ushort nodeId, ushort segmentId) {\n\t\t\tint i = -1;\n\t\t\tServices.NetService.IterateNodeSegments(nodeId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\t++i;\n\t\t\t\tif (segId == segmentId) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn i;\n\t\t}*/\n\n\t\tinternal uint GetLaneEndRoutingIndex(uint laneId, bool startNode) {\n\t\t\treturn (uint)(laneId + (startNode ? 0u : (uint)NetManager.MAX_LANE_COUNT));\n\t\t}\n\n\t\tpublic int CalcInnerSimilarLaneIndex(ushort segmentId, int laneIndex) {\n\t\t\tint ret = -1;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(segmentId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\tret = CalcInnerSimilarLaneIndex(segment.Info.m_lanes[laneIndex]);\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic int CalcInnerSimilarLaneIndex(NetInfo.Lane laneInfo) {\n\t\t\t// note: m_direction is correct here\n\t\t\treturn (byte)(laneInfo.m_direction & NetInfo.Direction.Forward) != 0 ? laneInfo.m_similarLaneIndex : laneInfo.m_similarLaneCount - laneInfo.m_similarLaneIndex - 1;\n\t\t}\n\n\t\tpublic int CalcOuterSimilarLaneIndex(ushort segmentId, int laneIndex) {\n\t\t\tint ret = -1;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(segmentId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\tret = CalcOuterSimilarLaneIndex(segment.Info.m_lanes[laneIndex]);\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic int CalcOuterSimilarLaneIndex(NetInfo.Lane laneInfo) {\n\t\t\t// note: m_direction is correct here\n\t\t\treturn (byte)(laneInfo.m_direction & NetInfo.Direction.Forward) != 0 ? laneInfo.m_similarLaneCount - laneInfo.m_similarLaneIndex - 1 : laneInfo.m_similarLaneIndex;\n\t\t}\n\n\t\tprotected int FindLaneWithMaxOuterIndex(int[] indicesSortedByOuterIndex, int targetOuterLaneIndex) {\n\t\t\treturn indicesSortedByOuterIndex[Math.Max(0, Math.Min(targetOuterLaneIndex, indicesSortedByOuterIndex.Length - 1))];\n\t\t}\n\n\t\tprotected int FindLaneByOuterIndex(LaneTransitionData[] laneTransitions, int num, ushort segmentId, int targetOuterLaneIndex) {\n\t\t\tfor (int i = 0; i < num; ++i) {\n\t\t\t\tint outerIndex = CalcOuterSimilarLaneIndex(segmentId, laneTransitions[i].laneIndex);\n\t\t\t\tif (outerIndex == targetOuterLaneIndex) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\n\t\tprotected int FindLaneByInnerIndex(LaneTransitionData[] laneTransitions, int num, ushort segmentId, int targetInnerLaneIndex) {\n\t\t\tfor (int i = 0; i < num; ++i) {\n\t\t\t\tint innerIndex = CalcInnerSimilarLaneIndex(segmentId, laneTransitions[i].laneIndex);\n\t\t\t\tif (innerIndex == targetInnerLaneIndex) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\n\t\tprotected bool IsOutgoingLane(ushort segmentId, bool startNode, int laneIndex) {\n\t\t\treturn IsIncomingOutgoingLane(segmentId, startNode, laneIndex, false);\n\t\t}\n\n\t\tprotected bool IsIncomingLane(ushort segmentId, bool startNode, int laneIndex) {\n\t\t\treturn IsIncomingOutgoingLane(segmentId, startNode, laneIndex, true);\n\t\t}\n\n\t\tprotected bool IsIncomingOutgoingLane(ushort segmentId, bool startNode, int laneIndex, bool incoming) {\n\t\t\tbool segIsInverted = false;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(segmentId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\tsegIsInverted = (segment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None;\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tNetInfo.Direction dir = startNode ? NetInfo.Direction.Forward : NetInfo.Direction.Backward;\n\t\t\tdir = incoming ^ segIsInverted ? NetInfo.InvertDirection(dir) : dir;\n\n\t\t\tNetInfo.Direction finalDir = NetInfo.Direction.None;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(segmentId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\tfinalDir = segment.Info.m_lanes[laneIndex].m_finalDirection;\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\treturn (finalDir & dir) != NetInfo.Direction.None;\n\t\t}\n\n\t\tprotected override void HandleInvalidSegment(SegmentGeometry geometry) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[1] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || GlobalConfig.Instance.Debug.SegmentId == geometry.SegmentId);\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"RoutingManager.HandleInvalidSegment({geometry.SegmentId}) called.\");\n\t\t\t}\n#endif\n\t\t\tFlags.removeHighwayLaneArrowFlagsAtSegment(geometry.SegmentId);\n\t\t\tResetRoutingData(geometry.SegmentId);\n\t\t}\n\n\t\tprotected override void HandleValidSegment(SegmentGeometry geometry) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[1] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || GlobalConfig.Instance.Debug.SegmentId == geometry.SegmentId);\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"RoutingManager.HandleValidSegment({geometry.SegmentId}) called.\");\n\t\t\t}\n#endif\n\t\t\tResetRoutingData(geometry.SegmentId);\n\t\t\tRequestRecalculation(geometry.SegmentId);\n\t\t}\n\n\t\tpublic override void OnAfterLoadData() {\n\t\t\tbase.OnAfterLoadData();\n\n\t\t\tRecalculateAll();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/SegmentEndManager.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Impl;\nusing TrafficManager.TrafficLight;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class SegmentEndManager : AbstractCustomManager, ISegmentEndManager {\n\t\tpublic static readonly SegmentEndManager Instance = new SegmentEndManager();\n\n\t\tprivate ISegmentEnd[] SegmentEnds;\n\n\t\tprivate SegmentEndManager() {\n\t\t\tSegmentEnds = new SegmentEnd[2 * NetManager.MAX_SEGMENT_COUNT];\n\t\t}\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"Segment ends:\");\n\t\t\tfor (int i = 0; i < SegmentEnds.Length; ++i) {\n\t\t\t\tif (SegmentEnds[i] == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLog._Debug($\"Segment end {i}: {SegmentEnds[i]}\");\n\t\t\t}\n\t\t}\n\n\t\tpublic ISegmentEnd GetSegmentEnd(ISegmentEndId endId) {\n\t\t\treturn GetSegmentEnd(endId.SegmentId, endId.StartNode);\n\t\t}\n\n\t\tpublic ISegmentEnd GetSegmentEnd(ushort segmentId, bool startNode) {\n\t\t\treturn SegmentEnds[GetIndex(segmentId, startNode)];\n\t\t}\n\n\t\tpublic ISegmentEnd GetOrAddSegmentEnd(ISegmentEndId endId) {\n\t\t\treturn GetOrAddSegmentEnd(endId.SegmentId, endId.StartNode);\n\t\t}\n\n\t\tpublic ISegmentEnd GetOrAddSegmentEnd(ushort segmentId, bool startNode) {\n\t\t\tISegmentEnd end = GetSegmentEnd(segmentId, startNode);\n\t\t\tif (end != null) {\n\t\t\t\treturn end;\n\t\t\t}\n\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeo == null) {\n\t\t\t\tLog.Warning($\"SegmentEndManager.GetOrAddSegmentEnd({segmentId}, {startNode}): Refusing to add segment end for invalid segment.\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tSegmentEndGeometry endGeo = segGeo.GetEnd(startNode);\n\t\t\tif (endGeo == null) {\n\t\t\t\tLog.Warning($\"SegmentEndManager.GetOrAddSegmentEnd({segmentId}, {startNode}): Refusing to add segment end for invalid segment end.\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn SegmentEnds[GetIndex(segmentId, startNode)] = new SegmentEnd(segmentId, startNode);\n\t\t}\n\n\t\tpublic void RemoveSegmentEnd(ISegmentEndId endId) {\n\t\t\tRemoveSegmentEnd(endId.SegmentId, endId.StartNode);\n\t\t}\n\n\t\tpublic void RemoveSegmentEnd(ushort segmentId, bool startNode) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || segmentId == GlobalConfig.Instance.Debug.SegmentId);\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"SegmentEndManager.RemoveSegmentEnd({segmentId}, {startNode}) called\");\n\t\t\t}\n#endif\n\t\t\tDestroySegmentEnd(GetIndex(segmentId, startNode));\n\t\t}\n\n\t\tpublic void RemoveSegmentEnds(ushort segmentId) {\n\t\t\tRemoveSegmentEnd(segmentId, true);\n\t\t\tRemoveSegmentEnd(segmentId, false);\n\t\t}\n\n\t\tpublic bool UpdateSegmentEnd(ISegmentEndId endId) {\n\t\t\treturn UpdateSegmentEnd(endId.SegmentId, endId.StartNode);\n\t\t}\n\n\t\tpublic bool UpdateSegmentEnd(ushort segmentId, bool startNode) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || segmentId == GlobalConfig.Instance.Debug.SegmentId);\n#endif\n\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeo == null) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"SegmentEndManager.UpdateSegmentEnd({segmentId}, {startNode}): Segment {segmentId} is invalid. Removing all segment ends.\");\n\t\t\t\t}\n#endif\n\t\t\t\tRemoveSegmentEnds(segmentId);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentEndGeometry endGeo = segGeo.GetEnd(startNode);\n\t\t\tif (endGeo == null) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"SegmentEndManager.UpdateSegmentEnd({segmentId}, {startNode}): Segment end {segmentId} @ {startNode} is invalid. Removing segment end.\");\n\t\t\t\t}\n#endif\n\t\t\t\tRemoveSegmentEnd(segmentId, startNode);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (TrafficPriorityManager.Instance.HasSegmentPrioritySign(segmentId, startNode) ||\n\t\t\t\tTrafficLightSimulationManager.Instance.HasTimedSimulation(endGeo.NodeId())) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"SegmentEndManager.UpdateSegmentEnd({segmentId}, {startNode}): Segment {segmentId} @ {startNode} has timed light or priority sign. Adding segment end {segmentId} @ {startNode}\");\n\t\t\t\t}\n#endif\n\t\t\t\t\tISegmentEnd end = GetOrAddSegmentEnd(segmentId, startNode);\n\t\t\t\tif (end == null) {\n\t\t\t\t\tLog.Warning($\"SegmentEndManager.UpdateSegmentEnd({segmentId}, {startNode}): Failed to add segment end.\");\n\t\t\t\t\treturn false;\n\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"SegmentEndManager.UpdateSegmentEnd({segmentId}, {startNode}): Added segment end. Updating now.\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tend.Update();\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"SegmentEndManager.UpdateSegmentEnd({segmentId}, {startNode}): Update of segment end finished.\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"SegmentEndManager.UpdateSegmentEnd({segmentId}, {startNode}): Segment {segmentId} @ {startNode} neither has timed light nor priority sign. Removing segment end {segmentId} @ {startNode}\");\n\t\t\t\t}\n#endif\n\t\t\t\tRemoveSegmentEnd(segmentId, startNode);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tprivate int GetIndex(ushort segmentId, bool startNode) {\n\t\t\treturn (int)segmentId + (startNode ? 0 : NetManager.MAX_SEGMENT_COUNT);\n\t\t}\n\n\t\t/*protected override void HandleInvalidSegment(SegmentGeometry geometry) {\n\t\t\tRemoveSegmentEnds(geometry.SegmentId);\n\t\t}\n\n\t\tprotected override void HandleValidSegment(SegmentGeometry geometry) {\n\t\t\tRemoveSegmentEnds(geometry.SegmentId);\n\t\t}*/\n\n\t\tprotected void DestroySegmentEnd(int index) {\n#if DEBUG\n\t\t\t//Log._Debug($\"SegmentEndManager.DestroySegmentEnd({index}) called\");\n#endif\n\t\t\tSegmentEnds[index]?.Destroy();\n\t\t\tSegmentEnds[index] = null;\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tfor (int i = 0; i < SegmentEnds.Length; ++i) {\n\t\t\t\tDestroySegmentEnd(i);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/SpeedLimitManager.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Util;\nusing UnityEngine;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class SpeedLimitManager : AbstractGeometryObservingManager, ICustomDataManager<List<Configuration.LaneSpeedLimit>>, ICustomDataManager<Dictionary<string, float>>, ISpeedLimitManager {\n\t\tpublic const NetInfo.LaneType LANE_TYPES = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;\n\t\tpublic const VehicleInfo.VehicleType VEHICLE_TYPES = VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Monorail;\n\n\t\tpublic const float MAX_SPEED = 10f * 2f; // 1000 km/h\n\t\tprivate Dictionary<string, float[]> vanillaLaneSpeedLimitsByNetInfoName; // For each NetInfo (by name) and lane index: game default speed limit\n\t\tprivate Dictionary<string, List<string>> childNetInfoNamesByCustomizableNetInfoName; // For each NetInfo (by name): Parent NetInfo (name)\n\t\tprivate List<NetInfo> customizableNetInfos;\n\n\t\tinternal Dictionary<string, int> CustomLaneSpeedLimitIndexByNetInfoName; // For each NetInfo (by name) and lane index: custom speed limit index\n\t\tinternal Dictionary<string, NetInfo> NetInfoByName; // For each name: NetInfo\n\n\t\tpublic static readonly SpeedLimitManager Instance = new SpeedLimitManager();\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"- Not implemented -\");\n\t\t\t// TODO implement\n\t\t}\n\n\t\tpublic readonly List<ushort> AvailableSpeedLimits;\n\n\t\tprivate SpeedLimitManager() {\n\t\t\tAvailableSpeedLimits = new List<ushort>();\n\t\t\tAvailableSpeedLimits.Add(10);\n\t\t\tAvailableSpeedLimits.Add(20);\n\t\t\tAvailableSpeedLimits.Add(30);\n\t\t\tAvailableSpeedLimits.Add(40);\n\t\t\tAvailableSpeedLimits.Add(50);\n\t\t\tAvailableSpeedLimits.Add(60);\n\t\t\tAvailableSpeedLimits.Add(70);\n\t\t\tAvailableSpeedLimits.Add(80);\n\t\t\tAvailableSpeedLimits.Add(90);\n\t\t\tAvailableSpeedLimits.Add(100);\n\t\t\tAvailableSpeedLimits.Add(110);\n\t\t\tAvailableSpeedLimits.Add(120);\n\t\t\tAvailableSpeedLimits.Add(130);\n\t\t\tAvailableSpeedLimits.Add(0);\n\n\t\t\tvanillaLaneSpeedLimitsByNetInfoName = new Dictionary<string, float[]>();\n\t\t\tCustomLaneSpeedLimitIndexByNetInfoName = new Dictionary<string, int>();\n\t\t\tcustomizableNetInfos = new List<NetInfo>();\n\t\t\tchildNetInfoNamesByCustomizableNetInfoName = new Dictionary<string, List<string>>();\n\t\t\tNetInfoByName = new Dictionary<string, NetInfo>();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if custom speed limits may be assigned to the given segment.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"segment\"></param>\n\t\t/// <returns></returns>\n\t\tpublic bool MayHaveCustomSpeedLimits(ushort segmentId, ref NetSegment segment) {\n\t\t\tif ((segment.m_flags & NetSegment.Flags.Created) == NetSegment.Flags.None)\n\t\t\t\treturn false;\n\t\t\tItemClass connectionClass = segment.Info.GetConnectionClass();\n\t\t\treturn (connectionClass.m_service == ItemClass.Service.Road ||\n\t\t\t\t(connectionClass.m_service == ItemClass.Service.PublicTransport && (connectionClass.m_subService == ItemClass.SubService.PublicTransportTrain || connectionClass.m_subService == ItemClass.SubService.PublicTransportTram || connectionClass.m_subService == ItemClass.SubService.PublicTransportMetro || connectionClass.m_subService == ItemClass.SubService.PublicTransportMonorail)));\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if custom speed limits may be assigned to the given lane info\n\t\t/// </summary>\n\t\t/// <param name=\"laneInfo\"></param>\n\t\t/// <returns></returns>\n\t\tpublic bool MayHaveCustomSpeedLimits(NetInfo.Lane laneInfo) {\n\t\t\treturn (laneInfo.m_laneType & LANE_TYPES) != NetInfo.LaneType.None &&\n\t\t\t\t\t(laneInfo.m_vehicleType & VEHICLE_TYPES) != VehicleInfo.VehicleType.None;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the currently set speed limit for the given segment and lane direction in terms of discrete speed limit levels.\n\t\t/// An in-game speed limit of 2.0 (e.g. on highway) is hereby translated into a discrete speed limit value of 100 (km/h).\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"finalDir\"></param>\n\t\t/// <returns></returns>\n\t\tpublic ushort GetCustomSpeedLimit(ushort segmentId, NetInfo.Direction finalDir) {\n\t\t\t// calculate the currently set mean speed limit\n\t\t\tif (segmentId == 0 || (Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Created) == NetSegment.Flags.None) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tvar segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\t\t\tuint curLaneId = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tint laneIndex = 0;\n\t\t\tfloat meanSpeedLimit = 0f;\n\t\t\tuint validLanes = 0;\n\t\t\twhile (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) {\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\tNetInfo.Direction d = laneInfo.m_finalDirection;\n\t\t\t\tif (d != finalDir)\n\t\t\t\t\tgoto nextIter;\n\t\t\t\tif (!MayHaveCustomSpeedLimits(laneInfo))\n\t\t\t\t\tgoto nextIter;\n\n\t\t\t\tushort? setSpeedLimit = Flags.getLaneSpeedLimit(curLaneId);\n\t\t\t\tif (setSpeedLimit != null)\n\t\t\t\t\tmeanSpeedLimit += ToGameSpeedLimit((ushort)setSpeedLimit); // custom speed limit\n\t\t\t\telse\n\t\t\t\t\tmeanSpeedLimit += laneInfo.m_speedLimit; // game default\n\t\t\t\t++validLanes;\n\n\t\t\t\tnextIter:\n\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\tlaneIndex++;\n\t\t\t}\n\n\t\t\tif (validLanes > 0)\n\t\t\t\tmeanSpeedLimit /= (float)validLanes;\n\t\t\tushort ret = LaneToCustomSpeedLimit(meanSpeedLimit);\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the average default speed limit for a given NetInfo object in terms of discrete speed limit levels.\n\t\t/// An in-game speed limit of 2.0 (e.g. on highway) is hereby translated into a discrete speed limit value of 100 (km/h).\n\t\t/// </summary>\n\t\t/// <param name=\"segmentInfo\"></param>\n\t\t/// <param name=\"finalDir\"></param>\n\t\t/// <returns></returns>\n\t\tpublic ushort GetAverageDefaultCustomSpeedLimit(NetInfo segmentInfo, NetInfo.Direction? finalDir=null) {\n\t\t\tfloat meanSpeedLimit = 0f;\n\t\t\tuint validLanes = 0;\n\t\t\tfor (int i = 0; i < segmentInfo.m_lanes.Length; ++i) {\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[i];\n\t\t\t\tNetInfo.Direction d = laneInfo.m_finalDirection;\n\t\t\t\tif (finalDir != null && d != finalDir)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!MayHaveCustomSpeedLimits(laneInfo))\n\t\t\t\t\tcontinue;\n\t\t\t\t\n\t\t\t\tmeanSpeedLimit += laneInfo.m_speedLimit;\n\t\t\t\t++validLanes;\n\t\t\t}\n\n\t\t\tif (validLanes > 0)\n\t\t\t\tmeanSpeedLimit /= (float)validLanes;\n\t\t\tushort ret = LaneToCustomSpeedLimit(meanSpeedLimit);\n\t\t\treturn ret;\n\t\t}\n\n        /// <summary>\n\t\t/// Determines the average custom speed limit for a given NetInfo object in terms of discrete speed limit levels.\n\t\t/// An in-game speed limit of 2.0 (e.g. on highway) is hereby translated into a discrete speed limit value of 100 (km/h).\n\t\t/// </summary>\n\t\t/// <param name=\"segmentInfo\"></param>\n\t\t/// <param name=\"finalDir\"></param>\n\t\t/// <returns></returns>\n\t\tpublic ushort GetAverageCustomSpeedLimit(ushort segmentId, ref NetSegment segment, NetInfo segmentInfo, NetInfo.Direction? finalDir = null) {\n            // calculate the currently set mean speed limit\n            float meanSpeedLimit = 0f;\n            uint validLanes = 0;\n            uint curLaneId = segment.m_lanes;\n            for (byte laneIndex = 0; laneIndex < segmentInfo.m_lanes.Length; ++laneIndex) {\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\tNetInfo.Direction d = laneInfo.m_finalDirection;\n\t\t\t\tif (finalDir != null && d != finalDir)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!MayHaveCustomSpeedLimits(laneInfo))\n\t\t\t\t\tcontinue;\n\n                meanSpeedLimit += GetLockFreeGameSpeedLimit(segmentId, laneIndex, curLaneId, laneInfo);\n                curLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n                ++validLanes;\n            }\n\n            if (validLanes > 0)\n                meanSpeedLimit /= (float)validLanes;\n            return (ushort)Mathf.Round(meanSpeedLimit);\n        }\n\n        /// <summary>\n        /// Determines the currently set speed limit for the given lane in terms of discrete speed limit levels.\n        /// An in-game speed limit of 2.0 (e.g. on highway) is hereby translated into a discrete speed limit value of 100 (km/h).\n        /// </summary>\n        /// <param name=\"laneId\"></param>\n        /// <returns></returns>\n        public ushort GetCustomSpeedLimit(uint laneId) {\n\t\t\t// check custom speed limit\n\t\t\tushort? setSpeedLimit = Flags.getLaneSpeedLimit(laneId);\n\t\t\tif (setSpeedLimit != null) {\n\t\t\t\treturn (ushort)setSpeedLimit;\n\t\t\t}\n\n\t\t\t// check default speed limit\n\t\t\tushort segmentId = Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_segment;\n\t\t\tif (!MayHaveCustomSpeedLimits(segmentId, ref Singleton<NetManager>.instance.m_segments.m_buffer[segmentId])) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t\n\t\t\tvar segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\n\t\t\tuint curLaneId = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tint laneIndex = 0;\n\t\t\twhile (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) {\n\t\t\t\tif (curLaneId == laneId) {\n\t\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\t\tif (!MayHaveCustomSpeedLimits(laneInfo))\n\t\t\t\t\t\treturn 0;\n\n\t\t\t\t\tushort ret = LaneToCustomSpeedLimit(laneInfo.m_speedLimit);\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\n\t\t\t\tlaneIndex++;\n\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t}\n\n\t\t\tLog.Warning($\"Speed limit for lane {laneId} could not be determined.\");\n\t\t\treturn 0; // no speed limit found\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the currently set speed limit for the given lane in terms of game (floating point) speed limit levels\n\t\t/// </summary>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <returns></returns>\n\t\tpublic float GetGameSpeedLimit(uint laneId) {\n\t\t\treturn ToGameSpeedLimit(GetCustomSpeedLimit(laneId));\n\t\t}\n\n\t\tpublic float GetLockFreeGameSpeedLimit(ushort segmentId, byte laneIndex, uint laneId, NetInfo.Lane laneInfo) {\n\t\t\tif (! Options.customSpeedLimitsEnabled || ! MayHaveCustomSpeedLimits(laneInfo)) {\n\t\t\t\treturn laneInfo.m_speedLimit;\n\t\t\t}\n\n\t\t\tfloat speedLimit = 0;\n\t\t\tushort?[] fastArray = Flags.laneSpeedLimitArray[segmentId];\n\t\t\tif (fastArray != null && fastArray.Length > laneIndex && fastArray[laneIndex] != null) {\n\t\t\t\tspeedLimit = ToGameSpeedLimit((ushort)fastArray[laneIndex]);\n\t\t\t} else {\n\t\t\t\tspeedLimit = laneInfo.m_speedLimit;\n\t\t\t}\n\t\t\treturn speedLimit;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Converts a custom speed limit to a game speed limit.\n\t\t/// </summary>\n\t\t/// <param name=\"customSpeedLimit\"></param>\n\t\t/// <returns></returns>\n\t\tpublic float ToGameSpeedLimit(ushort customSpeedLimit) {\n\t\t\tif (customSpeedLimit == 0)\n\t\t\t\treturn MAX_SPEED;\n\t\t\treturn (float)customSpeedLimit / 50f;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Converts a lane speed limit to a custom speed limit.\n\t\t/// </summary>\n\t\t/// <param name=\"laneSpeedLimit\"></param>\n\t\t/// <returns></returns>\n\t\tpublic ushort LaneToCustomSpeedLimit(float laneSpeedLimit, bool roundToSignLimits=true) {\n\t\t\tlaneSpeedLimit /= 2f; // 1 == 100 km/h\n\n\t\t\tif (! roundToSignLimits) {\n\t\t\t\treturn (ushort)Mathf.Round(laneSpeedLimit * 100f);\n\t\t\t}\n\n\t\t\t// translate the floating point speed limit into our discrete version\n\t\t\tushort speedLimit = 0;\n\t\t\tif (laneSpeedLimit < 0.15f)\n\t\t\t\tspeedLimit = 10;\n\t\t\telse if (laneSpeedLimit < 1.35f)\n\t\t\t\tspeedLimit = (ushort)((ushort)Mathf.Round(laneSpeedLimit * 10f) * 10u);\n\n\t\t\treturn speedLimit;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Explicitly stores currently set speed limits for all segments of the specified NetInfo\n\t\t/// </summary>\n\t\t/// <param name=\"info\"></param>\n\t\tpublic void FixCurrentSpeedLimits(NetInfo info) {\n\t\t\tif (info == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.FixCurrentSpeedLimits: info is null!\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (info.name == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.FixCurrentSpeedLimits: info.name is null!\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!customizableNetInfos.Contains(info))\n\t\t\t\treturn;\n\n\t\t\tfor (uint laneId = 1; laneId < NetManager.MAX_LANE_COUNT; ++laneId) {\n\t\t\t\tif (!Services.NetService.IsLaneValid(laneId))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tushort segmentId = Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_segment;\n\t\t\t\tNetInfo laneInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\t\t\t\tif (laneInfo.name != info.name && (!childNetInfoNamesByCustomizableNetInfoName.ContainsKey(info.name) || !childNetInfoNamesByCustomizableNetInfoName[info.name].Contains(laneInfo.name)))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tFlags.setLaneSpeedLimit(laneId, GetCustomSpeedLimit(laneId));\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Explicitly clear currently set speed limits for all segments of the specified NetInfo\n\t\t/// </summary>\n\t\t/// <param name=\"info\"></param>\n\t\tpublic void ClearCurrentSpeedLimits(NetInfo info) {\n\t\t\tif (info == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.ClearCurrentSpeedLimits: info is null!\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (info.name == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.ClearCurrentSpeedLimits: info.name is null!\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!customizableNetInfos.Contains(info))\n\t\t\t\treturn;\n\n\t\t\tfor (uint laneId = 1; laneId < NetManager.MAX_LANE_COUNT; ++laneId) {\n\t\t\t\tif (!Services.NetService.IsLaneValid(laneId))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tNetInfo laneInfo = Singleton<NetManager>.instance.m_segments.m_buffer[Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_segment].Info;\n\t\t\t\tif (laneInfo.name != info.name && (!childNetInfoNamesByCustomizableNetInfoName.ContainsKey(info.name) || !childNetInfoNamesByCustomizableNetInfoName[info.name].Contains(laneInfo.name)))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tFlags.removeLaneSpeedLimit(laneId);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the game default speed limit of the given NetInfo.\n\t\t/// </summary>\n\t\t/// <param name=\"info\">the NetInfo of which the game default speed limit should be determined</param>\n\t\t/// <param name=\"roundToSignLimits\">if true, custom speed limit are rounded to speed limits available as speed limit sign</param>\n\t\t/// <returns></returns>\n\t\tpublic ushort GetVanillaNetInfoSpeedLimit(NetInfo info, bool roundToSignLimits = true) {\n\t\t\tif (info == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.GetVanillaNetInfoSpeedLimit: info is null!\");\n#endif\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tif (info.m_netAI == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.GetVanillaNetInfoSpeedLimit: info.m_netAI is null!\");\n#endif\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tif (info.name == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.GetVanillaNetInfoSpeedLimit: info.name is null!\");\n#endif\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t/*if (! (info.m_netAI is RoadBaseAI))\n\t\t\t\treturn 0;*/\n\n\t\t\t//string infoName = ((RoadBaseAI)info.m_netAI).m_info.name;\n\t\t\tstring infoName = info.name;\n\t\t\tfloat[] vanillaSpeedLimits;\n\t\t\tif (!vanillaLaneSpeedLimitsByNetInfoName.TryGetValue(infoName, out vanillaSpeedLimits)) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tfloat? maxSpeedLimit = null;\n\t\t\tforeach (float speedLimit in vanillaSpeedLimits) {\n\t\t\t\tif (maxSpeedLimit == null || speedLimit > maxSpeedLimit) {\n\t\t\t\t\tmaxSpeedLimit = speedLimit;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (maxSpeedLimit == null)\n\t\t\t\treturn 0;\n\n\t\t\treturn LaneToCustomSpeedLimit((float)maxSpeedLimit, roundToSignLimits);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the custom speed limit of the given NetInfo.\n\t\t/// </summary>\n\t\t/// <param name=\"info\">the NetInfo of which the custom speed limit should be determined</param>\n\t\t/// <returns></returns>\n\t\tpublic int GetCustomNetInfoSpeedLimitIndex(NetInfo info) {\n\t\t\tif (info == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.SetCustomNetInfoSpeedLimitIndex: info is null!\");\n#endif\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tif (info.name == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.SetCustomNetInfoSpeedLimitIndex: info.name is null!\");\n#endif\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\t/*if (!(info.m_netAI is RoadBaseAI))\n\t\t\t\treturn -1;*/\n\n\t\t\t//string infoName = ((RoadBaseAI)info.m_netAI).m_info.name;\n\t\t\tstring infoName = info.name;\n\t\t\tint speedLimitIndex;\n\t\t\tif (!CustomLaneSpeedLimitIndexByNetInfoName.TryGetValue(infoName, out speedLimitIndex)) {\n\t\t\t\treturn AvailableSpeedLimits.IndexOf(GetVanillaNetInfoSpeedLimit(info, true));\n\t\t\t}\n\n\t\t\treturn speedLimitIndex;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets the custom speed limit of the given NetInfo.\n\t\t/// </summary>\n\t\t/// <param name=\"info\">the NetInfo for which the custom speed limit should be set</param>\n\t\t/// <returns></returns>\n\t\tpublic void SetCustomNetInfoSpeedLimitIndex(NetInfo info, int customSpeedLimitIndex) {\n\t\t\tif (info == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SetCustomNetInfoSpeedLimitIndex: info is null!\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (info.name == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SetCustomNetInfoSpeedLimitIndex: info.name is null!\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t/*if (!(info.m_netAI is RoadBaseAI))\n\t\t\t\treturn;*/\n\n\t\t\t/*RoadBaseAI baseAI = (RoadBaseAI)info.m_netAI;\n\t\t\tstring infoName = baseAI.m_info.name;*/\n\t\t\tstring infoName = info.name;\n\t\t\tCustomLaneSpeedLimitIndexByNetInfoName[infoName] = customSpeedLimitIndex;\n\n\t\t\tfloat gameSpeedLimit = ToGameSpeedLimit(AvailableSpeedLimits[customSpeedLimitIndex]);\n\n\t\t\t// save speed limit in all NetInfos\n\t\t\tLog._Debug($\"Updating parent NetInfo {infoName}: Setting speed limit to {gameSpeedLimit}\");\n\t\t\tUpdateNetInfoGameSpeedLimit(info, gameSpeedLimit);\n\n\t\t\tList<string> childNetInfoNames;\n\t\t\tif (childNetInfoNamesByCustomizableNetInfoName.TryGetValue(infoName, out childNetInfoNames)) {\n\t\t\t\tforeach (string childNetInfoName in childNetInfoNames) {\n\t\t\t\t\tNetInfo childNetInfo;\n\t\t\t\t\tif (NetInfoByName.TryGetValue(childNetInfoName, out childNetInfo)) {\n\t\t\t\t\t\tLog._Debug($\"Updating child NetInfo {childNetInfoName}: Setting speed limit to {gameSpeedLimit}\");\n\t\t\t\t\t\tCustomLaneSpeedLimitIndexByNetInfoName[childNetInfoName] = customSpeedLimitIndex;\n\t\t\t\t\t\tUpdateNetInfoGameSpeedLimit(childNetInfo, gameSpeedLimit);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void UpdateNetInfoGameSpeedLimit(NetInfo info, float gameSpeedLimit) {\n\t\t\tif (info == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.UpdateNetInfoGameSpeedLimit: info is null!\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (info.name == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.UpdateNetInfoGameSpeedLimit: info.name is null!\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (info.m_lanes == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.UpdateNetInfoGameSpeedLimit: info.name is null!\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLog._Debug($\"Updating speed limit of NetInfo {info.name} to {gameSpeedLimit}\");\n\n\t\t\tforeach (NetInfo.Lane lane in info.m_lanes) {\n\t\t\t\t// TODO refactor check\n\t\t\t\tif ((lane.m_vehicleType & VEHICLE_TYPES) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\tlane.m_speedLimit = gameSpeedLimit;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Converts a vehicle's velocity to a custom speed.\n\t\t/// </summary>\n\t\t/// <param name=\"vehicleSpeed\"></param>\n\t\t/// <returns></returns>\n\t\tpublic ushort VehicleToCustomSpeed(float vehicleSpeed) {\n\t\t\treturn LaneToCustomSpeedLimit(vehicleSpeed / 8f, false);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets the speed limit of a given lane.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"laneIndex\"></param>\n\t\t/// <param name=\"laneInfo\"></param>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <param name=\"speedLimit\"></param>\n\t\t/// <returns></returns>\n\t\tpublic bool SetSpeedLimit(ushort segmentId, uint laneIndex, NetInfo.Lane laneInfo, uint laneId, ushort speedLimit) {\n\t\t\tif (!MayHaveCustomSpeedLimits(laneInfo)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!AvailableSpeedLimits.Contains(speedLimit)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!Services.NetService.IsLaneValid(laneId)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tFlags.setLaneSpeedLimit(segmentId, laneIndex, laneId, speedLimit);\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets the speed limit of a given segment and lane direction.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"finalDir\"></param>\n\t\t/// <param name=\"speedLimit\"></param>\n\t\t/// <returns></returns>\n\t\tpublic bool SetSpeedLimit(ushort segmentId, NetInfo.Direction finalDir, ushort speedLimit) {\n\t\t\tif (!MayHaveCustomSpeedLimits(segmentId, ref Singleton<NetManager>.instance.m_segments.m_buffer[segmentId])) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!AvailableSpeedLimits.Contains(speedLimit)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tNetInfo segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\n\t\t\tif (segmentInfo == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.SetSpeedLimit: info is null!\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (segmentInfo.m_lanes == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"SpeedLimitManager.SetSpeedLimit: info.name is null!\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tuint curLaneId = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tint laneIndex = 0;\n\t\t\twhile (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) {\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\tNetInfo.Direction d = laneInfo.m_finalDirection;\n\t\t\t\tif (d != finalDir)\n\t\t\t\t\tgoto nextIter;\n\t\t\t\tif (!MayHaveCustomSpeedLimits(laneInfo))\n\t\t\t\t\tgoto nextIter;\n\t\t\t\t\n#if DEBUG\n\t\t\t\tLog._Debug($\"SpeedLimitManager: Setting speed limit of lane {curLaneId} to {speedLimit}\");\n#endif\n\t\t\t\tFlags.setLaneSpeedLimit(curLaneId, speedLimit);\n\n\t\t\tnextIter:\n\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\tlaneIndex++;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic List<NetInfo> GetCustomizableNetInfos() {\n\t\t\treturn customizableNetInfos;\n\t\t}\n\n\t\tpublic override void OnBeforeLoadData() {\n\t\t\tbase.OnBeforeLoadData();\n\n\t\t\t// determine vanilla speed limits and customizable NetInfos\n\t\t\tSteamHelper.DLC_BitMask dlcMask = SteamHelper.GetOwnedDLCMask();\n\n\t\t\tint numLoaded = PrefabCollection<NetInfo>.LoadedCount();\n\n\t\t\tvanillaLaneSpeedLimitsByNetInfoName.Clear();\n\t\t\tcustomizableNetInfos.Clear();\n\t\t\tCustomLaneSpeedLimitIndexByNetInfoName.Clear();\n\t\t\tchildNetInfoNamesByCustomizableNetInfoName.Clear();\n\t\t\tNetInfoByName.Clear();\n\n\t\t\tList<NetInfo> mainNetInfos = new List<NetInfo>();\n\n\t\t\tLog.Info($\"SpeedLimitManager.OnBeforeLoadData: {numLoaded} NetInfos loaded.\");\n\t\t\tfor (uint i = 0; i < numLoaded; ++i) {\n\t\t\t\tNetInfo info = PrefabCollection<NetInfo>.GetLoaded(i);\n\n\t\t\t\tif (info == null || info.m_netAI == null || !(info.m_netAI is RoadBaseAI || info.m_netAI is MetroTrackAI || info.m_netAI is TrainTrackBaseAI) || !(info.m_dlcRequired == 0 || (uint)(info.m_dlcRequired & dlcMask) != 0u)) {\n\t\t\t\t\tif (info == null)\n\t\t\t\t\t\tLog.Warning($\"SpeedLimitManager.OnBeforeLoadData: NetInfo @ {i} is null!\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tstring infoName = info.name;\n\t\t\t\tif (infoName == null) {\n\t\t\t\t\tLog.Warning($\"SpeedLimitManager.OnBeforeLoadData: NetInfo name @ {i} is null!\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (!vanillaLaneSpeedLimitsByNetInfoName.ContainsKey(infoName)) {\n\t\t\t\t\tif (info.m_lanes == null) {\n\t\t\t\t\t\tLog.Warning($\"SpeedLimitManager.OnBeforeLoadData: NetInfo lanes @ {i} is null!\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tLog.Info($\"Loaded road NetInfo: {infoName}\");\n\t\t\t\t\tNetInfoByName[infoName] = info;\n\t\t\t\t\tmainNetInfos.Add(info);\n\n\t\t\t\t\tfloat[] vanillaLaneSpeedLimits = new float[info.m_lanes.Length];\n\t\t\t\t\tfor (int k = 0; k < info.m_lanes.Length; ++k) {\n\t\t\t\t\t\tvanillaLaneSpeedLimits[k] = info.m_lanes[k].m_speedLimit;\n\t\t\t\t\t}\n\t\t\t\t\tvanillaLaneSpeedLimitsByNetInfoName[infoName] = vanillaLaneSpeedLimits;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmainNetInfos.Sort(delegate(NetInfo a, NetInfo b) {\n\t\t\t\tbool aRoad = a.m_netAI is RoadBaseAI;\n\t\t\t\tbool bRoad = b.m_netAI is RoadBaseAI;\n\n\t\t\t\tif (aRoad != bRoad) {\n\t\t\t\t\tif (aRoad)\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\telse\n\t\t\t\t\t\treturn 1;\n\t\t\t\t}\n\n\t\t\t\tbool aTrain = a.m_netAI is TrainTrackBaseAI;\n\t\t\t\tbool bTrain = b.m_netAI is TrainTrackBaseAI;\n\n\t\t\t\tif (aTrain != bTrain) {\n\t\t\t\t\tif (aTrain)\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\telse\n\t\t\t\t\t\treturn -1;\n\t\t\t\t}\n\n\t\t\t\tbool aMetro = a.m_netAI is MetroTrackAI;\n\t\t\t\tbool bMetro = b.m_netAI is MetroTrackAI;\n\n\t\t\t\tif (aMetro != bMetro) {\n\t\t\t\t\tif (aMetro)\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\telse\n\t\t\t\t\t\treturn -1;\n\t\t\t\t}\n\n\t\t\t\tif (aRoad && bRoad) {\n\t\t\t\t\tbool aHighway = ((RoadBaseAI)a.m_netAI).m_highwayRules;\n\t\t\t\t\tbool bHighway = ((RoadBaseAI)b.m_netAI).m_highwayRules;\n\n\t\t\t\t\tif (aHighway != bHighway) {\n\t\t\t\t\t\tif (aHighway)\n\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tint aNumVehicleLanes = 0;\n\t\t\t\tforeach (NetInfo.Lane lane in a.m_lanes) {\n\t\t\t\t\tif ((lane.m_laneType & LANE_TYPES) != NetInfo.LaneType.None)\n\t\t\t\t\t\t++aNumVehicleLanes;\n\t\t\t\t}\n\n\t\t\t\tint bNumVehicleLanes = 0;\n\t\t\t\tforeach (NetInfo.Lane lane in b.m_lanes) {\n\t\t\t\t\tif ((lane.m_laneType & LANE_TYPES) != NetInfo.LaneType.None)\n\t\t\t\t\t\t++bNumVehicleLanes;\n\t\t\t\t}\n\n\t\t\t\tint res = aNumVehicleLanes.CompareTo(bNumVehicleLanes);\n\t\t\t\tif (res == 0) {\n\t\t\t\t\treturn a.name.CompareTo(b.name);\n\t\t\t\t} else {\n\t\t\t\t\treturn res;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// identify parent NetInfos\n\t\t\tint x = 0;\n\t\t\twhile (x < mainNetInfos.Count) {\n\t\t\t\tNetInfo info = mainNetInfos[x];\n\t\t\t\tstring infoName = info.name;\n\n\t\t\t\t// find parent with prefix name\n\n\t\t\t\tbool foundParent = false;\n\t\t\t\tfor (int y = 0; y < mainNetInfos.Count; ++y) {\n\t\t\t\t\tNetInfo parentInfo = mainNetInfos[y];\n\n\t\t\t\t\tif (info.m_placementStyle == ItemClass.Placement.Procedural && !infoName.Equals(parentInfo.name) && infoName.StartsWith(parentInfo.name)) {\n\t\t\t\t\t\tLog.Info($\"Identified child NetInfo {infoName} of parent {parentInfo.name}\");\n\t\t\t\t\t\tList<string> childNetInfoNames;\n\t\t\t\t\t\tif (!childNetInfoNamesByCustomizableNetInfoName.TryGetValue(parentInfo.name, out childNetInfoNames)) {\n\t\t\t\t\t\t\tchildNetInfoNamesByCustomizableNetInfoName[parentInfo.name] = childNetInfoNames = new List<string>();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tchildNetInfoNames.Add(info.name);\n\t\t\t\t\t\tNetInfoByName[infoName] = info;\n\t\t\t\t\t\tfoundParent = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (foundParent) {\n\t\t\t\t\tmainNetInfos.RemoveAt(x);\n\t\t\t\t} else {\n\t\t\t\t\t++x;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcustomizableNetInfos = mainNetInfos;\n\t\t}\n\n\t\tprotected override void HandleInvalidSegment(SegmentGeometry geometry) {\n\t\t\tNetInfo segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[geometry.SegmentId].Info;\n\t\t\tuint curLaneId = Singleton<NetManager>.instance.m_segments.m_buffer[geometry.SegmentId].m_lanes;\n\t\t\tint laneIndex = 0;\n\t\t\twhile (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) {\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\tushort? setSpeedLimit = Flags.getLaneSpeedLimit(curLaneId);\n\n\t\t\t\tFlags.setLaneSpeedLimit(curLaneId, null);\n\n\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\tlaneIndex++;\n\t\t\t}\n\t\t}\n\n\t\tprotected override void HandleValidSegment(SegmentGeometry geometry) {\n\n\t\t}\n\n\t\tpublic bool LoadData(List<Configuration.LaneSpeedLimit> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading lane speed limit data. {data.Count} elements\");\n\t\t\tforeach (Configuration.LaneSpeedLimit laneSpeedLimit in data) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!Services.NetService.IsLaneValid(laneSpeedLimit.laneId)) {\n\t\t\t\t\t\tLog._Debug($\"SpeedLimitManager.LoadData: Skipping lane {laneSpeedLimit.laneId}: Lane is invalid\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tushort segmentId = Singleton<NetManager>.instance.m_lanes.m_buffer[laneSpeedLimit.laneId].m_segment;\n\t\t\t\t\tNetInfo info = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\t\t\t\t\tint customSpeedLimitIndex = GetCustomNetInfoSpeedLimitIndex(info);\n\t\t\t\t\tLog._Debug($\"SpeedLimitManager.LoadData: Handling lane {laneSpeedLimit.laneId}: Custom speed limit index of segment {segmentId} info ({info}, name={info?.name}, lanes={info?.m_lanes} is {customSpeedLimitIndex}\");\n\t\t\t\t\tif (customSpeedLimitIndex < 0 || AvailableSpeedLimits[customSpeedLimitIndex] != laneSpeedLimit.speedLimit) {\n\t\t\t\t\t\t// lane speed limit differs from default speed limit\n\t\t\t\t\t\tLog._Debug($\"SpeedLimitManager.LoadData: Loading lane speed limit: lane {laneSpeedLimit.laneId} = {laneSpeedLimit.speedLimit}\");\n\t\t\t\t\t\tFlags.setLaneSpeedLimit(laneSpeedLimit.laneId, laneSpeedLimit.speedLimit);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tLog._Debug($\"SpeedLimitManager.LoadData: Skipping lane speed limit of lane {laneSpeedLimit.laneId} ({laneSpeedLimit.speedLimit})\");\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore, as it's probably corrupt save data. it'll be culled on next save\n\t\t\t\t\tLog.Warning(\"SpeedLimitManager.LoadData: Error loading speed limits: \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\tList<Configuration.LaneSpeedLimit> ICustomDataManager<List<Configuration.LaneSpeedLimit>>.SaveData(ref bool success) {\n\t\t\tList<Configuration.LaneSpeedLimit> ret = new List<Configuration.LaneSpeedLimit>();\n\t\t\tforeach (KeyValuePair<uint, ushort> e in Flags.getAllLaneSpeedLimits()) {\n\t\t\t\ttry {\n\t\t\t\t\tConfiguration.LaneSpeedLimit laneSpeedLimit = new Configuration.LaneSpeedLimit(e.Key, e.Value);\n\t\t\t\t\tLog._Debug($\"Saving speed limit of lane {laneSpeedLimit.laneId}: {laneSpeedLimit.speedLimit}\");\n\t\t\t\t\tret.Add(laneSpeedLimit);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tLog.Error($\"Exception occurred while saving lane speed limit @ {e.Key}: {ex.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool LoadData(Dictionary<string, float> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading custom default speed limit data. {data.Count} elements\");\n\t\t\tforeach (KeyValuePair<string, float> e in data) {\n\t\t\t\tNetInfo netInfo = null;\n\t\t\t\tif (!NetInfoByName.TryGetValue(e.Key, out netInfo))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tushort customSpeedLimit = LaneToCustomSpeedLimit(e.Value, true);\n\t\t\t\tint customSpeedLimitIndex = AvailableSpeedLimits.IndexOf(customSpeedLimit);\n\t\t\t\tif (customSpeedLimitIndex >= 0) {\n\t\t\t\t\tSetCustomNetInfoSpeedLimitIndex(netInfo, customSpeedLimitIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\tDictionary<string, float> ICustomDataManager<Dictionary<string, float>>.SaveData(ref bool success) {\n\t\t\tDictionary<string, float> ret = new Dictionary<string, float>();\n\t\t\tforeach (KeyValuePair<string, int> e in CustomLaneSpeedLimitIndexByNetInfoName) {\n\t\t\t\ttry {\n\t\t\t\t\tushort customSpeedLimit = AvailableSpeedLimits[e.Value];\n\t\t\t\t\tfloat gameSpeedLimit = ToGameSpeedLimit(customSpeedLimit);\n\n\t\t\t\t\tret.Add(e.Key, gameSpeedLimit);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tLog.Error($\"Exception occurred while saving custom default speed limits @ {e.Key}: {ex.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n#if DEBUG\n\t\t/*public Dictionary<NetInfo, ushort> GetDefaultSpeedLimits() {\n\t\t\tDictionary<NetInfo, ushort> ret = new Dictionary<NetInfo, ushort>();\n\t\t\tint numLoaded = PrefabCollection<NetInfo>.LoadedCount();\n\t\t\tfor (uint i = 0; i < numLoaded; ++i) {\n\t\t\t\tNetInfo info = PrefabCollection<NetInfo>.GetLoaded(i);\n\t\t\t\tushort defaultSpeedLimit = GetAverageDefaultCustomSpeedLimit(info, NetInfo.Direction.Forward);\n\t\t\t\tret.Add(info, defaultSpeedLimit);\n\t\t\t\tLog._Debug($\"Loaded NetInfo: {info.name}, placementStyle={info.m_placementStyle}, availableIn={info.m_availableIn}, thumbnail={info.m_Thumbnail} connectionClass.service: {info.GetConnectionClass().m_service.ToString()}, connectionClass.subService: {info.GetConnectionClass().m_subService.ToString()}, avg. default speed limit: {defaultSpeedLimit}\");\n\t\t\t}\n\t\t\treturn ret;\n\t\t}*/\n#endif\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/TrafficLightManager.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.State;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Util;\n\nnamespace TrafficManager.Manager.Impl {\n\t/// <summary>\n\t/// Manages traffic light toggling\n\t/// </summary>\n\tpublic class TrafficLightManager : AbstractCustomManager, ICustomDataManager<List<Configuration.NodeTrafficLight>>, ICustomDataManager<string>, ITrafficLightManager {\n\t\tpublic static readonly TrafficLightManager Instance = new TrafficLightManager();\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"- Not implemented -\");\n\t\t\t// TODO implement\n\t\t}\n\n\t\tpublic bool SetTrafficLight(ushort nodeId, bool flag, ref NetNode node) {\n\t\t\tUnableReason reason;\n\t\t\treturn SetTrafficLight(nodeId, flag, ref node, out reason);\n\t\t}\n\n\t\tpublic bool SetTrafficLight(ushort nodeId, bool flag, ref NetNode node, out UnableReason reason) {\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == nodeId)\n\t\t\t\tLog._Debug($\"TrafficLightManager.SetTrafficLight: called for node {nodeId}, flag={flag}\");\n#endif\n\t\t\tif (! IsTrafficLightToggleable(nodeId, flag, ref node, out reason)) {\n#if DEBUGTTL\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == nodeId)\n\t\t\t\t\tLog._Debug($\"TrafficLightManager.SetTrafficLight: Traffic light @ {nodeId} is not toggleable\");\n#endif\n\t\t\t\tif (reason != UnableReason.HasTimedLight || !flag) {\n#if DEBUGTTL\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == nodeId)\n\t\t\t\t\t\tLog._Debug($\"TrafficLightManager.SetTrafficLight: ... but has timed light and we want to enable it\");\n#endif\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tNetNode.Flags flags = node.m_flags | NetNode.Flags.CustomTrafficLights;\n\t\t\tif ((bool)flag) {\n#if DEBUGTTL\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == nodeId)\n\t\t\t\t\tLog._Debug($\"Adding traffic light @ node {nodeId}\");\n#endif\n\t\t\t\tflags |= NetNode.Flags.TrafficLights;\n\t\t\t\tTrafficPriorityManager.Instance.RemovePrioritySignsFromNode(nodeId);\n\t\t\t} else {\n#if DEBUGTTL\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == nodeId)\n\t\t\t\t\tLog._Debug($\"Removing traffic light @ node {nodeId}\");\n#endif\n\t\t\t\tflags &= ~NetNode.Flags.TrafficLights;\n\t\t\t}\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == nodeId)\n\t\t\t\tLog._Debug($\"TrafficLightManager.SetTrafficLight: Setting traffic light at node {nodeId} -- flags={flags}\");\n#endif\n\t\t\tnode.m_flags = flags;\n\t\t\tConstants.ManagerFactory.GeometryManager.MarkAsUpdated(NodeGeometry.Get(nodeId), true);\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool AddTrafficLight(ushort nodeId, ref NetNode node) {\n\t\t\tUnableReason reason;\n\t\t\treturn AddTrafficLight(nodeId, ref node, out reason);\n\t\t}\n\n\t\tpublic bool AddTrafficLight(ushort nodeId, ref NetNode node, out UnableReason reason) {\n\t\t\tTrafficPriorityManager.Instance.RemovePrioritySignsFromNode(nodeId);\n\t\t\treturn SetTrafficLight(nodeId, true, ref node, out reason);\n\t\t}\n\n\t\tpublic bool RemoveTrafficLight(ushort nodeId, ref NetNode node) {\n\t\t\tUnableReason reason;\n\t\t\treturn RemoveTrafficLight(nodeId, ref node, out reason);\n\t\t}\n\n\t\tpublic bool RemoveTrafficLight(ushort nodeId, ref NetNode node, out UnableReason reason) {\n\t\t\treturn SetTrafficLight(nodeId, false, ref node, out reason);\n\t\t}\n\n\t\tpublic bool ToggleTrafficLight(ushort nodeId, ref NetNode node) {\n\t\t\treturn SetTrafficLight(nodeId, !HasTrafficLight(nodeId, ref node), ref node);\n\t\t}\n\n\t\tpublic bool ToggleTrafficLight(ushort nodeId, ref NetNode node, out UnableReason reason) {\n\t\t\treturn SetTrafficLight(nodeId, !HasTrafficLight(nodeId, ref node), ref node, out reason);\n\t\t}\n\n\t\tpublic bool IsTrafficLightToggleable(ushort nodeId, bool flag, ref NetNode node, out UnableReason reason) {\n\t\t\tif (!flag && TrafficLightSimulationManager.Instance.HasTimedSimulation(nodeId)) {\n\t\t\t\treason = UnableReason.HasTimedLight;\n#if DEBUGTTL\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == nodeId)\n\t\t\t\t\tLog._Debug($\"Cannot toggle traffic lights at node {nodeId}: Node has a timed traffic light\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (flag && !LogicUtil.CheckFlags((uint)node.m_flags, (uint)(NetNode.Flags.Created | NetNode.Flags.Deleted | NetNode.Flags.Junction), (uint)(NetNode.Flags.Created | NetNode.Flags.Junction))) {\n\t\t\t\treason = UnableReason.NoJunction;\n#if DEBUGTTL\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == nodeId)\n\t\t\t\t\tLog._Debug($\"Cannot toggle traffic lights at node {nodeId}: Node is not a junction\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (!flag && LogicUtil.CheckFlags((uint)node.m_flags, (uint)(NetNode.Flags.LevelCrossing), (uint)(NetNode.Flags.LevelCrossing))) {\n\t\t\t\treason = UnableReason.IsLevelCrossing;\n#if DEBUGTTL\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == nodeId)\n\t\t\t\t\tLog._Debug($\"Cannot toggle traffic lights at node {nodeId}: Node is a level crossing\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tint numRoads = 0;\n\t\t\tint numTrainTracks = 0;\n\t\t\tint numMonorailTracks = 0;\n\t\t\tint numPedSegments = 0;\n\t\t\tServices.NetService.IterateNodeSegments(nodeId, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\tNetInfo info = segment.Info;\n\t\t\t\tif (info.m_class.m_service == ItemClass.Service.Road) {\n\t\t\t\t\t++numRoads;\n\t\t\t\t} else if ((info.m_vehicleTypes & VehicleInfo.VehicleType.Train) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\t++numTrainTracks;\n\t\t\t\t} else if ((info.m_vehicleTypes & VehicleInfo.VehicleType.Monorail) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\t++numMonorailTracks;\n\t\t\t\t}\n\t\t\t\tif (info.m_hasPedestrianLanes) {\n\t\t\t\t\t++numPedSegments;\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tif (numRoads >= 2 || numTrainTracks >= 2 || numMonorailTracks >= 2 || numPedSegments != 0) {\n#if DEBUGTTL\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == nodeId)\n\t\t\t\t\tLog._Debug($\"Can toggle traffic lights at node {nodeId}: numRoads={numRoads} numTrainTracks={numTrainTracks} numMonorailTracks={numMonorailTracks} numPedSegments={numPedSegments}\");\n#endif\n\t\t\t\treason = UnableReason.None;\n\t\t\t\treturn true;\n\t\t\t}\n\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == nodeId)\n\t\t\t\tLog._Debug($\"Cannot toggle traffic lights at node {nodeId}: Insufficient segments. numRoads={numRoads} numTrainTracks={numTrainTracks} numMonorailTracks={numMonorailTracks} numPedSegments={numPedSegments}\");\n#endif\n\t\t\treason = UnableReason.InsufficientSegments;\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic bool IsTrafficLightEnablable(ushort nodeId, ref NetNode node, out UnableReason reason) {\n\t\t\treturn IsTrafficLightToggleable(nodeId, true, ref node, out reason);\n\t\t}\n\n\t\tpublic bool HasTrafficLight(ushort nodeId, ref NetNode node) {\n\t\t\treturn LogicUtil.CheckFlags((uint)node.m_flags, (uint)(NetNode.Flags.Created | NetNode.Flags.Deleted | NetNode.Flags.TrafficLights), (uint)(NetNode.Flags.Created | NetNode.Flags.TrafficLights));\n\t\t}\n\n\t\t[Obsolete]\n\t\tpublic bool LoadData(string data) {\n\t\t\tbool success = true;\n\t\t\tvar trafficLightDefs = data.Split(',');\n\n\t\t\tLog.Info($\"Loading junction traffic light data (old method)\");\n\n\t\t\tforeach (var split in trafficLightDefs.Select(def => def.Split(':')).Where(split => split.Length > 1)) {\n\t\t\t\ttry {\n\t\t\t\t\tLog._Debug($\"Traffic light split data: {split[0]} , {split[1]}\");\n\t\t\t\t\tvar nodeId = Convert.ToUInt16(split[0]);\n\t\t\t\t\tuint flag = Convert.ToUInt16(split[1]);\n\n\t\t\t\t\tif (!Services.NetService.IsNodeValid(nodeId))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tFlags.setNodeTrafficLight(nodeId, flag > 0);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore as it's probably bad save data.\n\t\t\t\t\tLog.Error($\"Error setting the NodeTrafficLights: \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\t[Obsolete]\n\t\tpublic string SaveData(ref bool success) {\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic bool LoadData(List<Configuration.NodeTrafficLight> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading toggled traffic lights (new method)\");\n\n\t\t\tforeach (Configuration.NodeTrafficLight nodeLight in data) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!Services.NetService.IsNodeValid(nodeLight.nodeId))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tLog._Debug($\"Setting traffic light @ {nodeLight.nodeId} to {nodeLight.trafficLight}\");\n\t\t\t\t\tServices.NetService.ProcessNode(nodeLight.nodeId, delegate (ushort nodeId, ref NetNode node) {\n\t\t\t\t\t\tSetTrafficLight(nodeLight.nodeId, nodeLight.trafficLight, ref node);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\t\t\t\t\t//Flags.setNodeTrafficLight(nodeLight.nodeId, nodeLight.trafficLight);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore as it's probably bad save data.\n\t\t\t\t\tLog.Error($\"Error setting the NodeTrafficLights @ {nodeLight.nodeId}: \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\tList<Configuration.NodeTrafficLight> ICustomDataManager<List<Configuration.NodeTrafficLight>>.SaveData(ref bool success) {\n\t\t\treturn null;\n\n\t\t\t/*List<Configuration.NodeTrafficLight> ret = new List<Configuration.NodeTrafficLight>();\n\t\t\tfor (uint nodeId = 0; nodeId < NetManager.MAX_NODE_COUNT; ++nodeId) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!Flags.mayHaveTrafficLight(nodeId))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tbool? hasTrafficLight = Flags.isNodeTrafficLight(nodeId);\n\t\t\t\t\tif (hasTrafficLight == null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tif ((bool)hasTrafficLight) {\n\t\t\t\t\t\tLog._Debug($\"Saving that node {nodeId} has a traffic light\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tLog._Debug($\"Saving that node {nodeId} does not have a traffic light\");\n\t\t\t\t\t}\n\n\t\t\t\t\tret.Add(new Configuration.NodeTrafficLight(nodeId, (bool)hasTrafficLight));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Exception occurred while saving node traffic light @ {nodeId}: {e.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;*/\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/TrafficLightSimulationManager.cs",
    "content": "using System;\nusing ColossalFramework;\nusing TrafficManager.Geometry;\nusing System.Collections.Generic;\nusing TrafficManager.State;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Util;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Traffic;\nusing System.Linq;\nusing CSUtil.Commons;\nusing TrafficManager.TrafficLight.Impl;\nusing TrafficManager.Geometry.Impl;\nusing CSUtil.Commons.Benchmark;\nusing TrafficManager.TrafficLight.Data;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class TrafficLightSimulationManager : AbstractGeometryObservingManager, ICustomDataManager<List<Configuration.TimedTrafficLights>>, ITrafficLightSimulationManager {\n\t\tpublic static readonly TrafficLightSimulationManager Instance = new TrafficLightSimulationManager();\n\t\tpublic const int SIM_MOD = 64;\n\t\n\t\t/// <summary>\n\t\t/// For each node id: traffic light simulation assigned to the node\n\t\t/// </summary>\n\t\tpublic TrafficLightSimulation[] TrafficLightSimulations;\n\t\t//public Dictionary<ushort, TrafficLightSimulation> TrafficLightSimulations = new Dictionary<ushort, TrafficLightSimulation>();\n\n\t\tprivate TrafficLightSimulationManager() {\n\t\t\tTrafficLightSimulations = new TrafficLightSimulation[NetManager.MAX_NODE_COUNT];\n\t\t\tfor (int i = 0; i < TrafficLightSimulations.Length; ++i) {\n\t\t\t\tTrafficLightSimulations[i] = new TrafficLightSimulation((ushort)i);\n\t\t\t}\n\t\t}\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"Traffic light simulations:\");\n\t\t\tfor (int i = 0; i < TrafficLightSimulations.Length; ++i) {\n\t\t\t\tif (! TrafficLightSimulations[i].HasSimulation()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLog._Debug($\"Simulation {i}: {TrafficLightSimulations[i]}\");\n\t\t\t}\n\t\t}\n\n\t\tpublic void SimulationStep() {\n\t\t\tint frame = (int)(Services.SimulationService.CurrentFrameIndex & (SIM_MOD - 1));\n\t\t\tint minIndex = frame * (NetManager.MAX_NODE_COUNT / SIM_MOD);\n\t\t\tint maxIndex = (frame + 1) * (NetManager.MAX_NODE_COUNT / SIM_MOD) - 1;\n\n\t\t\tushort failedNodeId = 0;\n\t\t\ttry {\n\t\t\t\tfor (int nodeId = minIndex; nodeId <= maxIndex; ++nodeId) {\n\t\t\t\t\tfailedNodeId = (ushort)nodeId;\n\t\t\t\t\tTrafficLightSimulations[nodeId].SimulationStep();\n\t\t\t\t}\n\t\t\t\tfailedNodeId = 0;\n\t\t\t} catch (Exception ex) {\n\t\t\t\tLog.Error($\"Error occured while simulating traffic light @ node {failedNodeId}: {ex.ToString()}\");\n\t\t\t\tif (failedNodeId != 0) {\n\t\t\t\t\tRemoveNodeFromSimulation((ushort)failedNodeId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds a manual traffic light simulation to the node with the given id\n\t\t/// </summary>\n\t\t/// <param name=\"nodeId\"></param>\n\t\tpublic bool SetUpManualTrafficLight(ushort nodeId) {\n\t\t\treturn TrafficLightSimulations[nodeId].SetUpManualTrafficLight();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds a timed traffic light simulation to the node with the given id\n\t\t/// </summary>\n\t\t/// <param name=\"nodeId\"></param>\n\t\tpublic bool SetUpTimedTrafficLight(ushort nodeId, IList<ushort> nodeGroup) { // TODO improve signature\n\t\t\tif (! TrafficLightSimulations[nodeId].SetUpTimedTrafficLight(nodeGroup)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Destroys the traffic light and removes it\n\t\t/// </summary>\n\t\t/// <param name=\"nodeId\"></param>\n\t\t/// <param name=\"destroyGroup\"></param>\n\t\tpublic void RemoveNodeFromSimulation(ushort nodeId, bool destroyGroup, bool removeTrafficLight) {\n#if DEBUG\n\t\t\tLog._Debug($\"TrafficLightSimulationManager.RemoveNodeFromSimulation({nodeId}, {destroyGroup}, {removeTrafficLight}) called.\");\n#endif\n\n\t\t\tif (! TrafficLightSimulations[nodeId].HasSimulation()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tTrafficLightManager tlm = TrafficLightManager.Instance;\n\n\t\t\tif (TrafficLightSimulations[nodeId].IsTimedLight()) {\n\t\t\t\t// remove/destroy all timed traffic lights in group\n\t\t\t\tList<ushort> oldNodeGroup = new List<ushort>(TrafficLightSimulations[nodeId].TimedLight.NodeGroup);\n\t\t\t\tforeach (var timedNodeId in oldNodeGroup) {\n\t\t\t\t\tif (! TrafficLightSimulations[timedNodeId].HasSimulation()) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (destroyGroup || timedNodeId == nodeId) {\n\t\t\t\t\t\t//Log._Debug($\"Slave: Removing simulation @ node {timedNodeId}\");\n\t\t\t\t\t\t//TrafficLightSimulations[timedNodeId].Destroy();\n\t\t\t\t\t\tRemoveNodeFromSimulation(timedNodeId);\n\t\t\t\t\t\tif (removeTrafficLight) {\n\t\t\t\t\t\t\tConstants.ServiceFactory.NetService.ProcessNode(timedNodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\t\t\t\t\ttlm.RemoveTrafficLight(timedNodeId, ref node);\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (TrafficLightSimulations[timedNodeId].IsTimedLight()) {\n\t\t\t\t\t\t\tTrafficLightSimulations[timedNodeId].TimedLight.RemoveNodeFromGroup(nodeId);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//Flags.setNodeTrafficLight(nodeId, false);\n\t\t\t//sim.DestroyTimedTrafficLight();\n\t\t\t//TrafficLightSimulations[nodeId].DestroyManualTrafficLight();\n\t\t\tRemoveNodeFromSimulation(nodeId);\n\t\t\tif (removeTrafficLight) {\n\t\t\t\tConstants.ServiceFactory.NetService.ProcessNode(nodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\t\ttlm.RemoveTrafficLight(nodeId, ref node);\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tpublic bool HasSimulation(ushort nodeId) {\n\t\t\treturn TrafficLightSimulations[nodeId].HasSimulation();\n\t\t}\n\n\t\tpublic bool HasManualSimulation(ushort nodeId) {\n\t\t\treturn TrafficLightSimulations[nodeId].IsManualLight();\n\t\t}\n\n\t\tpublic bool HasTimedSimulation(ushort nodeId) {\n\t\t\treturn TrafficLightSimulations[nodeId].IsTimedLight();\n\t\t}\n\n\t\tpublic bool HasActiveTimedSimulation(ushort nodeId) {\n\t\t\treturn TrafficLightSimulations[nodeId].IsTimedLightRunning();\n\t\t}\n\n\t\tpublic bool HasActiveSimulation(ushort nodeId) {\n\t\t\treturn TrafficLightSimulations[nodeId].IsSimulationRunning();\n\t\t}\n\n\t\tprivate void RemoveNodeFromSimulation(ushort nodeId) {\n#if DEBUG\n\t\t\tLog._Debug($\"TrafficLightSimulationManager.RemoveNodeFromSimulation({nodeId}) called.\");\n#endif\n\n\t\t\tTrafficLightSimulations[nodeId].Destroy();\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tfor (uint nodeId = 0; nodeId < NetManager.MAX_NODE_COUNT; ++nodeId) {\n\t\t\t\tTrafficLightSimulations[nodeId].Destroy();\n\t\t\t}\n\t\t}\n\n\t\tprotected override void HandleInvalidNode(NodeGeometry geometry) {\n\t\t\tRemoveNodeFromSimulation(geometry.NodeId, false, true);\n\t\t}\n\n\t\tprotected override void HandleValidNode(NodeGeometry geometry) {\n\t\t\tif (!TrafficLightSimulations[geometry.NodeId].HasSimulation()) {\n\t\t\t\t//Log._Debug($\"TrafficLightSimulationManager.HandleValidNode({geometry.NodeId}): Node is not controlled by a custom traffic light simulation.\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (! Flags.mayHaveTrafficLight(geometry.NodeId)) {\n\t\t\t\tLog._Debug($\"TrafficLightSimulationManager.HandleValidNode({geometry.NodeId}): Node must not have a traffic light: Removing traffic light simulation.\");\n\t\t\t\tRemoveNodeFromSimulation(geometry.NodeId, false, true);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tforeach (SegmentEndGeometry end in geometry.SegmentEndGeometries) {\n\t\t\t\tif (end == null)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tLog._Debug($\"TrafficLightSimulationManager.HandleValidNode({geometry.NodeId}): Adding live traffic lights to segment {end.SegmentId}\");\n\n\t\t\t\t// housekeep timed light\n\t\t\t\tCustomSegmentLightsManager.Instance.GetSegmentLights(end.SegmentId, end.StartNode).Housekeeping(true, true);\n\t\t\t}\n\n\t\t\t// ensure there is a physical traffic light\n\t\t\tConstants.ServiceFactory.NetService.ProcessNode(geometry.NodeId, delegate (ushort nodeId, ref NetNode node) {\n\t\t\t\tConstants.ManagerFactory.TrafficLightManager.AddTrafficLight(geometry.NodeId, ref node);\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tTrafficLightSimulations[geometry.NodeId].Update();\n\t\t}\n\n\t\tpublic bool LoadData(List<Configuration.TimedTrafficLights> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading {data.Count} timed traffic lights (new method)\");\n\n\t\t\tTrafficLightManager tlm = TrafficLightManager.Instance;\n\n\t\t\tHashSet<ushort> nodesWithSimulation = new HashSet<ushort>();\n\t\t\tforeach (Configuration.TimedTrafficLights cnfTimedLights in data) {\n\t\t\t\tnodesWithSimulation.Add(cnfTimedLights.nodeId);\n\t\t\t}\n\n\t\t\tDictionary<ushort, ushort> masterNodeIdBySlaveNodeId = new Dictionary<ushort, ushort>();\n\t\t\tDictionary<ushort, List<ushort>> nodeGroupByMasterNodeId = new Dictionary<ushort, List<ushort>>();\n\t\t\tforeach (Configuration.TimedTrafficLights cnfTimedLights in data) {\n\t\t\t\ttry {\n\t\t\t\t\t// TODO most of this should not be necessary at all if the classes around TimedTrafficLights class were properly designed\n\t\t\t\t\tList<ushort> currentNodeGroup = cnfTimedLights.nodeGroup.Distinct().ToList(); // enforce uniqueness of node ids\n\t\t\t\t\tif (!currentNodeGroup.Contains(cnfTimedLights.nodeId))\n\t\t\t\t\t\tcurrentNodeGroup.Add(cnfTimedLights.nodeId);\n\t\t\t\t\t// remove any nodes that are not configured to have a simulation\n\t\t\t\t\tcurrentNodeGroup = new List<ushort>(currentNodeGroup.Intersect(nodesWithSimulation));\n\n\t\t\t\t\t// remove invalid nodes from the group; find if any of the nodes in the group is already a master node\n\t\t\t\t\tushort masterNodeId = 0;\n\t\t\t\t\tint foundMasterNodes = 0;\n\t\t\t\t\tfor (int i = 0; i < currentNodeGroup.Count;) {\n\t\t\t\t\t\tushort nodeId = currentNodeGroup[i];\n\t\t\t\t\t\tif (!Services.NetService.IsNodeValid(currentNodeGroup[i])) {\n\t\t\t\t\t\t\tcurrentNodeGroup.RemoveAt(i);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t} else if (nodeGroupByMasterNodeId.ContainsKey(nodeId)) {\n\t\t\t\t\t\t\t// this is a known master node\n\t\t\t\t\t\t\tif (foundMasterNodes > 0) {\n\t\t\t\t\t\t\t\t// we already found another master node. ignore this node.\n\t\t\t\t\t\t\t\tcurrentNodeGroup.RemoveAt(i);\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// we found the first master node\n\t\t\t\t\t\t\tmasterNodeId = nodeId;\n\t\t\t\t\t\t\t++foundMasterNodes;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t++i;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (masterNodeId == 0) {\n\t\t\t\t\t\t// no master node defined yet, set the first node as a master node\n\t\t\t\t\t\tmasterNodeId = currentNodeGroup[0];\n\t\t\t\t\t}\n\n\t\t\t\t\t// ensure the master node is the first node in the list (TimedTrafficLights depends on this at the moment...)\n\t\t\t\t\tcurrentNodeGroup.Remove(masterNodeId);\n\t\t\t\t\tcurrentNodeGroup.Insert(0, masterNodeId);\n\n\t\t\t\t\t// update the saved node group and master-slave info\n\t\t\t\t\tnodeGroupByMasterNodeId[masterNodeId] = currentNodeGroup;\n\t\t\t\t\tforeach (ushort nodeId in currentNodeGroup) {\n\t\t\t\t\t\tmasterNodeIdBySlaveNodeId[nodeId] = masterNodeId;\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Warning($\"Error building timed traffic light group for TimedNode {cnfTimedLights.nodeId} (NodeGroup: {string.Join(\", \", cnfTimedLights.nodeGroup.Select(x => x.ToString()).ToArray())}): \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tforeach (Configuration.TimedTrafficLights cnfTimedLights in data) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!masterNodeIdBySlaveNodeId.ContainsKey(cnfTimedLights.nodeId))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tushort masterNodeId = masterNodeIdBySlaveNodeId[cnfTimedLights.nodeId];\n\t\t\t\t\tList<ushort> nodeGroup = nodeGroupByMasterNodeId[masterNodeId];\n\n\t\t\t\t\tLog._Debug($\"Adding timed light at node {cnfTimedLights.nodeId}. NodeGroup: {string.Join(\", \", nodeGroup.Select(x => x.ToString()).ToArray())}\");\n\n\t\t\t\t\tSetUpTimedTrafficLight(cnfTimedLights.nodeId, nodeGroup);\n\n\t\t\t\t\tint j = 0;\n\t\t\t\t\tforeach (Configuration.TimedTrafficLightsStep cnfTimedStep in cnfTimedLights.timedSteps) {\n\t\t\t\t\t\tLog._Debug($\"Loading timed step {j} at node {cnfTimedLights.nodeId}\");\n\t\t\t\t\t\tITimedTrafficLightsStep step = TrafficLightSimulations[cnfTimedLights.nodeId].TimedLight.AddStep(cnfTimedStep.minTime, cnfTimedStep.maxTime, (TrafficLight.StepChangeMetric)cnfTimedStep.changeMetric, cnfTimedStep.waitFlowBalance);\n\n\t\t\t\t\t\tforeach (KeyValuePair<ushort, Configuration.CustomSegmentLights> e in cnfTimedStep.segmentLights) {\n\t\t\t\t\t\t\tif (!Services.NetService.IsSegmentValid(e.Key))\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\te.Value.nodeId = cnfTimedLights.nodeId;\n\n\t\t\t\t\t\t\tLog._Debug($\"Loading timed step {j}, segment {e.Key} at node {cnfTimedLights.nodeId}\");\n\t\t\t\t\t\t\tICustomSegmentLights lights = null;\n\t\t\t\t\t\t\tif (!step.CustomSegmentLights.TryGetValue(e.Key, out lights)) {\n\t\t\t\t\t\t\t\tLog._Debug($\"No segment lights found at timed step {j} for segment {e.Key}, node {cnfTimedLights.nodeId}\");\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tConfiguration.CustomSegmentLights cnfLights = e.Value;\n\n\t\t\t\t\t\t\tLog._Debug($\"Loading pedestrian light @ seg. {e.Key}, step {j}: {cnfLights.pedestrianLightState} {cnfLights.manualPedestrianMode}\");\n\n\t\t\t\t\t\t\tlights.ManualPedestrianMode = cnfLights.manualPedestrianMode;\n\t\t\t\t\t\t\tlights.PedestrianLightState = cnfLights.pedestrianLightState;\n\n\t\t\t\t\t\t\tbool first = true; // v1.10.2 transitional code\n\t\t\t\t\t\t\tforeach (KeyValuePair<ExtVehicleType, Configuration.CustomSegmentLight> e2 in cnfLights.customLights) {\n\t\t\t\t\t\t\t\tLog._Debug($\"Loading timed step {j}, segment {e.Key}, vehicleType {e2.Key} at node {cnfTimedLights.nodeId}\");\n\t\t\t\t\t\t\t\tICustomSegmentLight light = null;\n\t\t\t\t\t\t\t\tif (!lights.CustomLights.TryGetValue(e2.Key, out light)) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"No segment light found for timed step {j}, segment {e.Key}, vehicleType {e2.Key} at node {cnfTimedLights.nodeId}\");\n\t\t\t\t\t\t\t\t\t// v1.10.2 transitional code START\n\t\t\t\t\t\t\t\t\tif (first) {\n\t\t\t\t\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t\t\t\t\t\tif (!lights.CustomLights.TryGetValue(CustomSegmentLights.DEFAULT_MAIN_VEHICLETYPE, out light)) {\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"No segment light found for timed step {j}, segment {e.Key}, DEFAULT vehicleType {CustomSegmentLights.DEFAULT_MAIN_VEHICLETYPE} at node {cnfTimedLights.nodeId}\");\n\t\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// v1.10.2 transitional code END\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t// v1.10.2 transitional code START\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t// v1.10.2 transitional code END\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tConfiguration.CustomSegmentLight cnfLight = e2.Value;\n\n\t\t\t\t\t\t\t\tlight.InternalCurrentMode = (TrafficLight.LightMode)cnfLight.currentMode; // TODO improve & remove\n\t\t\t\t\t\t\t\tlight.SetStates(cnfLight.mainLight, cnfLight.leftLight, cnfLight.rightLight, false);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t++j;\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore, as it's probably corrupt save data. it'll be culled on next save\n\t\t\t\t\tLog.Warning(\"Error loading data from TimedNode (new method): \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tforeach (Configuration.TimedTrafficLights cnfTimedLights in data) {\n\t\t\t\ttry {\n\t\t\t\t\tvar timedNode = TrafficLightSimulations[cnfTimedLights.nodeId].TimedLight;\n\n\t\t\t\t\ttimedNode.Housekeeping();\n\t\t\t\t\tif (cnfTimedLights.started) {\n\t\t\t\t\t\ttimedNode.Start(cnfTimedLights.currentStep);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Warning($\"Error starting timed light @ {cnfTimedLights.nodeId}: \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn success;\n\t\t}\n\n\t\tpublic List<Configuration.TimedTrafficLights> SaveData(ref bool success) {\n\t\t\tList<Configuration.TimedTrafficLights> ret = new List<Configuration.TimedTrafficLights>();\n\t\t\tfor (uint nodeId = 0; nodeId < NetManager.MAX_NODE_COUNT; ++nodeId) {\n\t\t\t\ttry {\n\t\t\t\t\tif (! TrafficLightSimulations[nodeId].IsTimedLight()) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tLog._Debug($\"Going to save timed light at node {nodeId}.\");\n\n\t\t\t\t\tvar timedNode = TrafficLightSimulations[nodeId].TimedLight;\n\t\t\t\t\ttimedNode.OnGeometryUpdate();\n\n\t\t\t\t\tConfiguration.TimedTrafficLights cnfTimedLights = new Configuration.TimedTrafficLights();\n\t\t\t\t\tret.Add(cnfTimedLights);\n\n\t\t\t\t\tcnfTimedLights.nodeId = timedNode.NodeId;\n\t\t\t\t\tcnfTimedLights.nodeGroup = new List<ushort>(timedNode.NodeGroup);\n\t\t\t\t\tcnfTimedLights.started = timedNode.IsStarted();\n\t\t\t\t\tint stepIndex = timedNode.CurrentStep;\n\t\t\t\t\tif (timedNode.IsStarted() && timedNode.GetStep(timedNode.CurrentStep).IsInEndTransition()) {\n\t\t\t\t\t\t// if in end transition save the next step\n\t\t\t\t\t\tstepIndex = (stepIndex + 1) % timedNode.NumSteps();\n\t\t\t\t\t}\n\t\t\t\t\tcnfTimedLights.currentStep = stepIndex;\n\t\t\t\t\tcnfTimedLights.timedSteps = new List<Configuration.TimedTrafficLightsStep>();\n\n\t\t\t\t\tfor (var j = 0; j < timedNode.NumSteps(); j++) {\n\t\t\t\t\t\tLog._Debug($\"Saving timed light step {j} at node {nodeId}.\");\n\t\t\t\t\t\tITimedTrafficLightsStep timedStep = timedNode.GetStep(j);\n\t\t\t\t\t\tConfiguration.TimedTrafficLightsStep cnfTimedStep = new Configuration.TimedTrafficLightsStep();\n\t\t\t\t\t\tcnfTimedLights.timedSteps.Add(cnfTimedStep);\n\n\t\t\t\t\t\tcnfTimedStep.minTime = timedStep.MinTime;\n\t\t\t\t\t\tcnfTimedStep.maxTime = timedStep.MaxTime;\n\t\t\t\t\t\tcnfTimedStep.changeMetric = (int)timedStep.ChangeMetric;\n\t\t\t\t\t\tcnfTimedStep.waitFlowBalance = timedStep.WaitFlowBalance;\n\t\t\t\t\t\tcnfTimedStep.segmentLights = new Dictionary<ushort, Configuration.CustomSegmentLights>();\n\t\t\t\t\t\tforeach (KeyValuePair<ushort, ICustomSegmentLights> e in timedStep.CustomSegmentLights) {\n\t\t\t\t\t\t\tLog._Debug($\"Saving timed light step {j}, segment {e.Key} at node {nodeId}.\");\n\n\t\t\t\t\t\t\tICustomSegmentLights segLights = e.Value;\n\t\t\t\t\t\t\tConfiguration.CustomSegmentLights cnfSegLights = new Configuration.CustomSegmentLights();\n\n\t\t\t\t\t\t\tushort lightsNodeId = segLights.NodeId;\n\t\t\t\t\t\t\tif (lightsNodeId == 0 || lightsNodeId != timedNode.NodeId) {\n\t\t\t\t\t\t\t\tLog.Warning($\"Inconsistency detected: Timed traffic light @ node {timedNode.NodeId} contains custom traffic lights for the invalid segment ({segLights.SegmentId}) at step {j}: nId={lightsNodeId}\");\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcnfSegLights.nodeId = lightsNodeId; // TODO not needed\n\t\t\t\t\t\t\tcnfSegLights.segmentId = segLights.SegmentId; // TODO not needed\n\t\t\t\t\t\t\tcnfSegLights.customLights = new Dictionary<ExtVehicleType, Configuration.CustomSegmentLight>();\n\t\t\t\t\t\t\tcnfSegLights.pedestrianLightState = segLights.PedestrianLightState;\n\t\t\t\t\t\t\tcnfSegLights.manualPedestrianMode = segLights.ManualPedestrianMode;\n\n\t\t\t\t\t\t\tcnfTimedStep.segmentLights.Add(e.Key, cnfSegLights);\n\n\t\t\t\t\t\t\tLog._Debug($\"Saving pedestrian light @ seg. {e.Key}, step {j}: {cnfSegLights.pedestrianLightState} {cnfSegLights.manualPedestrianMode}\");\n\n\t\t\t\t\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e2 in segLights.CustomLights) {\n\t\t\t\t\t\t\t\tLog._Debug($\"Saving timed light step {j}, segment {e.Key}, vehicleType {e2.Key} at node {nodeId}.\");\n\n\t\t\t\t\t\t\t\tICustomSegmentLight segLight = e2.Value;\n\t\t\t\t\t\t\t\tConfiguration.CustomSegmentLight cnfSegLight = new Configuration.CustomSegmentLight();\n\t\t\t\t\t\t\t\tcnfSegLights.customLights.Add(e2.Key, cnfSegLight);\n\n\t\t\t\t\t\t\t\tcnfSegLight.nodeId = lightsNodeId; // TODO not needed\n\t\t\t\t\t\t\t\tcnfSegLight.segmentId = segLights.SegmentId; // TODO not needed\n\t\t\t\t\t\t\t\tcnfSegLight.currentMode = (int)segLight.CurrentMode;\n\t\t\t\t\t\t\t\tcnfSegLight.leftLight = segLight.LightLeft;\n\t\t\t\t\t\t\t\tcnfSegLight.mainLight = segLight.LightMain;\n\t\t\t\t\t\t\t\tcnfSegLight.rightLight = segLight.LightRight;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Exception occurred while saving timed traffic light @ {nodeId}: {e.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/TrafficMeasurementManager.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Util;\nusing UnityEngine;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class TrafficMeasurementManager : AbstractCustomManager, ITrafficMeasurementManager {\n\t\tpublic const VehicleInfo.VehicleType VEHICLE_TYPES = VehicleInfo.VehicleType.Car;\n\t\tpublic const NetInfo.LaneType LANE_TYPES = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;\n\n\t\tpublic static readonly TrafficMeasurementManager Instance = new TrafficMeasurementManager();\n\n\t\tpublic const ushort REF_REL_SPEED_PERCENT_DENOMINATOR = 100;\n\t\tpublic const ushort REF_REL_SPEED = 10000;\n\n\t\tpublic struct LaneTrafficData {\n\t\t\t/// <summary>\n\t\t\t/// Number of seen vehicles since last speed measurement\n\t\t\t/// </summary>\n\t\t\tpublic ushort trafficBuffer;\n\n\t\t\t/// <summary>\n\t\t\t/// Number of seen vehicles before last speed measurement\n\t\t\t/// </summary>\n\t\t\tpublic ushort lastTrafficBuffer;\n\n\t\t\t/// <summary>\n\t\t\t/// All-time max. traffic buffer\n\t\t\t/// </summary>\n\t\t\tpublic ushort maxTrafficBuffer;\n\n\t\t\t/// <summary>\n\t\t\t/// Accumulated speeds since last traffic measurement\n\t\t\t/// </summary>\n\t\t\tpublic uint accumulatedSpeeds;\n\n\t\t\t/// <summary>\n\t\t\t/// Current lane mean speed, per ten thousands\n\t\t\t/// </summary>\n\t\t\tpublic ushort meanSpeed;\n\n\t\t\tpublic override string ToString() {\n\t\t\t\treturn $\"[LaneTrafficData\\n\" +\n\t\t\t\t\t\"\\t\" + $\"trafficBuffer = {trafficBuffer}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"lastTrafficBuffer = {lastTrafficBuffer}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"maxTrafficBuffer = {maxTrafficBuffer}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"trafficBuffer = {trafficBuffer}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"accumulatedSpeeds = {accumulatedSpeeds}\\n\" +\n\t\t\t\t\t\"\\t\" + $\"meanSpeed = {meanSpeed}\\n\" +\n\t\t\t\t\t\"LaneTrafficData]\";\n\t\t\t}\n\t\t}\n\n\t\tpublic struct SegmentDirTrafficData {\n\t\t\tpublic ushort meanSpeed;\n\n\t\t\tpublic override string ToString() {\n\t\t\t\treturn $\"[SegmentDirTrafficData\\n\" +\n\t\t\t\t\t\"\\t\" + $\"meanSpeed = {meanSpeed}\\n\" +\n\t\t\t\t\t\"SegmentDirTrafficData]\";\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Traffic data per segment and lane\n\t\t/// </summary>\n\t\tprivate LaneTrafficData[][] laneTrafficData;\n\n\t\t/// <summary>\n\t\t/// Traffic data per segment and traffic direction\n\t\t/// </summary>\n\t\tinternal SegmentDirTrafficData[] segmentDirTrafficData;\n\n\t\tprivate uint[] meanSpeeds = { 0, 0 };\n\t\tprivate int[] meanSpeedLanes = { 0, 0 };\n\n\t\tprivate TrafficMeasurementManager() {\n\t\t\tlaneTrafficData = new LaneTrafficData[NetManager.MAX_SEGMENT_COUNT][];\n\t\t\tsegmentDirTrafficData = new SegmentDirTrafficData[NetManager.MAX_SEGMENT_COUNT * 2];\n\n\t\t\tfor (int i = 0; i < segmentDirTrafficData.Length; ++i) {\n\t\t\t\tsegmentDirTrafficData[i].meanSpeed = REF_REL_SPEED;\n\t\t\t}\n\t\t\tResetTrafficStats();\n\t\t}\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"Lane traffic data:\");\n\t\t\tif (laneTrafficData == null) {\n\t\t\t\tLog._Debug($\"\\t<null>\");\n\t\t\t} else {\n\t\t\t\tfor (int i = 0; i < laneTrafficData.Length; ++i) {\n\t\t\t\t\tif (laneTrafficData[i] == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tLog._Debug($\"\\tSegment {i}:\");\n\t\t\t\t\tfor (int k = 0; k < laneTrafficData[i].Length; ++k) {\n\t\t\t\t\t\tLog._Debug($\"\\t\\tLane {k}: {laneTrafficData[i][k]}\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tLog._Debug($\"Segment direction traffic data:\");\n\t\t\tif (segmentDirTrafficData == null) {\n\t\t\t\tLog._Debug($\"\\t<null>\");\n\t\t\t} else {\n\t\t\t\tfor (int i = 0; i < segmentDirTrafficData.Length; ++i) {\n\t\t\t\t\tLog._Debug($\"\\tIndex {i}: {segmentDirTrafficData[i]}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic ushort CalcLaneRelativeMeanSpeed(ushort segmentId, byte laneIndex, uint laneId, NetInfo.Lane laneInfo) {\n\t\t\tif (laneTrafficData[segmentId] == null || laneIndex >= laneTrafficData[segmentId].Length) {\n\t\t\t\treturn REF_REL_SPEED;\n\t\t\t}\n\n\t\t\tushort currentBuf = laneTrafficData[segmentId][laneIndex].trafficBuffer;\n\t\t\tushort curRelSpeed = REF_REL_SPEED;\n\n\t\t\t// we use integer division here because it's faster\n\t\t\tif (currentBuf > 0) {\n\t\t\t\tuint laneVehicleSpeedLimit = Math.Min(3u * 8u, (uint)((Options.customSpeedLimitsEnabled ? SpeedLimitManager.Instance.GetLockFreeGameSpeedLimit(segmentId, laneIndex, laneId, laneInfo) : laneInfo.m_speedLimit) * 8f));\n\t\t\t\tif (laneVehicleSpeedLimit <= 0) {\n\t\t\t\t\t// fallback: custom lanes may not have valid values set for speed limit\n\t\t\t\t\tlaneVehicleSpeedLimit = 1;\n\t\t\t\t}\n\t\t\t\tcurRelSpeed = (ushort)Math.Min((uint)REF_REL_SPEED, ((laneTrafficData[segmentId][laneIndex].accumulatedSpeeds * (uint)REF_REL_SPEED) / currentBuf) / laneVehicleSpeedLimit); // 0 .. 10000, m_speedLimit of highway is 2, actual max. vehicle speed on highway is 16, that's why we use x*8 == x<<3 (don't ask why CO uses different units for velocity)\n\n\t\t\t\tif (curRelSpeed >= (uint)GlobalConfig.Instance.DynamicLaneSelection.VolumeMeasurementRelSpeedThreshold * (uint)REF_REL_SPEED_PERCENT_DENOMINATOR) {\n\t\t\t\t\tushort lastBuf = laneTrafficData[segmentId][laneIndex].lastTrafficBuffer;\n\t\t\t\t\tushort maxBuf = laneTrafficData[segmentId][laneIndex].maxTrafficBuffer;\n\n\t\t\t\t\tfloat factor = Mathf.Clamp01(1f - (float)lastBuf / (float)maxBuf);\n\t\t\t\t\tcurRelSpeed = (ushort)(curRelSpeed + (uint)(factor * (float)((uint)REF_REL_SPEED - (uint)curRelSpeed)));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn curRelSpeed;\n\t\t}\n\n\t\tpublic void SimulationStep(ushort segmentId, ref NetSegment segment) {\n\t\t\tGlobalConfig conf = GlobalConfig.Instance;\n\t\t\t\n\t\t\t// calculate traffic density\n\t\t\tNetInfo segmentInfo = segment.Info;\n\t\t\tuint curLaneId = segment.m_lanes;\n\t\t\tint numLanes = segmentInfo.m_lanes.Length;\n\n\t\t\tif (laneTrafficData[segmentId] == null || laneTrafficData[segmentId].Length < numLanes) {\n\t\t\t\tlaneTrafficData[segmentId] = new LaneTrafficData[numLanes];\n\t\t\t\tfor (int i = 0; i < numLanes; ++i) {\n\t\t\t\t\t//laneTrafficData[segmentId][i] = new LaneTrafficData();\n\t\t\t\t\tlaneTrafficData[segmentId][i].meanSpeed = REF_REL_SPEED;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// calculate max./min. lane speed\n\t\t\tfor (int i = 0; i < 2; ++i) {\n\t\t\t\tmeanSpeeds[i] = 0;\n\t\t\t\tmeanSpeedLanes[i] = 0;\n\t\t\t}\n\n\t\t\tcurLaneId = segment.m_lanes;\n\n\t\t\tbyte laneIndex = 0;\n\t\t\twhile (laneIndex < numLanes && curLaneId != 0u) {\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\n\t\t\t\tif ((laneInfo.m_laneType & LANE_TYPES) != NetInfo.LaneType.None && (laneInfo.m_vehicleType & VEHICLE_TYPES) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\tint dirIndex = GetDirIndex(laneInfo.m_finalDirection);\n\n\t\t\t\t\t// calculate reported mean speed\n\t\t\t\t\tushort newRelSpeed = CalcLaneRelativeMeanSpeed(segmentId, laneIndex, curLaneId, segment.Info.m_lanes[laneIndex]);\n\n\t\t\t\t\tmeanSpeeds[dirIndex] += newRelSpeed;\n\t\t\t\t\t++meanSpeedLanes[dirIndex];\n\n\t\t\t\t\tlaneTrafficData[segmentId][laneIndex].meanSpeed = newRelSpeed;\n\n\t\t\t\t\tushort trafficBuffer = laneTrafficData[segmentId][laneIndex].trafficBuffer;\n\n\t\t\t\t\t// remember historic data\n\t\t\t\t\tlaneTrafficData[segmentId][laneIndex].lastTrafficBuffer = trafficBuffer;\n\n\t\t\t\t\tif (trafficBuffer > laneTrafficData[segmentId][laneIndex].maxTrafficBuffer) {\n\t\t\t\t\t\tlaneTrafficData[segmentId][laneIndex].maxTrafficBuffer = trafficBuffer;\n\t\t\t\t\t}\n\n\t\t\t\t\t// reset buffers\n\t\t\t\t\tif (conf.AdvancedVehicleAI.MaxTrafficBuffer > 0) {\n\t\t\t\t\t\tif (laneTrafficData[segmentId][laneIndex].trafficBuffer > conf.AdvancedVehicleAI.MaxTrafficBuffer) {\n\t\t\t\t\t\t\tlaneTrafficData[segmentId][laneIndex].accumulatedSpeeds /= (laneTrafficData[segmentId][laneIndex].trafficBuffer / conf.AdvancedVehicleAI.MaxTrafficBuffer);\n\t\t\t\t\t\t\tlaneTrafficData[segmentId][laneIndex].trafficBuffer = (ushort)conf.AdvancedVehicleAI.MaxTrafficBuffer;\n\t\t\t\t\t\t} else if (laneTrafficData[segmentId][laneIndex].trafficBuffer == conf.AdvancedVehicleAI.MaxTrafficBuffer) {\n\t\t\t\t\t\t\tlaneTrafficData[segmentId][laneIndex].accumulatedSpeeds = 0;\n\t\t\t\t\t\t\tlaneTrafficData[segmentId][laneIndex].trafficBuffer = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlaneTrafficData[segmentId][laneIndex].accumulatedSpeeds = 0;\n\t\t\t\t\t\tlaneTrafficData[segmentId][laneIndex].trafficBuffer = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlaneIndex++;\n\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < 2; ++i) {\n\t\t\t\tint segDirIndex = i == 0 ? GetDirIndex(segmentId, NetInfo.Direction.Forward) : GetDirIndex(segmentId, NetInfo.Direction.Backward);\n\n\t\t\t\tif (meanSpeedLanes[i] > 0) {\n\t\t\t\t\tsegmentDirTrafficData[segDirIndex].meanSpeed = (ushort)Math.Min(REF_REL_SPEED, (meanSpeeds[i] / meanSpeedLanes[i]));\n\t\t\t\t} else {\n\t\t\t\t\tsegmentDirTrafficData[segDirIndex].meanSpeed = REF_REL_SPEED;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic bool GetLaneTrafficData(ushort segmentId, byte laneIndex, out LaneTrafficData trafficData) {\n\t\t\tif (laneTrafficData[segmentId] == null || laneIndex >= laneTrafficData[segmentId].Length) {\n\t\t\t\ttrafficData = default(LaneTrafficData);\n\t\t\t\treturn false;\n\t\t\t} else {\n\t\t\t\ttrafficData = laneTrafficData[segmentId][laneIndex];\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tpublic void DestroySegmentStats(ushort segmentId) {\n\t\t\tlaneTrafficData[segmentId] = null;\n\n\t\t\tint fwdIndex = GetDirIndex(segmentId, NetInfo.Direction.Forward);\n\t\t\tint backIndex = GetDirIndex(segmentId, NetInfo.Direction.Backward);\n\n\t\t\tsegmentDirTrafficData[fwdIndex] = default(SegmentDirTrafficData);\n\t\t\tsegmentDirTrafficData[fwdIndex].meanSpeed = REF_REL_SPEED;\n\n\t\t\tsegmentDirTrafficData[backIndex] = default(SegmentDirTrafficData);\n\t\t\tsegmentDirTrafficData[backIndex].meanSpeed = REF_REL_SPEED;\n\t\t}\n\n\t\tpublic void ResetTrafficStats() {\n\t\t\tfor (int i = 0; i < NetManager.MAX_SEGMENT_COUNT; ++i) {\n\t\t\t\tDestroySegmentStats((ushort)i);\n\t\t\t}\n\t\t}\n\n\t\tpublic void AddTraffic(ushort segmentId, byte laneIndex, ushort speed) {\n\t\t\tif (laneTrafficData[segmentId] == null || laneIndex >= laneTrafficData[segmentId].Length)\n\t\t\t\treturn;\n\n\t\t\tlaneTrafficData[segmentId][laneIndex].trafficBuffer = (ushort)Math.Min(65535u, (uint)laneTrafficData[segmentId][laneIndex].trafficBuffer + 1u);\n\t\t\tlaneTrafficData[segmentId][laneIndex].accumulatedSpeeds += (uint)speed;\n\t\t}\n\n\t\tinternal int GetDirIndex(ushort segmentId, NetInfo.Direction dir) {\n\t\t\treturn (int)segmentId + (dir == NetInfo.Direction.Backward ? NetManager.MAX_SEGMENT_COUNT : 0);\n\t\t}\n\n\t\tinternal int GetDirIndex(NetInfo.Direction dir) {\n\t\t\treturn dir == NetInfo.Direction.Backward ? 1 : 0;\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tResetTrafficStats();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/TrafficPriorityManager.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing ColossalFramework;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Custom.AI;\nusing UnityEngine;\nusing TrafficManager.State;\nusing System.Threading;\nusing TrafficManager.Util;\nusing TrafficManager.Traffic;\nusing TrafficManager.Geometry;\nusing CSUtil.Commons;\nusing TrafficManager.Geometry.Impl;\nusing static TrafficManager.Traffic.Data.PrioritySegment;\nusing TrafficManager.Traffic.Data;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class TrafficPriorityManager : AbstractGeometryObservingManager, ICustomDataManager<List<int[]>>, ICustomDataManager<List<Configuration.PrioritySegment>>, ITrafficPriorityManager {\n\t\tpublic static readonly TrafficPriorityManager Instance = new TrafficPriorityManager();\n\n\t\tpublic enum UnableReason {\n\t\t\tNone,\n\t\t\tNoJunction,\n\t\t\tHasTimedLight,\n\t\t\tInvalidSegment,\n\t\t\tNotIncoming\n\t\t}\n\n\t\t/// <summary>\n\t\t/// List of segments that are connected to roads with timed traffic lights or priority signs. Index: segment id\n\t\t/// </summary>\n\t\tprivate PrioritySegment[] PrioritySegments = null;\n\n\t\tprivate PrioritySegment[] invalidPrioritySegments;\n\n\t\tprivate TrafficPriorityManager() {\n\t\t\tPrioritySegments = new PrioritySegment[NetManager.MAX_SEGMENT_COUNT];\n\t\t\tinvalidPrioritySegments = new PrioritySegment[NetManager.MAX_SEGMENT_COUNT];\n\t\t}\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"Priority signs:\");\n\t\t\tfor (int i = 0; i < PrioritySegments.Length; ++i) {\n\t\t\t\tif (PrioritySegments[i].IsDefault()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLog._Debug($\"Segment {i}: {PrioritySegments[i]}\");\n\t\t\t}\n\t\t}\n\n\t\tprotected void AddInvalidPrioritySegment(ushort segmentId, ref PrioritySegment prioritySegment) {\n\t\t\tinvalidPrioritySegments[segmentId] = prioritySegment;\n\t\t}\n\n\t\tpublic bool MayNodeHavePrioritySigns(ushort nodeId) {\n\t\t\tUnableReason reason;\n\t\t\treturn MayNodeHavePrioritySigns(nodeId, out reason);\n\t\t}\n\n\t\tpublic bool MayNodeHavePrioritySigns(ushort nodeId, out UnableReason reason) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.NodeId <= 0 || nodeId == GlobalConfig.Instance.Debug.NodeId);\n#endif\n\t\t\tif (!Services.NetService.CheckNodeFlags(nodeId, NetNode.Flags.Created | NetNode.Flags.Deleted | NetNode.Flags.Junction, NetNode.Flags.Created | NetNode.Flags.Junction)) {\n\t\t\t\treason = UnableReason.NoJunction;\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.MayNodeHavePrioritySigns: nodeId={nodeId}, result=false, reason={reason}\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (TrafficLightSimulationManager.Instance.HasTimedSimulation(nodeId)) {\n\t\t\t\treason = UnableReason.HasTimedLight;\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.MayNodeHavePrioritySigns: nodeId={nodeId}, result=false, reason={reason}\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t//Log._Debug($\"TrafficPriorityManager.MayNodeHavePrioritySigns: nodeId={nodeId}, result=true\");\n\t\t\treason = UnableReason.None;\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool MaySegmentHavePrioritySign(ushort segmentId, bool startNode) {\n\t\t\tUnableReason reason;\n\t\t\treturn MaySegmentHavePrioritySign(segmentId, startNode, out reason);\n\t\t}\n\n\t\tpublic bool MaySegmentHavePrioritySign(ushort segmentId, bool startNode, out UnableReason reason) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || segmentId == GlobalConfig.Instance.Debug.SegmentId);\n#endif\n\t\t\tif (! Services.NetService.IsSegmentValid(segmentId)) {\n\t\t\t\treason = UnableReason.InvalidSegment;\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.MaySegmentHavePrioritySign: segmentId={segmentId}, startNode={startNode}, result=false, reason={reason}\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (! MayNodeHavePrioritySigns(Services.NetService.GetSegmentNodeId(segmentId, startNode), out reason)) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.MaySegmentHavePrioritySign: segmentId={segmentId}, startNode={startNode}, result=false, reason={reason}\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentEndGeometry endGeo = SegmentGeometry.Get(segmentId)?.GetEnd(startNode);\n\n\t\t\tif (endGeo.OutgoingOneWay) {\n\t\t\t\treason = UnableReason.NotIncoming;\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.MaySegmentHavePrioritySign: segmentId={segmentId}, startNode={startNode}, result=false, reason={reason}\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.MaySegmentHavePrioritySign: segmentId={segmentId}, startNode={startNode}, result=true\");\n\t\t\t}\n#endif\n\t\t\treason = UnableReason.None;\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool MaySegmentHavePrioritySign(ushort segmentId) {\n\t\t\tUnableReason reason;\n\t\t\treturn MaySegmentHavePrioritySign(segmentId, out reason);\n\t\t}\n\n\t\tpublic bool MaySegmentHavePrioritySign(ushort segmentId, out UnableReason reason) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || segmentId == GlobalConfig.Instance.Debug.SegmentId);\n#endif\n\t\t\tif (!Services.NetService.IsSegmentValid(segmentId)) {\n\t\t\t\treason = UnableReason.InvalidSegment;\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.MaySegmentHavePrioritySign: segmentId={segmentId}, result=false, reason={reason}\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool ret =\n\t\t\t\t(MaySegmentHavePrioritySign(segmentId, true, out reason) ||\n\t\t\t\tMaySegmentHavePrioritySign(segmentId, false, out reason));\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.MaySegmentHavePrioritySign: segmentId={segmentId}, result={ret}, reason={reason}\");\n\t\t\t}\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool HasSegmentPrioritySign(ushort segmentId) {\n\t\t\treturn !PrioritySegments[segmentId].IsDefault();\n\t\t}\n\n\t\tpublic bool HasSegmentPrioritySign(ushort segmentId, bool startNode) {\n\t\t\treturn PrioritySegments[segmentId].HasPrioritySignAtNode(startNode);\n\t\t}\n\n\t\tpublic bool HasNodePrioritySign(ushort nodeId) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.NodeId <= 0 || nodeId == GlobalConfig.Instance.Debug.NodeId);\n#endif\n\t\t\tbool ret = false;\n\t\t\tServices.NetService.IterateNodeSegments(nodeId, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\tif (HasSegmentPrioritySign(segmentId, nodeId == segment.m_startNode)) {\n\t\t\t\t\tret = true;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.HasNodePrioritySign: nodeId={nodeId}, result={ret}\");\n\t\t\t}\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool SetPrioritySign(ushort segmentId, bool startNode, PriorityType type) {\n\t\t\tUnableReason reason;\n\t\t\treturn SetPrioritySign(segmentId, startNode, type, out reason);\n\t\t}\n\n\t\tpublic bool SetPrioritySign(ushort segmentId, bool startNode, PriorityType type, out UnableReason reason) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || segmentId == GlobalConfig.Instance.Debug.SegmentId);\n#endif\n\n\t\t\tbool ret = true;\n\t\t\treason = UnableReason.None;\n\n\t\t\tif (type != PriorityType.None &&\n\t\t\t\t! MaySegmentHavePrioritySign(segmentId, startNode, out reason)) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.SetPrioritySign: Segment {segmentId} @ {startNode} may not have a priority sign: {reason}\");\n\t\t\t\t}\n#endif\n\t\t\t\tret = false;\n\t\t\t\ttype = PriorityType.None;\n\t\t\t}\n\n\t\t\tif (type != PriorityType.None) {\n\t\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\t\tif (segGeo == null) {\n\t\t\t\t\tLog.Error($\"TrafficPriorityManager.SetPrioritySign: No geometry information available for segment {segmentId}\");\n\t\t\t\t\treason = UnableReason.InvalidSegment;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tushort nodeId = segGeo.GetNodeId(startNode);\n\t\t\t\tServices.NetService.ProcessNode(nodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\t\tTrafficLightManager.Instance.RemoveTrafficLight(nodeId, ref node);\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (startNode) {\n\t\t\t\tPrioritySegments[segmentId].startType = type;\n\t\t\t} else {\n\t\t\t\tPrioritySegments[segmentId].endType = type;\n\t\t\t}\n\n\t\t\tSegmentEndManager.Instance.UpdateSegmentEnd(segmentId, startNode);\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.SetPrioritySign: segmentId={segmentId}, startNode={startNode}, type={type}, result={ret}, reason={reason}\");\n\t\t\t}\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic void RemovePrioritySignsFromNode(ushort nodeId) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.NodeId <= 0 || nodeId == GlobalConfig.Instance.Debug.NodeId);\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.RemovePrioritySignsFromNode: nodeId={nodeId}\");\n\t\t\t}\n#endif\n\n\t\t\tServices.NetService.IterateNodeSegments(nodeId, delegate(ushort segmentId, ref NetSegment segment) {\n\t\t\t\tRemovePrioritySignFromSegmentEnd(segmentId, nodeId == segment.m_startNode);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\tpublic void RemovePrioritySignsFromSegment(ushort segmentId) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || segmentId == GlobalConfig.Instance.Debug.SegmentId);\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.RemovePrioritySignsFromSegment: segmentId={segmentId}\");\n\t\t\t}\n#endif\n\n\t\t\tRemovePrioritySignFromSegmentEnd(segmentId, true);\n\t\t\tRemovePrioritySignFromSegmentEnd(segmentId, false);\n\t\t}\n\n\t\tpublic void RemovePrioritySignFromSegmentEnd(ushort segmentId, bool startNode) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.SegmentId <= 0 || segmentId == GlobalConfig.Instance.Debug.SegmentId);\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.RemovePrioritySignFromSegment: segmentId={segmentId}, startNode={startNode}\");\n\t\t\t}\n#endif\n\n\t\t\tif (startNode) {\n\t\t\t\tPrioritySegments[segmentId].startType = PriorityType.None;\n\t\t\t} else {\n\t\t\t\tPrioritySegments[segmentId].endType = PriorityType.None;\n\t\t\t}\n\n\t\t\tSegmentEndManager.Instance.UpdateSegmentEnd(segmentId, startNode);\n\t\t}\n\n\t\tpublic PriorityType GetPrioritySign(ushort segmentId, bool startNode) {\n\t\t\treturn startNode ? PrioritySegments[segmentId].startType : PrioritySegments[segmentId].endType;\n\t\t}\n\n\t\tpublic byte CountPrioritySignsAtNode(ushort nodeId, PriorityType sign) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.NodeId <= 0 || nodeId == GlobalConfig.Instance.Debug.NodeId);\n#endif\n\n\t\t\tbyte ret = 0;\n\t\t\tServices.NetService.IterateNodeSegments(nodeId, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\tif (GetPrioritySign(segmentId, segment.m_startNode == nodeId) == sign) {\n\t\t\t\t\t++ret;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.CountPrioritySignsAtNode: nodeId={nodeId}, sign={sign}, result={ret}\");\n\t\t\t}\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool HasPriority(ushort vehicleId, ref Vehicle vehicle, ref PathUnit.Position curPos, ushort transitNodeId, bool startNode, ref PathUnit.Position nextPos, ref NetNode transitNode) {\n\t\t\tSegmentEndGeometry endGeo = SegmentGeometry.Get(curPos.m_segment)?.GetEnd(startNode);\n\t\t\tif (endGeo == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"TrafficPriorityManager.HasPriority({vehicleId}): No segment end geometry found for segment {curPos.m_segment} @ {startNode}\");\n\t\t\t\treturn true;\n#endif\n\t\t\t}\n\n\t\t\t/*SegmentEnd end = SegmentEndManager.Instance.GetSegmentEnd(curPos.m_segment, startNode);\n\t\t\tif (end == null) {\n#if DEBUG\n\t\t\t\tLog.Warning($\"TrafficPriorityManager.HasPriority({vehicleId}): No segment end found for segment {curPos.m_segment} @ {startNode}\");\n\t\t\t\treturn true;\n#endif\n\t\t\t}\n\t\t\tushort transitNodeId = end.NodeId;*/\n\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.NodeId <= 0 || transitNodeId == GlobalConfig.Instance.Debug.NodeId);\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.HasPriority({vehicleId}): Checking vehicle {vehicleId} at node {transitNodeId}. Coming from seg. {curPos.m_segment}, start {startNode}, lane {curPos.m_lane}, going to seg. {nextPos.m_segment}, lane {nextPos.m_lane}\");\n\t\t\t}\n#else\n\t\t\tbool debug = false;\n#endif\n\n\t\t\tif ((vehicle.m_flags & Vehicle.Flags.Spawned) == 0) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog.Warning($\"TrafficPriorityManager.HasPriority({vehicleId}): Vehicle is not spawned.\");\n#endif\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif ((vehicle.m_flags & Vehicle.Flags.Emergency2) != 0) {\n\t\t\t\t// target vehicle is on emergency\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.HasPriority({vehicleId}): Vehicle is on emergency.\");\n#endif\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (vehicle.Info.m_vehicleType == VehicleInfo.VehicleType.Monorail) {\n\t\t\t\t// monorails do not obey priority signs\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.HasPriority({vehicleId}): Vehicle is a monorail.\");\n#endif\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\tPriorityType curSign = GetPrioritySign(curPos.m_segment, startNode);\n\t\t\tif (curSign == PriorityType.None) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.HasPriority({vehicleId}): Sign is None @ seg. {curPos.m_segment}, start {startNode} -> setting to Main\");\n#endif\n\t\t\t\tcurSign = PriorityType.Main;\n\t\t\t}\n\t\t\tbool onMain = curSign == PriorityType.Main;\n\n\t\t\t/*if (! Services.VehicleService.IsVehicleValid(vehicleId)) {\n\t\t\t\tcurEnd.RequestCleanup();\n\t\t\t\treturn true;\n\t\t\t}*/\n\n\t\t\t// calculate approx. time after which the transit node will be reached\n\t\t\tfloat targetTimeToTransitNode = Single.NaN;\n\t\t\tif (Options.simAccuracy <= 1) {\n\t\t\t\tVector3 targetToNode = transitNode.m_position - vehicle.GetLastFramePosition();\n\t\t\t\tVector3 targetVel = vehicle.GetLastFrameVelocity();\n\t\t\t\tfloat targetSpeed = targetVel.magnitude;\n\t\t\t\tfloat targetDistanceToTransitNode = targetToNode.magnitude;\n\n\t\t\t\tif (targetSpeed > 0)\n\t\t\t\t\ttargetTimeToTransitNode = targetDistanceToTransitNode / targetSpeed;\n\t\t\t\telse\n\t\t\t\t\ttargetTimeToTransitNode = 0;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.HasPriority({vehicleId}): estimated target time to transit node {transitNodeId} is {targetTimeToTransitNode} for vehicle {vehicleId}\");\n#endif\n\n\t\t\tArrowDirection targetToDir = endGeo.GetDirection(nextPos.m_segment); // absolute target direction of target vehicle\n\n\t\t\t// iterate over all cars approaching the transit node and check if the target vehicle should be prioritized\n\t\t\tVehicleStateManager vehStateManager = VehicleStateManager.Instance;\n\t\t\tCustomSegmentLightsManager segLightsManager = CustomSegmentLightsManager.Instance;\n\n\t\t\tNodeGeometry transitNodeGeo = NodeGeometry.Get(transitNodeId);\n\t\t\tforeach (SegmentEndGeometry otherEndGeo in transitNodeGeo.SegmentEndGeometries) {\n\t\t\t\tif (otherEndGeo == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tushort otherSegmentId = otherEndGeo.SegmentId;\n\t\t\t\tif (otherSegmentId == curPos.m_segment) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tbool otherStartNode = otherEndGeo.StartNode;\n\t\t\t\tif (otherEndGeo.OutgoingOneWay) {\n\t\t\t\t\t// not an incoming segment\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tICustomSegmentLights otherLights = null;\n\t\t\t\tif (Options.trafficLightPriorityRules) {\n\t\t\t\t\totherLights = segLightsManager.GetSegmentLights(otherSegmentId, otherStartNode, false);\n\t\t\t\t}\n\n\t\t\t\tPriorityType otherSign = GetPrioritySign(otherSegmentId, otherStartNode);\n\t\t\t\tif (otherSign == PriorityType.None) {\n\t\t\t\t\totherSign = PriorityType.Main;\n\t\t\t\t\t//continue;\n\t\t\t\t}\n\t\t\t\tbool incomingOnMain = otherSign == PriorityType.Main;\n\n\t\t\t\tISegmentEnd incomingEnd = SegmentEndManager.Instance.GetSegmentEnd(otherSegmentId, otherStartNode);\n\t\t\t\tif (incomingEnd == null) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog.Error($\"TrafficPriorityManager.HasPriority({vehicleId}): No segment end found for other segment {otherSegmentId} @ {otherStartNode}\");\n#endif\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tArrowDirection incomingFromDir = endGeo.GetDirection(otherSegmentId); // absolute incoming direction of incoming vehicle\n\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.HasPriority({vehicleId}): checking other segment {otherSegmentId} @ {transitNodeId}\");\n#endif\n\n\t\t\t\tushort incomingVehicleId = incomingEnd.FirstRegisteredVehicleId;\n\t\t\t\twhile (incomingVehicleId != 0) {\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug(\"\");\n\t\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.HasPriority({vehicleId}): checking other vehicle {incomingVehicleId} @ seg. {otherSegmentId}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tif (IsConflictingVehicle(debug, transitNode.m_position, targetTimeToTransitNode, vehicleId, ref vehicle, ref curPos, transitNodeId, startNode, ref nextPos, onMain, endGeo, targetToDir, incomingVehicleId, ref Singleton<VehicleManager>.instance.m_vehicles.m_buffer[incomingVehicleId], ref vehStateManager.VehicleStates[incomingVehicleId], incomingOnMain, otherEndGeo, otherLights, incomingFromDir)) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.HasPriority({vehicleId}): incoming vehicle {incomingVehicleId} is conflicting.\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\t// check next incoming vehicle\n\t\t\t\t\tincomingVehicleId = vehStateManager.VehicleStates[incomingVehicleId].nextVehicleIdOnSegment;\n\t\t\t\t}\n\t\t\t}\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.HasPriority({vehicleId}): No conflicting incoming vehicles found.\");\n\t\t\t}\n#endif\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate bool IsConflictingVehicle(bool debug, Vector3 transitNodePos, float targetTimeToTransitNode, ushort vehicleId, ref Vehicle vehicle,\n\t\t\t\t\t\tref PathUnit.Position curPos, ushort transitNodeId, bool startNode, ref PathUnit.Position nextPos, bool onMain, SegmentEndGeometry endGeo,\n\t\t\t\t\t\tArrowDirection targetToDir, ushort incomingVehicleId, ref Vehicle incomingVehicle, ref VehicleState incomingState, bool incomingOnMain,\n\t\t\t\t\t\tSegmentEndGeometry incomingEndGeo, ICustomSegmentLights incomingLights, ArrowDirection incomingFromDir) {\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Checking against other vehicle {incomingVehicleId}.\");\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): TARGET is coming from seg. {curPos.m_segment}, start {startNode}, lane {curPos.m_lane}, going to seg. {nextPos.m_segment}, lane {nextPos.m_lane}\");\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): INCOMING is coming from seg. {incomingState.currentSegmentId}, start {incomingState.currentStartNode}, lane {incomingState.currentLaneIndex}, going to seg. {incomingState.nextSegmentId}, lane {incomingState.nextLaneIndex}\\nincoming state: {incomingState}\");\n\t\t\t}\n#endif\n\n\t\t\tif ((incomingState.flags & VehicleState.Flags.Spawned) == VehicleState.Flags.None) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog.Warning($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming vehicle is not spawned.\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (incomingVehicle.Info.m_vehicleType == VehicleInfo.VehicleType.Monorail) {\n\t\t\t\t// monorails and cars do not collide\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming vehicle is a monorail.\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tArrowDirection incomingToRelDir = incomingEndGeo.GetDirection(incomingState.nextSegmentId); // relative target direction of incoming vehicle\n\n\t\t\tif (incomingLights != null) {\n\t\t\t\tICustomSegmentLight incomingLight = incomingLights.GetCustomLight(incomingState.currentLaneIndex);\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Detected traffic light. Incoming state ({incomingToRelDir}): {incomingLight.GetLightState(incomingToRelDir)}\");\n#endif\n\t\t\t\tif (incomingLight.IsRed(incomingToRelDir)) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming traffic light is red.\");\n#endif\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (incomingState.JunctionTransitState != VehicleJunctionTransitState.None) {\n\t\t\t\tVector3 incomingVel = incomingVehicle.GetLastFrameVelocity();\n\t\t\t\tbool incomingStateChangedRecently = incomingState.IsJunctionTransitStateNew();\n\t\t\t\tif (incomingState.JunctionTransitState == VehicleJunctionTransitState.Approach ||\n\t\t\t\t\tincomingState.JunctionTransitState == VehicleJunctionTransitState.Leave\n\t\t\t\t) {\n\t\t\t\t\tif ((incomingState.vehicleType & ExtVehicleType.RoadVehicle) != ExtVehicleType.None) {\n\t\t\t\t\t\tfloat incomingSqrSpeed = incomingVel.sqrMagnitude;\n\t\t\t\t\t\tif (!incomingStateChangedRecently && incomingSqrSpeed <= GlobalConfig.Instance.PriorityRules.MaxStopVelocity) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming {incomingVehicleId} is LEAVING or APPROACHING but not moving. -> BLOCKED\");\n#endif\n\t\t\t\t\t\t\tincomingState.JunctionTransitState = VehicleJunctionTransitState.Blocked;\n\t\t\t\t\t\t\tincomingStateChangedRecently = true;\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// incoming vehicle is (1) entering the junction or (2) leaving\n\t\t\t\t\tVector3 incomingPos = incomingVehicle.GetLastFramePosition();\n\t\t\t\t\tVector3 incomingToNode = transitNodePos - incomingPos;\n\n\t\t\t\t\t// check if incoming vehicle moves towards node\n\t\t\t\t\tfloat dot = Vector3.Dot(incomingToNode, incomingVel);\n\t\t\t\t\tif (dot <= 0) {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming {incomingVehicleId} is moving away from the transit node ({dot}). *IGNORING*\");\n#endif\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming {incomingVehicleId} is moving towards the transit node ({dot}). Distance: {incomingToNode.magnitude}\");\n#endif\n\n\t\t\t\t\t// check if estimated approach time of the incoming vehicle is within bounds (only if incoming vehicle is far enough away from the junction and target vehicle is moving)\n\t\t\t\t\tif (Options.simAccuracy <= 1 &&\n\t\t\t\t\t\t!Single.IsInfinity(targetTimeToTransitNode) &&\n\t\t\t\t\t\t!Single.IsNaN(targetTimeToTransitNode) &&\n\t\t\t\t\t\tincomingToNode.sqrMagnitude > GlobalConfig.Instance.PriorityRules.MaxPriorityCheckSqrDist\n\t\t\t\t\t) {\n\n\t\t\t\t\t\t// check speeds\n\t\t\t\t\t\tfloat incomingSpeed = incomingVel.magnitude;\n\t\t\t\t\t\tfloat incomingDistanceToTransitNode = incomingToNode.magnitude;\n\t\t\t\t\t\tfloat incomingTimeToTransitNode = Single.NaN;\n\n\t\t\t\t\t\tif (incomingSpeed > 0)\n\t\t\t\t\t\t\tincomingTimeToTransitNode = incomingDistanceToTransitNode / incomingSpeed;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tincomingTimeToTransitNode = Single.PositiveInfinity;\n\n\t\t\t\t\t\tfloat timeDiff = Mathf.Abs(incomingTimeToTransitNode - targetTimeToTransitNode);\n\t\t\t\t\t\tif (timeDiff > GlobalConfig.Instance.PriorityRules.MaxPriorityApproachTime) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming {incomingVehicleId} needs {incomingTimeToTransitNode} time units to get to the node where target needs {targetTimeToTransitNode} time units (diff = {timeDiff}). Difference to large. *IGNORING*\");\n#endif\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming {incomingVehicleId} needs {incomingTimeToTransitNode} time units to get to the node where target needs {targetTimeToTransitNode} time units (diff = {timeDiff}). Difference within bounds. Priority check required.\");\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming is stopped.\");\n#endif\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!incomingStateChangedRecently &&\n\t\t\t\t\t(incomingState.JunctionTransitState == VehicleJunctionTransitState.Blocked/* ||\n\t\t\t\t\t(incomingState.JunctionTransitState == VehicleJunctionTransitState.Stop && vehicleId < incomingVehicleId)*/)\n\t\t\t\t) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming {incomingVehicleId} is BLOCKED and has waited a bit or is STOP and targetVehicleId {vehicleId} < incomingVehicleId {incomingVehicleId}. *IGNORING*\");\n#endif\n\n\t\t\t\t\t// incoming vehicle waits because the junction is blocked or it does not get priority and we waited for some time. Allow target vehicle to enter the junciton.\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// check priority rules\n\t\t\t\tif (HasVehiclePriority(debug, vehicleId, ref vehicle, ref curPos, transitNodeId, startNode, ref nextPos, onMain, targetToDir, incomingVehicleId, ref incomingVehicle, ref incomingState, incomingOnMain, incomingFromDir, incomingToRelDir)) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming {incomingVehicleId} is not conflicting.\");\n#endif\n\t\t\t\t\treturn false;\n\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"==========> TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming {incomingVehicleId} IS conflicting.\");\n#endif\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TrafficPriorityManager.IsConflictingVehicle({vehicleId}, {incomingVehicleId}): Incoming {incomingVehicleId} (main) is not conflicting ({incomingState.JunctionTransitState}).\");\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Implements priority checking for two vehicles approaching or waiting at a junction.\n\t\t/// </summary>\n\t\t/// <param name=\"debug\"></param>\n\t\t/// <param name=\"transitNodeId\">id of the junction</param>\n\t\t/// <param name=\"vehicleId\">target vehicle for which priority is being checked</param>\n\t\t/// <param name=\"vehicle\">target vehicle data</param>\n\t\t/// <param name=\"targetCurPos\">target vehicle current path position</param>\n\t\t/// <param name=\"targetNextPos\">target vehicle next path position</param>\n\t\t/// <param name=\"onMain\">true if the target vehicle is coming from a main road</param>\n\t\t/// <param name=\"incomingVehicleId\">possibly conflicting incoming vehicle</param>\n\t\t/// <param name=\"incomingCurPos\">incoming vehicle current path position</param>\n\t\t/// <param name=\"incomingNextPos\">incoming vehicle next path position</param>\n\t\t/// <param name=\"incomingOnMain\">true if the incoming vehicle is coming from a main road</param>\n\t\t/// <returns>true if the target vehicle has priority, false otherwise</returns>\n\t\tprivate bool HasVehiclePriority(bool debug, ushort vehicleId, ref Vehicle vehicle, ref PathUnit.Position curPos, ushort transitNodeId, bool startNode, ref PathUnit.Position nextPos,\n\t\t\t\tbool onMain, ArrowDirection targetToDir, ushort incomingVehicleId, ref Vehicle incomingVehicle, ref VehicleState incomingState, bool incomingOnMain,\n\t\t\t\tArrowDirection incomingFromDir, ArrowDirection incomingToRelDir) {\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug(\"\");\n\t\t\t\tLog._Debug($\"  TrafficPriorityManager.HasVehiclePriority({vehicleId}, {incomingVehicleId}): *** Checking if vehicle {vehicleId} (main road = {onMain}) @ (seg. {curPos.m_segment}, start {startNode}, lane {curPos.m_lane}) -> (seg. {nextPos.m_segment}, lane {nextPos.m_lane}) has priority over {incomingVehicleId} (main road = {incomingOnMain}) @ (seg. {incomingState.currentSegmentId}, start {incomingState.currentStartNode}, lane {incomingState.currentLaneIndex}) -> (seg. {incomingState.nextSegmentId}, lane {incomingState.nextLaneIndex}).\");\n            }\n#endif\n\n\t\t\tif (targetToDir == ArrowDirection.None || incomingFromDir == ArrowDirection.None || incomingToRelDir == ArrowDirection.None) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.HasVehiclePriority({vehicleId}, {incomingVehicleId}): Invalid directions given: targetToDir={targetToDir}, incomingFromDir={incomingFromDir}, incomingToRelDir={incomingToRelDir}\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (curPos.m_segment == incomingState.currentSegmentId) {\n\t\t\t\t// both vehicles are coming from the same segment. do not apply priority rules in this case.\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.HasVehiclePriority({vehicleId}, {incomingVehicleId}): Both vehicles come from the same segment. *IGNORING*\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t/*       FORWARD\n\t\t\t *          |\n\t\t\t *          |\n\t\t\t * LEFT --- + --- RIGHT\n\t\t\t *          |\n\t\t\t *          |\n\t\t\t *        TURN\n\n\t\t\t/*\n\t\t\t * - Target car is always coming from TURN.\n\t\t\t * - Target car is going to `targetToDir` (relative to TURN).\n\t\t\t * - Incoming car is coming from `incomingFromDir` (relative to TURN).\n\t\t\t * - Incoming car is going to `incomingToRelDir` (relative to `incomingFromDir`).\n\t\t\t */\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"  TrafficPriorityManager.HasVehiclePriority({vehicleId}, {incomingVehicleId}): targetToDir: {targetToDir.ToString()}, incomingFromDir: {incomingFromDir.ToString()}, incomingToRelDir: {incomingToRelDir.ToString()}\");\n            }\n#endif\n\n\t\t\tif (Services.SimulationService.LeftHandDrive) {\n\t\t\t\t// mirror situation for left-hand traffic systems\n\t\t\t\ttargetToDir = ArrowDirectionUtil.InvertLeftRight(targetToDir);\n\t\t\t\tincomingFromDir = ArrowDirectionUtil.InvertLeftRight(incomingFromDir);\n\t\t\t\tincomingToRelDir = ArrowDirectionUtil.InvertLeftRight(incomingToRelDir);\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.HasVehiclePriority({vehicleId}, {incomingVehicleId}): LHD! targetToDir: {targetToDir.ToString()}, incomingFromDir: {incomingFromDir.ToString()}, incomingToRelDir: {incomingToRelDir.ToString()}\");\n\t\t\t\t}\n#endif\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"  TrafficPriorityManager.HasVehiclePriority({vehicleId}, {incomingVehicleId}): targetToDir={targetToDir}, incomingFromDir={incomingFromDir}, incomingToRelDir={incomingToRelDir}\");\n\t\t\t}\n#endif\n\n\t\t\t/*\n\t\t\t * (1) COLLISION DETECTION\n\t\t\t */\n\n\t\t\tbool sameTargets = nextPos.m_segment == incomingState.nextSegmentId;\n\t\t\tbool wouldCollide = DetectCollision(debug, ref curPos, transitNodeId, startNode, ref nextPos, ref incomingState, targetToDir, incomingFromDir, incomingToRelDir, vehicleId, incomingVehicleId);\n\n\t\t\tif (!wouldCollide) {\n\t\t\t\t// both vehicles would not collide. allow both to pass.\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.HasVehiclePriority({vehicleId}, {incomingVehicleId}): Cars {vehicleId} and {incomingVehicleId} would not collide. NO CONFLICT.\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t// -> vehicles would collide\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"  TrafficPriorityManager.HasVehiclePriority({vehicleId}, {incomingVehicleId}): Cars {vehicleId} and {incomingVehicleId} would collide. Checking priority rules.\");\n\t\t\t}\n#endif\n\n\t\t\t/*\n\t\t\t * (2) CHECK PRIORITY RULES\n\t\t\t */\n\n\t\t\tbool ret;\n\t\t\tif ((!onMain && !incomingOnMain) || (onMain && incomingOnMain)) {\n\t\t\t\t// both vehicles are on the same priority level: check common priority rules (left yields to right, left turning vehicles yield to others)\n\t\t\t\tret = HasPriorityOnSameLevel(debug, targetToDir, incomingFromDir, incomingToRelDir, vehicleId, incomingVehicleId);\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.HasVehiclePriority({vehicleId}, {incomingVehicleId}): Cars {vehicleId} and {incomingVehicleId} are on the same priority level. Checking commong priority rules. ret={ret}\");\n\t\t\t\t}\n#endif\n\t\t\t} else {\n\t\t\t\t// both vehicles are on a different priority level: prioritize vehicle on main road\n\t\t\t\tret = onMain;\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.HasVehiclePriority({vehicleId}, {incomingVehicleId}): Cars {vehicleId} and {incomingVehicleId} are on a different priority. Prioritizing vehicle on main road. ret={ret}\");\n\t\t\t\t}\n#endif\n\t\t\t}\n\n\t\t\tif (ret) {\n\t\t\t\t// check if the incoming vehicle is leaving (though the target vehicle has priority)\n\t\t\t\tbool incomingIsLeaving = incomingState.JunctionTransitState == VehicleJunctionTransitState.Leave;\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.HasVehiclePriority({vehicleId}, {incomingVehicleId}): >>> Car {vehicleId} has priority over {incomingVehicleId}. incomingIsLeaving={incomingIsLeaving}\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn !incomingIsLeaving;\n\t\t\t} else {\n\t\t\t\t// the target vehicle must wait\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.HasVehiclePriority({vehicleId}, {incomingVehicleId}): >>> Car {vehicleId} must wait for {incomingVehicleId}. returning FALSE.\");\n\t\t\t\t}\n#endif\n\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Checks if two vehicles are on a collision course.\n\t\t/// </summary>\n\t\t/// <param name=\"debug\">enable debugging</param>\n\t\t/// <param name=\"transitNodeId\">junction node</param>\n\t\t/// <param name=\"incomingState\">incoming vehicle state</param>\n\t\t/// <param name=\"targetToDir\">absolute target vehicle destination direction</param>\n\t\t/// <param name=\"incomingFromDir\">absolute incoming vehicle source direction</param>\n\t\t/// <param name=\"incomingToRelDir\">relative incoming vehicle destination direction</param>\n\t\t/// <param name=\"vehicleId\">(optional) target vehicle id</param>\n\t\t/// <param name=\"incomingVehicleId\">(optional) incoming vehicle id</param>\n\t\t/// <returns>true if both vehicles are on a collision course, false otherwise</returns>\n\t\tpublic bool DetectCollision(bool debug, ref PathUnit.Position curPos, ushort transitNodeId, bool startNode, ref PathUnit.Position nextPos,\n\t\t\tref VehicleState incomingState, ArrowDirection targetToDir, ArrowDirection incomingFromDir, ArrowDirection incomingToRelDir, ushort vehicleId=0, ushort incomingVehicleId=0\n\t\t) {\n\t\t\tbool sameTargets = nextPos.m_segment == incomingState.nextSegmentId;\n\t\t\tbool wouldCollide;\n\t\t\tbool incomingIsLeaving = incomingState.JunctionTransitState == VehicleJunctionTransitState.Leave;\n\t\t\tif (sameTargets) {\n\t\t\t\t// both are going to the same segment\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): Target and incoming are going to the same segment.\");\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (nextPos.m_lane == incomingState.nextLaneIndex) {\n\t\t\t\t\t// both are going to the same lane: lane order is always incorrect\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): Target and incoming are going to the same segment AND lane. lane order is incorrect!\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\twouldCollide = true;\n\t\t\t\t} else {\n\t\t\t\t\t// both are going to a different lane: check lane order\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): Target and incoming are going to the same segment BUT NOT to the same lane. Determining if lane order is correct.\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tswitch (targetToDir) {\n\t\t\t\t\t\tcase ArrowDirection.Left:\n\t\t\t\t\t\tcase ArrowDirection.Turn:\n\t\t\t\t\t\tdefault: // (should not happen)\n\t\t\t\t\t\t\t\t // target & incoming are going left: stay left\n\t\t\t\t\t\t\twouldCollide = !IsLaneOrderConflictFree(debug, nextPos.m_segment, transitNodeId, nextPos.m_lane, incomingState.nextLaneIndex); // stay left\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): Target is going {targetToDir}. Checking if lane {nextPos.m_lane} is LEFT to {incomingState.nextLaneIndex}. would collide? {wouldCollide}\");\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase ArrowDirection.Forward:\n\t\t\t\t\t\t\t// target is going forward/turn\n\t\t\t\t\t\t\tswitch (incomingFromDir) {\n\t\t\t\t\t\t\t\tcase ArrowDirection.Left:\n\t\t\t\t\t\t\t\tcase ArrowDirection.Forward:\n\t\t\t\t\t\t\t\t\t// target is going forward, incoming is coming from left/forward: stay right\n\t\t\t\t\t\t\t\t\twouldCollide = !IsLaneOrderConflictFree(debug, nextPos.m_segment, transitNodeId, incomingState.nextLaneIndex, nextPos.m_lane); // stay right\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): Target is going {targetToDir} and incoming is coming from {incomingFromDir}. Checking if lane {nextPos.m_lane} is RIGHT to {incomingState.nextLaneIndex}. would collide? {wouldCollide}\");\n\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase ArrowDirection.Right:\n\t\t\t\t\t\t\t\t\t// target is going forward, incoming is coming from right: stay left\n\t\t\t\t\t\t\t\t\twouldCollide = !IsLaneOrderConflictFree(debug, nextPos.m_segment, transitNodeId, nextPos.m_lane, incomingState.nextLaneIndex); // stay left\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): Target is going {targetToDir} and incoming is coming from {incomingFromDir}. Checking if lane {nextPos.m_lane} is LEFT to {incomingState.nextLaneIndex}. would collide? {wouldCollide}\");\n\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase ArrowDirection.Turn: // (should not happen)\n\t\t\t\t\t\t\t\tdefault: // (should not happen)\n\t\t\t\t\t\t\t\t\twouldCollide = false;\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\tLog.Warning($\"  TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): Target is going {targetToDir} and incoming is coming from {incomingFromDir} (SHOULD NOT HAPPEN). would collide? {wouldCollide}\");\n\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase ArrowDirection.Right:\n\t\t\t\t\t\t\t// target is going right: stay right\n\t\t\t\t\t\t\twouldCollide = !IsLaneOrderConflictFree(debug, nextPos.m_segment, transitNodeId, incomingState.nextLaneIndex, nextPos.m_lane); // stay right\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): Target is going RIGHT. Checking if lane {nextPos.m_lane} is RIGHT to {incomingState.nextLaneIndex}. would collide? {wouldCollide}\");\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"    TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): >>> would collide? {wouldCollide}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\t\t\t} else {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): Target and incoming are going to different segments.\");\n\t\t\t\t}\n#endif\n\t\t\t\tswitch (targetToDir) {\n\t\t\t\t\tcase ArrowDirection.Left:\n\t\t\t\t\t\tswitch (incomingFromDir) {\n\t\t\t\t\t\t\tcase ArrowDirection.Left:\n\t\t\t\t\t\t\t\twouldCollide = incomingToRelDir != ArrowDirection.Right;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ArrowDirection.Forward:\n\t\t\t\t\t\t\t\twouldCollide = incomingToRelDir != ArrowDirection.Left && incomingToRelDir != ArrowDirection.Turn;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ArrowDirection.Right:\n\t\t\t\t\t\t\t\twouldCollide = incomingToRelDir != ArrowDirection.Right && incomingToRelDir != ArrowDirection.Turn;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault: // (should not happen)\n\t\t\t\t\t\t\t\twouldCollide = false;\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tLog.Warning($\"  TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): Target is going {targetToDir}, incoming is coming from {incomingFromDir} and going {incomingToRelDir}. SHOULD NOT HAPPEN. would collide? {wouldCollide}\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ArrowDirection.Forward:\n\t\t\t\t\t\tswitch (incomingFromDir) {\n\t\t\t\t\t\t\tcase ArrowDirection.Left:\n\t\t\t\t\t\t\t\twouldCollide = incomingToRelDir != ArrowDirection.Right && incomingToRelDir != ArrowDirection.Turn;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ArrowDirection.Forward:\n\t\t\t\t\t\t\t\twouldCollide = incomingToRelDir != ArrowDirection.Right && incomingToRelDir != ArrowDirection.Forward;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ArrowDirection.Right:\n\t\t\t\t\t\t\t\twouldCollide = true; // TODO allow u-turns?\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault: // (should not happen)\n\t\t\t\t\t\t\t\twouldCollide = false;\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tLog.Warning($\"  TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): Target is going {targetToDir}, incoming is coming from {incomingFromDir} and going {incomingToRelDir}. SHOULD NOT HAPPEN. would collide? {wouldCollide}\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ArrowDirection.Right:\n\t\t\t\t\tcase ArrowDirection.Turn:\n\t\t\t\t\tdefault:\n\t\t\t\t\t\twouldCollide = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"  TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): Target is going {targetToDir}, incoming is coming from {incomingFromDir} and going {incomingToRelDir}. would collide? {wouldCollide}\");\n\t\t\t\t}\n#endif\n\t\t\t}\n\n\t\t\treturn wouldCollide;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Check common priority rules if both vehicles are on a collision course and on the same priority level [(main AND main) OR (!main AND !main)]:\n\t\t/// 1. left yields to right\n\t\t/// 2. left-turning vehicles must yield to straight-going vehicles\n\t\t/// </summary>\n\t\t/// <param name=\"debug\">enable debugging</param>\n\t\t/// <param name=\"targetToDir\">absolute target vehicle destination direction</param>\n\t\t/// <param name=\"incomingFromDir\">absolute incoming vehicle source direction</param>\n\t\t/// <param name=\"incomingToRelDir\">relative incoming vehicle destination direction</param>\n\t\t/// <param name=\"vehicleId\">(optional) target vehicle id</param>\n\t\t/// <param name=\"incomingVehicleId\">(optional) incoming vehicle id</param>\n\t\t/// <returns></returns>\n\t\tpublic bool HasPriorityOnSameLevel(bool debug, ArrowDirection targetToDir, ArrowDirection incomingFromDir, ArrowDirection incomingToRelDir, ushort vehicleId=0, ushort incomingVehicleId=0) {\n\t\t\tbool ret;\n\t\t\tswitch (incomingFromDir) {\n\t\t\t\tcase ArrowDirection.Left:\n\t\t\t\tcase ArrowDirection.Right:\n\t\t\t\t\t// (1) left yields to right\n\t\t\t\t\tret = incomingFromDir == ArrowDirection.Left;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tif (incomingToRelDir == ArrowDirection.Left || incomingToRelDir == ArrowDirection.Turn) {\n\t\t\t\t\t\t// (2) incoming vehicle must wait\n\t\t\t\t\t\tret = true;\n\t\t\t\t\t} else if (targetToDir == ArrowDirection.Left || targetToDir == ArrowDirection.Turn) {\n\t\t\t\t\t\t// (2) target vehicle must wait\n\t\t\t\t\t\tret = false;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// (should not happen)\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog.Warning($\"TrafficPriorityManager.HasPriorityOnSameLevel({vehicleId}, {incomingVehicleId}): targetToDir={targetToDir}, incomingFromDir={incomingFromDir}, incomingToRelDir={incomingToRelDir}: SHOULD NOT HAPPEN\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tret = true;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TrafficPriorityManager.HasPriorityOnSameLevel({vehicleId}, {incomingVehicleId}): targetToDir={targetToDir}, incomingFromDir={incomingFromDir}, incomingToRelDir={incomingToRelDir}: ret={ret}\");\n\t\t\t}\n#endif\n\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Checks if lane <paramref name=\"leftLaneIndex\"/> lies to the left of lane <paramref name=\"rightLaneIndex\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"debug\">enable debugging</param>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"nodeId\">transit node id</param>\n\t\t/// <param name=\"leftLaneIndex\">lane index that is checked to lie left</param>\n\t\t/// <param name=\"rightLaneIndex\">lane index that is checked to lie right</param>\n\t\t/// <returns></returns>\n\t\tpublic bool IsLaneOrderConflictFree(bool debug, ushort segmentId, ushort nodeId, byte leftLaneIndex, byte rightLaneIndex) { // TODO refactor\n\t\t\ttry {\n\t\t\t\tif (leftLaneIndex == rightLaneIndex) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\t\tNetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info;\n\n\t\t\t\tNetInfo.Direction dir = nodeId == netManager.m_segments.m_buffer[segmentId].m_startNode ? NetInfo.Direction.Backward : NetInfo.Direction.Forward;\n\t\t\t\tNetInfo.Direction dir2 = ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? dir : NetInfo.InvertDirection(dir);\n\t\t\t\tNetInfo.Direction dir3 = Services.SimulationService.LeftHandDrive ? NetInfo.InvertDirection(dir2) : dir2;\n\n\t\t\t\tNetInfo.Lane leftLane = segmentInfo.m_lanes[leftLaneIndex];\n\t\t\t\tNetInfo.Lane rightLane = segmentInfo.m_lanes[rightLaneIndex];\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"    IsLaneOrderConflictFree({segmentId}, {leftLaneIndex}, {rightLaneIndex}): dir={dir}, dir2={dir2}, dir3={dir3} laneDir={leftLane.m_direction}, leftLanePos={leftLane.m_position}, rightLanePos={rightLane.m_position}\");\n\t\t\t\t}\n#endif\n\n\t\t\t\tbool ret = (dir3 == NetInfo.Direction.Forward) ^ (leftLane.m_position < rightLane.m_position);\n\t\t\t\treturn ret;\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"IsLaneOrderConflictFree({segmentId}, {leftLaneIndex}, {rightLaneIndex}): Error: {e.ToString()}\");\n            }\n\t\t\treturn true;\n\t\t}\n\n\t\tprotected override void HandleInvalidSegment(SegmentGeometry geometry) {\n\t\t\tif (!PrioritySegments[geometry.SegmentId].IsDefault()) {\n\t\t\t\tAddInvalidPrioritySegment(geometry.SegmentId, ref PrioritySegments[geometry.SegmentId]);\n\t\t\t}\n\t\t\tRemovePrioritySignsFromSegment(geometry.SegmentId);\n\t\t}\n\n\t\tprotected override void HandleValidSegment(SegmentGeometry geometry) {\n\t\t\tif (! MaySegmentHavePrioritySign(geometry.SegmentId, true)) {\n\t\t\t\tRemovePrioritySignFromSegmentEnd(geometry.SegmentId, true);\n\t\t\t} else {\n\t\t\t\tUpdateNode(geometry.StartNodeId());\n\t\t\t}\n\n\t\t\tif (!MaySegmentHavePrioritySign(geometry.SegmentId, false)) {\n\t\t\t\tRemovePrioritySignFromSegmentEnd(geometry.SegmentId, false);\n\t\t\t} else {\n\t\t\t\tUpdateNode(geometry.EndNodeId());\n\t\t\t}\n\t\t}\n\n\t\tprotected override void HandleSegmentEndReplacement(NodeGeometry.SegmentEndReplacement replacement, SegmentEndGeometry newEndGeo) {\n\t\t\tISegmentEndId oldSegmentEndId = replacement.oldSegmentEndId;\n\t\t\tISegmentEndId newSegmentEndId = replacement.newSegmentEndId;\n\n\t\t\tPriorityType sign = PriorityType.None;\n\t\t\tif (oldSegmentEndId.StartNode) {\n\t\t\t\tsign = invalidPrioritySegments[oldSegmentEndId.SegmentId].startType;\n\t\t\t\tinvalidPrioritySegments[oldSegmentEndId.SegmentId].startType = PriorityType.None;\n\t\t\t} else {\n\t\t\t\tsign = invalidPrioritySegments[oldSegmentEndId.SegmentId].endType;\n\t\t\t\tinvalidPrioritySegments[oldSegmentEndId.SegmentId].endType = PriorityType.None;\n\t\t\t}\n\n\t\t\tif (sign == PriorityType.None) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLog._Debug($\"TrafficPriorityManager.HandleSegmentEndReplacement({replacement}): Segment replacement detected: {oldSegmentEndId.SegmentId} -> {newSegmentEndId.SegmentId}\\n\" +\n\t\t\t\t$\"Moving priority sign {sign} to new segment.\"\n\t\t\t);\n\n\t\t\tSetPrioritySign(newSegmentEndId.SegmentId, newSegmentEndId.StartNode, sign);\n\t\t}\n\n\t\tprotected void UpdateNode(ushort nodeId) {\n\t\t\tUnableReason reason;\n\t\t\tif (! MayNodeHavePrioritySigns(nodeId, out reason)) {\n\t\t\t\tRemovePrioritySignsFromNode(nodeId);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tfor (int i = 0; i < PrioritySegments.Length; ++i) {\n\t\t\t\tRemovePrioritySignsFromSegment((ushort)i);\n\t\t\t}\n\t\t\tfor (int i = 0; i < invalidPrioritySegments.Length; ++i) {\n\t\t\t\tinvalidPrioritySegments[i].Reset();\n\t\t\t}\n\t\t}\n\n\t\t[Obsolete]\n\t\tpublic bool LoadData(List<int[]> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading {data.Count} priority segments (old method)\");\n\t\t\tforeach (var segment in data) {\n\t\t\t\ttry {\n\t\t\t\t\tif (segment.Length < 3)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tif ((PriorityType)segment[2] == PriorityType.None) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tushort nodeId = (ushort)segment[0];\n\t\t\t\t\tushort segmentId = (ushort)segment[1];\n\t\t\t\t\tPriorityType sign = (PriorityType)segment[2];\n\n\t\t\t\t\tif (!Services.NetService.IsNodeValid(nodeId)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (!Services.NetService.IsSegmentValid(segmentId)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\t\t\tif (segGeo == null) {\n\t\t\t\t\t\tLog.Error($\"TrafficPriorityManager.LoadData: No geometry information available for segment {segmentId}\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbool startNode = segGeo.StartNodeId() == nodeId;\n\n\t\t\t\t\tSetPrioritySign(segmentId, startNode, sign);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore, as it's probably corrupt save data. it'll be culled on next save\n\t\t\t\t\tLog.Warning(\"Error loading data from Priority segments: \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\t[Obsolete]\n\t\tpublic List<int[]> SaveData(ref bool success) {\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic bool LoadData(List<Configuration.PrioritySegment> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading {data.Count} priority segments (new method)\");\n\t\t\tforeach (var prioSegData in data) {\n\t\t\t\ttry {\n\t\t\t\t\tif ((PriorityType)prioSegData.priorityType == PriorityType.None) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (!Services.NetService.IsNodeValid(prioSegData.nodeId)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (!Services.NetService.IsSegmentValid(prioSegData.segmentId)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(prioSegData.segmentId);\n\t\t\t\t\tif (segGeo == null) {\n\t\t\t\t\t\tLog.Error($\"TrafficPriorityManager.SaveData: No geometry information available for segment {prioSegData.segmentId}\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbool startNode = segGeo.StartNodeId() == prioSegData.nodeId;\n\n\t\t\t\t\tLog._Debug($\"Loading priority sign {(PriorityType)prioSegData.priorityType} @ seg. {prioSegData.segmentId}, start node? {startNode}\");\n\t\t\t\t\tSetPrioritySign(prioSegData.segmentId, startNode, (PriorityType)prioSegData.priorityType);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore, as it's probably corrupt save data. it'll be culled on next save\n\t\t\t\t\tLog.Warning(\"Error loading data from Priority segments: \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\tList<Configuration.PrioritySegment> ICustomDataManager<List<Configuration.PrioritySegment>>.SaveData(ref bool success) {\n\t\t\tList<Configuration.PrioritySegment> ret = new List<Configuration.PrioritySegment>();\n\t\t\tfor (uint segmentId = 0; segmentId < NetManager.MAX_SEGMENT_COUNT; ++segmentId) {\n\t\t\t\ttry {\n\t\t\t\t\tif (! Services.NetService.IsSegmentValid((ushort)segmentId) || ! HasSegmentPrioritySign((ushort)segmentId)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get((ushort)segmentId);\n\t\t\t\t\tif (segGeo == null) {\n\t\t\t\t\t\tLog.Error($\"TrafficPriorityManager.SaveData: No geometry information available for segment {segmentId}\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tPriorityType startSign = GetPrioritySign((ushort)segmentId, true);\n\t\t\t\t\tif (startSign != PriorityType.None) {\n\t\t\t\t\t\tushort startNodeId = segGeo.StartNodeId();\n\t\t\t\t\t\tif (Services.NetService.IsNodeValid(startNodeId)) {\n\t\t\t\t\t\t\tLog._Debug($\"Saving priority sign of type {startSign} @ start node {startNodeId} of segment {segmentId}\");\n\t\t\t\t\t\t\tret.Add(new Configuration.PrioritySegment((ushort)segmentId, startNodeId, (int)startSign));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tPriorityType endSign = GetPrioritySign((ushort)segmentId, false);\n\t\t\t\t\tif (endSign != PriorityType.None) {\n\t\t\t\t\t\tushort endNodeId = segGeo.EndNodeId();\n\t\t\t\t\t\tif (Services.NetService.IsNodeValid(endNodeId)) {\n\t\t\t\t\t\t\tLog._Debug($\"Saving priority sign of type {endSign} @ end node {endNodeId} of segment {segmentId}\");\n\t\t\t\t\t\t\tret.Add(new Configuration.PrioritySegment((ushort)segmentId, endNodeId, (int)endSign));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Exception occurred while saving priority segment @ seg. {segmentId}: {e.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/TurnOnRedManager.cs",
    "content": "using ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing TrafficManager.Traffic.Impl;\nusing TrafficManager.Util;\nusing static TrafficManager.Geometry.Impl.NodeGeometry;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class TurnOnRedManager : AbstractGeometryObservingManager, ITurnOnRedManager {\n\t\tpublic static TurnOnRedManager Instance { get; private set; } = new TurnOnRedManager();\n\n\t\tpublic TurnOnRedSegments[] TurnOnRedSegments { get; private set; }\n\n\t\tprivate TurnOnRedManager() {\n\t\t\tTurnOnRedSegments = new TurnOnRedSegments[2 * NetManager.MAX_SEGMENT_COUNT];\n\t\t}\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"Turn-on-red segments:\");\n\t\t\tfor (int i = 0; i < TurnOnRedSegments.Length; ++i) {\n\t\t\t\tLog._Debug($\"Segment end {i}: {TurnOnRedSegments[i]}\");\n\t\t\t}\n\t\t}\n\n\t\tprotected override void HandleValidSegment(SegmentGeometry geometry) {\n\t\t\tUpdateSegment(geometry);\n\t\t}\n\n\t\tprotected override void HandleInvalidSegment(SegmentGeometry geometry) {\n\t\t\tResetSegment(geometry.SegmentId);\n\t\t}\n\n\t\tprotected void UpdateSegment(SegmentGeometry geometry) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[25];\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TurnOnRedManager.UpdateSegment({geometry.SegmentId}) called.\");\n\t\t\t}\n#endif\n\n\t\t\tResetSegment(geometry.SegmentId);\n\t\t\tSegmentEndGeometry startEndGeo = geometry.GetEnd(true);\n\t\t\tif (startEndGeo != null) {\n\t\t\t\tUpdateSegmentEnd(startEndGeo);\n\t\t\t}\n\n\t\t\tSegmentEndGeometry endEndGeo = geometry.GetEnd(false);\n\t\t\tif (endEndGeo != null) {\n\t\t\t\tUpdateSegmentEnd(endEndGeo);\n\t\t\t}\n\t\t}\n\n\t\tprotected void UpdateSegmentEnd(SegmentEndGeometry endGeo) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[25];\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TurnOnRedManager.UpdateSegmentEnd({endGeo.SegmentId}, {endGeo.StartNode}) called.\");\n\t\t\t}\n#endif\n\n\t\t\t// check if traffic can flow to the node and that there is at least one outgoing segment\n\t\t\tif (endGeo.OutgoingOneWay || endGeo.NumOutgoingSegments <= 0) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TurnOnRedManager.UpdateSegmentEnd({endGeo.SegmentId}, {endGeo.StartNode}): outgoing one-way or insufficient number of outgoing segments.\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbool lhd = Services.SimulationService.LeftHandDrive;\n\t\t\tushort nodeId = endGeo.NodeId();\n\n\t\t\t// check node\n\t\t\t// note that we must not check for the `TrafficLights` flag here because the flag might not be loaded yet\n\t\t\tbool nodeValid = false;\n\t\t\tServices.NetService.ProcessNode(nodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\tnodeValid =\n\t\t\t\t\t(node.m_flags & NetNode.Flags.LevelCrossing) == NetNode.Flags.None &&\n\t\t\t\t\tnode.Info?.m_class?.m_service != ItemClass.Service.Beautification;\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tif (! nodeValid) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TurnOnRedManager.UpdateSegmentEnd({endGeo.SegmentId}, {endGeo.StartNode}): node invalid\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// get left/right segments\n\t\t\tushort leftSegmentId = 0;\n\t\t\tushort rightSegmentId = 0;\n\t\t\tServices.NetService.ProcessSegment(endGeo.SegmentId, delegate (ushort segId, ref NetSegment seg) {\n\t\t\t\tseg.GetLeftAndRightSegments(nodeId, out leftSegmentId, out rightSegmentId);\n\t\t\t\treturn true;\n\t\t\t});\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TurnOnRedManager.UpdateSegmentEnd({endGeo.SegmentId}, {endGeo.StartNode}): got left/right segments: {leftSegmentId}/{rightSegmentId}\");\n\t\t\t}\n#endif\n\n\t\t\t// validate left/right segments according to geometric properties\n\t\t\tif (leftSegmentId != 0 && !endGeo.IsLeftSegment(leftSegmentId)) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TurnOnRedManager.UpdateSegmentEnd({endGeo.SegmentId}, {endGeo.StartNode}): left segment is not geometrically left\");\n\t\t\t\t}\n#endif\n\t\t\t\tleftSegmentId = 0;\n\t\t\t}\n\n\t\t\tif (rightSegmentId != 0 && !endGeo.IsRightSegment(rightSegmentId)) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TurnOnRedManager.UpdateSegmentEnd({endGeo.SegmentId}, {endGeo.StartNode}): right segment is not geometrically right\");\n\t\t\t\t}\n#endif\n\t\t\t\trightSegmentId = 0;\n\t\t\t}\n\n\t\t\t// check for incoming one-ways\n\t\t\tif (leftSegmentId != 0 && SegmentGeometry.Get(leftSegmentId).GetEnd(nodeId).IncomingOneWay) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TurnOnRedManager.UpdateSegmentEnd({endGeo.SegmentId}, {endGeo.StartNode}): left segment is incoming one-way\");\n\t\t\t\t}\n#endif\n\t\t\t\tleftSegmentId = 0;\n\t\t\t}\n\n\t\t\tif (rightSegmentId != 0 && SegmentGeometry.Get(rightSegmentId).GetEnd(nodeId).IncomingOneWay) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TurnOnRedManager.UpdateSegmentEnd({endGeo.SegmentId}, {endGeo.StartNode}): right segment is incoming one-way\");\n\t\t\t\t}\n#endif\n\t\t\t\trightSegmentId = 0;\n\t\t\t}\n\n\t\t\tif (endGeo.IncomingOneWay) {\n\t\t\t\tif (lhd && rightSegmentId != 0 || !lhd && leftSegmentId != 0) {\n\t\t\t\t\t// special case: one-way to one-way in non-preferred direction\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"TurnOnRedManager.UpdateSegmentEnd({endGeo.SegmentId}, {endGeo.StartNode}): source is incoming one-way. checking for one-way in non-preferred direction\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tushort targetSegmentId = lhd ? rightSegmentId : leftSegmentId;\n\t\t\t\t\tSegmentEndGeometry targetEndGeo = SegmentGeometry.Get(targetSegmentId)?.GetEnd(nodeId);\n\t\t\t\t\tif (targetEndGeo == null || !targetEndGeo.OutgoingOneWay) {\n\t\t\t\t\t\tif (targetEndGeo == null) {\n\t\t\t\t\t\t\tLog.Error($\"TurnOnRedManager.UpdateSegmentEnd({endGeo.SegmentId}, {endGeo.StartNode}): One-way to one-way: Target segment end geometry not found for segment id {targetSegmentId} @ {nodeId}\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// disallow turn in non-preferred direction\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"TurnOnRedManager.UpdateSegmentEnd({endGeo.SegmentId}, {endGeo.StartNode}): turn in non-preferred direction {(lhd ? \"right\" : \"left\")} disallowed\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tif (lhd) {\n\t\t\t\t\t\t\trightSegmentId = 0;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tleftSegmentId = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (lhd) {\n\t\t\t\t// default case (LHD): turn in preferred direction\n\t\t\t\trightSegmentId = 0;\n\t\t\t} else {\n\t\t\t\t// default case (RHD): turn in preferred direction\n\t\t\t\tleftSegmentId = 0;\n\t\t\t}\n\n\t\t\tint index = GetIndex(endGeo.SegmentId, endGeo.StartNode);\n\t\t\tTurnOnRedSegments[index].leftSegmentId = leftSegmentId;\n\t\t\tTurnOnRedSegments[index].rightSegmentId = rightSegmentId;\n\n#if DEBUG\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"TurnOnRedManager.UpdateSegmentEnd({endGeo.SegmentId}, {endGeo.StartNode}): Finished calculation. leftSegmentId={leftSegmentId}, rightSegmentId={rightSegmentId}\");\n\t\t\t}\n#endif\n\t\t}\n\n\t\tprotected void ResetSegment(ushort segmentId) {\n\t\t\tTurnOnRedSegments[GetIndex(segmentId, true)].Reset();\n\t\t\tTurnOnRedSegments[GetIndex(segmentId, false)].Reset();\n\t\t}\n\n\t\tpublic override void OnBeforeLoadData() {\n\t\t\tbase.OnBeforeLoadData();\n\n\t\t\t// JunctionRestrictionsManager requires our data during loading of custom data\n\t\t\tfor (uint i = 0; i < NetManager.MAX_SEGMENT_COUNT; ++i) {\n\t\t\t\tSegmentGeometry geo = SegmentGeometry.Get((ushort)i);\n\t\t\t\tif (geo != null && geo.IsValid()) {\n\t\t\t\t\tHandleValidSegment(geo);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tfor (int i = 0; i < TurnOnRedSegments.Length; ++i) {\n\t\t\t\tTurnOnRedSegments[i].Reset();\n\t\t\t}\n\t\t}\n\n\t\tpublic int GetIndex(ushort segmentId, bool startNode) {\n\t\t\treturn (int)segmentId + (startNode ? 0 : NetManager.MAX_SEGMENT_COUNT);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/UtilityManager.cs",
    "content": "﻿using ColossalFramework;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing CSUtil.Commons;\nusing TrafficManager.Custom.AI;\nusing System.Threading;\nusing UnityEngine;\nusing TrafficManager.Geometry.Impl;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class UtilityManager : AbstractCustomManager, IUtilityManager {\n\t\tpublic static UtilityManager Instance { get; private set; } = null;\n\n\t\tstatic UtilityManager() {\n\t\t\tInstance = new UtilityManager();\n\t\t}\n\n\t\tpublic void ClearTraffic() {\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(Singleton<VehicleManager>.instance);\n\n\t\t\t\tfor (uint i = 0; i < Singleton<VehicleManager>.instance.m_vehicles.m_size; ++i) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t(Singleton<VehicleManager>.instance.m_vehicles.m_buffer[i].m_flags & Vehicle.Flags.Created) != 0)\n\t\t\t\t\t\tSingleton<VehicleManager>.instance.ReleaseVehicle((ushort)i);\n\t\t\t\t}\n\n\t\t\t\tTrafficMeasurementManager.Instance.ResetTrafficStats();\n\t\t\t} catch (Exception ex) {\n\t\t\t\tLog.Error($\"Error occured while trying to clear traffic: {ex.ToString()}\");\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(Singleton<VehicleManager>.instance);\n\t\t\t}\n\t\t}\n\n\t\tpublic void RemoveParkedVehicles() {\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(Singleton<VehicleManager>.instance);\n\n\t\t\t\tfor (uint i = 0; i < Singleton<VehicleManager>.instance.m_parkedVehicles.m_size; ++i) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t(Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[i].m_flags & (ushort)VehicleParked.Flags.Created) != 0)\n\t\t\t\t\t\tSingleton<VehicleManager>.instance.ReleaseParkedVehicle((ushort)i);\n\t\t\t\t}\n\t\t\t} catch (Exception ex) {\n\t\t\t\tLog.Error($\"Error occured while trying to remove parked vehicles: {ex.ToString()}\");\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(Singleton<VehicleManager>.instance);\n\t\t\t}\n\t\t}\n\n\t\tpublic void PrintAllDebugInfo() {\n\t\t\tLog._Debug($\"UtilityManager.PrintAllDebugInfo(): Pausing simulation.\");\n\t\t\tSingleton<SimulationManager>.instance.ForcedSimulationPaused = true;\n\n\t\t\tLog._Debug(\"=== Flags.PrintDebugInfo() ===\");\n\t\t\ttry {\n\t\t\t\tFlags.PrintDebugInfo();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"Error occurred while printing debug info for flags: {e}\");\n\t\t\t}\n\n\t\t\tLog._Debug(\"=== SegmentGeometry.PrintDebugInfo() ===\");\n\t\t\ttry {\n\t\t\t\tSegmentGeometry.PrintDebugInfo();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"Error occurred while printing debug info for segment geometries: {e}\");\n\t\t\t}\n\n\t\t\tLog._Debug(\"=== NodeGeometry.PrintDebugInfo() ===\");\n\t\t\ttry {\n\t\t\t\tNodeGeometry.PrintDebugInfo();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"Error occurred while printing debug info for node geometries: {e}\");\n\t\t\t}\n\n\t\t\tforeach (ICustomManager manager in LoadingExtension.RegisteredManagers) {\n\t\t\t\ttry {\n\t\t\t\t\tmanager.PrintDebugInfo();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Error occurred while printing debug info for manager {manager.GetType().Name}: {e}\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tLog._Debug($\"UtilityManager.PrintAllDebugInfo(): Unpausing simulation.\");\n\t\t\tSingleton<SimulationManager>.instance.ForcedSimulationPaused = false;\n\t\t}\n\n\t\tpublic void ResetStuckEntities() {\n\t\t\tLog.Info($\"UtilityManager.RemoveStuckEntities() called.\");\n\n\t\t\tLog.Info($\"UtilityManager.RemoveStuckEntities(): Pausing simulation.\");\n\t\t\tSingleton<SimulationManager>.instance.ForcedSimulationPaused = true;\n\n\t\t\tLog.Info($\"UtilityManager.RemoveStuckEntities(): Waiting for all paths.\");\n\t\t\tSingleton<PathManager>.instance.WaitForAllPaths();\n\n\t\t\tLog.Info($\"UtilityManager.RemoveStuckEntities(): Resetting citizen instances that are waiting for a path.\");\n\t\t\tfor (uint citizenInstanceId = 1; citizenInstanceId < CitizenManager.MAX_INSTANCE_COUNT; ++citizenInstanceId) {\n\t\t\t\t//Log._Debug($\"UtilityManager.RemoveStuckEntities(): Processing instance {citizenInstanceId}.\");\n\t\t\t\tif ((Singleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId].m_flags & CitizenInstance.Flags.WaitingPath) != CitizenInstance.Flags.None) {\n\t\t\t\t\tCitizenAI ai = Singleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId].Info.m_citizenAI;\n\n\t\t\t\t\tif (Singleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId].m_path != 0u) {\n\t\t\t\t\t\tLog.Info($\"Resetting stuck citizen instance {citizenInstanceId} (waiting for path)\");\n\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(Singleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId].m_path);\n\t\t\t\t\t\tSingleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId].m_path = 0u;\n\t\t\t\t\t}\n\t\t\t\t\tSingleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId].m_flags &= ~(CitizenInstance.Flags.WaitingTransport | CitizenInstance.Flags.EnteringVehicle | CitizenInstance.Flags.BoredOfWaiting | CitizenInstance.Flags.WaitingTaxi | CitizenInstance.Flags.WaitingPath);\n\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\tif (Singleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId].m_path == 0 &&\n\t\t\t\t\t\t(Singleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId].m_flags & CitizenInstance.Flags.Character) != CitizenInstance.Flags.None) {\n\t\t\t\t\t\tLog._Debug($\"Found potential floating citizen instance: {citizenInstanceId} Source building: {Singleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId].m_sourceBuilding} Target building: {Singleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId].m_targetBuilding} Distance to target position: {(Singleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId].GetLastFramePosition() - (Vector3)Singleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId].m_targetPos).magnitude}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tLog.Info($\"UtilityManager.RemoveStuckEntities(): Resetting vehicles that are waiting for a path.\");\n\t\t\tfor (uint vehicleId = 1; vehicleId < VehicleManager.MAX_VEHICLE_COUNT; ++vehicleId) {\n\t\t\t\t//Log._Debug($\"UtilityManager.RemoveStuckEntities(): Processing vehicle {vehicleId}.\");\n\t\t\t\tif ((Singleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId].m_flags & Vehicle.Flags.WaitingPath) != 0) {\n\t\t\t\t\tif (Singleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId].m_path != 0u) {\n\t\t\t\t\t\tLog.Info($\"Resetting stuck vehicle {vehicleId} (waiting for path)\");\n\n\t\t\t\t\t\tSingleton<PathManager>.instance.ReleasePath(Singleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId].m_path);\n\t\t\t\t\t\t\tSingleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId].m_path = 0u;\n\t\t\t\t\t}\n\t\t\t\t\tSingleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId].m_flags &= ~Vehicle.Flags.WaitingPath;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tLog.Info($\"UtilityManager.RemoveStuckEntities(): Resetting vehicles that are parking and where no parked vehicle is assigned to the driver.\");\n\t\t\tfor (uint vehicleId = 1; vehicleId < VehicleManager.MAX_VEHICLE_COUNT; ++vehicleId) {\n\t\t\t\t//Log._Debug($\"UtilityManager.RemoveStuckEntities(): Processing vehicle {vehicleId}.\");\n\t\t\t\tif ((Singleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId].m_flags & Vehicle.Flags.Parking) != 0) {\n\t\t\t\t\tushort driverInstanceId = CustomPassengerCarAI.GetDriverInstanceId((ushort)vehicleId, ref Singleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId]);\n\t\t\t\t\tuint citizenId = Singleton<CitizenManager>.instance.m_instances.m_buffer[(int)driverInstanceId].m_citizen;\n\t\t\t\t\tif (citizenId != 0u && Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenId].m_parkedVehicle == 0) {\n\n\t\t\t\t\t\tLog.Info($\"Resetting vehicle {vehicleId} (parking without parked vehicle)\");\n\t\t\t\t\t\tSingleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId].m_flags &= ~Vehicle.Flags.Parking;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tLog.Info($\"UtilityManager.RemoveStuckEntities(): Unpausing simulation.\");\n\t\t\tSingleton<SimulationManager>.instance.ForcedSimulationPaused = false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/VehicleBehaviorManager.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing CSUtil.Commons;\nusing CSUtil.Commons.Benchmark;\nusing GenericGameBridge.Service;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing TrafficManager.UI;\nusing TrafficManager.Util;\nusing UnityEngine;\nusing static TrafficManager.Traffic.Data.PrioritySegment;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class VehicleBehaviorManager : AbstractCustomManager, IVehicleBehaviorManager {\n\t\tpublic const float MIN_SPEED = 8f * 0.2f; // 10 km/h\n\t\tpublic const float ICY_ROADS_MIN_SPEED = 8f * 0.4f; // 20 km/h\n\t\tpublic const float ICY_ROADS_STUDDED_MIN_SPEED = 8f * 0.8f; // 40 km/h\n\t\tpublic const float WET_ROADS_MAX_SPEED = 8f * 2f; // 100 km/h\n\t\tpublic const float WET_ROADS_FACTOR = 0.75f;\n\t\tpublic const float BROKEN_ROADS_MAX_SPEED = 8f * 1.6f; // 80 km/h\n\t\tpublic const float BROKEN_ROADS_FACTOR = 0.75f;\n\n\t\tpublic const VehicleInfo.VehicleType RECKLESS_VEHICLE_TYPES = VehicleInfo.VehicleType.Car;\n\n\t\tprivate static PathUnit.Position DUMMY_POS = default(PathUnit.Position);\n\t\tprivate static readonly uint[] POW2MASKS = new uint[] {\n\t\t\t1u, 2u, 4u, 8u,\n\t\t\t16u, 32u, 64u, 128u,\n\t\t\t256u, 512u, 1024u, 2048u,\n\t\t\t4096u, 8192u, 16384u, 32768u,\n\t\t\t65536u, 131072u, 262144u, 524288u,\n\t\t\t1048576u, 2097152u, 4194304u, 8388608u,\n\t\t\t16777216u, 33554432u, 67108864u, 134217728u,\n\t\t\t268435456u, 536870912u, 1073741824u, 2147483648u\n\t\t};\n\n\t\tpublic static readonly VehicleBehaviorManager Instance = new VehicleBehaviorManager();\n\n\t\tprivate VehicleBehaviorManager() {\n\n\t\t}\n\n\t\tpublic bool IsSpaceReservationAllowed(ushort transitNodeId, PathUnit.Position sourcePos, PathUnit.Position targetPos) {\n\t\t\tif (!Options.timedLightsEnabled) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (TrafficLightSimulationManager.Instance.HasActiveTimedSimulation(transitNodeId)) {\n\t\t\t\tRoadBaseAI.TrafficLightState vehLightState;\n\t\t\t\tRoadBaseAI.TrafficLightState pedLightState;\n#if DEBUG\n\t\t\t\tVehicle dummyVeh = default(Vehicle);\n#endif\n\t\t\t\tCustomRoadAI.GetTrafficLightState(\n#if DEBUG\n\t\t\t\t\t0, ref dummyVeh,\n#endif\n\t\t\t\t\ttransitNodeId, sourcePos.m_segment, sourcePos.m_lane, targetPos.m_segment, ref Singleton<NetManager>.instance.m_segments.m_buffer[sourcePos.m_segment], 0, out vehLightState, out pedLightState);\n\n\t\t\t\tif (vehLightState == RoadBaseAI.TrafficLightState.Red) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Checks for traffic lights and priority signs when changing segments (for road & rail vehicles).\n\t\t/// Sets the maximum allowed speed <paramref name=\"maxSpeed\"/> if segment change is not allowed (otherwise <paramref name=\"maxSpeed\"/> has to be set by the calling method).\n\t\t/// </summary>\n\t\t/// <param name=\"frontVehicleId\">vehicle id</param>\n\t\t/// <param name=\"vehicleData\">vehicle data</param>\n\t\t/// <param name=\"sqrVelocity\">last frame squared velocity</param>\n\t\t/// <param name=\"prevPos\">previous path position</param>\n\t\t/// <param name=\"prevTargetNodeId\">previous target node</param>\n\t\t/// <param name=\"prevLaneID\">previous lane</param>\n\t\t/// <param name=\"position\">current path position</param>\n\t\t/// <param name=\"targetNodeId\">transit node</param>\n\t\t/// <param name=\"laneID\">current lane</param>\n\t\t/// <param name=\"nextPosition\">next path position</param>\n\t\t/// <param name=\"nextTargetNodeId\">next target node</param>\n\t\t/// <param name=\"maxSpeed\">maximum allowed speed (only valid if method returns false)</param>\n\t\t/// <returns>true, if the vehicle may change segments, false otherwise.</returns>\n\t\tpublic bool MayChangeSegment(ushort frontVehicleId, ref Vehicle vehicleData, float sqrVelocity, ref PathUnit.Position prevPos, ref NetSegment prevSegment, ushort prevTargetNodeId, uint prevLaneID, ref PathUnit.Position position, ushort targetNodeId, ref NetNode targetNode, uint laneID, ref PathUnit.Position nextPosition, ushort nextTargetNodeId, out float maxSpeed) {\n\t\t\treturn MayChangeSegment(frontVehicleId, ref Constants.ManagerFactory.VehicleStateManager.VehicleStates[frontVehicleId], ref vehicleData, sqrVelocity, ref prevPos, ref prevSegment, prevTargetNodeId, prevLaneID, ref position, targetNodeId, ref targetNode, laneID, ref nextPosition, nextTargetNodeId, out maxSpeed);\n\t\t}\n\n\t\tprotected bool MayChangeSegment(ushort frontVehicleId, ref VehicleState vehicleState, ref Vehicle vehicleData, float sqrVelocity, ref PathUnit.Position prevPos, ref NetSegment prevSegment, ushort prevTargetNodeId, uint prevLaneID, ref PathUnit.Position position, ushort targetNodeId, ref NetNode targetNode, uint laneID, ref PathUnit.Position nextPosition, ushort nextTargetNodeId, out float maxSpeed) {\n\t\t\t//public bool MayChangeSegment(ushort frontVehicleId, ref VehicleState vehicleState, ref Vehicle vehicleData, float sqrVelocity, bool isRecklessDriver, ref PathUnit.Position prevPos, ref NetSegment prevSegment, ushort prevTargetNodeId, uint prevLaneID, ref PathUnit.Position position, ushort targetNodeId, ref NetNode targetNode, uint laneID, ref PathUnit.Position nextPosition, ushort nextTargetNodeId, out float maxSpeed) {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[13] && (GlobalConfig.Instance.Debug.NodeId <= 0 || targetNodeId == GlobalConfig.Instance.Debug.NodeId);\n#endif\n\n#if BENCHMARK\n\t\t\t//using (var bm = new Benchmark(null, \"MayDespawn\")) {\n#endif\n\n\t\t\tif (prevTargetNodeId != targetNodeId\n\t\t\t\t|| (vehicleData.m_blockCounter == 255 && !VehicleBehaviorManager.Instance.MayDespawn(ref vehicleData)) // NON-STOCK CODE\n\t\t\t) {\n\t\t\t\t// method should only be called if targetNodeId == prevTargetNode\n\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave;\n\t\t\t\tmaxSpeed = 0f;\n\t\t\t\treturn true;\n\t\t\t}\n#if BENCHMARK\n\t\t\t//}\n#endif\n\n\n\t\t\tif (vehicleState.JunctionTransitState == VehicleJunctionTransitState.Leave) {\n\t\t\t\t// vehicle was already allowed to leave the junction\n\t\t\t\tmaxSpeed = 0f;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif ((vehicleState.JunctionTransitState == VehicleJunctionTransitState.Stop || vehicleState.JunctionTransitState == VehicleJunctionTransitState.Blocked) &&\n\t\t\t\tvehicleState.lastTransitStateUpdate >> VehicleState.JUNCTION_RECHECK_SHIFT >= Constants.ServiceFactory.SimulationService.CurrentFrameIndex >> VehicleState.JUNCTION_RECHECK_SHIFT) {\n\t\t\t\t// reuse recent result\n\t\t\t\tmaxSpeed = 0f;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool isRecklessDriver = vehicleState.recklessDriver;\n\n\t\t\tvar netManager = Singleton<NetManager>.instance;\n\n\t\t\tbool hasActiveTimedSimulation = (Options.timedLightsEnabled && TrafficLightSimulationManager.Instance.HasActiveTimedSimulation(targetNodeId));\n\t\t\tbool hasTrafficLightFlag = (targetNode.m_flags & NetNode.Flags.TrafficLights) != NetNode.Flags.None;\n\t\t\tif (hasActiveTimedSimulation && !hasTrafficLightFlag) {\n\t\t\t\tTrafficLightManager.Instance.AddTrafficLight(targetNodeId, ref targetNode);\n\t\t\t}\n\t\t\tbool hasTrafficLight = hasTrafficLightFlag || hasActiveTimedSimulation;\n\t\t\tbool checkTrafficLights = true;\n\t\t\tbool isTargetStartNode = prevSegment.m_startNode == targetNodeId;\n\t\t\tbool isLevelCrossing = (targetNode.m_flags & NetNode.Flags.LevelCrossing) != NetNode.Flags.None;\n\t\t\tif ((vehicleData.Info.m_vehicleType & (VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Monorail)) == VehicleInfo.VehicleType.None) {\n\t\t\t\t// check if to check space\n\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomVehicleAI.MayChangeSegment: Vehicle {frontVehicleId} is not a train.\");\n#endif\n\n\t\t\t\t// stock priority signs\n\t\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Emergency2) == (Vehicle.Flags)0 &&\n\t\t\t\t\t((NetLane.Flags)netManager.m_lanes.m_buffer[prevLaneID].m_flags & (NetLane.Flags.YieldStart | NetLane.Flags.YieldEnd)) != NetLane.Flags.None &&\n\t\t\t\t\t(targetNode.m_flags & (NetNode.Flags.Junction | NetNode.Flags.TrafficLights | NetNode.Flags.OneWayIn)) == NetNode.Flags.Junction) {\n\t\t\t\t\tif (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Tram || vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Train) {\n\t\t\t\t\t\tif ((vehicleData.m_flags2 & Vehicle.Flags2.Yielding) == (Vehicle.Flags2)0) {\n\t\t\t\t\t\t\tif (sqrVelocity < 0.01f) {\n\t\t\t\t\t\t\t\tvehicleData.m_flags2 |= Vehicle.Flags2.Yielding;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmaxSpeed = 0f;\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvehicleData.m_waitCounter = (byte)Mathf.Min((int)(vehicleData.m_waitCounter + 1), 4);\n\t\t\t\t\t\t\tif (vehicleData.m_waitCounter < 4) {\n\t\t\t\t\t\t\t\tmaxSpeed = 0f;\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tvehicleData.m_flags2 &= ~Vehicle.Flags2.Yielding;\n\t\t\t\t\t\t\tvehicleData.m_waitCounter = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (sqrVelocity > 0.01f) {\n\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop;\n\t\t\t\t\t\tmaxSpeed = 0f;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// entering blocked junctions\n\t\t\t\tif (MustCheckSpace(prevPos.m_segment, isTargetStartNode, ref targetNode, isRecklessDriver)) {\n#if BENCHMARK\n\t\t\t\t\t//using (var bm = new Benchmark(null, \"CheckSpace\")) {\n#endif\n\t\t\t\t\t// check if there is enough space\n\t\t\t\t\tvar len = vehicleState.totalLength + 4f;\n\t\t\t\t\tif (!netManager.m_lanes.m_buffer[laneID].CheckSpace(len)) {\n\t\t\t\t\t\tvar sufficientSpace = false;\n\t\t\t\t\t\tif (nextPosition.m_segment != 0 && netManager.m_lanes.m_buffer[laneID].m_length < 30f) {\n\t\t\t\t\t\t\tNetNode.Flags nextTargetNodeFlags = netManager.m_nodes.m_buffer[nextTargetNodeId].m_flags;\n\t\t\t\t\t\t\tif ((nextTargetNodeFlags & (NetNode.Flags.Junction | NetNode.Flags.OneWayOut | NetNode.Flags.OneWayIn)) != NetNode.Flags.Junction ||\n\t\t\t\t\t\t\t\tnetManager.m_nodes.m_buffer[nextTargetNodeId].CountSegments() == 2) {\n\t\t\t\t\t\t\t\tuint nextLaneId = PathManager.GetLaneID(nextPosition);\n\t\t\t\t\t\t\t\tif (nextLaneId != 0u) {\n\t\t\t\t\t\t\t\t\tsufficientSpace = netManager.m_lanes.m_buffer[nextLaneId].CheckSpace(len);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!sufficientSpace) {\n\t\t\t\t\t\t\tmaxSpeed = 0f;\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"Vehicle {frontVehicleId}: Setting JunctionTransitState to BLOCKED\");\n#endif\n\n\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Blocked;\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t\t//}\n#endif\n\t\t\t\t}\n\n\t\t\t\tbool isJoinedJunction = ((NetLane.Flags)netManager.m_lanes.m_buffer[prevLaneID].m_flags & NetLane.Flags.JoinedJunction) != NetLane.Flags.None;\n\t\t\t\tcheckTrafficLights = !isJoinedJunction || isLevelCrossing;\n\t\t\t} else {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomVehicleAI.MayChangeSegment: Vehicle {frontVehicleId} is a train/metro/monorail.\");\n#endif\n\n\t\t\t\tif (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Monorail) {\n\t\t\t\t\t// vanilla traffic light flags are not rendered on monorail tracks\n\t\t\t\t\tcheckTrafficLights = hasActiveTimedSimulation;\n\t\t\t\t} else if (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Train) {\n\t\t\t\t\t// vanilla traffic light flags are not rendered on train tracks, except for level crossings\n\t\t\t\t\tcheckTrafficLights = hasActiveTimedSimulation || isLevelCrossing;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (vehicleState.JunctionTransitState == VehicleJunctionTransitState.Blocked) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"Vehicle {frontVehicleId}: Setting JunctionTransitState from BLOCKED to APPROACH\");\n#endif\n\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Approach;\n\t\t\t}\n\n\t\t\tITrafficPriorityManager prioMan = TrafficPriorityManager.Instance;\n\t\t\tICustomSegmentLightsManager segLightsMan = CustomSegmentLightsManager.Instance;\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Emergency2) == 0 || isLevelCrossing) {\n\t\t\t\tif (hasTrafficLight && checkTrafficLights) {\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Node {targetNodeId} has a traffic light.\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tRoadBaseAI.TrafficLightState vehicleLightState;\n\t\t\t\t\tbool stopCar = false;\n#if BENCHMARK\n\t\t\t\t\t//using (var bm = new Benchmark(null, \"CheckTrafficLight\")) {\n#endif\n\n\t\t\t\t\tvar destinationInfo = targetNode.Info;\n\n\t\t\t\t\tuint currentFrameIndex = Singleton<SimulationManager>.instance.m_currentFrameIndex;\n\t\t\t\t\tuint targetNodeLower8Bits = (uint)((targetNodeId << 8) / 32768);\n\n\t\t\t\t\tRoadBaseAI.TrafficLightState pedestrianLightState;\n\t\t\t\t\tbool vehicles;\n\t\t\t\t\tbool pedestrians;\n\t\t\t\t\tCustomRoadAI.GetTrafficLightState(\n#if DEBUG\n\t\t\t\t\t\t\tfrontVehicleId, ref vehicleData,\n#endif\n\t\t\t\t\t\t\ttargetNodeId, prevPos.m_segment, prevPos.m_lane, position.m_segment, ref prevSegment, currentFrameIndex - targetNodeLower8Bits, out vehicleLightState, out pedestrianLightState, out vehicles, out pedestrians);\n\n\t\t\t\t\tif (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Car && isRecklessDriver && !isLevelCrossing) {\n\t\t\t\t\t\tvehicleLightState = RoadBaseAI.TrafficLightState.Green;\n\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Vehicle {frontVehicleId} has TL state {vehicleLightState} at node {targetNodeId}\");\n#endif\n\n\t\t\t\t\tuint random = currentFrameIndex - targetNodeLower8Bits & 255u;\n\t\t\t\t\tif (!vehicles && random >= 196u) {\n\t\t\t\t\t\tvehicles = true;\n\t\t\t\t\t\tRoadBaseAI.SetTrafficLightState(targetNodeId, ref prevSegment, currentFrameIndex - targetNodeLower8Bits, vehicleLightState, pedestrianLightState, vehicles, pedestrians);\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (vehicleLightState) {\n\t\t\t\t\t\tcase RoadBaseAI.TrafficLightState.RedToGreen:\n\t\t\t\t\t\t\tif (random < 60u) {\n\t\t\t\t\t\t\t\tstopCar = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\t\t\tstopCar = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase RoadBaseAI.TrafficLightState.GreenToRed:\n\t\t\t\t\t\t\tif (random >= 30u) {\n\t\t\t\t\t\t\t\tstopCar = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t\t//}\n#endif\n\n\t\t\t\t\t// Turn-on-red: Check if turning in the preferred direction, and if turning while it's red is allowed\n\t\t\t\t\tif (\n\t\t\t\t\t\tOptions.turnOnRedEnabled &&\n\t\t\t\t\t\tstopCar &&\n\t\t\t\t\t\t(vehicleState.vehicleType & ExtVehicleType.RoadVehicle) != ExtVehicleType.None &&\n\t\t\t\t\t\tsqrVelocity <= GlobalConfig.Instance.PriorityRules.MaxYieldVelocity * GlobalConfig.Instance.PriorityRules.MaxYieldVelocity &&\n\t\t\t\t\t\t!isRecklessDriver\n\t\t\t\t\t) {\n\t\t\t\t\t\tIJunctionRestrictionsManager junctionRestrictionsManager = Constants.ManagerFactory.JunctionRestrictionsManager;\n\t\t\t\t\t\tITurnOnRedManager turnOnRedMan = Constants.ManagerFactory.TurnOnRedManager;\n\t\t\t\t\t\tbool lhd = Constants.ServiceFactory.SimulationService.LeftHandDrive;\n\t\t\t\t\t\tint torIndex = turnOnRedMan.GetIndex(prevPos.m_segment, isTargetStartNode);\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t(turnOnRedMan.TurnOnRedSegments[torIndex].leftSegmentId == position.m_segment &&\n\t\t\t\t\t\t\tjunctionRestrictionsManager.IsTurnOnRedAllowed(lhd, prevPos.m_segment, isTargetStartNode)) ||\n\t\t\t\t\t\t\t(turnOnRedMan.TurnOnRedSegments[torIndex].rightSegmentId == position.m_segment &&\n\t\t\t\t\t\t\tjunctionRestrictionsManager.IsTurnOnRedAllowed(!lhd, prevPos.m_segment, isTargetStartNode))\n\t\t\t\t\t\t) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Vehicle may turn on red to target segment {position.m_segment}, lane {position.m_lane}\");\n#endif\n\t\t\t\t\t\t\tstopCar = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// check priority rules at unprotected traffic lights\n\t\t\t\t\tif (!stopCar && Options.prioritySignsEnabled && Options.trafficLightPriorityRules && segLightsMan.IsSegmentLight(prevPos.m_segment, isTargetStartNode)) {\n\t\t\t\t\t\tbool hasPriority = true;\n#if BENCHMARK\n\t\t\t\t\t\t//using (var bm = new Benchmark(null, \"CheckPriorityRulesAtTTL\")) {\n#endif\n\t\t\t\t\t\thasPriority = prioMan.HasPriority(frontVehicleId, ref vehicleData, ref prevPos, targetNodeId, isTargetStartNode, ref position, ref targetNode);\n#if BENCHMARK\n\t\t\t\t\t\t//}\n#endif\n\n\t\t\t\t\t\tif (!hasPriority) {\n\t\t\t\t\t\t\t// green light but other cars are incoming and they have priority: stop\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Green traffic light (or turn on red allowed) but detected traffic with higher priority: stop.\");\n#endif\n\t\t\t\t\t\t\tstopCar = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (stopCar) {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Setting JunctionTransitState to STOP\");\n#endif\n\n\t\t\t\t\t\tif (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Tram || vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Train) {\n\t\t\t\t\t\t\tvehicleData.m_flags2 |= Vehicle.Flags2.Yielding;\n\t\t\t\t\t\t\tvehicleData.m_waitCounter = 0;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop;\n\t\t\t\t\t\tmaxSpeed = 0f;\n\t\t\t\t\t\tvehicleData.m_blockCounter = 0;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Setting JunctionTransitState to LEAVE ({vehicleLightState})\");\n#endif\n\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave;\n\n\t\t\t\t\t\tif (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Tram || vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Train) {\n\t\t\t\t\t\t\tvehicleData.m_flags2 &= ~Vehicle.Flags2.Yielding;\n\t\t\t\t\t\t\tvehicleData.m_waitCounter = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (Options.prioritySignsEnabled && vehicleData.Info.m_vehicleType != VehicleInfo.VehicleType.Monorail) {\n#if BENCHMARK\n\t\t\t\t\t//using (var bm = new Benchmark(null, \"CheckPriorityRules\")) {\n#endif\n\n#if DEBUG\n\t\t\t\t\t//bool debug = destinationNodeId == 10864;\n\t\t\t\t\t//bool debug = destinationNodeId == 13531;\n\t\t\t\t\t//bool debug = false;// targetNodeId == 5027;\n#endif\n\t\t\t\t\t//bool debug = false;\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Vehicle is arriving @ seg. {prevPos.m_segment} ({position.m_segment}, {nextPosition.m_segment}), node {targetNodeId} which is not a traffic light.\");\n#endif\n\n\t\t\t\t\tvar sign = prioMan.GetPrioritySign(prevPos.m_segment, isTargetStartNode);\n\t\t\t\t\tif (sign != PrioritySegment.PriorityType.None) {\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Vehicle is arriving @ seg. {prevPos.m_segment} ({position.m_segment}, {nextPosition.m_segment}), node {targetNodeId} which is not a traffic light and is a priority segment.\");\n#endif\n\n#if DEBUG\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): JunctionTransitState={vehicleState.JunctionTransitState.ToString()}\");\n#endif\n\n\t\t\t\t\t\tif (vehicleState.JunctionTransitState == VehicleJunctionTransitState.None) {\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Setting JunctionTransitState to APPROACH (prio)\");\n#endif\n\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Approach;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (vehicleState.JunctionTransitState != VehicleJunctionTransitState.Leave) {\n\t\t\t\t\t\t\tbool hasPriority;\n\t\t\t\t\t\t\tswitch (sign) {\n\t\t\t\t\t\t\t\tcase PriorityType.Stop:\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): STOP sign. waittime={vehicleState.waitTime}, sqrVelocity={sqrVelocity}\");\n#endif\n\n\t\t\t\t\t\t\t\t\tmaxSpeed = 0f;\n\n\t\t\t\t\t\t\t\t\tif (vehicleState.waitTime < GlobalConfig.Instance.PriorityRules.MaxPriorityWaitTime) {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Setting JunctionTransitState to STOP (wait) waitTime={vehicleState.waitTime}\");\n#endif\n\t\t\t\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop;\n\n\t\t\t\t\t\t\t\t\t\tif (sqrVelocity <= GlobalConfig.Instance.PriorityRules.MaxStopVelocity * GlobalConfig.Instance.PriorityRules.MaxStopVelocity) {\n\t\t\t\t\t\t\t\t\t\t\tvehicleState.waitTime++;\n\n\t\t\t\t\t\t\t\t\t\t\t//float minStopWaitTime = Singleton<SimulationManager>.instance.m_randomizer.UInt32(3);\n\t\t\t\t\t\t\t\t\t\t\tif (vehicleState.waitTime >= 2) {\n\t\t\t\t\t\t\t\t\t\t\t\tif (Options.simAccuracy >= 4) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave;\n\t\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t\thasPriority = prioMan.HasPriority(frontVehicleId, ref vehicleData, ref prevPos, targetNodeId, isTargetStartNode, ref position, ref targetNode);\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): hasPriority={hasPriority}\");\n#endif\n\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (!hasPriority) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvehicleData.m_blockCounter = 0;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Setting JunctionTransitState to LEAVE (min wait timeout)\");\n#endif\n\t\t\t\t\t\t\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Vehicle must first come to a full stop in front of the stop sign.\");\n#endif\n\t\t\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Vehicle has come to a full stop.\");\n#endif\n\t\t\t\t\t\t\t\t\t\t\tvehicleState.waitTime = 0;\n\t\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Max. wait time exceeded. Setting JunctionTransitState to LEAVE (max wait timeout)\");\n#endif\n\t\t\t\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase PriorityType.Yield:\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): YIELD sign. waittime={vehicleState.waitTime}\");\n#endif\n\n\t\t\t\t\t\t\t\t\tif (vehicleState.waitTime < GlobalConfig.Instance.PriorityRules.MaxPriorityWaitTime) {\n\t\t\t\t\t\t\t\t\t\tvehicleState.waitTime++;\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Setting JunctionTransitState to STOP (wait)\");\n#endif\n\t\t\t\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop;\n\n\t\t\t\t\t\t\t\t\t\tif (sqrVelocity <= GlobalConfig.Instance.PriorityRules.MaxYieldVelocity * GlobalConfig.Instance.PriorityRules.MaxYieldVelocity || Options.simAccuracy <= 2) {\n\t\t\t\t\t\t\t\t\t\t\tif (Options.simAccuracy >= 4) {\n\t\t\t\t\t\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave;\n\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\thasPriority = prioMan.HasPriority(frontVehicleId, ref vehicleData, ref prevPos, targetNodeId, isTargetStartNode, ref position, ref targetNode);\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): hasPriority: {hasPriority}\");\n#endif\n\n\t\t\t\t\t\t\t\t\t\t\t\tif (!hasPriority) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tvehicleData.m_blockCounter = 0;\n\t\t\t\t\t\t\t\t\t\t\t\t\tmaxSpeed = 0f;\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Setting JunctionTransitState to LEAVE (no incoming cars)\");\n#endif\n\t\t\t\t\t\t\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Vehicle has not yet reached yield speed (reduce {sqrVelocity} by {vehicleState.reduceSqrSpeedByValueToYield})\");\n#endif\n\n\t\t\t\t\t\t\t\t\t\t\t// vehicle has not yet reached yield speed\n\t\t\t\t\t\t\t\t\t\t\tmaxSpeed = GlobalConfig.Instance.PriorityRules.MaxYieldVelocity;\n\t\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Setting JunctionTransitState to LEAVE (max wait timeout)\");\n#endif\n\t\t\t\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase PriorityType.Main:\n\t\t\t\t\t\t\t\tdefault:\n#if DEBUG\n\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): MAIN sign. waittime={vehicleState.waitTime}\");\n#endif\n\t\t\t\t\t\t\t\t\tmaxSpeed = 0f;\n\n\t\t\t\t\t\t\t\t\tif (Options.simAccuracy == 4)\n\t\t\t\t\t\t\t\t\t\treturn true;\n\n\t\t\t\t\t\t\t\t\tif (vehicleState.waitTime < GlobalConfig.Instance.PriorityRules.MaxPriorityWaitTime) {\n\t\t\t\t\t\t\t\t\t\tvehicleState.waitTime++;\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Setting JunctionTransitState to STOP (wait)\");\n#endif\n\t\t\t\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Stop;\n\n\t\t\t\t\t\t\t\t\t\thasPriority = prioMan.HasPriority(frontVehicleId, ref vehicleData, ref prevPos, targetNodeId, isTargetStartNode, ref position, ref targetNode);\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): {hasPriority}\");\n#endif\n\n\t\t\t\t\t\t\t\t\t\tif (!hasPriority) {\n\t\t\t\t\t\t\t\t\t\t\tvehicleData.m_blockCounter = 0;\n\t\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t\t\t}\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Setting JunctionTransitState to LEAVE (no conflicting car)\");\n#endif\n\t\t\t\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave;\n\t\t\t\t\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Max. wait time exceeded. Setting JunctionTransitState to LEAVE (max wait timeout)\");\n#endif\n\t\t\t\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Leave;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (sqrVelocity <= GlobalConfig.Instance.PriorityRules.MaxStopVelocity * GlobalConfig.Instance.PriorityRules.MaxStopVelocity && (vehicleState.vehicleType & ExtVehicleType.RoadVehicle) != ExtVehicleType.None) {\n\t\t\t\t\t\t\t// vehicle is not moving. reset allowance to leave junction\n#if DEBUG\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayChangeSegment({frontVehicleId}): Setting JunctionTransitState from LEAVE to BLOCKED (speed to low)\");\n#endif\n\t\t\t\t\t\t\tvehicleState.JunctionTransitState = VehicleJunctionTransitState.Blocked;\n\n\t\t\t\t\t\t\tmaxSpeed = 0f;\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t\t//}\n#endif\n\t\t\t\t}\n\t\t\t}\n\t\t\tmaxSpeed = 0f; // maxSpeed should be set by caller\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Checks for traffic lights and priority signs when changing segments (for rail vehicles).\n\t\t/// Sets the maximum allowed speed <paramref name=\"maxSpeed\"/> if segment change is not allowed (otherwise <paramref name=\"maxSpeed\"/> has to be set by the calling method).\n\t\t/// </summary>\n\t\t/// <param name=\"frontVehicleId\">vehicle id</param>\n\t\t/// <param name=\"vehicleData\">vehicle data</param>\n\t\t/// <param name=\"sqrVelocity\">last frame squared velocity</param>\n\t\t/// <param name=\"prevPos\">previous path position</param>\n\t\t/// <param name=\"prevTargetNodeId\">previous target node</param>\n\t\t/// <param name=\"prevLaneID\">previous lane</param>\n\t\t/// <param name=\"position\">current path position</param>\n\t\t/// <param name=\"targetNodeId\">transit node</param>\n\t\t/// <param name=\"laneID\">current lane</param>\n\t\t/// <param name=\"maxSpeed\">maximum allowed speed (only valid if method returns false)</param>\n\t\t/// <returns>true, if the vehicle may change segments, false otherwise.</returns>\n\t\tpublic bool MayChangeSegment(ushort frontVehicleId, ref Vehicle vehicleData, float sqrVelocity, ref PathUnit.Position prevPos, ref NetSegment prevSegment, ushort prevTargetNodeId, uint prevLaneID, ref PathUnit.Position position, ushort targetNodeId, ref NetNode targetNode, uint laneID, out float maxSpeed) {\n\t\t\treturn MayChangeSegment(frontVehicleId, ref Constants.ManagerFactory.VehicleStateManager.VehicleStates[frontVehicleId], ref vehicleData, sqrVelocity, ref prevPos, ref prevSegment, prevTargetNodeId, prevLaneID, ref position, targetNodeId, ref targetNode, laneID, ref DUMMY_POS, 0, out maxSpeed);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Checks if a vehicle must check if the subsequent segment is empty while going from segment <paramref name=\"segmentId\"/>\n\t\t/// through node <paramref name=\"startNode\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">source segment id</param>\n\t\t/// <param name=\"startNode\">is transit node start node of source segment?</param>\n\t\t/// <param name=\"node\">transit node</param>\n\t\t/// <param name=\"isRecklessDriver\">reckless driver?</param>\n\t\t/// <returns></returns>\n\t\tprotected bool MustCheckSpace(ushort segmentId, bool startNode, ref NetNode node, bool isRecklessDriver) {\n\t\t\tbool checkSpace;\n\t\t\tif (isRecklessDriver) {\n\t\t\t\tcheckSpace = (node.m_flags & NetNode.Flags.LevelCrossing) != NetNode.Flags.None;\n\t\t\t} else {\n\t\t\t\tif (Options.junctionRestrictionsEnabled) {\n\t\t\t\t\tcheckSpace = !JunctionRestrictionsManager.Instance.IsEnteringBlockedJunctionAllowed(segmentId, startNode);\n\t\t\t\t} else {\n\t\t\t\t\tcheckSpace = (node.m_flags & (NetNode.Flags.Junction | NetNode.Flags.OneWayOut | NetNode.Flags.OneWayIn)) == NetNode.Flags.Junction && node.CountSegments() != 2;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn checkSpace;\n\t\t}\n\n\t\tpublic bool MayDespawn(ref Vehicle vehicleData) {\n\t\t\treturn !Options.disableDespawning || ((vehicleData.m_flags2 & (Vehicle.Flags2.Blown | Vehicle.Flags2.Floating)) != 0) || (vehicleData.m_flags & Vehicle.Flags.Parking) != 0;\n\t\t}\n\n\t\tpublic float CalcMaxSpeed(ushort vehicleId, ref VehicleState state, VehicleInfo vehicleInfo, PathUnit.Position position, ref NetSegment segment, Vector3 pos, float maxSpeed) {\n\t\t\tif (Singleton<NetManager>.instance.m_treatWetAsSnow) {\n\t\t\t\tDistrictManager districtManager = Singleton<DistrictManager>.instance;\n\t\t\t\tbyte district = districtManager.GetDistrict(pos);\n\t\t\t\tDistrictPolicies.CityPlanning cityPlanningPolicies = districtManager.m_districts.m_buffer[(int)district].m_cityPlanningPolicies;\n\t\t\t\tif ((cityPlanningPolicies & DistrictPolicies.CityPlanning.StuddedTires) != DistrictPolicies.CityPlanning.None) {\n\t\t\t\t\tif (Options.strongerRoadConditionEffects) {\n\t\t\t\t\t\tif (maxSpeed > ICY_ROADS_STUDDED_MIN_SPEED)\n\t\t\t\t\t\t\tmaxSpeed = ICY_ROADS_STUDDED_MIN_SPEED + (float)(255 - segment.m_wetness) * 0.0039215686f * (maxSpeed - ICY_ROADS_STUDDED_MIN_SPEED);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmaxSpeed *= 1f - (float)segment.m_wetness * 0.0005882353f; // vanilla: -15% .. ±0%\n\t\t\t\t\t}\n\t\t\t\t\tdistrictManager.m_districts.m_buffer[(int)district].m_cityPlanningPoliciesEffect |= DistrictPolicies.CityPlanning.StuddedTires;\n\t\t\t\t} else {\n\t\t\t\t\tif (Options.strongerRoadConditionEffects) {\n\t\t\t\t\t\tif (maxSpeed > ICY_ROADS_MIN_SPEED)\n\t\t\t\t\t\t\tmaxSpeed = ICY_ROADS_MIN_SPEED + (float)(255 - segment.m_wetness) * 0.0039215686f * (maxSpeed - ICY_ROADS_MIN_SPEED);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmaxSpeed *= 1f - (float)segment.m_wetness * 0.00117647066f; // vanilla: -30% .. ±0%\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (Options.strongerRoadConditionEffects) {\n\t\t\t\t\tfloat minSpeed = Math.Min(maxSpeed * WET_ROADS_FACTOR, WET_ROADS_MAX_SPEED); // custom: -25% .. 0\n\t\t\t\t\tif (maxSpeed > minSpeed)\n\t\t\t\t\t\tmaxSpeed = minSpeed + (float)(255 - segment.m_wetness) * 0.0039215686f * (maxSpeed - minSpeed);\n\t\t\t\t} else {\n\t\t\t\t\tmaxSpeed *= 1f - (float)segment.m_wetness * 0.0005882353f; // vanilla: -15% .. ±0%\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (Options.strongerRoadConditionEffects) {\n\t\t\t\tfloat minSpeed = Math.Min(maxSpeed * BROKEN_ROADS_FACTOR, BROKEN_ROADS_MAX_SPEED);\n\t\t\t\tif (maxSpeed > minSpeed) {\n\t\t\t\t\tmaxSpeed = minSpeed + (float)segment.m_condition * 0.0039215686f * (maxSpeed - minSpeed);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tmaxSpeed *= 1f + (float)segment.m_condition * 0.0005882353f; // vanilla: ±0% .. +15 %\n\t\t\t}\n\n\t\t\tmaxSpeed = ApplyRealisticSpeeds(maxSpeed, vehicleId, ref state, vehicleInfo);\n\t\t\tmaxSpeed = Math.Max(MIN_SPEED, maxSpeed); // at least 10 km/h\n\n\t\t\treturn maxSpeed;\n\t\t}\n\n\t\tpublic uint GetStaticVehicleRand(ushort vehicleId) {\n\t\t\treturn vehicleId % 100u;\n\t\t}\n\n\t\tpublic uint GetTimedVehicleRand(ushort vehicleId) {\n\t\t\tuint intv = VehicleState.MAX_TIMED_RAND / 2u;\n\t\t\tuint range = intv * (uint)(vehicleId % (100u / intv)); // is one of [0, 50]\n\t\t\tuint step = VehicleStateManager.Instance.VehicleStates[vehicleId].timedRand;\n\t\t\tif (step >= intv) {\n\t\t\t\tstep = VehicleState.MAX_TIMED_RAND - step;\n\t\t\t}\n\n\t\t\treturn range + step;\n\t\t}\n\n\t\tpublic float ApplyRealisticSpeeds(float speed, ushort vehicleId, ref VehicleState state, VehicleInfo vehicleInfo) {\n\t\t\tif (Options.realisticSpeeds) {\n\t\t\t\tfloat vehicleRand = 0.01f * (float)GetTimedVehicleRand(vehicleId);\n\t\t\t\tif (vehicleInfo.m_isLargeVehicle) {\n\t\t\t\t\tspeed *= 0.75f + vehicleRand * 0.25f; // a little variance, 0.75 .. 1\n\t\t\t\t} else if (state.recklessDriver) {\n\t\t\t\t\tspeed *= 1.1f + vehicleRand * 0.5f; // woohooo, 1.1 .. 1.6\n\t\t\t\t} else {\n\t\t\t\t\tspeed *= 0.8f + vehicleRand * 0.5f; // a little variance, 0.8 .. 1.3\n\t\t\t\t}\n\t\t\t} else if (state.recklessDriver) {\n\t\t\t\tspeed *= 1.5f;\n\t\t\t}\n\t\t\treturn speed;\n\t\t}\n\n\t\tpublic bool IsRecklessDriver(ushort vehicleId, ref Vehicle vehicleData) {\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (Options.evacBussesMayIgnoreRules && vehicleData.Info.GetService() == ItemClass.Service.Disaster) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (Options.recklessDrivers == 3) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif ((vehicleData.Info.m_vehicleType & RECKLESS_VEHICLE_TYPES) == VehicleInfo.VehicleType.None) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn (uint)vehicleId % Options.getRecklessDriverModulo() == 0;\n\t\t}\n\n\t\tpublic int FindBestLane(ushort vehicleId, ref Vehicle vehicleData, ref VehicleState vehicleState, uint currentLaneId, PathUnit.Position currentPathPos, NetInfo currentSegInfo, PathUnit.Position next1PathPos, NetInfo next1SegInfo, PathUnit.Position next2PathPos, NetInfo next2SegInfo, PathUnit.Position next3PathPos, NetInfo next3SegInfo, PathUnit.Position next4PathPos) {\n\t\t\ttry {\n\t\t\t\tGlobalConfig conf = GlobalConfig.Instance;\n#if DEBUG\n\t\t\t\tbool debug = false;\n\t\t\t\tif (conf.Debug.Switches[17]) {\n\t\t\t\t\tushort nodeId = Services.NetService.GetSegmentNodeId(currentPathPos.m_segment, currentPathPos.m_offset < 128);\n\t\t\t\t\tdebug = (conf.Debug.VehicleId == 0 || conf.Debug.VehicleId == vehicleId) && (conf.Debug.NodeId == 0 || conf.Debug.NodeId == nodeId);\n\t\t\t\t}\n\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): currentLaneId={currentLaneId}, currentPathPos=[seg={currentPathPos.m_segment}, lane={currentPathPos.m_lane}, off={currentPathPos.m_offset}] next1PathPos=[seg={next1PathPos.m_segment}, lane={next1PathPos.m_lane}, off={next1PathPos.m_offset}] next2PathPos=[seg={next2PathPos.m_segment}, lane={next2PathPos.m_lane}, off={next2PathPos.m_offset}] next3PathPos=[seg={next3PathPos.m_segment}, lane={next3PathPos.m_lane}, off={next3PathPos.m_offset}] next4PathPos=[seg={next4PathPos.m_segment}, lane={next4PathPos.m_lane}, off={next4PathPos.m_offset}]\");\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (vehicleState.lastAltLaneSelSegmentId == currentPathPos.m_segment) {\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Skipping alternative lane selection: Already calculated.\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn next1PathPos.m_lane;\n\t\t\t\t}\n\t\t\t\tvehicleState.lastAltLaneSelSegmentId = currentPathPos.m_segment;\n\n\t\t\t\tbool recklessDriver = vehicleState.recklessDriver;\n\n\t\t\t\t// cur -> next1\n\t\t\t\tfloat vehicleLength = 1f + vehicleState.totalLength;\n\t\t\t\tbool startNode = currentPathPos.m_offset < 128;\n\t\t\t\tuint currentFwdRoutingIndex = RoutingManager.Instance.GetLaneEndRoutingIndex(currentLaneId, startNode);\n\n#if DEBUG\n\t\t\t\tif (currentFwdRoutingIndex < 0 || currentFwdRoutingIndex >= RoutingManager.Instance.laneEndForwardRoutings.Length) {\n\t\t\t\t\tLog.Error($\"Invalid array index: currentFwdRoutingIndex={currentFwdRoutingIndex}, RoutingManager.Instance.laneEndForwardRoutings.Length={RoutingManager.Instance.laneEndForwardRoutings.Length} (currentLaneId={currentLaneId}, startNode={startNode})\");\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (!RoutingManager.Instance.laneEndForwardRoutings[currentFwdRoutingIndex].routed) {\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): No forward routing for next path position available.\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn next1PathPos.m_lane;\n\t\t\t\t}\n\n\t\t\t\tLaneTransitionData[] currentFwdTransitions = RoutingManager.Instance.laneEndForwardRoutings[currentFwdRoutingIndex].transitions;\n\n\t\t\t\tif (currentFwdTransitions == null) {\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): No forward transitions found for current lane {currentLaneId} at startNode {startNode}.\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn next1PathPos.m_lane;\n\t\t\t\t}\n\n\t\t\t\tVehicleInfo vehicleInfo = vehicleData.Info;\n\t\t\t\tfloat vehicleMaxSpeed = vehicleInfo.m_maxSpeed / 8f;\n\t\t\t\tfloat vehicleCurSpeed = vehicleData.GetLastFrameVelocity().magnitude / 8f;\n\n\t\t\t\tfloat bestStayMeanSpeed = 0f;\n\t\t\t\tfloat bestStaySpeedDiff = float.PositiveInfinity; // best speed difference on next continuous lane\n\t\t\t\tint bestStayTotalLaneDist = int.MaxValue;\n\t\t\t\tbyte bestStayNext1LaneIndex = next1PathPos.m_lane;\n\n\t\t\t\tfloat bestOptMeanSpeed = 0f;\n\t\t\t\tfloat bestOptSpeedDiff = float.PositiveInfinity; // best speed difference on all next lanes\n\t\t\t\tint bestOptTotalLaneDist = int.MaxValue;\n\t\t\t\tbyte bestOptNext1LaneIndex = next1PathPos.m_lane;\n\n\t\t\t\tbool foundSafeLaneChange = false;\n\t\t\t\t//bool foundClearBackLane = false;\n\t\t\t\t//bool foundClearFwdLane = false;\n\n\t\t\t\t//ushort reachableNext1LanesMask = 0;\n\t\t\t\tuint reachableNext2LanesMask = 0;\n\t\t\t\tuint reachableNext3LanesMask = 0;\n\n\t\t\t\t//int numReachableNext1Lanes = 0;\n\t\t\t\tint numReachableNext2Lanes = 0;\n\t\t\t\tint numReachableNext3Lanes = 0;\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Starting lane-finding algorithm now. vehicleMaxSpeed={vehicleMaxSpeed}, vehicleCurSpeed={vehicleCurSpeed} vehicleLength={vehicleLength}\");\n\t\t\t\t}\n#endif\n\n\t\t\t\tuint mask;\n\t\t\t\tfor (int i = 0; i < currentFwdTransitions.Length; ++i) {\n\t\t\t\t\tif (currentFwdTransitions[i].segmentId != next1PathPos.m_segment) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!(currentFwdTransitions[i].type == LaneEndTransitionType.Default ||\n\t\t\t\t\t\tcurrentFwdTransitions[i].type == LaneEndTransitionType.LaneConnection ||\n\t\t\t\t\t\t(recklessDriver && currentFwdTransitions[i].type == LaneEndTransitionType.Relaxed))\n\t\t\t\t\t) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (currentFwdTransitions[i].distance > 1) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Skipping current transition {currentFwdTransitions[i]} (distance too large)\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!VehicleRestrictionsManager.Instance.MayUseLane(vehicleState.vehicleType, next1PathPos.m_segment, currentFwdTransitions[i].laneIndex, next1SegInfo)) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Skipping current transition {currentFwdTransitions[i]} (vehicle restrictions)\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tint minTotalLaneDist = int.MaxValue;\n\n\t\t\t\t\tif (next2PathPos.m_segment != 0) {\n\t\t\t\t\t\t// next1 -> next2\n\t\t\t\t\t\tuint next1FwdRoutingIndex = RoutingManager.Instance.GetLaneEndRoutingIndex(currentFwdTransitions[i].laneId, !currentFwdTransitions[i].startNode);\n#if DEBUG\n\t\t\t\t\t\tif (next1FwdRoutingIndex < 0 || next1FwdRoutingIndex >= RoutingManager.Instance.laneEndForwardRoutings.Length) {\n\t\t\t\t\t\t\tLog.Error($\"Invalid array index: next1FwdRoutingIndex={next1FwdRoutingIndex}, RoutingManager.Instance.laneEndForwardRoutings.Length={RoutingManager.Instance.laneEndForwardRoutings.Length} (currentFwdTransitions[i].laneId={currentFwdTransitions[i].laneId}, !currentFwdTransitions[i].startNode={!currentFwdTransitions[i].startNode})\");\n\t\t\t\t\t\t}\n#endif\n\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Exploring transitions for next1 lane id={currentFwdTransitions[i].laneId}, seg.={currentFwdTransitions[i].segmentId}, index={currentFwdTransitions[i].laneIndex}, startNode={!currentFwdTransitions[i].startNode}: {RoutingManager.Instance.laneEndForwardRoutings[next1FwdRoutingIndex]}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tif (!RoutingManager.Instance.laneEndForwardRoutings[next1FwdRoutingIndex].routed) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tLaneTransitionData[] next1FwdTransitions = RoutingManager.Instance.laneEndForwardRoutings[next1FwdRoutingIndex].transitions;\n\n\t\t\t\t\t\tif (next1FwdTransitions == null) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbool foundNext1Next2 = false;\n\t\t\t\t\t\tfor (int j = 0; j < next1FwdTransitions.Length; ++j) {\n\t\t\t\t\t\t\tif (next1FwdTransitions[j].segmentId != next2PathPos.m_segment) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (!(next1FwdTransitions[j].type == LaneEndTransitionType.Default ||\n\t\t\t\t\t\t\t\tnext1FwdTransitions[j].type == LaneEndTransitionType.LaneConnection ||\n\t\t\t\t\t\t\t\t(recklessDriver && next1FwdTransitions[j].type == LaneEndTransitionType.Relaxed))\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (next1FwdTransitions[j].distance > 1) {\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Skipping next1 transition {next1FwdTransitions[j]} (distance too large)\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (!VehicleRestrictionsManager.Instance.MayUseLane(vehicleState.vehicleType, next2PathPos.m_segment, next1FwdTransitions[j].laneIndex, next2SegInfo)) {\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Skipping next1 transition {next1FwdTransitions[j]} (vehicle restrictions)\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (next3PathPos.m_segment != 0) {\n\t\t\t\t\t\t\t\t// next2 -> next3\n\t\t\t\t\t\t\t\tuint next2FwdRoutingIndex = RoutingManager.Instance.GetLaneEndRoutingIndex(next1FwdTransitions[j].laneId, !next1FwdTransitions[j].startNode);\n#if DEBUG\n\t\t\t\t\t\t\t\tif (next2FwdRoutingIndex < 0 || next2FwdRoutingIndex >= RoutingManager.Instance.laneEndForwardRoutings.Length) {\n\t\t\t\t\t\t\t\t\tLog.Error($\"Invalid array index: next2FwdRoutingIndex={next2FwdRoutingIndex}, RoutingManager.Instance.laneEndForwardRoutings.Length={RoutingManager.Instance.laneEndForwardRoutings.Length} (next1FwdTransitions[j].laneId={next1FwdTransitions[j].laneId}, !next1FwdTransitions[j].startNode={!next1FwdTransitions[j].startNode})\");\n\t\t\t\t\t\t\t\t}\n#endif\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Exploring transitions for next2 lane id={next1FwdTransitions[j].laneId}, seg.={next1FwdTransitions[j].segmentId}, index={next1FwdTransitions[j].laneIndex}, startNode={!next1FwdTransitions[j].startNode}: {RoutingManager.Instance.laneEndForwardRoutings[next2FwdRoutingIndex]}\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\tif (!RoutingManager.Instance.laneEndForwardRoutings[next2FwdRoutingIndex].routed) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tLaneTransitionData[] next2FwdTransitions = RoutingManager.Instance.laneEndForwardRoutings[next2FwdRoutingIndex].transitions;\n\n\t\t\t\t\t\t\t\tif (next2FwdTransitions == null) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tbool foundNext2Next3 = false;\n\t\t\t\t\t\t\t\tfor (int k = 0; k < next2FwdTransitions.Length; ++k) {\n\t\t\t\t\t\t\t\t\tif (next2FwdTransitions[k].segmentId != next3PathPos.m_segment) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (!(next2FwdTransitions[k].type == LaneEndTransitionType.Default ||\n\t\t\t\t\t\t\t\t\t\tnext2FwdTransitions[k].type == LaneEndTransitionType.LaneConnection ||\n\t\t\t\t\t\t\t\t\t\t(recklessDriver && next2FwdTransitions[k].type == LaneEndTransitionType.Relaxed))\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (next2FwdTransitions[k].distance > 1) {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Skipping next2 transition {next2FwdTransitions[k]} (distance too large)\");\n\t\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (!VehicleRestrictionsManager.Instance.MayUseLane(vehicleState.vehicleType, next3PathPos.m_segment, next2FwdTransitions[k].laneIndex, next3SegInfo)) {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Skipping next2 transition {next2FwdTransitions[k]} (vehicle restrictions)\");\n\t\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (next4PathPos.m_segment != 0) {\n\t\t\t\t\t\t\t\t\t\t// next3 -> next4\n\t\t\t\t\t\t\t\t\t\tuint next3FwdRoutingIndex = RoutingManager.Instance.GetLaneEndRoutingIndex(next2FwdTransitions[k].laneId, !next2FwdTransitions[k].startNode);\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (next3FwdRoutingIndex < 0 || next3FwdRoutingIndex >= RoutingManager.Instance.laneEndForwardRoutings.Length) {\n\t\t\t\t\t\t\t\t\t\t\tLog.Error($\"Invalid array index: next3FwdRoutingIndex={next3FwdRoutingIndex}, RoutingManager.Instance.laneEndForwardRoutings.Length={RoutingManager.Instance.laneEndForwardRoutings.Length} (next2FwdTransitions[k].laneId={next2FwdTransitions[k].laneId}, !next2FwdTransitions[k].startNode={!next2FwdTransitions[k].startNode})\");\n\t\t\t\t\t\t\t\t\t\t}\n#endif\n\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Exploring transitions for next3 lane id={next2FwdTransitions[k].laneId}, seg.={next2FwdTransitions[k].segmentId}, index={next2FwdTransitions[k].laneIndex}, startNode={!next2FwdTransitions[k].startNode}: {RoutingManager.Instance.laneEndForwardRoutings[next3FwdRoutingIndex]}\");\n\t\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\t\tif (!RoutingManager.Instance.laneEndForwardRoutings[next3FwdRoutingIndex].routed) {\n\t\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tLaneTransitionData[] next3FwdTransitions = RoutingManager.Instance.laneEndForwardRoutings[next3FwdRoutingIndex].transitions;\n\n\t\t\t\t\t\t\t\t\t\tif (next3FwdTransitions == null) {\n\t\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// check if original next4 lane is accessible via the next3 lane\n\t\t\t\t\t\t\t\t\t\tbool foundNext3Next4 = false;\n\t\t\t\t\t\t\t\t\t\tfor (int l = 0; l < next3FwdTransitions.Length; ++l) {\n\t\t\t\t\t\t\t\t\t\t\tif (next3FwdTransitions[l].segmentId != next4PathPos.m_segment) {\n\t\t\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\tif (!(next3FwdTransitions[l].type == LaneEndTransitionType.Default ||\n\t\t\t\t\t\t\t\t\t\t\t\tnext3FwdTransitions[l].type == LaneEndTransitionType.LaneConnection ||\n\t\t\t\t\t\t\t\t\t\t\t\t(recklessDriver && next3FwdTransitions[l].type == LaneEndTransitionType.Relaxed))\n\t\t\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\tif (next3FwdTransitions[l].distance > 1) {\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Skipping next3 transition {next3FwdTransitions[l]} (distance too large)\");\n\t\t\t\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\tif (next3FwdTransitions[l].laneIndex == next4PathPos.m_lane) {\n\t\t\t\t\t\t\t\t\t\t\t\t// we found a valid routing from [current lane] (currentPathPos) to [next1 lane] (next1Pos), [next2 lane] (next2Pos), [next3 lane] (next3Pos), and [next4 lane] (next4Pos)\n\n\t\t\t\t\t\t\t\t\t\t\t\tfoundNext3Next4 = true;\n\t\t\t\t\t\t\t\t\t\t\t\tint totalLaneDist = next1FwdTransitions[j].distance + next2FwdTransitions[k].distance + next3FwdTransitions[l].distance;\n\t\t\t\t\t\t\t\t\t\t\t\tif (totalLaneDist < minTotalLaneDist) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tminTotalLaneDist = totalLaneDist;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n#if DEBUG\n\t\t\t\t\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Found candidate transition with totalLaneDist={totalLaneDist}: {currentLaneId} -> {currentFwdTransitions[i]} -> {next1FwdTransitions[j]} -> {next2FwdTransitions[k]} -> {next3FwdTransitions[l]}\");\n\t\t\t\t\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t} // for l\n\n\t\t\t\t\t\t\t\t\t\tif (foundNext3Next4) {\n\t\t\t\t\t\t\t\t\t\t\tfoundNext2Next3 = true;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tfoundNext2Next3 = true;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (foundNext2Next3) {\n\t\t\t\t\t\t\t\t\t\tmask = POW2MASKS[next2FwdTransitions[k].laneIndex];\n\t\t\t\t\t\t\t\t\t\tif ((reachableNext3LanesMask & mask) == 0) {\n\t\t\t\t\t\t\t\t\t\t\t++numReachableNext3Lanes;\n\t\t\t\t\t\t\t\t\t\t\treachableNext3LanesMask |= mask;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} // for k\n\n\t\t\t\t\t\t\t\tif (foundNext2Next3) {\n\t\t\t\t\t\t\t\t\tfoundNext1Next2 = true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tfoundNext1Next2 = true;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (foundNext1Next2) {\n\t\t\t\t\t\t\t\tmask = POW2MASKS[next1FwdTransitions[j].laneIndex];\n\t\t\t\t\t\t\t\tif ((reachableNext2LanesMask & mask) == 0) {\n\t\t\t\t\t\t\t\t\t++numReachableNext2Lanes;\n\t\t\t\t\t\t\t\t\treachableNext2LanesMask |= mask;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} // for j\n\n\t\t\t\t\t\tif (next3PathPos.m_segment != 0 && !foundNext1Next2) {\n\t\t\t\t\t\t\t// go to next candidate next1 lane\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t/*mask = POW2MASKS[currentFwdTransitions[i].laneIndex];\n\t\t\t\t\tif ((reachableNext1LanesMask & mask) == 0) {\n\t\t\t\t\t\t++numReachableNext1Lanes;\n\t\t\t\t\t\treachableNext1LanesMask |= mask;\n\t\t\t\t\t}*/\n\n\t\t\t\t\t// This lane is a valid candidate.\n\n\t\t\t\t\t//bool next1StartNode = next1PathPos.m_offset < 128;\n\t\t\t\t\t//ushort next1TransitNode = 0;\n\t\t\t\t\t//Services.NetService.ProcessSegment(next1PathPos.m_segment, delegate (ushort next1SegId, ref NetSegment next1Seg) {\n\t\t\t\t\t//\tnext1TransitNode = next1StartNode ? next1Seg.m_startNode : next1Seg.m_endNode;\n\t\t\t\t\t//\treturn true;\n\t\t\t\t\t//});\n\n\t\t\t\t\t//bool next1TransitNodeIsJunction = false;\n\t\t\t\t\t//Services.NetService.ProcessNode(next1TransitNode, delegate (ushort nId, ref NetNode node) {\n\t\t\t\t\t//\tnext1TransitNodeIsJunction = (node.m_flags & NetNode.Flags.Junction) != NetNode.Flags.None;\n\t\t\t\t\t//\treturn true;\n\t\t\t\t\t//});\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Check if next1 lane is clear\n\t\t\t\t\t */\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Checking for traffic on next1 lane id={currentFwdTransitions[i].laneId}.\");\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tbool laneChange = currentFwdTransitions[i].distance != 0;\n\t\t\t\t\t/*bool next1LaneClear = true;\n\t\t\t\t\tif (laneChange) {\n\t\t\t\t\t\t// check for traffic on next1 lane\n\t\t\t\t\t\tfloat reservedSpace = 0;\n\t\t\t\t\t\tServices.NetService.ProcessLane(currentFwdTransitions[i].laneId, delegate (uint next1LaneId, ref NetLane next1Lane) {\n\t\t\t\t\t\t\treservedSpace = next1Lane.GetReservedSpace();\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (currentFwdTransitions[i].laneIndex == next1PathPos.m_lane) {\n\t\t\t\t\t\t\treservedSpace -= vehicleLength;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tnext1LaneClear = reservedSpace <= (recklessDriver ? conf.AltLaneSelectionMaxRecklessReservedSpace : conf.AltLaneSelectionMaxReservedSpace);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (foundClearFwdLane && !next1LaneClear) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}*/\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Check traffic on the lanes in front of the candidate lane in order to prevent vehicles from backing up traffic\n\t\t\t\t\t */\n\t\t\t\t\tbool prevLanesClear = true;\n\t\t\t\t\tif (laneChange) {\n\t\t\t\t\t\tuint next1BackRoutingIndex = RoutingManager.Instance.GetLaneEndRoutingIndex(currentFwdTransitions[i].laneId, currentFwdTransitions[i].startNode);\n#if DEBUG\n\t\t\t\t\t\tif (next1BackRoutingIndex < 0 || next1BackRoutingIndex >= RoutingManager.Instance.laneEndForwardRoutings.Length) {\n\t\t\t\t\t\t\tLog.Error($\"Invalid array index: next1BackRoutingIndex={next1BackRoutingIndex}, RoutingManager.Instance.laneEndForwardRoutings.Length={RoutingManager.Instance.laneEndForwardRoutings.Length} (currentFwdTransitions[i].laneId={currentFwdTransitions[i].laneId}, currentFwdTransitions[i].startNode={currentFwdTransitions[i].startNode})\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tif (!RoutingManager.Instance.laneEndBackwardRoutings[next1BackRoutingIndex].routed) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tLaneTransitionData[] next1BackTransitions = RoutingManager.Instance.laneEndBackwardRoutings[next1BackRoutingIndex].transitions;\n\n\t\t\t\t\t\tif (next1BackTransitions == null) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor (int j = 0; j < next1BackTransitions.Length; ++j) {\n\t\t\t\t\t\t\tif (next1BackTransitions[j].segmentId != currentPathPos.m_segment ||\n\t\t\t\t\t\t\t\tnext1BackTransitions[j].laneIndex == currentPathPos.m_lane) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (!(next1BackTransitions[j].type == LaneEndTransitionType.Default ||\n\t\t\t\t\t\t\t\tnext1BackTransitions[j].type == LaneEndTransitionType.LaneConnection ||\n\t\t\t\t\t\t\t\t(recklessDriver && next1BackTransitions[j].type == LaneEndTransitionType.Relaxed))\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (next1BackTransitions[j].distance > 1) {\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Skipping next1 backward transition {next1BackTransitions[j]} (distance too large)\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Checking for upcoming traffic in front of next1 lane id={currentFwdTransitions[i].laneId}. Checking back transition {next1BackTransitions[j]}\");\n\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\tServices.NetService.ProcessLane(next1BackTransitions[j].laneId, delegate (uint prevLaneId, ref NetLane prevLane) {\n\t\t\t\t\t\t\t\tprevLanesClear = prevLane.GetReservedSpace() <= (recklessDriver ? conf.DynamicLaneSelection.MaxRecklessReservedSpace : conf.DynamicLaneSelection.MaxReservedSpace);\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tif (!prevLanesClear) {\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Back lane {next1BackTransitions[j].laneId} is not clear!\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Back lane {next1BackTransitions[j].laneId} is clear!\");\n\t\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Checking for coming up traffic in front of next1 lane. prevLanesClear={prevLanesClear}\");\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tif (/*foundClearBackLane*/foundSafeLaneChange && !prevLanesClear) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// calculate lane metric\n#if DEBUG\n\t\t\t\t\tif (currentFwdTransitions[i].laneIndex < 0 || currentFwdTransitions[i].laneIndex >= next1SegInfo.m_lanes.Length) {\n\t\t\t\t\t\tLog.Error($\"Invalid array index: currentFwdTransitions[i].laneIndex={currentFwdTransitions[i].laneIndex}, next1SegInfo.m_lanes.Length={next1SegInfo.m_lanes.Length}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tNetInfo.Lane next1LaneInfo = next1SegInfo.m_lanes[currentFwdTransitions[i].laneIndex];\n\t\t\t\t\tfloat next1MaxSpeed = SpeedLimitManager.Instance.GetLockFreeGameSpeedLimit(currentFwdTransitions[i].segmentId, currentFwdTransitions[i].laneIndex, currentFwdTransitions[i].laneId, next1LaneInfo);\n\t\t\t\t\tfloat targetSpeed = Math.Min(vehicleMaxSpeed, ApplyRealisticSpeeds(next1MaxSpeed, vehicleId, ref vehicleState, vehicleInfo));\n\n\t\t\t\t\tushort meanSpeed = TrafficMeasurementManager.Instance.CalcLaneRelativeMeanSpeed(currentFwdTransitions[i].segmentId, currentFwdTransitions[i].laneIndex, currentFwdTransitions[i].laneId, next1LaneInfo);\n\n\t\t\t\t\tfloat relMeanSpeedInPercent = meanSpeed / (TrafficMeasurementManager.REF_REL_SPEED / TrafficMeasurementManager.REF_REL_SPEED_PERCENT_DENOMINATOR);\n\t\t\t\t\tfloat randSpeed = 0f;\n\t\t\t\t\tif (conf.DynamicLaneSelection.LaneSpeedRandInterval > 0) {\n\t\t\t\t\t\trandSpeed = Services.SimulationService.Randomizer.Int32((uint)conf.DynamicLaneSelection.LaneSpeedRandInterval + 1u) - conf.DynamicLaneSelection.LaneSpeedRandInterval / 2f;\n\t\t\t\t\t\trelMeanSpeedInPercent += randSpeed;\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat relMeanSpeed = relMeanSpeedInPercent / (float)TrafficMeasurementManager.REF_REL_SPEED_PERCENT_DENOMINATOR;\n\t\t\t\t\tfloat next1MeanSpeed = relMeanSpeed * next1MaxSpeed;\n\n\t\t\t\t\t/*if (\n#if DEBUG\n\t\t\t\t\tconf.Debug.Switches[19] &&\n#endif\n\t\t\t\t\tnext1LaneInfo.m_similarLaneCount > 1) {\n\t\t\t\t\t\tfloat relLaneInnerIndex = ((float)RoutingManager.Instance.CalcOuterSimilarLaneIndex(next1LaneInfo) / (float)next1LaneInfo.m_similarLaneCount);\n\t\t\t\t\t\tfloat rightObligationFactor = conf.AltLaneSelectionMostOuterLaneSpeedFactor + (conf.AltLaneSelectionMostInnerLaneSpeedFactor - conf.AltLaneSelectionMostOuterLaneSpeedFactor) * relLaneInnerIndex;\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Applying obligation factor to next1 lane {currentFwdTransitions[i].laneId}: relLaneInnerIndex={relLaneInnerIndex}, rightObligationFactor={rightObligationFactor}, next1MaxSpeed={next1MaxSpeed}, relMeanSpeedInPercent={relMeanSpeedInPercent}, randSpeed={randSpeed}, next1MeanSpeed={next1MeanSpeed} => new next1MeanSpeed={Mathf.Max(rightObligationFactor * next1MaxSpeed, next1MeanSpeed)}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tnext1MeanSpeed = Mathf.Min(rightObligationFactor * next1MaxSpeed, next1MeanSpeed);\n\t\t\t\t\t}*/\n\n\t\t\t\t\tfloat speedDiff = next1MeanSpeed - targetSpeed; // > 0: lane is faster than vehicle would go. < 0: vehicle could go faster than this lane allows\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Calculated metric for next1 lane {currentFwdTransitions[i].laneId}: next1MaxSpeed={next1MaxSpeed} next1MeanSpeed={next1MeanSpeed} targetSpeed={targetSpeed} speedDiff={speedDiff} bestSpeedDiff={bestOptSpeedDiff} bestStaySpeedDiff={bestStaySpeedDiff}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tif (!laneChange) {\n\t\t\t\t\t\tif ((float.IsInfinity(bestStaySpeedDiff) ||\n\t\t\t\t\t\t\t(bestStaySpeedDiff < 0 && speedDiff > bestStaySpeedDiff) ||\n\t\t\t\t\t\t\t(bestStaySpeedDiff > 0 && speedDiff < bestStaySpeedDiff && speedDiff >= 0))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tbestStaySpeedDiff = speedDiff;\n\t\t\t\t\t\t\tbestStayNext1LaneIndex = currentFwdTransitions[i].laneIndex;\n\t\t\t\t\t\t\tbestStayMeanSpeed = next1MeanSpeed;\n\t\t\t\t\t\t\tbestStayTotalLaneDist = minTotalLaneDist;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t//bool foundFirstClearFwdLane = laneChange && !foundClearFwdLane && next1LaneClear;\n\t\t\t\t\t\t//bool foundFirstClearBackLane = laneChange && !foundClearBackLane && prevLanesClear;\n\t\t\t\t\t\tbool foundFirstSafeLaneChange = !foundSafeLaneChange && /*next1LaneClear &&*/ prevLanesClear;\n\t\t\t\t\t\tif (/*(foundFirstClearFwdLane && !foundClearBackLane) ||\n\t\t\t\t\t\t\t(foundFirstClearBackLane && !foundClearFwdLane) ||*/\n\t\t\t\t\t\t\tfoundFirstSafeLaneChange ||\n\t\t\t\t\t\t\tfloat.IsInfinity(bestOptSpeedDiff) ||\n\t\t\t\t\t\t\t(bestOptSpeedDiff < 0 && speedDiff > bestOptSpeedDiff) ||\n\t\t\t\t\t\t\t(bestOptSpeedDiff > 0 && speedDiff < bestOptSpeedDiff && speedDiff >= 0)) {\n\t\t\t\t\t\t\tbestOptSpeedDiff = speedDiff;\n\t\t\t\t\t\t\tbestOptNext1LaneIndex = currentFwdTransitions[i].laneIndex;\n\t\t\t\t\t\t\tbestOptMeanSpeed = next1MeanSpeed;\n\t\t\t\t\t\t\tbestOptTotalLaneDist = minTotalLaneDist;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/*if (foundFirstClearBackLane) {\n\t\t\t\t\t\t\tfoundClearBackLane = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (foundFirstClearFwdLane) {\n\t\t\t\t\t\t\tfoundClearFwdLane = true;\n\t\t\t\t\t\t}*/\n\n\t\t\t\t\t\tif (foundFirstSafeLaneChange) {\n\t\t\t\t\t\t\tfoundSafeLaneChange = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} // for i\n\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): best lane index: {bestOptNext1LaneIndex}, best stay lane index: {bestStayNext1LaneIndex}, path lane index: {next1PathPos.m_lane})\\nbest speed diff: {bestOptSpeedDiff}, best stay speed diff: {bestStaySpeedDiff}\\nfoundClearBackLane=XXfoundClearBackLaneXX, foundClearFwdLane=XXfoundClearFwdLaneXX, foundSafeLaneChange={foundSafeLaneChange}\\nbestMeanSpeed={bestOptMeanSpeed}, bestStayMeanSpeed={bestStayMeanSpeed}\");\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (float.IsInfinity(bestStaySpeedDiff)) {\n\t\t\t\t\t// no continuous lane found\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): ===> no continuous lane found -- selecting bestOptNext1LaneIndex={bestOptNext1LaneIndex}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn bestOptNext1LaneIndex;\n\t\t\t\t}\n\n\t\t\t\tif (float.IsInfinity(bestOptSpeedDiff)) {\n\t\t\t\t\t// no lane change found\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): ===> no lane change found -- selecting bestStayNext1LaneIndex={bestStayNext1LaneIndex}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn bestStayNext1LaneIndex;\n\t\t\t\t}\n\n\t\t\t\t// decide if vehicle should stay or change\n\n\t\t\t\t// vanishing lane change opportunity detection\n\t\t\t\tint vehSel = vehicleId % 6;\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): vehMod4={vehSel} numReachableNext2Lanes={numReachableNext2Lanes} numReachableNext3Lanes={numReachableNext3Lanes}\");\n\t\t\t\t}\n#endif\n\t\t\t\tif ((numReachableNext3Lanes == 1 && vehSel <= 2) || // 3/6 % of all vehicles will change lanes 3 segments in front\n\t\t\t\t\t(numReachableNext2Lanes == 1 && vehSel <= 4) // 2/6 % of all vehicles will change lanes 2 segments in front, 1/5 will change at the last opportunity\n\t\t\t\t) {\n\t\t\t\t\t// vehicle must reach a certain lane since lane changing opportunities will vanish\n\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): vanishing lane change opportunities detected: numReachableNext2Lanes={numReachableNext2Lanes} numReachableNext3Lanes={numReachableNext3Lanes}, vehSel={vehSel}, bestOptTotalLaneDist={bestOptTotalLaneDist}, bestStayTotalLaneDist={bestStayTotalLaneDist}\");\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tif (bestOptTotalLaneDist < bestStayTotalLaneDist) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): ===> vanishing lane change opportunities -- selecting bestOptTotalLaneDist={bestOptTotalLaneDist}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn bestOptNext1LaneIndex;\n\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): ===> vanishing lane change opportunities -- selecting bestStayTotalLaneDist={bestStayTotalLaneDist}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn bestStayNext1LaneIndex;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (bestStaySpeedDiff == 0 || bestOptMeanSpeed < 0.1f) {\n\t\t\t\t\t/*\n\t\t\t\t\t * edge cases:\n\t\t\t\t\t *   (1) continuous lane is super optimal\n\t\t\t\t\t *   (2) best mean speed is near zero\n\t\t\t\t\t */\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): ===> edge case: continuous lane is optimal ({bestStaySpeedDiff == 0}) / best mean speed is near zero ({bestOptMeanSpeed < 0.1f}) -- selecting bestStayNext1LaneIndex={bestStayNext1LaneIndex}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn bestStayNext1LaneIndex;\n\t\t\t\t}\n\n\t\t\t\tif (bestStayTotalLaneDist != bestOptTotalLaneDist && Math.Max(bestStayTotalLaneDist, bestOptTotalLaneDist) > conf.DynamicLaneSelection.MaxOptLaneChanges) {\n\t\t\t\t\t/*\n\t\t\t\t\t * best route contains more lane changes than allowed: choose lane with the least number of future lane changes\n\t\t\t\t\t */\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): maximum best total lane distance = {Math.Max(bestStayTotalLaneDist, bestOptTotalLaneDist)} > AltLaneSelectionMaxOptLaneChanges\");\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tif (bestOptTotalLaneDist < bestStayTotalLaneDist) {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): ===> selecting lane change option for minimizing number of future lane changes -- selecting bestOptNext1LaneIndex={bestOptNext1LaneIndex}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn bestOptNext1LaneIndex;\n\t\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): ===> selecting stay option for minimizing number of future lane changes -- selecting bestStayNext1LaneIndex={bestStayNext1LaneIndex}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn bestStayNext1LaneIndex;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (bestStaySpeedDiff < 0 && bestOptSpeedDiff > bestStaySpeedDiff) {\n\t\t\t\t\t// found a lane change that improves vehicle speed\n\t\t\t\t\t//float improvement = 100f * ((bestOptSpeedDiff - bestStaySpeedDiff) / ((bestStayMeanSpeed + bestOptMeanSpeed) / 2f));\n\t\t\t\t\tushort optImprovementInKmH = SpeedLimitManager.Instance.LaneToCustomSpeedLimit(bestOptSpeedDiff - bestStaySpeedDiff, false);\n\t\t\t\t\tfloat speedDiff = Mathf.Abs(bestOptMeanSpeed - vehicleCurSpeed);\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): a lane change for speed improvement is possible. optImprovementInKmH={optImprovementInKmH} km/h speedDiff={speedDiff} (bestOptMeanSpeed={bestOptMeanSpeed}, vehicleCurVelocity={vehicleCurSpeed}, foundSafeLaneChange={foundSafeLaneChange})\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tif (optImprovementInKmH >= conf.DynamicLaneSelection.MinSafeSpeedImprovement &&\n\t\t\t\t\t\t(foundSafeLaneChange || (speedDiff <= conf.DynamicLaneSelection.MaxUnsafeSpeedDiff))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t// speed improvement is significant\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): ===> found a faster lane to change to and speed improvement is significant -- selecting bestOptNext1LaneIndex={bestOptNext1LaneIndex} (foundSafeLaneChange={foundSafeLaneChange}, speedDiff={speedDiff})\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn bestOptNext1LaneIndex;\n\t\t\t\t\t}\n\n\t\t\t\t\t// insufficient improvement\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): ===> found a faster lane to change to but speed improvement is NOT significant OR lane change is unsafe -- selecting bestStayNext1LaneIndex={bestStayNext1LaneIndex} (foundSafeLaneChange={foundSafeLaneChange})\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn bestStayNext1LaneIndex;\n\t\t\t\t} else if (!recklessDriver && foundSafeLaneChange && bestStaySpeedDiff > 0 && bestOptSpeedDiff < bestStaySpeedDiff && bestOptSpeedDiff >= 0) {\n\t\t\t\t\t// found a lane change that allows faster vehicles to overtake\n\t\t\t\t\tfloat optimization = 100f * ((bestStaySpeedDiff - bestOptSpeedDiff) / ((bestStayMeanSpeed + bestOptMeanSpeed) / 2f));\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): found a lane change that optimizes overall traffic. optimization={optimization}%\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tif (optimization >= conf.DynamicLaneSelection.MinSafeTrafficImprovement) {\n\t\t\t\t\t\t// traffic optimization is significant\n#if DEBUG\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): ===> found a lane that optimizes overall traffic and traffic optimization is significant -- selecting bestOptNext1LaneIndex={bestOptNext1LaneIndex}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\treturn bestOptNext1LaneIndex;\n\t\t\t\t\t}\n\n\t\t\t\t\t// insufficient optimization\n#if DEBUG\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): ===> found a lane that optimizes overall traffic but optimization is NOT significant -- selecting bestStayNext1LaneIndex={bestStayNext1LaneIndex}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\treturn bestOptNext1LaneIndex;\n\t\t\t\t}\n\n\t\t\t\t// suboptimal safe lane change\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.FindBestLane({vehicleId}): ===> suboptimal safe lane change detected -- selecting bestStayNext1LaneIndex={bestStayNext1LaneIndex}\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn bestStayNext1LaneIndex;\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"VehicleBehaviorManager.FindBestLane({vehicleId}): Exception occurred: {e}\");\n\t\t\t}\n\t\t\treturn next1PathPos.m_lane;\n\t\t}\n\n\t\tpublic bool MayFindBestLane(ushort vehicleId, ref Vehicle vehicleData, ref VehicleState vehicleState) {\n\t\t\tGlobalConfig conf = GlobalConfig.Instance;\n#if DEBUG\n\t\t\tbool debug = false; // conf.Debug.Switches[17] && (conf.Debug.VehicleId == 0 || conf.Debug.VehicleId == vehicleId);\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayFindBestLane({vehicleId}) called.\");\n\t\t\t}\n#endif\n\n\t\t\tif (!Options.advancedAI) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayFindBestLane({vehicleId}): Skipping lane checking. Advanced Vehicle AI is disabled.\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (vehicleState.heavyVehicle) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayFindBestLane({vehicleId}): Skipping lane checking. Vehicle is heavy.\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif ((vehicleState.vehicleType & (ExtVehicleType.RoadVehicle & ~ExtVehicleType.Bus)) == ExtVehicleType.None) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayFindBestLane({vehicleId}): Skipping lane checking. vehicleType={vehicleState.vehicleType}\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tuint vehicleRand = GetStaticVehicleRand(vehicleId);\n\n\t\t\tif (vehicleRand < 100 - (int)Options.altLaneSelectionRatio) {\n#if DEBUG\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"VehicleBehaviorManager.MayFindBestLane({vehicleId}): Skipping lane checking (randomization).\");\n\t\t\t\t}\n#endif\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/VehicleRestrictionsManager.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Util;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class VehicleRestrictionsManager : AbstractGeometryObservingManager, ICustomDataManager<List<Configuration.LaneVehicleTypes>>, IVehicleRestrictionsManager {\n\t\tpublic const NetInfo.LaneType LANE_TYPES = NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle;\n\t\tpublic const VehicleInfo.VehicleType VEHICLE_TYPES = VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Monorail;\n\t\tpublic const ExtVehicleType EXT_VEHICLE_TYPES = ExtVehicleType.PassengerTrain | ExtVehicleType.CargoTrain | ExtVehicleType.PassengerCar | ExtVehicleType.Bus | ExtVehicleType.Taxi | ExtVehicleType.CargoTruck | ExtVehicleType.Service | ExtVehicleType.Emergency;\n\t\tpublic static readonly float[] PATHFIND_PENALTIES = new float[] { 10f, 100f, 1000f };\n\n\t\tpublic static readonly VehicleRestrictionsManager Instance = new VehicleRestrictionsManager();\n\n\t\tprivate VehicleRestrictionsManager() {\n\n\t\t}\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"- Not implemented -\");\n\t\t\t// TODO implement\n\t\t}\n\n\t\t/// <summary>\n\t\t/// For each segment id and lane index: Holds the default set of vehicle types allowed for the lane\n\t\t/// </summary>\n\t\tprivate ExtVehicleType?[][][] defaultVehicleTypeCache = null;\n\n\t\t/// <summary>\n\t\t/// Determines the allowed vehicle types that may approach the given node from the given segment.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"nodeId\"></param>\n\t\t/// <returns></returns>\n\t\t[Obsolete]\n\t\tpublic ExtVehicleType GetAllowedVehicleTypes(ushort segmentId, ushort nodeId, VehicleRestrictionsMode busLaneMode) { // TODO optimize method (don't depend on collections!)\n\t\t\tExtVehicleType ret = ExtVehicleType.None;\n\t\t\tforeach (ExtVehicleType vehicleType in GetAllowedVehicleTypesAsSet(segmentId, nodeId, busLaneMode)) {\n\t\t\t\tret |= vehicleType;\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the allowed vehicle types that may approach the given node from the given segment.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"nodeId\"></param>\n\t\t/// <returns></returns>\n\t\t[Obsolete]\n\t\tpublic HashSet<ExtVehicleType> GetAllowedVehicleTypesAsSet(ushort segmentId, ushort nodeId, VehicleRestrictionsMode busLaneMode) {\n\t\t\tHashSet<ExtVehicleType> ret = new HashSet<ExtVehicleType>(GetAllowedVehicleTypesAsDict(segmentId, nodeId, busLaneMode).Values);\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the allowed vehicle types that may approach the given node from the given segment (lane-wise).\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"nodeId\"></param>\n\t\t/// <returns></returns>\n\t\tpublic IDictionary<byte, ExtVehicleType> GetAllowedVehicleTypesAsDict(ushort segmentId, ushort nodeId, VehicleRestrictionsMode busLaneMode) {\n\t\t\tIDictionary<byte, ExtVehicleType> ret = new TinyDictionary<byte, ExtVehicleType>();\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tif (segmentId == 0 || (netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Created) == NetSegment.Flags.None ||\n\t\t\t\tnodeId == 0 || (netManager.m_nodes.m_buffer[nodeId].m_flags & NetNode.Flags.Created) == NetNode.Flags.None) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\tvar dir = NetInfo.Direction.Forward;\n\t\t\tvar dir2 = ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? dir : NetInfo.InvertDirection(dir);\n\n\t\t\tNetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info;\n\t\t\tuint curLaneId = netManager.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tint numLanes = segmentInfo.m_lanes.Length;\n\t\t\tuint laneIndex = 0;\n\t\t\twhile (laneIndex < numLanes && curLaneId != 0u) {\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\tif (laneInfo.m_laneType == NetInfo.LaneType.Vehicle || laneInfo.m_laneType == NetInfo.LaneType.TransportVehicle) {\n\t\t\t\t\tif ((laneInfo.m_vehicleType & VEHICLE_TYPES) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\tushort toNodeId = (laneInfo.m_finalDirection & dir2) != NetInfo.Direction.None ? netManager.m_segments.m_buffer[segmentId].m_endNode : netManager.m_segments.m_buffer[segmentId].m_startNode;\n\t\t\t\t\t\tif ((laneInfo.m_finalDirection & NetInfo.Direction.Both) == NetInfo.Direction.Both || toNodeId == nodeId) {\n\t\t\t\t\t\t\tExtVehicleType vehicleTypes = GetAllowedVehicleTypes(segmentId, segmentInfo, laneIndex, laneInfo, busLaneMode);\n\t\t\t\t\t\t\tret[(byte)laneIndex] = vehicleTypes;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t++laneIndex;\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the allowed vehicle types for the given segment and lane.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"laneIndex\"></param>\n\t\t/// <param name=\"segmentInfo\"></param>\n\t\t/// <param name=\"laneInfo\"></param>\n\t\t/// <returns></returns>\n\t\tpublic ExtVehicleType GetAllowedVehicleTypes(ushort segmentId, NetInfo segmentInfo, uint laneIndex, NetInfo.Lane laneInfo, VehicleRestrictionsMode busLaneMode) {\n\t\t\tExtVehicleType?[] fastArray = Flags.laneAllowedVehicleTypesArray[segmentId];\n\t\t\tif (fastArray != null && fastArray.Length > laneIndex && fastArray[laneIndex] != null) {\n\t\t\t\treturn (ExtVehicleType)fastArray[laneIndex];\n\t\t\t}\n\n\t\t\treturn GetDefaultAllowedVehicleTypes(segmentId, segmentInfo, laneIndex, laneInfo, busLaneMode);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the default set of allowed vehicle types for a given segment and lane.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"segmentInfo\"></param>\n\t\t/// <param name=\"laneIndex\"></param>\n\t\t/// <param name=\"laneInfo\"></param>\n\t\t/// <returns></returns>\n\t\tpublic ExtVehicleType GetDefaultAllowedVehicleTypes(ushort segmentId, NetInfo segmentInfo, uint laneIndex, NetInfo.Lane laneInfo, VehicleRestrictionsMode busLaneMode) {\n\t\t\t// manage cached default vehicle types\n\t\t\tif (defaultVehicleTypeCache == null) {\n\t\t\t\tdefaultVehicleTypeCache = new ExtVehicleType?[NetManager.MAX_SEGMENT_COUNT][][];\n\t\t\t}\n\n\t\t\tExtVehicleType?[] cachedDefaultTypes = null;\n\t\t\tint cacheIndex = (int)busLaneMode;\n\t\t\t\n\t\t\tif (defaultVehicleTypeCache[segmentId] != null) {\n\t\t\t\tcachedDefaultTypes = defaultVehicleTypeCache[segmentId][cacheIndex];\n\t\t\t}\n\n\t\t\tif (cachedDefaultTypes == null || cachedDefaultTypes.Length != segmentInfo.m_lanes.Length) {\n\t\t\t\tExtVehicleType?[][] segmentCache = new ExtVehicleType?[3][];\n\t\t\t\tsegmentCache[0] = new ExtVehicleType?[segmentInfo.m_lanes.Length];\n\t\t\t\tsegmentCache[1] = new ExtVehicleType?[segmentInfo.m_lanes.Length];\n\t\t\t\tsegmentCache[2] = new ExtVehicleType?[segmentInfo.m_lanes.Length];\n\t\t\t\tdefaultVehicleTypeCache[segmentId] = segmentCache;\n\t\t\t\tcachedDefaultTypes = segmentCache[cacheIndex];\n\t\t\t}\n\n\t\t\tExtVehicleType? defaultVehicleType = cachedDefaultTypes[laneIndex];\n\t\t\tif (defaultVehicleType == null) {\n\t\t\t\tdefaultVehicleType = GetDefaultAllowedVehicleTypes(laneInfo, busLaneMode);\n\t\t\t\tcachedDefaultTypes[laneIndex] = defaultVehicleType;\n\t\t\t}\n\t\t\treturn (ExtVehicleType)defaultVehicleType;\n\t\t}\n\n\t\tpublic ExtVehicleType GetDefaultAllowedVehicleTypes(NetInfo.Lane laneInfo, VehicleRestrictionsMode busLaneMode) {\n\t\t\tExtVehicleType ret = ExtVehicleType.None;\n\t\t\tif ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Bicycle) != VehicleInfo.VehicleType.None)\n\t\t\t\tret |= ExtVehicleType.Bicycle;\n\t\t\tif ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Tram) != VehicleInfo.VehicleType.None)\n\t\t\t\tret |= ExtVehicleType.Tram;\n\t\t\tif (busLaneMode == VehicleRestrictionsMode.Restricted ||\n\t\t\t\t\t(busLaneMode == VehicleRestrictionsMode.Configured && Options.banRegularTrafficOnBusLanes)) {\n\t\t\t\tif ((laneInfo.m_laneType & NetInfo.LaneType.TransportVehicle) != NetInfo.LaneType.None)\n\t\t\t\t\tret |= ExtVehicleType.RoadPublicTransport | ExtVehicleType.Service | ExtVehicleType.Emergency;\n\t\t\t\telse if ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None)\n\t\t\t\t\tret |= ExtVehicleType.RoadVehicle;\n\t\t\t} else {\n\t\t\t\tif ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None)\n\t\t\t\t\tret |= ExtVehicleType.RoadVehicle;\n\t\t\t}\n\t\t\tif ((laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Monorail)) != VehicleInfo.VehicleType.None)\n\t\t\t\tret |= ExtVehicleType.RailVehicle;\n\t\t\tif ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Ship) != VehicleInfo.VehicleType.None)\n\t\t\t\tret |= ExtVehicleType.Ship;\n\t\t\tif ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Plane) != VehicleInfo.VehicleType.None)\n\t\t\t\tret |= ExtVehicleType.Plane;\n\t\t\tif ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Ferry) != VehicleInfo.VehicleType.None)\n\t\t\t\tret |= ExtVehicleType.Ferry;\n\t\t\tif ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Blimp) != VehicleInfo.VehicleType.None)\n\t\t\t\tret |= ExtVehicleType.Blimp;\n\t\t\tif ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.CableCar) != VehicleInfo.VehicleType.None)\n\t\t\t\tret |= ExtVehicleType.CableCar;\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the default set of allowed vehicle types for a given lane.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"segmentInfo\"></param>\n\t\t/// <param name=\"laneIndex\"></param>\n\t\t/// <param name=\"laneInfo\"></param>\n\t\t/// <returns></returns>\n\t\tinternal ExtVehicleType GetDefaultAllowedVehicleTypes(uint laneId, VehicleRestrictionsMode busLaneMode) {\n\t\t\tif (((NetLane.Flags)Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags & NetLane.Flags.Created) == NetLane.Flags.None)\n\t\t\t\treturn ExtVehicleType.None;\n\t\t\tushort segmentId = Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_segment;\n\t\t\tif ((Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Created) == NetSegment.Flags.None)\n\t\t\t\treturn ExtVehicleType.None;\n\n\t\t\tNetInfo segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\t\t\tuint curLaneId = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tint numLanes = segmentInfo.m_lanes.Length;\n\t\t\tuint laneIndex = 0;\n\t\t\twhile (laneIndex < numLanes && curLaneId != 0u) {\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\tif (curLaneId == laneId) {\n\t\t\t\t\treturn GetDefaultAllowedVehicleTypes(segmentId, segmentInfo, laneIndex, laneInfo, busLaneMode);\n\t\t\t\t}\n\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t++laneIndex;\n\t\t\t}\n\n\t\t\treturn ExtVehicleType.None;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Sets the allowed vehicle types for the given segment and lane.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"laneIndex\"></param>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <param name=\"allowedTypes\"></param>\n\t\t/// <returns></returns>\n\t\tinternal bool SetAllowedVehicleTypes(ushort segmentId, NetInfo segmentInfo, uint laneIndex, NetInfo.Lane laneInfo, uint laneId, ExtVehicleType allowedTypes) {\n\t\t\tif (! Services.NetService.IsLaneValid(laneId)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (! Services.NetService.IsSegmentValid(segmentId)) {\n\t\t\t\t// TODO we do not need the segmentId given here. Lane is enough\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tallowedTypes &= GetBaseMask(segmentInfo.m_lanes[laneIndex], VehicleRestrictionsMode.Configured); // ensure default base mask\n\t\t\tFlags.setLaneAllowedVehicleTypes(segmentId, laneIndex, laneId, allowedTypes);\n\t\t\tNotifyStartEndNode(segmentId);\n\n\t\t\tif (OptionsManager.Instance.MayPublishSegmentChanges()) {\n\t\t\t\tServices.NetService.PublishSegmentChanges(segmentId);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds the given vehicle type to the set of allowed vehicles at the specified lane\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"laneIndex\"></param>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <param name=\"laneInfo\"></param>\n\t\t/// <param name=\"road\"></param>\n\t\t/// <param name=\"vehicleType\"></param>\n\t\tpublic void AddAllowedType(ushort segmentId, NetInfo segmentInfo, uint laneIndex, uint laneId, NetInfo.Lane laneInfo, ExtVehicleType vehicleType) {\n\t\t\tif (!Services.NetService.IsLaneValid(laneId)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!Services.NetService.IsSegmentValid(segmentId)) {\n\t\t\t\t// TODO we do not need the segmentId given here. Lane is enough\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tExtVehicleType allowedTypes = GetAllowedVehicleTypes(segmentId, segmentInfo, laneIndex, laneInfo, VehicleRestrictionsMode.Configured);\n\t\t\tallowedTypes |= vehicleType;\n\t\t\tallowedTypes &= GetBaseMask(segmentInfo.m_lanes[laneIndex], VehicleRestrictionsMode.Configured); // ensure default base mask\n\t\t\tFlags.setLaneAllowedVehicleTypes(segmentId, laneIndex, laneId, allowedTypes);\n\t\t\tNotifyStartEndNode(segmentId);\n\n\t\t\tif (OptionsManager.Instance.MayPublishSegmentChanges()) {\n\t\t\t\tServices.NetService.PublishSegmentChanges(segmentId);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes the given vehicle type from the set of allowed vehicles at the specified lane\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"laneIndex\"></param>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <param name=\"laneInfo\"></param>\n\t\t/// <param name=\"road\"></param>\n\t\t/// <param name=\"vehicleType\"></param>\n\t\tpublic void RemoveAllowedType(ushort segmentId, NetInfo segmentInfo, uint laneIndex, uint laneId, NetInfo.Lane laneInfo, ExtVehicleType vehicleType) {\n\t\t\tif (!Services.NetService.IsLaneValid(laneId)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!Services.NetService.IsSegmentValid(segmentId)) {\n\t\t\t\t// TODO we do not need the segmentId given here. Lane is enough\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tExtVehicleType allowedTypes = GetAllowedVehicleTypes(segmentId, segmentInfo, laneIndex, laneInfo, VehicleRestrictionsMode.Configured);\n\t\t\tallowedTypes &= ~vehicleType;\n\t\t\tallowedTypes &= GetBaseMask(segmentInfo.m_lanes[laneIndex], VehicleRestrictionsMode.Configured); // ensure default base mask\n\t\t\tFlags.setLaneAllowedVehicleTypes(segmentId, laneIndex, laneId, allowedTypes);\n\t\t\tNotifyStartEndNode(segmentId);\n\n\t\t\tif (OptionsManager.Instance.MayPublishSegmentChanges()) {\n\t\t\t\tServices.NetService.PublishSegmentChanges(segmentId);\n\t\t\t}\n\t\t}\n\n\t\tpublic void ToggleAllowedType(ushort segmentId, NetInfo segmentInfo, uint laneIndex, uint laneId, NetInfo.Lane laneInfo, ExtVehicleType vehicleType, bool add) {\n\t\t\tif (add)\n\t\t\t\tAddAllowedType(segmentId, segmentInfo, laneIndex, laneId, laneInfo, vehicleType);\n\t\t\telse\n\t\t\t\tRemoveAllowedType(segmentId, segmentInfo, laneIndex, laneId, laneInfo, vehicleType);\n\t\t}\n\n\t\tpublic bool HasSegmentRestrictions(ushort segmentId) { // TODO clean up restrictions (currently we do not check if restrictions are equal with the base type)\n\t\t\tbool ret = false;\n\t\t\tServices.NetService.IterateSegmentLanes(segmentId, delegate (uint laneId, ref NetLane lane, NetInfo.Lane laneInfo, ushort segId, ref NetSegment segment, byte laneIndex) {\n\t\t\t\tExtVehicleType defaultMask = GetDefaultAllowedVehicleTypes(laneInfo, VehicleRestrictionsMode.Unrestricted);\n\t\t\t\tExtVehicleType currentMask = GetAllowedVehicleTypes(segmentId, segment.Info, laneIndex, laneInfo, VehicleRestrictionsMode.Configured);\n\n\t\t\t\tif (defaultMask != currentMask) {\n\t\t\t\t\tret = true;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if a vehicle may use the given lane.\n\t\t/// </summary>\n\t\t/// <param name=\"debug\"></param>\n\t\t/// <param name=\"segmentId\"></param>\n\t\t/// <param name=\"laneIndex\"></param>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <param name=\"laneInfo\"></param>\n\t\t/// <returns></returns>\n\t\tpublic bool MayUseLane(ExtVehicleType type, ushort segmentId, byte laneIndex, NetInfo segmentInfo) {\n\t\t\tif (type == ExtVehicleType.None /* || type == ExtVehicleType.Tram*/)\n\t\t\t\treturn true;\n\n\t\t\t/*if (laneInfo == null)\n\t\t\t\tlaneInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info.m_lanes[laneIndex];*/\n\n\t\t\tif (segmentInfo == null || laneIndex >= segmentInfo.m_lanes.Length) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\tif ((laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) == VehicleInfo.VehicleType.None)\n\t\t\t\treturn true;\n\n\t\t\tif (!Options.vehicleRestrictionsEnabled) {\n\t\t\t\treturn (GetDefaultAllowedVehicleTypes(laneInfo, VehicleRestrictionsMode.Configured) & type) != ExtVehicleType.None;\n\t\t\t}\n\n\t\t\treturn ((GetAllowedVehicleTypes(segmentId, segmentInfo, laneIndex, laneInfo, VehicleRestrictionsMode.Configured) & type) != ExtVehicleType.None);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the maximum allowed set of vehicles (the base mask) for a given lane\n\t\t/// </summary>\n\t\t/// <param name=\"laneInfo\"></param>\n\t\t/// <returns></returns>\n\t\tpublic ExtVehicleType GetBaseMask(NetInfo.Lane laneInfo, VehicleRestrictionsMode includeBusLanes) {\n\t\t\treturn GetDefaultAllowedVehicleTypes(laneInfo, includeBusLanes);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the maximum allowed set of vehicles (the base mask) for a given lane\n\t\t/// </summary>\n\t\t/// <param name=\"laneInfo\"></param>\n\t\t/// <returns></returns>\n\t\tpublic ExtVehicleType GetBaseMask(uint laneId, VehicleRestrictionsMode includeBusLanes) {\n\t\t\tif (((NetLane.Flags)Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags & NetLane.Flags.Created) == NetLane.Flags.None)\n\t\t\t\treturn ExtVehicleType.None;\n\t\t\tushort segmentId = Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_segment;\n\t\t\tif ((Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Created) == NetSegment.Flags.None)\n\t\t\t\treturn ExtVehicleType.None;\n\n\t\t\tNetInfo segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\t\t\tuint curLaneId = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tint numLanes = segmentInfo.m_lanes.Length;\n\t\t\tuint laneIndex = 0;\n\t\t\twhile (laneIndex < numLanes && curLaneId != 0u) {\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\tif (curLaneId == laneId) {\n\t\t\t\t\treturn GetBaseMask(laneInfo, includeBusLanes);\n\t\t\t\t}\n\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t++laneIndex;\n\t\t\t}\n\t\t\treturn ExtVehicleType.None;\n\t\t}\n\n\t\tpublic bool IsAllowed(ExtVehicleType? allowedTypes, ExtVehicleType vehicleType) {\n\t\t\treturn allowedTypes == null || ((ExtVehicleType)allowedTypes & vehicleType) != ExtVehicleType.None;\n\t\t}\n\n\t\tpublic bool IsBicycleAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.Bicycle);\n\t\t}\n\n\t\tpublic bool IsBusAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.Bus);\n\t\t}\n\n\t\tpublic bool IsCargoTrainAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.CargoTrain);\n\t\t}\n\n\t\tpublic bool IsCargoTruckAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.CargoTruck);\n\t\t}\n\n\t\tpublic bool IsEmergencyAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.Emergency);\n\t\t}\n\n\t\tpublic bool IsPassengerCarAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.PassengerCar);\n\t\t}\n\n\t\tpublic bool IsPassengerTrainAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.PassengerTrain);\n\t\t}\n\n\t\tpublic bool IsServiceAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.Service);\n\t\t}\n\n\t\tpublic bool IsTaxiAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.Taxi);\n\t\t}\n\n\t\tpublic bool IsTramAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.Tram);\n\t\t}\n\n\t\tpublic bool IsBlimpAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.Blimp);\n\t\t}\n\n\t\tpublic bool IsCableCarAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.CableCar);\n\t\t}\n\n\t\tpublic bool IsFerryAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.Ferry);\n\t\t}\n\n\t\tpublic bool IsRailVehicleAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.RailVehicle);\n\t\t}\n\n\t\tpublic bool IsRoadVehicleAllowed(ExtVehicleType? allowedTypes) {\n\t\t\treturn IsAllowed(allowedTypes, ExtVehicleType.RoadVehicle);\n\t\t}\n\n\t\tpublic bool IsRailLane(NetInfo.Lane laneInfo) {\n\t\t\treturn (laneInfo.m_vehicleType & VehicleInfo.VehicleType.Train) != VehicleInfo.VehicleType.None;\n\t\t}\n\n\t\tpublic bool IsRoadLane(NetInfo.Lane laneInfo) {\n\t\t\treturn (laneInfo.m_vehicleType & VehicleInfo.VehicleType.Car) != VehicleInfo.VehicleType.None;\n\t\t}\n\n\t\tpublic bool IsTramLane(NetInfo.Lane laneInfo) {\n\t\t\treturn (laneInfo.m_vehicleType & VehicleInfo.VehicleType.Tram) != VehicleInfo.VehicleType.None;\n\t\t}\n\n\t\tpublic bool IsRailSegment(NetInfo segmentInfo) {\n\t\t\tItemClass connectionClass = segmentInfo.GetConnectionClass();\n\t\t\treturn connectionClass.m_service == ItemClass.Service.PublicTransport && connectionClass.m_subService == ItemClass.SubService.PublicTransportTrain;\n\t\t}\n\n\t\tpublic bool IsRoadSegment(NetInfo segmentInfo) {\n\t\t\tItemClass connectionClass = segmentInfo.GetConnectionClass();\n\t\t\treturn connectionClass.m_service == ItemClass.Service.Road;\n\t\t}\n\n\t\tpublic bool IsMonorailSegment(NetInfo segmentInfo) {\n\t\t\tItemClass connectionClass = segmentInfo.GetConnectionClass();\n\t\t\treturn connectionClass.m_service == ItemClass.Service.PublicTransport && connectionClass.m_subService == ItemClass.SubService.PublicTransportMonorail;\n\t\t}\n\n\t\tinternal void ClearCache(ushort segmentId) {\n\t\t\tif (defaultVehicleTypeCache != null) {\n\t\t\t\tdefaultVehicleTypeCache[segmentId] = null;\n\t\t\t}\n\t\t}\n\n\t\tinternal void ClearCache() {\n\t\t\tdefaultVehicleTypeCache = null;\n\t\t}\n\n\t\tpublic void NotifyStartEndNode(ushort segmentId) {\n\t\t\t// TODO this is hacky. Instead of notifying geometry observers we should add a seperate notification mechanic\n\t\t\t// notify observers of start node and end node (e.g. for separate traffic lights)\n\t\t\tushort startNodeId = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_startNode;\n\t\t\tushort endNodeId = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_endNode;\n\t\t\tif (startNodeId != 0) {\n\t\t\t\tConstants.ManagerFactory.GeometryManager.MarkAsUpdated(NodeGeometry.Get(startNodeId));\n\t\t\t}\n\t\t\tif (endNodeId != 0) {\n\t\t\t\tConstants.ManagerFactory.GeometryManager.MarkAsUpdated(NodeGeometry.Get(endNodeId));\n\t\t\t}\n\t\t}\n\n\t\tprotected override void HandleInvalidSegment(SegmentGeometry geometry) {\n\t\t\tFlags.resetSegmentVehicleRestrictions(geometry.SegmentId);\n\t\t\tClearCache(geometry.SegmentId);\n\t\t}\n\n\t\tprotected override void HandleValidSegment(SegmentGeometry geometry) {\n\t\t\t\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tClearCache();\n\t\t}\n\n\t\tpublic bool LoadData(List<Configuration.LaneVehicleTypes> data) {\n\t\t\tbool success = true;\n\t\t\tLog.Info($\"Loading lane vehicle restriction data. {data.Count} elements\");\n\t\t\tforeach (Configuration.LaneVehicleTypes laneVehicleTypes in data) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!Services.NetService.IsLaneValid(laneVehicleTypes.laneId))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tExtVehicleType baseMask = GetBaseMask(laneVehicleTypes.laneId, VehicleRestrictionsMode.Configured);\n\t\t\t\t\tExtVehicleType maskedType = laneVehicleTypes.vehicleTypes & baseMask;\n\t\t\t\t\tLog._Debug($\"Loading lane vehicle restriction: lane {laneVehicleTypes.laneId} = {laneVehicleTypes.vehicleTypes}, masked = {maskedType}\");\n\t\t\t\t\tif (maskedType != baseMask) {\n\t\t\t\t\t\tFlags.setLaneAllowedVehicleTypes(laneVehicleTypes.laneId, maskedType);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tLog._Debug($\"Masked type does not differ from base type. Ignoring.\");\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t// ignore, as it's probably corrupt save data. it'll be culled on next save\n\t\t\t\t\tLog.Warning(\"Error loading data from vehicle restrictions: \" + e.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\tpublic List<Configuration.LaneVehicleTypes> SaveData(ref bool success) {\n\t\t\tList<Configuration.LaneVehicleTypes> ret = new List<Configuration.LaneVehicleTypes>();\n\t\t\tforeach (KeyValuePair<uint, ExtVehicleType> e in Flags.getAllLaneAllowedVehicleTypes()) {\n\t\t\t\ttry {\n\t\t\t\t\tret.Add(new Configuration.LaneVehicleTypes(e.Key, e.Value));\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tLog.Error($\"Exception occurred while saving lane vehicle restrictions @ {e.Key}: {ex.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/Impl/VehicleStateManager.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Threading;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.Traffic.Data;\nusing UnityEngine;\n\nnamespace TrafficManager.Manager.Impl {\n\tpublic class VehicleStateManager : AbstractCustomManager, IVehicleStateManager {\n\t\tpublic static readonly VehicleStateManager Instance = new VehicleStateManager();\n\n\t\tpublic const VehicleInfo.VehicleType VEHICLE_TYPES = VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Tram | VehicleInfo.VehicleType.Metro | VehicleInfo.VehicleType.Monorail;\n\n\t\t/// <summary>\n\t\t/// Known vehicles and their current known positions. Index: vehicle id\n\t\t/// </summary>\n\t\tpublic VehicleState[] VehicleStates { get; private set; } = null;\n\n\t\tstatic VehicleStateManager() {\n\t\t\tInstance = new VehicleStateManager();\n\t\t}\n\n\t\tprotected override void InternalPrintDebugInfo() {\n\t\t\tbase.InternalPrintDebugInfo();\n\t\t\tLog._Debug($\"Vehicle states:\");\n\t\t\tfor (int i = 0; i < VehicleStates.Length; ++i) {\n\t\t\t\tif ((VehicleStates[i].flags & VehicleState.Flags.Spawned) == VehicleState.Flags.None) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLog._Debug($\"Vehicle {i}: {VehicleStates[i]}\");\n\t\t\t}\n\t\t}\n\n\t\tprivate VehicleStateManager() {\n\t\t\tVehicleStates = new VehicleState[VehicleManager.MAX_VEHICLE_COUNT];\n\t\t\tfor (uint i = 0; i < VehicleManager.MAX_VEHICLE_COUNT; ++i) {\n\t\t\t\tVehicleStates[i] = new VehicleState((ushort)i);\n\t\t\t}\n\t\t}\n\n\t\tinternal void LogTraffic(ushort vehicleId) {\n\t\t\tLogTraffic(vehicleId, ref VehicleStates[vehicleId]);\n\t\t}\n\n\t\tprotected void LogTraffic(ushort vehicleId, ref VehicleState state) {\n\t\t\tif (state.currentSegmentId == 0) {\n\t\t\t\treturn;\n\t\t\t}\n#if MEASUREDENSITY\n\t\t\tushort length = (ushort)state.totalLength;\n\t\t\tif (length == 0) {\n\t\t\t\treturn;\n\t\t\t}\n#endif\n\n\t\t\tstate.StepRand();\n\n\t\t\tTrafficMeasurementManager.Instance.AddTraffic(state.currentSegmentId, state.currentLaneIndex\n#if MEASUREDENSITY\n\t\t\t\t, length\n#endif\n\t\t\t\t, (ushort) state.Velocity);\n\t\t}\n\n\t\tinternal void OnCreateVehicle(ushort vehicleId, ref Vehicle vehicleData) {\n\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created ||\n\t\t\t\t(vehicleData.Info.m_vehicleType & VEHICLE_TYPES) == VehicleInfo.VehicleType.None) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleStateManager.OnCreateVehicle({vehicleId}): unhandled vehicle! flags: {vehicleData.m_flags}, type: {vehicleData.Info.m_vehicleType}\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleStateManager.OnCreateVehicle({vehicleId}): calling OnCreate for vehicle {vehicleId}\");\n#endif\n\n\t\t\tVehicleStates[vehicleId].OnCreate(ref vehicleData);\n\t\t}\n\n\t\tinternal ExtVehicleType OnStartPathFind(ushort vehicleId, ref Vehicle vehicleData, ExtVehicleType? vehicleType) {\n\t\t\tif ((vehicleData.Info.m_vehicleType & VEHICLE_TYPES) == VehicleInfo.VehicleType.None ||\n\t\t\t\t(vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleStateManager.OnStartPathFind({vehicleId}, {vehicleType}): unhandled vehicle! type: {vehicleData.Info.m_vehicleType}\");\n#endif\n\t\t\t\treturn ExtVehicleType.None;\n\t\t\t}\n\n\t\t\t/*ushort connectedVehicleId = vehicleId;\n\t\t\twhile (connectedVehicleId != 0) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleStateManager.OnStartPathFind({vehicleId}, {vehicleType}): overriding vehicle type for connected vehicle {connectedVehicleId} of vehicle {vehicleId} (leading)\");\n#endif\n\t\t\t\tVehicleStates[connectedVehicleId].vehicleType = type;\n\n\t\t\t\tconnectedVehicleId = Singleton<VehicleManager>.instance.m_vehicles.m_buffer[connectedVehicleId].m_leadingVehicle;\n\t\t\t}*/\n\n\t\t\tExtVehicleType ret = VehicleStates[vehicleId].OnStartPathFind(ref vehicleData, vehicleType);\n\n\t\t\tushort connectedVehicleId = vehicleId;\n\t\t\twhile (true) {\n\t\t\t\tconnectedVehicleId = Singleton<VehicleManager>.instance.m_vehicles.m_buffer[connectedVehicleId].m_trailingVehicle;\n\n\t\t\t\tif (connectedVehicleId == 0) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleStateManager.OnStartPathFind({vehicleId}, {vehicleType}): overriding vehicle type for connected vehicle {connectedVehicleId} of vehicle {vehicleId} (trailing)\");\n#endif\n\t\t\t\tVehicleStates[connectedVehicleId].OnStartPathFind(ref Singleton<VehicleManager>.instance.m_vehicles.m_buffer[connectedVehicleId], vehicleType);\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tinternal void OnSpawnVehicle(ushort vehicleId, ref Vehicle vehicleData) {\n\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Spawned)) != (Vehicle.Flags.Created | Vehicle.Flags.Spawned) ||\n\t\t\t\t(vehicleData.Info.m_vehicleType & VEHICLE_TYPES) == VehicleInfo.VehicleType.None) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleStateManager.OnSpawnVehicle({vehicleId}): unhandled vehicle! flags: {vehicleData.m_flags}, type: {vehicleData.Info.m_vehicleType}, path: {vehicleData.m_path}\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleStateManager.OnSpawnVehicle({vehicleId}): calling OnSpawn for vehicle {vehicleId}\");\n#endif\n\n\t\t\tushort connectedVehicleId = vehicleId;\n\t\t\twhile (connectedVehicleId != 0) {\n\t\t\t\tVehicleStates[connectedVehicleId].OnSpawn(ref Singleton<VehicleManager>.instance.m_vehicles.m_buffer[connectedVehicleId]);\n\t\t\t\tconnectedVehicleId = Singleton<VehicleManager>.instance.m_vehicles.m_buffer[connectedVehicleId].m_trailingVehicle;\n\t\t\t}\n\t\t}\n\n\t\tpublic void SetNextVehicleIdOnSegment(ushort vehicleId, ushort nextVehicleId) {\n\t\t\tVehicleStates[vehicleId].nextVehicleIdOnSegment = nextVehicleId;\n\t\t}\n\n\t\tpublic void SetPreviousVehicleIdOnSegment(ushort vehicleId, ushort previousVehicleId) {\n\t\t\tVehicleStates[vehicleId].previousVehicleIdOnSegment = previousVehicleId;\n\t\t}\n\n\t\tinternal void UpdateVehiclePosition(ushort vehicleId, ref Vehicle vehicleData) {\n\t\t\tushort connectedVehicleId = vehicleId;\n\t\t\twhile (connectedVehicleId != 0) {\n\t\t\t\tUpdateVehiclePosition(ref Singleton<VehicleManager>.instance.m_vehicles.m_buffer[connectedVehicleId], ref VehicleStates[connectedVehicleId]);\n\t\t\t\tconnectedVehicleId = Singleton<VehicleManager>.instance.m_vehicles.m_buffer[connectedVehicleId].m_trailingVehicle;\n\t\t\t}\n\t\t}\n\n\t\tprotected void UpdateVehiclePosition(ref Vehicle vehicleData, ref VehicleState state) {\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleStateManager.UpdateVehiclePosition({state.vehicleId}) called\");\n#endif\n\n\t\t\tif (vehicleData.m_path == 0 || (vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0 ||\n\t\t\t\t(state.lastPathId == vehicleData.m_path && state.lastPathPositionIndex == vehicleData.m_pathPositionIndex)\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tPathManager pathManager = Singleton<PathManager>.instance;\n\n\t\t\t// update vehicle position for timed traffic lights and priority signs\n\t\t\tint coarsePathPosIndex = vehicleData.m_pathPositionIndex >> 1;\n\t\t\tPathUnit.Position curPathPos;\n\t\t\tPathUnit.Position nextPathPos = default(PathUnit.Position);\n\t\t\t//if ((vehicleData.m_pathPositionIndex & 1) == 0) {\n\t\t\t\tcurPathPos = pathManager.m_pathUnits.m_buffer[vehicleData.m_path].GetPosition(coarsePathPosIndex);\n\t\t\t\tpathManager.m_pathUnits.m_buffer[vehicleData.m_path].GetNextPosition(coarsePathPosIndex, out nextPathPos);\n\t\t\t/*} else {\n\t\t\t\tuint firstUnitId = vehicleData.m_path;\n\t\t\t\tint firstCoarsePathPosIndex = coarsePathPosIndex;\n\t\t\t\tbool invalid = false;\n\t\t\t\tif (PathUnit.GetNextPosition(ref firstUnitId, ref firstCoarsePathPosIndex, out curPathPos, out invalid)) {\n\t\t\t\t\tuint secondUnitId = firstUnitId;\n\t\t\t\t\tint secondCoarsePathPosIndex = firstCoarsePathPosIndex;\n\t\t\t\t\tPathUnit.GetNextPosition(ref secondUnitId, ref secondCoarsePathPosIndex, out nextPathPos, out invalid);\n\t\t\t\t}\n\t\t\t}*/\n\t\t\tstate.UpdatePosition(ref vehicleData, ref curPathPos, ref nextPathPos);\n\t\t}\n\n\t\tinternal void OnDespawnVehicle(ushort vehicleId, ref Vehicle vehicleData) {\n\t\t\tif ((vehicleData.Info.m_vehicleType & VEHICLE_TYPES) == VehicleInfo.VehicleType.None ||\n\t\t\t\t(vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Spawned)) == 0) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleStateManager.OnDespawnVehicle({vehicleId}): unhandled vehicle! type: {vehicleData.Info.m_vehicleType}\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tushort /*connectedVehicleId = vehicleId;\n\t\t\twhile (connectedVehicleId != 0) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleStateManager.OnDespawnVehicle({vehicleId}): calling OnDespawn for connected vehicle {connectedVehicleId} of vehicle {vehicleId} (leading)\");\n#endif\n\t\t\t\tVehicleStates[connectedVehicleId].OnDespawn();\n\n\t\t\t\tconnectedVehicleId = Singleton<VehicleManager>.instance.m_vehicles.m_buffer[connectedVehicleId].m_leadingVehicle;\n\t\t\t}\n\n\t\t\t*/connectedVehicleId = vehicleId;\n\t\t\twhile (connectedVehicleId != 0) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleStateManager.OnDespawnVehicle({vehicleId}): calling OnDespawn for connected vehicle {connectedVehicleId} of vehicle {vehicleId} (trailing)\");\n#endif\n\t\t\t\tVehicleStates[connectedVehicleId].OnDespawn();\n\t\t\t\tconnectedVehicleId = Singleton<VehicleManager>.instance.m_vehicles.m_buffer[connectedVehicleId].m_trailingVehicle;\n\t\t\t}\n\t\t}\n\n\t\tinternal void OnReleaseVehicle(ushort vehicleId, ref Vehicle vehicleData) {\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleStateManager.OnReleaseVehicle({vehicleId}) called.\");\n#endif\n\t\t\tif ((vehicleData.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) != Vehicle.Flags.Created ||\n\t\t\t\t(vehicleData.Info.m_vehicleType & VEHICLE_TYPES) == VehicleInfo.VehicleType.None) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleStateManager.OnReleaseVehicle({vehicleId}): unhandled vehicle! flags: {vehicleData.m_flags}, type: {vehicleData.Info.m_vehicleType}\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleStateManager.OnReleaseVehicle({vehicleId}): calling OnRelease for vehicle {vehicleId}\");\n#endif\n\n\t\t\tVehicleStates[vehicleId].OnRelease(ref vehicleData);\n\t\t}\n\n\t\tinternal void InitAllVehicles() {\n\t\t\tLog._Debug(\"VehicleStateManager: InitAllVehicles()\");\n\t\t\tVehicleManager vehicleManager = Singleton<VehicleManager>.instance;\n\n\t\t\tfor (uint vehicleId = 0; vehicleId < VehicleManager.MAX_VEHICLE_COUNT; ++vehicleId) {\n\t\t\t\tServices.VehicleService.ProcessVehicle((ushort)vehicleId, delegate (ushort vId, ref Vehicle vehicle) {\n\t\t\t\t\tif ((vehicle.m_flags & Vehicle.Flags.Created) == 0) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tOnCreateVehicle(vId, ref vehicle);\n\n\t\t\t\t\tif ((vehicle.m_flags & Vehicle.Flags.Emergency2) != 0) {\n\t\t\t\t\t\tOnStartPathFind(vId, ref vehicle, ExtVehicleType.Emergency);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((vehicle.m_flags & Vehicle.Flags.Spawned) == 0) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tOnSpawnVehicle(vId, ref vehicle);\n\t\t\t\t\t\t\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tpublic ushort GetFrontVehicleId(ushort vehicleId, ref Vehicle vehicleData) {\n\t\t\tbool reversed = (vehicleData.m_flags & Vehicle.Flags.Reversed) != 0;\n\t\t\tushort frontVehicleId = vehicleId;\n\t\t\tif (reversed) {\n\t\t\t\tfrontVehicleId = vehicleData.GetLastVehicle(vehicleId);\n\t\t\t} else {\n\t\t\t\tfrontVehicleId = vehicleData.GetFirstVehicle(vehicleId);\n\t\t\t}\n\n\t\t\treturn frontVehicleId;\n\t\t}\n\n\t\tpublic override void OnLevelUnloading() {\n\t\t\tbase.OnLevelUnloading();\n\t\t\tfor (int i = 0; i < VehicleStates.Length; ++i) {\n\t\t\t\tVehicleStates[i].OnDespawn();\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnAfterLoadData() {\n\t\t\tbase.OnAfterLoadData();\n\t\t\tInitAllVehicles();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Manager/README.md",
    "content": "# TM:PE -- /Manager\nManager classes that allow for creating and controlling custom behavior.\n## Classes\n- **AbstractCustomManager**: Abstract base class for all custom managers. Provides virtual callback methods for performing actions on loading/saving/unloading the game.\n- **AbstractNodeGeometryObservingManager**: Abstract base class for all custom managers that need to react to changes in node geometry.\n- **AbstractSegmentGeometryObservingManager**: Abstract base class for all custom managers that need to react to changes in segment geometry.\n- **AdvancedParkingManager**: Implements the main Parking AI logic (TODO: currently not everything is in there, some logic is still in the **CustomPassengerCarAI**, **CustomResidentAI**, **CustomTouristAI** and **CustomHumanAI** classes).\n- **CustomSegmentLightsManager**: Manages all custom traffic lights that control actual traffic at in-game junctions\n- **ExtBuildingManager**: Manages extended building information that is used by the Parking AI \n- **ExtCitizenInstanceManager**: Manages extended citizen instance information that is used by the Parking AI\n- **ICustomDataManager**: Interface that is implemented by all managers that need to load/save data together with the savegame (note that the class **SerializableDataExtension** needs to know these managers during load/save). \n- **ICustomManager**: Interface that is implemented by all managers.\n- **JunctionRestrictionManager**: Manages all junction restrictions (zebra crossings, entering blocked junctions, lane changing when going straight and u-turns).\n- **LaneArrowManager**: Manages all custom lane arrows.\n- **LaneConnectionManager**: Manages all custom lane connections that are drawn by the player.\n- **OptionsManager**: Manages loading/saving mod options\n- **ParkingRestrictionsManager**: Manages all custom parking restrictions where cars may be prohibited to park on selected road segments.\n- **RoutingManager**: Implements custom path-finding logic for lane arrows, lane connections and highway rules (but not the actual Advanced Vehicle AI cost calculation, this is being done in **CustomPathFind**)\n- **SegmentEndManager**: Manages all segment ends that keep track of vehicles approaching at priority signs and timed traffic lights\n- **SpeedLimitManager**: Manages custom speed limits.\n- **TrafficLightManager**: Offers traffic light toggling functionality and controls which junctions may have traffic lights\n- **TrafficLightSimulationManager**: Manages the creation/deletion of manual and timed lights. Delegates the simulation of timed traffic lights (entry point: **SimulationStep**).\n- **TrafficMeasurementManager**: Manages traffic measurement data used by the Advanced Vehicle AI\n- **TrafficPriorityManager**: Manages priority signs and implements priority rules at junctions with priority signs (entry point: **HasVehiclePriority**).   \n- **UtilityManager**: Offers auxiliary functions that must be executed within the simulation thread\n- **VehicleBehaviorManager**: Implements vehicle behavior (mainly at junctions). Traffic light states and priority rule checking is delegated here.\n- **VehicleRestrictionsManager**: Manages custom vehicle restrictions (custom bans for cars, cargo trucks, busses, etc.).\n- **VehicleStateManager**: Manages vehicle states (both positional and general vehicle states are stored).\n\n"
  },
  {
    "path": "TLM/TLM/PrintTransportLines.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing UnityEngine;\n\nnamespace TrafficManager {\n\tpublic class PrintTransportLines {\n#if XXX\n\t\tpublic void run() {\n\t\t\tfor (int i = 0; i < TransportManager.MAX_LINE_COUNT; ++i) {\n\t\t\t\t/*if (TransportManager.instance.m_lines.m_buffer[i].Info.m_transportType != TransportInfo.TransportType.Tram) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}*/\n\t\t\t\t//Log.Message(\"Transport line \" + i + \":\");\n\t\t\t\tif ((TransportManager.instance.m_lines.m_buffer[i].m_flags & TransportLine.Flags.Created) == TransportLine.Flags.None) {\n\t\t\t\t\t//Log.Message(\"\\tTransport line is not created.\");\n\t\t\t\t}\n\t\t\t\t//Log.Message(\"\\tFlags: \" + TransportManager.instance.m_lines.m_buffer[i].m_flags + \", cat: \" + TransportManager.instance.m_lines.m_buffer[i].Info.category + \", type: \" + TransportManager.instance.m_lines.m_buffer[i].Info.m_transportType + \", name: \" + TransportManager.instance.GetLineName((ushort)i));\n\t\t\t\tushort firstStopNodeId = TransportManager.instance.m_lines.m_buffer[i].m_stops;\n\t\t\t\tushort stopNodeId = firstStopNodeId;\n\t\t\t\tVector3 lastNodePos = Vector3.zero;\n\t\t\t\tint index = 1;\n\t\t\t\twhile (stopNodeId != 0) {\n\t\t\t\t\tVector3 pos = NetManager.instance.m_nodes.m_buffer[stopNodeId].m_position;\n\t\t\t\t\tif (NetManager.instance.m_nodes.m_buffer[stopNodeId].m_problems != Notification.Problem.None) {\n\t\t\t\t\t\tLog.Message(\"\\tStop node #\" + index + \" -- \" + stopNodeId + \": Flags: \" + NetManager.instance.m_nodes.m_buffer[stopNodeId].m_flags + \", Transport line: \" + NetManager.instance.m_nodes.m_buffer[stopNodeId].m_transportLine + \", Problems: \" + NetManager.instance.m_nodes.m_buffer[stopNodeId].m_problems + \" Pos: \" + pos + \", Dist. to lat pos: \" + (lastNodePos - pos).magnitude);\n\t\t\t\t\t\tLog.Message(\"\\tFlags: \" + TransportManager.instance.m_lines.m_buffer[i].m_flags + \", cat: \" + TransportManager.instance.m_lines.m_buffer[i].Info.category + \", type: \" + TransportManager.instance.m_lines.m_buffer[i].Info.m_transportType + \", name: \" + TransportManager.instance.GetLineName((ushort)i));\n\t\t\t\t\t}\n\t\t\t\t\tlastNodePos = pos;\n\n\t\t\t\t\tushort nextSegment = TransportLine.GetNextSegment(stopNodeId);\n\t\t\t\t\tif (nextSegment != 0) {\n\t\t\t\t\t\tstopNodeId = NetManager.instance.m_segments.m_buffer[(int)nextSegment].m_endNode;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t++index;\n\n\t\t\t\t\tif (stopNodeId == firstStopNodeId) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (index > 10000) {\n\t\t\t\t\t\tLog.Message(\"Too many iterations!\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following\n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"TLM\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"TLM\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2015\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible\n// to COM components.  If you need to access a type in this assembly from\n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"70591292-D092-4DC5-AFB9-3AA950E240BE\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version\n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers\n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.*\")]\n"
  },
  {
    "path": "TLM/TLM/README.md",
    "content": "# TM:PE -- /\nThis is the project root directory.\n## Classes\n- **CodeProfiler**: Helper class for profiling\n- **Constants**: Well, constant things\n- **LoadingExtension**: Extends **LoadingExtensionBase**. Performs detouring of game methods and checks for incompatible mods.\n- **ThreadingExtension**: Extends **ThreadingExtensionBase**. Calls several custom **SimulationStep** methods.\n- **TrafficManagerMod**: Implements **IUserMod**. Mod main class. Defines mod version and required game version.\n- **TrafficManagerMode**: Enum to express the menu visibility state (None=closed, Activated=open)"
  },
  {
    "path": "TLM/TLM/Resources/incompatible_mods.txt",
    "content": "1581695572;Traffic Manager: President Edition\n1348361731;Traffic Manager: President Edition ALPHA/DEBUG\n498363759;Traffic Manager + Improved AI\n563720449;Traffic Manager + Improved AI (Japanese Ver.)\n492391912;Improvedd AI (Traffic++)\n409184143;Traffic++\n626024868;Traffic++ V2\n407335588;No Despawn Mod\n600733054;No On-Street Parking\n1628112268;RightTurnNoStop\n411833858;Toggle Traffic Lights\n512341354;Central Services Dispatcher (WtM)\n844180955;City Drive\n529979180;CSL Service Reserve\n433249875;[ARIS] Enhanced Hearse AI\n583556014;Enhanced Hearse AI [Fixed for v1.4+]\n813835241;Enhanced Hearse AI [1.6]\n439582006;[ARIS] Enhanced Garbage Truck AI\n583552152;Enhanced Garbage Truck AI [Fixed for v1.4+]\n813835391;Enhanced Garbage Truck AI [1.6]\n413847191;SOM - Services Optimisation Module\n418556522;Road Anarchy\n954034590;Road Anarchy V2\n726005715;Roads United Core+\n680748394;Roads United: North America\n532863263;Multi-track Station Enabler\n442957897;Multi-track Station Enabler\n553184329;Sharp Junction Angles\n478820060;Network Extensions Project (v1)\n658653260;Network Nodes Editor [Experimental]\n929114228;New Roads for Network Extensions\n436253779;Road Protractor\n651610627;Road Color Changer Continued\n422554572;81 Tiles Updated\n414702884;Zoneable Pedestrian Paths\n631694768;Extended Road Upgrade\n649522495;District Service Limit\n428094792;[ARIS] Remove Stuck Vehicles\n587530437;Remove Stuck Vehicles [Fixed for v1.4+]\n813834836;Remove Stuck Vehicles [1.6]\n421028969;[ARIS] Skylines Overwatch\n583538182;Skylines Overwatch [Fixed for v1.3+]\n813833476;Skylines Overwatch [1.6]\n408209297;Extended Road Upgrade\n417926819;Road Assistant\n631930385;Realistic Vehicle Speeds"
  },
  {
    "path": "TLM/TLM/Resources/lang.txt",
    "content": "Switch_traffic_lights Switch traffic lights\nAdd_priority_signs Add priority signs\nManual_traffic_lights Manual traffic lights\nTimed_traffic_lights Timed traffic lights\nChange_lane_arrows Change lane arrows\nClear_Traffic Clear Traffic\nDisable_despawning Disable despawning\nEnable_despawning Enable despawning\nNODE_IS_LIGHT Junction has a traffic light.\\nDelete the traffic light by choosing \"Switch traffic lights\" and clicking on this node.\nNODE_IS_TIMED_LIGHT Junction is part of a timed script.\\nSelect \"Timed traffic lights\", click on this node and click on \"Remove\" first.\nSelect_nodes_windowTitle Select nodes\nSelect_nodes Select nodes\nNode Node\nDeselect_all_nodes Deselect all nodes\nSetup_timed_traffic_light Setup timed traffic light\nState State\nSkip Skip\nup up\ndown down\nView View\nEdit Edit\nDelete Delete\nTimed_traffic_lights_manager Timed traffic lights manager\nAdd_step Add step\nRemove_timed_traffic_light Remove timed traffic light\nMin._Time: Min. Time:\nMax._Time: Max. Time:\nSave Save\nAdd Add\nSensitivity Sensitivity\nVery_Low Very Low\nLow Low\nMedium Medium\nHigh High\nVery_high Very high\nExtreme_long_green/red_phases Extreme long green/red phases\nVery_long_green/red_phases Very long green/red phases\nLong_green/red_phases Long green/red phases\nModerate_green/red_phases Moderate green/red phases\nShort_green/red_phases Short green/red phases\nVery_short_green/red_phases Very short green/red phases\nExtreme_short_green/red_phases Extreme short green/red phases\nHide_counters Hide counters\nShow_counters Show counters\nStart Start\nStop Stop\nEnable_test_mode_(stay_in_current_step) Enable test mode (stay in current step)\navg._flow avg. flow\navg._wait avg. wait\nmin/max min/max\nLane Lane\nSet_Speed Set Speed {0}\nMax_speed Max speed\nSegment Segment\nincoming incoming\nEnable_Advanced_Vehicle_AI Enable Advanced Vehicle AI\nVehicles_may_enter_blocked_junctions Vehicles may enter blocked junctions\nAll_vehicles_may_ignore_lane_arrows All vehicles may ignore lane arrows\nBusses_may_ignore_lane_arrows Buses may ignore lane arrows\nReckless_driving Reckless driving\nTMPE_Title Traffic Manager: President Edition\nSettings_are_defined_for_each_savegame_separately Settings are defined for each savegame separately\nSimulation_accuracy Simulation accuracy (higher accuracy reduces performance)\nEnable_highway_specific_lane_merging/splitting_rules Enable highway specific lane merging/splitting rules\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): Drivers like to change their lane (only applied if Advanced AI is enabled)\nMaintenance Maintenance\nVery_often Very often\nOften Often\nSometimes Sometimes\nRarely Rarely\nVery_rarely Very rarely\nOnly_if_necessary Only if necessary\nNodes_and_segments Nodes and segments\nLanes Lanes\nPath_Of_Evil_(10_%) Path Of Evil (10 %)\nRush_Hour_(5_%) Rush Hour (5 %)\nMinor_Complaints_(2_%) Minor Complaints (2 %)\nHoly_City_(0_%) Holy City (0 %)\nForget_toggled_traffic_lights Forget toggled traffic lights\nRoad_is_already_in_a_group! Road is already in a group!\nAll_selected_roads_must_be_of_the_same_type! All selected roads must be of the same type!\nCreate_group Create group\nDelete_group Delete group\nAdd_zoning Add zoning\nRemove_zoning Remove zoning\nLane_Arrow_Changer_Disabled_Highway The lane arrow changer for this lane is disabled because you activated the highway rule system.\nAdd_junction_to_timed_light Add a junction to this traffic light\nRemove_junction_from_timed_light Remove a junction from this traffic light\nSelect_junction Select a junction\nCancel Cancel\nSpeed_limits Speed limits\nPersistently_visible_overlays Persistently visible overlays\nPriority_signs Priority signs\nVehicles_may_do_u-turns_at_junctions Vehicles may do u-turns at junctions\nVehicles_going_straight_may_change_lanes_at_junctions Vehicles going straight on may change lanes at junctions\nAllow_u-turns Allow u-turns\nAllow_lane_changing_for_vehicles_going_straight Allow lane changing for vehicles going straight\nAllow_vehicles_to_enter_a_blocked_junction Allow vehicles to enter a blocked junction\nRoad_condition_has_a_bigger_impact_on_vehicle_speed Road condition has a bigger impact on vehicle speed\nVehicle_restrictions Vehicle restrictions\nCopy Copy\nPaste Paste\nInvert Invert\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions Apply vehicle restrictions to all road segments between two junctions\nAllow_all_vehicles Allow all vehicles\nBan_all_vehicles Ban all vehicles\nSet_all_traffic_lights_to_red Set all traffic lights to red\nRotate_left Rotate left\nRotate_right Rotate right\nName Name\nApply Apply\nSelect_a_timed_traffic_light_program Select a timed traffic light program\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction The chosen traffic light pattern is incompatible with this junction\nAdvanced_AI_cannot_be_activated Advanced AI cannot be activated\nThe_Advanced_Vehicle_AI_cannot_be_activated The Advanced Vehicle AI cannot be activated because you are already using another mod that modifies vehicle behavior (e.g. Improved AI or Traffic++).\nEnable_dynamic_path_calculation Enable dynamic path calculation\nLane_Arrow_Changer_Disabled_Connection The lane arrow changer for this lane is disabled because you have created lane connections by hand.\nLane_connector Lane connector\nConnected_lanes Connected lanes\nUse_alternative_view_mode Use alternative view mode\nRoad_type Road type\nDefault_speed_limit Default speed limit\nUnit_system Unit system\nMetric Metric\nImperial Imperial\nUse_more_CPU_cores_for_route_calculation_if_available Use more CPU cores for route calculation (if available) \nActivated_features Activated features\nJunction_restrictions Junction restrictions\nProhibit_spawning_of_pocket_cars Prohibit cims to spawn pocket cars\nReset_stuck_cims_and_vehicles Reset stuck cims and vehicles\nDefault_speed_limits Default speed limits\nLooking_for_a_parking_spot Looking for a parking spot\nDriving_to_a_parking_spot Driving to a parking spot\nDriving_to_another_parking_spot Driving to another parking spot\nEntering_vehicle Entering vehicle\nWalking_to_car Walking to car\nUsing_public_transport Using public transport\nWalking Walking\nThinking_of_a_good_parking_spot Thinking of a good parking spot\nSwitch_view Switch view\nOutgoing_demand Outgoing demand\nIncoming_demand Incoming demand\nAdvanced_Vehicle_AI Advanced Vehicle AI\nHeavy_trucks_prefer_outer_lanes_on_highways Heavy vehicles prefer outer lanes on highways\nParking_AI Parking AI\nEnable_more_realistic_parking Enable more realistic parking\nReset_custom_speed_limits Reset custom speed limits\nReload_global_configuration Reload global configuration\nReset_global_configuration Reset global configuration\nGeneral General\nGameplay Gameplay\nOverlays Overlays\nRealistic_speeds Realistic speeds\nEvacuation_busses_may_ignore_traffic_rules Evacuation buses may ignore traffic rules\nEvacuation_busses_may_only_be_used_to_reach_a_shelter Evacuation buses may only be used to reach a shelter\nVehicle_behavior Vehicle behavior\nPolicies_&_Restrictions Policies & Restrictions\nAt_junctions At junctions\nIn_case_of_emergency In case of emergency\nShow_lane-wise_speed_limits Show lane-wise speed limits\nLanguage Language\nGame_language Game language\nrequires_game_restart requires game restart\nCustomizations_come_into_effect_instantaneously Customizations come into effect instantaneously\nOptions Options\nLock_main_menu_button_position Lock main menu button position\nLock_main_menu_position Lock main menu position\nRecalculating_lane_routing Recalculating lane routing\nPlease_wait Please wait\nParking_restrictions Parking restrictions\nSimulation Simulation\nOn_roads On roads\nBan_private_cars_and_trucks_on_bus_lanes Ban private cars and trucks on bus lanes\ndefault default\nflow_ratio flow ratio\nwait_ratio wait ratio\nAfter_min._time_has_elapsed_switch_to_next_step_if After min. time has elapsed, switch to next step if\nAdaptive_step_switching Adaptive step switching\nDynamic_lane_section Dynamic lane selection\nPercentage_of_vehicles_performing_dynamic_lane_section Percentage of vehicles performing dynamic lane selection\nVehicle_restrictions_aggression Vehicle restrictions aggression\nStrict Strict\nShow_path-find_stats Show path-find stats\nRemove_this_vehicle Remove this vehicle\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights Vehicles follow priority rules at junctions with timed traffic lights\nEnable_tutorial_messages Enable tutorial messages\nTMPE_TUTORIAL_HEAD_MainMenu Traffic Manager: President Edition\nTMPE_TUTORIAL_BODY_MainMenu Welcome to TM:PE!\\n\\nUser manual: http://www.viathinksoft.de/tmpe\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool Junction restrictions\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool Control how vehicles and pedestrians shall behave at junctions.\\n\\n1. Click on the junction you want to manage\\n2. Click on the appropriate icon to toggle restrictions.\\n\\nAvailable restrictions:\\n- Allow/Disallow lane changing for vehicle going straight on\\n- Allow/Disallow u-turns\\n- Allow/Disallow vehicles to enter a blocked junction\\n- Allow/disallow pedestrians to cross the street\nTMPE_TUTORIAL_HEAD_LaneArrowTool Lane arrows\nTMPE_TUTORIAL_BODY_LaneArrowTool Restrict the set of directions that vehicles are allowed to take.\\n\\n1. Click on a road segment next to a junction\\n2. Select the allowed directions.\nTMPE_TUTORIAL_HEAD_LaneConnectorTool Lane connector\nTMPE_TUTORIAL_BODY_LaneConnectorTool Connect two or more lanes with each other in order to tell which lanes vehicles may use.\\n\\n1. Click on a lane changing point (grey circles).\\n2. Click on a source marker.\\n3. Click on a target marker to connect it with the source marker.\\n4. Click anywhere with your secondary mouse button to return back to source marker selection.\\n\\nAvailable hotkeys:\\n\\n- Delete or Backspace: Remove all lane connections\\n- Shift + S: Cycle through all available \"stay on lane\" patterns\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool Manual traffic lights\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool Try out custom traffic lights.\\n\\n1. Click on a junction\\n2. Use the tool to control traffic lights.\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool Parking restrictions\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool Control where parking is allowed.\\n\\nClick on the \"P\" icons.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold while clicking to apply parking restrictions to multiple road segments at once\nTMPE_TUTORIAL_HEAD_PrioritySignsTool Priority signs\nTMPE_TUTORIAL_BODY_PrioritySignsTool Define priority rules at junctions.\\n\\n1. Click on a junction.\\n2. Click on the blank spots to cycle through the available priority signs (priority road, yield, stop).\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift to add priority signs to multiple road segments at once. Click again while holding Shift to cycle through all available patterns.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool Speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool Set up speed restrictions.\\n\\n1. In the window, click on the speed limit you want to set.\\n2. Click on a road segment to change the speed limit.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply speed limits to multiple road segments at once.\\n- Ctrl: Hold Ctrl to control speed limits per individual lane.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool Timed traffic lights\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool Set up timed traffic lights.\\n\\n1. Click on a junction.\\n2. In the window, click on \"Add step\".\\n3. Click on the overlay elements to set desired traffic lights states.\\n4. Click on \"Add\".\\n5. Repeat as desired.\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool Toggle traffic lights\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool Add or remove traffic lights to/from junctions.\\n\\nClick on a junction to toggle traffic lights.\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool Vehicle restrictions\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool Ban vehicles from certain road segments.\\n\\n1. Click on a road segment.\\n2. Click on the icons to toggle restrictions.\\n\\nDistinguished vehicle types:\\n\\n- Road vehicles: Passenger cars, buses, taxis, cargo trucks, service vehicles, emergency vehicles\\n- Rail vehicles: Passenger trains, cargo trains\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply restrictions to multiple road segments at once.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults Default speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. Use the arrows in the upper half to cycle through all road types.\\n2. In the lower half, select a speed limit.\\n3. Click on \"Save\" to set the selected speed limit as default for future road segments of the selected type. Click on \"Save & Apply\" to also update all existing roads of the selected type.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep Add a timed step \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. Within the in-game overlay, click on the traffic lights to change their state. Use the \"Change mode\" button to add directional traffic lights.\\n2. Enter both a minimum and maximum duration for the step. After the min. time has elapsed the traffic light will count and compare approaching vehicles.\\n3. Optionally, select a step switching type. Per default, the step changes if roughly less vehicles are driving than waiting.\\n4. Optionally, adjust the light's sensitivity. For example, move the slider to the left to make the timed traffic light less sensitive for waiting vehicles. \nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy Copy a timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy Click on another junction to paste the timed traffic light.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction Add a junction to the timed traffic light \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction Click on another junction to add it. Both lights will be joined such that the timed program will then control both junctions at once.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction Remove a junction from the timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction Click on one of the junctions that are controlled by this timed program. The selected traffic light will be removed such that the timed programm will no longer manage it.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nPublic_transport Public transport\nPrevent_excessive_transfers_at_public_transport_stations Prevent unnecessary transfers at public transport stations\nCompact_main_menu Compact main menu\nWindow_transparency Window transparency\nOverlay_transparency Overlay transparency\nRemove_this_citizen Remove this citizen\nShow_error_message_if_a_mod_incompatibility_is_detected Show error message if a mod incompatibility is detected\nRemove_parked_vehicles Remove parked vehicles\nNode_is_level_crossing This junction is a level crossing.\\nYou cannot disable traffic lights here.\nExperimental_features Experimental features\nTurn_on_red Turn on red\nVehicles_may_turn_on_red Vehicles may turn at red traffic lights\nAlso_apply_to_left/right_turns_between_one-way_streets Also apply to left & right turns between one-way streets"
  },
  {
    "path": "TLM/TLM/Resources/lang_de.txt",
    "content": "Switch_traffic_lights Ampeln setzen\nAdd_priority_signs Vorfahrtsschilder\nManual_traffic_lights Manuelle Ampeln\nTimed_traffic_lights Zeitgesteuerte Ampeln\nChange_lane_arrows Richtungspfeile\nClear_Traffic Verkehr löschen\nDisable_despawning Despawn ausschalten\nEnable_despawning Despawn einschalten\nNODE_IS_LIGHT Die Kreuzung hat eine Ampel.\\nLösche zunächst die Ampel, indem du auf \"Ampeln setzen\" und dann auf die Kreuzung klickst.\nNODE_IS_TIMED_LIGHT Die Kreuzung wird von einer Zeitsteuerung kontrolliert.\\nWähle \"Zeitgesteuerte Ampeln\" aus, klicke auf die Kreuzung und klicke auf \"Entfernen\".\nSelect_nodes_windowTitle Auswahl der Kreuzungen\nSelect_nodes Wähle eine oder mehrere Kreuzungen aus\nNode Knotenpunkt\nDeselect_all_nodes Auswahl löschen\nSetup_timed_traffic_light Zeitgesteuerte Ampel einrichten\nState Schritt\nSkip Weiter\nup hoch\ndown runter\nView Anzeigen\nEdit Ändern\nDelete Löschen\nTimed_traffic_lights_manager Verwaltung zeitgesteuerter Ampeln\nAdd_step Schritt hinzufügen\nRemove_timed_traffic_light Ampel entfernen\nMin._Time: Min. Zeit:\nMax._Time: Max. Zeit:\nSave Speichern\nAdd Hinzufügen\nSensitivity Empfindlichkeit\nVery_Low Sehr niedrig\nLow Niedrig\nMedium Mittel\nHigh Hoch\nVery_high Sehr hoch\nExtreme_long_green/red_phases Extrem lange Grün-/Rotphasen\nVery_long_green/red_phases Sehr lange Grün-/Rotphasen\nLong_green/red_phases Lange Grün-/Rotphasen\nModerate_green/red_phases Moderate Grün-/Rotphasen\nShort_green/red_phases Kurze Grün-/Rotphasen\nVery_short_green/red_phases Sehr kurze Grün-/Rotphasen\nExtreme_short_green/red_phases Extrem kurze Grün-/Rotphasen\nHide_counters Zähler verstecken\nShow_counters Zähler anzeigen\nStart Start\nStop Stopp\nEnable_test_mode_(stay_in_current_step) Testmodus aktivieren (im aktuellen Schritt bleiben)\navg._flow fahrend\navg._wait wartend\nmin/max min/max\nLane Fahrspur\nSet_Speed Geschwindigkeit setzen {0}\nMax_speed Max. Geschwindigkeit\nSegment Segment\nincoming eingehend\nEnable_Advanced_Vehicle_AI Verbesserte Fahrzeug-KI aktivieren\nVehicles_may_enter_blocked_junctions Fahrzeuge dürfen in blockierte Kreuzungen einfahren\nAll_vehicles_may_ignore_lane_arrows Alle Fahrzeuge dürfen Richtungspfeile ignorieren\nBusses_may_ignore_lane_arrows Busse dürfen Richtungspfeile ignorieren\nReckless_driving Rücksichtsloses Fahren\nTMPE_Title Traffic Manager: President Edition\nSettings_are_defined_for_each_savegame_separately Einstellungen werden pro Spielstand gespeichert\nSimulation_accuracy Genauigkeit der Simulation (höhere Genauigkeit reduziert Performance)\nEnable_highway_specific_lane_merging/splitting_rules Aktiviere spezifische Einordnungsregeln auf der Autobahn\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): Autofahrer wechseln ihre Spur (wird nur angewendet wenn die KI eingeschaltet ist)\nMaintenance Wartung\nVery_often Sehr häufig\nOften Häufig\nSometimes Manchmal\nRarely Selten\nVery_rarely Sehr selten\nOnly_if_necessary Nur falls nötig\nNodes_and_segments Knoten und Segmente\nLanes Fahrspuren\nPath_Of_Evil_(10_%) Pfad des Bösen (10 %)\nRush_Hour_(5_%) Feierabendverkehr (5 %)\nMinor_Complaints_(2_%) Einige Beschwerden (2 %)\nHoly_City_(0_%) Heilige Stadt (0 %)\nForget_toggled_traffic_lights Gesetzte/Entfernte Ampeln vergessen\nRoad_is_already_in_a_group! Straße ist bereits in einer Gruppe!\nAll_selected_roads_must_be_of_the_same_type! Alle ausgewählten Straßen müssen vom gleichen Typ sein!\nCreate_group Gruppe erstellen\nDelete_group Gruppe löschen\nAdd_zoning Zonen aktivieren\nRemove_zoning Zonen löschen\nLane_Arrow_Changer_Disabled_Highway Die Richtungspfeile für diese Spur können nicht geändert werden, weil du die speziellen Regeln für Autobahnen aktiviert hast.\nAdd_junction_to_timed_light Kreuzung zur Ampel hinzufügen\nRemove_junction_from_timed_light Kreuzung von der Ampel entfernen\nSelect_junction Wähle eine Kreuzung aus\nCancel Abbrechen\nSpeed_limits Geschwindigkeitsbeschränkungen\nPersistently_visible_overlays Dauerhaft sichtbare Overlays\nPriority_signs Vorfahrtsschilder\nVehicles_may_do_u-turns_at_junctions Fahrzeuge dürfen an Kreuzungen wenden\nVehicles_going_straight_may_change_lanes_at_junctions Fahrz. dürfen die Spur wechseln, wenn sie an Kreuzungen geradeaus fahren\nAllow_u-turns Wenden erlauben\nAllow_lane_changing_for_vehicles_going_straight Spurwechsel für Fahrzeuge erlauben, die geradeaus fahren\nAllow_vehicles_to_enter_a_blocked_junction Erlaube Fahrzeuge, in eine blockierte Kreuzung einzufahren\nRoad_condition_has_a_bigger_impact_on_vehicle_speed Der Straßenzustand hat einen größeren Einfluss auf die Geschwindigkeit\nVehicle_restrictions Fahrzeugbeschränkungen\nCopy Kopieren\nPaste Einfügen\nInvert Invertieren\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions Wende Fahrzeugbeschränkungen auf alle Straßensegmente zwischen zwei Kreuzungen an\nAllow_all_vehicles Erlaube alle Fahrzeuge\nBan_all_vehicles Verbiete alle Fahrzeuge\nSet_all_traffic_lights_to_red Alle Ampeln auf rot setzen\nRotate_left Links rotieren\nRotate_right Rechts rotieren\nName Name\nApply Anwenden\nSelect_a_timed_traffic_light_program Wähle ein zeitgesteuertes Ampelprogramm aus \nThe_chosen_traffic_light_program_is_incompatible_to_this_junction Das ausgewählte Ampelprogramm ist inkompatibel zu dieser Kreuzung\nAdvanced_AI_cannot_be_activated Erweiterte Fahrzeug-KI kann nicht aktiviert werden\nThe_Advanced_Vehicle_AI_cannot_be_activated Die erweiterte Fahrzeug-KI kann nicht aktiviert werden, weil du bereits einen anderen Mod verwendest, der das Verhalten von Fahrzeugen verändern (z.B. Improved AI oder Traffic++).\nEnable_dynamic_path_calculation Aktiviere die dynamische Pfadberechnung\nLane_Arrow_Changer_Disabled_Connection Die Richtungspfeile für diese Spur können nicht geändert werden, weil du Spuren von Hand verbunden hast.\nLane_connector Fahrspurverbinder\nConnected_lanes Verbundene Fahrspuren\nUse_alternative_view_mode Verwende den alternativen Anzeigemodus\nRoad_type Straßentyp\nDefault_speed_limit Standard-Geschwindigkeitsbeschränkung\nUnit_system Einheitensystem\nMetric Metrisch\nImperial Imperial\nUse_more_CPU_cores_for_route_calculation_if_available Verwende mehr CPU-Kerne zur Routenberechnung (falls verfügbar)\nActivated_features Aktivierte Features\nJunction_restrictions Kreuzungsbeschränkungen\nProhibit_spawning_of_pocket_cars Verbiete Cims das Spawnen von Autos \"aus der Tasche heraus\"\nReset_stuck_cims_and_vehicles Setze steckengebliebene Cims und Fahrzeuge zurück\nDefault_speed_limits Standard-Geschwindigkeitsbeschränkungen\nLooking_for_a_parking_spot Sucht einen Parkplatz\nDriving_to_a_parking_spot Fährt zum Parkplatz\nDriving_to_another_parking_spot Fährt zu einem anderen Parkplatz\nEntering_vehicle Steigt ins Auto ein\nWalking_to_car Läuft zum Auto\nUsing_public_transport Verwendet ÖPNV\nWalking Läuft\nThinking_of_a_good_parking_spot Erinnert sich an einen Parkplatz\nSwitch_view Ansicht wechseln\nOutgoing_demand Ausgehende Nachfrage\nIncoming_demand Eingehende Nachfrage\nAdvanced_Vehicle_AI Verbesserte Fahrzeug-KI\nHeavy_trucks_prefer_outer_lanes_on_highways Schwere Laster bevorzugen die äußeren Spuren auf Autobahnen\nParking_AI Parkplatz-KI\nEnable_more_realistic_parking Aktiviere realistisches Parken\nReset_custom_speed_limits Geschwindigkeitsbegrenzungen auf Standard zurücksetzen\nReload_global_configuration Globale Konfiguration neuladen\nReset_global_configuration Globale Konfiguration zurücksetzen\nGeneral Allgemein\nGameplay Gameplay\nOverlays Overlays\nRealistic_speeds Realistische Geschwindigkeiten\nEvacuation_busses_may_ignore_traffic_rules Evakuierungsbusse dürfen Verkehrsregeln missachten\nEvacuation_busses_may_only_be_used_to_reach_a_shelter Evakuierungsbusse dienen nur zum Erreichen einer Notunterkunft\nVehicle_behavior Fahrzeugverhalten\nPolicies_&_Restrictions Richtlinien & Einschränkungen\nAt_junctions An Kreuzungen\nIn_case_of_emergency Im Notfall\nShow_lane-wise_speed_limits Zeige spurweise Geschwindigkeitsbeschränkungen\nLanguage Sprache\nGame_language Spielsprache\nrequires_game_restart erfordert einen Neustart des Spiels\nCustomizations_come_into_effect_instantaneously Anpassungen treten sofort in Kraft\nOptions Optionen\nLock_main_menu_button_position Position der Hauptmenü-Schaltfläche sperren\nLock_main_menu_position Position des Hauptmenüs sperren \nRecalculating_lane_routing Berechne Routenführung neu\nPlease_wait Bitte warten\nParking_restrictions Parkverbote\nSimulation Simulation\nOn_roads Auf Straßen\nBan_private_cars_and_trucks_on_bus_lanes Verbiete private Fahrzeuge und Lieferwagen auf Busspuren\ndefault Standard\nflow_ratio fahrende Fzg.\nwait_ratio wartende Fzg.\nAfter_min._time_has_elapsed_switch_to_next_step_if Wenn die min. Zeit abläuft, wechsle zum nächsten Schritt falls\nAdaptive_step_switching Adaptive Schrittumschaltung\nDynamic_lane_section Dynamische Fahrspurwahl\nPercentage_of_vehicles_performing_dynamic_lane_section Prozentsatz der Fahrzeuge mit dynamischer Fahrspurwahl\nVehicle_restrictions_aggression Auslegung der Fahrzeugbeschränkungen\nStrict Strict\nShow_path-find_stats Path-Find Statistik anzeigen\nRemove_this_vehicle Dieses Fahrzeug entfernen\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights Fahrzeuge befolgen an zeitgesteuerten Ampeln die Vorrangsregelung\nEnable_tutorial_messages Tutorial aktivieren\nTMPE_TUTORIAL_HEAD_MainMenu Traffic Manager: President Edition\nTMPE_TUTORIAL_BODY_MainMenu Willkommen bei TM:PE!\\n\\nBenutzerhandbuch: http://www.viathinksoft.de/tmpe\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool Kreuzungsbeschränkungen\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool Steuere, wie Fahrzeuge und Fußgänger sich an Kreuzungen zu verhalten haben.\\n\\n1. Klicke auf die Kreuzung, die du kontrollieren möchtest.\\n2. Klicke auf das entsprechende Symbol, um die Beschränkungen ein- oder auszuschalten.\\n\\nVerfügbare Beschränkungen:\\n- Erlaube/Verbiete Spurwechsel für Fahrzeuge, die geradeaus fahren.\\n- Erlaube/Verbiete das Wenden.\\n- Erlaube/Verbiete Fahrzeugen, in die blockierte Kreuzung einzufahren.\\n- Erlaube/Verbiete Fußgängern, die Straße zu überqueren.\nTMPE_TUTORIAL_HEAD_LaneArrowTool Richtungspfeile\nTMPE_TUTORIAL_BODY_LaneArrowTool Limitiere die Richtungen, in die Fahrzeuge fahren dürfen.\\n\\n1. Klicke auf ein Straßensegment nahe einer Kreuzung.\\n2. Wähle die erlaubten Richtungen aus.  \nTMPE_TUTORIAL_HEAD_LaneConnectorTool Fahrspurverbinder\nTMPE_TUTORIAL_BODY_LaneConnectorTool Verbinde zwei oder mehr Fahrspuren miteinander, um Fahrzeugen nur bestimmte Fahrspurwechsel zu erlauben.\\n\\n1. Klicke auf einen Spurwechselpunkt (grauer Kreis).\\n2. Klicke auf eine Quellspurmarkierung.\\n3. Klicke auf eine Zielspurmarkierung, um die Zielspur mit der Quellspur zu verbinden.\\n4. Klicke irgendwo mit der sekundären Maustaste, um zur Qullspurselektion zurückzukehren.\\n\\nVerfügbare Tastenkombinationen:\\n\\n-Entf. oder Rücktaste: Lösche alle Fahrspurverbindungen\\n- Umschalt + S: Wechsle durch alle verfügbaren \"Bleib auf der Spur\"-Vorlagen.\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool Manuelle Ampeln\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool Probiere die Ampelschaltungen von TM:PE aus.\\n\\n1. Klicke auf eine Kreuzung.\\n2. Verwende das Werkzeug, um die Ampel zu kontrollieren.  \nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool Parkverbote\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool Steuere, wo das Parken erlaubt ist.\\n\\nKlicke auf die \"P\"-Symbole.\\n\\nVerfügbare Tastenkombinationen:\\n\\n- Umschalt: Halte die Taste beim Klicken, um Parkverbote auch auf benachbarte Straßensegmente anzuwenden.\nTMPE_TUTORIAL_HEAD_PrioritySignsTool Vorfahrtsschilder\nTMPE_TUTORIAL_BODY_PrioritySignsTool Definiere Vorfahrtsregeln an Kreuzungen.\\n\\n1. Klicke auf eine Kreuzung.\\n2. Klicke auf eine der leeren Punkte, um zwischen den verfügbaren Vorfahrtsschildern umzuschalten (Hauptstraße, Vorfahrt gewähren, Stopp).\\n\\nVerfügbare Tastenkombinationen:\\n\\n- Umschalt: Halte die Taste, um Vorfahrtsschilder auch auf benachbarten Kreuzungen zu setzen. Klicke nochmals während du Umschalt gedrückt hälst, um alle verfügbaren Vorlagen durchzuwechseln.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool Geschwindigkeitsbeschränkungen\nTMPE_TUTORIAL_BODY_SpeedLimitsTool Setze Geschwindigkeitsbeschränkungen.\\n\\n1. Wähle im Fenster eine Geschwindigkeitsbegrenzung aus.\\n2. Klicke auf ein Straßensegment, um die ausgewählte Begrenzung anzuwenden.\\n\\nVerfügbare Tastenkombinationen:\\n\\n- Umschalt: Halte die Taste beim Klicken, um Geschwindigkeitsbegrenzungen auch auf benachbarte Straßensegmente anzuwenden.\\n- Strg: Halte die Taste beim Klicken, um Geschwindigkeitsbegrenzungen für einzelne Fahrspuren zu setzen.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool Zeitgesteuerte Ampelschaltungen\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool Definiere zeitgesteuerte Ampeln.\\n\\n1. Klicke auf eine Kreuzung.\\n2. Klicke im Fenster auf \"Schritt hinzufügen\".\\n3. Klick auf die Overlayelemente, um den gewünschten Zustand der Ampeln festzulegen.\\n4. Klicke auf \"Hinzufügen\".\\n5. Wiederhole die obigen Schritte bei Bedarf.\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool Ampeln setzen \nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool Füge Ampeln hinzu oder lösche sie von Kreuzungen.\\n\\nKlicke auf eine Kreuzung, um ihre Ampel ein- oder auszuschalten.\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool Fahrzeugbeschränkungen\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool Erlaube oder verbiete Fahrzeugen, bestimmte Straßensegmente zu benutzen.\\n\\n1. Klicke auf ein Straßensegment.\\n2. Klicke auf die Symbole, um die Beschränkungen ein- oder auszuschalten.\\n\\nUnterschiedene Fahrzeugtypen:\\n\\n- Straßenfahrzeuge: KFZ, Busse, Taxen, Lieferwagen/Laster, Dienstleistungen, Notfallfahrzeuge\\n- Züge: Passagierzüge, Frachtzüge\\n\\nVerfügbare Tastenkombinationen:\\n\\n- Umschalt: Halte die Taste beim Klicken, um Fahrzeugbeschränkungen auch auf benachbarte Straßen-/Schienensegmente anzuwenden.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults Standard-Geschwindigkeitsbegrenzungen\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. Benutze die Pfeiltasten in der oberen Hälfte des Fensters, um zwischen allen verfügbaren Straßensegmenten umzuschalten.\\n2. Wähle auf die gleiche Weise in der unteren Hälfte die gewünschte Geschwindigkeitsbegrenzung aus.\\n3. Klicke auf \"Speichern\" um die Auswahl für in der Zukunft gebaute Straßensegmente des gewählten Typs anzuwenden. Klicke auf \"Speichern & Anwenden\", um die Auswahl auch für bereits gebaute Straßen des gewählten Typs anzuwenden.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep Füge einen neuen Zeitschritt hinzu \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. Klick auf eine Ampel innerhalb des Overlays, um ihren Zustand zu ändern. Verwende die \"Change mode\"-Schaltfläche im Overlay, um Ampeln für verschiedene Richtungen hinzuzufügen.\\n2. Gebe eine Minimal- und Maximaldauer für den Zeitschritt ein. Sobald die Minimalzeit verstrichen ist, wird die Ampelschaltung die ankommendenen Fahrzeuge messen und die Zahlen für alle Richtungen miteinander vergleichen.\\n3. (optional) Konfiguriere die adaptive Schrittumschaltung. Im Standard wird der Schritt gewechselt, wenn (grob) weniger Fahrzeuge die Kreuzung durchqueren als Fahrzeuge warten.\\n4. (optional) Konfiguriere die Sensitivität der Ampelschaltung. Ziehe den Regler z.B. nach links, um die Ampel weniger sensibel für wartende Fahrzeuge zu machen. \nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy Kopiere eine zeitgesteuerte Ampel\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy Klicke auf eine andere Kreuzung, um die Ampelschaltung dort einzufügen.\\n\\nKlicke irgendwo mit der sekundären Maustaste, um die Operation abzubrechen.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction Füge der Ampelschaltung eine andere Kreuzung hinzu \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction Klicke auf eine andere Kreuzung, um sie der Schaltung hinzuzufügen. Die Ampelschaltung wird danach beide Kreuzungen gleichzeitig steuern.\\n\\nKlicke irgendwo mit der sekundären Maustaste, um die Operation abzubrechen.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction Entferne eine Kreuzung von der Ampelschaltung.\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction Klicke auf eine der Kreuzungen, die momentan von der Ampelschaltungen kontrolliert werden. Die selektierte Ampel wird entfernt, so dass die Ampelschaltung die Kreuzung nicht mehr kontrolliert.\\n\\nKlicke irgendwo mit der sekundären Maustaste, um die Operation abzubrechen.\nPublic_transport Öffentlicher Personennahverkehr\nPrevent_excessive_transfers_at_public_transport_stations Verhindere übertriebenes Umsteigen an Haltestellen\nCompact_main_menu Kompaktes Hauptmenü\nWindow_transparency Fenstertransparenz\nOverlay_transparency Overlaytransparenz\nRemove_this_citizen Diesen Cim entfernen\nShow_error_message_if_a_mod_incompatibility_is_detected Zeige Fehlermeldung, wenn eine Mod-Inkompatibilität erkannt wurde\nRemove_parked_vehicles Entferne geparkte Autos\nNode_is_level_crossing Diese Kreuzung ist ein Bahnübergang.\\nDu kannst die Ampeln hier nicht entfernen.\nExperimental_features Experimentelle Features\nTurn_on_red Turn on red\nVehicles_may_turn_on_red Fahrzeuge dürfen an roten Ampeln abbiegen\nAlso_apply_to_left/right_turns_between_one-way_streets Gilt auch für Links- & Rechtskurven zwischen Einbahnstraßen"
  },
  {
    "path": "TLM/TLM/Resources/lang_es.txt",
    "content": "Switch_traffic_lights Agregar/Borrar semáforos\nAdd_priority_signs Agregar señales de tránsito\nManual_traffic_lights Semáforos manuales\nTimed_traffic_lights Semáforos programados\nChange_lane_arrows Editar flechas de pistas\nClear_Traffic Limpiar Tráfico\nDisable_despawning Desactivar desaparición\nEnable_despawning Activar desaparición\nNODE_IS_LIGHT El cruce tiene semáforos.\\nBorra los semáforos seleccionando \"Agregar/Borrar semáforos\" y haz clic en este cruce.\nNODE_IS_TIMED_LIGHT El cruce es parte de un programa temporizado.\\nSelecciona \"Semáforos programados\", haz clic en este cruce y luego en \"Eliminar\".\nSelect_nodes_windowTitle Seleccionar cruces\nSelect_nodes Seleccionar cruces\nNode Cruce\nDeselect_all_nodes Deseleccionar todos los cruces\nSetup_timed_traffic_light Ajustar semáforos programados\nState Estado\nSkip Saltar\nup arriba\ndown abajo\nView Ver\nEdit Editar\nDelete Borrar\nTimed_traffic_lights_manager Administrador de semáforos programados\nAdd_step Añadir fase\nRemove_timed_traffic_light Borrar semáforo programado\nMin._Time: Tiempo min:\nMax._Time: Tiempo máx:\nSave Guardar\nAdd Agregar\nSensitivity Sensibilidad\nVery_Low Muy Baja\nLow Baja\nMedium Media\nHigh Alta\nVery_high Muy alta\nExtreme_long_green/red_phases Fases verde/rojo extremadamente largas\nVery_long_green/red_phases Fases verde/rojo muy largas\nLong_green/red_phases Fases verde/rojo largas\nModerate_green/red_phases Fases verde/rojo moderadas\nShort_green/red_phases Fases verde/rojo cortas\nVery_short_green/red_phases Fases verde/rojo muy cortas\nExtreme_short_green/red_phases Fases verde/rojo extremadamente cortas\nHide_counters Ocultar contadores\nShow_counters Mostrar contadores\nStart Iniciar\nStop Detener\nEnable_test_mode_(stay_in_current_step) Activar modo de prueba (mantiene la fase actual)\navg._flow Tránsito promedio\navg._wait Espera promedio\nmin/max min/máx\nLane Vía\nSet_Speed Ajustar Velocidad {0}\nMax_speed Velocidad Máxima\nSegment Segmento\nincoming viniendo\nEnable_Advanced_Vehicle_AI Activar IA de vehículos avanzada\nVehicles_may_enter_blocked_junctions Vehículos pueden entrar a cruces bloqueados\nAll_vehicles_may_ignore_lane_arrows Todos los vehículos pueden ignorar las flechas de las vías\nBusses_may_ignore_lane_arrows Buses pueden ignorar flechas de las vías\nReckless_driving Conducción agresiva (BETA)\nTMPE_Title Traffic Manager: President Edition\nSettings_are_defined_for_each_savegame_separately Los ajustes son definidos para cada partida por separado\nSimulation_accuracy Precisión de la simulación (más alto reduce el rendimiento)\nEnable_highway_specific_lane_merging/splitting_rules Activar control de unión/división de vías de autopista\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): Conductores prefieren cambiar de vía (sólo si IA Avanzada está activada)\nMaintenance Mantenimiento\nVery_often Muy frecuentemente\nOften Frecuentemente\nSometimes A veces\nRarely Raramente\nVery_rarely Muy raramente\nOnly_if_necessary Sólo si es necesario\nNodes_and_segments Cruces y segmentos\nLanes Vías\nPath_Of_Evil_(10_%) El mismo infierno (10 %)\nRush_Hour_(5_%) Hora Punta (5 %)\nMinor_Complaints_(2_%) Colisiones menores (2 %)\nHoly_City_(0_%) Ciudad divina (0 %)\nForget_toggled_traffic_lights Borrar semáforos (no afecta semáforos programados)\nRoad_is_already_in_a_group! ¡La calle ya está en un grupo!\nAll_selected_roads_must_be_of_the_same_type! ¡Todas las calles deben ser del mismo tipo!\nCreate_group Crear Grupo\nDelete_group Borrar Grupo\nAdd_zoning Agregar zona\nRemove_zoning Borrar zona\nLane_Arrow_Changer_Disabled_Highway El editor de flechas está desactivado debido a que has activado el control de autopistas.\nAdd_junction_to_timed_light Agregar cruce a este conjunto temporizador\nRemove_junction_from_timed_light Remover cruce de este conjunnto temporizador\nSelect_junction Seleccionar un cruce\nCancel Cancelar\nSpeed_limits Límites de velocidad\nPersistently_visible_overlays Datos mostrados en el juego\nPriority_signs Señales de prioridad\nVehicles_may_do_u-turns_at_junctions Los vehículos pueden hacer giros en \"U\" en los cruces\nVehicles_going_straight_may_change_lanes_at_junctions Los vehículos que circulan recto pueden cambiar de pista en intersecciones\nAllow_u-turns Permitir giros en \"U\"\nAllow_lane_changing_for_vehicles_going_straight Permitir cambio de pista a los vehículos que circulan recto\nAllow_vehicles_to_enter_a_blocked_junction Permitor que los vehículos ingresen a cruces bloqueados\nRoad_condition_has_a_bigger_impact_on_vehicle_speed La condición de la calle tiene un mayor impacto en la rapidez de los vehículos\nVehicle_restrictions Restricción vehicular\nCopy Copiar\nPaste Pegar\nInvert Invertir\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions Aplicar restricción vehicular a todas los segmentos de calle entre dos cruces\nAllow_all_vehicles Permitir a todos los vehículos\nBan_all_vehicles Prohibir a todos los vehículos\nSet_all_traffic_lights_to_red Poner todos los semáforos en rojo\nRotate_left Girar a la izq.\nRotate_right Girar a la der.\nName Nombre\nApply Aplicar\nSelect_a_timed_traffic_light_program Seleccionar un semáforo programado\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction El programa de semáforo elegido es incompatible para este cruce\nAdvanced_AI_cannot_be_activated IA Avanzada no puede ser activada\nThe_Advanced_Vehicle_AI_cannot_be_activated La IA de vehículos avanzada no puede ser activada debido a que estás usando algún otro mod que modifica el comportamiento de los vehículos (por ejemplo Improved AI o Traffic++)\nEnable_dynamic_path_calculation Activar el cálculo dinámico de las rutas\nLane_Arrow_Changer_Disabled_Connection El modificador de flechas de la carretera fue desactivado porque has creado las conexiones manualmente.\nLane_connector Conector de carriles\nConnected_lanes Carriles conectados\nUse_alternative_view_mode Usar modo de vista alternativo\nRoad_type Tipo de carretera\nDefault_speed_limit Límite de velocidad por defecto\nUnit_system Sistema de medida\nMetric Métrico\nImperial Imperial\nUse_more_CPU_cores_for_route_calculation_if_available Usar más nucleos del procesador para el cálculo de rutas (si está disponible)\nActivated_features Características activadas\nJunction_restrictions Restricciones del cruce\nProhibit_spawning_of_pocket_cars Prohibir la creación de autos pequeños\nReset_stuck_cims_and_vehicles Restablecer cims y vehículos atascados\nDefault_speed_limits Límites de velocidad por defecto\nLooking_for_a_parking_spot Buscando un estacionamiento\nDriving_to_a_parking_spot Conduciendo a un estacionamiento\nDriving_to_another_parking_spot Conduciendo a otro estacionamiento\nEntering_vehicle Vehículo entrante\nWalking_to_car Caminando al auto\nUsing_public_transport Usando transporte público\nWalking Caminando\nThinking_of_a_good_parking_spot Pensando en un buen estacionamiento\nSwitch_view Cambiar vista\nOutgoing_demand Demanda saliente\nIncoming_demand Demanda entrante\nAdvanced_Vehicle_AI IA de vehículos avanzada\nHeavy_trucks_prefer_outer_lanes_on_highways Camiones prefieren los carriles exteriores en las autopistas\nParking_AI IA de estacionamientos\nEnable_more_realistic_parking  Activar estacionamientos más realista\nReset_custom_speed_limits Restablecer límites de velocidad personalizados\nReload_global_configuration Recargar configuración global\nReset_global_configuration Restablecer configuración global\nGeneral General\nGameplay Jugabilidad\nOverlays Capas de información\nRealistic_speeds Velocidades realistas\nEvacuation_busses_may_ignore_traffic_rules Los buses de evacuación pueden ignorar reglas de tránsito\nEvacuation_busses_may_only_be_used_to_reach_a_shelter Los buses de evacuación sólo pueden usarse para llegar a un refugio\nVehicle_behavior Comportamiento vehícular\nPolicies_&_Restrictions Normas y restricciones\nAt_junctions En los cruces\nIn_case_of_emergency En caso de emergencia\nShow_lane-wise_speed_limits Mostrar límite de velocidad en el carril\nLanguage Idioma\nGame_language Idioma del juego\nrequires_game_restart Requiere reiniciar el juego\nCustomizations_come_into_effect_instantaneously Los modificaciones tendrán efecto instantáneamente\nOptions Opciones\nLock_main_menu_button_position Bloquear la posición del menú principal\nLock_main_menu_position Bloquear posición del menú principal\nRecalculating_lane_routing Recalculando la ruta del carril\nPlease_wait Espera por favor\nParking_restrictions Restricciones de los estacionamientos\nSimulation Simulation\nOn_roads On roads\nBan_private_cars_and_trucks_on_bus_lanes Ban private cars and trucks on bus lanes\ndefault default\nflow_ratio flow ratio\nwait_ratio wait ratio\nAfter_min._time_has_elapsed_switch_to_next_step_if After min. time has elapsed, switch to next step if\nAdaptive_step_switching Adaptive step switching\nDynamic_lane_section Dynamic lane selection\nPercentage_of_vehicles_performing_dynamic_lane_section Percentage of vehicles performing dynamic lane selection\nVehicle_restrictions_aggression Vehicle restrictions aggression\nStrict Strict\nShow_path-find_stats Show path-find stats\nRemove_this_vehicle Remove this vehicle\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights Vehicles follow priority rules at junctions with timed traffic lights\nEnable_tutorial_messages Enable tutorial messages\nTMPE_TUTORIAL_HEAD_MainMenu Traffic Manager: President Edition\nTMPE_TUTORIAL_BODY_MainMenu Welcome to TM:PE!\\n\\nUser manual: http://www.viathinksoft.de/tmpe\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool Junction restrictions\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool Control how vehicles and pedestrians shall behave at junctions.\\n\\n1. Click on the junction you want to manage\\n2. Click on the appropriate icon to toggle restrictions.\\n\\nAvailable restrictions:\\n- Allow/Disallow lane changing for vehicle going straight on\\n- Allow/Disallow u-turns\\n- Allow/Disallow vehicles to enter a blocked junction\\n- Allow/disallow pedestrians to cross the street\nTMPE_TUTORIAL_HEAD_LaneArrowTool Lane arrows\nTMPE_TUTORIAL_BODY_LaneArrowTool Restrict the set of directions that vehicles are allowed to take.\\n\\n1. Click on a road segment next to a junction\\n2. Select the allowed directions.\nTMPE_TUTORIAL_HEAD_LaneConnectorTool Lane connector\nTMPE_TUTORIAL_BODY_LaneConnectorTool Connect two or more lanes with each other in order to tell which lanes vehicles may use.\\n\\n1. Click on a lane changing point (grey circles).\\n2. Click on a source marker.\\n3. Click on a target marker to connect it with the source marker.\\n4. Click anywhere with your secondary mouse button to return back to source marker selection.\\n\\nAvailable hotkeys:\\n\\n- Delete or Backspace: Remove all lane connections\\n- Shift + S: Cycle through all available \"stay on lane\" patterns\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool Manual traffic lights\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool Try out custom traffic lights.\\n\\n1. Click on a junction\\n2. Use the tool to control traffic lights.\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool Parking restrictions\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool Control where parking is allowed.\\n\\nClick on the \"P\" icons.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold while clicking to apply parking restrictions to multiple road segments at once\nTMPE_TUTORIAL_HEAD_PrioritySignsTool Priority signs\nTMPE_TUTORIAL_BODY_PrioritySignsTool Define priority rules at junctions.\\n\\n1. Click on a junction.\\n2. Click on the blank spots to cycle through the available priority signs (priority road, yield, stop).\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift to add priority signs to multiple road segments at once. Click again while holding Shift to cycle through all available patterns.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool Speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool Set up speed restrictions.\\n\\n1. In the window, click on the speed limit you want to set.\\n2. Click on a road segment to change the speed limit.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply speed limits to multiple road segments at once.\\n- Ctrl: Hold Ctrl to control speed limits per individual lane.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool Timed traffic lights\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool Set up timed traffic lights.\\n\\n1. Click on a junction.\\n2. In the window, click on \"Add step\".\\n3. Click on the overlay elements to set desired traffic lights states.\\n4. Click on \"Add\".\\n5. Repeat as desired.\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool Toggle traffic lights\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool Add or remove traffic lights to/from junctions.\\n\\nClick on a junction to toggle traffic lights.\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool Vehicle restrictions\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool Ban vehicles from certain road segments.\\n\\n1. Click on a road segment.\\n2. Click on the icons to toggle restrictions.\\n\\nDistinguished vehicle types:\\n\\n- Road vehicles: Passenger cars, busses, taxis, cargo trucks, service vehicles, emergency vehicles\\n- Rail vehicles: Passenger trains, cargo trains\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply restrictions to multiple road segments at once.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults Default speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. Use the arrows in the upper half to cycle through all road types.\\n2. In the lower half, select a speed limit.\\n3. Click on \"Save\" to set the selected speed limit as default for future road segments of the selected type. Click on \"Save & Apply\" to also update all existing roads of the selected type.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep Add a timed step \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. Within the in-game overlay, click on the traffic lights to change their state. Use the \"Change mode\" button to add directional traffic lights.\\n2. Enter both a minimum and maximum duration for the step. After the min. time has elapsed the traffic light will count and compare approaching vehicles.\\n3. Optionally, select a step switching type. Per default, the step changes if roughly less vehicles are driving than waiting.\\n4. Optionally, adjust the light's sensitivity. For example, move the slider to the left to make the timed traffic light less sensitive for waiting vehicles. \nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy Copy a timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy Click on another junction to paste the timed traffic light.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction Add a junction to the timed traffic light \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction Click on another junction to add it. Both lights will be joined such that the timed program will then control both junctions at once.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction Remove a junction from the timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction Click on one of the junctions that are controlled by this timed program. The selected traffic light will be removed such that the timed programm will no longer manage it.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nPublic_transport Public transport\nPrevent_excessive_transfers_at_public_transport_stations Prevent unnecessary transfers at public transport stations\nCompact_main_menu Compact main menu\nWindow_transparency Window transparency\nOverlay_transparency Overlay transparency\nRemove_this_citizen Remove this citizen\nShow_error_message_if_a_mod_incompatibility_is_detected Show error message if a mod incompatibility is detected\nRemove_parked_vehicles Remove parked vehicles\nNode_is_level_crossing This junction is a level crossing.\\nYou cannot disable traffic lights here.\nExperimental_features Experimental features\nTurn_on_red Turn on red\nVehicles_may_turn_on_red Los vehículos pueden girar en los semáforos rojos.\nAlso_apply_to_left/right_turns_between_one-way_streets Also apply to left & right turns between one-way streets"
  },
  {
    "path": "TLM/TLM/Resources/lang_fr.txt",
    "content": "Switch_traffic_lights (Dés)activer les feux tricolores\nAdd_priority_signs Ajouter des signes de priorité\nManual_traffic_lights Feux tricolores manuels\nTimed_traffic_lights Feux tricolores chronométrés\nChange_lane_arrows Changer les directions de voies\nClear_Traffic Nettoyer le trafic\nDisable_despawning Désactiver la disparition\nEnable_despawning Activer la disparition\nNODE_IS_LIGHT Cette intersection a des feux rouges.\\nSupprimez-les en sélectionnant \"(Dés)activer les feux tricolores\" et en cliquant sur l'intersection.\nNODE_IS_TIMED_LIGHT Cette intersection a des feux chronométrés.\\nSélectionnez \"Feux tricolores chronométrés\", cliquer sur l'intersection, puis sur \"Retirer\" d'abord.\nSelect_nodes_windowTitle Sélection des intersections\nSelect_nodes Sélectionnez des intersections\nNode Intersection\nDeselect_all_nodes Désélectionner toutes les intersections\nSetup_timed_traffic_light Créer un chronométrage des feux\nState Etat\nSkip Passer\nup haut\ndown bas\nView Voir\nEdit Modifier\nDelete Suppr.\nTimed_traffic_lights_manager Gestionnaire de Feux tricolores chronométrés\nAdd_step Ajouter un état\nRemove_timed_traffic_light Retirer le chronométrage\nMin._Time: Temps Min:\nMax._Time: Temps Max:\nSave Sauvegarder\nAdd Ajouter\nSensitivity Sensibilité\nVery_Low Très Basse\nLow Basse\nMedium Moyenne\nHigh Haute\nVery_high Très haute\nExtreme_long_green/red_phases Phases rouge/vert extrêmement longues\nVery_long_green/red_phases Phases rouge/vert très longues\nLong_green/red_phases Phases rouge/vert longues\nModerate_green/red_phases Phases rouge/vert moyennement longues\nShort_green/red_phases Phases rouge/vert courtes\nVery_short_green/red_phases Phases rouge/vert très courtes\nExtreme_short_green/red_phases Phases rouge/vert extrêmement courtes\nHide_counters Masquer les compteurs\nShow_counters Afficher les compteurs\nStart Démarrer\nStop Stop\nEnable_test_mode_(stay_in_current_step) Activer le mode test (rester dans l'état actuel)\navg._flow flux moy.\navg._wait attente moy.\nmin/max min/max\nLane Voie\nSet_Speed Régler la vitesse {0}\nMax_speed Vitesse max.\nSegment Segment\nincoming bientôt\nEnable_Advanced_Vehicle_AI Activer l'IA avancée des véhicules\nVehicles_may_enter_blocked_junctions Les véhicules peuvent entrer dans les intersections bloquées\nAll_vehicles_may_ignore_lane_arrows Tous les véhicules peuvent ignorer les directions des voies\nBusses_may_ignore_lane_arrows Les bus peuvent ignorer les directions des voies\nReckless_driving Conduite téméraire (fonctionnalité Bêta)\nTMPE_Title Traffic Manager: Edition Président\nSettings_are_defined_for_each_savegame_separately Paramètres définis pour chaque sauvegarde séparément\nSimulation_accuracy Précision de la simulation (une plus haute précision réduit les performances)\nEnable_highway_specific_lane_merging/splitting_rules Activer les règles spécifiques de divergence/convergence sur les autoroutes\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): Les conducteurs veulent changer de voies: (seulement si l'IA avancée est activée)\nMaintenance Maintenance\nVery_often Très souvent\nOften Souvent\nSometimes Parfois\nRarely Rarement\nVery_rarely Très rarement\nOnly_if_necessary Seulement si nécessaire\nNodes_and_segments Nœuds et segments\nLanes Voies\nPath_Of_Evil_(10_%) Chemin du Mal (10 %)\nRush_Hour_(5_%) Heures de Pointe (5 %)\nMinor_Complaints_(2_%) Plaintes mineures (2 %)\nHoly_City_(0_%) Ville Sainte (0 %)\nForget_toggled_traffic_lights Oublier les feux tricolores retirés\nRoad_is_already_in_a_group! Cette route est déjà dans un groupe !\nAll_selected_roads_must_be_of_the_same_type! Toutes les routes sélectionnées doivent être du même type!\nCreate_group Créer un groupe\nDelete_group Supprimer un groupe\nAdd_zoning Ajouter le zonage\nRemove_zoning Retirer le zonage\nLane_Arrow_Changer_Disabled_Highway Le modificateur de directions pour cette voie est désactivé car vous avez activé le système de règles pour autoroutes.\nAdd_junction_to_timed_light Ajouter une intersection à ce feu tricolore\nRemove_junction_from_timed_light Retirer une intersection de ce feu tricolore\nSelect_junction Sélectionnez une intersection\nCancel Annuler\nSpeed_limits Limitations de vitesse\nPersistently_visible_overlays Annonces en superposition visibles de façon permanente\nPriority_signs Signes de priorité\nVehicles_may_do_u-turns_at_junctions Les véhicules peuvent faire demi-tour aux intersections\nVehicles_going_straight_may_change_lanes_at_junctions Les véhicules allant tout droit peuvent changer de voie aux intersections\nAllow_u-turns Autoriser les demi-tours\nAllow_lane_changing_for_vehicles_going_straight Autoriser le changement de voie pour les véhicules allant tout droit\nAllow_vehicles_to_enter_a_blocked_junction Autoriser les véhicules à entrer dans une jonction bloquée\nRoad_condition_has_a_bigger_impact_on_vehicle_speed La condition de la voirie a une plus grand impact sur la vitesse des véhicules\nVehicle_restrictions Restrictions des véhicules\nCopy Copier\nPaste Coller\nInvert Inverser\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions Appliquer les restrictions de véhicules à tous les segments de route entre deux jonctions\nAllow_all_vehicles Autoriser tous les véhicules\nBan_all_vehicles Bannir tous les véhicules\nSet_all_traffic_lights_to_red Mettre tous les feux au rouge\nRotate_left Rotation vers la gauche\nRotate_right Rotation vers la droite\nName Nommer\nApply Appliquer\nSelect_a_timed_traffic_light_program Sélectionner un programme de chronométrage de feux\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction Le modèle de chronométrage choisi est incompatible avec cette jonction\nAdvanced_AI_cannot_be_activated L'IA avancée ne peut pas être activée\nThe_Advanced_Vehicle_AI_cannot_be_activated L'IA avancée de véhicule ne peut être activée car vous utilisez déjà un autre mod qui modifie modifie le comportement des véhicules (par exemple Improved AI ou Traffic++).\nEnable_dynamic_path_calculation Activer le calcul dynamique des chemins\nLane_Arrow_Changer_Disabled_Connection Le modificateur des directions de voies est désactivé pour cette voie car vous avez manuellement créé des connections de voies.\nLane_connector Connecteur de voies\nConnected_lanes Voies connectées\nUse_alternative_view_mode Utiliser le mode de vue alternatif\nRoad_type Type de route\nDefault_speed_limit Limite de vitesse par défaut\nUnit_system Système d'unités\nMetric Système métrique\nImperial Système impérial\nUse_more_CPU_cores_for_route_calculation_if_available Utiliser plus de cœurs du CPU pour le calcul de trajets (si disponible)\nActivated_features caractéristiques activées\nJunction_restrictions Restrictions de jonction\nProhibit_spawning_of_pocket_cars Prohibit spawning of pocket cars\nReset_stuck_cims_and_vehicles Réinit. citoyens et véhicules coincés\nDefault_speed_limits Limites des vitesse par défaut\nLooking_for_a_parking_spot Cherche une place de stationnement\nDriving_to_a_parking_spot Se dirige vers un stationnement\nDriving_to_another_parking_spot Se dirige vers un autre stationnement\nEntering_vehicle Entre dans un véhicule\nWalking_to_car Se dirige vers une voiture\nUsing_public_transport Utilise les transports en commun\nWalking Marche\nThinking_of_a_good_parking_spot Réfléchi à un bon stationnement\nSwitch_view Changer de vue\nOutgoing_demand Demande sortante\nIncoming_demand Demande entrante\nAdvanced_Vehicle_AI IA avancée des véhicules\nHeavy_trucks_prefer_outer_lanes_on_highways Les véhicules lourds préfèrent les voies extérieures sur les autoroutes\nParking_AI IA de stationnement\nEnable_more_realistic_parking Activer le stationnement plus réaliste\nReset_custom_speed_limits Réinitialiser les limites de vitesse persos\nReload_global_configuration Recharger la config. globale\nReset_global_configuration Réinitialiser la config. globale\nGeneral Général\nGameplay Gameplay\nOverlays Interface\nRealistic_speeds Vitesses réalistes\nEvacuation_busses_may_ignore_traffic_rules Les bus d'évacuation peuvent ignorer les règles de traffic\nEvacuation_busses_may_only_be_used_to_reach_a_shelter Les bus d'évacuation peuvent peut-être n'être utilisés que pour atteindre un abri\nVehicle_behavior Comportement des véhicules\nPolicies_&_Restrictions Restrictions et politiques\nAt_junctions Aux jonctions\nIn_case_of_emergency En cas d'urgence\nShow_lane-wise_speed_limits Voir limites de vitesse par voie\nLanguage Langue\nGame_language Langue du jeu\nrequires_game_restart nécessite le redémarrage du jeu\nCustomizations_come_into_effect_instantaneously Les personnalisations prennent effet instantanément\nOptions Options\nLock_main_menu_button_position Verrouiller la position du bouton du menu principal\nLock_main_menu_position Verrouiller la position du menu principal\nRecalculating_lane_routing Recalcul en cours des routing des voies\nPlease_wait Merci de patienter\nParking_restrictions Restrictions de stationnement\nSimulation Simulation\nOn_roads Sur les routes\nBan_private_cars_and_trucks_on_bus_lanes Interdire les voitures privées et les camions dans les couloirs de bus\ndefault par défaut\nflow_ratio ratio de mouvement\nwait_ratio ratio d'attente\nAfter_min._time_has_elapsed_switch_to_next_step_if Après le temps minimum écoulé, passez à l'étape suivante si\nAdaptive_step_switching Commutation d'étape adaptive\nDynamic_lane_section Sélection de voie dynamique\nPercentage_of_vehicles_performing_dynamic_lane_section Pourcentage de véhicules effectuant une sélection de voie dynamique\nVehicle_restrictions_aggression Restrictions agressives des véhicules\nStrict Stricte\nShow_path-find_stats Afficher le chemin d'acès des stats\nRemove_this_vehicle Retirer ce véhicule\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights Les véhicules suivent les règles de priorité aux carrefours avec des feux de circulations temporiés\nEnable_tutorial_messages Activier les messages du didactitiel\nTMPE_TUTORIAL_HEAD_MainMenu Gestionnaire de traffic : Édition Président\nTMPE_TUTORIAL_BODY_MainMenu Bienvenue à TM:PE !\\n\\nManuel d'utilisateur : http://www.viathinksoft.de/tmpe\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool Restrictions aux carrefours\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool Comment contrôler le comportement des véhicules et des piétons aux carrefours.\\n\\n1. Cliquer sur le carrefour que vous voulez gérer\\n2. Cliquez sur l'icône appropriée pour basculer entre les restrictions.\\n\\nRestrictions disponibles :\\n- Autoriser/Interdire le changement de voie pour que le véhicule continue tout droit\\n- Autoriser/Interdire les demi-tours\\n- Autoriser/Interdire aux véhicules d'entrer dans un carrefour bloqué\\n- Autoriser/Interdire aux pietons de traverser la rue\nTMPE_TUTORIAL_HEAD_LaneArrowTool Flèche des voies de circulation\nTMPE_TUTORIAL_BODY_LaneArrowTool Restreindre l'ensemble des directions que les véhicules sont autorisés à prendre.\\n\\n1. Cliquez sur un segment de route à côté d'une intersection\\n2. Sélectionnez les directions autorisées.\nTMPE_TUTORIAL_HEAD_LaneConnectorTool Connecteur des voies de circulation\nTMPE_TUTORIAL_BODY_LaneConnectorTool Reliez deux ou plusieurs voies entre elles afin de déterminer les voies que les véhicules peuvent utiliser.\\n\\n1. Cliquez sur une intersection (cercles gris).\\n2. Cliquez sur un marqueur de source.\\n3. Cliquez sur un marqueur cible pour le relier au marqueur source.\\n4. Cliquez n'importe où avec votre bouton secondaire de la souris pour revenir à la sélection du marqueur source.\\n\\nTouches de raccourci disponibles :\\n\\n- Supprimer ou Retour arrière : Supprimer toutes les connexions de voie\\n- Shift + S : Parcourez tous les modèles disponibles \"rester sur la voie\"\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool Manuel des feux de circulation\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool Essayez les feux de circulation personnalisés.\\n\\n1. Cliquez sur une intersection\\n2. Utilisez l'outil pour contrôler les feux de circulation.\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool Restrictions de stationnement\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool Contrôler où le stationnement est autorisé.\\n\\nCliquez sur les icônes \"P\".\\n\\nTouches de raccourci disponibles :\\n\\n- Shift : Maintenez la touche enfoncée tout en cliquant pour appliquer des restrictions de stationnement à plusieurs segments de route à la fois\nTMPE_TUTORIAL_HEAD_PrioritySignsTool Signes prioritaires\nTMPE_TUTORIAL_BODY_PrioritySignsTool Définir les règles de priorité aux intersations.\\n\\n1. Cliquer sur l'intersection.\\n2. Cliquez sur les cercles vides pour parcourir les panneaux de priorité disponibles (route prioritaire, cédez le passage, stop, vide).\\n\\nTouches de raccourci disponibles :\\n\\n- Shift : Maintenez la touche Maj enfoncée pour ajouter des signes de priorité à plusieurs segments de route à la fois. Cliquez à nouveau tout en maintenant la touche Maj enfoncée pour parcourir tous les motifs disponibles.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool Limites de vitesse\nTMPE_TUTORIAL_BODY_SpeedLimitsTool Configurer les restrictions de limitation de vitesse.\\n\\n1. Dans la fenêtre, cliquez sur la limite de vitesse que vous souhaitez définir.\\n2. Cliquez sur un segment de route pour changer la limitation de vitesse.\\n\\nTouches de raccourci disponibles :\\n\\n- Shift : Maintenez la touche Maj enfoncée tout en cliquant pour appliquer des limitations de vitesse à plusieurs segments de route à la fois.\\n- Ctrl : Maintenez la touche Ctrl enfoncée pour contrôler les limitations de vitesse par voie individuelle.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool Feux de circulation chronométrés\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool Mettre en place des feux de circulation chronométrés.\\n\\n1. Cliquez sur une intersection.\\n2. Dans la fenêtre, cliquez sur \"Ajouter une étape\".\\n3. Cliquez sur les éléments de superposition pour définir les états de feux de circulation souhaités.\\n4. Cliquez sur \"Ajouter\".\\n5. Répétez comme vous le désiré.\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool Basculer les feux de circulation\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool Ajouter ou supprimer des feux de circulation/aux intersections.\\n\\nCliquez sur une intersection pour basculer les feux de circulation..\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool Restrictions de véhicules\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool Interdire les véhicules sur certaines portion de route.\\n\\n1. Cliquez sur une portion ou segment de route.\\n2. Cliquez sur les icônes pour faire basculer les restrictions.\\n\\nDistinction des types de véhicules :\\n\\n- Véhicules routiers : Voitures de tourisme, autobus, taxis, camions de fret, véhicules de service, véhicules d'urgence\\n- Véhicules ferroviaires : Trains de voyageurs, trains de marchandises\\n\\nTouches de raccourci disponibles :\\n\\n- Shift : Maintenez la touche Maj enfoncée tout en cliquant pour appliquer des restrictions à plusieurs portions ou segments de route à la fois.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults Limites de vitesse par défaut\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. Utilisez les flèches dans la moitié supérieure pour parcourir tous les types de routes.\\n2. Dans la moitié inférieure, sélectionnez une limite de vitesse.\\n3. Cliquez sur \"Enregistrer\" pour définir la limite de vitesse sélectionnée par défaut pour les futurs segments de route du type sélectionné. Cliquez sur \"Enregistrer et appliquer\" pour également mettre à jour toutes les routes existantes du type sélectionné.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep Ajouter une étape chronométrée \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. Pendant le jeu, cliquez sur les feux de circulation pour changer leur état. Utilisez le bouton \"Changer de mode\" pour ajouter des feux de circulation directionnels.\\n2. Entrez à la fois une durée minimale et maximale pour l'étape. Après le temps minimum écoulé, le feu de circulation comptera et comparera les véhicules qui approchent.\\n3. En option, sélectionnez un type de changement d'étape. Par défaut, l'étape change si à peu près moins de véhicules roulent qu'en attente.\\n4. En option, ajustez la sensibilité de la lumière. Par exemple, déplacez le curseur vers la gauche pour rendre le feu de circulation temporisé moins sensible pour les véhicules en attente. \nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy Copier un feu de circulation minuté\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy Cliquez sur un autre carrefour pour coller le feu de circulation chronométré.\\n\\nCliquez n'importe où avec votre bouton secondaire de la souris pour annuler l'opération.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction Ajouter à l'intersection ou au carrefour un feu de circulation minuté \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction Cliquez sur une autre intersection pour l'ajouter. Les deux lumières seront jointes de sorte que le programme chronométré contrôlera ensuite les deux intersections à la fois.\\n\\nCliquez n'importe où avec votre bouton secondaire de la souris pour annuler l'opération.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction Retirer à l'intersection le feu de circulation minuté\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction Cliquez sur l'une des jonctions contrôlées par ce programme temporisé. Le feu de signalisation sélectionné sera supprimé de telle sorte que le programme chronométré ne le gérera plus.\\n\\nCliquez n'importe où avec votre bouton secondaire de la souris pour annuler l'opération.\nPublic_transport Transport public\nPrevent_excessive_transfers_at_public_transport_stations Empêcher les transferts inutiles dans les stations de transport public\nCompact_main_menu Menu principal compact\nWindow_transparency Fenêtre transparente\nOverlay_transparency Superposition de transparence\nRemove_this_citizen Retirer ce citoyen\nShow_error_message_if_a_mod_incompatibility_is_detected Afficher le message d'erreur si une incompatibilité d'un mod est détectée\nRemove_parked_vehicles Enlever les véhicules stationnés\nNode_is_level_crossing Cette intersection est un passage à niveau.\\nous ne pouvez pas désactiver les feux de circulation ici.\nExperimental_features Experimental features\nTurn_on_red Tourner aux feux rouges\nVehicles_may_turn_on_red Les véhicules peuvent tourner aux feux rouges\nAlso_apply_to_left/right_turns_between_one-way_streets Also apply to left & right turns between one-way streets"
  },
  {
    "path": "TLM/TLM/Resources/lang_it.txt",
    "content": "Switch_traffic_lights Attiva/Disattiva semaforo\nAdd_priority_signs Aggiungi segnale stradale\nManual_traffic_lights Semaforo (manuale)\nTimed_traffic_lights Semaforo (temporizzato)\nChange_lane_arrows Modifica frecce di direzione\nClear_Traffic Resetta traffico\nDisable_despawning Disabilita il despawn\nEnable_despawning Abilita il despawn\nNODE_IS_LIGHT In questo incrocio c'è un semaforo.\\nElimina il semaforo scegliendo \"Attiva/Disattiva semaforo\"\nNODE_IS_TIMED_LIGHT In questo incrocio c'è un semaforo temporizzato\\nPrima seleziona \"Semaforo (temporizzato)\", clicca su questo nodo e poi \"Rimuovi\".\nSelect_nodes_windowTitle Seleziona nodi\nSelect_nodes Seleziona nodi\nNode Nodo\nDeselect_all_nodes Deseleziona tutti i nodi\nSetup_timed_traffic_light Configura semaforo temporizzato\nState Stato\nSkip Salta\nup Su\ndown Giù\nView Vedi\nEdit Modifica\nDelete Elimina\nTimed_traffic_lights_manager Gestore semafori temporizzati\nAdd_step Aggiungi stato\nRemove_timed_traffic_light Rimuovi semaforo temporizzato\nMin._Time: Min. Tempo:\nMax._Time: Max. Tempo:\nSave Salva\nAdd Aggiungi\nSensitivity Sensibilità\nVery_Low Molto basso\nLow Basso\nMedium Medio\nHigh Alto\nVery_high Molto alto\nExtreme_long_green/red_phases Passaggio verde/rosso estremamente lungo\nVery_long_green/red_phases Passaggio verde/rosso molto lungo\nLong_green/red_phases Passaggio verde/rosso lungo\nModerate_green/red_phases Passaggio verde/rosso moderato\nShort_green/red_phases Passaggio verde/rosso corto\nVery_short_green/red_phases Passaggio verde/rosso molto corto\nExtreme_short_green/red_phases Passaggio verde/rosso estremamente corto\nHide_counters Nascondi countdown\nShow_counters Mostra countdown\nStart Avvia\nStop Stop\nEnable_test_mode_(stay_in_current_step) Abilita modalità test (rimani nello step corrente)\navg._flow Flusso medio\navg._wait Attesa media\nmin/max min/max\nLane Corsia\nSet_Speed Imposta velocità {0}\nMax_speed Velocità massima\nSegment Segmento\nincoming In arrivo\nEnable_Advanced_Vehicle_AI Abilita IA dei Veicoli Avanzata\nVehicles_may_enter_blocked_junctions I veicoli possono entrare/occupare un incrocio bloccato\nAll_vehicles_may_ignore_lane_arrows Tutti i veicoli possono ignorare le frecce di direzione\nBusses_may_ignore_lane_arrows I bus possono ignorare le frecce di direzione\nReckless_driving Guida spericolata\nTMPE_Title Traffic Manager: President Edition\nSettings_are_defined_for_each_savegame_separately I settaggi possono essere impostati separatamente per ogni salvataggio\nSimulation_accuracy Precisione simulazione (Un'elevata precisione riduce le performance)\nEnable_highway_specific_lane_merging/splitting_rules Abilita regolazione specifica per le corsie autostradali\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): Agli automobilisti piace cambiare corsia (applicata solo se attiva la IA dei Veicoli Avanzata)\nMaintenance Manutenzione\nVery_often Molto di frequente\nOften Di frequente\nSometimes A volte\nRarely Raramente\nVery_rarely Molto raramente\nOnly_if_necessary Solo se necessario\nNodes_and_segments Nodi e segmenti\nLanes Corsie\nPath_Of_Evil_(10_%) Molto elevata (10 %)\nRush_Hour_(5_%) Elevata (5 %)\nMinor_Complaints_(2_%) Modesta (2 %)\nHoly_City_(0_%) Pacifica (0 %)\nForget_toggled_traffic_lights \"Dimentica\" semafori impostati\nRoad_is_already_in_a_group! La strada è già in un gruppo!\nAll_selected_roads_must_be_of_the_same_type! Tutte le strade selezionate devono essere dello stesso tipo!\nCreate_group Crea gruppo\nDelete_group Elimina gruppo\nAdd_zoning Aggiungi zoning\nRemove_zoning Rimuovi zoning\nLane_Arrow_Changer_Disabled_Highway La modifica delle frecce di direzione per questa corsia è disabilitata perchè è attivo il sistema di regole autostradali predefinito. Puoi disabilitarlo nelle opzioni alla voce \"Abilita regolazione specifica per le corsie autostradali\".\nAdd_junction_to_timed_light Aggiungi un incrocio a questo semaforo\nRemove_junction_from_timed_light Rimuovi un incrocio a questo semaforo\nSelect_junction Seleziona un incrocio\nCancel Annulla\nSpeed_limits Limiti di velocità\nPersistently_visible_overlays Overlays visibili persistentemente\nPriority_signs Segnali stradali\nVehicles_may_do_u-turns_at_junctions I veicoli possono effettuare una svolta a \"U\" all'incrocio\nVehicles_going_straight_may_change_lanes_at_junctions I veicoli che proseguono dritto possono cambiare corsia all'incrocio\nAllow_u-turns Permetti le svolte a \"U\"\nAllow_lane_changing_for_vehicles_going_straight Permetti il cambio corsia per i veicoli che proseguono dritto all'incrocio\nAllow_vehicles_to_enter_a_blocked_junction Permetti ai veicoli di entrare/occupare un incrocio bloccato/intasato\nRoad_condition_has_a_bigger_impact_on_vehicle_speed La condizione della strada ha un grosso impatto sulla velocità dei veicoli\nVehicle_restrictions Restrizioni veicoli\nCopy Copia\nPaste Incolla\nInvert Inverti\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions Applica le restrizioni al tratto di strada compreso tra due incroci\nAllow_all_vehicles Permetti l'accesso a tutti i veicoli\nBan_all_vehicles Banna tutti i veicoli\nSet_all_traffic_lights_to_red Imposta tutti i semafori sul Rosso\nRotate_left Ruota a sinistra\nRotate_right Ruota a destra\nName Nome\nApply Applica\nSelect_a_timed_traffic_light_program Seleziona pattern per il semaforo temporizzato\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction Il pattern scelto è incompatibile con questo incrocio\nAdvanced_AI_cannot_be_activated La IA Avanzata non può essere attivata\nThe_Advanced_Vehicle_AI_cannot_be_activated La IA Avanzata non può essere attivata perchè stai usando un'altra mod che modifica il comportamento dei veicoli (es. Improved AI o Traffic++).\nEnable_dynamic_path_calculation Abilita calcolo del percorso dinamico\nLane_Arrow_Changer_Disabled_Connection La modifica delle frecce di direzione per questa corsia è disabilitata perchè hai creato la connessione della corsia a mano.\nLane_connector Connettore corsia\nConnected_lanes Corsie connesse\nUse_alternative_view_mode Utilizza modilità vista alternativa\nRoad_type Tipo di strada\nDefault_speed_limit Limite velocità di default\nUnit_system Unità di misura\nMetric Metrico\nImperial Imperiale\nUse_more_CPU_cores_for_route_calculation_if_available Utilizza più core della CPU per il calcolo del percorso (se disponile) \nActivated_features Features attivate\nJunction_restrictions Restrizioni incrocio\nProhibit_spawning_of_pocket_cars Vieta ai \"cims\" di spawnare \"pocket cars\"\nReset_stuck_cims_and_vehicles Resetta cims e veicoli bloccati\nDefault_speed_limits Limiti di velocità di default\nLooking_for_a_parking_spot In cerca di un parcheggio\nDriving_to_a_parking_spot Guidando verso un parcheggio\nDriving_to_another_parking_spot Guidando verso un altro parcheggio\nEntering_vehicle Entrando nel veicolo\nWalking_to_car Camminando verso l'auto\nUsing_public_transport Utilizzando il trasporto pubblico\nWalking Camminando\nThinking_of_a_good_parking_spot Pensando ad un buon parcheggio\nSwitch_view Cambia vista\nOutgoing_demand Outgoing demand\nIncoming_demand Incoming demand\nAdvanced_Vehicle_AI IA Avanzata\nHeavy_trucks_prefer_outer_lanes_on_highways I veicoli pesanti preferiscono corsie esterne sulle autostrade\nParking_AI Parking AI\nEnable_more_realistic_parking Abilita utilizzo più realistico dei parcheggi\nReset_custom_speed_limits Resetta i limiti di velocita personalizzati\nReload_global_configuration Ricarica configurazione globale\nReset_global_configuration Resetta configurazione globale\nGeneral Generale\nGameplay Gameplay\nOverlays Overlays\nRealistic_speeds Velocità realistica dei veicoli\nEvacuation_busses_may_ignore_traffic_rules I bus di evacuazione possono ignorare le regole stradali\nEvacuation_busses_may_only_be_used_to_reach_a_shelter I bus di evacuazione possono essere usati solo per raggiungere un rifugio\nVehicle_behavior Comportamente del veicolo\nPolicies_&_Restrictions Policies & Restrizioni\nAt_junctions Agli incroci\nIn_case_of_emergency In caso di emergenza\nShow_lane-wise_speed_limits Mostra limite velocità per ogni corsia\nLanguage Lingua\nGame_language Lingua di gioco\nrequires_game_restart Richiede il riavvio del gioco\nCustomizations_come_into_effect_instantaneously Le modifiche avranno effetto immediatamente\nOptions Opzioni\nLock_main_menu_button_position Blocca posizione del bottone (menu)\nLock_main_menu_position Blocca posizione del menu\nRecalculating_lane_routing Ricalcolo percorso corsia\nPlease_wait Attendere prego\nParking_restrictions Divieti di sosta\nSimulation Simulazione\nOn_roads Su strade\nBan_private_cars_and_trucks_on_bus_lanes Vieta alle auto e ai camion di utilizzare le corsie riservate ai bus\ndefault predefinito\nflow_ratio Indice flusso\nwait_ratio Indice attesa\nAfter_min._time_has_elapsed_switch_to_next_step_if Dopo min. tempo trascorso, passare al passo successivo se\nAdaptive_step_switching Azionamento adattivo prossimo stato\nDynamic_lane_section Selezione corsia dinamica \nPercentage_of_vehicles_performing_dynamic_lane_section Percentuale di veicoli che effettuano la selezione dinamica della corsia\nVehicle_restrictions_aggression Rigorosità restrizioni veicoli\nStrict Rigoroso\nShow_path-find_stats Mostra statistiche path-find \nRemove_this_vehicle Rimuovi questo veicolo\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights I veicoli seguono le regole di precedenza agli incroci con semafori a tempo\nEnable_tutorial_messages Enable tutorial messages\nTMPE_TUTORIAL_HEAD_MainMenu Traffic Manager: President Edition\nTMPE_TUTORIAL_BODY_MainMenu Welcome to TM:PE!\\n\\nUser manual: http://www.viathinksoft.de/tmpe\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool Junction restrictions\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool Control how vehicles and pedestrians shall behave at junctions.\\n\\n1. Click on the junction you want to manage\\n2. Click on the appropriate icon to toggle restrictions.\\n\\nAvailable restrictions:\\n- Allow/Disallow lane changing for vehicle going straight on\\n- Allow/Disallow u-turns\\n- Allow/Disallow vehicles to enter a blocked junction\\n- Allow/disallow pedestrians to cross the street\nTMPE_TUTORIAL_HEAD_LaneArrowTool Lane arrows\nTMPE_TUTORIAL_BODY_LaneArrowTool Restrict the set of directions that vehicles are allowed to take.\\n\\n1. Click on a road segment next to a junction\\n2. Select the allowed directions.\nTMPE_TUTORIAL_HEAD_LaneConnectorTool Lane connector\nTMPE_TUTORIAL_BODY_LaneConnectorTool Connect two or more lanes with each other in order to tell which lanes vehicles may use.\\n\\n1. Click on a lane changing point (grey circles).\\n2. Click on a source marker.\\n3. Click on a target marker to connect it with the source marker.\\n4. Click anywhere with your secondary mouse button to return back to source marker selection.\\n\\nAvailable hotkeys:\\n\\n- Delete or Backspace: Remove all lane connections\\n- Shift + S: Cycle through all available \"stay on lane\" patterns\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool Manual traffic lights\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool Try out custom traffic lights.\\n\\n1. Click on a junction\\n2. Use the tool to control traffic lights.\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool Parking restrictions\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool Control where parking is allowed.\\n\\nClick on the \"P\" icons.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold while clicking to apply parking restrictions to multiple road segments at once\nTMPE_TUTORIAL_HEAD_PrioritySignsTool Priority signs\nTMPE_TUTORIAL_BODY_PrioritySignsTool Define priority rules at junctions.\\n\\n1. Click on a junction.\\n2. Click on the blank spots to cycle through the available priority signs (priority road, yield, stop).\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift to add priority signs to multiple road segments at once. Click again while holding Shift to cycle through all available patterns.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool Speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool Set up speed restrictions.\\n\\n1. In the window, click on the speed limit you want to set.\\n2. Click on a road segment to change the speed limit.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply speed limits to multiple road segments at once.\\n- Ctrl: Hold Ctrl to control speed limits per individual lane.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool Timed traffic lights\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool Set up timed traffic lights.\\n\\n1. Click on a junction.\\n2. In the window, click on \"Add step\".\\n3. Click on the overlay elements to set desired traffic lights states.\\n4. Click on \"Add\".\\n5. Repeat as desired.\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool Toggle traffic lights\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool Add or remove traffic lights to/from junctions.\\n\\nClick on a junction to toggle traffic lights.\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool Vehicle restrictions\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool Ban vehicles from certain road segments.\\n\\n1. Click on a road segment.\\n2. Click on the icons to toggle restrictions.\\n\\nDistinguished vehicle types:\\n\\n- Road vehicles: Passenger cars, busses, taxis, cargo trucks, service vehicles, emergency vehicles\\n- Rail vehicles: Passenger trains, cargo trains\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply restrictions to multiple road segments at once.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults Default speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. Use the arrows in the upper half to cycle through all road types.\\n2. In the lower half, select a speed limit.\\n3. Click on \"Save\" to set the selected speed limit as default for future road segments of the selected type. Click on \"Save & Apply\" to also update all existing roads of the selected type.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep Add a timed step \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. Within the in-game overlay, click on the traffic lights to change their state. Use the \"Change mode\" button to add directional traffic lights.\\n2. Enter both a minimum and maximum duration for the step. After the min. time has elapsed the traffic light will count and compare approaching vehicles.\\n3. Optionally, select a step switching type. Per default, the step changes if roughly less vehicles are driving than waiting.\\n4. Optionally, adjust the light's sensitivity. For example, move the slider to the left to make the timed traffic light less sensitive for waiting vehicles. \nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy Copy a timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy Click on another junction to paste the timed traffic light.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction Add a junction to the timed traffic light \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction Click on another junction to add it. Both lights will be joined such that the timed program will then control both junctions at once.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction Remove a junction from the timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction Click on one of the junctions that are controlled by this timed program. The selected traffic light will be removed such that the timed programm will no longer manage it.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nPublic_transport Public transport\nPrevent_excessive_transfers_at_public_transport_stations Prevent unnecessary transfers at public transport stations\nCompact_main_menu Compact main menu\nWindow_transparency Window transparency\nOverlay_transparency Overlay transparency\nRemove_this_citizen Remove this citizen\nShow_error_message_if_a_mod_incompatibility_is_detected Show error message if a mod incompatibility is detected\nRemove_parked_vehicles Remove parked vehicles\nNode_is_level_crossing This junction is a level crossing.\\nYou cannot disable traffic lights here.\nExperimental_features Experimental features\nTurn_on_red Girare ai semafori rossi\nVehicles_may_turn_on_red I veicoli possono girare ai semafori rossi\nAlso_apply_to_left/right_turns_between_one-way_streets Also apply to left & right turns between one-way streets"
  },
  {
    "path": "TLM/TLM/Resources/lang_ja.txt",
    "content": "Switch_traffic_lights 信号の付け外し\nAdd_priority_signs 優先関係標識の追加\nManual_traffic_lights 信号の手動切替\nTimed_traffic_lights 時間設定付き信号\nChange_lane_arrows 車線矢印の変更\nClear_Traffic 移動中車両の除去\nDisable_despawning スタック除去無効化\nEnable_despawning スタック除去有効化\nNODE_IS_LIGHT この交差点には信号があります。\\n信号を削除するには「信号の付け外し」を選んでこの交差点をクリックしてください。\nNODE_IS_TIMED_LIGHT この交差点には時間設定付き信号があります。\\nまず「時間設定付き信号」を選んでこの交差点をクリックして「削除」をクリックしてください。\nSelect_nodes_windowTitle 交差点の選択\nSelect_nodes 交差点を選択してください\nNode 交差点\nDeselect_all_nodes 全交差点の選択を解除\nSetup_timed_traffic_light 時間設定付き信号の設定\nState 状態\nSkip 次へ\nup 上へ\ndown 下へ\nView 表示\nEdit 編集\nDelete 削除\nTimed_traffic_lights_manager 時間設定付き信号の管理\nAdd_step 状態の追加\nRemove_timed_traffic_light 時間設定付き信号の削除\nMin._Time: 最短時間:\nMax._Time: 最長時間:\nSave 保存\nAdd 追加\nSensitivity 感度\nVery_Low とても低い\nLow 低い\nMedium 中くらい\nHigh 高い\nVery_high とても高い\nExtreme_long_green/red_phases 混雑してきてもほとんど切り替えない\nVery_long_green/red_phases 混雑してきてもあまり切り替えない\nLong_green/red_phases 混雑具合によってそこそこに切り替える\nModerate_green/red_phases 混雑具合によって適度に切り替える\nShort_green/red_phases 混雑具合によって細かく切り替える\nVery_short_green/red_phases 混雑具合によってとても細かく切り替える\nExtreme_short_green/red_phases 混雑具合によって非常に細かく切り替える\nHide_counters カウンターの非表示\nShow_counters カウンターの表示\nStart 開始\nStop 停止\nEnable_test_mode_(stay_in_current_step) テストモード有効 (現在の状態に留まる)\navg._flow 平均流量\navg._wait 平均待ち\nmin/max 最短/最長\nLane 車線\nSet_Speed 速度{0}に設定\nMax_speed 最大速度\nSegment 道路\nincoming 台流入\nEnable_Advanced_Vehicle_AI Advanced Vehicle AIを有効にする\nVehicles_may_enter_blocked_junctions 交差点内での停車を許可\nAll_vehicles_may_ignore_lane_arrows 全車両に対して車線矢印の無視を許可\nBusses_may_ignore_lane_arrows バスに対して車線矢印の無視を許可\nReckless_driving 無謀運転 (ベータ機能/いくらかの市民が交通ルールを無視します)\nTMPE_Title Traffic Manager: President Edition\nSettings_are_defined_for_each_savegame_separately 設定は各セーブデータ毎に保存されます\nSimulation_accuracy シミュレーションの正確さ (高いとパフォーマンスが低下します)\nEnable_highway_specific_lane_merging/splitting_rules 高速道路特有の車線合流/分離ルールを適用する\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): 車線変更の頻度 (Advanced AI有効時のみ適用されます)\nMaintenance メンテナンス\nVery_often 非常に頻繁\nOften 頻繁\nSometimes 時々\nRarely まれ\nVery_rarely 非常にまれ\nOnly_if_necessary 必要時のみ\nNodes_and_segments 交差点と道路の表示\nLanes 車線\nPath_Of_Evil_(10_%) 悪路 (10 %)\nRush_Hour_(5_%) ラッシュアワー (5 %)\nMinor_Complaints_(2_%) ほとんど不満なし (2 %)\nHoly_City_(0_%) 天国 (0 %)\nForget_toggled_traffic_lights 付け外しした信号を元に戻す\nRoad_is_already_in_a_group! この道路は既にグループ内にあります！\nAll_selected_roads_must_be_of_the_same_type! 選択する道路は全て同じタイプである必要があります！\nCreate_group グループの作成\nDelete_group グループの削除\nAdd_zoning 区間の追加\nRemove_zoning 区間の削除\nLane_Arrow_Changer_Disabled_Highway 高速道路ルールシステムを適用しているため、この車線の矢印は変更できません\nAdd_junction_to_timed_light この信号に交差点を追加\nRemove_junction_from_timed_light この信号から交差点を削除 \nSelect_junction 交差点を選択してください \nCancel キャンセル \nSpeed_limits 速度制限\nPersistently_visible_overlays オーバーレイ表示し続ける情報\nPriority_signs 優先関係標識\nVehicles_may_do_u-turns_at_junctions 交差点でのUターンを許可\nVehicles_going_straight_may_change_lanes_at_junctions 直進車両の交差点での車線変更を許可\nAllow_u-turns Uターンを許可\nAllow_lane_changing_for_vehicles_going_straight 直進車両の車線変更を許可\nAllow_vehicles_to_enter_a_blocked_junction 交差点内での停車を許可\nRoad_condition_has_a_bigger_impact_on_vehicle_speed 路面状況がより大きな影響を車両速度に与える\nVehicle_restrictions 車両規制\nCopy コピー\nPaste 貼り付け\nInvert 反転\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions 交差点間の全道路に車両規制を適用\nAllow_all_vehicles 全車両を許可\nBan_all_vehicles 全車両を禁止\nSet_all_traffic_lights_to_red 全信号を赤に設定\nRotate_left 左に回転\nRotate_right 右に回転\nName 名前\nApply 適用\nSelect_a_timed_traffic_light_program 時間設定付き信号のプログラムを選択してください\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction 選択された信号パターンはこの交差点と互換性がありません\nAdvanced_AI_cannot_be_activated Advanced Vehicle AIは有効にできません\nThe_Advanced_Vehicle_AI_cannot_be_activated Advanced Vehicle AIは、車両の振る舞いを変える他のMOD(例えばImproved AIやTraffic++)を既に使っているため、有効にできません.\nEnable_dynamic_path_calculation Enable dynamic path calculation\nLane_Arrow_Changer_Disabled_Connection The lane arrow changer for this lane is disabled because you have created lane connections by hand.\nLane_connector Lane connector\nConnected_lanes Connected lanes\nUse_alternative_view_mode Use alternative view mode\nRoad_type Road type\nDefault_speed_limit Default speed limit\nUnit_system Unit system\nMetric Metric\nImperial Imperial\nUse_more_CPU_cores_for_route_calculation_if_available Use more CPU cores for route calculation (if available)\nActivated_features Activated features\nJunction_restrictions Junction restrictions\nProhibit_spawning_of_pocket_cars Prohibit spawning of pocket cars\nReset_stuck_cims_and_vehicles Reset stuck cims and vehicles\nDefault_speed_limits Default speed limits\nLooking_for_a_parking_spot Looking for a parking spot\nDriving_to_a_parking_spot Driving to a parking spot\nDriving_to_another_parking_spot Driving to another parking spot\nEntering_vehicle Entering vehicle\nWalking_to_car Walking to car\nUsing_public_transport Using public transport\nWalking Walking\nThinking_of_a_good_parking_spot Thinking of a good parking spot\nSwitch_view Switch view\nOutgoing_demand Outgoing demand\nIncoming_demand Incoming demand\nAdvanced_Vehicle_AI Advanced Vehicle AI\nHeavy_trucks_prefer_outer_lanes_on_highways Heavy vehicles prefer outer lanes on highways\nParking_AI Parking AI\nEnable_more_realistic_parking Enable more realistic parking\nReset_custom_speed_limits Reset custom speed limits\nReload_global_configuration Reload global configuration\nReset_global_configuration Reset global configuration\nGeneral General\nGameplay Gameplay\nOverlays Overlays\nRealistic_speeds Realistic speeds\nEvacuation_busses_may_ignore_traffic_rules Evacuation busses may ignore traffic rules\nEvacuation_busses_may_only_be_used_to_reach_a_shelter Evacuation busses may only be used to reach a shelter\nVehicle_behavior Vehicle behavior\nPolicies_&_Restrictions Policies & Restrictions\nAt_junctions At junctions\nIn_case_of_emergency In case of emergency\nShow_lane-wise_speed_limits Show lane-wise speed limits\nLanguage Language\nGame_language Game language\nrequires_game_restart requires game restart\nCustomizations_come_into_effect_instantaneously Customizations come into effect instantaneously\nOptions Options\nLock_main_menu_button_position Lock main menu button position\nLock_main_menu_position Lock main menu position\nRecalculating_lane_routing Recalculating lane routing\nPlease_wait Please wait\nParking_restrictions Parking restrictions\nSimulation Simulation\nOn_roads On roads\nBan_private_cars_and_trucks_on_bus_lanes Ban private cars and trucks on bus lanes\ndefault default\nflow_ratio flow ratio\nwait_ratio wait ratio\nAfter_min._time_has_elapsed_switch_to_next_step_if After min. time has elapsed, switch to next step if\nAdaptive_step_switching Adaptive step switching\nDynamic_lane_section Dynamic lane selection\nPercentage_of_vehicles_performing_dynamic_lane_section Percentage of vehicles performing dynamic lane selection\nVehicle_restrictions_aggression Vehicle restrictions aggression\nStrict Strict\nShow_path-find_stats Show path-find stats\nRemove_this_vehicle Remove this vehicle\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights Vehicles follow priority rules at junctions with timed traffic lights\nEnable_tutorial_messages Enable tutorial messages\nTMPE_TUTORIAL_HEAD_MainMenu Traffic Manager: President Edition\nTMPE_TUTORIAL_BODY_MainMenu Welcome to TM:PE!\\n\\nUser manual: http://www.viathinksoft.de/tmpe\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool Junction restrictions\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool Control how vehicles and pedestrians shall behave at junctions.\\n\\n1. Click on the junction you want to manage\\n2. Click on the appropriate icon to toggle restrictions.\\n\\nAvailable restrictions:\\n- Allow/Disallow lane changing for vehicle going straight on\\n- Allow/Disallow u-turns\\n- Allow/Disallow vehicles to enter a blocked junction\\n- Allow/disallow pedestrians to cross the street\nTMPE_TUTORIAL_HEAD_LaneArrowTool Lane arrows\nTMPE_TUTORIAL_BODY_LaneArrowTool Restrict the set of directions that vehicles are allowed to take.\\n\\n1. Click on a road segment next to a junction\\n2. Select the allowed directions.\nTMPE_TUTORIAL_HEAD_LaneConnectorTool Lane connector\nTMPE_TUTORIAL_BODY_LaneConnectorTool Connect two or more lanes with each other in order to tell which lanes vehicles may use.\\n\\n1. Click on a lane changing point (grey circles).\\n2. Click on a source marker.\\n3. Click on a target marker to connect it with the source marker.\\n4. Click anywhere with your secondary mouse button to return back to source marker selection.\\n\\nAvailable hotkeys:\\n\\n- Delete or Backspace: Remove all lane connections\\n- Shift + S: Cycle through all available \"stay on lane\" patterns\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool Manual traffic lights\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool Try out custom traffic lights.\\n\\n1. Click on a junction\\n2. Use the tool to control traffic lights.\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool Parking restrictions\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool Control where parking is allowed.\\n\\nClick on the \"P\" icons.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold while clicking to apply parking restrictions to multiple road segments at once\nTMPE_TUTORIAL_HEAD_PrioritySignsTool Priority signs\nTMPE_TUTORIAL_BODY_PrioritySignsTool Define priority rules at junctions.\\n\\n1. Click on a junction.\\n2. Click on the blank spots to cycle through the available priority signs (priority road, yield, stop).\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift to add priority signs to multiple road segments at once. Click again while holding Shift to cycle through all available patterns.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool Speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool Set up speed restrictions.\\n\\n1. In the window, click on the speed limit you want to set.\\n2. Click on a road segment to change the speed limit.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply speed limits to multiple road segments at once.\\n- Ctrl: Hold Ctrl to control speed limits per individual lane.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool Timed traffic lights\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool Set up timed traffic lights.\\n\\n1. Click on a junction.\\n2. In the window, click on \"Add step\".\\n3. Click on the overlay elements to set desired traffic lights states.\\n4. Click on \"Add\".\\n5. Repeat as desired.\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool Toggle traffic lights\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool Add or remove traffic lights to/from junctions.\\n\\nClick on a junction to toggle traffic lights.\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool Vehicle restrictions\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool Ban vehicles from certain road segments.\\n\\n1. Click on a road segment.\\n2. Click on the icons to toggle restrictions.\\n\\nDistinguished vehicle types:\\n\\n- Road vehicles: Passenger cars, busses, taxis, cargo trucks, service vehicles, emergency vehicles\\n- Rail vehicles: Passenger trains, cargo trains\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply restrictions to multiple road segments at once.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults Default speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. Use the arrows in the upper half to cycle through all road types.\\n2. In the lower half, select a speed limit.\\n3. Click on \"Save\" to set the selected speed limit as default for future road segments of the selected type. Click on \"Save & Apply\" to also update all existing roads of the selected type.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep Add a timed step \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. Within the in-game overlay, click on the traffic lights to change their state. Use the \"Change mode\" button to add directional traffic lights.\\n2. Enter both a minimum and maximum duration for the step. After the min. time has elapsed the traffic light will count and compare approaching vehicles.\\n3. Optionally, select a step switching type. Per default, the step changes if roughly less vehicles are driving than waiting.\\n4. Optionally, adjust the light's sensitivity. For example, move the slider to the left to make the timed traffic light less sensitive for waiting vehicles. \nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy Copy a timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy Click on another junction to paste the timed traffic light.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction Add a junction to the timed traffic light \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction Click on another junction to add it. Both lights will be joined such that the timed program will then control both junctions at once.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction Remove a junction from the timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction Click on one of the junctions that are controlled by this timed program. The selected traffic light will be removed such that the timed programm will no longer manage it.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nPublic_transport Public transport\nPrevent_excessive_transfers_at_public_transport_stations Prevent unnecessary transfers at public transport stations\nCompact_main_menu Compact main menu\nWindow_transparency Window transparency\nOverlay_transparency Overlay transparency\nRemove_this_citizen Remove this citizen\nShow_error_message_if_a_mod_incompatibility_is_detected Show error message if a mod incompatibility is detected\nRemove_parked_vehicles Remove parked vehicles\nNode_is_level_crossing This junction is a level crossing.\\nYou cannot disable traffic lights here.\nExperimental_features Experimental features\nTurn_on_red Turn on red\nVehicles_may_turn_on_red 車両が赤信号で曲がることがある\nAlso_apply_to_left/right_turns_between_one-way_streets Also apply to left & right turns between one-way streets"
  },
  {
    "path": "TLM/TLM/Resources/lang_ko.txt",
    "content": "Switch_traffic_lights 신호등 추가/제거\nAdd_priority_signs 교통 표지판 추가\nManual_traffic_lights 신호등 직접 통제\nTimed_traffic_lights 신호등 시간표 설정\nChange_lane_arrows 도로 화살표 변경\nClear_Traffic 모든 교통차량 초기화\nDisable_despawning 차 사라짐 비활성화\nEnable_despawning 차 사라짐 활성화\nNODE_IS_LIGHT 교차로에 신호등을 생성합니다.\\n'신호등 추가/제거'를 통해 이 도로 교차로에 신호등을 추가하거나 제거 할 수 있습니다.\nNODE_IS_TIMED_LIGHT 이미 신호등 시간표가 설정되어 있습니다.\\n'신호등 시간표 설정'에서 '제거'를 통해 삭제해야지만 가능합니다.\nSelect_nodes_windowTitle 선택된 교차로 목록\nSelect_nodes 교차로를 선택하세요\nNode 도로\nDeselect_all_nodes 모든 교차로 선택 취소\nSetup_timed_traffic_light 신호등 시간 설정\nState 상태\nSkip 스킵\nup 위로\ndown 아래로\nView 보기\nEdit 수정\nDelete 삭제\nTimed_traffic_lights_manager 신호등 시간표 관리자\nAdd_step 단계 추가\nRemove_timed_traffic_light 설정된 신호등 시간표 제거\nMin._Time: 최소 시간 :\nMax._Time: 최대 시간 :\nSave 저장\nAdd 추가\nSensitivity 신호등 주기 범위\nVery_Low 매우 낮음\nLow 낮음\nMedium 중간\nHigh 높음\nVery_high 매우 높음\nExtreme_long_green/red_phases 극도로 긴 신호등 주기 변화\nVery_long_green/red_phases 매우 긴 신호등 주기 변화\nLong_green/red_phases 긴 신호등 주기 변화\nModerate_green/red_phases 평범한 신호등주기 변화\nShort_green/red_phases 짧은 신호등주기 변화\nVery_short_green/red_phases 매우 짧은 신호등주기 변화\nExtreme_short_green/red_phases 극도로 짧은 신호등주기 변화\nHide_counters 카운터 숨기기\nShow_counters 카운터 보이기\nStart 시작\nStop 중지\nEnable_test_mode_(stay_in_current_step) 테스트 모드 활성화 (현 상태를 유지한 채 진행)\navg._flow 평균 흐름\navg._wait 평균 대기\nmin/max 최소/최대\nLane 차선\nSet_Speed 설정된 속도 {0}\nMax_speed 최고 속도\nSegment 세그먼트(구간)\nincoming 들어오는\nEnable_Advanced_Vehicle_AI 발전된 차량 AI 활성화\nVehicles_may_enter_blocked_junctions 차량이 진입 금지도로에 들어갈 수 있습니다.\nAll_vehicles_may_ignore_lane_arrows 모든 차량이 도로 화살표를 무시 할 수 있습니다.\nBusses_may_ignore_lane_arrows 버스가 도로 화살표를 무시 할 수 있습니다.\nReckless_driving 난폭 운전 (베타 기능)\nTMPE_Title 트래픽 매니저 : 대통령 에디션\nSettings_are_defined_for_each_savegame_separately 설정은 각 저장된 게임마다 별개로 작동합니다.\nSimulation_accuracy 시뮬레이션 정확도 (높을수록 더 많은 연산을 요구합니다)\nEnable_highway_specific_lane_merging/splitting_rules 고속도로 특정도로 병합/분할 기능 활성화\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): 운전자가 차선을 변경합니다 (발전된 차량 AI를 사용 중일 때만 적용됩니다)\nMaintenance 유지보수 (추가정보)\nVery_often 매우 자주\nOften 자주\nSometimes 가끔\nRarely 드물게\nVery_rarely 매우 드물게\nOnly_if_necessary 필요한 경우에만\nNodes_and_segments 도로와 세그먼트(노드)\nLanes 차선\nPath_Of_Evil_(10_%) 악마의 도로 (10 %)\nRush_Hour_(5_%) 출퇴근정도의 혼잡  (5 %)\nMinor_Complaints_(2_%) 사소한 불평들 (2 %)\nHoly_City_(0_%) 평온한 도시 (0 %)\nForget_toggled_traffic_lights 선택된 신호등 잊기\nRoad_is_already_in_a_group! 도로에 이미 완성된 그룹이 있습니다!\nAll_selected_roads_must_be_of_the_same_type! 같은 유형의 도로가 선택되어 있습니다!\nCreate_group 그룹 생성\nDelete_group 그룹 제거\nAdd_zoning 지역설정 추가\nRemove_zoning 지역설정 제거\nLane_Arrow_Changer_Disabled_Highway 고속도로 시스템 규칙으로 인해 이 도로 화살표는 변경 할 수 없습니다(발전된 차량 AI기능 참고)\nAdd_junction_to_timed_light 교차로 신호등 추가\nRemove_junction_from_timed_light 교차로 신호등 제거\nSelect_junction 교차로 선택\nCancel 취소\nSpeed_limits 속도 제한\nPersistently_visible_overlays 화면에 출력할 코드 선택\nPriority_signs 교통 표지판\nVehicles_may_do_u-turns_at_junctions 교차로에서 차량이 유턴 할 수 있습니다.\nVehicles_going_straight_may_change_lanes_at_junctions 교차로에서 차량이 차선을 변경 할 수 있습니다.\nAllow_u-turns 유턴 허가\nAllow_lane_changing_for_vehicles_going_straight 직진하는 차량의 차선 변경을 허가\nAllow_vehicles_to_enter_a_blocked_junction 차량 출입금지 도로에 차량 출입 허가\nRoad_condition_has_a_bigger_impact_on_vehicle_speed 도로상태가 차량속도에 크게 영향을 미칩니다\nVehicle_restrictions 차량 제한\nCopy 복사\nPaste 붙이기\nInvert 반전\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions 두 교차로 구간에 동일한 차량 제한 적용\nAllow_all_vehicles 모든 차량 허가\nBan_all_vehicles 모든 차량 금지\nSet_all_traffic_lights_to_red 모든 신호등을 빨간불로 설정\nRotate_left 반시계방향 신호회전\nRotate_right 시계방향 신호회전\nName 이름\nApply 적용\nSelect_a_timed_traffic_light_program 선택된 신호등 프로그램\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction 선택한 교통 신호규칙이 이 교차로와 맞지 않습니다\nAdvanced_AI_cannot_be_activated 발전된 AI 활성화 불가\nThe_Advanced_Vehicle_AI_cannot_be_activated 이미 다른 모드로 차량 AI 향상을 사용 중이므로 발전된 차량 AI를 사용 할 수 없습니다.(예 : 향상된 AI, 트래픽 C++)\nEnable_dynamic_path_calculation 동적인 경로 계산 활성화\nLane_Arrow_Changer_Disabled_Connection 차선 연결로 이미 수정된 교차로이므로 차선변경을 할 수 없습니다.\nLane_connector 차선 연결\nConnected_lanes 연결된 차선\nUse_alternative_view_mode 대체보기 모드 사용\nRoad_type 도로 유형\nDefault_speed_limit 기본 속도 제한\nUnit_system 유닛 시스템\nMetric 미터법\nImperial 마일법\nUse_more_CPU_cores_for_route_calculation_if_available 경로 계산을 위한 더 많은 CPU코어 사용(가능한 경우)\nActivated_features 기능 활성화\nJunction_restrictions 교차로 규칙\nProhibit_spawning_of_pocket_cars 시민들의 포켓차량 소환 금지\nReset_stuck_cims_and_vehicles 갇혀있는 시민과 차량 초기화\nDefault_speed_limits 기본 속도 제한\nLooking_for_a_parking_spot 주차공간 찾는 중\nDriving_to_a_parking_spot 해당 주차공간으로 운전 중\nDriving_to_another_parking_spot 다른 주차공간으로 운전 중\nEntering_vehicle 차량 탑승 중\nWalking_to_car 차 있던 곳으로 가는 중\nUsing_public_transport 대중교통 이용 중\nWalking 걷는 중\nThinking_of_a_good_parking_spot 주차하기에 좋은 장소를 생각 중\nSwitch_view 보기 전환\nOutgoing_demand 나가는 수요\nIncoming_demand 들어오는 수요\nAdvanced_Vehicle_AI 발전된 차량 AI\nHeavy_trucks_prefer_outer_lanes_on_highways 무거운(화물)차량은 고속도로 바깥 차선을 선호\nParking_AI 주차 AI\nEnable_more_realistic_parking 좀 더 현실적인 주차 활성화\nReset_custom_speed_limits 커스텀 속도 초기화\nReload_global_configuration 글로벌(global) 설정 불러오기\nReset_global_configuration 글로벌(global) 설정 초기화\nGeneral 기본\nGameplay 게임플레이\nOverlays 화면표시\nRealistic_speeds 현실적인 속도\nEvacuation_busses_may_ignore_traffic_rules 대피 버스는 교통규칙을 무시합니다\nEvacuation_busses_may_only_be_used_to_reach_a_shelter 대피 버스는 대피소 도착을 위한 용도로만 사용할 수 있습니다\nVehicle_behavior 차량 제한\nPolicies_&_Restrictions 정책 및 제한\nAt_junctions 교차로 설정\nIn_case_of_emergency 긴급 상황 설정\nShow_lane-wise_speed_limits 차선 속도제한 보기\nLanguage 언어\nGame_language 게임 언어\nrequires_game_restart 게임 재시작 후 적용됩니다\nCustomizations_come_into_effect_instantaneously 커스텀 기능이 즉시 적용됩니다\nOptions 설정\nLock_main_menu_button_position TMPE 모드버튼 위치 잠금\nLock_main_menu_position TMPE 모드매뉴 위치 잠금\nRecalculating_lane_routing 모든 경로 재 계산중\nPlease_wait 잠시만 기다려주십시오\nParking_restrictions 주차 제한\nSimulation 시뮬레이션\nOn_roads 도로 설정\nBan_private_cars_and_trucks_on_bus_lanes 버스전용 차선에 승용차 및 트럭 출입을 금지합니다\ndefault 기본값\nflow_ratio 차량 이동량\nwait_ratio 차량 대기량\nAfter_min._time_has_elapsed_switch_to_next_step_if 최소시간 경과후 다음단계로 넘어갑니다 :\nAdaptive_step_switching 적응형 신호 변경\nDynamic_lane_section 유동적 차량 차선변경 빈도\nPercentage_of_vehicles_performing_dynamic_lane_section 유동적 차량 차선변경 빈도 백분율\nVehicle_restrictions_aggression 진입불가 도로 회피 빈도\nStrict 엄격하게 제어함\nShow_path-find_stats 현재 경로를 찾는 차량수를 PFs 수치로 표기합니다\nRemove_this_vehicle 해당 차량을 제거합니다\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights 차량이 신호등 시간표가 구성된 교차로에서 교통 표지판 규칙을 준수합니다\nEnable_tutorial_messages 튜토리얼 메세지 활성화\nTMPE_TUTORIAL_HEAD_MainMenu Traffic Manager: President Edition\nTMPE_TUTORIAL_BODY_MainMenu TM:PE에 오신것을 환영합니다!\\n\\n영문 가이드: http://www.viathinksoft.de/tmpe\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool 교차로 규칙 도움말\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool 차량과 보행자가 교차로에서 어떠한 규칙을 따라야하는지 통제할 수 있습니다\\n\\n1. 규칙을 설정하고 싶은 교차로를 클릭합니다\\n2. 통제하고 싶은 구역 아이콘을 클릭합니다\\n\\n적용할 수 있는 규칙 :\\n- 직진하면서 차선 변경 허용/비허용\\n- 유턴 허용/비허용\\n- 꼬리물기 허용/비허용\\n- 보행자의 횡단보도 허용/비허용\nTMPE_TUTORIAL_HEAD_LaneArrowTool 도로 화살표 변경 도움말\nTMPE_TUTORIAL_BODY_LaneArrowTool 차량이 회전할 수 있는 방향을 통제할 수 있습니다.\\n\\n1. 변경하고 싶은 교차로 도로구간을 클릭합니다\\n2. 화살표를 클릭하여 방향을 지정합니다.\nTMPE_TUTORIAL_HEAD_LaneConnectorTool 차선 연결 도움말\nTMPE_TUTORIAL_BODY_LaneConnectorTool 두개 이상의 차선을 연결하여 차량이 해당 차선으로 이동하도록 명령할 수 있습니다.\\n\\n1. 차선을 변경할 포인트를 클릭합니다(회색 원)\\n2. 시작할 차선을 클릭합니다\\n3. 연결하고 싶은 차선 원을 클릭하여 연결합니다\\n4. 빈공간을 우클릭하면 다시 전 단계로 돌아갈 수 있습니다\\n\\n사용할 수 있는 단축키 :\\n\\n- Delete , Backspace : 모든 연결된 차선들을 삭제합니다\\n- Shift + S : 직진차선 교차점에서 원을 돌며 패턴을 적용 할 수 있습니다\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool 신호등 직접 통제 도움말\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool 신호등을 직접 통제해 볼 수 있습니다\\n\\n1. 교차로를 클릭합니다\\n2. 신호등 통제툴을 통해 신호를 통제할 수 있습니다\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool 주차 제한 도움말\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool 어디에 주차를 허용할 것인지 통제 할 수 있습니다.\\n\\n\"P\"아이콘을 클릭합니다.\\n\\n사용가능한 단축키 :\\n\\n- Shift : 쉬프트키를 누른상태로 클릭하면 교차로내의 모든 도로구간에 해당 규칙을 적용합니다\nTMPE_TUTORIAL_HEAD_PrioritySignsTool 교통 표지판 추가 도움말\nTMPE_TUTORIAL_BODY_PrioritySignsTool 교차로에서 교통 표지판 규칙을 준수하게 만들 수 있습니다.\\n\\n1. 교차로를 클릭합니다.\\n2. 검정색 원을 클릭하여 사용 가능한 교통표지판을 규정합니다(우선도로, 양보, 정지).\\n\\n사용할 수 있는 단축키 :\\n\\n- Shift : 쉬프트키를 누른 상태로 해당 도로구간에서 추가하고 싶은 표지판을 클릭합니다. 다시 클릭하면 원을 돌면서 사용 가능한 패턴으로 교통 표지판 규칙이 설정됩니다\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool 속도 제한 도움말\nTMPE_TUTORIAL_BODY_SpeedLimitsTool 속도를 준수하도록 제한 할 수 있습니다.\\n\\n1. 속도 제한 패널창에서, 제한하고 싶은 속도를 클릭합니다.\\n 해당 도로구간을 클릭하여 속도 제한을 변경합니다\\n\\n사용할 수 있는 단축키 :\\n\\n- Shift : 쉬프트키를 누른 상태로 도로구간을 클릭하면 한번에 적용 됩니다.\\n- Ctrl : 컨트롤키를 누른 상태로 각 차선마다 속도를 제한 할 수 있습니다\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool 신호등 시간표 설정 도움말\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool 신호등 시간표를 구성 할 수 있습니다.\\n\\n1. 교차로를 클릭합니다.\\n2. 선택된 교차로 목록 패널에서 \"신호등 시간 설정\"을 클릭합니다.\\n3. 신호등 시간표 관리자 패널에서 현재 구성된 단계들을 볼 수 있습니다.\\n4. \"단계 추가\"를 클릭합니다.\\n5. 원하는 만큼 계속 반복합니다.\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool 신호등 추가/제거 도움말\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool 교차로에 있는 신호등을 추가/제거 할 수 있습니다.\\n\\n교차로를 클릭하여 신호등을 추가/제거 하십시오.\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool 차량 제한 도움말\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool 특정 도로구간에 차량접근을 통제 할 수 있습니다.\\n\\n1. 도로구간을 클릭하십시오.\\n2. 아이콘을 클릭하여 접근을 통제하십시오.\\n\\n 유형별 차량 종류 :\\n\\n- 도로 차량 : 승용차, 버스, 택시, 화물차, 서비스차량, 긴급차량\\n- 철도 차량 : 여객기차, 화물기차\\n\\n사용할 수 있는 단축키 :\\n\\n- Shift : 쉬프트키를 누른 상태로 도로구간을 클릭하면 한번에 해당차량 접근을 통제할 수 있습니다.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults 기본 속도 제한 도움말\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. 화살표를 통해 도로 유형을 정합니다.\\n2. 아래에 위치한 화살표를 통해 제한할 속도를 정합니다.\\n3. \"저장\"을 클릭하면 앞으로 설치되는 해당 도로유형이 설정된 속도 제한값으로 설정됩니다.\\n4. \"저장 & 적용\"을 클릭하면 모든 이전에 설치되어 있던 해당 도로도 해당 속도제한 규칙이 적용됩니다.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep 시간 구성하는 방법\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. 신호등 변경 툴에서, 신호등을 클릭하여 신호를 변경할 수 있습니다. \"Change mode\"를 클릭하면 신호등 방향을 구체적으로 설정 할 수 있습니다\\n2. 최소시간과 최대시간은 분으로 구성되어 있습니다. 시간이 경과되면 접근하는 차량 수를 신호등이 비교를 합니다.\\n3. 설정을 통해 다음단계로 어떻게 넘어갈 것인지 구성하십시오. 기본적으로는 운전하는 차량 수보다 대기 중인 차량수가 많으면 다음 단계로 변경됩니다.\\n4. 신호등 주기 설정을 조절할 수 있습니다. 예를 들어,  슬라이드를 왼쪽으로 움직이게 하면 대기중인 차가 신호가 변경된 후 덜 혼잡하게 지나갈 수 있습니다.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy 신호등 시간표 복제 방법\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy 클릭을 통해 다른 교차로에 같은 신호등 시간표를 적용할 수 있습니다.\\n\\n원하는 곳을 클릭하여 복제한 후 마우스 우클릭을 통해 복제를 종료할 수 있습니다.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction 교차로에 신호등 시간표 추가하는 방법\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction 다른 교차로를 클릭하여 추가하십시오. 선택된 교차로의 신호등들은 구성된 시간표와 신호에 맞게 같이 작동되게 됩니다.\\n\\n교차로에서 신호등 시간표를 추가하는 것을 중단하려면 마우스 우클릭을 클릭하십시오.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction 교차로에 신호등 시간표 제거하는 방법\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction 신호등 시간표가 구성되어 있는 교차로를 클릭하십시오. 클릭된 신호등은 더 이상 해당 시간표 통제를 받지않게 됩니다.\\n\\n교차로에서 신호등 시간표를 제거하는 것을 중단하려면 마우스 우클릭을 클릭하십시오.\nPublic_transport 대중 교통\nPrevent_excessive_transfers_at_public_transport_stations 시민들의 다른 대중교통 노선 이용 시 패턴 최적화\nCompact_main_menu TMPE 메뉴 아이콘 크게/작게 설정\nWindow_transparency 패널 투명도(오른쪽으로 갈수록 투명함)\nOverlay_transparency 보조 아이콘 투명도(도로 위 표지판, 속도제한 아이콘 투명도 등)\nRemove_this_citizen 해당 시민 삭제\nShow_error_message_if_a_mod_incompatibility_is_detected 모드와 비 호환되는 모드 발견 시 에러 보여주기\nRemove_parked_vehicles 주차된 차량 제거하기\nNode_is_level_crossing 해당 교차로는 평면 교차로입니다.\\n따라서 신호등을 설치할 수 없습니다\nExperimental_features Experimental features\nTurn_on_red Turn on red\nVehicles_may_turn_on_red 빨간 신호등에서 차량이 돌아올 수 있습니다.\nAlso_apply_to_left/right_turns_between_one-way_streets Also apply to left & right turns between one-way streets"
  },
  {
    "path": "TLM/TLM/Resources/lang_nl.txt",
    "content": "Switch_traffic_lights Stoplichten omwisselen\nAdd_priority_signs Plaats voorrangsborden\nManual_traffic_lights Handmatige stoplichten\nTimed_traffic_lights Tijdgestuurde stoplichten\nChange_lane_arrows Voorsorteerpijlen aanpassen\nClear_Traffic Wegen vrijmaken\nDisable_despawning Voertuigdverdwijning uitschakelen\nEnable_despawning Voertuigverdwijning inschakelen\nNODE_IS_LIGHT De kruising heeft een stoplicht.\\nJe kunt het stoplicht verwijderen door eerst op \"stoplichten omwisselen\" te klikken en daarna op de kruising.\nNODE_IS_TIMED_LIGHT De kruising heeft tijdgestuurde stoplichten.\\nJe kunt de tijdsturing verwijderen door op \"Tijdgestuurde stoplichten\" te klikken, daarn op de kruising en daarna op \"Verwijderen\".\nSelect_nodes_windowTitle Kruisingselectie\nSelect_nodes Selecteert één of meerdere kruisingen\nNode Kruising\nDeselect_all_nodes Deselecteer alle kruisingen\nSetup_timed_traffic_light Tijdgestuurde stoplichten inrichten\nState Stap\nSkip Verder\nup omhoog\ndown omlaag\nView Weergeven\nEdit Wijzigen\nDelete Verwijderen\nTimed_traffic_lights_manager Beheer van tijdgestuurde stoplichten\nAdd_step Stap toevoegen\nRemove_timed_traffic_light Stoplichten verwijderen\nMin._Time: Min. tijd:\nMax._Time: Max. tijd:\nSave Opslaan\nAdd Toevoegen\nSensitivity Gevoeligheid\nVery_Low Zeer laag\nLow Laag\nMedium Middelhoog\nHigh Hoog\nVery_high Zeer hoog\nExtreme_long_green/red_phases Extreem lange groen-/roodfasen\nVery_long_green/red_phases Zeer lange groen-/roodfasen\nLong_green/red_phases Lange groen-/roodfasen\nModerate_green/red_phases Normale groen-/roodfasen\nShort_green/red_phases korte groen-/roodfasen\nVery_short_green/red_phases Zeer korte groen-/roodfasen\nExtreme_short_green/red_phases Extreem korten groen-/roodfasen\nHide_counters Tellers verbergen\nShow_counters Tellers tonen\nStart Start\nStop Stop\nEnable_test_mode_(stay_in_current_step) Testmodus activeren (huidige stap vasthouden)\navg._flow rijdend\navg._wait wachtend\nmin/max min/max\nLane Rijstrook\nSet_Speed Snelheid instellen {0}\nMax_speed Max. snelheid\nSegment Segment\nincoming inkomend\nEnable_Advanced_Vehicle_AI Verbeterde voertuig-KI activeren\nVehicles_may_enter_blocked_junctions Voertuigen moge geblokkeerde kruisingen oprijden\nAll_vehicles_may_ignore_lane_arrows Alle voertuigen mogen voorsorteerpijlen negeren\nBusses_may_ignore_lane_arrows Bussen mogen voorsorteerpijlen negeren\nReckless_driving Roekeloos rijden\nTMPE_Title Traffic Manager: Presidentiële editie\nSettings_are_defined_for_each_savegame_separately Instellingen worden per spelstand opgeslagen\nSimulation_accuracy Genauigkeit der Simulation (hogere precisie verlaagt prestaties)\nEnable_highway_specific_lane_merging/splitting_rules Activeer speciale verkeersregels op snelwegen\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): Autobestuurders wisselen van rijstrook (heeft alleen effect als de KI ingeschakeld is)\nMaintenance Onderhoud\nVery_often Zeer vaak\nOften Vaak\nSometimes Regelmatig\nRarely Zelden\nVery_rarely Zeer zelden\nOnly_if_necessary Alleen indien nodig\nNodes_and_segments Kruisingen en segmenten\nLanes Rijkstroken\nPath_Of_Evil_(10_%) Route des kwaads (10 %)\nRush_Hour_(5_%) Spitsuur (5 %)\nMinor_Complaints_(2_%) Enige klachten (2 %)\nHoly_City_(0_%) Heilige stad (0 %)\nForget_toggled_traffic_lights Stoplichtaanpassingen vergeten\nRoad_is_already_in_a_group! Weg behoort reeds tot een groep!\nAll_selected_roads_must_be_of_the_same_type! Alle geselecteerde wegen moeten van hetzelfde soort zijn!\nCreate_group Gruppe aanmaken\nDelete_group Gruppe verwijderen\nAdd_zoning Zones activeren\nRemove_zoning Zones verwijderen\nLane_Arrow_Changer_Disabled_Highway Het is niet mogelijk de voorrangspijlen op deze rijstroom te veranderen, omdat je de speciale regels voor snelwegen geactiveerd hebt.\nAdd_junction_to_timed_light Kruising aan stoplicht toevoegen\nRemove_junction_from_timed_light Kruising van stoplicht verwijderen\nSelect_junction Selecteer een kruising\nCancel Afbreken\nSpeed_limits Snelheidslimiet\nPersistently_visible_overlays Permanent zichtbare projecties\nPriority_signs Voorrangsborden\nVehicles_may_do_u-turns_at_junctions Voertuigen kunnen u-bochten op kruisingen maken\nVehicles_going_straight_may_change_lanes_at_junctions Voertuigen mogen van rijstrook wisselen als ze rechtdoor op kruisingen rijden\nAllow_u-turns U-bochten toestaan\nAllow_lane_changing_for_vehicles_going_straight Wisselen van rijstook toestaan voor voertuigen die rechtdoor rijden\nAllow_vehicles_to_enter_a_blocked_junction Sta voertuigen toe om een geblokkeerde kruising op te rijden\nRoad_condition_has_a_bigger_impact_on_vehicle_speed De wegtoestand heeft een grotere invloed op de voertuigsnelheid\nVehicle_restrictions Voertuigbeperkingen\nCopy Kopiëren\nPaste Invoegen\nInvert Inverteren\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions Pas voertuigbeperkingen toe op alle wegsegmenten tussen twee kruisingen\nAllow_all_vehicles Sta alle voertuigen toe\nBan_all_vehicles Verbied alle voertuigen\nSet_all_traffic_lights_to_red Alle stoplichten op rood zetten\nRotate_left Linksom draaien\nRotate_right Rechtsom draaien\nName Naam\nApply Toepassen\nSelect_a_timed_traffic_light_program Selecteer een programma voor tijdgestuurde stoplichten\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction Het geselecteerde stoplichtenprogramma is niet compatibel met deze kruising\nAdvanced_AI_cannot_be_activated Verbeterde voertuig-KI kan niet worden geactiveerd\nThe_Advanced_Vehicle_AI_cannot_be_activated De verbeterde voertuig-KI kan niet geactiveerd worden, omdat je reeds aan andere mod gebruikt, die het gedrag van voertuigen verandert (bijv. Improved AI of Traffic++).\nEnable_dynamic_path_calculation Activeer dynamische routeberekening\nChanges_made_with_this_tool_will_take_full_effect_after_some_minutes Changes made with this tool will take full effect after some minutes\nLane_Arrow_Changer_Disabled_Connection Voor deze rijstrook kan de rijstrookpijl niet aangepast worden, omdat je handmatig rijstrookverbindingen hebt gemaakt.\nLane_connector Rijstrookverbinder\nConnected_lanes Verbonden rijstroken\nUse_alternative_view_mode Gebruik alternatief aanzicht\nRoad_type Wegsoort\nDefault_speed_limit Standaard snelheidslimiet\nUnit_system Eenhedenstelsel\nMetric Metrisch\nImperial Emperisch\nUse_more_CPU_cores_for_route_calculation_if_available Gebruik meer processorkernen voor routeberekening indien beschikbaar\nActivated_features Geactiveerde functies\nJunction_restrictions Splitsingsbeperkingen\nProhibit_spawning_of_pocket_cars Verbied opduiken van broekzakautomobielen\nReset_stuck_cims_and_vehicles Reset vastgelopen inwoners en voertuigen\nDefault_speed_limits Standaard snelheidslimieten\nLooking_for_a_parking_spot Op zoek naar een parkeerplaats\nDriving_to_a_parking_spot Rijdt naar een parkeerplaats\nDriving_to_another_parking_spot Rijdt naar een andere parkeerplaats\nEntering_vehicle Stapt in voertuig\nWalking_to_car Loopt naar auto\nUsing_public_transport Gebruikt openbaar vervoer\nWalking Wandelt\nThinking_of_a_good_parking_spot Na aan het denken over een goede parkeerplaats\nSwitch_view Aanzicht wisselen\nOutgoing_demand Uitgaande vraag\nIncoming_demand Inkomende vraag\nAdvanced_Vehicle_AI Geavanceerde voertuig-KI\nHeavy_trucks_prefer_outer_lanes_on_highways Zware vrachtverkeer verkiest buitenste rijstroken op snelwegen\nParking_AI Parkeer-KI\nEnable_more_realistic_parking Schakel realistischer parkeren in\nReset_custom_speed_limits Reset aangepaste snelheidslimieten\nReload_global_configuration Herlaad algemene configuratie\nReset_global_configuration Reset algemene configuratie\nGeneral Algemeen\nGameplay Spelverloop\nOverlays Transparanten\nRealistic_speeds Realistische snelheden\nEvacuation_busses_may_ignore_traffic_rules Evacuatiebussen mogen verkeersregels overtreden\nEvacuation_busses_may_only_be_used_to_reach_a_shelter Evacuatiebussen mogen enkel gebruikt worden om schuilkelders te bereiken\nVehicle_behavior Vortuiggedrag\nPolicies_&_Restrictions Beleidsregels & beperkingen\nAt_junctions Bij splitsingen\nIn_case_of_emergency In geval van nood\nShow_lane-wise_speed_limits Toon rijstrookspecifieke snelheidslimieten\nLanguage Taal\nGame_language Taal van spel\nrequires_game_restart Vereist spelherstart\nCustomizations_come_into_effect_instantaneously Aanpassingen zijn onmiddelijk van toepassing\nOptions Instellingen\nLock_main_menu_button_position Vergendel positie van hoofdmenuknop\nLock_main_menu_position Vergrendel positie van hoofdmenu\nRecalculating_lane_routing Rijstrookroutering wordt herberekend\nPlease_wait Even geduld graag\nParking_restrictions Parkeerbeperkingen\nOn_roads On roads\nBan_private_cars_and_trucks_on_bus_lanes Ban private cars and trucks on bus lanes\ndefault default\nflow_ratio flow ratio\nwait_ratio wait ratio\nAfter_min._time_has_elapsed_switch_to_next_step_if After min. time has elapsed, switch to next step if\nAdaptive_step_switching Adaptive step switching\nDynamic_lane_section Dynamic lane selection\nPercentage_of_vehicles_performing_dynamic_lane_section Percentage of vehicles performing dynamic lane selection\nVehicle_restrictions_aggression Vehicle restrictions aggression\nStrict Strict\nShow_path-find_stats Show path-find stats\nRemove_this_vehicle Remove this vehicle\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights Vehicles follow priority rules at junctions with timed traffic lights\nEnable_tutorial_messages Enable tutorial messages\nTMPE_TUTORIAL_HEAD_MainMenu Traffic Manager: President Edition\nTMPE_TUTORIAL_BODY_MainMenu Welcome to TM:PE!\\n\\nUser manual: http://www.viathinksoft.de/tmpe\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool Junction restrictions\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool Control how vehicles and pedestrians shall behave at junctions.\\n\\n1. Click on the junction you want to manage\\n2. Click on the appropriate icon to toggle restrictions.\\n\\nAvailable restrictions:\\n- Allow/Disallow lane changing for vehicle going straight on\\n- Allow/Disallow u-turns\\n- Allow/Disallow vehicles to enter a blocked junction\\n- Allow/disallow pedestrians to cross the street\nTMPE_TUTORIAL_HEAD_LaneArrowTool Lane arrows\nTMPE_TUTORIAL_BODY_LaneArrowTool Restrict the set of directions that vehicles are allowed to take.\\n\\n1. Click on a road segment next to a junction\\n2. Select the allowed directions.\nTMPE_TUTORIAL_HEAD_LaneConnectorTool Lane connector\nTMPE_TUTORIAL_BODY_LaneConnectorTool Connect two or more lanes with each other in order to tell which lanes vehicles may use.\\n\\n1. Click on a lane changing point (grey circles).\\n2. Click on a source marker.\\n3. Click on a target marker to connect it with the source marker.\\n4. Click anywhere with your secondary mouse button to return back to source marker selection.\\n\\nAvailable hotkeys:\\n\\n- Delete or Backspace: Remove all lane connections\\n- Shift + S: Cycle through all available \"stay on lane\" patterns\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool Manual traffic lights\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool Try out custom traffic lights.\\n\\n1. Click on a junction\\n2. Use the tool to control traffic lights.\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool Parking restrictions\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool Control where parking is allowed.\\n\\nClick on the \"P\" icons.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold while clicking to apply parking restrictions to multiple road segments at once\nTMPE_TUTORIAL_HEAD_PrioritySignsTool Priority signs\nTMPE_TUTORIAL_BODY_PrioritySignsTool Define priority rules at junctions.\\n\\n1. Click on a junction.\\n2. Click on the blank spots to cycle through the available priority signs (priority road, yield, stop).\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift to add priority signs to multiple road segments at once. Click again while holding Shift to cycle through all available patterns.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool Speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool Set up speed restrictions.\\n\\n1. In the window, click on the speed limit you want to set.\\n2. Click on a road segment to change the speed limit.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply speed limits to multiple road segments at once.\\n- Ctrl: Hold Ctrl to control speed limits per individual lane.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool Timed traffic lights\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool Set up timed traffic lights.\\n\\n1. Click on a junction.\\n2. In the window, click on \"Add step\".\\n3. Click on the overlay elements to set desired traffic lights states.\\n4. Click on \"Add\".\\n5. Repeat as desired.\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool Toggle traffic lights\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool Add or remove traffic lights to/from junctions.\\n\\nClick on a junction to toggle traffic lights.\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool Vehicle restrictions\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool Ban vehicles from certain road segments.\\n\\n1. Click on a road segment.\\n2. Click on the icons to toggle restrictions.\\n\\nDistinguished vehicle types:\\n\\n- Road vehicles: Passenger cars, busses, taxis, cargo trucks, service vehicles, emergency vehicles\\n- Rail vehicles: Passenger trains, cargo trains\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply restrictions to multiple road segments at once.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults Default speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. Use the arrows in the upper half to cycle through all road types.\\n2. In the lower half, select a speed limit.\\n3. Click on \"Save\" to set the selected speed limit as default for future road segments of the selected type. Click on \"Save & Apply\" to also update all existing roads of the selected type.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep Add a timed step \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. Within the in-game overlay, click on the traffic lights to change their state. Use the \"Change mode\" button to add directional traffic lights.\\n2. Enter both a minimum and maximum duration for the step. After the min. time has elapsed the traffic light will count and compare approaching vehicles.\\n3. Optionally, select a step switching type. Per default, the step changes if roughly less vehicles are driving than waiting.\\n4. Optionally, adjust the light's sensitivity. For example, move the slider to the left to make the timed traffic light less sensitive for waiting vehicles. \nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy Copy a timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy Click on another junction to paste the timed traffic light.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction Add a junction to the timed traffic light \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction Click on another junction to add it. Both lights will be joined such that the timed program will then control both junctions at once.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction Remove a junction from the timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction Click on one of the junctions that are controlled by this timed program. The selected traffic light will be removed such that the timed programm will no longer manage it.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nPublic_transport Public transport\nPrevent_excessive_transfers_at_public_transport_stations Prevent unnecessary transfers at public transport stations\nCompact_main_menu Compact main menu\nWindow_transparency Window transparency\nOverlay_transparency Overlay transparency\nRemove_this_citizen Remove this citizen\nShow_error_message_if_a_mod_incompatibility_is_detected Show error message if a mod incompatibility is detected\nRemove_parked_vehicles Remove parked vehicles\nNode_is_level_crossing This junction is a level crossing.\\nYou cannot disable traffic lights here.\nExperimental_features Experimental features\nTurn_on_red Turn on red\nVehicles_may_turn_on_red Voertuigen kunnen bij rode verkeerslichten draaien\nAlso_apply_to_left/right_turns_between_one-way_streets Also apply to left & right turns between one-way streets"
  },
  {
    "path": "TLM/TLM/Resources/lang_pl.txt",
    "content": "Switch_traffic_lights Włącznik sygnalizacji\nAdd_priority_signs Dodaj znaki pierwszeństwa\nManual_traffic_lights Sygnalizacja Ręczna\nTimed_traffic_lights Sygnalizacja Czasowa\nChange_lane_arrows Zmień strzałki pasów\nClear_Traffic Wyczyść ruch\nDisable_despawning Zablokuj znikanie pojazdów\nEnable_despawning Odblokuj znikanie pojazdów\nNODE_IS_LIGHT Skrzyżowanie posiada sygnalizację. \\nUsuń sygnalizację poprzez \"Włącznik sygnalizacji\" i kliknięcie na tym węźle.\nNODE_IS_TIMED_LIGHT Skrzyżowanie jest częścią skryptu czasowego. \\nWybierz \"Sygnalizacja Czasowa\", najpierw kliknij na tym węźle i potem na \"Usuń\".\nSelect_nodes_windowTitle Wybór węzłów\nSelect_nodes Wybierz węzły\nNode Węzeł\nDeselect_all_nodes Odznacz wszystkie węzły\nSetup_timed_traffic_light Ustaw czasową sygnalizację świetlną\nState Stan\nSkip Pomiń\nup w górę\ndown w dół\nView Wyświetl\nEdit Edytuj\nDelete Skasuj\nTimed_traffic_lights_manager Menedżer Sygnalizacji czasowej\nAdd_step Dodaj krok\nRemove_timed_traffic_light Usuń sygnalizację czasową\nMin._Time: Min. Czas:\nMax._Time: Max. Czas:\nSave Zapisz\nAdd Dodaj\nSensitivity Czułość\nVery_Low Bardzo niska\nLow Niska\nMedium Średnia\nHigh Wysoka\nVery_high Bardzo wysoka\nExtreme_long_green/red_phases Ekstremalnie długi ziel./czerw.\nVery_long_green/red_phases Bardzo długi ziel./czerw.\nLong_green/red_phases Długi ziel./czerw.\nModerate_green/red_phases Średni okres ziel./czerw.\nShort_green/red_phases Krótki ziel./czerw.\nVery_short_green/red_phases Bardzo krótki ziel./czerw.\nExtreme_short_green/red_phases Ekstremalnie krótki ziel./czerw.\nHide_counters Ukryj liczniki\nShow_counters Pokaż liczniki\nStart Start\nStop Stop\nEnable_test_mode_(stay_in_current_step) Aktywuj tryb testu (pozostań w tym kroku)\navg._flow śr. przepływ\navg._wait śr. oczekiwanie\nmin/max min/max\nLane Pas\nSet_Speed Ustaw Prędkość {0}\nMax_speed Max Prędkość\nSegment Segment\nincoming nadjeżdżające\nEnable_Advanced_Vehicle_AI Włącz Zaawansowaną Sztuczną Inteligencję Pojazdów\nVehicles_may_enter_blocked_junctions Pojazdy mogą wjeżdżać na zablokowane skrzyżowania\nAll_vehicles_may_ignore_lane_arrows Wszystkie pojazdy mogą ignorować strzałki pasów\nBusses_may_ignore_lane_arrows Autobusy mogą ignorować strzałki pasów\nReckless_driving Lekkomyślna jazda (funkcja w wer. BETA)\nTMPE_Title Menedżer ruchu: Edycja Prezydencka\nSettings_are_defined_for_each_savegame_separately ustawienia są definiowane oddzielnie dla każdego zapisu\nSimulation_accuracy Dokładność symulacji (większa dokładność zmiejsza wydajność gry)\nEnable_highway_specific_lane_merging/splitting_rules Aktywuj Zmienione zasady podziału/łączenia pasów dla autostrad\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): Chęć kierowców do zmiany pasa (tylko gdy Zaawansowana SI jest włączona)\nMaintenance Konserwacja (dodatkowe informacje)\nVery_often Bardzo często\nOften Często\nSometimes Czasami\nRarely Rzadko\nVery_rarely Bardzo Rzadko\nOnly_if_necessary Tylko jeśli konieczna\nNodes_and_segments Pokaż węzły i segmenty\nLanes Pasy\nPath_Of_Evil_(10_%) Diabelska Ścieżka (10 %)\nRush_Hour_(5_%) Godziny Szczytu (5 %)\nMinor_Complaints_(2_%) Drobne Skargi (2 %)\nHoly_City_(0_%) Święte Miasto (0 %)\nForget_toggled_traffic_lights Unuń ustawione sygnalizacje czasowe\nRoad_is_already_in_a_group! Droga jest już w grupie\nAll_selected_roads_must_be_of_the_same_type! Wszystkie zaznaczone drogi muszą być tego samego typu\nCreate_group Stwórz grupę\nDelete_group Skasuj grupę\nAdd_zoning Aktywuj Strefy\nRemove_zoning Skasuj Strefy\nLane_Arrow_Changer_Disabled_Highway Zmiana strzałek pasów została wyłączona dla tego pasa ponieważ aktywowałeś/łaś Zmienione zasady łączenia pasów dla Autostrad\nAdd_junction_to_timed_light Dodaj skrzyżowanie do tego węzła sygnalizacji\nRemove_junction_from_timed_light Wyklucz skrzyżowanie z tego węzła sygnalizacji\nSelect_junction Wybierz skrzyżowanie\nCancel Anuluj\nSpeed_limits Ograniczenia prędkości\nPersistently_visible_overlays Trwale widoczne nakładki\nPriority_signs Znaki pierwszeństwa\nVehicles_may_do_u-turns_at_junctions Pojazdy mogą zawracać na skrzyżowaniach\nVehicles_going_straight_may_change_lanes_at_junctions Pojazdy jadące na wprost mogą zmieniać pasy ruchu na skrzyżowaniach\nAllow_u-turns Zezwól na zawracanie\nAllow_lane_changing_for_vehicles_going_straight Zezwól na zmianę pasa ruchu dla pojazdów jadących na wprost\nAllow_vehicles_to_enter_a_blocked_junction Zezwól pojazdom na wjazd na zablokowane skrzyżowanie\nRoad_condition_has_a_bigger_impact_on_vehicle_speed Kondycja nawierzchni drogi ma większy wpływ na prędkość pojazdu\nVehicle_restrictions Ograniczenia ruchu pojazdów\nCopy Kopiuj\nPaste Wklej\nInvert Odwróć\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions Zastosuj ograniczenia ruchu pojazdów do wszystkich segmentów drogi pomiędzy dwoma skrzyżowaniami\nAllow_all_vehicles Zezwól dla wszystkich pojazdów\nBan_all_vehicles Zablokuj dla wszystkich pojazdów\nSet_all_traffic_lights_to_red Ustaw wszystkie sygnalizatory na sygnał czerwony\nRotate_left Obróć w lewo\nRotate_right Obróć w prawo\nName Nazwa\nApply Zastosuj\nSelect_a_timed_traffic_light_program Wybierz program dla czasowej sygnalizacji świetlnej\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction Wybrany szablon sygnalizacji jest niekompatybilny z tym skrzyżowaniem\nAdvanced_AI_cannot_be_activated Nie można włączyć Zaawansowanej SI\nThe_Advanced_Vehicle_AI_cannot_be_activated Zaawansowana SI nie może zostać włączona, ponieważ aktywowana została inna modyfikacja zmieniająca zachowanie pojazdów (np:. Improved AI lub Traffic++).\nEnable_dynamic_path_calculation Aktywuj dynamiczne wyznaczanie ścieżki\nLane_Arrow_Changer_Disabled_Connection Zmiana strzałek pasów ruchu jest niedostępna, ponieważ pasy ruchu zostały połączone ręcznie.\nLane_connector Połącz pasy ruchu\nConnected_lanes Połączenia pasów ruchu\nUse_alternative_view_mode Użyj alternatywnego widoku\nRoad_type Typ drogi\nDefault_speed_limit Domyślny limit prędkości\nUnit_system System jednostek\nMetric Metryczne\nImperial Imperialne\nUse_more_CPU_cores_for_route_calculation_if_available Użyj więcej rdzeni procesora do obliczania tras (jeśli dostępne)\nActivated_features Aktywowane funkcje\nJunction_restrictions Ograniczenia na skrzyżowaniach\nProhibit_spawning_of_pocket_cars Zabroń używania \"kieszonkowych samochodów\"\nReset_stuck_cims_and_vehicles Zresetuj zablokowane samochody i ludzi\nDefault_speed_limits Standardowe limity prędkości\nLooking_for_a_parking_spot Poszukuje miejsca parkingowego\nDriving_to_a_parking_spot Jedzie do miejsca parkingowego\nDriving_to_another_parking_spot Jedzie do innego miejsca parkingowego\nEntering_vehicle Wsiada do samochodu\nWalking_to_car Idzie do samochodu\nUsing_public_transport Używa transportu publicznego\nWalking Idzie\nThinking_of_a_good_parking_spot Myśli o odpowiednim miejscem parkingowym\nSwitch_view Zmień widok\nOutgoing_demand Zapotrzebowanie - wychodzące\nIncoming_demand Zapotrzebowanie - przychodzące\nAdvanced_Vehicle_AI Zaawansowana SI pojazdów\nHeavy_trucks_prefer_outer_lanes_on_highways Samochody ciężarowe preferują zewnętrzne pasy na autostradach\nParking_AI SI parkowania\nEnable_more_realistic_parking Aktywuj bardziej realistyczny system parkowania samochodów\nReset_custom_speed_limits Zresetuj limity prędkości użytkownika\nReload_global_configuration Przeładuj ustawienia globalne\nReset_global_configuration Zresetuj ustawienia globalne\nGeneral Ogólne\nGameplay Rozgrywka\nOverlays Nakładki\nRealistic_speeds Realistyczne prędkości\nEvacuation_busses_may_ignore_traffic_rules Autobusy ewakuacyjne mogą ignorować przepisy\nEvacuation_busses_may_only_be_used_to_reach_a_shelter Autobusy ewakuacyjne mogą przewozić tylko do schroniena\nVehicle_behavior Zachowanie pojazdu\nPolicies_&_Restrictions Zasady i Ograniczenia\nAt_junctions Na strzyżowaniach\nIn_case_of_emergency W sytuacjach nadzwyczajnych\nShow_lane-wise_speed_limits Pokaż limity prędkości dla pasów\nLanguage Język\nGame_language Język gry\nrequires_game_restart wymagane ponowne uruchomienie gry\nCustomizations_come_into_effect_instantaneously Modyfikacje ustawień mają natychmiastowy efekt\nOptions Opcje\nLock_main_menu_button_position Zablokuj pozycję przycisku menu głównego\nLock_main_menu_position Zablokuj pozycję menu głównego\nRecalculating_lane_routing Ponowne obliczanie tras\nPlease_wait Proszę czekać\nParking_restrictions Ograniczenia parkingowe\nSimulation Symulacja\nOn_roads Na drogach\nBan_private_cars_and_trucks_on_bus_lanes Zakaz jazdy buspasem dla samochodów osobowych i ciężarówek\ndefault domyślny\nflow_ratio wskaźnik przepływu\nwait_ratio wskaźnik oczekiwania\nAfter_min._time_has_elapsed_switch_to_next_step_if Po upływie min. czasu, przejdź do następnego kroku\nAdaptive_step_switching Adaptacyjna zmiana kroku\nDynamic_lane_section Obszar dynamicznej zmiany pasa ruchu\nPercentage_of_vehicles_performing_dynamic_lane_section Procent pojazdów zmieniajacych pas w obszarze dynamicznej zmiany pasa ruchu\nVehicle_restrictions_aggression Agrasywność ograniczeń ruchu pojazdów\nStrict Ścisła\nShow_path-find_stats Pokaż statystyki znajdowania tras\nRemove_this_vehicle Usuń ten pojazd\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights Pojazdy poruszają się zgodnie z regułami pierwszeństwa na skrzyżowaniach z sygnalizacją czasową\nEnable_tutorial_messages Włącz podpowiedzi\nTMPE_TUTORIAL_HEAD_MainMenu Traffic Manager: President Edition\nTMPE_TUTORIAL_BODY_MainMenu Witaj w TM:PE!\\n\\nPodręcznik użytkownika(Ang.): http://www.viathinksoft.de/tmpe \\n\\nTranslacja Krzychu1245. Zauważyłeś/łaś literówkę, błąd, cokolwiek do poprawy? Odezwij się na Steam;)\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool Ograniczenia na skrzyżowaniach\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool Decyduj jak pojazdy i piesi mają się zachowywać na skrzyżowaniach.\\n\\n1. Wybierz skrzyżowanie do ustawienia zasad\\n2. Kliknij na odpowiedniej ikonie by zmienić ograniczenia.\\n\\nDostępne zasady:\\n- Pozwól/Zabroń: zmiany pasa ruchu pojazdom jadącym na wprost \\n- Pozwól/Zabroń: zawracanie\\n- Pozwól/Zabroń: wjazd na zablokowane/zakorkowane skrzyżowanie\\n- Pozwól/Zabroń: przejście pieszych przez drogę\nTMPE_TUTORIAL_HEAD_LaneArrowTool Narzędzie zmiany kierunków pasów ruchu\nTMPE_TUTORIAL_BODY_LaneArrowTool Ustaw możliwe kierunki w którym pojazd może się poruszać z danego pasa ruchu.\\n\\n1. Wybierz segment drogi tuż przy skrzyżowaniu\\n2. Ustaw dozwolone kierunki ruchu.\nTMPE_TUTORIAL_HEAD_LaneConnectorTool Połącz pasy ruchu\nTMPE_TUTORIAL_BODY_LaneConnectorTool Połącz dwa lub więcej pasów ruchu ze sobą, aby wyznaczyć możliwe ścieżki poruszającym sie nimi pojazdom.\\n\\n1. Kliknij na wybranym punkcie (szare okręgi).\\n2. Wybierz jeden z kolorowych okręgów symbolizujących koniec pasa.\\n3. Następnie kliknij na inny, aby je ze sobą połączyć. \\n4. Kliknij gdziekolwiek prawym klawiszem myszy, aby powtórzyć wybór końcowego znacznika.\\n\\nSkróty klawiszowe:\\n\\n- Delete lub Backspace: Usuń wszystkie połącznia\\n- Shift + S: Przełączaj między dostępnymi schematami \"pozostań na pasie\"\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool Ręczna sygnalizaja świetlna\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool Wypróbuj własną sygnalizację świetlną.\\n\\n1. Kliknij na skrzyżowaniu\\n2. Użyj narzędzia do sterowania ręczną sygnalizacją świetlną.\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool Ograniczenia parkingowe\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool Decyduj gdzie możliwe jest parkowanie.\\n\\nKliknij na ikonę \"P\".\\n\\nSkróty klawiszowe:\\n\\n- Shift: przytrzymaj podczas klikania, aby zastowować ograniczenia parkingowe dla kilku segmentów drogi jednocześnie\nTMPE_TUTORIAL_HEAD_PrioritySignsTool Znaki pierwszeństwa\nTMPE_TUTORIAL_BODY_PrioritySignsTool Zdefiniuj zasady pierwszeństwa na skrzyżowaniach.\\n\\n1. Kliknij na skrzyżowaniu.\\n2. Klikaj na wybranym półprzezroczystym okręgu by zmienić pierwszeństwo na tym odcinku(droga z pierwszeństwem, ustąp pierwszeństwa, stop).\\n\\nSkróty klawiszowe:\\n\\n- Shift: Przytrzymuj Shift, aby dodać znaki pierwszeństwa do kilku segmentów drogi jednocześnie. Kliknij ponownie, przytrzymując klawisz Shift, aby zmieniać pomiędzy dostępnymi schematami.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool Limity prędkości\nTMPE_TUTORIAL_BODY_SpeedLimitsTool Ustaw limity prędkości.\\n\\n1. W okienku, kliknij na wybrany limit prędkości.\\n2. Kliknij na wybrany odcinek drogi, aby zastosować limit.\\n\\nSkróty klawiszowe:\\n\\n- Shift: Przytrzymaj Shift podczas wybierania, aby zastosować limit to wielu segmentów.\\n- Ctrl: Przytrzymaj Ctrl, aby ustawić limit dla konkretnego pasa ruchu.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool Czasowa sygnalizacja świetlna\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool Ustawianie czasowej sygnalizacji.\\n\\n1. Wybierz skrzyżowanie.\\n2. W okienku, kliknij \"Ustaw czasową sygnalizację świetlną\", następnie \"Dodaj krok\".\\n3. Ustaw pożądany stan sygnalizacji, klikając na wybrany sygnalizator.\\n4. Kliknij \"Dodaj\".\\n5. Powtórz kroki jeśli konieczne.\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool Przełącz na sygnalizację świetlną\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool Dodaj lub usuń sygnalizację świetlną ze skrzyżowania.\\n\\nKliknij na wybranym skrzyżowaniu, aby przełączyć stan.\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool Ograniczenia ruchu pojazdów\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool Zabroń wjazdu pojazdom na konkretnych odcinkach drogi.\\n\\n1. Kliknij na odcinku drogi.\\n2. Kliknij na wybranej ikonie, aby wł/wył ograniczenie.\\n\\nDostępne typy pojazdów:\\n\\n- Pojazdy drogowe: samochody, autobust, taxi, ciężarówki, pojazdy usługowe, pojazdy uprzywilejowane\\n- Pojazdy szynowe: pociągi pasażerskie, towarowe\\n\\nSkróty klawiszowe:\\n\\n- Shift: Przytrzymaj Shift podczas klikania, aby zastosować ograniczenia do kilku odcinków drogi jednocześnie.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults Domyślne limity prędkości\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. Użyj strzałek u góry, aby przęłączać pomiędzy dostępnymi typami dróg.\\n2. Poniżej, wybierz limit prędkości.\\n3. Naciśnij \"Zapisz\", aby ustawić wybrany limit prędkości jako domyślny dla nowo wybudowanych dróg wybranego typu. Naciścij \"Zapisz i zastosuj\", aby zaktualizować wszystkie już istniejące drogi wybranego typu.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep Dodaj krok \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. W ramach nakładki, kliknij na sygnalizatorze, aby zmienić jego stan. Użyj przycisku \"Zmiana trybu\", aby dodać kierunkowe sygnalizatory.\\n2. Ustaw minimalny oraz maksymalny czas aktualnego kroku. Po upłynięciu minimalnego czasu sygnalizacja zacznie zliczać i porównywać ilość nadjeżdżających pojazdów.\\n3. Opcjonalnie, wybierz tryb zmiany kroku. Domyślnie, krok zostaje zmieniony jeśli liczba poruszających się pojazdów jest dużo mniejsza niż oczekujących.\\n4. Opcjonalnie, możesz dostosować czułość. Na przykład, przesuń suwak w lewo by zmniejszyć czułość na oczekujące pojazdy. \nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy Kopiuj czasową sygnalizację\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy Kliknij na innym skrzyżowaniu, aby wkleić skopiowane ustawienie.\\n\\nKliknij gdziekolwiek prawym przyciskiem myszy, aby anulować operację.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction Dodaj skrzyżowanie do czasowej sygnalizacji \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction Kliknij na innym skrzyżowaniu, aby je dodać. Obydwa skrzyżowania zostaną połączone, ustawienia czasowej sygnalizacji będą działać na nich jednocześnie.\\n\\nKliknij gdziekolwiek prawym klawiszem myszy, aby anulować operację.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction Usuń skrzyżowanie spod kontroli czasowej sygnalizacji\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction Kliknij na jednym ze skrzyżować kontrolowanych przez czasową sygnalizację. Wybrane skrzyżowanie zostanie usunięte spod kontroli programu czasowej sygnalizacji.\\n\\nKliknij gdziekolwiek prawym klawiszem myszy, aby anulować operację.\nPublic_transport Transport publiczny\nPrevent_excessive_transfers_at_public_transport_stations Zapobiegaj nadmiernej liczbie przesiadek na przystankach transportu publicznego\nCompact_main_menu Kompaktowe menu główne\nWindow_transparency Przezroczystość okna\nOverlay_transparency Przezroczystość nakł.\nRemove_this_citizen Usuń tego mieszkańca\nShow_error_message_if_a_mod_incompatibility_is_detected Pokaż informację o błędzie jeśli wykryto niezgodność modów\nRemove_parked_vehicles Usuń zaparkowane pojazdy\nNode_is_level_crossing To jest przejazd kolejowy. \\nNie możesz tutaj wyłączyć sygnalizacji!\nExperimental_features Funkcje eksperymentalne\nTurn_on_red Skręć w prawo na czerwonym\nVehicles_may_turn_on_red Pojazdy mogą skręcić w prawo na czerwonym świetle\nAlso_apply_to_left/right_turns_between_one-way_streets Uwzględnia również skręt w lewo/prawo pomiędzy drogami jednokierunkowymi"
  },
  {
    "path": "TLM/TLM/Resources/lang_pt.txt",
    "content": "Switch_traffic_lights Interruptor de Semáforos\nAdd_priority_signs Placas de preferência \nManual_traffic_lights Semáforo manual\nTimed_traffic_lights Semáforos cronometrados \nChange_lane_arrows Alterar setas de pista\nClear_Traffic Limpar tráfego\nDisable_despawning Desativar despawning\nEnable_despawning Ativar despawning\nNODE_IS_LIGHT O cruzamento tem um semáforo.\\nExclua o semáforo, escolhendo \"Interruptor de Semáforos\" e clicando em seu cruzamento.\nNODE_IS_TIMED_LIGHT Esta junção é parte de um script cronometrado.\\nPrimeiro selecione \"Semáforos cronometrados\", selecione este cruzamento e clique em remover. \nSelect_nodes_windowTitle Selecione um cruzamento\nSelect_nodes Selecione cruzamento(s)\nNode Cruzamento\nDeselect_all_nodes Cancelar a seleção de todos os cruzamentos\nSetup_timed_traffic_light Criar semáforo cronometrado\nState Estado\nSkip Pular\nup cima\ndown baixo\nView Ver\nEdit Editar\nDelete Apagar\nTimed_traffic_lights_manager Gerenciador de semáforos cronometrados\nAdd_step Adicionar Passo\nRemove_timed_traffic_light Remover semáforo cronometrado\nMin._Time: Tempo Min.:\nMax._Time: Tempo Max.:\nSave Salvar\nAdd Adicionar\nSensitivity Sensibilidade\nVery_Low Muito Baixa\nLow Baixa\nMedium Média\nHigh Alta\nVery_high Muito Alta\nExtreme_long_green/red_phases Fases verde/vermelho extremamente longa\nVery_long_green/red_phases Fases verde/vermelho muito longa\nLong_green/red_phases Fases verde/vermelho longa\nModerate_green/red_phases Fases verde/vermelho moderada\nShort_green/red_phases Fases verde/vermelho curta\nVery_short_green/red_phases Fases verde/vermelho muito curta\nExtreme_short_green/red_phases Fases verde/vermelho extremamente curta\nHide_counters Ocultar contadores\nShow_counters Mostrar contadores\nStart Iniciar\nStop Parar\nEnable_test_mode_(stay_in_current_step) Ativar modo de teste (Mantém no passo atual)\navg._flow fluxo médio\navg._wait Espera média\nmin/max min/max\nLane Pista\nSet_Speed Setar velocidade {0}\nMax_speed Velocidade Máxima \nSegment Segmento\nincoming entrando\nEnable_Advanced_Vehicle_AI Ativar IA avançada de veículo \nVehicles_may_enter_blocked_junctions Veículos podem entrar em cruzamentos bloqueados\nAll_vehicles_may_ignore_lane_arrows Todos os veículos podem ignorar setas da pista\nBusses_may_ignore_lane_arrows ônibus podem ignorar setas da pista\nReckless_driving Direção imprudente (recurso BETA)\nTMPE_Title Traffic Manager: President Edition\nSettings_are_defined_for_each_savegame_separately As configurações são definidas para cada jogo salvo separadamente\nSimulation_accuracy A precisão da simulação (maior precisão reduz o desempenho)\nEnable_highway_specific_lane_merging/splitting_rules Habilitar regras de divisão e mesclagem de rodovia\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): Motoristas gostam de mudar sua faixa:\\n(só é aplicado se IA avançada de veiculo estiver habilitado)\nMaintenance Manunteção\nVery_often Muitas vezes\nOften Frequentemente\nSometimes Às vezes\nRarely Raramente\nVery_rarely Muito raramente\nOnly_if_necessary Apenas se necessário\nNodes_and_segments Cruzamentos e segmentos\nLanes Faixas\nPath_Of_Evil_(10_%) Caminho do mal (10 %)\nRush_Hour_(5_%) Hora do Rush (5 %)\nMinor_Complaints_(2_%) Reclamações de menor importância (2 %) \nHoly_City_(0_%) Cidade Santa (0 %)\nForget_toggled_traffic_lights Esquecer os semáforos alternados \nRoad_is_already_in_a_group! Pista já esta em um grupo!\nAll_selected_roads_must_be_of_the_same_type! Todas as pistas selecionadas devem ser do mesmo tipo!\nCreate_group Criar grupo\nDelete_group Apagar grupo\nAdd_zoning Adicionar zoneamento\nRemove_zoning Remover zoneamento\nLane_Arrow_Changer_Disabled_Highway O trocador de seta de pista esta desabilitado porque você ativou o sistema de regras para rodovia.\nAdd_junction_to_timed_light Adicionar cruzamento ao semáforo\nRemove_junction_from_timed_light Remover cruzamento do semáforo\nSelect_junction Selecionar cruzamento\nCancel Cancelar\nSpeed_limits Limite de Velocidade\nPersistently_visible_overlays Sobreposições persistentemente visíveis\nPriority_signs Placas de preferência \nVehicles_may_do_u-turns_at_junctions Veículos podem fazer retorno nos cruzamentos\nVehicles_going_straight_may_change_lanes_at_junctions Veículos seguindo reto pode mudar de faixa nos cruzamentos\nAllow_u-turns Permitir retornos\nAllow_lane_changing_for_vehicles_going_straight Permitir mudança de faixa para veículos seguindo reto\nAllow_vehicles_to_enter_a_blocked_junction Permitir entrada de veículos em cruzamentos bloqueados\nRoad_condition_has_a_bigger_impact_on_vehicle_speed Condição de estrada tem um impacto maior na velocidade do veículo\nVehicle_restrictions Restrições de veículos\nCopy Copiar\nPaste Colar\nInvert Inveter\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions Aplicar restrições de veículos para todos os segmentos de estrada entre dois cruzamentos\nAllow_all_vehicles Permitir todos os veículos\nBan_all_vehicles Proibir todos os veículos\nSet_all_traffic_lights_to_red Definir todos os semáforos para vermelho\nRotate_left Girar para a esquerda\nRotate_right Girar para a direita\nName Nome\nApply Aplicar\nSelect_a_timed_traffic_light_program Selecione um programa de semáforo cronometrado\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction O padrão de semáforo escolhido é incompatível com este cruzamento\nAdvanced_AI_cannot_be_activated IA avançada não pode ser ativada\nThe_Advanced_Vehicle_AI_cannot_be_activated a IA avançada não pode ser ativada porque você já esta usando outro mod que modifica o comportamento do veículo (Traffic++, Improved AI por exemplo)\nEnable_dynamic_path_calculation Ativar o cálculo de caminho dinâmico\nLane_Arrow_Changer_Disabled_Connection O trocador de seta de pista esta desabilitado para essa pista porque você criou uma conexão de pista manualmente.\nLane_connector Conector de pistas\nConnected_lanes Pistas conectadas\nUse_alternative_view_mode Usar modo de visão alternativo\nRoad_type Tipo de pista\nDefault_speed_limit Limite de velocidade padrão\nUnit_system Sistema de unidade\nMetric Metrico\nImperial Imperial\nUse_more_CPU_cores_for_route_calculation_if_available Usar mais nucleos de CPU para calcular a rota (se disponivel)\nActivated_features Recursos ativados\nJunction_restrictions Restrições de junção\nProhibit_spawning_of_pocket_cars Proibir o spawn de carros de bolso\nReset_stuck_cims_and_vehicles Resetar carros e cims presos\nDefault_speed_limits Limites de velocidade padrão\nLooking_for_a_parking_spot Procurando uma vaga de estacionamento\nDriving_to_a_parking_spot Dirigindo para uma vaga de estacionamento\nDriving_to_another_parking_spot Dirigindo para outra vaga de estacionamento\nEntering_vehicle Entrando no veículo\nWalking_to_car Caminhando para carro\nUsing_public_transport Usando transporte público\nWalking Caminhando\nThinking_of_a_good_parking_spot Pensando em uma boa vaga de estacionamento\nSwitch_view Trocar visão\nOutgoing_demand Demanda de saída\nIncoming_demand Demanda de entrada\nAdvanced_Vehicle_AI IA avançada de veículo\nHeavy_trucks_prefer_outer_lanes_on_highways Os veículos pesados preferem faixas externas nas rodovias\nParking_AI IA de estacionamento\nEnable_more_realistic_parking Habilitar estacionamento mais realista\nReset_custom_speed_limits Resetar limites de velocidade modificados\nReload_global_configuration Recarregar configuração global\nReset_global_configuration Resetar configuração global\nGeneral Geral\nGameplay Gameplay\nOverlays Sobreposições\nRealistic_speeds Velocidades realistas\nEvacuation_busses_may_ignore_traffic_rules Os ônibus de evacuação podem ignorar as regras de trânsito\nEvacuation_busses_may_only_be_used_to_reach_a_shelter Os ônibus de evacuação só podem ser usados para alcançar um abrigo\nVehicle_behavior Comportamento do veículo\nPolicies_&_Restrictions Políticas e Restrições\nAt_junctions Nas junções\nIn_case_of_emergency Em caso de emergência\nShow_lane-wise_speed_limits Mostrar limites de velocidade por faixa.\nLanguage Linguagem\nGame_language Linguagem do Jogo\nrequires_game_restart Requer reinicialização do jogo\nCustomizations_come_into_effect_instantaneously Personalizações entram em vigor instantaneamente\nOptions Opções\nLock_main_menu_button_position Travar posição do botão de menu principal\nLock_main_menu_position Travar posição do menu principal\nRecalculating_lane_routing Recalculando o roteamento de faixa\nPlease_wait Por favor, aguarde\nParking_restrictions Restrições de estacionamento\nSimulation Simulation\nOn_roads On roads\nBan_private_cars_and_trucks_on_bus_lanes Ban private cars and trucks on bus lanes\ndefault default\nflow_ratio flow ratio\nwait_ratio wait ratio\nAfter_min._time_has_elapsed_switch_to_next_step_if After min. time has elapsed, switch to next step if\nAdaptive_step_switching Adaptive step switching\nDynamic_lane_section Dynamic lane selection\nPercentage_of_vehicles_performing_dynamic_lane_section Percentage of vehicles performing dynamic lane selection\nVehicle_restrictions_aggression Vehicle restrictions aggression\nStrict Strict\nShow_path-find_stats Show path-find stats\nRemove_this_vehicle Remove this vehicle\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights Vehicles follow priority rules at junctions with timed traffic lights\nEnable_tutorial_messages Enable tutorial messages\nTMPE_TUTORIAL_HEAD_MainMenu Traffic Manager: President Edition\nTMPE_TUTORIAL_BODY_MainMenu Welcome to TM:PE!\\n\\nUser manual: http://www.viathinksoft.de/tmpe\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool Junction restrictions\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool Control how vehicles and pedestrians shall behave at junctions.\\n\\n1. Click on the junction you want to manage\\n2. Click on the appropriate icon to toggle restrictions.\\n\\nAvailable restrictions:\\n- Allow/Disallow lane changing for vehicle going straight on\\n- Allow/Disallow u-turns\\n- Allow/Disallow vehicles to enter a blocked junction\\n- Allow/disallow pedestrians to cross the street\nTMPE_TUTORIAL_HEAD_LaneArrowTool Lane arrows\nTMPE_TUTORIAL_BODY_LaneArrowTool Restrict the set of directions that vehicles are allowed to take.\\n\\n1. Click on a road segment next to a junction\\n2. Select the allowed directions.\nTMPE_TUTORIAL_HEAD_LaneConnectorTool Lane connector\nTMPE_TUTORIAL_BODY_LaneConnectorTool Connect two or more lanes with each other in order to tell which lanes vehicles may use.\\n\\n1. Click on a lane changing point (grey circles).\\n2. Click on a source marker.\\n3. Click on a target marker to connect it with the source marker.\\n4. Click anywhere with your secondary mouse button to return back to source marker selection.\\n\\nAvailable hotkeys:\\n\\n- Delete or Backspace: Remove all lane connections\\n- Shift + S: Cycle through all available \"stay on lane\" patterns\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool Manual traffic lights\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool Try out custom traffic lights.\\n\\n1. Click on a junction\\n2. Use the tool to control traffic lights.\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool Parking restrictions\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool Control where parking is allowed.\\n\\nClick on the \"P\" icons.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold while clicking to apply parking restrictions to multiple road segments at once\nTMPE_TUTORIAL_HEAD_PrioritySignsTool Priority signs\nTMPE_TUTORIAL_BODY_PrioritySignsTool Define priority rules at junctions.\\n\\n1. Click on a junction.\\n2. Click on the blank spots to cycle through the available priority signs (priority road, yield, stop).\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift to add priority signs to multiple road segments at once. Click again while holding Shift to cycle through all available patterns.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool Speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool Set up speed restrictions.\\n\\n1. In the window, click on the speed limit you want to set.\\n2. Click on a road segment to change the speed limit.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply speed limits to multiple road segments at once.\\n- Ctrl: Hold Ctrl to control speed limits per individual lane.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool Timed traffic lights\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool Set up timed traffic lights.\\n\\n1. Click on a junction.\\n2. In the window, click on \"Add step\".\\n3. Click on the overlay elements to set desired traffic lights states.\\n4. Click on \"Add\".\\n5. Repeat as desired.\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool Toggle traffic lights\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool Add or remove traffic lights to/from junctions.\\n\\nClick on a junction to toggle traffic lights.\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool Vehicle restrictions\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool Ban vehicles from certain road segments.\\n\\n1. Click on a road segment.\\n2. Click on the icons to toggle restrictions.\\n\\nDistinguished vehicle types:\\n\\n- Road vehicles: Passenger cars, busses, taxis, cargo trucks, service vehicles, emergency vehicles\\n- Rail vehicles: Passenger trains, cargo trains\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply restrictions to multiple road segments at once.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults Default speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. Use the arrows in the upper half to cycle through all road types.\\n2. In the lower half, select a speed limit.\\n3. Click on \"Save\" to set the selected speed limit as default for future road segments of the selected type. Click on \"Save & Apply\" to also update all existing roads of the selected type.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep Add a timed step \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. Within the in-game overlay, click on the traffic lights to change their state. Use the \"Change mode\" button to add directional traffic lights.\\n2. Enter both a minimum and maximum duration for the step. After the min. time has elapsed the traffic light will count and compare approaching vehicles.\\n3. Optionally, select a step switching type. Per default, the step changes if roughly less vehicles are driving than waiting.\\n4. Optionally, adjust the light's sensitivity. For example, move the slider to the left to make the timed traffic light less sensitive for waiting vehicles. \nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy Copy a timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy Click on another junction to paste the timed traffic light.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction Add a junction to the timed traffic light \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction Click on another junction to add it. Both lights will be joined such that the timed program will then control both junctions at once.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction Remove a junction from the timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction Click on one of the junctions that are controlled by this timed program. The selected traffic light will be removed such that the timed programm will no longer manage it.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nPublic_transport Public transport\nPrevent_excessive_transfers_at_public_transport_stations Prevent unnecessary transfers at public transport stations\nCompact_main_menu Compact main menu\nWindow_transparency Window transparency\nOverlay_transparency Overlay transparency\nRemove_this_citizen Remove this citizen\nShow_error_message_if_a_mod_incompatibility_is_detected Show error message if a mod incompatibility is detected\nRemove_parked_vehicles Remove parked vehicles\nNode_is_level_crossing This junction is a level crossing.\\nYou cannot disable traffic lights here.\nExperimental_features Experimental features\nTurn_on_red Virar nos semáforos vermelhos\nVehicles_may_turn_on_red Veículos podem virar nos semáforos vermelhos\nAlso_apply_to_left/right_turns_between_one-way_streets Also apply to left & right turns between one-way streets"
  },
  {
    "path": "TLM/TLM/Resources/lang_ru.txt",
    "content": "Switch_traffic_lights Установка светофора\nAdd_priority_signs Знаки приоритета\nManual_traffic_lights Ручной светофор\nTimed_traffic_lights Настраиваемый светофор\nChange_lane_arrows Стрелки движения\nClear_Traffic Очистить трафик\nDisable_despawning Выключить исчезновение машин\nEnable_despawning Включить исчезновение машин\nNODE_IS_LIGHT На перекрёстке работает светофор.\\nДля его удаления выберите \"Установка светофора\" и нажмите на этот узел.\nNODE_IS_TIMED_LIGHT Перекрёсток содержит скрипт настраиваемого светофора.\\nВыберите \"Настраиваемый светофор\", нажмите на этот узел и выберите \"Удалить\".\nSelect_nodes_windowTitle Список узлов\nSelect_nodes Выберите перекрёстки\nNode Узел\nDeselect_all_nodes Очистить список\nSetup_timed_traffic_light Настройка интервала времени\nState Статус\nSkip Пропустить\nup вверх\ndown вниз\nView Смотреть\nEdit Править\nDelete Удалить\nTimed_traffic_lights_manager Управление настраиваемым светофором\nAdd_step Добавить шаг\nRemove_timed_traffic_light Выключить настраиваемый светофор\nMin._Time: Мин. время:\nMax._Time: Макс. время:\nSave Сохранить\nAdd Добавить\nSensitivity Чувствительность\nVery_Low Очень медленно\nLow Замедленное\nMedium Среднее\nHigh Высокое\nVery_high Очень высоко\nExtreme_long_green/red_phases Слишком долгие фазы зеленый/красный\nVery_long_green/red_phases Очень долгие фазы зеленый/красный\nLong_green/red_phases Долгие фазы зеленый/красный\nModerate_green/red_phases Умеренные фазы зеленый/красный\nShort_green/red_phases Короткие фазы зеленый/красный\nVery_short_green/red_phases Очень короткие фазы зеленый/красный\nExtreme_short_green/red_phases Слишком короткие фазы зеленый/красный\nHide_counters Скрыть счётчики\nShow_counters Показать счётчики\nStart Начать\nStop Остановить\nEnable_test_mode_(stay_in_current_step) Тестовый режим (остаться в текущем шаге)\navg._flow средний поток\navg._wait среднее ожидание\nmin/max мин/макс\nLane Полоса\nSet_Speed Установка скорости {0}\nMax_speed Макс. скорость\nSegment Сегмент\nincoming входящее\nEnable_Advanced_Vehicle_AI Активировать продвинутый AI\nVehicles_may_enter_blocked_junctions Разрешить заезжать на заблокированные перекрёстки\nAll_vehicles_may_ignore_lane_arrows Весь транспорт может игнорировать стрелки движения\nBusses_may_ignore_lane_arrows Автобусы могут игнорировать стрелки движения\nReckless_driving Опасное вождение\nTMPE_Title Traffic Manager: President Edition\nSettings_are_defined_for_each_savegame_separately Настройки для каждого сохранения свои\nSimulation_accuracy Точность моделирования (высокое значение снижает производительность)\nEnable_highway_specific_lane_merging/splitting_rules Включить особые правила слияния/пересечения полос на шоссе\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): Водители могут перестраиваться на более свободные полосы (только при продвинутом AI):\nMaintenance Инструменты\nVery_often Очень часто\nOften Часто\nSometimes Иногда\nRarely Редко\nVery_rarely Очень редко\nOnly_if_necessary Только при необходимости\nNodes_and_segments Узлы и сегменты\nLanes Полосы\nPath_Of_Evil_(10_%) Дорога зла (10 %)\nRush_Hour_(5_%) Час пик (5 %)\nMinor_Complaints_(2_%) Немного жалоб (2 %)\nHoly_City_(0_%) Святой город (0 %)\nForget_toggled_traffic_lights Забыть расположение светофоров\nRoad_is_already_in_a_group! Дорога уже находится в группе!\nAll_selected_roads_must_be_of_the_same_type! Все выбранные дороги должны быть одинакового типа!\nCreate_group Создать группу\nDelete_group Удалить группу\nAdd_zoning Добавить зонирование\nRemove_zoning Удалить зонирование\nLane_Arrow_Changer_Disabled_Highway Изменение стрелки полосы для этого перекрестка отключено, по причине вашей активации системы правил шоссе.\nAdd_junction_to_timed_light Добавить перекрёсток в список\nRemove_junction_from_timed_light Убрать перекрёсток из списка\nSelect_junction Добавление перекрёстка в список\nCancel Отмена\nSpeed_limits Ограничение скорости\nPersistently_visible_overlays Постоянно видимые оверлеи\nPriority_signs Знаки приоритета\nVehicles_may_do_u-turns_at_junctions Транспорт может разворачиваться на перекрёстках\nVehicles_going_straight_may_change_lanes_at_junctions Транспорт, идущий прямо, может перестраиваться на соседнюю полосу\nAllow_u-turns Разрешить разворачиваться\nAllow_lane_changing_for_vehicles_going_straight Транспорт, идущий прямо, может менять полосу\nAllow_vehicles_to_enter_a_blocked_junction Разрешить заезжать на заблокированные перекрёстки\nRoad_condition_has_a_bigger_impact_on_vehicle_speed Состояние дороги оказывает большее влияние на скорость\nVehicle_restrictions Ограничения транспорта\nCopy Копировать\nPaste Вставить\nInvert Инвертировать\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions Ввести ограничения для транспорта на все участки дороги между двумя перекрёстками\nAllow_all_vehicles Разрешить всему транспорту\nBan_all_vehicles Запретить всему транспорту\nSet_all_traffic_lights_to_red Включить везде красный свет\nRotate_left Повернуть налево\nRotate_right Повернуть направо\nName Название\nApply Применить\nSelect_a_timed_traffic_light_program Выбор режима настраиваемого светофора\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction Выбранный режим светофора несовместим с данным перекрёстком\nAdvanced_AI_cannot_be_activated Продвинутый AI не может быть активирован по причине используемого вами другого мода, который изменяет поведение транспорта (например Improved AI, Traffic++).\nThe_Advanced_Vehicle_AI_cannot_be_activated Продвинутый AI не активирован, потому что вами уже используется иной мод, что меняет поведение транспорта (например Improved AI, Traffic++).\nEnable_dynamic_path_calculation Включить динамическое вычисление пути\nLane_Arrow_Changer_Disabled_Connection Переключатель стрелки движения для этого перекрёстка отключён, потому что вы создали соединение перекрёстка вручную.\nLane_connector Линии движения\nConnected_lanes Связанные маршруты\nUse_alternative_view_mode Альтернативный вид просмотра\nRoad_type Тип дороги\nDefault_speed_limit Ограничение скорости по-умолчанию\nUnit_system Системы единиц\nMetric Метрическая\nImperial Международная\nUse_more_CPU_cores_for_route_calculation_if_available Многоядерный процессор для вычисления маршрута (при наличии)\nActivated_features Активированные опции\nJunction_restrictions Ограничения на перекрёстках\nProhibit_spawning_of_pocket_cars Запрет появления \"карманных\"(из сумки) автомобилей\nReset_stuck_cims_and_vehicles Удаление застрявших пешеходов и транспорта\nDefault_speed_limits Ограничения скорости по умолчанию\nLooking_for_a_parking_spot Поиск места для парковки\nDriving_to_a_parking_spot Движение к месту парковки\nDriving_to_another_parking_spot Движение на другую парковку\nEntering_vehicle Посадка в транспорт\nWalking_to_car Идёт к автомобилю\nUsing_public_transport Использует общественный транспорт\nWalking Идёт\nThinking_of_a_good_parking_spot Думает о хорошем месте для парковки\nSwitch_view Переключение обзора\nOutgoing_demand Исходящее требование\nIncoming_demand Входящее требование\nAdvanced_Vehicle_AI Продвинутый AI транспорта\nHeavy_trucks_prefer_outer_lanes_on_highways Большегрузные траки предпочитают крайние полосы на шоссе\nParking_AI AI парковки\nEnable_more_realistic_parking Включить более реалистичную парковку\nReset_custom_speed_limits Сбросить свои ограничения скорости\nReload_global_configuration Перезагрузить общую конфигурацию\nReset_global_configuration Сбросить общую конфигурацию\nGeneral Общее\nGameplay Геймплей\nOverlays Оверлеи\nRealistic_speeds Реалистичные скорости\nEvacuation_busses_may_ignore_traffic_rules Автобусы эвакуации игнорируют правила движения\nEvacuation_busses_may_only_be_used_to_reach_a_shelter Эвакуационные автобусы, только чтобы добраться до аварийного убежища\nVehicle_behavior Поведение транспорта\nPolicies_&_Restrictions Политики и Ограничения\nAt_junctions На перекрёстках\nIn_case_of_emergency В случае крайней необходимости\nShow_lane-wise_speed_limits Показывать ограничения скорости по полосам\nLanguage Локализация\nGame_language Язык игры\nrequires_game_restart требуется перезапуск игры\nCustomizations_come_into_effect_instantaneously Настройки применяются сразу\nOptions Свойства\nLock_main_menu_button_position Блокировка позиции кнопки Главного меню\nLock_main_menu_position Блокировка позиции Главного меню\nRecalculating_lane_routing Обновление маршрута\nPlease_wait Подождите, пожалуйста\nParking_restrictions Ограничения парковки\nSimulation Моделирование\nOn_roads На дорогах\nBan_private_cars_and_trucks_on_bus_lanes Запрет частных автомобилей и грузовиков на автобусных дорогах\ndefault По умолчанию\nflow_ratio плотность движения\nwait_ratio плотность пробок\nAfter_min._time_has_elapsed_switch_to_next_step_if После небольшого ожидания, переключитесь на следующий шаг, если\nAdaptive_step_switching Адаптивное переключение ступеней\nDynamic_lane_section Выбор динамической полосы\nPercentage_of_vehicles_performing_dynamic_lane_section Процент транспортных средств с динамическим выбором полосы движения\nVehicle_restrictions_aggression Следование транспортным ограничениям\nStrict Строгое\nShow_path-find_stats Показать статистику поиска пути\nRemove_this_vehicle Удалить этот транспорт\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights Следование правилам приоритета на перекрёстках с настраиваемыми светофорами\nEnable_tutorial_messages Включить обучающие сообщения\nTMPE_TUTORIAL_HEAD_MainMenu Traffic Manager: President Edition\nTMPE_TUTORIAL_BODY_MainMenu Welcome to TM:PE!\\n\\nUser manual: http://www.viathinksoft.de/tmpe\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool Ограничения на перекрёстках\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool Контроль поведения транспортных средств и пешеходов на перекрёстках.\\n\\n1. Нажмите на перекрёсток, которым вы хотите управлять.\\n2. Нажмите на соответствующий значок, чтобы переключить ограничения.\\n\\nДоступные ограничения:\\n- Разрешить/Запретить смену полосы для автомобиля, идущего прямо\\n- Разрешить/запретить разворот\\n- Разрешить/Запретить въезд на заблокированный перекрёсток\\n- Разрешить/Запретить пешеходам переходить улицу\nTMPE_TUTORIAL_HEAD_LaneArrowTool Стрелки дорожной разметки\nTMPE_TUTORIAL_BODY_LaneArrowTool Ограничение направлений транспортных средств.\\n\\n1. Нажмите на сегмент дороги рядом с перекрестком.\\n2. Выберите разрешённые маршруты.\nTMPE_TUTORIAL_HEAD_LaneConnectorTool Линии движения\nTMPE_TUTORIAL_BODY_LaneConnectorTool Соедините две или более линии вместе, чтобы транспортные средства могли использовать разные полосы.\\n\\n1. Нажмите на точку смены полосы (серый круг).\\n2. Нажмите на маркер исходной линии.\\n3. Нажмите маркер цели, чтобы связать его с маркером источника.\\n4. Щёлкните в любом месте с помощью дополнительной кнопки мыши, чтобы вернуться к выбору маркера источника.\\n\\nДоступные горячие клавиши:\\n\\n- Delete or Backspace: Удалить все Линии движения\\n- Shift + S: Пролистайте все доступные шаблоны \"Остаться на полосе\"\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool Ручной светофор\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool Попробуйте настраивать светофоры.\\n\\n1. Нажмите на перекрёсток\\n2. Используйте инструмент для управления светофорами.\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool Ограничения парковки\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool Контролирование парковки.\\n\\nНажмите на значок \"P\".\\n\\nГорячая клавиша:\\n\\n- Shift: Удерживайте нажатой кнопку, чтобы применить ограничения на парковку для нескольких сегментов дороги\nTMPE_TUTORIAL_HEAD_PrioritySignsTool Знаки приоритета\nTMPE_TUTORIAL_BODY_PrioritySignsTool Определение правил приоритета на перекрёстках.\\n\\n1. Нажмите на перекрёсток.\\n2. Нажмите на пустые ячейки, чтобы посмотреть доступные знаки приоритета (Главная дорога, Второстепенная дорога, Stop).\\n\\nГорячая клавиша:\\n\\n- Shift: Удерживайте Shift, чтобы добавить знаки приоритета сразу нескольким сегментам дороги. Нажмите ещё раз, удерживая Shift, чтобы просмотреть все доступные шаблоны.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool Ограничение скорости\nTMPE_TUTORIAL_BODY_SpeedLimitsTool Настройка ограничений скорости.\\n\\n1. В окне нажмите на ограничение скорости, которое вы хотите установить.\\n2. Нажмите на сегмент дороги, чтобы изменить ограничение скорости.\\n\\nДоступные горячие клавиши:\\n\\n- Shift: Удерживайте нажатой клавишу Shift, чтобы одновременно применять ограничения скорости для нескольких сегментов дороги.\\n- Ctrl: Удерживайте Ctrl, чтобы контролировать ограничения скорости на каждую полосу.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool Настраиваемые светофоры\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool Настройка светофоров.\\n\\n1. Нажмите на перекрёсток. В окне нажмите \"Настройка интервала времени\"\\n2. В окне нажмите \"Добавить шаг\".\\n3. Нажмите на элементы наложения(оверлеи), чтобы установить требуемые режимы светофоров.\\n4. Нажмите \"Добавить\".\\n5. Повторите по необходимости.\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool Расположение светофоров\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool Добавление или удаление светофоров на перекрёстках.\\n\\nНажмите на перекрёсток для управления светофорами.\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool Ограничение движения типов транспорта\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool Разрешить или запретить типам транспорта использовать определённые сегменты дороги.\\n\\n1. Нажмите на сегмент дороги.\\n2. Нажмите на значки, чтобы переключить ограничения.\\n\\nИспользуемые типы транспортных средств:\\n\\n- Дорожные транспортные средства: легковые автомобили, автобусы, такси, грузовые автомобили, служебные транспортные средства, аварийные транспортные средства\\n- Железнодорожные транспортные средства: пассажирские поезда, грузовые поезда\\n\\nГорячая клавиша:\\n\\n- Shift: Удерживайте нажатой клавишу Shift, одновременно применяя ограничения для нескольких сегментов дороги.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults Стандартные ограничения скорости\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. Используйте клавиши со стрелками в верхней половине окна для переключения между всеми доступными сегментами дороги.\\n2. В нижней половине выберите ограничение скорости.\\n3. Нажмите \"Сохранить\", чтобы установить выбранный предел скорости по умолчанию для будущих сегментов дороги выбранного типа. Нажмите \"Сохранить и применить\", чтобы обновить все существующие дороги выбранного типа.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep Добавить временной шаг \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. Внутри игрового оверлея нажмите на светофоры, чтобы изменить их состояние. Используйте кнопку \"Изменить режим\", чтобы добавить стационарные светофоры.\\n2. Введите как минимальную, так и максимальную продолжительность для шага. Очень скоро светофор начнёт считать и сравнивать приближающиеся транспортные средства.\\n3. (опционально) Настройте адаптивное шаговое переключение. По умолчанию этот шаг изменяется когда, примерно, меньше транспортных средств пересекает перекрёсток, чем ожидающего транспорта.\\n4. При необходимости отрегулируйте чувствительность переключения. Например, переместите ползунок влево, чтобы сделать настраиваемый светофор менее чувствительным к ожидающему транспорту. \nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy Скопируйте настраиваемый светофор\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy Нажмите на другой перекрёсток, чтобы разместить настраиваемый светофор.\\n\\nЩёлкните в любом месте дополнительной кнопкой мышки, чтобы отменить операцию.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction Добавить другой перекрёсток в шаблон светофора \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction Нажмите на другой перекрёсток, чтобы добавить его. Программа с таймером будет контролировать светофоры на обоих перекрёстках одновременно.\\n\\nЩёлкните в любом месте дополнительной кнопкой мышки, чтобы отменить операцию.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction Удалите перекрёсток из схемы настраиваемого светофора\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction Нажмите на один из перекрёстков, которые контролируются этой программой времени. Выбранный светофор будет удален таким образом, что программа с таймером больше не будет управлять им.\\n\\nЩёлкните в любом месте дополнительной кнопкой мышки, чтобы отменить операцию.\nPublic_transport Общественный транспорт\nPrevent_excessive_transfers_at_public_transport_stations Предотвращение ненужных пересадок на общественном транспорте\nCompact_main_menu Компактное Главное меню\nWindow_transparency Прозрачность окна\nOverlay_transparency Прозрачность элементов(оверлеев) TM:PE\nRemove_this_citizen Удалить этого гражданина\nShow_error_message_if_a_mod_incompatibility_is_detected Показывать сообщение об ошибке при несовместимости мода\nRemove_parked_vehicles Удалить припаркованный транспорт\nNode_is_level_crossing Это перекрёсток уровней.\\nЗдесь нельзя отключить светофоры.\nExperimental_features Experimental features\nTurn_on_red Turn on red\nVehicles_may_turn_on_red Транспортные средства могут повернуть на красный светофор\nAlso_apply_to_left/right_turns_between_one-way_streets Also apply to left & right turns between one-way streets"
  },
  {
    "path": "TLM/TLM/Resources/lang_template.txt",
    "content": "Switch_traffic_lights\nAdd_priority_signs\nManual_traffic_lights\nTimed_traffic_lights\nChange_lane_arrows\nClear_Traffic\nDisable_despawning\nEnable_despawning\nNODE_IS_LIGHT\nNODE_IS_TIMED_LIGHT\nSelect_nodes_windowTitle\nSelect_nodes\nNode\nDeselect_all_nodes\nSetup_timed_traffic_light\nState\nSkip\nup\ndown\nView\nEdit\nDelete\nTimed_traffic_lights_manager\nAdd_step\nRemove_timed_traffic_light\nMin._Time:\nMax._Time:\nSave\nAdd\nSensitivity\nVery_Low\nLow\nMedium\nHigh\nVery_high\nExtreme_long_green/red_phases\nVery_long_green/red_phases\nLong_green/red_phases\nModerate_green/red_phases\nShort_green/red_phases\nVery_short_green/red_phases\nExtreme_short_green/red_phases\nHide_counters\nShow_counters\nStart\nStop\nEnable_test_mode_(stay_in_current_step)\navg._flow\navg._wait\nmin/max\nLane\nSet_Speed\nMax_speed\nSegment\nincoming\nEnable_Advanced_Vehicle_AI\nVehicles_may_enter_blocked_junctions\nAll_vehicles_may_ignore_lane_arrows\nBusses_may_ignore_lane_arrows\nReckless_driving\nTMPE_Title\nSettings_are_defined_for_each_savegame_separately\nSimulation_accuracy\nEnable_highway_specific_lane_merging/splitting_rules\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled):\nMaintenance\nVery_often\nOften\nSometimes\nRarely\nVery_rarely\nOnly_if_necessary\nNodes_and_segments\nLanes\nPath_Of_Evil_(10_%)\nRush_Hour_(5_%)\nMinor_Complaints_(2_%)\nHoly_City_(0_%)\nForget_toggled_traffic_lights\nRoad_is_already_in_a_group!\nAll_selected_roads_must_be_of_the_same_type!\nCreate_group\nDelete_group\nAdd_zoning\nRemove_zoning\nLane_Arrow_Changer_Disabled_Highway\nAdd_junction_to_timed_light\nRemove_junction_from_timed_light\nSelect_junction\nCancel\nSpeed_limits\nPersistently_visible_overlays\nPriority_signs\nVehicles_may_do_u-turns_at_junctions\nVehicles_going_straight_may_change_lanes_at_junctions\nAllow_u-turns\nAllow_lane_changing_for_vehicles_going_straight\nAllow_vehicles_to_enter_a_blocked_junction\nRoad_condition_has_a_bigger_impact_on_vehicle_speed\nVehicle_restrictions\nCopy\nPaste\nInvert\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions\nAllow_all_vehicles\nBan_all_vehicles\nSet_all_traffic_lights_to_red\nRotate_left\nRotate_right\nName\nApply\nSelect_a_timed_traffic_light_program\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction\nAdvanced_AI_cannot_be_activated\nThe_Advanced_Vehicle_AI_cannot_be_activated\nEnable_dynamic_path_calculation\nLane_Arrow_Changer_Disabled_Connection\nLane_connector\nConnected_lanes\nUse_alternative_view_mode\nRoad_type\nDefault_speed_limit\nUnit_system\nMetric\nImperial\nUse_more_CPU_cores_for_route_calculation_if_available\nActivated_features\nJunction_restrictions\nProhibit_spawning_of_pocket_cars\nReset_stuck_cims_and_vehicles\nDefault_speed_limits\nLooking_for_a_parking_spot\nDriving_to_a_parking_spot\nDriving_to_another_parking_spot\nEntering_vehicle\nWalking_to_car\nUsing_public_transport\nWalking\nThinking_of_a_good_parking_spot\nSwitch_view\nOutgoing_demand\nIncoming_demand\nAdvanced_Vehicle_AI\nHeavy_trucks_prefer_outer_lanes_on_highways\nParking_AI\nEnable_more_realistic_parking\nReset_custom_speed_limits\nReload_global_configuration\nReset_global_configuration\nGeneral\nGameplay\nOverlays\nRealistic_speeds\nEvacuation_busses_may_ignore_traffic_rules\nEvacuation_busses_may_only_be_used_to_reach_a_shelter\nVehicle_behavior\nPolicies_&_Restrictions\nAt_junctions\nIn_case_of_emergency\nShow_lane-wise_speed_limits\nLanguage\nGame_language\nrequires_game_restart\nCustomizations_come_into_effect_instantaneously\nOptions\nLock_main_menu_button_position\nLock_main_menu_position\nRecalculating_lane_routing\nPlease_wait\nParking_restrictions\nSimulation\nOn_roads\nBan_private_cars_and_trucks_on_bus_lanes\ndefault\nflow_ratio\nwait_ratio\nAfter_min._time_has_elapsed_switch_to_next_step_if\nAdaptive_step_switching\nDynamic_lane_section\nPercentage_of_vehicles_performing_dynamic_lane_section\nVehicle_restrictions_aggression\nStrict\nShow_path-find_stats\nRemove_this_vehicle\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights\nEnable_tutorial_messages\nTMPE_TUTORIAL_HEAD_MainMenu\nTMPE_TUTORIAL_BODY_MainMenu\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool\nTMPE_TUTORIAL_HEAD_LaneArrowTool\nTMPE_TUTORIAL_BODY_LaneArrowTool\nTMPE_TUTORIAL_HEAD_LaneConnectorTool\nTMPE_TUTORIAL_BODY_LaneConnectorTool\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool\nTMPE_TUTORIAL_HEAD_PrioritySignsTool\nTMPE_TUTORIAL_BODY_PrioritySignsTool\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool\nTMPE_TUTORIAL_BODY_SpeedLimitsTool\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep \nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction\nPublic_transport\nPrevent_excessive_transfers_at_public_transport_stations\nCompact_main_menu\nWindow_transparency\nOverlay_transparency\nRemove_this_citizen\nShow_error_message_if_a_mod_incompatibility_is_detected\nRemove_parked_vehicles\nNode_is_level_crossing\nExperimental_features\nTurn_on_red\nVehicles_may_turn_on_red\nAlso_apply_to_left/right_turns_between_one-way_streets"
  },
  {
    "path": "TLM/TLM/Resources/lang_zh-tw.txt",
    "content": "Switch_traffic_lights 增刪號誌燈\nAdd_priority_signs 增訂優先通行權\nManual_traffic_lights 手控號誌燈\nTimed_traffic_lights 號誌燈時相設定\nChange_lane_arrows 改變車道遵行方向\nClear_Traffic 消除所有車輛\nDisable_despawning 久塞車輛永不消失\nEnable_despawning 允許久塞車輛消失\nNODE_IS_LIGHT 具有預設燈號的道路交岔點。\\n選用「增刪號誌燈」並點選該道路交岔點來刪除號誌燈。\nNODE_IS_TIMED_LIGHT 具有自訂程序燈號的道路交岔點。\\n先選用「號誌燈時相設定」，再點選該道路交岔點並點選「移除」以移除設定。\nSelect_nodes_windowTitle 請點選道路交岔點\nSelect_nodes 請點選道路交岔點\nNode 道路交岔點\nDeselect_all_nodes 取消選擇所有道路交岔點\nSetup_timed_traffic_light 設定號誌燈時相\nState 狀態\nSkip 跳過\nup 上移\ndown 下移\nView 檢視\nEdit 編輯\nDelete 刪除\nTimed_traffic_lights_manager 號誌燈時相設定工具\nAdd_step 增加時相\nRemove_timed_traffic_light 移除號誌燈時相設定\nMin._Time: 最短時間\nMax._Time: 最長時間\nSave 儲存\nAdd 增加\nSensitivity 靈敏度\nVery_Low 非常低\nLow 低\nMedium 中\nHigh 高\nVery_high 非常高\nExtreme_long_green/red_phases 最長的紅、綠燈時相\nVery_long_green/red_phases 非常長的紅、綠燈時相\nLong_green/red_phases 稍長的紅、綠燈時相\nModerate_green/red_phases 中等的紅、綠燈時相\nShort_green/red_phases 稍短的紅、綠燈時相\nVery_short_green/red_phases 非常短的紅、綠燈時相\nExtreme_short_green/red_phases 最短的紅、綠燈時相\nHide_counters 隱藏讀秒\nShow_counters 顯示讀秒\nStart 啟用\nStop 停止\nEnable_test_mode_(stay_in_current_step) 允許測試模式（停留在當前時相）\navg._flow 平均通過\navg._wait 平均停等\nmin/max 最短/最長\nLane 車道\nSet_Speed 設定速限 {0}\nMax_speed 最高速限\nSegment 路段\nincoming 進入\nEnable_Advanced_Vehicle_AI 啟用加強版行車 AI\nVehicles_may_enter_blocked_junctions 塞車回堵時不用保持路口淨空。\nAll_vehicles_may_ignore_lane_arrows 所有車輛可忽略車道遵行方向\nBusses_may_ignore_lane_arrows 公車可忽略車道遵行方向\nReckless_driving 違規駕駛率（功能測試中）\nTMPE_Title 交通管理器：尊爵版\nSettings_are_defined_for_each_savegame_separately 每個存檔保有各自獨立的設定\nSimulation_accuracy 模擬精準度（較高的精準度會降低效能）\nEnable_highway_specific_lane_merging/splitting_rules 施行高速公路獨特的車道單邊出入規則\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): 駕駛人變換車道的習慣（僅適用於加強版 AI 啟用時）\nMaintenance 錯誤修復\nVery_often 很頻繁\nOften 經常\nSometimes 有時候\nRarely 偶爾\nVery_rarely 甚少\nOnly_if_necessary 僅必要時\nNodes_and_segments 道路交岔點和路段\nLanes 車道\nPath_Of_Evil_(10_%) 馬路我最大(10%)\nRush_Hour_(5_%) 上班趕時間(5%)\nMinor_Complaints_(2_%) 對不起借過(2%)\nHoly_City_(0_%) 守法乖寶寶(0%)\nForget_toggled_traffic_lights 放棄設定的交通燈號\nRoad_is_already_in_a_group! 道路已在連鎖群組中\nAll_selected_roads_must_be_of_the_same_type! 所有選擇的道路必須是同類型的\nCreate_group 創建連鎖群組\nDelete_group 刪除連鎖群組\nAdd_zoning 增加區劃\nRemove_zoning 移除區劃\nLane_Arrow_Changer_Disabled_Highway 改變車道遵行方向的功能已關閉，因為您已施行高速公路特規行駛系統\nAdd_junction_to_timed_light 將此交岔路口加入號誌燈時相連鎖\nRemove_junction_from_timed_light 將此交岔路口移出號誌燈時相連鎖\nSelect_junction 選取一處道路交叉點\nCancel 取消\nSpeed_limits 速限\nPersistently_visible_overlays 固定檢視的項目\nPriority_signs 優先通行權標誌\nVehicles_may_do_u-turns_at_junctions 車輛可在路口迴轉\nVehicles_going_straight_may_change_lanes_at_junctions 直行車可在交岔路口上變換車道\nAllow_u-turns 允許迴轉\nAllow_lane_changing_for_vehicles_going_straight 允許直行車變換車道\nAllow_vehicles_to_enter_a_blocked_junction 允許塞車回堵時不用保持路口淨空\nRoad_condition_has_a_bigger_impact_on_vehicle_speed 道路狀態對車速有較大的影響\nVehicle_restrictions 車種限定\nCopy 複製\nPaste 貼上\nInvert 對調選擇\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions 對兩個路口間的全部路段套用車種限定\nAllow_all_vehicles 通行所有車種\nBan_all_vehicles 禁行所有車種\nSet_all_traffic_lights_to_red 將所有號誌燈設為紅燈\nRotate_left 向左轉動\nRotate_right 向右轉動\nName 名稱\nApply 套用\nSelect_a_timed_traffic_light_program 請點選一個號誌燈的時相程序\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction 所選的號誌燈時相不適用於此交叉路口\nAdvanced_AI_cannot_be_activated 加強版 AI 無法啟動\nThe_Advanced_Vehicle_AI_cannot_be_activated 加強版車輛AI無法啟動，因為您所使用的其它車輛行控模組正作動中（例如 Improved AI 或 Traffic++）\nEnable_dynamic_path_calculation 啟用動態的路線運算\nLane_Arrow_Changer_Disabled_Connection 因為您已手動創建了車道的連接，故在此車道上無法使用改變遵行方向的功能\nLane_connector 車道連接工具\nConnected_lanes 已連接車道\nUse_alternative_view_mode 使用替代瀏覽模式\nRoad_type 道路類型\nDefault_speed_limit 預設道路速限\nUnit_system 單位制\nMetric 公制\nImperial 英制\nUse_more_CPU_cores_for_route_calculation_if_available 使用更多的CPU核心來做路線運算 (若有的話)\nActivated_features 已啟用的功能\nJunction_restrictions 交岔路口動線連接制定\nProhibit_spawning_of_pocket_cars 禁止行人憑空變出汽車移動\nReset_stuck_cims_and_vehicles 重置卡死的行人和車輛\nDefault_speed_limits 預設速限\nLooking_for_a_parking_spot 尋找停車位中\nDriving_to_a_parking_spot 開往停車位中\nDriving_to_another_parking_spot 開往另一處停車位中\nEntering_vehicle 上車中\nWalking_to_car 走向汽車中\nUsing_public_transport 使用大眾交通工具中\nWalking 走路中\nThinking_of_a_good_parking_spot 盤算一個好停車位中\nSwitch_view 切換檢視\nOutgoing_demand 出貨需求\nIncoming_demand 進貨需求\nAdvanced_Vehicle_AI 加強版行車AI\nHeavy_trucks_prefer_outer_lanes_on_highways 大型車在高速公路上優先行駛於外側車道\nParking_AI 道路停車AI\nEnable_more_realistic_parking 啟用更多現實中的停車位\nReset_custom_speed_limits 重置自訂速限\nReload_global_configuration 重新載入所有設定\nReset_global_configuration 重置所有設定\nGeneral 普通\nGameplay 遊戲\nOverlays 重疊\nRealistic_speeds 現實速度\nEvacuation_busses_may_ignore_traffic_rules 緊急疏散公車可忽略交通規則\nEvacuation_busses_may_only_be_used_to_reach_a_shelter 緊急疏散公車僅可用於到達避難所\nVehicle_behavior 車輛行為\nPolicies_&_Restrictions 政策與限制\nAt_junctions 位在交岔路口\nIn_case_of_emergency 在緊急狀況下\nShow_lane-wise_speed_limits 顯示車道速限\nLanguage 語言\nGame_language 遊戲顯示語言\nrequires_game_restart 需要重新啟動遊戲\nCustomizations_come_into_effect_instantaneously 自訂值即時生效\nOptions 選項\nLock_main_menu_button_position 鎖定主選單的選項位置\nLock_main_menu_position 鎖定主選單的位置\nRecalculating_lane_routing 重新規劃行駛路線\nPlease_wait 請稍候\nParking_restrictions 停車限制\nSimulation Simulation\nOn_roads On roads\nBan_private_cars_and_trucks_on_bus_lanes Ban private cars and trucks on bus lanes\ndefault default\nflow_ratio flow ratio\nwait_ratio wait ratio\nAfter_min._time_has_elapsed_switch_to_next_step_if After min. time has elapsed, switch to next step if\nAdaptive_step_switching Adaptive step switching\nDynamic_lane_section Dynamic lane selection\nPercentage_of_vehicles_performing_dynamic_lane_section Percentage of vehicles performing dynamic lane selection\nVehicle_restrictions_aggression Vehicle restrictions aggression\nStrict Strict\nShow_path-find_stats Show path-find stats\nRemove_this_vehicle Remove this vehicle\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights Vehicles follow priority rules at junctions with timed traffic lights\nEnable_tutorial_messages Enable tutorial messages\nTMPE_TUTORIAL_HEAD_MainMenu Traffic Manager: President Edition\nTMPE_TUTORIAL_BODY_MainMenu Welcome to TM:PE!\\n\\nUser manual: http://www.viathinksoft.de/tmpe\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool Junction restrictions\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool Control how vehicles and pedestrians shall behave at junctions.\\n\\n1. Click on the junction you want to manage\\n2. Click on the appropriate icon to toggle restrictions.\\n\\nAvailable restrictions:\\n- Allow/Disallow lane changing for vehicle going straight on\\n- Allow/Disallow u-turns\\n- Allow/Disallow vehicles to enter a blocked junction\\n- Allow/disallow pedestrians to cross the street\nTMPE_TUTORIAL_HEAD_LaneArrowTool Lane arrows\nTMPE_TUTORIAL_BODY_LaneArrowTool Restrict the set of directions that vehicles are allowed to take.\\n\\n1. Click on a road segment next to a junction\\n2. Select the allowed directions.\nTMPE_TUTORIAL_HEAD_LaneConnectorTool Lane connector\nTMPE_TUTORIAL_BODY_LaneConnectorTool Connect two or more lanes with each other in order to tell which lanes vehicles may use.\\n\\n1. Click on a lane changing point (grey circles).\\n2. Click on a source marker.\\n3. Click on a target marker to connect it with the source marker.\\n4. Click anywhere with your secondary mouse button to return back to source marker selection.\\n\\nAvailable hotkeys:\\n\\n- Delete or Backspace: Remove all lane connections\\n- Shift + S: Cycle through all available \"stay on lane\" patterns\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool Manual traffic lights\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool Try out custom traffic lights.\\n\\n1. Click on a junction\\n2. Use the tool to control traffic lights.\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool Parking restrictions\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool Control where parking is allowed.\\n\\nClick on the \"P\" icons.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold while clicking to apply parking restrictions to multiple road segments at once\nTMPE_TUTORIAL_HEAD_PrioritySignsTool Priority signs\nTMPE_TUTORIAL_BODY_PrioritySignsTool Define priority rules at junctions.\\n\\n1. Click on a junction.\\n2. Click on the blank spots to cycle through the available priority signs (priority road, yield, stop).\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift to add priority signs to multiple road segments at once. Click again while holding Shift to cycle through all available patterns.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool Speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool Set up speed restrictions.\\n\\n1. In the window, click on the speed limit you want to set.\\n2. Click on a road segment to change the speed limit.\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply speed limits to multiple road segments at once.\\n- Ctrl: Hold Ctrl to control speed limits per individual lane.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool Timed traffic lights\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool Set up timed traffic lights.\\n\\n1. Click on a junction.\\n2. In the window, click on \"Add step\".\\n3. Click on the overlay elements to set desired traffic lights states.\\n4. Click on \"Add\".\\n5. Repeat as desired.\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool Toggle traffic lights\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool Add or remove traffic lights to/from junctions.\\n\\nClick on a junction to toggle traffic lights.\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool Vehicle restrictions\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool Ban vehicles from certain road segments.\\n\\n1. Click on a road segment.\\n2. Click on the icons to toggle restrictions.\\n\\nDistinguished vehicle types:\\n\\n- Road vehicles: Passenger cars, busses, taxis, cargo trucks, service vehicles, emergency vehicles\\n- Rail vehicles: Passenger trains, cargo trains\\n\\nAvailable hotkeys:\\n\\n- Shift: Hold Shift while clicking to apply restrictions to multiple road segments at once.\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults Default speed limits\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. Use the arrows in the upper half to cycle through all road types.\\n2. In the lower half, select a speed limit.\\n3. Click on \"Save\" to set the selected speed limit as default for future road segments of the selected type. Click on \"Save & Apply\" to also update all existing roads of the selected type.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep Add a timed step \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. Within the in-game overlay, click on the traffic lights to change their state. Use the \"Change mode\" button to add directional traffic lights.\\n2. Enter both a minimum and maximum duration for the step. After the min. time has elapsed the traffic light will count and compare approaching vehicles.\\n3. Optionally, select a step switching type. Per default, the step changes if roughly less vehicles are driving than waiting.\\n4. Optionally, adjust the light's sensitivity. For example, move the slider to the left to make the timed traffic light less sensitive for waiting vehicles. \nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy Copy a timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy Click on another junction to paste the timed traffic light.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction Add a junction to the timed traffic light \nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction Click on another junction to add it. Both lights will be joined such that the timed program will then control both junctions at once.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction Remove a junction from the timed traffic light\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction Click on one of the junctions that are controlled by this timed program. The selected traffic light will be removed such that the timed programm will no longer manage it.\\n\\nClick anywhere with your secondary mouse button to cancel the operation.\nPublic_transport Public transport\nPrevent_excessive_transfers_at_public_transport_stations Prevent unnecessary transfers at public transport stations\nCompact_main_menu Compact main menu\nWindow_transparency Window transparency\nOverlay_transparency Overlay transparency\nRemove_this_citizen Remove this citizen\nShow_error_message_if_a_mod_incompatibility_is_detected Show error message if a mod incompatibility is detected\nRemove_parked_vehicles Remove parked vehicles\nNode_is_level_crossing This junction is a level crossing.\\nYou cannot disable traffic lights here.\nExperimental_features Experimental features\nTurn_on_red Turn on red\nVehicles_may_turn_on_red 車輛可能會在紅色交通燈處轉彎\nAlso_apply_to_left/right_turns_between_one-way_streets Also apply to left & right turns between one-way streets"
  },
  {
    "path": "TLM/TLM/Resources/lang_zh.txt",
    "content": "Switch_traffic_lights 红绿灯增删\nAdd_priority_signs 添加路口优先权\nManual_traffic_lights 手动控制红绿灯\nTimed_traffic_lights 红绿灯定时设置\nChange_lane_arrows 变更车道方向\nClear_Traffic 清除所有车辆\nDisable_despawning 禁止车辆消失\nEnable_despawning 允许车辆消失\nNODE_IS_LIGHT 路口有红绿灯。\\n请选择「红绿灯增删」，并点选该路口来删除红绿灯。\nNODE_IS_TIMED_LIGHT 路口有自定时红绿灯，\\n请选择「红绿灯定时设置」，再点选该路口并点选「删除」以删除设定。\nSelect_nodes_windowTitle 请选择路口\nSelect_nodes 请选择路口\nNode 路口\nDeselect_all_nodes 取消选择所选路口\nSetup_timed_traffic_light 设置红绿灯定时\nState 状态\nSkip 跳过\nup 往上\ndown 往下\nView 视图\nEdit 编辑\nDelete 删除\nTimed_traffic_lights_manager 红绿灯定时设置工具\nAdd_step 添加定时\nRemove_timed_traffic_light 删除红绿灯定时设置\nMin._Time: 最短时间(秒)\nMax._Time: 最长时间(秒)\nSave 保存\nAdd 添加\nSensitivity 灵敏度\nVery_Low 非常低\nLow 低\nMedium 中\nHigh 高\nVery_high 非常高\nExtreme_long_green/red_phases 极长的红绿灯时间\nVery_long_green/red_phases 非常长的红绿灯时间\nLong_green/red_phases 较长的红绿灯时间\nModerate_green/red_phases 适当的红绿灯时间\nShort_green/red_phases 较短的红绿灯时间\nVery_short_green/red_phases 非常短的红绿灯时间\nExtreme_short_green/red_phases 极短的红绿灯时间\nHide_counters 隐藏计数器\nShow_counters 显示计数器\nStart 启动\nStop 停止\nEnable_test_mode_(stay_in_current_step) 启用测试模式（保持当前步骤）\navg._flow 平均通行\navg._wait 平均等待\nmin/max 最短/最长\nLane 车道\nSet_Speed 设定限速 {0}\nMax_speed 最高限速\nSegment 路段\nincoming 进入\nEnable_Advanced_Vehicle_AI 启用高级车辆AI\nVehicles_may_enter_blocked_junctions 车辆可以进入堵塞路口加塞\nAll_vehicles_may_ignore_lane_arrows 所有车辆可忽略车道方向\nBusses_may_ignore_lane_arrows 公交车可忽略车道方向\nReckless_driving 违规驾驶\nTMPE_Title 交通管理：总统版\nSettings_are_defined_for_each_savegame_separately 设置于每个存档独立存储\\n(载入存档时会读取该存档中所存储的存档时TMPE的设置.建议载入存档后设置再存档,以后就不用再重复设置)\nSimulation_accuracy 模拟真实度（高真实度会影响性能）\nEnable_highway_specific_lane_merging/splitting_rules 实行高速公路特定车道合流/分离规则\nDrivers_want_to_change_lanes_(only_applied_if_Advanced_AI_is_enabled): 司机倾向变更车道（仅适用于高级AI启用时)\nMaintenance 维护\nVery_often 总是\nOften 经常\nSometimes 偶尔\nRarely 很少\nVery_rarely 极少\nOnly_if_necessary 仅在必要时\nNodes_and_segments 道路节点和段\nLanes 车道\nPath_Of_Evil_(10_%) 无视法纪 (10 %)\nRush_Hour_(5_%) 鲁莽驾驶 (5 %)\nMinor_Complaints_(2_%) 稍有违法 (2 %)\nHoly_City_(0_%) 遵纪守法 (0 %)\nForget_toggled_traffic_lights 重置所有红绿灯设置\nRoad_is_already_in_a_group! 道路已经在组合中\nAll_selected_roads_must_be_of_the_same_type! 所有选中的道路必须是同一类型的\nCreate_group 创建组合\nDelete_group 删除组合\nAdd_zoning 添加分区\nRemove_zoning 删除分区\nLane_Arrow_Changer_Disabled_Highway 不能改变这条车道的箭头因已启用高速公路特定规则\nAdd_junction_to_timed_light 向定时红绿灯添加一路口\nRemove_junction_from_timed_light 从定时红绿灯移除一路口\nSelect_junction 选择一路口\nCancel 取消\nSpeed_limits 道路限速设置\nPersistently_visible_overlays 持续可见的覆盖\nPriority_signs 优先通行权设置\nVehicles_may_do_u-turns_at_junctions 车辆可在路口掉头\nVehicles_going_straight_may_change_lanes_at_junctions 直行车辆可以在路口变更车道\nAllow_u-turns 允许车辆掉头\nAllow_lane_changing_for_vehicles_going_straight 允许直行车辆变更车道\nAllow_vehicles_to_enter_a_blocked_junction 允许车辆进入堵塞路口\nRoad_condition_has_a_bigger_impact_on_vehicle_speed 道路状况对车辆速度有较大影响\nVehicle_restrictions 道路车种限制\nCopy 复制\nPaste 粘贴\nInvert 反转\nApply_vehicle_restrictions_to_all_road_segments_between_two_junctions 应用车种限制设定至两个路口间所有路段\nAllow_all_vehicles 允许所有车种通行\nBan_all_vehicles 禁止所有车种通行\nSet_all_traffic_lights_to_red 将所有红绿灯设为红灯\nRotate_left 向左旋转\nRotate_right 向右旋转\nName 名称\nApply 应用\nSelect_a_timed_traffic_light_program 请选择一个红绿灯来设定定时\nThe_chosen_traffic_light_program_is_incompatible_to_this_junction 所选择的红绿灯模式不能用于此路口\nAdvanced_AI_cannot_be_activated 高级车辆AI无法启动\nThe_Advanced_Vehicle_AI_cannot_be_activated 高级车辆AI无法启动，因为您已启用其他车辆控制MOD\nEnable_dynamic_path_calculation 启用动态路径计算\nLane_Arrow_Changer_Disabled_Connection 车道箭头不可改变因为你已经手动创建车道连接\nLane_connector 车道连接工具\nConnected_lanes 已连接车道\nUse_alternative_view_mode 使用替代视图模式\nRoad_type 道路类型\nDefault_speed_limit 默认道路限速\nUnit_system 单元制度\nMetric 公制\nImperial 英制\nUse_more_CPU_cores_for_route_calculation_if_available 使用更多的CPU内核来计算路线(如果可用)\nActivated_features 启用功能\nJunction_restrictions 路口管制\nProhibit_spawning_of_pocket_cars 禁止袖珍车产生\nReset_stuck_cims_and_vehicles 重置卡住的行人和车辆\nDefault_speed_limits 默认速度限制\nLooking_for_a_parking_spot 寻找停车位\nDriving_to_a_parking_spot 行驶到停车位\nDriving_to_another_parking_spot 行驶到另一个停车位\nEntering_vehicle 进入车辆\nWalking_to_car 走向汽车\nUsing_public_transport 使用公共交通\nWalking 步行\nThinking_of_a_good_parking_spot 找一个好停车位\nSwitch_view 切换视图\nOutgoing_demand 出货需求\nIncoming_demand 进货需求\nAdvanced_Vehicle_AI 高级车辆AI\nHeavy_trucks_prefer_outer_lanes_on_highways 大型车辆在高速公路上优先行驶外侧车道\nParking_AI 停车AI\nEnable_more_realistic_parking 启用更真实的停车位\nReset_custom_speed_limits 重置自定义速度限制\nReload_global_configuration 重新加载所有设定\nReset_global_configuration 重置所有设定\nGeneral 常规\nGameplay 游戏\nOverlays 覆盖\nRealistic_speeds 真实车速\nEvacuation_busses_may_ignore_traffic_rules 应急疏散巴士可忽略交通规则\nEvacuation_busses_may_only_be_used_to_reach_a_shelter 应急疏散巴士仅可用于到达避难所\nVehicle_behavior 车辆行为\nPolicies_&_Restrictions 政策与限制\nAt_junctions 位于路口\nIn_case_of_emergency 在紧急情况下\nShow_lane-wise_speed_limits 显示道路各车道限速设置\nLanguage 语言\nGame_language 同游戏语言\nrequires_game_restart 重启后生效\nCustomizations_come_into_effect_instantaneously 订制模拟真实度将立即生效\nOptions 选项\nLock_main_menu_button_position 锁定主菜单按钮位置\nLock_main_menu_position 锁定主菜单位置\nRecalculating_lane_routing 重新计算车道路线\nPlease_wait 请稍候\nParking_restrictions 路边停车限制\nSimulation 模拟\nOn_roads 专用道路\nBan_private_cars_and_trucks_on_bus_lanes 禁止私家车和卡车驶入公交车专用道\ndefault 默认\nflow_ratio 通行比例\nwait_ratio 等候比例\nAfter_min._time_has_elapsed_switch_to_next_step_if 在设定最短时间过后，跳转至下一步骤，如果\nAdaptive_step_switching 渐进逐步切换\nDynamic_lane_section 智能车道选择\nPercentage_of_vehicles_performing_dynamic_lane_section 进行智能车道选择的车辆百分比\nVehicle_restrictions_aggression 车辆交规严格度\nStrict 严苛\nShow_path-find_stats 显示车辆寻找路线数据\nRemove_this_vehicle 移除此车辆\nVehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights 优先通行规则适用于带自定时红绿灯的路口\nEnable_tutorial_messages 启用教程信息\nTMPE_TUTORIAL_HEAD_MainMenu 交通管理：总统版\nTMPE_TUTORIAL_BODY_MainMenu 欢迎使用TM:PE！\\n\\n使用手册：http://www.viathinksoft.de/tmpe\nTMPE_TUTORIAL_HEAD_JunctionRestrictionsTool 路口管制\nTMPE_TUTORIAL_BODY_JunctionRestrictionsTool 控制车辆和行人在路口的行为。\\n\\n1. 单击你想要设置的路口\\n2. 单击合适的图标来切换限制\\n\\n可选限制（允许/不许）：\\n- 直行车辆在路口变更车道\\n- 车辆在路口掉头\\n- 车辆进入堵塞路口\\n- 行人横穿道路\nTMPE_TUTORIAL_HEAD_LaneArrowTool 变更车道方向\nTMPE_TUTORIAL_BODY_LaneArrowTool 限制车辆的可选方向。\\n\\n1. 单击路口前的路段\\n2. 选择允许的方向\nTMPE_TUTORIAL_HEAD_LaneConnectorTool 车道连接工具\nTMPE_TUTORIAL_BODY_LaneConnectorTool 连接2+条车道来告知车辆可用的车道。\\n\\n1. 单击一个车道变更点(灰色圆圈)\\n2. 单击一个来源车道\\n3. 单击目标车道来建立连接\\n4. 任意位置单击右键返回来源车道选择\\n\\n可用热键：\\n\\n- Delete or Backspace：移除所有车道连接\\n- Shift + S：(循环)查看所有\"stay on lane\"模式\nTMPE_TUTORIAL_HEAD_ManualTrafficLightsTool 手动控制红绿灯\nTMPE_TUTORIAL_BODY_ManualTrafficLightsTool 尝试自定时的红绿灯。\\n\\n1. 单击一个路口\\n2. 使用此工具来控制红绿灯\nTMPE_TUTORIAL_HEAD_ParkingRestrictionsTool 路边停车限制\nTMPE_TUTORIAL_BODY_ParkingRestrictionsTool 控制是否允许停车。\\n\\n单击\"P\"标志\\n\\n可用热键：\\n\\n- Shift：按住可一次性应用路边停车限制至多个路段\nTMPE_TUTORIAL_HEAD_PrioritySignsTool 优先通行权设置\nTMPE_TUTORIAL_BODY_PrioritySignsTool 设置路口的优先通行权。\\n\\n1. 单击一个路口\\n2. 单击空白点来(循环)选择可用的优先通行权(干道、减速让行、停车让行)\\n\\n可用热键：\\n\\n- Shift：按住可一次性应用优先通行权至多个路段;按住时单击可(循环)选择可用的优先通行权\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool 道路限速设置\nTMPE_TUTORIAL_BODY_SpeedLimitsTool 设置道路限速。\\n\\n1. 在窗口中单击你想要设置的限速\\n2. 单击一个路段来应用限速\\n\\n可用热键：\\n\\n- Shift：按住可一次性应用限速至多个路段\\n- Ctrl：按住来单独设置每条车道的限速\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool 红绿灯定时设置\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool 设置红绿灯定时。\\n\\n1. 单击一个路口\\n2. 在窗口中单击\"添加定时\"\\n3. 单击覆盖元素来设置所要的红绿灯状态\\n4. 单击\"添加\"\\n5. 按需重复\nTMPE_TUTORIAL_HEAD_ToggleTrafficLightsTool 红绿灯增删\nTMPE_TUTORIAL_BODY_ToggleTrafficLightsTool 对路口增加/删除红绿灯。\\n\\n单击一个路口来增删红绿灯\nTMPE_TUTORIAL_HEAD_VehicleRestrictionsTool 道路车种限制\nTMPE_TUTORIAL_BODY_VehicleRestrictionsTool 禁止车辆通过特定路段。\\n\\n1. 单击一个路段\\n2. 单击图标来切换限制\\n\\n车辆种类：\\n\\n- 公路车辆：客车、公交车、出租车、货车、服务车辆、应急车辆\\n- 轨道车辆：客运列车、货运列车\\n\\n可用热键：\\n\\n- Shift：按住可一次性应用车种限制至多个路段\nTMPE_TUTORIAL_HEAD_SpeedLimitsTool_Defaults 默认速度限制\nTMPE_TUTORIAL_BODY_SpeedLimitsTool_Defaults 1. 在上半部分使用箭头来(循环)选择所有的道路种类\\n2. 在下半部分选择限速\\n3. 单击\"保存\"来设置所选限速为新建该种类道路的默认;单击\"保存 & 应用\"来同时更新所选种类已有的道路\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddStep 添加定时\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddStep 1. 单击红绿灯来切换他们的状态;使用\"改变模式\"按钮来添加定向的红绿灯\\n2. 输入一个最小和最大区间：经过最小时间后红绿灯将统计并比较一段时间内的来车\\n3. 可选红绿灯变换类型：默认情况下通行车辆少于等待车辆时变换\\n4. 可选调整红绿灯的敏感度：例如移动滑条至左侧来使自定时红绿灯对等待车辆较不敏感\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_Copy 复制自定时红绿灯\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_Copy 单击另一个路口来粘贴。\\n\\n任意位置单击右键来取消\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_AddJunction 向自定时红绿灯添加一个路口\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_AddJunction 单击另一个路口来添加：两个红绿灯将被连接从而定时器将会同时控制两个路口\\n\\n任意位置单击右键来取消\nTMPE_TUTORIAL_HEAD_TimedTrafficLightsTool_RemoveJunction 从自定时红绿灯移除一个路口\nTMPE_TUTORIAL_BODY_TimedTrafficLightsTool_RemoveJunction 单击一个由该定时器控制的路口：所选的红绿灯将被移除从而该定时器将不再控制它\\n\\n任意位置单击右键来取消\nPublic_transport 公共交通\nPrevent_excessive_transfers_at_public_transport_stations 公共交通避免非必要换乘\nCompact_main_menu 紧凑主菜单\nWindow_transparency 窗口透明度\nOverlay_transparency 覆盖透明度\nRemove_this_citizen 移除此居民\nShow_error_message_if_a_mod_incompatibility_is_detected 检测到MOD不兼容时提示\nRemove_parked_vehicles 移除停放的车辆\nNode_is_level_crossing 此节点级别交汇，不可禁用红绿灯。\nExperimental_features Experimental features\nTurn_on_red Turn on red\nVehicles_may_turn_on_red 车辆红灯可转弯\nAlso_apply_to_left/right_turns_between_one-way_streets Also apply to left & right turns between one-way streets"
  },
  {
    "path": "TLM/TLM/State/ConfigData/AdvancedVehicleAI.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.State.ConfigData {\n\tpublic class AdvancedVehicleAI {\n\t\t/// <summary>\n\t\t/// Junction randomization for randomized lane selection\n\t\t/// </summary>\n\t\tpublic uint LaneRandomizationJunctionSel = 3;\n\n\t\t/// <summary>\n\t\t/// Cost factor for lane randomization\n\t\t/// </summary>\n\t\tpublic float LaneRandomizationCostFactor = 1f;\n\n\t\t/// <summary>\n\t\t/// minimum base lane changing cost\n\t\t/// </summary>\n\t\tpublic float LaneChangingBaseMinCost = 1.1f;\n\n\t\t/// <summary>\n\t\t/// maximum base lane changing cost\n\t\t/// </summary>\n\t\tpublic float LaneChangingBaseMaxCost = 1.5f;\n\n\t\t/// <summary>\n\t\t/// base cost for changing lanes in front of junctions\n\t\t/// </summary>\n\t\tpublic float LaneChangingJunctionBaseCost = 2f;\n\n\t\t/// <summary>\n\t\t/// base cost for traversing junctions\n\t\t/// </summary>\n\t\tpublic float JunctionBaseCost = 0.1f;\n\n\t\t/// <summary>\n\t\t/// > 1 lane changing cost factor\n\t\t/// </summary>\n\t\tpublic float MoreThanOneLaneChangingCostFactor = 2f;\n\n\t\t/// <summary>\n\t\t/// Relative factor for lane traffic cost calculation\n\t\t/// </summary>\n\t\tpublic float TrafficCostFactor = 4f;\n\n\t\t/// <summary>\n\t\t/// lane density random interval\n\t\t/// </summary>\n\t\tpublic float LaneDensityRandInterval = 20f;\n\n\t\t/// <summary>\n\t\t/// Threshold for resetting traffic buffer\n\t\t/// </summary>\n\t\tpublic uint MaxTrafficBuffer = 10;\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/State/ConfigData/Debug.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Traffic;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\n\nnamespace TrafficManager.State.ConfigData {\n#if DEBUG\n\tpublic class Debug {\n\t\tpublic bool[] Switches = {\n\t\t\t\tfalse, // 0: path-finding debug log\n\t\t\t\tfalse, // 1: routing basic debug log\n\t\t\t\tfalse, // 2: parking ai debug log (basic)\n\t\t\t\tfalse, // 3: do not actually repair stuck vehicles/cims, just report\n\t\t\t\tfalse, // 4: parking ai debug log (extended)\n\t\t\t\tfalse, // 5: geometry debug log\n\t\t\t\tfalse, // 6: debug parking AI distance issue\n\t\t\t\tfalse, // 7: debug TTL\n\t\t\t\tfalse, // 8: debug routing\n\t\t\t\tfalse, // 9: debug vehicle to segment end linking\n\t\t\t\tfalse, // 10: prevent routing recalculation on global configuration reload\n\t\t\t\tfalse, // 11: debug junction restrictions\n\t\t\t\tfalse, // 12: - unused -\n\t\t\t\tfalse, // 13: priority rules debug\n\t\t\t\tfalse, // 14: disable GUI overlay of citizens having a valid path\n\t\t\t\tfalse, // 15: disable checking of other vehicles for trams\n\t\t\t\tfalse, // 16: debug TramBaseAI.SimulationStep (2)\n\t\t\t\tfalse, // 17: debug alternative lane selection\n\t\t\t\tfalse, // 18: transport line path-find debugging\n\t\t\t\tfalse, // 19: enable obligation to drive on the right hand side of the road\n\t\t\t\tfalse, // 20: debug realistic public transport\n\t\t\t\tfalse, // 21: debug \"CalculateSegmentPosition\"\n\t\t\t\tfalse, // 22: parking ai debug log (vehicles)\n\t\t\t\tfalse, // 23: debug lane connections\n                false, // 24: debug resource loading\n\t\t\t\tfalse // 25: debug turn-on-red\n\t\t\t};\n\n\t\tpublic int NodeId = 0;\n\t\tpublic int SegmentId = 0;\n\t\tpublic int StartSegmentId = 0;\n\t\tpublic int EndSegmentId = 0;\n\t\tpublic int VehicleId = 0;\n\t\tpublic int CitizenInstanceId = 0;\n\t\tpublic uint CitizenId = 0;\n\t\tpublic uint SourceBuildingId = 0;\n\t\tpublic uint TargetBuildingId = 0;\n\t\tpublic ExtVehicleType ExtVehicleType = ExtVehicleType.None;\n\t\tpublic ExtPathMode ExtPathMode = ExtPathMode.None;\n\t}\n#endif\n}\n"
  },
  {
    "path": "TLM/TLM/State/ConfigData/DynamicLaneSelection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.State.ConfigData {\n\tpublic class DynamicLaneSelection {\n\t\t/// <summary>\n\t\t/// Maximum allowed reserved space on previous vehicle lane\n\t\t/// </summary>\n\t\tpublic float MaxReservedSpace = 0.5f;\n\n\t\t/// <summary>\n\t\t/// Maximum allowed reserved space on previous vehicle lane (for reckless drivers)\n\t\t/// </summary>\n\t\tpublic float MaxRecklessReservedSpace = 10f;\n\n\t\t/// <summary>\n\t\t/// Lane speed randomization interval\n\t\t/// </summary>\n\t\tpublic float LaneSpeedRandInterval = 5f;\n\n\t\t/// <summary>\n\t\t/// Maximum number of considered lane changes\n\t\t/// </summary>\n\t\tpublic int MaxOptLaneChanges = 2;\n\n\t\t/// <summary>\n\t\t/// Maximum allowed speed difference for safe lane changes\n\t\t/// </summary>\n\t\tpublic float MaxUnsafeSpeedDiff = 0.4f;\n\n\t\t/// <summary>\n\t\t/// Minimum required speed improvement for safe lane changes\n\t\t/// </summary>\n\t\tpublic float MinSafeSpeedImprovement = 25f;\n\n\t\t/// <summary>\n\t\t/// Minimum required traffic flow improvement for safe lane changes\n\t\t/// </summary>\n\t\tpublic float MinSafeTrafficImprovement = 20f;\n\n\t\t/// <summary>\n\t\t/// Minimum relative speed (in %) where volume measurement starts\n\t\t/// </summary>\n\t\tpublic ushort VolumeMeasurementRelSpeedThreshold = 50;\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/State/ConfigData/Main.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.UI.MainMenu;\n\nnamespace TrafficManager.State.ConfigData {\n\tpublic class Main {\n\t\t/// <summary>\n\t\t/// Main menu button position\n\t\t/// </summary>\n\t\tpublic int MainMenuButtonX = 464;\n\t\tpublic int MainMenuButtonY = 10;\n\t\tpublic bool MainMenuButtonPosLocked = false;\n\n\t\t/// <summary>\n\t\t/// Main menu position\n\t\t/// </summary>\n\t\tpublic int MainMenuX = MainMenuPanel.DEFAULT_MENU_X;\n\t\tpublic int MainMenuY = MainMenuPanel.DEFAULT_MENU_Y;\n\t\tpublic bool MainMenuPosLocked = false;\n\n\t\t/// <summary>\n\t\t/// Already displayed tutorial messages\n\t\t/// </summary>\n\t\tpublic string[] DisplayedTutorialMessages = new string[0];\n\n\t\t/// <summary>\n\t\t/// Determines if tutorial messages shall show up\n\t\t/// </summary>\n\t\tpublic bool EnableTutorial = true;\n\n\t\t/// <summary>\n\t\t/// Determines if the main menu shall be displayed in a tiny format\n\t\t/// </summary>\n\t\tpublic bool TinyMainMenu = true;\n\n\t\t/// <summary>\n\t\t/// User interface transparency\n\t\t/// </summary>\n\t\tpublic byte GuiTransparency = 30;\n\n\t\t/// <summary>\n\t\t/// Overlay transparency\n\t\t/// </summary>\n\t\tpublic byte OverlayTransparency = 40;\n\n\t\t/// <summary>\n\t\t/// Extended mod compatibility check\n\t\t/// </summary>\n\t\tpublic bool ShowCompatibilityCheckErrorMessage = false;\n\n\t\tpublic void AddDisplayedTutorialMessage(string messageKey) {\n\t\t\tHashSet<string> newMessages = DisplayedTutorialMessages != null ? new HashSet<string>(DisplayedTutorialMessages) : new HashSet<string>();\n\t\t\tnewMessages.Add(messageKey);\n\t\t\tDisplayedTutorialMessages = newMessages.ToArray();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/State/ConfigData/ParkingAI.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.State.ConfigData {\n\tpublic class ParkingAI {\n\t\t/// <summary>\n\t\t/// Target position randomization to allow opposite road-side parking\n\t\t/// </summary>\n\t\tpublic uint ParkingSpacePositionRand = 32;\n\n\t\t/// <summary>\n\t\t/// parking space search in vicinity is randomized. Cims do not always select the nearest parking space possible.\n\t\t/// A value of 1u always selects the nearest parking space.\n\t\t/// A value of 2u selects the nearest parking space with 50% chance, the next one with 25%, then 12.5% and so on.\n\t\t/// A value of 3u selects the nearest parking space with 66.67% chance, the next one with 22.22%, then 7.4% and so on.\n\t\t/// A value of 4u selects the nearest parking space with 75% chance, the next one with 18.75%, then 4.6875% and so on.\n\t\t/// A value of N selects the nearest parking space with (N-1)/N chance, the next one with (1-(N-1)/N)*(N-1)/N, then (1-(N-1)/N)^2*(N-1)/N and so on.\n\t\t/// </summary>\n\t\tpublic uint VicinityParkingSpaceSelectionRand = 4u;\n\n\t\t/// <summary>\n\t\t/// maximum number of parking attempts for passenger cars\n\t\t/// </summary>\n\t\tpublic int MaxParkingAttempts = 10;\n\n\t\t/// <summary>\n\t\t/// maximum required squared distance between citizen instance and parked vehicle before the parked car is turned into a vehicle\n\t\t/// </summary>\n\t\tpublic float MaxParkedCarInstanceSwitchSqrDistance = 6f;\n\n\t\t/// <summary>\n\t\t/// maximum distance between building and pedestrian lane\n\t\t/// </summary>\n\t\tpublic float MaxBuildingToPedestrianLaneDistance = 96f;\n\n\t\t/// <summary>\n\t\t/// Maximum allowed distance between home/source building and parked car when travelling home without forced to use the car\n\t\t/// </summary>\n\t\tpublic float MaxParkedCarDistanceToHome = 256f;\n\n\t\t/// <summary>\n\t\t/// minimum required distance between target building and parked car for using a car\n\t\t/// </summary>\n\t\tpublic float MaxParkedCarDistanceToBuilding = 512f;\n\n\t\t/// <summary>\n\t\t/// public transport demand increment on path-find failure\n\t\t/// </summary>\n\t\tpublic uint PublicTransportDemandIncrement = 10u;\n\n\t\t/// <summary>\n\t\t/// public transport demand increment if waiting time was exceeded\n\t\t/// </summary>\n\t\tpublic uint PublicTransportDemandWaitingIncrement = 3u;\n\n\t\t/// <summary>\n\t\t/// public transport demand decrement on simulation step\n\t\t/// </summary>\n\t\tpublic uint PublicTransportDemandDecrement = 1u;\n\n\t\t/// <summary>\n\t\t/// public transport demand decrement on path-find success\n\t\t/// </summary>\n\t\tpublic uint PublicTransportDemandUsageDecrement = 7u;\n\n\t\t/// <summary>\n\t\t/// parking space demand decrement on simulation step\n\t\t/// </summary>\n\t\tpublic uint ParkingSpaceDemandDecrement = 1u;\n\n\t\t/// <summary>\n\t\t/// minimum parking space demand delta when a passenger car could be spawned\n\t\t/// </summary>\n\t\tpublic int MinSpawnedCarParkingSpaceDemandDelta = -5;\n\n\t\t/// <summary>\n\t\t/// maximum parking space demand delta when a passenger car could be spawned\n\t\t/// </summary>\n\t\tpublic int MaxSpawnedCarParkingSpaceDemandDelta = 3;\n\n\t\t/// <summary>\n\t\t/// minimum parking space demand delta when a parking spot could be found\n\t\t/// </summary>\n\t\tpublic int MinFoundParkPosParkingSpaceDemandDelta = -5;\n\n\t\t/// <summary>\n\t\t/// maximum parking space demand delta when a parking spot could be found\n\t\t/// </summary>\n\t\tpublic int MaxFoundParkPosParkingSpaceDemandDelta = 3;\n\n\t\t/// <summary>\n\t\t/// parking space demand increment when no parking spot could be found while trying to park\n\t\t/// </summary>\n\t\tpublic uint FailedParkingSpaceDemandIncrement = 5u;\n\n\t\t/// <summary>\n\t\t/// parking space demand increment when no parking spot could be found while trying to spawn a parked vehicle\n\t\t/// </summary>\n\t\tpublic uint FailedSpawnParkingSpaceDemandIncrement = 10u;\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/State/ConfigData/PathFinding.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.State.ConfigData {\n\tpublic class PathFinding {\n\t\t/// <summary>\n\t\t/// penalty for busses not driving on bus lanes\n\t\t/// </summary>\n\t\tpublic float PublicTransportLanePenalty = 10f;\n\n\t\t/// <summary>\n\t\t/// reward for public transport staying on transport lane\n\t\t/// </summary>\n\t\tpublic float PublicTransportLaneReward = 0.1f;\n\n\t\t/// <summary>\n\t\t/// maximum penalty for heavy vehicles driving on an inner lane\n\t\t/// </summary>\n\t\tpublic float HeavyVehicleMaxInnerLanePenalty = 0.5f;\n\n\t\t/// <summary>\n\t\t/// Junction randomization for randomized lane selection\n\t\t/// </summary>\n\t\tpublic uint HeavyVehicleInnerLanePenaltySegmentSel = 3;\n\n\t\t/// <summary>\n\t\t/// artifical lane distance for vehicles that change to lanes which have an incompatible lane arrow configuration\n\t\t/// </summary>\n\t\tpublic byte IncompatibleLaneDistance = 2;\n\n\t\t/// <summary>\n\t\t/// artifical lane distance for u-turns\n\t\t/// </summary>\n\t\tpublic int UturnLaneDistance = 2;\n\n\t\t/// <summary>\n\t\t/// Maximum walking distance\n\t\t/// </summary>\n\t\tpublic float MaxWalkingDistance = 2500f;\n\n\t\t/// <summary>\n\t\t/// Minimum penalty for entering public transport vehicles\n\t\t/// </summary>\n\t\tpublic float PublicTransportTransitionMinPenalty = 0f;\n\n\t\t/// <summary>\n\t\t/// Maximum penalty for entering public transport vehicles\n\t\t/// </summary>\n\t\tpublic float PublicTransportTransitionMaxPenalty = 100f;\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/State/ConfigData/PriorityRules.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.State.ConfigData {\n\tpublic class PriorityRules {\n\t\t/// <summary>\n\t\t/// maximum incoming vehicle square distance to junction for priority signs\n\t\t/// </summary>\n\t\tpublic float MaxPriorityCheckSqrDist = 225f;\n\n\t\t/// <summary>\n\t\t/// maximum junction approach time for priority signs\n\t\t/// </summary>\n\t\tpublic float MaxPriorityApproachTime = 15f;\n\n\t\t/// <summary>\n\t\t/// maximum waiting time at priority signs\n\t\t/// </summary>\n\t\tpublic uint MaxPriorityWaitTime = 100;\n\n\t\t/// <summary>\n\t\t/// Maximum yield velocity\n\t\t/// </summary>\n\t\tpublic float MaxYieldVelocity = 2.5f;\n\n\t\t/// <summary>\n\t\t/// Maximum stop velocity\n\t\t/// </summary>\n\t\tpublic float MaxStopVelocity = 0.1f;\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/State/ConfigData/TimedTrafficLights.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.TrafficLight;\n\nnamespace TrafficManager.State.ConfigData {\n\tpublic class TimedTrafficLights {\n\t\t/// <summary>\n\t\t/// TTL wait/flow calculation mode\n\t\t/// </summary>\n\t\tpublic FlowWaitCalcMode FlowWaitCalcMode = FlowWaitCalcMode.Mean;\n\n\t\t/// <summary>\n\t\t/// Default TTL flow-to-wait ratio\n\t\t/// </summary>\n\t\tpublic float FlowToWaitRatio = 0.8f;\n\n\t\t/// <summary>\n\t\t/// TTL smoothing factor for flowing/waiting vehicles\n\t\t/// </summary>\n\t\tpublic float SmoothingFactor = 0.1f;\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/State/Configuration.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Runtime.Serialization;\nusing System.Xml.Serialization;\nusing TrafficManager.State;\n\n// TODO this class should be moved to TrafficManager.State, but the deserialization fails if we just do that now. Anyway, we should get rid of these crazy lists of arrays. So let's move the class when we decide rework the load/save system.\nnamespace TrafficManager {\n\t[Serializable]\n\tpublic class Configuration {\n\t\t[Serializable]\n\t\tpublic class LaneSpeedLimit {\n\t\t\tpublic uint laneId;\n\t\t\tpublic ushort speedLimit;\n\n\t\t\tpublic LaneSpeedLimit(uint laneId, ushort speedLimit) {\n\t\t\t\tthis.laneId = laneId;\n\t\t\t\tthis.speedLimit = speedLimit;\n\t\t\t}\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class LaneVehicleTypes {\n\t\t\tpublic uint laneId;\n\t\t\tpublic Traffic.ExtVehicleType vehicleTypes;\n\n\t\t\tpublic LaneVehicleTypes(uint laneId, Traffic.ExtVehicleType vehicleTypes) {\n\t\t\t\tthis.laneId = laneId;\n\t\t\t\tthis.vehicleTypes = vehicleTypes;\n\t\t\t}\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class TimedTrafficLights {\n\t\t\tpublic ushort nodeId;\n\t\t\tpublic List<ushort> nodeGroup;\n\t\t\tpublic bool started;\n\t\t\tpublic int currentStep;\n\t\t\tpublic List<TimedTrafficLightsStep> timedSteps;\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class TimedTrafficLightsStep {\n\t\t\tpublic int minTime;\n\t\t\tpublic int maxTime;\n\t\t\tpublic int changeMetric;\n\t\t\tpublic float waitFlowBalance;\n\t\t\tpublic Dictionary<ushort, CustomSegmentLights> segmentLights;\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class CustomSegmentLights {\n\t\t\tpublic ushort nodeId;\n\t\t\tpublic ushort segmentId;\n\t\t\tpublic Dictionary<Traffic.ExtVehicleType, CustomSegmentLight> customLights;\n\t\t\tpublic RoadBaseAI.TrafficLightState? pedestrianLightState;\n\t\t\tpublic bool manualPedestrianMode;\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class CustomSegmentLight {\n\t\t\tpublic ushort nodeId;\n\t\t\tpublic ushort segmentId;\n\t\t\tpublic int currentMode;\n\t\t\tpublic RoadBaseAI.TrafficLightState leftLight;\n\t\t\tpublic RoadBaseAI.TrafficLightState mainLight;\n\t\t\tpublic RoadBaseAI.TrafficLightState rightLight;\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class SegmentNodeConf {\n\t\t\tpublic ushort segmentId;\n\t\t\tpublic SegmentNodeFlags startNodeFlags = null;\n\t\t\tpublic SegmentNodeFlags endNodeFlags = null;\n\n\t\t\tpublic SegmentNodeConf(ushort segmentId) {\n\t\t\t\tthis.segmentId = segmentId;\n\t\t\t}\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class ParkingRestriction {\n\t\t\tpublic ushort segmentId;\n\t\t\tpublic bool forwardParkingAllowed;\n\t\t\tpublic bool backwardParkingAllowed;\n\n\t\t\tpublic ParkingRestriction(ushort segmentId) {\n\t\t\t\tthis.segmentId = segmentId;\n\t\t\t\tforwardParkingAllowed = true;\n\t\t\t\tbackwardParkingAllowed = true;\n\t\t\t}\n\t\t}\n\t\t\n\t\t[Serializable]\n\t\tpublic class SegmentNodeFlags {\n\t\t\tpublic bool? uturnAllowed = null;\n            public bool? turnOnRedAllowed = null; // controls near turns // TODO fix naming when the serialization system is updated\n\t\t\tpublic bool? farTurnOnRedAllowed = null;\n\t\t\tpublic bool? straightLaneChangingAllowed = null;\n\t\t\tpublic bool? enterWhenBlockedAllowed = null;\n\t\t\tpublic bool? pedestrianCrossingAllowed = null;\n\n\t\t\tpublic bool IsDefault() {\n\t\t\t\tbool uturnIsDefault = uturnAllowed == null || (bool)uturnAllowed == Options.allowUTurns;\n                bool turnOnRedIsDefault = turnOnRedAllowed == null || (bool)turnOnRedAllowed;\n\t\t\t\tbool farTurnOnRedIsDefault = farTurnOnRedAllowed == null || (bool)farTurnOnRedAllowed;\n\t\t\t\tbool straightChangeIsDefault = straightLaneChangingAllowed == null || (bool)straightLaneChangingAllowed == Options.allowLaneChangesWhileGoingStraight;\n\t\t\t\tbool enterWhenBlockedIsDefault = enterWhenBlockedAllowed == null || (bool)enterWhenBlockedAllowed == Options.allowEnterBlockedJunctions;\n\t\t\t\tbool pedCrossingIsDefault = pedestrianCrossingAllowed == null || (bool)pedestrianCrossingAllowed;\n\n\t\t\t\treturn uturnIsDefault && turnOnRedIsDefault && farTurnOnRedIsDefault && straightChangeIsDefault && enterWhenBlockedIsDefault && pedCrossingIsDefault;\n\t\t\t}\n\n\t\t\tpublic override string ToString() {\n\t\t\t\treturn $\"uturnAllowed={uturnAllowed}, turnOnRedAllowed={turnOnRedAllowed}, farTurnOnRedAllowed={farTurnOnRedAllowed}, straightLaneChangingAllowed={straightLaneChangingAllowed}, enterWhenBlockedAllowed={enterWhenBlockedAllowed}, pedestrianCrossingAllowed={pedestrianCrossingAllowed}\";\n\t\t\t}\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class LaneConnection {\n\t\t\tpublic uint lowerLaneId;\n\t\t\tpublic uint higherLaneId;\n\t\t\tpublic bool lowerStartNode;\n\n\t\t\tpublic LaneConnection(uint lowerLaneId, uint higherLaneId, bool lowerStartNode) {\n\t\t\t\tif (lowerLaneId >= higherLaneId)\n\t\t\t\t\tthrow new ArgumentException();\n\t\t\t\tthis.lowerLaneId = lowerLaneId;\n\t\t\t\tthis.higherLaneId = higherLaneId;\n\t\t\t\tthis.lowerStartNode = lowerStartNode;\n\t\t\t}\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class LaneArrowData {\n\t\t\tpublic uint laneId;\n\t\t\tpublic uint arrows;\n\n\t\t\tpublic LaneArrowData(uint laneId, uint arrows) {\n\t\t\t\tthis.laneId = laneId;\n\t\t\t\tthis.arrows = arrows;\n\t\t\t}\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class PrioritySegment {\n\t\t\tpublic ushort segmentId;\n\t\t\tpublic ushort nodeId;\n\t\t\tpublic int priorityType;\n\n\t\t\tpublic PrioritySegment(ushort segmentId, ushort nodeId, int priorityType) {\n\t\t\t\tthis.segmentId = segmentId;\n\t\t\t\tthis.nodeId = nodeId;\n\t\t\t\tthis.priorityType = priorityType;\n\t\t\t}\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class NodeTrafficLight {\n\t\t\tpublic ushort nodeId;\n\t\t\tpublic bool trafficLight;\n\n\t\t\tpublic NodeTrafficLight(ushort nodeId, bool trafficLight) {\n\t\t\t\tthis.nodeId = nodeId;\n\t\t\t\tthis.trafficLight = trafficLight;\n\t\t\t}\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class ExtCitizenInstanceData {\n\t\t\tpublic uint instanceId;\n\t\t\tpublic int pathMode;\n\t\t\tpublic int failedParkingAttempts;\n\t\t\tpublic ushort parkingSpaceLocationId;\n\t\t\tpublic int parkingSpaceLocation;\n\t\t\tpublic ushort parkingPathStartPositionSegment;\n\t\t\tpublic byte parkingPathStartPositionLane;\n\t\t\tpublic byte parkingPathStartPositionOffset;\n\t\t\tpublic uint returnPathId;\n\t\t\tpublic int returnPathState;\n\t\t\tpublic float lastDistanceToParkedCar;\n\n\t\t\tpublic ExtCitizenInstanceData(uint instanceId) {\n\t\t\t\tthis.instanceId = instanceId;\n\t\t\t\tpathMode = 0;\n\t\t\t\tfailedParkingAttempts = 0;\n\t\t\t\tparkingSpaceLocationId = 0;\n\t\t\t\tparkingSpaceLocation = 0;\n\t\t\t\tparkingPathStartPositionSegment = 0;\n\t\t\t\tparkingPathStartPositionLane = 0;\n\t\t\t\tparkingPathStartPositionOffset = 0;\n\t\t\t\treturnPathId = 0;\n\t\t\t\treturnPathState = 0;\n\t\t\t\tlastDistanceToParkedCar = 0;\n\t\t\t}\n\t\t}\n\n\t\t[Serializable]\n\t\tpublic class ExtCitizenData {\n\t\t\tpublic uint citizenId;\n\t\t\tpublic int lastTransportMode;\n\n\t\t\tpublic ExtCitizenData(uint citizenId) {\n\t\t\t\tthis.citizenId = citizenId;\n\t\t\t\tlastTransportMode = 0;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Stored ext. citizen data\n\t\t/// </summary>\n\t\tpublic List<ExtCitizenData> ExtCitizens = new List<ExtCitizenData>();\n\n\t\t/// <summary>\n\t\t/// Stored ext. citizen instance data\n\t\t/// </summary>\n\t\tpublic List<ExtCitizenInstanceData> ExtCitizenInstances = new List<ExtCitizenInstanceData>();\n\n\t\t/// <summary>\n\t\t/// Stored toggled traffic lights\n\t\t/// </summary>\n\t\tpublic List<NodeTrafficLight> ToggledTrafficLights = new List<NodeTrafficLight>();\n\n\t\t/// <summary>\n\t\t/// Stored lane connections\n\t\t/// </summary>\n\t\tpublic List<LaneConnection> LaneConnections = new List<LaneConnection>();\n\n\t\t/// <summary>\n\t\t/// Stored lane arrows\n\t\t/// </summary>\n\t\tpublic List<LaneArrowData> LaneArrows = new List<LaneArrowData>();\n\n\t\t/// <summary>\n\t\t/// Stored lane speed limits\n\t\t/// </summary>\n\t\tpublic List<LaneSpeedLimit> LaneSpeedLimits = new List<LaneSpeedLimit>();\n\n\t\t/// <summary>\n\t\t/// Stored vehicle restrictions\n\t\t/// </summary>\n\t\tpublic List<LaneVehicleTypes> LaneAllowedVehicleTypes = new List<LaneVehicleTypes>();\n\n\t\t/// <summary>\n\t\t/// Timed traffic lights\n\t\t/// </summary>\n\t\tpublic List<TimedTrafficLights> TimedLights = new List<Configuration.TimedTrafficLights>();\n\n\t\t/// <summary>\n\t\t/// Segment-at-Node configurations\n\t\t/// </summary>\n\t\tpublic List<SegmentNodeConf> SegmentNodeConfs = new List<SegmentNodeConf>();\n\n\t\t/// <summary>\n\t\t/// Custom default speed limits (in game speed units)\n\t\t/// </summary>\n\t\tpublic Dictionary<string, float> CustomDefaultSpeedLimits = new Dictionary<string, float>();\n\n\t\t/// <summary>\n\t\t/// Priority segments\n\t\t/// </summary>\n\t\tpublic List<PrioritySegment> CustomPrioritySegments = new List<PrioritySegment>();\n\n\t\t/// <summary>\n\t\t/// Parking restrictions\n\t\t/// </summary>\n\t\tpublic List<ParkingRestriction> ParkingRestrictions = new List<ParkingRestriction>();\n\n\t\t[Obsolete]\n\t\tpublic string NodeTrafficLights = \"\";\n\t\t[Obsolete]\n\t\tpublic string NodeCrosswalk = \"\";\n\t\t[Obsolete]\n\t\tpublic string LaneFlags = \"\";\n\n\t\t[Obsolete]\n\t\tpublic List<int[]> PrioritySegments = new List<int[]>();\n\t\t[Obsolete]\n\t\tpublic List<int[]> NodeDictionary = new List<int[]>();\n\t\t[Obsolete]\n\t\tpublic List<int[]> ManualSegments = new List<int[]>();\n\n\t\t[Obsolete]\n\t\tpublic List<int[]> TimedNodes = new List<int[]>();\n\t\t[Obsolete]\n\t\tpublic List<ushort[]> TimedNodeGroups = new List<ushort[]>();\n\t\t[Obsolete]\n\t\tpublic List<int[]> TimedNodeSteps = new List<int[]>();\n\t\t[Obsolete]\n\t\tpublic List<int[]> TimedNodeStepSegments = new List<int[]>();\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/State/Flags.cs",
    "content": "﻿#define DEBUGFLAGSx\n\nusing ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Threading;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Traffic;\nusing TrafficManager.Util;\n\nnamespace TrafficManager.State {\n\t[Obsolete]\n\tpublic class Flags {\n\t\t[Flags]\n\t\tpublic enum LaneArrows { // compatible with NetLane.Flags\n\t\t\tNone = 0,\n\t\t\tForward = 16,\n\t\t\tLeft = 32,\n\t\t\tRight = 64,\n\t\t\tLeftForward = 48,\n\t\t\tLeftRight = 96,\n\t\t\tForwardRight = 80,\n\t\t\tLeftForwardRight = 112\n\t\t}\n\n\t\tpublic enum LaneArrowChangeResult {\n\t\t\tInvalid,\n\t\t\tHighwayArrows,\n\t\t\tLaneConnection,\n\t\t\tSuccess\n\t\t}\n\n\t\tpublic static readonly uint lfr = (uint)NetLane.Flags.LeftForwardRight;\n\t\t\n\t\t/// <summary>\n\t\t/// For each lane: Defines the lane arrows which are set\n\t\t/// </summary>\n\t\tprivate static LaneArrows?[] laneArrowFlags = null;\n\n\t\t/// <summary>\n\t\t/// For each lane (by id): list of lanes that are connected with this lane by the T++ lane connector\n\t\t/// key 1: source lane id\n\t\t/// key 2: at start node?\n\t\t/// values: target lane id\n\t\t/// </summary>\n\t\tinternal static uint[][][] laneConnections = null;\n\n\t\t/// <summary>\n\t\t/// For each lane: Defines the currently set speed limit\n\t\t/// </summary>\n\t\tprivate static Dictionary<uint, ushort> laneSpeedLimit = null; // TODO remove\n\n\t\tinternal static ushort?[][] laneSpeedLimitArray; // for faster, lock-free access, 1st index: segment id, 2nd index: lane index\n\n\t\t/// <summary>\n\t\t/// For each lane: Defines the lane arrows which are set in highway rule mode (they are not saved)\n\t\t/// </summary>\n\t\tprivate static LaneArrows?[] highwayLaneArrowFlags = null;\n\n\t\t/// <summary>\n\t\t/// For each lane: Defines the allowed vehicle types\n\t\t/// </summary>\n\t\tinternal static ExtVehicleType?[][] laneAllowedVehicleTypesArray; // for faster, lock-free access, 1st index: segment id, 2nd index: lane index\n\n\t\tprivate static object laneSpeedLimitLock = new object();\n\n\t\tinternal static void PrintDebugInfo() {\n\t\t\tLog.Info(\"------------------------\");\n\t\t\tLog.Info(\"--- LANE ARROW FLAGS ---\");\n\t\t\tLog.Info(\"------------------------\");\n\t\t\tfor (uint i = 0; i < laneArrowFlags.Length; ++i) {\n\t\t\t\tif (highwayLaneArrowFlags[i] != null || laneArrowFlags[i] != null) {\n\t\t\t\t\tLog.Info($\"Lane {i}: valid? {Constants.ServiceFactory.NetService.IsLaneValid(i)}\");\n\t\t\t\t}\n\n\t\t\t\tif (highwayLaneArrowFlags[i] != null) {\n\t\t\t\t\tLog.Info($\"\\thighway arrows: {highwayLaneArrowFlags[i]}\");\n\t\t\t\t}\n\n\t\t\t\tif (laneArrowFlags[i] != null) {\n\t\t\t\t\tLog.Info($\"\\tcustom arrows: {laneArrowFlags[i]}\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tLog.Info(\"------------------------\");\n\t\t\tLog.Info(\"--- LANE CONNECTIONS ---\");\n\t\t\tLog.Info(\"------------------------\");\n\t\t\tfor (uint i = 0; i < laneConnections.Length; ++i) {\n\t\t\t\tif (laneConnections[i] == null)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tushort segmentId = Singleton<NetManager>.instance.m_lanes.m_buffer[i].m_segment;\n\t\t\t\tLog.Info($\"Lane {i}: valid? {Constants.ServiceFactory.NetService.IsLaneValid(i)}, seg. valid? {Constants.ServiceFactory.NetService.IsSegmentValid(segmentId)}\");\n\t\t\t\tfor (int x = 0; x < 2; ++x) {\n\t\t\t\t\tif (laneConnections[i][x] == null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tushort nodeId = x == 0 ? Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_startNode : Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_endNode;\n\t\t\t\t\tLog.Info($\"\\tNode idx {x} ({nodeId}, seg. {segmentId}): valid? {Constants.ServiceFactory.NetService.IsNodeValid(nodeId)}\");\n\n\t\t\t\t\tfor (int y = 0; y < laneConnections[i][x].Length; ++y) {\n\t\t\t\t\t\tif (laneConnections[i][x][y] == 0)\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\tLog.Info($\"\\t\\tEntry {y}: {laneConnections[i][x][y]} (valid? {Constants.ServiceFactory.NetService.IsLaneValid(laneConnections[i][x][y])})\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tLog.Info(\"-------------------------\");\n\t\t\tLog.Info(\"--- LANE SPEED LIMITS ---\");\n\t\t\tLog.Info(\"-------------------------\");\n\t\t\tfor (uint i = 0; i < laneSpeedLimitArray.Length; ++i) {\n\t\t\t\tif (laneSpeedLimitArray[i] == null)\n\t\t\t\t\tcontinue;\n\t\t\t\tLog.Info($\"Segment {i}: valid? {Constants.ServiceFactory.NetService.IsSegmentValid((ushort)i)}\");\n\t\t\t\tfor (int x = 0; x < laneSpeedLimitArray[i].Length; ++x) {\n\t\t\t\t\tif (laneSpeedLimitArray[i][x] == null)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tLog.Info($\"\\tLane idx {x}: {laneSpeedLimitArray[i][x]}\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tLog.Info(\"---------------------------------\");\n\t\t\tLog.Info(\"--- LANE VEHICLE RESTRICTIONS ---\");\n\t\t\tLog.Info(\"---------------------------------\");\n\t\t\tfor (uint i = 0; i < laneAllowedVehicleTypesArray.Length; ++i) {\n\t\t\t\tif (laneAllowedVehicleTypesArray[i] == null)\n\t\t\t\t\tcontinue;\n\t\t\t\tLog.Info($\"Segment {i}: valid? {Constants.ServiceFactory.NetService.IsSegmentValid((ushort)i)}\");\n\t\t\t\tfor (int x = 0; x < laneAllowedVehicleTypesArray[i].Length; ++x) {\n\t\t\t\t\tif (laneAllowedVehicleTypesArray[i][x] == null)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tLog.Info($\"\\tLane idx {x}: {laneAllowedVehicleTypesArray[i][x]}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t[Obsolete]\n\t\tpublic static bool mayHaveTrafficLight(ushort nodeId) {\n\t\t\tif (nodeId <= 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif ((Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_flags & (NetNode.Flags.Created | NetNode.Flags.Deleted)) != NetNode.Flags.Created) {\n\t\t\t\t//Log._Debug($\"Flags: Node {nodeId} may not have a traffic light (not created). flags={Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_flags}\");\n\t\t\t\tSingleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_flags &= ~NetNode.Flags.TrafficLights;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tItemClass connectionClass = Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].Info.GetConnectionClass();\n\t\t\tif ((Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_flags & NetNode.Flags.Junction) == NetNode.Flags.None &&\n\t\t\t\tconnectionClass.m_service != ItemClass.Service.PublicTransport\n\t\t\t\t) {\n\t\t\t\t//Log._Debug($\"Flags: Node {nodeId} may not have a traffic light (no junction or not public transport). flags={Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_flags} connectionClass={connectionClass?.m_service}\");\n\t\t\t\tSingleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_flags &= ~NetNode.Flags.TrafficLights;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (connectionClass == null ||\n\t\t\t\t(connectionClass.m_service != ItemClass.Service.Road &&\n\t\t\t\tconnectionClass.m_service != ItemClass.Service.PublicTransport)) {\n\t\t\t\t//Log._Debug($\"Flags: Node {nodeId} may not have a traffic light (no connection class). connectionClass={connectionClass?.m_service}\");\n\t\t\t\tSingleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_flags &= ~NetNode.Flags.TrafficLights;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\t[Obsolete]\n\t\tpublic static bool setNodeTrafficLight(ushort nodeId, bool flag) {\n\t\t\tif (nodeId <= 0)\n\t\t\t\treturn false;\n\n#if DEBUGFLAGS\n\t\t\tLog._Debug($\"Flags: Set node traffic light: {nodeId}={flag}\");\n#endif\n\n\t\t\tif (!mayHaveTrafficLight(nodeId)) {\n\t\t\t\t//Log.Warning($\"Flags: Refusing to add/delete traffic light to/from node: {nodeId} {flag}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tConstants.ServiceFactory.NetService.ProcessNode(nodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\tNetNode.Flags flags = node.m_flags | NetNode.Flags.CustomTrafficLights;\n\t\t\t\tif ((bool)flag) {\n#if DEBUGFLAGS\n\t\t\t\t\tLog._Debug($\"Adding traffic light @ node {nId}\");\n#endif\n\t\t\t\t\tflags |= NetNode.Flags.TrafficLights;\n\t\t\t\t} else {\n#if DEBUGFLAGS\n\t\t\t\t\tLog._Debug($\"Removing traffic light @ node {nId}\");\n#endif\n\t\t\t\t\tflags &= ~NetNode.Flags.TrafficLights;\n\t\t\t\t}\n\t\t\t\tnode.m_flags = flags;\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn true;\n\t\t}\n\n\t\t[Obsolete]\n\t\tinternal static bool isNodeTrafficLight(ushort nodeId) {\n\t\t\tif (nodeId <= 0)\n\t\t\t\treturn false;\n\n\t\t\tif ((Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_flags & (NetNode.Flags.Created | NetNode.Flags.Deleted)) != NetNode.Flags.Created)\n\t\t\t\treturn false;\n\n\t\t\treturn (Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_flags & NetNode.Flags.TrafficLights) != NetNode.Flags.None;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes lane connections that point from lane <paramref name=\"sourceLaneId\"/> to lane <paramref name=\"targetLaneId\"/> at node <paramref name=\"startNode\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"sourceLaneId\"></param>\n\t\t/// <param name=\"targetLaneId\"></param>\n\t\t/// <param name=\"startNode\"></param>\n\t\t/// <returns></returns>\n\t\tprivate static bool RemoveSingleLaneConnection(uint sourceLaneId, uint targetLaneId, bool startNode) {\n#if DEBUGFLAGS\n\t\t\tLog._Debug($\"Flags.CleanupLaneConnections({sourceLaneId}, {targetLaneId}, {startNode}) called.\");\n#endif\n\t\t\tint nodeArrayIndex = startNode ? 0 : 1;\n\n\t\t\tif (laneConnections[sourceLaneId] == null || laneConnections[sourceLaneId][nodeArrayIndex] == null)\n\t\t\t\treturn false;\n\n\t\t\tuint[] srcLaneConnections = laneConnections[sourceLaneId][nodeArrayIndex];\n\n\t\t\tbool ret = false;\n\t\t\tint remainingConnections = 0;\n\t\t\tfor (int i = 0; i < srcLaneConnections.Length; ++i) {\n\t\t\t\tif (srcLaneConnections[i] != targetLaneId) {\n\t\t\t\t\t++remainingConnections;\n\t\t\t\t} else {\n\t\t\t\t\tret = true;\n\t\t\t\t\tsrcLaneConnections[i] = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (remainingConnections <= 0) {\n\t\t\t\tlaneConnections[sourceLaneId][nodeArrayIndex] = null;\n\t\t\t\tif (laneConnections[sourceLaneId][1 - nodeArrayIndex] == null)\n\t\t\t\t\tlaneConnections[sourceLaneId] = null; // total cleanup\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\tif (remainingConnections != srcLaneConnections.Length) {\n\t\t\t\tlaneConnections[sourceLaneId][nodeArrayIndex] = new uint[remainingConnections];\n\t\t\t\tint k = 0;\n\t\t\t\tfor (int i = 0; i < srcLaneConnections.Length; ++i) {\n\t\t\t\t\tif (srcLaneConnections[i] == 0)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tlaneConnections[sourceLaneId][nodeArrayIndex][k++] = srcLaneConnections[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes any lane connections that exist between two given lanes\n\t\t/// </summary>\n\t\t/// <param name=\"lane1Id\"></param>\n\t\t/// <param name=\"lane2Id\"></param>\n\t\t/// <param name=\"startNode1\"></param>\n\t\t/// <returns></returns>\n\t\tinternal static bool RemoveLaneConnection(uint lane1Id, uint lane2Id, bool startNode1) {\n#if DEBUGCONN\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[23];\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"Flags.RemoveLaneConnection({lane1Id}, {lane2Id}, {startNode1}) called.\");\n#endif\n\t\t\tbool lane1Valid = CheckLane(lane1Id);\n\t\t\tbool lane2Valid = CheckLane(lane2Id);\n\n\t\t\tbool ret = false;\n\n\t\t\tif (! lane1Valid) {\n\t\t\t\t// remove all incoming/outgoing lane connections\n\t\t\t\tRemoveLaneConnections(lane1Id);\n\t\t\t\tret = true;\n\t\t\t}\n\t\t\t\n\t\t\tif (! lane2Valid) {\n\t\t\t\t// remove all incoming/outgoing lane connections\n\t\t\t\tRemoveLaneConnections(lane2Id);\n\t\t\t\tret = true;\n\t\t\t}\n\n\t\t\tif (lane1Valid || lane2Valid) {\n\t\t\t\tushort commonNodeId;\n\t\t\t\tbool startNode2;\n\n\t\t\t\tLaneConnectionManager.Instance.GetCommonNodeId(lane1Id, lane2Id, startNode1, out commonNodeId, out startNode2); // TODO refactor\n\t\t\t\tif (commonNodeId == 0) {\n\t\t\t\t\tLog.Warning($\"Flags.RemoveLaneConnection({lane1Id}, {lane2Id}, {startNode1}): Could not identify common node between lanes {lane1Id} and {lane2Id}\");\n\t\t\t\t}\n\n\t\t\t\tif (RemoveSingleLaneConnection(lane1Id, lane2Id, startNode1))\n\t\t\t\t\tret = true;\n\t\t\t\tif (RemoveSingleLaneConnection(lane2Id, lane1Id, startNode2))\n\t\t\t\t\tret = true;\n\t\t\t}\n\n#if DEBUGCONN\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"Flags.RemoveLaneConnection({lane1Id}, {lane2Id}, {startNode1}). ret={ret}\");\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Removes all incoming/outgoing lane connections of the given lane\n\t\t/// </summary>\n\t\t/// <param name=\"laneId\"></param>\n\t\t/// <param name=\"startNode\"></param>\n\t\tinternal static void RemoveLaneConnections(uint laneId, bool? startNode=null) {\n#if DEBUGCONN\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[23];\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"Flags.RemoveLaneConnections({laneId}, {startNode}) called. laneConnections[{laneId}]={laneConnections[laneId]}\");\n#endif\n\t\t\tif (laneConnections[laneId] == null)\n\t\t\t\treturn;\n\n\t\t\tbool laneValid = CheckLane(laneId);\n\t\t\tbool clearBothSides = startNode == null || !laneValid;\n#if DEBUGCONN\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"Flags.RemoveLaneConnections({laneId}, {startNode}): laneValid={laneValid}, clearBothSides={clearBothSides}\");\n#endif\n\t\t\tint? nodeArrayIndex = null;\n\t\t\tif (!clearBothSides) {\n\t\t\t\tnodeArrayIndex = (bool)startNode ? 0 : 1;\n\t\t\t}\n\n\t\t\tfor (int k = 0; k <= 1; ++k) {\n\t\t\t\tif (nodeArrayIndex != null && k != (int)nodeArrayIndex)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tbool startNode1 = k == 0;\n\n\t\t\t\tif (laneConnections[laneId][k] == null)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tfor (int i = 0; i < laneConnections[laneId][k].Length; ++i) {\n\t\t\t\t\tuint otherLaneId = laneConnections[laneId][k][i];\n\t\t\t\t\tushort commonNodeId;\n\t\t\t\t\tbool startNode2;\n\t\t\t\t\tLaneConnectionManager.Instance.GetCommonNodeId(laneId, otherLaneId, startNode1, out commonNodeId, out startNode2); // TODO refactor\n\t\t\t\t\tif (commonNodeId == 0) {\n\t\t\t\t\t\tLog.Warning($\"Flags.RemoveLaneConnections({laneId}, {startNode}): Could not identify common node between lanes {laneId} and {otherLaneId}\");\n\t\t\t\t\t}\n\n\t\t\t\t\tRemoveSingleLaneConnection(otherLaneId, laneId, startNode2);\n\t\t\t\t}\n\n\t\t\t\tlaneConnections[laneId][k] = null;\n\t\t\t}\n\n\t\t\tif (clearBothSides)\n\t\t\t\tlaneConnections[laneId] = null;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// adds lane connections between two given lanes\n\t\t/// </summary>\n\t\t/// <param name=\"lane1Id\"></param>\n\t\t/// <param name=\"lane2Id\"></param>\n\t\t/// <param name=\"startNode1\"></param>\n\t\t/// <returns></returns>\n\t\tinternal static bool AddLaneConnection(uint lane1Id, uint lane2Id, bool startNode1) {\n\t\t\tbool lane1Valid = CheckLane(lane1Id);\n\t\t\tbool lane2Valid = CheckLane(lane2Id);\n\n\t\t\tif (!lane1Valid) {\n\t\t\t\t// remove all incoming/outgoing lane connections\n\t\t\t\tRemoveLaneConnections(lane1Id);\n\t\t\t}\n\n\t\t\tif (!lane2Valid) {\n\t\t\t\t// remove all incoming/outgoing lane connections\n\t\t\t\tRemoveLaneConnections(lane2Id);\n\t\t\t}\n\n\t\t\tif (!lane1Valid || !lane2Valid)\n\t\t\t\treturn false;\n\n\t\t\tushort commonNodeId;\n\t\t\tbool startNode2;\n\t\t\tLaneConnectionManager.Instance.GetCommonNodeId(lane1Id, lane2Id, startNode1, out commonNodeId, out startNode2); // TODO refactor\n\n\t\t\tif (commonNodeId != 0) {\n\t\t\t\tCreateLaneConnection(lane1Id, lane2Id, startNode1);\n\t\t\t\tCreateLaneConnection(lane2Id, lane1Id, startNode2);\n\n\t\t\t\treturn true;\n\t\t\t} else\n\t\t\t\treturn false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds a lane connection from lane <paramref name=\"sourceLaneId\"/> to lane <paramref name=\"targetLaneId\"/> at node <paramref name=\"startNode\"/>\n\t\t/// Assumes that both lanes are valid.\n\t\t/// </summary>\n\t\t/// <param name=\"sourceLaneId\"></param>\n\t\t/// <param name=\"targetLaneId\"></param>\n\t\t/// <param name=\"startNode\"></param>\n\t\tprivate static void CreateLaneConnection(uint sourceLaneId, uint targetLaneId, bool startNode) {\n\t\t\tif (laneConnections[sourceLaneId] == null) {\n\t\t\t\tlaneConnections[sourceLaneId] = new uint[2][];\n\t\t\t}\n\n\t\t\tint nodeArrayIndex = startNode ? 0 : 1;\n\n\t\t\tif (laneConnections[sourceLaneId][nodeArrayIndex] == null) {\n\t\t\t\tlaneConnections[sourceLaneId][nodeArrayIndex] = new uint[] { targetLaneId };\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tuint[] oldConnections = laneConnections[sourceLaneId][nodeArrayIndex];\n\t\t\tlaneConnections[sourceLaneId][nodeArrayIndex] = new uint[oldConnections.Length + 1];\n\t\t\tArray.Copy(oldConnections, laneConnections[sourceLaneId][nodeArrayIndex], oldConnections.Length);\n\t\t\tlaneConnections[sourceLaneId][nodeArrayIndex][oldConnections.Length] = targetLaneId;\n\t\t}\n\n\t\tinternal static bool CheckLane(uint laneId) { // TODO refactor\n\t\t\tif (laneId <= 0)\n\t\t\t\treturn false;\n\t\t\tif (((NetLane.Flags)Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags & (NetLane.Flags.Created | NetLane.Flags.Deleted)) != NetLane.Flags.Created)\n\t\t\t\treturn false;\n\n\t\t\tushort segmentId = Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_segment;\n\t\t\tif (segmentId <= 0)\n\t\t\t\treturn false;\n\t\t\tif ((Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created)\n\t\t\t\treturn false;\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic static void setLaneSpeedLimit(uint laneId, ushort? speedLimit) {\n\t\t\tif (!CheckLane(laneId))\n\t\t\t\treturn;\n\n\t\t\tushort segmentId = Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_segment;\n\n\t\t\tNetInfo segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\t\t\tuint curLaneId = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tuint laneIndex = 0;\n\t\t\twhile (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) {\n\t\t\t\tif (curLaneId == laneId) {\n\t\t\t\t\tsetLaneSpeedLimit(segmentId, laneIndex, laneId, speedLimit);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlaneIndex++;\n\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t}\n\t\t}\n\n\t\tpublic static void removeLaneSpeedLimit(uint laneId) {\n\t\t\tsetLaneSpeedLimit(laneId, null);\n\t\t}\n\n\t\tpublic static void setLaneSpeedLimit(ushort segmentId, uint laneIndex, ushort speedLimit) {\n\t\t\tif (segmentId <= 0 || laneIndex < 0)\n\t\t\t\treturn;\n\t\t\tif ((Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created)\n\t\t\t\treturn;\n\t\t\tNetInfo segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\t\t\tif (laneIndex >= segmentInfo.m_lanes.Length) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// find the lane id\n\t\t\tuint laneId = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tfor (int i = 0; i < laneIndex; ++i) {\n\t\t\t\tif (laneId == 0)\n\t\t\t\t\treturn; // no valid lane found\n\t\t\t\tlaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_nextLane;\n\t\t\t}\n\n\t\t\tsetLaneSpeedLimit(segmentId, laneIndex, laneId, speedLimit);\n\t\t}\n\n\t\tpublic static void setLaneSpeedLimit(ushort segmentId, uint laneIndex, uint laneId, ushort? speedLimit) {\n\t\t\tif (segmentId <= 0 || laneIndex < 0 || laneId <= 0)\n\t\t\t\treturn;\n\t\t\tif ((Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created)\n\t\t\t\treturn;\n\t\t\tif (((NetLane.Flags)Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags & (NetLane.Flags.Created | NetLane.Flags.Deleted)) != NetLane.Flags.Created)\n\t\t\t\treturn;\n\t\t\tNetInfo segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\t\t\tif (laneIndex >= segmentInfo.m_lanes.Length) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(laneSpeedLimitLock);\n#if DEBUGFLAGS\n\t\t\t\tLog._Debug($\"Flags.setLaneSpeedLimit: setting speed limit of lane index {laneIndex} @ seg. {segmentId} to {speedLimit}\");\n#endif\n\n\t\t\t\tif (speedLimit == null) {\n\t\t\t\t\tlaneSpeedLimit.Remove(laneId);\n\n\t\t\t\t\tif (laneSpeedLimitArray[segmentId] == null)\n\t\t\t\t\t\treturn;\n\t\t\t\t\tif (laneIndex >= laneSpeedLimitArray[segmentId].Length)\n\t\t\t\t\t\treturn;\n\t\t\t\t\tlaneSpeedLimitArray[segmentId][laneIndex] = null;\n\t\t\t\t} else {\n\t\t\t\t\tlaneSpeedLimit[laneId] = (ushort)speedLimit;\n\n\t\t\t\t\t// save speed limit into the fast-access array.\n\t\t\t\t\t// (1) ensure that the array is defined and large enough\n\t\t\t\t\tif (laneSpeedLimitArray[segmentId] == null) {\n\t\t\t\t\t\tlaneSpeedLimitArray[segmentId] = new ushort?[segmentInfo.m_lanes.Length];\n\t\t\t\t\t} else if (laneSpeedLimitArray[segmentId].Length < segmentInfo.m_lanes.Length) {\n\t\t\t\t\t\tvar oldArray = laneSpeedLimitArray[segmentId];\n\t\t\t\t\t\tlaneSpeedLimitArray[segmentId] = new ushort?[segmentInfo.m_lanes.Length];\n\t\t\t\t\t\tArray.Copy(oldArray, laneSpeedLimitArray[segmentId], oldArray.Length);\n\t\t\t\t\t}\n\t\t\t\t\t// (2) insert the custom speed limit\n\t\t\t\t\tlaneSpeedLimitArray[segmentId][laneIndex] = speedLimit;\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(laneSpeedLimitLock);\n\t\t\t}\n\t\t}\n\n\t\tpublic static void setLaneAllowedVehicleTypes(uint laneId, ExtVehicleType vehicleTypes) {\n\t\t\tif (laneId <= 0)\n\t\t\t\treturn;\n\t\t\tif (((NetLane.Flags)Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags & (NetLane.Flags.Created | NetLane.Flags.Deleted)) != NetLane.Flags.Created)\n\t\t\t\treturn;\n\n\t\t\tushort segmentId = Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_segment;\n\t\t\tif (segmentId <= 0)\n\t\t\t\treturn;\n\t\t\tif ((Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created)\n\t\t\t\treturn;\n\n\t\t\tNetInfo segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\t\t\tuint curLaneId = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tuint laneIndex = 0;\n\t\t\twhile (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) {\n\t\t\t\tif (curLaneId == laneId) {\n\t\t\t\t\tsetLaneAllowedVehicleTypes(segmentId, laneIndex, laneId, vehicleTypes);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlaneIndex++;\n\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t}\n\t\t}\n\n\t\tpublic static void setLaneAllowedVehicleTypes(ushort segmentId, uint laneIndex, uint laneId, ExtVehicleType vehicleTypes) {\n\t\t\tif (segmentId <= 0 || laneIndex < 0 || laneId <= 0)\n\t\t\t\treturn;\n\t\t\tif ((Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created)\n\t\t\t\treturn;\n\t\t\tif (((NetLane.Flags)Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags & (NetLane.Flags.Created | NetLane.Flags.Deleted)) != NetLane.Flags.Created)\n\t\t\t\treturn;\n\t\t\tNetInfo segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\t\t\tif (laneIndex >= segmentInfo.m_lanes.Length) {\n\t\t\t\treturn;\n\t\t\t}\n\n#if DEBUGFLAGS\n\t\t\tLog._Debug($\"Flags.setLaneAllowedVehicleTypes: setting allowed vehicles of lane index {laneIndex} @ seg. {segmentId} to {vehicleTypes.ToString()}\");\n#endif\n\n\t\t\t// save allowed vehicle types into the fast-access array.\n\t\t\t// (1) ensure that the array is defined and large enough\n\t\t\tif (laneAllowedVehicleTypesArray[segmentId] == null) {\n\t\t\t\tlaneAllowedVehicleTypesArray[segmentId] = new ExtVehicleType?[segmentInfo.m_lanes.Length];\n\t\t\t} else if (laneAllowedVehicleTypesArray[segmentId].Length < segmentInfo.m_lanes.Length) {\n\t\t\t\tvar oldArray = laneAllowedVehicleTypesArray[segmentId];\n\t\t\t\tlaneAllowedVehicleTypesArray[segmentId] = new ExtVehicleType?[segmentInfo.m_lanes.Length];\n\t\t\t\tArray.Copy(oldArray, laneAllowedVehicleTypesArray[segmentId], oldArray.Length);\n\t\t\t}\n\t\t\t// (2) insert the custom speed limit\n\t\t\tlaneAllowedVehicleTypesArray[segmentId][laneIndex] = vehicleTypes;\n\t\t}\n\n\t\tpublic static void resetSegmentVehicleRestrictions(ushort segmentId) {\n\t\t\tif (segmentId <= 0)\n\t\t\t\treturn;\n#if DEBUGFLAGS\n\t\t\tLog._Debug($\"Flags.resetSegmentVehicleRestrictions: Resetting vehicle restrictions of segment {segmentId}.\");\n#endif\n\t\t\tlaneAllowedVehicleTypesArray[segmentId] = null;\n\t\t}\n\n\t\tpublic static void resetSegmentArrowFlags(ushort segmentId) {\n\t\t\tif (segmentId <= 0)\n\t\t\t\treturn;\n#if DEBUGFLAGS\n\t\t\tLog._Debug($\"Flags.resetSegmentArrowFlags: Resetting lane arrows of segment {segmentId}.\");\n#endif\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tNetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info;\n\n\t\t\tuint curLaneId = netManager.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tint numLanes = segmentInfo.m_lanes.Length;\n\t\t\tint laneIndex = 0;\n\t\t\twhile (laneIndex < numLanes && curLaneId != 0u) {\n#if DEBUGFLAGS\n\t\t\t\tLog._Debug($\"Flags.resetSegmentArrowFlags: Resetting lane arrows of segment {segmentId}: Resetting lane {curLaneId}.\");\n#endif\n\t\t\t\tlaneArrowFlags[curLaneId] = null;\n\n\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t++laneIndex;\n\t\t\t}\n\t\t}\n\n\t\tpublic static bool setLaneArrowFlags(uint laneId, LaneArrows flags, bool overrideHighwayArrows=false) {\n#if DEBUGFLAGS\n\t\t\tLog._Debug($\"Flags.setLaneArrowFlags({laneId}, {flags}, {overrideHighwayArrows}) called\");\n#endif\n\n\t\t\tif (!mayHaveLaneArrows(laneId)) {\n#if DEBUGFLAGS\n\t\t\t\tLog._Debug($\"Flags.setLaneArrowFlags({laneId}, {flags}, {overrideHighwayArrows}): lane must not have lane arrows\");\n#endif\n\t\t\t\tremoveLaneArrowFlags(laneId);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (!overrideHighwayArrows && highwayLaneArrowFlags[laneId] != null) {\n#if DEBUGFLAGS\n\t\t\t\tLog._Debug($\"Flags.setLaneArrowFlags({laneId}, {flags}, {overrideHighwayArrows}): highway arrows may not be overridden\");\n#endif\n\t\t\t\treturn false; // disallow custom lane arrows in highway rule mode\n\t\t\t}\n\n\t\t\tif (overrideHighwayArrows) {\n#if DEBUGFLAGS\n\t\t\t\tLog._Debug($\"Flags.setLaneArrowFlags({laneId}, {flags}, {overrideHighwayArrows}): overriding highway arrows\");\n#endif\n\t\t\t\thighwayLaneArrowFlags[laneId] = null;\n\t\t\t}\n\n#if DEBUGFLAGS\n\t\t\tLog._Debug($\"Flags.setLaneArrowFlags({laneId}, {flags}, {overrideHighwayArrows}): setting flags\");\n#endif\n\t\t\tlaneArrowFlags[laneId] = flags;\n\t\t\treturn applyLaneArrowFlags(laneId, false);\n\t\t}\n\n\t\tpublic static void setHighwayLaneArrowFlags(uint laneId, LaneArrows flags, bool check=true) {\n\t\t\tif (check && !mayHaveLaneArrows(laneId)) {\n\t\t\t\tremoveLaneArrowFlags(laneId);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\thighwayLaneArrowFlags[laneId] = flags;\n#if DEBUGFLAGS\n\t\t\tLog._Debug($\"Flags.setHighwayLaneArrowFlags: Setting highway arrows of lane {laneId} to {flags}\");\n#endif\n\t\t\tapplyLaneArrowFlags(laneId, false);\n\t\t}\n\n\t\tpublic static bool toggleLaneArrowFlags(uint laneId, bool startNode, LaneArrows flags, out LaneArrowChangeResult res) {\n\t\t\tif (!mayHaveLaneArrows(laneId)) {\n\t\t\t\tremoveLaneArrowFlags(laneId);\n\t\t\t\tres = LaneArrowChangeResult.Invalid;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (highwayLaneArrowFlags[laneId] != null) {\n\t\t\t\tres = LaneArrowChangeResult.HighwayArrows;\n\t\t\t\treturn false; // disallow custom lane arrows in highway rule mode\n\t\t\t}\n\n\t\t\tif (LaneConnectionManager.Instance.HasConnections(laneId, startNode)) { // TODO refactor\n\t\t\t\tres = LaneArrowChangeResult.LaneConnection;\n\t\t\t\treturn false; // custom lane connection present\n\t\t\t}\n\n\t\t\tLaneArrows? arrows = laneArrowFlags[laneId];\n\t\t\tif (arrows == null) {\n\t\t\t\t// read currently defined arrows\n\t\t\t\tuint laneFlags = (uint)Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags;\n\t\t\t\tlaneFlags &= lfr; // filter arrows\n\t\t\t\tarrows = (LaneArrows)laneFlags;\n\t\t\t}\n\n\t\t\tarrows ^= flags;\n\t\t\tlaneArrowFlags[laneId] = arrows;\n\t\t\tif (applyLaneArrowFlags(laneId, false)) {\n\t\t\t\tres = LaneArrowChangeResult.Success;\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\tres = LaneArrowChangeResult.Invalid;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tinternal static bool mayHaveLaneArrows(uint laneId, bool? startNode=null) {\n\t\t\tif (laneId <= 0)\n\t\t\t\treturn false;\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tif (((NetLane.Flags)Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags & (NetLane.Flags.Created | NetLane.Flags.Deleted)) != NetLane.Flags.Created)\n\t\t\t\treturn false;\n\n\t\t\tushort segmentId = netManager.m_lanes.m_buffer[laneId].m_segment;\n\n\t\t\tvar dir = NetInfo.Direction.Forward;\n\t\t\tvar dir2 = ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? dir : NetInfo.InvertDirection(dir);\n\n\t\t\tNetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info;\n\t\t\tuint curLaneId = netManager.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tint numLanes = segmentInfo.m_lanes.Length;\n\t\t\tint laneIndex = 0;\n\t\t\tint wIter = 0;\n\t\t\twhile (laneIndex < numLanes && curLaneId != 0u) {\n\t\t\t\t++wIter;\n\t\t\t\tif (wIter >= 100) {\n\t\t\t\t\tLog.Error(\"Too many iterations in Flags.mayHaveLaneArrows!\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (curLaneId == laneId) {\n\t\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\t\tbool isStartNode = (laneInfo.m_finalDirection & dir2) == NetInfo.Direction.None;\n\t\t\t\t\tif (startNode != null && isStartNode != startNode)\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tushort nodeId = isStartNode ? netManager.m_segments.m_buffer[segmentId].m_startNode : netManager.m_segments.m_buffer[segmentId].m_endNode;\n\n\t\t\t\t\tif ((netManager.m_nodes.m_buffer[nodeId].m_flags & (NetNode.Flags.Created | NetNode.Flags.Deleted)) != NetNode.Flags.Created)\n\t\t\t\t\t\treturn false;\n\t\t\t\t\treturn (netManager.m_nodes.m_buffer[nodeId].m_flags & NetNode.Flags.Junction) != NetNode.Flags.None;\n\t\t\t\t}\n\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t++laneIndex;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic static ushort? getLaneSpeedLimit(uint laneId) {\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(laneSpeedLimitLock);\n\n\t\t\t\tushort speedLimit;\n\t\t\t\tif (laneId <= 0 || !laneSpeedLimit.TryGetValue(laneId, out speedLimit)) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\treturn speedLimit;\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(laneSpeedLimitLock);\n\t\t\t}\n\t\t}\n\n\t\tinternal static IDictionary<uint, ushort> getAllLaneSpeedLimits() {\n\t\t\tIDictionary<uint, ushort> ret = new Dictionary<uint, ushort>();\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(laneSpeedLimitLock);\n\n\t\t\t\tret = new Dictionary<uint, ushort>(laneSpeedLimit);\n\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(laneSpeedLimitLock);\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n\t\tinternal static IDictionary<uint, ExtVehicleType> getAllLaneAllowedVehicleTypes() {\n\t\t\tIDictionary<uint, ExtVehicleType> ret = new Dictionary<uint, ExtVehicleType>();\n\n\t\t\tfor (uint segmentId = 0; segmentId < NetManager.MAX_SEGMENT_COUNT; ++segmentId) {\n\t\t\t\tConstants.ServiceFactory.NetService.ProcessSegment((ushort)segmentId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\t\tif ((segment.m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created)\n\t\t\t\t\t\treturn true;\n\n\t\t\t\t\tExtVehicleType?[] allowedTypes = laneAllowedVehicleTypesArray[segId];\n\t\t\t\t\tif (allowedTypes == null) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tConstants.ServiceFactory.NetService.IterateSegmentLanes(segId, ref segment, delegate (uint laneId, ref NetLane lane, NetInfo.Lane laneInfo, ushort sId, ref NetSegment seg, byte laneIndex) {\n\t\t\t\t\t\tif (laneInfo.m_vehicleType == VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (laneIndex >= allowedTypes.Length) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tExtVehicleType? allowedType = allowedTypes[laneIndex];\n\n\t\t\t\t\t\tif (allowedType == null) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tret.Add(laneId, (ExtVehicleType)allowedType);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t}\n\t\t\t\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic static LaneArrows? getLaneArrowFlags(uint laneId) {\n\t\t\treturn laneArrowFlags[laneId];\n\t\t}\n\n\t\tpublic static LaneArrows? getHighwayLaneArrowFlags(uint laneId) {\n\t\t\treturn highwayLaneArrowFlags[laneId];\n\t\t}\n\n\t\tpublic static void removeHighwayLaneArrowFlags(uint laneId) {\n#if DEBUGFLAGS\n\t\t\tLog._Debug($\"Flags.removeHighwayLaneArrowFlags: Removing highway arrows of lane {laneId}\");\n#endif\n\t\t\tif (highwayLaneArrowFlags[laneId] != null) {\n\t\t\t\thighwayLaneArrowFlags[laneId] = null;\n\t\t\t\tapplyLaneArrowFlags(laneId, false);\n\t\t\t}\n\t\t}\n\n\t\tpublic static void applyAllFlags() {\n\t\t\tfor (uint i = 0; i < laneArrowFlags.Length; ++i) {\n\t\t\t\tapplyLaneArrowFlags(i);\n\t\t\t}\n\t\t}\n\n\t\tpublic static bool applyLaneArrowFlags(uint laneId, bool check=true) {\n#if DEBUGFLAGS\n\t\t\tLog._Debug($\"Flags.applyLaneArrowFlags({laneId}, {check}) called\");\n#endif\n\n\t\t\tif (laneId <= 0)\n\t\t\t\treturn true;\n\n\t\t\tif (check && !mayHaveLaneArrows(laneId)) {\n\t\t\t\tremoveLaneArrowFlags(laneId);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tLaneArrows? hwArrows = highwayLaneArrowFlags[laneId];\n\t\t\tLaneArrows? arrows = laneArrowFlags[laneId];\n\t\t\tuint laneFlags = (uint)Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags;\n\n\t\t\tif (hwArrows != null) {\n\t\t\t\tlaneFlags &= ~lfr; // remove all arrows\n\t\t\t\tlaneFlags |= (uint)hwArrows; // add highway arrows\n\t\t\t} else if (arrows != null) {\n\t\t\t\tLaneArrows flags = (LaneArrows)arrows;\n\t\t\t\tlaneFlags &= ~lfr; // remove all arrows\n\t\t\t\tlaneFlags |= (uint)flags; // add desired arrows\n\t\t\t}\n\n#if DEBUGFLAGS\n\t\t\tLog._Debug($\"Flags.applyLaneArrowFlags: Setting lane flags of lane {laneId} to {((NetLane.Flags)laneFlags).ToString()}\");\n#endif\n\t\t\tSingleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags = Convert.ToUInt16(laneFlags);\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic static LaneArrows getFinalLaneArrowFlags(uint laneId, bool check=true) {\n\t\t\tif (! mayHaveLaneArrows(laneId)) {\n#if DEBUGFLAGS\n\t\t\t\tLog._Debug($\"Lane {laneId} may not have lane arrows\");\n#endif\n\t\t\t\treturn LaneArrows.None;\n\t\t\t}\n\n\t\t\tuint ret = 0;\n\t\t\tLaneArrows? hwArrows = highwayLaneArrowFlags[laneId];\n\t\t\tLaneArrows? arrows = laneArrowFlags[laneId];\n\n\t\t\tif (hwArrows != null) {\n\t\t\t\tret &= ~lfr; // remove all arrows\n\t\t\t\tret |= (uint)hwArrows; // add highway arrows\n\t\t\t} else if (arrows != null) {\n\t\t\t\tLaneArrows flags = (LaneArrows)arrows;\n\t\t\t\tret &= ~lfr; // remove all arrows\n\t\t\t\tret |= (uint)flags; // add desired arrows\n\t\t\t} else {\n\t\t\t\tConstants.ServiceFactory.NetService.ProcessLane(laneId, delegate (uint lId, ref NetLane lane) {\n\t\t\t\t\tret = lane.m_flags;\n\t\t\t\t\tret &= (uint)LaneArrows.LeftForwardRight;\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn (LaneArrows)ret;\n\t\t}\n\n\t\tpublic static void removeLaneArrowFlags(uint laneId) {\n\t\t\tif (laneId <= 0)\n\t\t\t\treturn;\n\n\t\t\tif (highwayLaneArrowFlags[laneId] != null)\n\t\t\t\treturn; // modification of arrows in highway rule mode is forbidden\n\n\t\t\tlaneArrowFlags[laneId] = null;\n\t\t\tuint laneFlags = (uint)Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags;\n\n\t\t\tif (((NetLane.Flags)Singleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags & (NetLane.Flags.Created | NetLane.Flags.Deleted)) == NetLane.Flags.Created) {\n\t\t\t\tSingleton<NetManager>.instance.m_lanes.m_buffer[laneId].m_flags &= (ushort)~lfr;\n\t\t\t}\n\t\t}\n\n\t\tinternal static void removeHighwayLaneArrowFlagsAtSegment(ushort segmentId) {\n\t\t\tif ((Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_flags & (NetSegment.Flags.Created | NetSegment.Flags.Deleted)) != NetSegment.Flags.Created)\n\t\t\t\treturn;\n\n\t\t\tint i = 0;\n\t\t\tuint curLaneId = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_lanes;\n\n\t\t\twhile (i < Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info.m_lanes.Length && curLaneId != 0u) {\n\t\t\t\tFlags.removeHighwayLaneArrowFlags(curLaneId);\n\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t++i;\n\t\t\t} // foreach lane\n\t\t}\n\n\t\tpublic static void clearHighwayLaneArrows() {\n\t\t\tfor (uint i = 0; i < Singleton<NetManager>.instance.m_lanes.m_size; ++i) {\n\t\t\t\thighwayLaneArrowFlags[i] = null;\n\t\t\t}\n\t\t}\n\n\t\tpublic static void resetSpeedLimits() {\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(laneSpeedLimitLock);\n\t\t\t\tlaneSpeedLimit.Clear();\n\t\t\t\tfor (int i = 0; i < Singleton<NetManager>.instance.m_segments.m_size; ++i) {\n\t\t\t\t\tlaneSpeedLimitArray[i] = null;\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(laneSpeedLimitLock);\n\t\t\t}\n\t\t}\n\t\t\n\t\tinternal static void OnLevelUnloading() {\n\t\t\tfor (uint i = 0; i < laneConnections.Length; ++i) {\n\t\t\t\tlaneConnections[i] = null;\n\t\t\t}\n\n\t\t\tfor (uint i = 0; i < laneSpeedLimitArray.Length; ++i) {\n\t\t\t\tlaneSpeedLimitArray[i] = null;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(laneSpeedLimitLock);\n\t\t\t\tlaneSpeedLimit.Clear();\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(laneSpeedLimitLock);\n\t\t\t}\n\n\t\t\tfor (uint i = 0; i < laneAllowedVehicleTypesArray.Length; ++i) {\n\t\t\t\tlaneAllowedVehicleTypesArray[i] = null;\n\t\t\t}\n\n\t\t\tfor (uint i = 0; i < laneArrowFlags.Length; ++i) {\n\t\t\t\tlaneArrowFlags[i] = null;\n\t\t\t}\n\n\t\t\tfor (uint i = 0; i < highwayLaneArrowFlags.Length; ++i) {\n\t\t\t\thighwayLaneArrowFlags[i] = null;\n\t\t\t}\n\t\t}\n\n\t\tstatic Flags() {\n\t\t\tlaneConnections = new uint[NetManager.MAX_LANE_COUNT][][];\n\t\t\tlaneSpeedLimitArray = new ushort?[NetManager.MAX_SEGMENT_COUNT][];\n\t\t\tlaneSpeedLimit = new Dictionary<uint, ushort>();\n\t\t\tlaneAllowedVehicleTypesArray = new ExtVehicleType?[NetManager.MAX_SEGMENT_COUNT][];\n\t\t\tlaneArrowFlags = new LaneArrows?[NetManager.MAX_LANE_COUNT];\n\t\t\thighwayLaneArrowFlags = new LaneArrows?[NetManager.MAX_LANE_COUNT];\n\t\t}\n\n\t\tpublic static void OnBeforeLoadData() {\n\t\t\t\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/State/GlobalConfig.cs",
    "content": "﻿#define RUSHHOUR\n\nusing ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Xml;\nusing System.Xml.Serialization;\nusing TrafficManager.Manager;\nusing TrafficManager.State.ConfigData;\nusing TrafficManager.Traffic;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Util;\n\nnamespace TrafficManager.State {\n\t[XmlRootAttribute(\"GlobalConfig\", Namespace = \"http://www.viathinksoft.de/tmpe\", IsNullable = false)]\n\tpublic class GlobalConfig : GenericObservable<GlobalConfig> {\n\t\tpublic const string FILENAME = \"TMPE_GlobalConfig.xml\";\n\t\tpublic const string BACKUP_FILENAME = FILENAME + \".bak\";\n\t\tprivate static int LATEST_VERSION = 17;\n#if DEBUG\n\t\tprivate static uint lastModificationCheckFrame = 0;\n#endif\n\n\t\tpublic static int? RushHourParkingSearchRadius { get; private set; } = null;\n\n#if RUSHHOUR\n\t\tprivate static DateTime? rushHourConfigModifiedTime = null;\n\t\tprivate const string RUSHHOUR_CONFIG_FILENAME = \"RushHourOptions.xml\";\n#endif\n\n\t\tpublic static GlobalConfig Instance {\n\t\t\tget {\n\t\t\t\treturn instance;\n\t\t\t}\n\t\t\tprivate set {\n\t\t\t\tif (value != null && instance != null) {\n\t\t\t\t\tvalue.Observers = instance.Observers;\n\t\t\t\t\tvalue.ObserverLock = instance.ObserverLock;\n\t\t\t\t}\n\t\t\t\tinstance = value;\n\t\t\t\tif (instance != null) {\n\t\t\t\t\tinstance.NotifyObservers(instance);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static GlobalConfig instance = null;\n\n\t\t//private object ObserverLock = new object();\n\n\t\t/// <summary>\n\t\t/// Holds a list of observers which are being notified as soon as the configuration is updated\n\t\t/// </summary>\n\t\t//private List<IObserver<GlobalConfig>> observers = new List<IObserver<GlobalConfig>>();\n\n\t\tstatic GlobalConfig() {\n\t\t\tReload();\n\t\t}\n\n\t\tinternal static void OnLevelUnloading() {\n#if RUSHHOUR\n\t\t\trushHourConfigModifiedTime = null;\n\t\t\tRushHourParkingSearchRadius = null;\n#endif\n\t\t}\n\n\t\tprivate static DateTime ModifiedTime = DateTime.MinValue;\n\n\t\t/// <summary>\n\t\t/// Configuration version\n\t\t/// </summary>\n\t\tpublic int Version = LATEST_VERSION;\n\n\t\t/// <summary>\n\t\t/// Language to use (if null then the game's language is being used)\n\t\t/// </summary>\n\t\tpublic string LanguageCode = null;\n\n#if DEBUG\n\t\tpublic Debug Debug = new Debug();\n#endif\n\n\t\tpublic AdvancedVehicleAI AdvancedVehicleAI = new AdvancedVehicleAI();\n\n\t\tpublic DynamicLaneSelection DynamicLaneSelection = new DynamicLaneSelection();\n\n\t\tpublic Main Main = new Main();\n\n\t\tpublic ParkingAI ParkingAI = new ParkingAI();\n\n\t\tpublic PathFinding PathFinding = new PathFinding();\n\n\t\tpublic PriorityRules PriorityRules = new PriorityRules();\n\n\t\tpublic TimedTrafficLights TimedTrafficLights = new TimedTrafficLights();\n\n\t\tinternal static void WriteConfig() {\n\t\t\tModifiedTime = WriteConfig(Instance);\n\t\t}\n\n\t\tprivate static GlobalConfig WriteDefaultConfig(GlobalConfig oldConfig, bool resetAll, out DateTime modifiedTime) {\n\t\t\tLog._Debug($\"Writing default config...\");\n\t\t\tGlobalConfig conf = new GlobalConfig();\n\t\t\tif (!resetAll && oldConfig != null) {\n\t\t\t\tconf.Main.MainMenuButtonX = oldConfig.Main.MainMenuButtonX;\n\t\t\t\tconf.Main.MainMenuButtonY = oldConfig.Main.MainMenuButtonY;\n\n\t\t\t\tconf.Main.MainMenuX = oldConfig.Main.MainMenuX;\n\t\t\t\tconf.Main.MainMenuY = oldConfig.Main.MainMenuY;\n\n\t\t\t\tconf.Main.MainMenuButtonPosLocked = oldConfig.Main.MainMenuButtonPosLocked;\n\t\t\t\tconf.Main.MainMenuPosLocked = oldConfig.Main.MainMenuPosLocked;\n\n\t\t\t\tconf.Main.GuiTransparency = oldConfig.Main.GuiTransparency;\n\t\t\t\tconf.Main.OverlayTransparency = oldConfig.Main.OverlayTransparency;\n\n\t\t\t\tconf.Main.TinyMainMenu = oldConfig.Main.TinyMainMenu;\n\n\t\t\t\tconf.Main.EnableTutorial = oldConfig.Main.EnableTutorial;\n\t\t\t\tconf.Main.DisplayedTutorialMessages = oldConfig.Main.DisplayedTutorialMessages;\n\t\t\t}\n\t\t\tmodifiedTime = WriteConfig(conf);\n\t\t\treturn conf;\n\t\t}\n\n\t\tprivate static DateTime WriteConfig(GlobalConfig config, string filename=FILENAME) {\n\t\t\ttry {\n\t\t\t\tLog.Info($\"Writing global config to file '{filename}'...\");\n\t\t\t\tXmlSerializer serializer = new XmlSerializer(typeof(GlobalConfig));\n\t\t\t\tusing (TextWriter writer = new StreamWriter(filename)) {\n\t\t\t\t\tserializer.Serialize(writer, config);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"Could not write global config: {e.ToString()}\");\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\treturn File.GetLastWriteTime(FILENAME);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Warning($\"Could not determine modification date of global config: {e.ToString()}\");\n\t\t\t\treturn DateTime.Now;\n\t\t\t}\n\t\t}\n\n\t\tpublic static GlobalConfig Load(out DateTime modifiedTime) {\n\t\t\ttry {\n\t\t\t\tmodifiedTime = File.GetLastWriteTime(FILENAME);\n\n\t\t\t\tLog.Info($\"Loading global config from file '{FILENAME}'...\");\n\t\t\t\tusing (FileStream fs = new FileStream(FILENAME, FileMode.Open)) {\n\t\t\t\t\tXmlSerializer serializer = new XmlSerializer(typeof(GlobalConfig));\n\t\t\t\t\tLog.Info($\"Global config loaded.\");\n\t\t\t\t\tGlobalConfig conf = (GlobalConfig)serializer.Deserialize(fs);\n\t\t\t\t\tif (LoadingExtension.IsGameLoaded\n#if DEBUG\n\t\t\t\t\t\t&& !conf.Debug.Switches[10]\n#endif\n\t\t\t\t\t\t) {\n\t\t\t\t\t\tConstants.ManagerFactory.RoutingManager.RequestFullRecalculation();\n\t\t\t\t\t}\n\n#if DEBUG\n\t\t\t\t\tif (conf.Debug == null) {\n\t\t\t\t\t\tconf.Debug = new Debug();\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tif (conf.AdvancedVehicleAI == null) {\n\t\t\t\t\t\tconf.AdvancedVehicleAI = new AdvancedVehicleAI();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (conf.DynamicLaneSelection == null) {\n\t\t\t\t\t\tconf.DynamicLaneSelection = new DynamicLaneSelection();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (conf.ParkingAI == null) {\n\t\t\t\t\t\tconf.ParkingAI = new ParkingAI();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (conf.PathFinding == null) {\n\t\t\t\t\t\tconf.PathFinding = new PathFinding();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (conf.PriorityRules == null) {\n\t\t\t\t\t\tconf.PriorityRules = new PriorityRules();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (conf.TimedTrafficLights == null) {\n\t\t\t\t\t\tconf.TimedTrafficLights = new TimedTrafficLights();\n\t\t\t\t\t}\n\n\t\t\t\t\treturn conf;\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Warning($\"Could not load global config: {e} Generating default config.\");\n\t\t\t\treturn WriteDefaultConfig(null, false, out modifiedTime);\n\t\t\t}\n\t\t}\n\n\t\tpublic static void Reload(bool checkVersion=true) {\n\t\t\tDateTime modifiedTime;\n\t\t\tGlobalConfig conf = Load(out modifiedTime);\n\t\t\tif (checkVersion && conf.Version != -1 && conf.Version < LATEST_VERSION) {\n\t\t\t\t// backup old config and reset\n\t\t\t\tstring filename = BACKUP_FILENAME;\n\t\t\t\ttry {\n\t\t\t\t\tint backupIndex = 0;\n\t\t\t\t\twhile (File.Exists(filename)) {\n\t\t\t\t\t\tfilename = BACKUP_FILENAME + \".\" + backupIndex;\n\t\t\t\t\t\t++backupIndex;\n\t\t\t\t\t}\n\t\t\t\t\tWriteConfig(conf, filename);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Warning($\"Error occurred while saving backup config to '{filename}': {e.ToString()}\");\n\t\t\t\t}\n\t\t\t\tReset(conf);\n\t\t\t} else {\n\t\t\t\tInstance = conf;\n\t\t\t\tModifiedTime = WriteConfig(Instance);\n\t\t\t}\n\t\t}\n\n\t\tpublic static void Reset(GlobalConfig oldConfig, bool resetAll=false) {\n\t\t\tLog.Info($\"Resetting global config.\");\n\t\t\tDateTime modifiedTime;\n\t\t\tInstance = WriteDefaultConfig(oldConfig, resetAll, out modifiedTime);\n\t\t\tModifiedTime = modifiedTime;\n\t\t}\n\n\t\tprivate static void ReloadIfNewer() {\n\t\t\ttry {\n\t\t\t\tDateTime modifiedTime = File.GetLastWriteTime(FILENAME);\n\t\t\t\tif (modifiedTime > ModifiedTime) {\n\t\t\t\t\tLog.Info($\"Detected modification of global config.\");\n\t\t\t\t\tReload(false);\n\t\t\t\t}\n\t\t\t} catch (Exception) {\n\t\t\t\tLog.Warning(\"Could not determine modification date of global config.\");\n\t\t\t}\n\t\t}\n\n#if RUSHHOUR\n\t\tprivate static void ReloadRushHourConfigIfNewer() { // TODO refactor\n\t\t\ttry {\n\t\t\t\tDateTime newModifiedTime = File.GetLastWriteTime(RUSHHOUR_CONFIG_FILENAME);\n\t\t\t\tif (rushHourConfigModifiedTime != null) {\n\t\t\t\t\tif (newModifiedTime <= rushHourConfigModifiedTime)\n\t\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\trushHourConfigModifiedTime = newModifiedTime;\n\n\t\t\t\tXmlDocument doc = new XmlDocument();\n\t\t\t\tdoc.Load(RUSHHOUR_CONFIG_FILENAME);\n\t\t\t\tXmlNode root = doc.DocumentElement;\n\n\t\t\t\tXmlNode betterParkingNode = root.SelectSingleNode(\"OptionPanel/data/BetterParking\");\n\t\t\t\tXmlNode parkingSpaceRadiusNode = root.SelectSingleNode(\"OptionPanel/data/ParkingSearchRadius\");\n\n\t\t\t\tif (\"True\".Equals(betterParkingNode.InnerText)) {\n\t\t\t\t\tRushHourParkingSearchRadius = int.Parse(parkingSpaceRadiusNode.InnerText);\n\t\t\t\t}\n\n\t\t\t\tLog._Debug($\"RushHour config has changed. Setting searchRadius={RushHourParkingSearchRadius}\");\n\t\t\t} catch (Exception ex) {\n\t\t\t\tLog.Error(\"GlobalConfig.ReloadRushHourConfigIfNewer: \" + ex.ToString());\n\t\t\t}\n\t\t}\n#endif\n\n\t\tpublic void SimulationStep() {\n#if RUSHHOUR\n\t\t\tif (LoadingExtension.IsRushHourLoaded) {\n\t\t\t\tReloadRushHourConfigIfNewer();\n\t\t\t}\n#endif\n\t\t}\n\n\t\t/*public IDisposable Subscribe(IObserver<GlobalConfig> observer) {\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(ObserverLock);\n\t\t\t\tLog.Info($\"Adding {observer} as observer of global config\");\n\t\t\t\tobservers.Add(observer);\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(ObserverLock);\n\t\t\t}\n\t\t\treturn new GenericUnsubscriber<GlobalConfig>(observers, observer, ObserverLock);\n\t\t}*/\n\n\t\t/*protected void NotifyObservers() {\n\t\t\t//Log.Warning($\"NodeGeometry.NotifyObservers(): CurrentSegmentReplacement={CurrentSegmentReplacement}\");\n\n\t\t\tList<IObserver<GlobalConfig>> myObservers = new List<IObserver<GlobalConfig>>(observers); // in case somebody unsubscribes while iterating over subscribers\n\t\t\tforeach (IObserver<GlobalConfig> observer in myObservers) {\n\t\t\t\ttry {\n\t\t\t\t\tLog.Info($\"Notifying global config observer {observer}\");\n\t\t\t\t\tobserver.OnUpdate(this);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"GlobalConfig.NotifyObservers: An exception occured while notifying an observer of global config: {e}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}*/\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/State/Options.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\nusing UnityEngine;\nusing ColossalFramework;\nusing ColossalFramework.UI;\nusing ICities;\nusing TrafficManager.Geometry;\nusing TrafficManager.State;\nusing TrafficManager.UI;\nusing ColossalFramework.Plugins;\nusing ColossalFramework.Globalization;\nusing TrafficManager.Manager;\nusing CSUtil.Commons;\nusing System.Reflection;\nusing TrafficManager.Manager.Impl;\n\nnamespace TrafficManager.State {\n\n\tpublic class Options : MonoBehaviour {\n\t\tprivate static UIDropDown languageDropdown = null;\n\t\tprivate static UIDropDown simAccuracyDropdown = null;\n\t\t//private static UIDropDown laneChangingRandomizationDropdown = null;\n\t\tprivate static UICheckBox instantEffectsToggle = null;\n\t\tprivate static UICheckBox lockButtonToggle = null;\n\t\tprivate static UICheckBox lockMenuToggle = null;\n\t\tprivate static UISlider guiTransparencySlider = null;\n\t\tprivate static UISlider overlayTransparencySlider = null;\n\t\tprivate static UICheckBox tinyMenuToggle = null;\n\t\tprivate static UICheckBox enableTutorialToggle = null;\n\t\tprivate static UICheckBox showCompatibilityCheckErrorToggle = null;\n\t\tprivate static UICheckBox realisticSpeedsToggle = null;\n\t\tprivate static UIDropDown recklessDriversDropdown = null;\n\t\tprivate static UICheckBox relaxedBussesToggle = null;\n\t\tprivate static UICheckBox allRelaxedToggle = null;\n\t\tprivate static UICheckBox evacBussesMayIgnoreRulesToggle = null;\n\t\tprivate static UICheckBox prioritySignsOverlayToggle = null;\n\t\tprivate static UICheckBox timedLightsOverlayToggle = null;\n\t\tprivate static UICheckBox speedLimitsOverlayToggle = null;\n\t\tprivate static UICheckBox vehicleRestrictionsOverlayToggle = null;\n\t\tprivate static UICheckBox parkingRestrictionsOverlayToggle = null;\n\t\tprivate static UICheckBox junctionRestrictionsOverlayToggle = null;\n\t\tprivate static UICheckBox connectedLanesOverlayToggle = null;\n\t\tprivate static UICheckBox nodesOverlayToggle = null;\n\t\tprivate static UICheckBox vehicleOverlayToggle = null;\n#if DEBUG\n\t\tprivate static UICheckBox citizenOverlayToggle = null;\n\t\tprivate static UICheckBox buildingOverlayToggle = null;\n#endif\n\t\tprivate static UICheckBox allowEnterBlockedJunctionsToggle = null;\n\t\tprivate static UICheckBox allowUTurnsToggle = null;\n\t\tprivate static UICheckBox allowNearTurnOnRedToggle = null;\n\t\tprivate static UICheckBox allowFarTurnOnRedToggle = null;\n\t\tprivate static UICheckBox allowLaneChangesWhileGoingStraightToggle = null;\n\t\tprivate static UICheckBox trafficLightPriorityRulesToggle = null;\n\t\tprivate static UIDropDown vehicleRestrictionsAggressionDropdown = null;\n\t\tprivate static UICheckBox banRegularTrafficOnBusLanesToggle = null;\n\t\tprivate static UICheckBox disableDespawningToggle = null;\n\n\t\tprivate static UICheckBox strongerRoadConditionEffectsToggle = null;\n\t\tprivate static UICheckBox prohibitPocketCarsToggle = null;\n\t\tprivate static UICheckBox advancedAIToggle = null;\n\t\tprivate static UICheckBox realisticPublicTransportToggle = null;\n\t\tprivate static UISlider altLaneSelectionRatioSlider = null;\n\t\tprivate static UICheckBox highwayRulesToggle = null;\n\t\tprivate static UICheckBox preferOuterLaneToggle = null;\n\t\tprivate static UICheckBox showLanesToggle = null;\n#if QUEUEDSTATS\n\t\tprivate static UICheckBox showPathFindStatsToggle = null;\n#endif\n\t\tprivate static UIButton resetStuckEntitiesBtn = null;\n\n\t\tprivate static UICheckBox enablePrioritySignsToggle = null;\n\t\tprivate static UICheckBox enableTimedLightsToggle = null;\n\t\tprivate static UICheckBox enableCustomSpeedLimitsToggle = null;\n\t\tprivate static UICheckBox enableVehicleRestrictionsToggle = null;\n\t\tprivate static UICheckBox enableParkingRestrictionsToggle = null;\n\t\tprivate static UICheckBox enableJunctionRestrictionsToggle = null;\n\t\tprivate static UICheckBox turnOnRedEnabledToggle = null;\n\t\tprivate static UICheckBox enableLaneConnectorToggle = null;\n\n\t\tprivate static UIButton removeParkedVehiclesBtn = null;\n#if DEBUG\n\t\tprivate static UIButton resetSpeedLimitsBtn = null;\n\t\tprivate static List<UICheckBox> debugSwitchFields = new List<UICheckBox>();\n\t\tprivate static List<UITextField> debugValueFields = new List<UITextField>();\n\t\tprivate static UITextField pathCostMultiplicatorField = null;\n\t\tprivate static UITextField pathCostMultiplicator2Field = null;\n#endif\n\t\tprivate static UIButton reloadGlobalConfBtn = null;\n\t\tprivate static UIButton resetGlobalConfBtn = null;\n\n\t\tpublic static bool instantEffects = true;\n\t\tpublic static int simAccuracy = 0;\n\t\t//public static int laneChangingRandomization = 2;\n\t\tpublic static bool realisticSpeeds = true;\n\t\tpublic static int recklessDrivers = 3;\n\t\tpublic static bool relaxedBusses = false;\n\t\tpublic static bool allRelaxed = false;\n\t\tpublic static bool evacBussesMayIgnoreRules = false;\n\t\tpublic static bool prioritySignsOverlay = false;\n\t\tpublic static bool timedLightsOverlay = false;\n\t\tpublic static bool speedLimitsOverlay = false;\n\t\tpublic static bool vehicleRestrictionsOverlay = false;\n\t\tpublic static bool parkingRestrictionsOverlay = false;\n\t\tpublic static bool junctionRestrictionsOverlay = false;\n\t\tpublic static bool connectedLanesOverlay = false;\n#if QUEUEDSTATS\n\t\tpublic static bool showPathFindStats =\n#if DEBUG\n\t\t\ttrue;\n#else\n\t\t\tfalse;\n#endif\n\n#endif\n#if DEBUG\n\t\tpublic static bool nodesOverlay = false;\n\t\tpublic static bool vehicleOverlay = false;\n\t\tpublic static bool citizenOverlay = false;\n\t\tpublic static bool buildingOverlay = false;\n#else\n\t\tpublic static bool nodesOverlay = false;\n\t\tpublic static bool vehicleOverlay = false;\n\t\tpublic static bool citizenOverlay = false;\n\t\tpublic static bool buildingOverlay = false;\n#endif\n\t\tpublic static bool allowEnterBlockedJunctions = false;\n\t\tpublic static bool allowUTurns = false;\n\t\tpublic static bool allowNearTurnOnRed = false;\n\t\tpublic static bool allowFarTurnOnRed = false;\n\t\tpublic static bool allowLaneChangesWhileGoingStraight = false;\n\t\tpublic static bool trafficLightPriorityRules = false;\n\t\tpublic static bool banRegularTrafficOnBusLanes = false;\n\t\tpublic static bool advancedAI = false;\n\t\tpublic static bool realisticPublicTransport = false;\n\t\tpublic static byte altLaneSelectionRatio = 0;\n\t\tpublic static bool highwayRules = false;\n#if DEBUG\n\t\tpublic static bool showLanes = true;\n#else\n\t\tpublic static bool showLanes = false;\n#endif\n\t\tpublic static bool strongerRoadConditionEffects = false;\n\t\tpublic static bool prohibitPocketCars = false;\n\t\tpublic static bool disableDespawning = false;\n\t\tpublic static bool preferOuterLane = false;\n\t\t//public static byte publicTransportUsage = 1;\n\n\t\tpublic static bool prioritySignsEnabled = true;\n\t\tpublic static bool timedLightsEnabled = true;\n\t\tpublic static bool customSpeedLimitsEnabled = true;\n\t\tpublic static bool vehicleRestrictionsEnabled = true;\n\t\tpublic static bool parkingRestrictionsEnabled = true;\n\t\tpublic static bool junctionRestrictionsEnabled = true;\n\t\tpublic static bool turnOnRedEnabled = true;\n\t\tpublic static bool laneConnectorEnabled = true;\n\n\t\tpublic static VehicleRestrictionsAggression vehicleRestrictionsAggression = VehicleRestrictionsAggression.Medium;\n\n\t\tpublic static bool MenuRebuildRequired {\n\t\t\tget { return false; }\n\t\t\tinternal set {\n\t\t\t\tif (value) {\n\t\t\t\t\tif (LoadingExtension.BaseUI != null) {\n\t\t\t\t\t\tLoadingExtension.BaseUI.RebuildMenu();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic static void makeSettings(UIHelperBase helper) {\n\t\t\t// tabbing code is borrowed from RushHour mod\n\t\t\t// https://github.com/PropaneDragon/RushHour/blob/release/RushHour/Options/OptionHandler.cs\n\n\t\t\tUIHelper actualHelper = helper as UIHelper;\n\t\t\tUIComponent container = actualHelper.self as UIComponent;\n\n\t\t\tUITabstrip tabStrip = container.AddUIComponent<UITabstrip>();\n\t\t\ttabStrip.relativePosition = new Vector3(0, 0);\n\t\t\ttabStrip.size = new Vector2(container.width - 20, 40);\n\n\t\t\tUITabContainer tabContainer = container.AddUIComponent<UITabContainer>();\n\t\t\ttabContainer.relativePosition = new Vector3(0, 40);\n\t\t\ttabContainer.size = new Vector2(container.width - 20, container.height - tabStrip.height - 20);\n\t\t\ttabStrip.tabPages = tabContainer;\n\n\t\t\tint tabIndex = 0;\n\t\t\t// GENERAL\n\n\t\t\tAddOptionTab(tabStrip, Translation.GetString(\"General\"));// tabStrip.AddTab(Translation.GetString(\"General\"), tabTemplate, true);\n\t\t\ttabStrip.selectedIndex = tabIndex;\n\n\t\t\tUIPanel currentPanel = tabStrip.tabContainer.components[tabIndex] as UIPanel;\n\t\t\tcurrentPanel.autoLayout = true;\n\t\t\tcurrentPanel.autoLayoutDirection = LayoutDirection.Vertical;\n\t\t\tcurrentPanel.autoLayoutPadding.top = 5;\n\t\t\tcurrentPanel.autoLayoutPadding.left = 10;\n\t\t\tcurrentPanel.autoLayoutPadding.right = 10;\n\n\t\t\tUIHelper panelHelper = new UIHelper(currentPanel);\n\n\t\t\tvar generalGroup = panelHelper.AddGroup(Translation.GetString(\"General\"));\n\n\t\t\tstring[] languageLabels = new string[Translation.AVAILABLE_LANGUAGE_CODES.Count + 1];\n\t\t\tlanguageLabels[0] = Translation.GetString(\"Game_language\");\n\t\t\tfor (int i = 0; i < Translation.AVAILABLE_LANGUAGE_CODES.Count; ++i) {\n\t\t\t\tlanguageLabels[i + 1] = Translation.LANGUAGE_LABELS[Translation.AVAILABLE_LANGUAGE_CODES[i]];\n\t\t\t}\n\t\t\tint languageIndex = 0;\n\t\t\tstring curLangCode = GlobalConfig.Instance.LanguageCode;\n\t\t\tif (curLangCode != null) {\n\t\t\t\tlanguageIndex = Translation.AVAILABLE_LANGUAGE_CODES.IndexOf(curLangCode);\n\t\t\t\tif (languageIndex < 0) {\n\t\t\t\t\tlanguageIndex = 0;\n\t\t\t\t} else {\n\t\t\t\t\t++languageIndex;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlanguageDropdown = generalGroup.AddDropdown(Translation.GetString(\"Language\") + \":\", languageLabels, languageIndex, onLanguageChanged) as UIDropDown;\n\t\t\tlockButtonToggle = generalGroup.AddCheckbox(Translation.GetString(\"Lock_main_menu_button_position\"), GlobalConfig.Instance.Main.MainMenuButtonPosLocked, onLockButtonChanged) as UICheckBox;\n\t\t\tlockMenuToggle = generalGroup.AddCheckbox(Translation.GetString(\"Lock_main_menu_position\"), GlobalConfig.Instance.Main.MainMenuPosLocked, onLockMenuChanged) as UICheckBox;\n\t\t\ttinyMenuToggle = generalGroup.AddCheckbox(Translation.GetString(\"Compact_main_menu\"), GlobalConfig.Instance.Main.TinyMainMenu, onTinyMenuChanged) as UICheckBox;\n\t\t\tguiTransparencySlider = generalGroup.AddSlider(Translation.GetString(\"Window_transparency\") + \":\", 0, 90, 5, GlobalConfig.Instance.Main.GuiTransparency, onGuiTransparencyChanged) as UISlider;\n\t\t\tguiTransparencySlider.parent.Find<UILabel>(\"Label\").width = 500;\n\t\t\toverlayTransparencySlider = generalGroup.AddSlider(Translation.GetString(\"Overlay_transparency\") + \":\", 0, 90, 5, GlobalConfig.Instance.Main.OverlayTransparency, onOverlayTransparencyChanged) as UISlider;\n\t\t\toverlayTransparencySlider.parent.Find<UILabel>(\"Label\").width = 500;\n\t\t\tenableTutorialToggle = generalGroup.AddCheckbox(Translation.GetString(\"Enable_tutorial_messages\"), GlobalConfig.Instance.Main.EnableTutorial, onEnableTutorialsChanged) as UICheckBox;\n\t\t\tshowCompatibilityCheckErrorToggle = generalGroup.AddCheckbox(Translation.GetString(\"Show_error_message_if_a_mod_incompatibility_is_detected\"), GlobalConfig.Instance.Main.ShowCompatibilityCheckErrorMessage, onShowCompatibilityCheckErrorChanged) as UICheckBox;\n\n\t\t\tvar simGroup = panelHelper.AddGroup(Translation.GetString(\"Simulation\"));\n\t\t\tsimAccuracyDropdown = simGroup.AddDropdown(Translation.GetString(\"Simulation_accuracy\") + \":\", new string[] { Translation.GetString(\"Very_high\"), Translation.GetString(\"High\"), Translation.GetString(\"Medium\"), Translation.GetString(\"Low\"), Translation.GetString(\"Very_Low\") }, simAccuracy, onSimAccuracyChanged) as UIDropDown;\n\t\t\tinstantEffectsToggle = simGroup.AddCheckbox(Translation.GetString(\"Customizations_come_into_effect_instantaneously\"), instantEffects, onInstantEffectsChanged) as UICheckBox;\n\n\t\t\t// GAMEPLAY\n\t\t\t++tabIndex;\n\n\t\t\tAddOptionTab(tabStrip, Translation.GetString(\"Gameplay\"));\n\t\t\ttabStrip.selectedIndex = tabIndex;\n\n\t\t\tcurrentPanel = tabStrip.tabContainer.components[tabIndex] as UIPanel;\n\t\t\tcurrentPanel.autoLayout = true;\n\t\t\tcurrentPanel.autoLayoutDirection = LayoutDirection.Vertical;\n\t\t\tcurrentPanel.autoLayoutPadding.top = 5;\n\t\t\tcurrentPanel.autoLayoutPadding.left = 10;\n\t\t\tcurrentPanel.autoLayoutPadding.right = 10;\n\n\t\t\tpanelHelper = new UIHelper(currentPanel);\n\n\t\t\tvar vehBehaviorGroup = panelHelper.AddGroup(Translation.GetString(\"Vehicle_behavior\"));\n\n\t\t\trecklessDriversDropdown = vehBehaviorGroup.AddDropdown(Translation.GetString(\"Reckless_driving\") + \":\", new string[] { Translation.GetString(\"Path_Of_Evil_(10_%)\"), Translation.GetString(\"Rush_Hour_(5_%)\"), Translation.GetString(\"Minor_Complaints_(2_%)\"), Translation.GetString(\"Holy_City_(0_%)\") }, recklessDrivers, onRecklessDriversChanged) as UIDropDown;\n\t\t\trecklessDriversDropdown.width = 300;\n\t\t\trealisticSpeedsToggle = vehBehaviorGroup.AddCheckbox(Translation.GetString(\"Realistic_speeds\"), realisticSpeeds, onRealisticSpeedsChanged) as UICheckBox;\n\t\t\tif (SteamHelper.IsDLCOwned(SteamHelper.DLC.SnowFallDLC)) {\n\t\t\t\tstrongerRoadConditionEffectsToggle = vehBehaviorGroup.AddCheckbox(Translation.GetString(\"Road_condition_has_a_bigger_impact_on_vehicle_speed\"), strongerRoadConditionEffects, onStrongerRoadConditionEffectsChanged) as UICheckBox;\n\t\t\t}\n\t\t\tdisableDespawningToggle = vehBehaviorGroup.AddCheckbox(Translation.GetString(\"Disable_despawning\"), disableDespawning, onDisableDespawningChanged) as UICheckBox;\n\n\t\t\tvar vehAiGroup = panelHelper.AddGroup(Translation.GetString(\"Advanced_Vehicle_AI\"));\n\t\t\tadvancedAIToggle = vehAiGroup.AddCheckbox(Translation.GetString(\"Enable_Advanced_Vehicle_AI\"), advancedAI, onAdvancedAIChanged) as UICheckBox;\n\t\t\taltLaneSelectionRatioSlider = vehAiGroup.AddSlider(Translation.GetString(\"Dynamic_lane_section\") + \":\", 0, 100, 5, altLaneSelectionRatio, onAltLaneSelectionRatioChanged) as UISlider;\n\t\t\taltLaneSelectionRatioSlider.parent.Find<UILabel>(\"Label\").width = 450;\n\n\t\t\tvar parkAiGroup = panelHelper.AddGroup(Translation.GetString(\"Parking_AI\"));\n\t\t\tprohibitPocketCarsToggle = parkAiGroup.AddCheckbox(Translation.GetString(\"Enable_more_realistic_parking\"), prohibitPocketCars, onProhibitPocketCarsChanged) as UICheckBox;\n\n\t\t\tvar ptGroup = panelHelper.AddGroup(Translation.GetString(\"Public_transport\"));\n\t\t\trealisticPublicTransportToggle = ptGroup.AddCheckbox(Translation.GetString(\"Prevent_excessive_transfers_at_public_transport_stations\"), realisticPublicTransport, onRealisticPublicTransportChanged) as UICheckBox;\n\n\t\t\t// VEHICLE RESTRICTIONS\n\t\t\t++tabIndex;\n\n\t\t\tAddOptionTab(tabStrip, Translation.GetString(\"Policies_&_Restrictions\"));\n\t\t\ttabStrip.selectedIndex = tabIndex;\n\n\t\t\tcurrentPanel = tabStrip.tabContainer.components[tabIndex] as UIPanel;\n\t\t\tcurrentPanel.autoLayout = true;\n\t\t\tcurrentPanel.autoLayoutDirection = LayoutDirection.Vertical;\n\t\t\tcurrentPanel.autoLayoutPadding.top = 5;\n\t\t\tcurrentPanel.autoLayoutPadding.left = 10;\n\t\t\tcurrentPanel.autoLayoutPadding.right = 10;\n\n\t\t\tpanelHelper = new UIHelper(currentPanel);\n\n\t\t\tvar atJunctionsGroup = panelHelper.AddGroup(Translation.GetString(\"At_junctions\"));\n#if DEBUG\n\t\t\tallRelaxedToggle = atJunctionsGroup.AddCheckbox(Translation.GetString(\"All_vehicles_may_ignore_lane_arrows\"), allRelaxed, onAllRelaxedChanged) as UICheckBox;\n#endif\n            relaxedBussesToggle = atJunctionsGroup.AddCheckbox(Translation.GetString(\"Busses_may_ignore_lane_arrows\"), relaxedBusses, onRelaxedBussesChanged) as UICheckBox;\n            allowEnterBlockedJunctionsToggle = atJunctionsGroup.AddCheckbox(Translation.GetString(\"Vehicles_may_enter_blocked_junctions\"), allowEnterBlockedJunctions, onAllowEnterBlockedJunctionsChanged) as UICheckBox;\n\t\t\tallowUTurnsToggle = atJunctionsGroup.AddCheckbox(Translation.GetString(\"Vehicles_may_do_u-turns_at_junctions\"), allowUTurns, onAllowUTurnsChanged) as UICheckBox;\n\t\t\tallowNearTurnOnRedToggle = atJunctionsGroup.AddCheckbox(Translation.GetString(\"Vehicles_may_turn_on_red\"), allowNearTurnOnRed, onAllowNearTurnOnRedChanged) as UICheckBox;\n\t\t\tallowFarTurnOnRedToggle = atJunctionsGroup.AddCheckbox(Translation.GetString(\"Also_apply_to_left/right_turns_between_one-way_streets\"), allowFarTurnOnRed, onAllowFarTurnOnRedChanged) as UICheckBox;\n\t\t\tallowLaneChangesWhileGoingStraightToggle = atJunctionsGroup.AddCheckbox(Translation.GetString(\"Vehicles_going_straight_may_change_lanes_at_junctions\"), allowLaneChangesWhileGoingStraight, onAllowLaneChangesWhileGoingStraightChanged) as UICheckBox;\n\t\t\ttrafficLightPriorityRulesToggle = atJunctionsGroup.AddCheckbox(Translation.GetString(\"Vehicles_follow_priority_rules_at_junctions_with_timed_traffic_lights\"), trafficLightPriorityRules, onTrafficLightPriorityRulesChanged) as UICheckBox;\n\n\t\t\tIndent(allowFarTurnOnRedToggle);\n\n\t\t\tvar onRoadsGroup = panelHelper.AddGroup(Translation.GetString(\"On_roads\"));\n\t\t\tvehicleRestrictionsAggressionDropdown = onRoadsGroup.AddDropdown(Translation.GetString(\"Vehicle_restrictions_aggression\") + \":\", new string[] { Translation.GetString(\"Low\"), Translation.GetString(\"Medium\"), Translation.GetString(\"High\"), Translation.GetString(\"Strict\") }, (int)vehicleRestrictionsAggression, onVehicleRestrictionsAggressionChanged) as UIDropDown;\n\t\t\tbanRegularTrafficOnBusLanesToggle = onRoadsGroup.AddCheckbox(Translation.GetString(\"Ban_private_cars_and_trucks_on_bus_lanes\"), banRegularTrafficOnBusLanes, onBanRegularTrafficOnBusLanesChanged) as UICheckBox;\n\t\t\thighwayRulesToggle = onRoadsGroup.AddCheckbox(Translation.GetString(\"Enable_highway_specific_lane_merging/splitting_rules\"), highwayRules, onHighwayRulesChanged) as UICheckBox;\n\t\t\tpreferOuterLaneToggle = onRoadsGroup.AddCheckbox(Translation.GetString(\"Heavy_trucks_prefer_outer_lanes_on_highways\"), preferOuterLane, onPreferOuterLaneChanged) as UICheckBox;\n\n\t\t\tif (SteamHelper.IsDLCOwned(SteamHelper.DLC.NaturalDisastersDLC)) {\n\t\t\t\tvar inCaseOfEmergencyGroup = panelHelper.AddGroup(Translation.GetString(\"In_case_of_emergency\"));\n\t\t\t\tevacBussesMayIgnoreRulesToggle = inCaseOfEmergencyGroup.AddCheckbox(Translation.GetString(\"Evacuation_busses_may_ignore_traffic_rules\"), evacBussesMayIgnoreRules, onEvacBussesMayIgnoreRulesChanged) as UICheckBox;\n\t\t\t}\n\n\t\t\t// OVERLAYS\n\t\t\t++tabIndex;\n\n\t\t\tAddOptionTab(tabStrip, Translation.GetString(\"Overlays\"));\n\t\t\ttabStrip.selectedIndex = tabIndex;\n\n\t\t\tcurrentPanel = tabStrip.tabContainer.components[tabIndex] as UIPanel;\n\t\t\tcurrentPanel.autoLayout = true;\n\t\t\tcurrentPanel.autoLayoutDirection = LayoutDirection.Vertical;\n\t\t\tcurrentPanel.autoLayoutPadding.top = 5;\n\t\t\tcurrentPanel.autoLayoutPadding.left = 10;\n\t\t\tcurrentPanel.autoLayoutPadding.right = 10;\n\n\t\t\tpanelHelper = new UIHelper(currentPanel);\n\n\t\t\tprioritySignsOverlayToggle = panelHelper.AddCheckbox(Translation.GetString(\"Priority_signs\"), prioritySignsOverlay, onPrioritySignsOverlayChanged) as UICheckBox;\n\t\t\ttimedLightsOverlayToggle = panelHelper.AddCheckbox(Translation.GetString(\"Timed_traffic_lights\"), timedLightsOverlay, onTimedLightsOverlayChanged) as UICheckBox;\n\t\t\tspeedLimitsOverlayToggle = panelHelper.AddCheckbox(Translation.GetString(\"Speed_limits\"), speedLimitsOverlay, onSpeedLimitsOverlayChanged) as UICheckBox;\n\t\t\tvehicleRestrictionsOverlayToggle = panelHelper.AddCheckbox(Translation.GetString(\"Vehicle_restrictions\"), vehicleRestrictionsOverlay, onVehicleRestrictionsOverlayChanged) as UICheckBox;\n\t\t\tparkingRestrictionsOverlayToggle = panelHelper.AddCheckbox(Translation.GetString(\"Parking_restrictions\"), parkingRestrictionsOverlay, onParkingRestrictionsOverlayChanged) as UICheckBox;\n\t\t\tjunctionRestrictionsOverlayToggle = panelHelper.AddCheckbox(Translation.GetString(\"Junction_restrictions\"), junctionRestrictionsOverlay, onJunctionRestrictionsOverlayChanged) as UICheckBox;\n\t\t\tconnectedLanesOverlayToggle = panelHelper.AddCheckbox(Translation.GetString(\"Connected_lanes\"), connectedLanesOverlay, onConnectedLanesOverlayChanged) as UICheckBox;\n\t\t\tnodesOverlayToggle = panelHelper.AddCheckbox(Translation.GetString(\"Nodes_and_segments\"), nodesOverlay, onNodesOverlayChanged) as UICheckBox;\n\t\t\tshowLanesToggle = panelHelper.AddCheckbox(Translation.GetString(\"Lanes\"), showLanes, onShowLanesChanged) as UICheckBox;\n#if DEBUG\n\t\t\tvehicleOverlayToggle = panelHelper.AddCheckbox(Translation.GetString(\"Vehicles\"), vehicleOverlay, onVehicleOverlayChanged) as UICheckBox;\n\t\t\tcitizenOverlayToggle = panelHelper.AddCheckbox(Translation.GetString(\"Citizens\"), citizenOverlay, onCitizenOverlayChanged) as UICheckBox;\n\t\t\tbuildingOverlayToggle = panelHelper.AddCheckbox(Translation.GetString(\"Buildings\"), buildingOverlay, onBuildingOverlayChanged) as UICheckBox;\n#endif\n\n\t\t\t// MAINTENANCE\n\t\t\t++tabIndex;\n\n\t\t\tAddOptionTab(tabStrip, Translation.GetString(\"Maintenance\"));\n\t\t\ttabStrip.selectedIndex = tabIndex;\n\n\t\t\tcurrentPanel = tabStrip.tabContainer.components[tabIndex] as UIPanel;\n\t\t\tcurrentPanel.autoLayout = true;\n\t\t\tcurrentPanel.autoLayoutDirection = LayoutDirection.Vertical;\n\t\t\tcurrentPanel.autoLayoutPadding.top = 5;\n\t\t\tcurrentPanel.autoLayoutPadding.left = 10;\n\t\t\tcurrentPanel.autoLayoutPadding.right = 10;\n\n\t\t\tpanelHelper = new UIHelper(currentPanel);\n\n\t\t\tvar maintenanceGroup = panelHelper.AddGroup(Translation.GetString(\"Maintenance\"));\n\n\t\t\tresetStuckEntitiesBtn = maintenanceGroup.AddButton(Translation.GetString(\"Reset_stuck_cims_and_vehicles\"), onClickResetStuckEntities) as UIButton;\n\t\t\tremoveParkedVehiclesBtn = maintenanceGroup.AddButton(Translation.GetString(\"Remove_parked_vehicles\"), onClickRemoveParkedVehicles) as UIButton;\n#if DEBUG\n\t\t\tresetSpeedLimitsBtn = maintenanceGroup.AddButton(Translation.GetString(\"Reset_custom_speed_limits\"), onClickResetSpeedLimits) as UIButton;\n#endif\n\t\t\treloadGlobalConfBtn = maintenanceGroup.AddButton(Translation.GetString(\"Reload_global_configuration\"), onClickReloadGlobalConf) as UIButton;\n\t\t\tresetGlobalConfBtn = maintenanceGroup.AddButton(Translation.GetString(\"Reset_global_configuration\"), onClickResetGlobalConf) as UIButton;\n\n#if QUEUEDSTATS\n\t\t\tshowPathFindStatsToggle = maintenanceGroup.AddCheckbox(Translation.GetString(\"Show_path-find_stats\"), showPathFindStats, onShowPathFindStatsChanged) as UICheckBox;\n#endif\n\n\t\t\tvar featureGroup = panelHelper.AddGroup(Translation.GetString(\"Activated_features\")) as UIHelper;\n\t\t\tenablePrioritySignsToggle = featureGroup.AddCheckbox(Translation.GetString(\"Priority_signs\"), prioritySignsEnabled, onPrioritySignsEnabledChanged) as UICheckBox;\n\t\t\tenableTimedLightsToggle = featureGroup.AddCheckbox(Translation.GetString(\"Timed_traffic_lights\"), timedLightsEnabled, onTimedLightsEnabledChanged) as UICheckBox;\n\t\t\tenableCustomSpeedLimitsToggle = featureGroup.AddCheckbox(Translation.GetString(\"Speed_limits\"), customSpeedLimitsEnabled, onCustomSpeedLimitsEnabledChanged) as UICheckBox;\n\t\t\tenableVehicleRestrictionsToggle = featureGroup.AddCheckbox(Translation.GetString(\"Vehicle_restrictions\"), vehicleRestrictionsEnabled, onVehicleRestrictionsEnabledChanged) as UICheckBox;\n\t\t\tenableParkingRestrictionsToggle = featureGroup.AddCheckbox(Translation.GetString(\"Parking_restrictions\"), parkingRestrictionsEnabled, onParkingRestrictionsEnabledChanged) as UICheckBox;\n\t\t\tenableJunctionRestrictionsToggle = featureGroup.AddCheckbox(Translation.GetString(\"Junction_restrictions\"), junctionRestrictionsEnabled, onJunctionRestrictionsEnabledChanged) as UICheckBox;\n\t\t\tturnOnRedEnabledToggle = featureGroup.AddCheckbox(Translation.GetString(\"Turn_on_red\"), turnOnRedEnabled, onTurnOnRedEnabledChanged) as UICheckBox;\n\t\t\tenableLaneConnectorToggle = featureGroup.AddCheckbox(Translation.GetString(\"Lane_connector\"), laneConnectorEnabled, onLaneConnectorEnabledChanged) as UICheckBox;\n\n\t\t\tIndent(turnOnRedEnabledToggle);\n#if DEBUG\n\n\t\t\t// GLOBAL CONFIG\n\t\t\t/*\n\t\t\tAddOptionTab(tabStrip, Translation.GetString(\"Global_configuration\"));// tabStrip.AddTab(Translation.GetString(\"General\"), tabTemplate, true);\n\t\t\ttabStrip.selectedIndex = tabIndex;\n\n\t\t\tcurrentPanel = tabStrip.tabContainer.components[tabIndex] as UIPanel;\n\t\t\tcurrentPanel.autoLayout = true;\n\t\t\tcurrentPanel.autoLayoutDirection = LayoutDirection.Vertical;\n\t\t\tcurrentPanel.autoLayoutPadding.top = 5;\n\t\t\tcurrentPanel.autoLayoutPadding.left = 10;\n\t\t\tcurrentPanel.autoLayoutPadding.right = 10;\n\n\t\t\tpanelHelper = new UIHelper(currentPanel);\n\n\t\t\tGlobalConfig globalConf = GlobalConfig.Instance;\n\n\t\t\tvar aiTrafficMeasurementConfGroup = panelHelper.AddGroup(Translation.GetString(\"Advanced_Vehicle_AI\") + \": \" + Translation.GetString(\"General\"));\n\n\t\t\taiTrafficMeasurementConfGroup.AddSlider(Translation.GetString(\"Live_traffic_buffer_size\"), 0f, 10000f, 100f, globalConf.MaxTrafficBuffer, onMaxTrafficBufferChanged);\n\t\t\taiTrafficMeasurementConfGroup.AddSlider(Translation.GetString(\"Path-find_traffic_buffer_size\"), 0f, 10000f, 100f, globalConf.MaxPathFindTrafficBuffer, onMaxPathFindTrafficBufferChanged);\n\t\t\taiTrafficMeasurementConfGroup.AddSlider(Translation.GetString(\"Max._congestion_measurements\"), 0f, 1000f, 10f, globalConf.MaxNumCongestionMeasurements, onMaxNumCongestionMeasurementsChanged);\n\n\t\t\tvar aiLaneSelParamConfGroup = panelHelper.AddGroup(Translation.GetString(\"Advanced_Vehicle_AI\") + \": \" + Translation.GetString(\"Lane_selection_parameters\"));\n\n\t\t\taiLaneSelParamConfGroup.AddSlider(Translation.GetString(\"Lane_density_randomization\"), 0f, 100f, 1f, globalConf.LaneDensityRandInterval, onLaneDensityRandIntervalChanged);\n\t\t\taiLaneSelParamConfGroup.AddSlider(Translation.GetString(\"Lane_density_discretization\"), 0f, 100f, 1f, globalConf.LaneDensityDiscretization, onLaneTrafficDiscretizationChanged);\n\t\t\taiLaneSelParamConfGroup.AddSlider(Translation.GetString(\"Lane_spread_randomization\"), 0f, 100f, 1f, globalConf.LaneUsageRandInterval, onLaneUsageRandIntervalChanged);\n\t\t\taiLaneSelParamConfGroup.AddSlider(Translation.GetString(\"Lane_spread_discretization\"), 0f, 100f, 1f, globalConf.LaneUsageDiscretization, onLaneUsageDiscretizationChanged);\n\t\t\taiLaneSelParamConfGroup.AddSlider(Translation.GetString(\"Congestion_rel._velocity_threshold\"), 0f, 100f, 1f, globalConf.CongestionSqrSpeedThreshold, onCongestionSqrSpeedThresholdChanged);\n\t\t\taiLaneSelParamConfGroup.AddSlider(Translation.GetString(\"Max._walking_distance\"), 0f, 10000f, 100f, globalConf.MaxWalkingDistance, onMaxWalkingDistanceChanged);\n\n\t\t\tvar aiLaneSelFactorConfGroup = panelHelper.AddGroup(Translation.GetString(\"Advanced_Vehicle_AI\") + \": \" + Translation.GetString(\"Lane_selection_factors\"));\n\n\t\t\taiLaneSelFactorConfGroup.AddSlider(Translation.GetString(\"Spread_randomization_factor\"), 1f, 5f, 0.05f, globalConf.UsageCostFactor, onUsageCostFactorChanged);\n\t\t\taiLaneSelFactorConfGroup.AddSlider(Translation.GetString(\"Traffic_avoidance_factor\"), 1f, 5f, 0.05f, globalConf.TrafficCostFactor, onTrafficCostFactorChanged);\n\t\t\taiLaneSelFactorConfGroup.AddSlider(Translation.GetString(\"Public_transport_lane_penalty\"), 1f, 50f, 0.5f, globalConf.PublicTransportLanePenalty, onPublicTransportLanePenaltyChanged);\n\t\t\taiLaneSelFactorConfGroup.AddSlider(Translation.GetString(\"Public_transport_lane_reward\"), 0f, 1f, 0.05f, globalConf.PublicTransportLaneReward, onPublicTransportLaneRewardChanged);\n\t\t\taiLaneSelFactorConfGroup.AddSlider(Translation.GetString(\"Heavy_vehicle_max._inner_lane_penalty\"), 0f, 5f, 0.05f, globalConf.HeavyVehicleMaxInnerLanePenalty, onHeavyVehicleMaxInnerLanePenaltyChanged);\n\t\t\taiLaneSelFactorConfGroup.AddSlider(Translation.GetString(\"Vehicle_restrictions_penalty\"), 0f, 1000f, 25f, globalConf.VehicleRestrictionsPenalty, onVehicleRestrictionsPenaltyChanged);\n\n\t\t\tvar aiLaneChangeParamConfGroup = panelHelper.AddGroup(Translation.GetString(\"Advanced_Vehicle_AI\") + \": \" + Translation.GetString(\"Lane_changing_parameters\"));\n\n\t\t\taiLaneChangeParamConfGroup.AddSlider(Translation.GetString(\"U-turn_lane_distance\"), 1f, 5f, 1f, globalConf.UturnLaneDistance, onUturnLaneDistanceChanged);\n\t\t\taiLaneChangeParamConfGroup.AddSlider(Translation.GetString(\"Incompatible_lane_distance\"), 1f, 5f, 1f, globalConf.IncompatibleLaneDistance, onIncompatibleLaneDistanceChanged);\n\t\t\taiLaneChangeParamConfGroup.AddSlider(Translation.GetString(\"Lane_changing_randomization_modulo\"), 1f, 100f, 1f, globalConf.RandomizedLaneChangingModulo, onRandomizedLaneChangingModuloChanged);\n\t\t\taiLaneChangeParamConfGroup.AddSlider(Translation.GetString(\"Min._controlled_segments_in_front_of_highway_interchanges\"), 1f, 10f, 1f, globalConf.MinHighwayInterchangeSegments, onMinHighwayInterchangeSegmentsChanged);\n\t\t\taiLaneChangeParamConfGroup.AddSlider(Translation.GetString(\"Max._controlled_segments_in_front_of_highway_interchanges\"), 1f, 30f, 1f, globalConf.MaxHighwayInterchangeSegments, onMaxHighwayInterchangeSegmentsChanged);\n\n\t\t\tvar aiLaneChangeFactorConfGroup = panelHelper.AddGroup(Translation.GetString(\"Advanced_Vehicle_AI\") + \": \" + Translation.GetString(\"Lane_changing_cost_factors\"));\n\n\t\t\taiLaneChangeFactorConfGroup.AddSlider(Translation.GetString(\"On_city_roads\"), 1f, 5f, 0.05f, globalConf.CityRoadLaneChangingBaseCost, onCityRoadLaneChangingBaseCostChanged);\n\t\t\taiLaneChangeFactorConfGroup.AddSlider(Translation.GetString(\"On_highways\"), 1f, 5f, 0.05f, globalConf.HighwayLaneChangingBaseCost, onHighwayLaneChangingBaseCostChanged);\n\t\t\taiLaneChangeFactorConfGroup.AddSlider(Translation.GetString(\"In_front_of_highway_interchanges\"), 1f, 5f, 0.05f, globalConf.HighwayInterchangeLaneChangingBaseCost, onHighwayInterchangeLaneChangingBaseCostChanged);\n\t\t\taiLaneChangeFactorConfGroup.AddSlider(Translation.GetString(\"For_heavy_vehicles\"), 1f, 5f, 0.05f, globalConf.HeavyVehicleLaneChangingCostFactor, onHeavyVehicleLaneChangingCostFactorChanged);\n\t\t\taiLaneChangeFactorConfGroup.AddSlider(Translation.GetString(\"On_congested_roads\"), 1f, 5f, 0.05f, globalConf.CongestionLaneChangingCostFactor, onCongestionLaneChangingCostFactorChanged);\n\t\t\taiLaneChangeFactorConfGroup.AddSlider(Translation.GetString(\"When_changing_multiple_lanes_at_once\"), 1f, 5f, 0.05f, globalConf.MoreThanOneLaneChangingCostFactor, onMoreThanOneLaneChangingCostFactorChanged);\n\n\t\t\tvar aiParkingLaneChangeFactorConfGroup = panelHelper.AddGroup(Translation.GetString(\"Parking_AI\") + \": \" + Translation.GetString(\"General\"));\n\t\t\t*/\n\n\t\t\t// DEBUG\n\t\t\t/*++tabIndex;\n\n\t\t\tsettingsButton = tabStrip.AddTab(\"Debug\", tabTemplate, true);\n\t\t\tsettingsButton.textPadding = new RectOffset(10, 10, 10, 10);\n\t\t\tsettingsButton.autoSize = true;\n\t\t\tsettingsButton.tooltip = \"Debug\";\n\n\t\t\tcurrentPanel = tabStrip.tabContainer.components[tabIndex] as UIPanel;\n\t\t\tcurrentPanel.autoLayout = true;\n\t\t\tcurrentPanel.autoLayoutDirection = LayoutDirection.Vertical;\n\t\t\tcurrentPanel.autoLayoutPadding.top = 5;\n\t\t\tcurrentPanel.autoLayoutPadding.left = 10;\n\t\t\tcurrentPanel.autoLayoutPadding.right = 10;\n\n\t\t\tpanelHelper = new UIHelper(currentPanel);\n\t\t\t\n\t\t\tdebugSwitchFields.Clear();\n\t\t\tfor (int i = 0; i < Debug.Switches.Length; ++i) {\n\t\t\t\tint index = i;\n\t\t\t\tstring varName = $\"Debug switch #{i}\";\n\t\t\t\tdebugSwitchFields.Add(panelHelper.AddCheckbox(varName, Debug.Switches[i], delegate (bool newVal) { onBoolValueChanged(varName, newVal, ref Debug.Switches[index]); }) as UICheckBox);\n\t\t\t}\n\n\t\t\tdebugValueFields.Clear();\n\t\t\tfor (int i = 0; i < debugValues.Length; ++i) {\n\t\t\t\tint index = i;\n\t\t\t\tstring varName = $\"Debug value #{i}\";\n\t\t\t\tdebugValueFields.Add(panelHelper.AddTextfield(varName, String.Format(\"{0:0.##}\", debugValues[i]), delegate(string newValStr) { onFloatValueChanged(varName, newValStr, ref debugValues[index]); }, null) as UITextField);\n\t\t\t}*/\n#endif\n\n\t\t\ttabStrip.selectedIndex = 0;\n\t\t}\n\n\t\tprivate static void Indent<T>(T component) where T : UIComponent {\n\t\t\tUIPanel panel = component.parent as UIPanel;\n\t\t\tpanel.autoLayout = false;\n\t\t\tcomponent.relativePosition += new Vector3(30, 0);\n\t\t}\n\n\t\tprivate static UIButton AddOptionTab(UITabstrip tabStrip, string caption) {\n\t\t\tUIButton tabButton = tabStrip.AddTab(caption);\n\n\t\t\ttabButton.normalBgSprite = \"SubBarButtonBase\";\n\t\t\ttabButton.disabledBgSprite = \"SubBarButtonBaseDisabled\";\n\t\t\ttabButton.focusedBgSprite = \"SubBarButtonBaseFocused\";\n\t\t\ttabButton.hoveredBgSprite = \"SubBarButtonBaseHovered\";\n\t\t\ttabButton.pressedBgSprite = \"SubBarButtonBasePressed\";\n\n\t\t\ttabButton.textPadding = new RectOffset(10, 10, 10, 10);\n\t\t\ttabButton.autoSize = true;\n\t\t\ttabButton.tooltip = caption;\n\n\t\t\treturn tabButton;\n\t\t}\n\n\t\tprivate static bool checkGameLoaded() {\n\t\t\tif (!SerializableDataExtension.StateLoading && !LoadingExtension.IsGameLoaded) {\n\t\t\t\tUIView.library.ShowModal<ExceptionPanel>(\"ExceptionPanel\").SetMessage(\"Nope!\", Translation.GetString(\"Settings_are_defined_for_each_savegame_separately\") + \". https://www.viathinksoft.de/tmpe/#options\", false);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate static void onGuiTransparencyChanged(float newVal) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tsetGuiTransparency((byte)Mathf.RoundToInt(newVal));\n\t\t\tguiTransparencySlider.tooltip = Translation.GetString(\"Window_transparency\") + \": \" + GlobalConfig.Instance.Main.GuiTransparency + \" %\";\n\n\t\t\tGlobalConfig.WriteConfig();\n\n\t\t\tLog._Debug($\"GuiTransparency changed to {GlobalConfig.Instance.Main.GuiTransparency}\");\n\t\t}\n\n\t\tprivate static void onOverlayTransparencyChanged(float newVal) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tsetOverlayTransparency((byte)Mathf.RoundToInt(newVal));\n\t\t\toverlayTransparencySlider.tooltip = Translation.GetString(\"Overlay_transparency\") + \": \" + GlobalConfig.Instance.Main.OverlayTransparency + \" %\";\n\n\t\t\tGlobalConfig.WriteConfig();\n\n\t\t\tLog._Debug($\"OverlayTransparency changed to {GlobalConfig.Instance.Main.OverlayTransparency}\");\n\t\t}\n\n\t\tprivate static void onAltLaneSelectionRatioChanged(float newVal) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tsetAltLaneSelectionRatio((byte)Mathf.RoundToInt(newVal));\n\t\t\taltLaneSelectionRatioSlider.tooltip = Translation.GetString(\"Percentage_of_vehicles_performing_dynamic_lane_section\") + \": \" + altLaneSelectionRatio + \" %\";\n\n\t\t\tLog._Debug($\"altLaneSelectionRatio changed to {altLaneSelectionRatio}\");\n\t\t}\n\n\t\tprivate static void onPrioritySignsOverlayChanged(bool newPrioritySignsOverlay) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"prioritySignsOverlay changed to {newPrioritySignsOverlay}\");\n\t\t\tprioritySignsOverlay = newPrioritySignsOverlay;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tprivate static void onTimedLightsOverlayChanged(bool newTimedLightsOverlay) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"timedLightsOverlay changed to {newTimedLightsOverlay}\");\n\t\t\ttimedLightsOverlay = newTimedLightsOverlay;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tprivate static void onSpeedLimitsOverlayChanged(bool newSpeedLimitsOverlay) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"speedLimitsOverlay changed to {newSpeedLimitsOverlay}\");\n\t\t\tspeedLimitsOverlay = newSpeedLimitsOverlay;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tprivate static void onVehicleRestrictionsOverlayChanged(bool newVehicleRestrictionsOverlay) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"vehicleRestrictionsOverlay changed to {newVehicleRestrictionsOverlay}\");\n\t\t\tvehicleRestrictionsOverlay = newVehicleRestrictionsOverlay;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tprivate static void onParkingRestrictionsOverlayChanged(bool newParkingRestrictionsOverlay) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"parkingRestrictionsOverlay changed to {newParkingRestrictionsOverlay}\");\n\t\t\tparkingRestrictionsOverlay = newParkingRestrictionsOverlay;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tprivate static void onJunctionRestrictionsOverlayChanged(bool newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"junctionRestrictionsOverlay changed to {newValue}\");\n\t\t\tjunctionRestrictionsOverlay = newValue;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tprivate static void onConnectedLanesOverlayChanged(bool newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"connectedLanesOverlay changed to {newValue}\");\n\t\t\tconnectedLanesOverlay = newValue;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tprivate static void onLanguageChanged(int newLanguageIndex) {\n\t\t\tbool localeChanged = false;\n\n\t\t\tif (newLanguageIndex <= 0) {\n\t\t\t\tGlobalConfig.Instance.LanguageCode = null;\n\t\t\t\tGlobalConfig.WriteConfig();\n\t\t\t\tMenuRebuildRequired = true;\n\t\t\t\tlocaleChanged = true;\n\t\t\t} else if (newLanguageIndex - 1 < Translation.AVAILABLE_LANGUAGE_CODES.Count) {\n\t\t\t\tGlobalConfig.Instance.LanguageCode = Translation.AVAILABLE_LANGUAGE_CODES[newLanguageIndex - 1];\n\t\t\t\tGlobalConfig.WriteConfig();\n\t\t\t\tMenuRebuildRequired = true;\n\t\t\t\tlocaleChanged = true;\n\t\t\t} else {\n\t\t\t\tLog.Warning($\"Options.onLanguageChanged: Invalid language index: {newLanguageIndex}\");\n\t\t\t}\n\n\t\t\tif (localeChanged) {\n\t\t\t\tMethodInfo onChangedHandler = typeof(OptionsMainPanel).GetMethod(\"OnLocaleChanged\", BindingFlags.Instance | BindingFlags.NonPublic);\n\t\t\t\tif (onChangedHandler != null) {\n\t\t\t\t\tonChangedHandler.Invoke(UIView.library.Get<OptionsMainPanel>(\"OptionsPanel\"), new object[0] { });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static void onLockButtonChanged(bool newValue) {\n\t\t\tLog._Debug($\"Button lock changed to {newValue}\");\n\t\t\tLoadingExtension.BaseUI.MainMenuButton.SetPosLock(newValue);\n\t\t\tGlobalConfig.Instance.Main.MainMenuButtonPosLocked = newValue;\n\t\t\tGlobalConfig.WriteConfig();\n\t\t}\n\n\t\tprivate static void onLockMenuChanged(bool newValue) {\n\t\t\tLog._Debug($\"Menu lock changed to {newValue}\");\n\t\t\tLoadingExtension.BaseUI.MainMenu.SetPosLock(newValue);\n\t\t\tGlobalConfig.Instance.Main.MainMenuPosLocked = newValue;\n\t\t\tGlobalConfig.WriteConfig();\n\t\t}\n\n\t\tprivate static void onTinyMenuChanged(bool newValue) {\n\t\t\tLog._Debug($\"Menu tiny changed to {newValue}\");\n\t\t\tGlobalConfig.Instance.Main.TinyMainMenu = newValue;\n\t\t\tGlobalConfig.Instance.NotifyObservers(GlobalConfig.Instance);\n\t\t\tGlobalConfig.WriteConfig();\n\t\t}\n\n\t\tprivate static void onEnableTutorialsChanged(bool newValue) {\n\t\t\tLog._Debug($\"Enable tutorial messages changed to {newValue}\");\n\t\t\tGlobalConfig.Instance.Main.EnableTutorial = newValue;\n\t\t\tGlobalConfig.WriteConfig();\n\t\t}\n\n\t\tprivate static void onShowCompatibilityCheckErrorChanged(bool newValue) {\n\t\t\tLog._Debug($\"Show mod compatibility error changed to {newValue}\");\n\t\t\tGlobalConfig.Instance.Main.ShowCompatibilityCheckErrorMessage = newValue;\n\t\t\tGlobalConfig.WriteConfig();\n\t\t}\n\n\t\tprivate static void onInstantEffectsChanged(bool newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"Instant effects changed to {newValue}\");\n\t\t\tinstantEffects = newValue;\n\t\t}\n\n\t\tprivate static void onSimAccuracyChanged(int newAccuracy) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"Simulation accuracy changed to {newAccuracy}\");\n\t\t\tsimAccuracy = newAccuracy;\n\t\t}\n\n\t\tprivate static void onVehicleRestrictionsAggressionChanged(int newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"vehicleRestrictionsAggression changed to {newValue}\");\n\t\t\tsetVehicleRestrictionsAggression((VehicleRestrictionsAggression)newValue);\n\t\t}\n\n\t\t/*private static void onLaneChangingRandomizationChanged(int newLaneChangingRandomization) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"Lane changing frequency changed to {newLaneChangingRandomization}\");\n\t\t\tlaneChangingRandomization = newLaneChangingRandomization;\n\t\t}*/\n\n\t\tprivate static void onRecklessDriversChanged(int newRecklessDrivers) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"Reckless driver amount changed to {newRecklessDrivers}\");\n\t\t\trecklessDrivers = newRecklessDrivers;\n\t\t}\n\n\t\tprivate static void onRelaxedBussesChanged(bool newRelaxedBusses) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"Relaxed busses changed to {newRelaxedBusses}\");\n\t\t\trelaxedBusses = newRelaxedBusses;\n\t\t}\n\n\t\tprivate static void onAllRelaxedChanged(bool newAllRelaxed) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"All relaxed changed to {newAllRelaxed}\");\n\t\t\tallRelaxed = newAllRelaxed;\n\t\t}\n\n\t\tprivate static void onAdvancedAIChanged(bool newAdvancedAI) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"advancedAI changed to {newAdvancedAI}\");\n\t\t\tsetAdvancedAI(newAdvancedAI);\n\t\t}\n\n\t\tprivate static void onHighwayRulesChanged(bool newHighwayRules) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tbool changed = newHighwayRules != highwayRules;\n\t\t\tif (!changed) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLog._Debug($\"Highway rules changed to {newHighwayRules}\");\n\t\t\thighwayRules = newHighwayRules;\n\t\t\tFlags.clearHighwayLaneArrows();\n\t\t\tFlags.applyAllFlags();\n\t\t\tRoutingManager.Instance.RequestFullRecalculation();\n\t\t}\n\n\t\tprivate static void onPreferOuterLaneChanged(bool val) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tpreferOuterLane = val;\n\t\t}\n\n\t\tprivate static void onPrioritySignsEnabledChanged(bool val) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tMenuRebuildRequired = true;\n\t\t\tprioritySignsEnabled = val;\n\t\t\tif (!val) {\n\t\t\t\tsetPrioritySignsOverlay(false);\n\t\t\t\tsetTrafficLightPriorityRules(false);\n\t\t\t}\n\t\t}\n\n\t\tprivate static void onTimedLightsEnabledChanged(bool val) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tMenuRebuildRequired = true;\n\t\t\ttimedLightsEnabled = val;\n\t\t\tif (!val) {\n\t\t\t\tsetTimedLightsOverlay(false);\n\t\t\t\tsetTrafficLightPriorityRules(false);\n\t\t\t}\n\t\t}\n\n\t\tprivate static void onCustomSpeedLimitsEnabledChanged(bool val) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tMenuRebuildRequired = true;\n\t\t\tcustomSpeedLimitsEnabled = val;\n\t\t\tif (!val)\n\t\t\t\tsetSpeedLimitsOverlay(false);\n\t\t}\n\n\t\tprivate static void onVehicleRestrictionsEnabledChanged(bool val) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tMenuRebuildRequired = true;\n\t\t\tvehicleRestrictionsEnabled = val;\n\t\t\tif (!val)\n\t\t\t\tsetVehicleRestrictionsOverlay(false);\n\t\t}\n\n\t\tprivate static void onParkingRestrictionsEnabledChanged(bool val) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tMenuRebuildRequired = true;\n\t\t\tparkingRestrictionsEnabled = val;\n\t\t\tif (!val)\n\t\t\t\tsetParkingRestrictionsOverlay(false);\n\t\t}\n\n\t\tprivate static void onJunctionRestrictionsEnabledChanged(bool val) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tMenuRebuildRequired = true;\n\t\t\tjunctionRestrictionsEnabled = val;\n\t\t\tif (!val) {\n\t\t\t\tsetAllowUTurns(false);\n\t\t\t\tsetAllowEnterBlockedJunctions(false);\n\t\t\t\tsetAllowLaneChangesWhileGoingStraight(false);\n\t\t\t\tsetTurnOnRedEnabled(false);\n\t\t\t\tsetJunctionRestrictionsOverlay(false);\n\t\t\t}\n\t\t}\n\n\t\tprivate static void onTurnOnRedEnabledChanged(bool val) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tsetTurnOnRedEnabled(val);\n\t\t}\n\n\t\tprivate static void onLaneConnectorEnabledChanged(bool val) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tbool changed = val != laneConnectorEnabled;\n\t\t\tif (!changed) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tMenuRebuildRequired = true;\n\t\t\tlaneConnectorEnabled = val;\n\t\t\tRoutingManager.Instance.RequestFullRecalculation();\n\t\t\tif (!val)\n\t\t\t\tsetConnectedLanesOverlay(false);\n\t\t}\n\n\t\tprivate static void onEvacBussesMayIgnoreRulesChanged(bool value) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"evacBussesMayIgnoreRules changed to {value}\");\n\t\t\tevacBussesMayIgnoreRules = value;\n\t\t}\n\n\t\tprivate static void onAllowEnterBlockedJunctionsChanged(bool newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\t\t\tif (newValue && !junctionRestrictionsEnabled) {\n\t\t\t\tsetAllowEnterBlockedJunctions(false);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLog._Debug($\"allowEnterBlockedJunctions changed to {newValue}\");\n\t\t\tsetAllowEnterBlockedJunctions(newValue);\n\t\t}\n\n\t\tprivate static void onAllowUTurnsChanged(bool newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\t\t\tif (newValue && !junctionRestrictionsEnabled) {\n\t\t\t\tsetAllowUTurns(false);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLog._Debug($\"allowUTurns changed to {newValue}\");\n\t\t\tsetAllowUTurns(newValue);\n\t\t}\n\n\t\tprivate static void onAllowNearTurnOnRedChanged(bool newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\t\t\tif (newValue && !turnOnRedEnabled) {\n\t\t\t\tsetAllowNearTurnOnRed(false);\n\t\t\t\tsetAllowFarTurnOnRed(false);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLog._Debug($\"allowNearTurnOnRed changed to {newValue}\");\n\t\t\tsetAllowNearTurnOnRed(newValue);\n\n\t\t\tif (! newValue) {\n\t\t\t\tsetAllowFarTurnOnRed(false);\n\t\t\t}\n\t\t}\n\n\t\tprivate static void onAllowFarTurnOnRedChanged(bool newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\t\t\tif (newValue && (!turnOnRedEnabled || !allowNearTurnOnRed)) {\n\t\t\t\tsetAllowFarTurnOnRed(false);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLog._Debug($\"allowFarTurnOnRed changed to {newValue}\");\n\t\t\tsetAllowFarTurnOnRed(newValue);\n\t\t}\n\n\t\tprivate static void onAllowLaneChangesWhileGoingStraightChanged(bool newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\t\t\tif (newValue && !junctionRestrictionsEnabled) {\n\t\t\t\tsetAllowLaneChangesWhileGoingStraight(false);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLog._Debug($\"allowLaneChangesWhileGoingStraight changed to {newValue}\");\n\t\t\tallowLaneChangesWhileGoingStraight = newValue;\n\t\t}\n\n\t\tprivate static void onTrafficLightPriorityRulesChanged(bool newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\t\t\tif (newValue && !prioritySignsEnabled) {\n\t\t\t\tsetTrafficLightPriorityRules(false);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLog._Debug($\"trafficLightPriorityRules changed to {newValue}\");\n\t\t\ttrafficLightPriorityRules = newValue;\n\t\t\tif (newValue) {\n\t\t\t\tsetPrioritySignsEnabled(true);\n\t\t\t\tsetTimedLightsEnabled(true);\n\t\t\t}\n\t\t}\n\n\t\tprivate static void onBanRegularTrafficOnBusLanesChanged(bool newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"banRegularTrafficOnBusLanes changed to {newValue}\");\n\t\t\tbanRegularTrafficOnBusLanes = newValue;\n\t\t\tVehicleRestrictionsManager.Instance.ClearCache();\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tprivate static void onStrongerRoadConditionEffectsChanged(bool newStrongerRoadConditionEffects) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"strongerRoadConditionEffects changed to {newStrongerRoadConditionEffects}\");\n\t\t\tstrongerRoadConditionEffects = newStrongerRoadConditionEffects;\n\t\t}\n\n\t\tprivate static void onProhibitPocketCarsChanged(bool newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"prohibitPocketCars changed to {newValue}\");\n\n\t\t\tprohibitPocketCars = newValue;\n\t\t\tif (prohibitPocketCars) {\n\t\t\t\tAdvancedParkingManager.Instance.OnEnableFeature();\n\t\t\t} else {\n\t\t\t\tAdvancedParkingManager.Instance.OnDisableFeature();\n\t\t\t}\n\t\t}\n\n\t\tprivate static void onRealisticPublicTransportChanged(bool newValue) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"realisticPublicTransport changed to {newValue}\");\n\t\t\trealisticPublicTransport = newValue;\n\t\t}\n\n\t\tprivate static void onRealisticSpeedsChanged(bool value) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"realisticSpeeds changed to {value}\");\n\t\t\trealisticSpeeds = value;\n\t\t}\n\n\t\tprivate static void onDisableDespawningChanged(bool value) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"disableDespawning changed to {value}\");\n\t\t\tdisableDespawning = value;\n\t\t}\n\n\t\tprivate static void onNodesOverlayChanged(bool newNodesOverlay) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"Nodes overlay changed to {newNodesOverlay}\");\n\t\t\tnodesOverlay = newNodesOverlay;\n\t\t}\n\n\t\tprivate static void onShowLanesChanged(bool newShowLanes) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"Show lanes changed to {newShowLanes}\");\n\t\t\tshowLanes = newShowLanes;\n\t\t}\n\n\t\tprivate static void onVehicleOverlayChanged(bool newVal) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"Vehicle overlay changed to {newVal}\");\n\t\t\tvehicleOverlay = newVal;\n\t\t}\n\n\t\tprivate static void onCitizenOverlayChanged(bool newVal) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"Citizen overlay changed to {newVal}\");\n\t\t\tcitizenOverlay = newVal;\n\t\t}\n\n\t\tprivate static void onBuildingOverlayChanged(bool newVal) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"Building overlay changed to {newVal}\");\n\t\t\tbuildingOverlay = newVal;\n\t\t}\n\n#if QUEUEDSTATS\n\t\tprivate static void onShowPathFindStatsChanged(bool newVal) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"Show path-find stats changed to {newVal}\");\n\t\t\tshowPathFindStats = newVal;\n\t\t}\n#endif\n\n\t\tprivate static void onFloatValueChanged(string varName, string newValueStr, ref float var) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\ttry {\n\t\t\t\tfloat newValue = Single.Parse(newValueStr);\n\t\t\t\tvar = newValue;\n\t\t\t\tLog._Debug($\"{varName} changed to {newValue}\");\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Warning($\"An invalid value was inserted: '{newValueStr}'. Error: {e.ToString()}\");\n\t\t\t\t//UIView.library.ShowModal<ExceptionPanel>(\"ExceptionPanel\").SetMessage(\"Invalid value\", \"An invalid value was inserted.\", false);\n\t\t\t}\n\t\t}\n\n\t\tprivate static void onBoolValueChanged(string varName, bool newVal, ref bool var) {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tvar = newVal;\n\t\t\tLog._Debug($\"{varName} changed to {newVal}\");\n\t\t}\n\n\t\tprivate static void onClickResetStuckEntities() {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tConstants.ServiceFactory.SimulationService.AddAction(() => {\n\t\t\t\tUtilityManager.Instance.ResetStuckEntities();\n\t\t\t});\n\t\t}\n\n\t\tprivate static void onClickRemoveParkedVehicles() {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tConstants.ServiceFactory.SimulationService.AddAction(() => {\n\t\t\t\tUtilityManager.Instance.RemoveParkedVehicles();\n\t\t\t});\n\t\t}\n\n\t\tprivate static void onClickResetSpeedLimits() {\n\t\t\tif (!checkGameLoaded())\n\t\t\t\treturn;\n\n\t\t\tFlags.resetSpeedLimits();\n\t\t}\n\n\t\tprivate static void onClickReloadGlobalConf() {\n\t\t\tGlobalConfig.Reload();\n\t\t}\n\n\t\tprivate static void onClickResetGlobalConf() {\n\t\t\tGlobalConfig.Reset(null, true);\n\t\t}\n\n\t\tpublic static void setSimAccuracy(int newAccuracy) {\n\t\t\tsimAccuracy = newAccuracy;\n\t\t\tif (simAccuracyDropdown != null)\n\t\t\t\tsimAccuracyDropdown.selectedIndex = newAccuracy;\n\t\t}\n\n\t\tpublic static void setVehicleRestrictionsAggression(VehicleRestrictionsAggression val) {\n\t\t\tbool changed = vehicleRestrictionsAggression != val;\n\t\t\tvehicleRestrictionsAggression = val;\n\t\t\tif (changed && vehicleRestrictionsAggressionDropdown != null) {\n\t\t\t\tvehicleRestrictionsAggressionDropdown.selectedIndex = (int)val;\n\t\t\t}\n\t\t}\n\n\t\t/*public static void setLaneChangingRandomization(int newLaneChangingRandomization) {\n\t\t\tlaneChangingRandomization = newLaneChangingRandomization;\n\t\t\tif (laneChangingRandomizationDropdown != null)\n\t\t\t\tlaneChangingRandomizationDropdown.selectedIndex = newLaneChangingRandomization;\n\t\t}*/\n\n\t\tpublic static void setRecklessDrivers(int newRecklessDrivers) {\n\t\t\trecklessDrivers = newRecklessDrivers;\n\t\t\tif (recklessDriversDropdown != null)\n\t\t\t\trecklessDriversDropdown.selectedIndex = newRecklessDrivers;\n\t\t}\n\n\t\tinternal static bool isStockLaneChangerUsed() {\n\t\t\treturn !advancedAI;\n\t\t}\n\n\t\tpublic static void setRelaxedBusses(bool newRelaxedBusses) {\n\t\t\trelaxedBusses = newRelaxedBusses;\n\t\t\tif (relaxedBussesToggle != null)\n\t\t\t\trelaxedBussesToggle.isChecked = newRelaxedBusses;\n\t\t}\n\n\t\tpublic static void setAllRelaxed(bool newAllRelaxed) {\n\t\t\tallRelaxed = newAllRelaxed;\n\t\t\tif (allRelaxedToggle != null)\n\t\t\t\tallRelaxedToggle.isChecked = newAllRelaxed;\n\t\t}\n\n\t\tpublic static void setHighwayRules(bool newHighwayRules) {\n\t\t\thighwayRules = newHighwayRules;\n\n\t\t\tif (highwayRulesToggle != null)\n\t\t\t\thighwayRulesToggle.isChecked = highwayRules;\n\t\t}\n\n\t\tpublic static void setPreferOuterLane(bool val) {\n\t\t\tpreferOuterLane = val;\n\n\t\t\tif (preferOuterLaneToggle != null)\n\t\t\t\tpreferOuterLaneToggle.isChecked = preferOuterLane;\n\t\t}\n\n\t\tpublic static void setShowLanes(bool newShowLanes) {\n\t\t\tshowLanes = newShowLanes;\n\t\t\tif (showLanesToggle != null)\n\t\t\t\tshowLanesToggle.isChecked = newShowLanes;\n\t\t}\n\n\t\tpublic static void setAdvancedAI(bool newAdvancedAI) {\n\t\t\tbool changed = newAdvancedAI != advancedAI;\n\t\t\tadvancedAI = newAdvancedAI;\n\n\t\t\tif (changed && advancedAIToggle != null) {\n\t\t\t\tadvancedAIToggle.isChecked = newAdvancedAI;\n\t\t\t}\n\n\t\t\tif (changed && !newAdvancedAI) {\n\t\t\t\tsetAltLaneSelectionRatio(0);\n\t\t\t}\n\t\t}\n\n\t\tpublic static void setGuiTransparency(byte val) {\n\t\t\tbool changed = val != GlobalConfig.Instance.Main.GuiTransparency;\n\t\t\tGlobalConfig.Instance.Main.GuiTransparency = val;\n\n\t\t\tif (changed && guiTransparencySlider != null) {\n\t\t\t\tguiTransparencySlider.value = val;\n\t\t\t}\n\t\t}\n\n\t\tpublic static void setOverlayTransparency(byte val) {\n\t\t\tbool changed = val != GlobalConfig.Instance.Main.OverlayTransparency;\n\t\t\tGlobalConfig.Instance.Main.OverlayTransparency = val;\n\n\t\t\tif (changed && overlayTransparencySlider != null) {\n\t\t\t\toverlayTransparencySlider.value = val;\n\t\t\t}\n\t\t}\n\n\t\tpublic static void setAltLaneSelectionRatio(byte val) {\n\t\t\tbool changed = val != altLaneSelectionRatio;\n\t\t\taltLaneSelectionRatio = val;\n\n\t\t\tif (changed && altLaneSelectionRatioSlider != null) {\n\t\t\t\taltLaneSelectionRatioSlider.value = val;\n\t\t\t}\n\n\t\t\tif (changed && altLaneSelectionRatio > 0) {\n\t\t\t\tsetAdvancedAI(true);\n\t\t\t}\n\t\t}\n\n\t\tpublic static void setEvacBussesMayIgnoreRules(bool value) {\n\t\t\tif (! SteamHelper.IsDLCOwned(SteamHelper.DLC.NaturalDisastersDLC))\n\t\t\t\tvalue = false;\n\n\t\t\tevacBussesMayIgnoreRules = value;\n\t\t\tif (evacBussesMayIgnoreRulesToggle != null)\n\t\t\t\tevacBussesMayIgnoreRulesToggle.isChecked = value;\n\t\t}\n\n\t\tpublic static void setInstantEffects(bool value) {\n\t\t\tinstantEffects = value;\n\t\t\tif (instantEffectsToggle != null)\n\t\t\t\tinstantEffectsToggle.isChecked = value;\n\t\t}\n\n\t\tpublic static void setMayEnterBlockedJunctions(bool newMayEnterBlockedJunctions) {\n\t\t\tallowEnterBlockedJunctions = newMayEnterBlockedJunctions;\n\t\t\tif (allowEnterBlockedJunctionsToggle != null)\n\t\t\t\tallowEnterBlockedJunctionsToggle.isChecked = newMayEnterBlockedJunctions;\n\t\t}\n\n\t\tpublic static void setStrongerRoadConditionEffects(bool newStrongerRoadConditionEffects) {\n\t\t\tif (!SteamHelper.IsDLCOwned(SteamHelper.DLC.SnowFallDLC)) {\n\t\t\t\tnewStrongerRoadConditionEffects = false;\n\t\t\t}\n\n\t\t\tstrongerRoadConditionEffects = newStrongerRoadConditionEffects;\n\t\t\tif (strongerRoadConditionEffectsToggle != null)\n\t\t\t\tstrongerRoadConditionEffectsToggle.isChecked = newStrongerRoadConditionEffects;\n\t\t}\n\n\t\tpublic static void setProhibitPocketCars(bool newValue) {\n\t\t\tbool valueChanged = newValue != prohibitPocketCars;\n\t\t\tprohibitPocketCars = newValue;\n\t\t\tif (prohibitPocketCarsToggle != null)\n\t\t\t\tprohibitPocketCarsToggle.isChecked = newValue;\n\t\t}\n\n\t\tpublic static void setRealisticPublicTransport(bool newValue) {\n\t\t\tbool valueChanged = newValue != realisticPublicTransport;\n\t\t\trealisticPublicTransport = newValue;\n\t\t\tif (realisticPublicTransportToggle != null)\n\t\t\t\trealisticPublicTransportToggle.isChecked = newValue;\n\t\t}\n\n\t\tpublic static void setRealisticSpeeds(bool newValue) {\n\t\t\trealisticSpeeds = newValue;\n\t\t\tif (realisticSpeedsToggle != null)\n\t\t\t\trealisticSpeedsToggle.isChecked = newValue;\n\t\t}\n\n\t\tpublic static void setDisableDespawning(bool value) {\n\t\t\tdisableDespawning = value;\n\n\t\t\tif (disableDespawningToggle != null)\n\t\t\t\tdisableDespawningToggle.isChecked = value;\n\t\t}\n\n\t\tpublic static void setAllowUTurns(bool value) {\n\t\t\tallowUTurns = value;\n\t\t\tif (allowUTurnsToggle != null)\n\t\t\t\tallowUTurnsToggle.isChecked = value;\n\t\t\tConstants.ManagerFactory.JunctionRestrictionsManager.UpdateAllDefaults();\n\t\t}\n\n\t\tpublic static void setAllowNearTurnOnRed(bool newValue) {\n\t\t\tallowNearTurnOnRed = newValue;\n\t\t\tif (allowNearTurnOnRedToggle != null)\n\t\t\t\tallowNearTurnOnRedToggle.isChecked = newValue;\n\t\t\tConstants.ManagerFactory.JunctionRestrictionsManager.UpdateAllDefaults();\n\t\t}\n\n\t\tpublic static void setAllowFarTurnOnRed(bool newValue) {\n\t\t\tallowFarTurnOnRed = newValue;\n\t\t\tif (allowFarTurnOnRedToggle != null)\n\t\t\t\tallowFarTurnOnRedToggle.isChecked = newValue;\n\t\t\tConstants.ManagerFactory.JunctionRestrictionsManager.UpdateAllDefaults();\n\t\t}\n\n\t\tpublic static void setAllowLaneChangesWhileGoingStraight(bool value) {\n\t\t\tallowLaneChangesWhileGoingStraight = value;\n\t\t\tif (allowLaneChangesWhileGoingStraightToggle != null)\n\t\t\t\tallowLaneChangesWhileGoingStraightToggle.isChecked = value;\n\t\t\tConstants.ManagerFactory.JunctionRestrictionsManager.UpdateAllDefaults();\n\t\t}\n\n\t\tpublic static void setAllowEnterBlockedJunctions(bool value) {\n\t\t\tallowEnterBlockedJunctions = value;\n\t\t\tif (allowEnterBlockedJunctionsToggle != null)\n\t\t\t\tallowEnterBlockedJunctionsToggle.isChecked = value;\n\t\t\tConstants.ManagerFactory.JunctionRestrictionsManager.UpdateAllDefaults();\n\t\t}\n\n\t\tpublic static void setTrafficLightPriorityRules(bool value) {\n\t\t\ttrafficLightPriorityRules = value;\n\t\t\tif (trafficLightPriorityRulesToggle != null)\n\t\t\t\ttrafficLightPriorityRulesToggle.isChecked = value;\n\t\t}\n\n\t\tpublic static void setBanRegularTrafficOnBusLanes(bool value) {\n\t\t\tbanRegularTrafficOnBusLanes = value;\n\t\t\tif (banRegularTrafficOnBusLanesToggle != null)\n\t\t\t\tbanRegularTrafficOnBusLanesToggle.isChecked = value;\n\n\t\t\tVehicleRestrictionsManager.Instance.ClearCache();\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tpublic static void setPrioritySignsOverlay(bool newPrioritySignsOverlay) {\n\t\t\tprioritySignsOverlay = newPrioritySignsOverlay;\n\t\t\tif (prioritySignsOverlayToggle != null)\n\t\t\t\tprioritySignsOverlayToggle.isChecked = newPrioritySignsOverlay;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tpublic static void setTimedLightsOverlay(bool newTimedLightsOverlay) {\n\t\t\ttimedLightsOverlay = newTimedLightsOverlay;\n\t\t\tif (timedLightsOverlayToggle != null)\n\t\t\t\ttimedLightsOverlayToggle.isChecked = newTimedLightsOverlay;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tpublic static void setSpeedLimitsOverlay(bool newSpeedLimitsOverlay) {\n\t\t\tspeedLimitsOverlay = newSpeedLimitsOverlay;\n\t\t\tif (speedLimitsOverlayToggle != null)\n\t\t\t\tspeedLimitsOverlayToggle.isChecked = newSpeedLimitsOverlay;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tpublic static void setVehicleRestrictionsOverlay(bool newVehicleRestrictionsOverlay) {\n\t\t\tvehicleRestrictionsOverlay = newVehicleRestrictionsOverlay;\n\t\t\tif (vehicleRestrictionsOverlayToggle != null)\n\t\t\t\tvehicleRestrictionsOverlayToggle.isChecked = newVehicleRestrictionsOverlay;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tpublic static void setParkingRestrictionsOverlay(bool newParkingRestrictionsOverlay) {\n\t\t\tparkingRestrictionsOverlay = newParkingRestrictionsOverlay;\n\t\t\tif (parkingRestrictionsOverlayToggle != null)\n\t\t\t\tparkingRestrictionsOverlayToggle.isChecked = newParkingRestrictionsOverlay;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tpublic static void setJunctionRestrictionsOverlay(bool newValue) {\n\t\t\tjunctionRestrictionsOverlay = newValue;\n\t\t\tif (junctionRestrictionsOverlayToggle != null)\n\t\t\t\tjunctionRestrictionsOverlayToggle.isChecked = newValue;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tpublic static void setConnectedLanesOverlay(bool newValue) {\n\t\t\tconnectedLanesOverlay = newValue;\n\t\t\tif (connectedLanesOverlayToggle != null)\n\t\t\t\tconnectedLanesOverlayToggle.isChecked = newValue;\n\t\t}\n\n\t\tpublic static void setNodesOverlay(bool newNodesOverlay) {\n\t\t\tnodesOverlay = newNodesOverlay;\n\t\t\tif (nodesOverlayToggle != null)\n\t\t\t\tnodesOverlayToggle.isChecked = newNodesOverlay;\n\n\t\t\tUIBase.GetTrafficManagerTool(false)?.InitializeSubTools();\n\t\t}\n\n\t\tpublic static void setVehicleOverlay(bool newVal) {\n\t\t\tvehicleOverlay = newVal;\n\t\t\tif (vehicleOverlayToggle != null)\n\t\t\t\tvehicleOverlayToggle.isChecked = newVal;\n\t\t}\n\n\t\tpublic static void setPrioritySignsEnabled(bool newValue) {\n\t\t\tMenuRebuildRequired = true;\n\t\t\tprioritySignsEnabled = newValue;\n\t\t\tif (enablePrioritySignsToggle != null)\n\t\t\t\tenablePrioritySignsToggle.isChecked = newValue;\n\t\t\tif (!newValue)\n\t\t\t\tsetPrioritySignsOverlay(false);\n\t\t}\n\n\t\tpublic static void setTimedLightsEnabled(bool newValue) {\n\t\t\tMenuRebuildRequired = true;\n\t\t\ttimedLightsEnabled = newValue;\n\t\t\tif (enableTimedLightsToggle != null)\n\t\t\t\tenableTimedLightsToggle.isChecked = newValue;\n\t\t\tif (!newValue)\n\t\t\t\tsetTimedLightsOverlay(false);\n\t\t}\n\n\t\tpublic static void setCustomSpeedLimitsEnabled(bool newValue) {\n\t\t\tMenuRebuildRequired = true;\n\t\t\tcustomSpeedLimitsEnabled = newValue;\n\t\t\tif (enableCustomSpeedLimitsToggle != null)\n\t\t\t\tenableCustomSpeedLimitsToggle.isChecked = newValue;\n\t\t\tif (!newValue)\n\t\t\t\tsetSpeedLimitsOverlay(false);\n\t\t}\n\n\t\tpublic static void setVehicleRestrictionsEnabled(bool newValue) {\n\t\t\tMenuRebuildRequired = true;\n\t\t\tvehicleRestrictionsEnabled = newValue;\n\t\t\tif (enableVehicleRestrictionsToggle != null)\n\t\t\t\tenableVehicleRestrictionsToggle.isChecked = newValue;\n\t\t\tif (!newValue)\n\t\t\t\tsetVehicleRestrictionsOverlay(false);\n\t\t}\n\n\t\tpublic static void setParkingRestrictionsEnabled(bool newValue) {\n\t\t\tMenuRebuildRequired = true;\n\t\t\tparkingRestrictionsEnabled = newValue;\n\t\t\tif (enableParkingRestrictionsToggle != null)\n\t\t\t\tenableParkingRestrictionsToggle.isChecked = newValue;\n\t\t\tif (!newValue)\n\t\t\t\tsetParkingRestrictionsOverlay(false);\n\t\t}\n\n\t\tpublic static void setJunctionRestrictionsEnabled(bool newValue) {\n\t\t\tMenuRebuildRequired = true;\n\t\t\tjunctionRestrictionsEnabled = newValue;\n\t\t\tif (enableJunctionRestrictionsToggle != null)\n\t\t\t\tenableJunctionRestrictionsToggle.isChecked = newValue;\n\t\t\tif (!newValue)\n\t\t\t\tsetJunctionRestrictionsOverlay(false);\n\t\t}\n\n\t\tpublic static void setTurnOnRedEnabled(bool newValue) {\n\t\t\tturnOnRedEnabled = newValue;\n\t\t\tif (turnOnRedEnabledToggle != null)\n\t\t\t\tturnOnRedEnabledToggle.isChecked = newValue;\n\t\t\tif (!newValue) {\n\t\t\t\tsetAllowNearTurnOnRed(false);\n\t\t\t\tsetAllowFarTurnOnRed(false);\n\t\t\t}\n\t\t}\n\n\t\tpublic static void setLaneConnectorEnabled(bool newValue) {\n\t\t\tMenuRebuildRequired = true;\n\t\t\tlaneConnectorEnabled = newValue;\n\t\t\tif (enableLaneConnectorToggle != null)\n\t\t\t\tenableLaneConnectorToggle.isChecked = newValue;\n\t\t\tif (!newValue)\n\t\t\t\tsetConnectedLanesOverlay(false);\n\t\t}\n\n#if QUEUEDSTATS\n\t\tpublic static void setShowPathFindStats(bool value) {\n\t\t\tshowPathFindStats = value;\n\t\t\tif (showPathFindStatsToggle != null)\n\t\t\t\tshowPathFindStatsToggle.isChecked = value;\n\t\t}\n#endif\n\n\t\t/*internal static int getLaneChangingRandomizationTargetValue() {\n\t\t\tint ret = 100;\n\t\t\tswitch (laneChangingRandomization) {\n\t\t\t\tcase 0:\n\t\t\t\t\tret = 2;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tret = 4;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tret = 10;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tret = 20;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tret = 50;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn ret;\n\t\t}*/\n\n\t\t/*internal static float getLaneChangingProbability() {\n\t\t\tswitch (laneChangingRandomization) {\n\t\t\t\tcase 0:\n\t\t\t\t\treturn 0.5f;\n\t\t\t\tcase 1:\n\t\t\t\t\treturn 0.25f;\n\t\t\t\tcase 2:\n\t\t\t\t\treturn 0.1f;\n\t\t\t\tcase 3:\n\t\t\t\t\treturn 0.05f;\n\t\t\t\tcase 4:\n\t\t\t\t\treturn 0.01f;\n\t\t\t}\n\t\t\treturn 0.01f;\n\t\t}*/\n\n\t\tinternal static int getRecklessDriverModulo() {\n\t\t\tswitch (recklessDrivers) {\n\t\t\t\tcase 0:\n\t\t\t\t\treturn 10;\n\t\t\t\tcase 1:\n\t\t\t\t\treturn 20;\n\t\t\t\tcase 2:\n\t\t\t\t\treturn 50;\n\t\t\t\tcase 3:\n\t\t\t\t\treturn 10000;\n\t\t\t}\n\t\t\treturn 10000;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/State/README.md",
    "content": "# TM:PE -- /State\nConfiguration and classes for loading and saving.\n## Classes\n- **Configuration**: Serializable classes that is used to store the custom game state together with the savegame data.\n- **Flags**: Custom game state storage backend (deprecated, marked for deletion: Custom game state information should be accessed through the *Manager classes).\n- **GlobalConfig**: Represents the global configuration that is stored in TMPE_GlobalConfig.xml\n- **Options**: Holds player-set options. Builds the options dialog.\n- **SerializableDataExtension**: Main load/save class. Serializes/Deserializes **Configuration** instances to/from savegame data."
  },
  {
    "path": "TLM/TLM/State/SerializableDataExtension.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Runtime.Serialization.Formatters.Binary;\nusing System.Threading;\nusing ColossalFramework;\nusing ICities;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing UnityEngine;\nusing Random = UnityEngine.Random;\nusing Timer = System.Timers.Timer;\nusing TrafficManager.State;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.UI;\nusing TrafficManager.Manager;\nusing TrafficManager.Traffic;\nusing ColossalFramework.UI;\nusing TrafficManager.Util;\nusing System.Linq;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Geometry.Impl;\n\nnamespace TrafficManager.State {\n\tpublic class SerializableDataExtension : SerializableDataExtensionBase {\n\t\tprivate const string DataId = \"TrafficManager_v1.0\";\n\n\t\tprivate static ISerializableData _serializableData;\n\t\tprivate static Configuration _configuration;\n\t\tpublic static bool StateLoading = false;\n\n\t\tpublic override void OnCreated(ISerializableData serializableData) {\n\t\t\t_serializableData = serializableData;\n\t\t}\n\n\t\tpublic override void OnReleased() {\n\t\t}\n\t\t\n\t\tpublic override void OnLoadData() {\n\t\t\tLog.Info(\"Loading Traffic Manager: PE Data\");\n\t\t\tStateLoading = true;\n\t\t\tbool loadingSucceeded = true;\n\t\t\ttry {\n\t\t\t\tLog.Info(\"Initializing flags\");\n\t\t\t\tFlags.OnBeforeLoadData();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"OnLoadData: Error while initializing Flags: {e.ToString()}\");\n\t\t\t\tloadingSucceeded = false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tLog.Info(\"Initializing node geometries\");\n\t\t\t\tNodeGeometry.OnBeforeLoadData();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"OnLoadData: Error while initializing NodeGeometry: {e.ToString()}\");\n\t\t\t\tloadingSucceeded = false;\n\t\t\t}\n\t\t\t\n\t\t\ttry {\n\t\t\t\tLog.Info(\"Initializing segment geometries\");\n\t\t\t\tSegmentGeometry.OnBeforeLoadData();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"OnLoadData: Error while initializing SegmentGeometry: {e.ToString()}\");\n\t\t\t\tloadingSucceeded = false;\n\t\t\t}\n\n\t\t\tforeach (ICustomManager manager in LoadingExtension.RegisteredManagers) {\n\t\t\t\ttry {\n\t\t\t\t\tLog.Info($\"OnBeforeLoadData: {manager.GetType().Name}\");\n\t\t\t\t\tmanager.OnBeforeLoadData();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"OnLoadData: Error while initializing {manager.GetType().Name}: {e.ToString()}\");\n\t\t\t\t\tloadingSucceeded = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tLog.Info(\"Initialization done. Loading mod data now.\");\n\n\t\t\ttry {\n\t\t\t\tbyte[] data = _serializableData.LoadData(DataId);\n\t\t\t\tDeserializeData(data);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"OnLoadData: Error while deserializing data: {e.ToString()}\");\n\t\t\t\tloadingSucceeded = false;\n\t\t\t}\n\n\t\t\t// load options\n\t\t\ttry {\n\t\t\t\tbyte[] options = _serializableData.LoadData(\"TMPE_Options\");\n\t\t\t\tif (options != null) {\n\t\t\t\t\tif (! OptionsManager.Instance.LoadData(options)) {\n\t\t\t\t\t\tloadingSucceeded = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"OnLoadData: Error while loading options: {e.ToString()}\");\n\t\t\t\tloadingSucceeded = false;\n\t\t\t}\n\n\t\t\tif (loadingSucceeded)\n\t\t\t\tLog.Info(\"OnLoadData completed successfully.\");\n\t\t\telse {\n\t\t\t\tLog.Info(\"An error occurred while loading.\");\n\t\t\t\t//UIView.library.ShowModal<ExceptionPanel>(\"ExceptionPanel\").SetMessage(\"An error occurred while loading\", \"Traffic Manager: President Edition detected an error while loading. Please do NOT save this game under the old filename, otherwise your timed traffic lights, custom lane arrows, etc. are in danger. Instead, please navigate to http://steamcommunity.com/sharedfiles/filedetails/?id=583429740 and follow the steps under 'In case problems arise'.\", true);\n\t\t\t}\n\n\t\t\tStateLoading = false;\n\t\t\t\n\t\t\tforeach (ICustomManager manager in LoadingExtension.RegisteredManagers) {\n\t\t\t\ttry {\n\t\t\t\t\tLog.Info($\"OnAfterLoadData: {manager.GetType().Name}\");\n\t\t\t\t\tmanager.OnAfterLoadData();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"OnLoadData: Error while initializing {manager.GetType().Name}: {e.ToString()}\");\n\t\t\t\t\tloadingSucceeded = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static void DeserializeData(byte[] data) {\n\t\t\tbool error = false;\n\t\t\ttry {\n\t\t\t\tif (data != null && data.Length != 0) {\n\t\t\t\t\tLog.Info($\"Loading Data from New Load Routine! Length={data.Length}\");\n\t\t\t\t\tvar memoryStream = new MemoryStream();\n\t\t\t\t\tmemoryStream.Write(data, 0, data.Length);\n\t\t\t\t\tmemoryStream.Position = 0;\n\n\t\t\t\t\tvar binaryFormatter = new BinaryFormatter();\n\t\t\t\t\t_configuration = (Configuration)binaryFormatter.Deserialize(memoryStream);\n\t\t\t\t} else {\n\t\t\t\t\tLog.Info(\"No data to deserialize!\");\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"Error deserializing data: {e.ToString()}\");\n\t\t\t\tLog.Info(e.StackTrace);\n\t\t\t\terror = true;\n\t\t\t}\n\n\t\t\tif (!error) {\n\t\t\t\tLoadDataState(out error);\n\t\t\t}\n\n\t\t\tif (error) {\n\t\t\t\tthrow new ApplicationException(\"An error occurred while loading\");\n\t\t\t}\n\t\t}\n\n\t\tprivate static void LoadDataState(out bool error) {\n\t\t\terror = false;\n\n\t\t\tLog.Info(\"Loading State from Config\");\n\t\t\tif (_configuration == null) {\n\t\t\t\tLog.Warning(\"Configuration NULL, Couldn't load save data. Possibly a new game?\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tTrafficPriorityManager prioMan = TrafficPriorityManager.Instance;\n\n\t\t\t// load ext. citizens\n\t\t\tif (_configuration.ExtCitizens != null) {\n\t\t\t\tif (!ExtCitizenManager.Instance.LoadData(_configuration.ExtCitizens)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Ext. citizen data structure undefined!\");\n\t\t\t}\n\n\t\t\t// load ext. citizen instances\n\t\t\tif (_configuration.ExtCitizenInstances != null) {\n\t\t\t\tif (!ExtCitizenInstanceManager.Instance.LoadData(_configuration.ExtCitizenInstances)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Ext. citizen instance data structure undefined!\");\n\t\t\t}\n\n\t\t\t// load priority segments\n\t\t\tif (_configuration.PrioritySegments != null) {\n\t\t\t\tif (! TrafficPriorityManager.Instance.LoadData(_configuration.PrioritySegments)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Priority segments data structure (old) undefined!\");\n\t\t\t}\n\n\t\t\tif (_configuration.CustomPrioritySegments != null) {\n\t\t\t\tif (!TrafficPriorityManager.Instance.LoadData(_configuration.CustomPrioritySegments)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Priority segments data structure (new) undefined!\");\n\t\t\t}\n\n\t\t\t// load parking restrictions\n\t\t\tif (_configuration.ParkingRestrictions != null) {\n\t\t\t\tif (!ParkingRestrictionsManager.Instance.LoadData(_configuration.ParkingRestrictions)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Parking restrctions structure undefined!\");\n\t\t\t}\n\n\t\t\t// load vehicle restrictions (warning: has to be done before loading timed lights!)\n\t\t\tif (_configuration.LaneAllowedVehicleTypes != null) {\n\t\t\t\tif (! VehicleRestrictionsManager.Instance.LoadData(_configuration.LaneAllowedVehicleTypes)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Vehicle restrctions structure undefined!\");\n\t\t\t}\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tif (_configuration.TimedLights != null) {\n\t\t\t\tif (! TrafficLightSimulationManager.Instance.LoadData(_configuration.TimedLights)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Timed traffic lights data structure undefined!\");\n\t\t\t}\n\n\t\t\t// load toggled traffic lights (old method)\n\t\t\tif (_configuration.NodeTrafficLights != null) {\n\t\t\t\tif (! TrafficLightManager.Instance.LoadData(_configuration.NodeTrafficLights)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Junction traffic lights data structure (old) undefined!\");\n\t\t\t}\n\n\t\t\t// load toggled traffic lights (new method)\n\t\t\tif (_configuration.ToggledTrafficLights != null) {\n\t\t\t\tif (!TrafficLightManager.Instance.LoadData(_configuration.ToggledTrafficLights)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Junction traffic lights data structure (new) undefined!\");\n\t\t\t}\n\n\t\t\t// load lane arrrows (old method)\n\t\t\tif (_configuration.LaneFlags != null) {\n\t\t\t\tif (!LaneArrowManager.Instance.LoadData(_configuration.LaneFlags)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Lane arrow data structure (old) undefined!\");\n\t\t\t}\n\n\t\t\t// load lane arrows (new method)\n\t\t\tif (_configuration.LaneArrows != null) {\n\t\t\t\tif (!LaneArrowManager.Instance.LoadData(_configuration.LaneArrows)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Lane arrow data structure (new) undefined!\");\n\t\t\t}\n\n\t\t\t// load lane connections\n\t\t\tif (_configuration.LaneConnections != null) {\n\t\t\t\tif (!LaneConnectionManager.Instance.LoadData(_configuration.LaneConnections)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Lane connection data structure undefined!\");\n\t\t\t}\n\n\t\t\t// Load custom default speed limits\n\t\t\tif (_configuration.CustomDefaultSpeedLimits != null) {\n\t\t\t\tif (!SpeedLimitManager.Instance.LoadData(_configuration.CustomDefaultSpeedLimits)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// load speed limits\n\t\t\tif (_configuration.LaneSpeedLimits != null) {\n\t\t\t\tif (!SpeedLimitManager.Instance.LoadData(_configuration.LaneSpeedLimits)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Lane speed limit structure undefined!\");\n\t\t\t}\n\n\t\t\t// Load segment-at-node flags\n\t\t\tif (_configuration.SegmentNodeConfs != null) {\n\t\t\t\tif (!JunctionRestrictionsManager.Instance.LoadData(_configuration.SegmentNodeConfs)) {\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.Info(\"Segment-at-node structure undefined!\");\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnSaveData() {\n\t\t\tbool success = true;\n\n\t\t\t/*try {\n\t\t\t\tLog.Info(\"Recalculating segment geometries\");\n\t\t\t\tSegmentGeometry.OnBeforeSaveData();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"OnSaveData: Exception occurred while calling SegmentGeometry.OnBeforeSaveData: {e.ToString()}\");\n\t\t\t\terror = true;\n\t\t\t}*/\n\n\t\t\tforeach (ICustomManager manager in LoadingExtension.RegisteredManagers) {\n\t\t\t\ttry {\n\t\t\t\t\tLog.Info($\"OnBeforeSaveData: {manager.GetType().Name}\");\n\t\t\t\t\tmanager.OnBeforeSaveData();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"OnSaveData: Error while notifying {manager.GetType().Name}.OnBeforeSaveData: {e.ToString()}\");\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tLog.Info(\"Saving Mod Data.\");\n\t\t\t\tvar configuration = new Configuration();\n\n\t\t\t\tTrafficPriorityManager prioMan = TrafficPriorityManager.Instance;\n\n\t\t\t\tconfiguration.ExtCitizens = ExtCitizenManager.Instance.SaveData(ref success);\n\t\t\t\tconfiguration.ExtCitizenInstances = ExtCitizenInstanceManager.Instance.SaveData(ref success);\n\n\t\t\t\tconfiguration.PrioritySegments = ((ICustomDataManager<List<int[]>>)TrafficPriorityManager.Instance).SaveData(ref success);\n\t\t\t\tconfiguration.CustomPrioritySegments = ((ICustomDataManager<List<Configuration.PrioritySegment>>)TrafficPriorityManager.Instance).SaveData(ref success);\n\n\t\t\t\tconfiguration.SegmentNodeConfs = JunctionRestrictionsManager.Instance.SaveData(ref success);\n\n\t\t\t\tconfiguration.TimedLights = TrafficLightSimulationManager.Instance.SaveData(ref success);\n\n\t\t\t\t//configuration.NodeTrafficLights = ((ICustomDataManager<string>)TrafficLightManager.Instance).SaveData(ref success);\n\t\t\t\t//configuration.ToggledTrafficLights = ((ICustomDataManager<List<Configuration.NodeTrafficLight>>)TrafficLightManager.Instance).SaveData(ref success);\n\t\t\t\t\n\t\t\t\tconfiguration.LaneFlags = ((ICustomDataManager<string>)LaneArrowManager.Instance).SaveData(ref success);\n\t\t\t\tconfiguration.LaneArrows = ((ICustomDataManager<List<Configuration.LaneArrowData>>)LaneArrowManager.Instance).SaveData(ref success);\n\n\t\t\t\tconfiguration.LaneConnections = LaneConnectionManager.Instance.SaveData(ref success);\n\n\t\t\t\tconfiguration.LaneSpeedLimits = ((ICustomDataManager<List<Configuration.LaneSpeedLimit>>)SpeedLimitManager.Instance).SaveData(ref success);\n\n\t\t\t\tconfiguration.CustomDefaultSpeedLimits = ((ICustomDataManager<Dictionary<string, float>>)SpeedLimitManager.Instance).SaveData(ref success);\n\n\t\t\t\tconfiguration.LaneAllowedVehicleTypes = VehicleRestrictionsManager.Instance.SaveData(ref success);\n\t\t\t\tconfiguration.ParkingRestrictions = ParkingRestrictionsManager.Instance.SaveData(ref success);\n\n\t\t\t\ttry {\n\t\t\t\t\t// save options\n\t\t\t\t\t_serializableData.SaveData(\"TMPE_Options\", OptionsManager.Instance.SaveData(ref success));\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tLog.Error(\"Unexpected error while saving options: \" + ex.Message);\n\t\t\t\t\tsuccess = false;\n\t\t\t\t}\n\n\t\t\t\tvar binaryFormatter = new BinaryFormatter();\n\t\t\t\tvar memoryStream = new MemoryStream();\n\n\t\t\t\ttry {\n\t\t\t\t\tbinaryFormatter.Serialize(memoryStream, configuration);\n\t\t\t\t\tmemoryStream.Position = 0;\n\t\t\t\t\tLog.Info($\"Save data byte length {memoryStream.Length}\");\n\t\t\t\t\t_serializableData.SaveData(DataId, memoryStream.ToArray());\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tLog.Error(\"Unexpected error while saving data: \" + ex.ToString());\n\t\t\t\t\tsuccess = false;\n\t\t\t\t} finally {\n\t\t\t\t\tmemoryStream.Close();\n\t\t\t\t}\n\n\t\t\t\tforeach (ICustomManager manager in LoadingExtension.RegisteredManagers) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tLog.Info($\"OnAfterSaveData: {manager.GetType().Name}\");\n\t\t\t\t\t\tmanager.OnAfterSaveData();\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLog.Error($\"OnSaveData: Error while notifying {manager.GetType().Name}.OnAfterSaveData: {e.ToString()}\");\n\t\t\t\t\t\tsuccess = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tsuccess = false;\n\t\t\t\tLog.Error($\"Error occurred while saving data: {e.ToString()}\");\n\t\t\t\t//UIView.library.ShowModal<ExceptionPanel>(\"ExceptionPanel\").SetMessage(\"An error occurred while saving\", \"Traffic Manager: President Edition detected an error while saving. To help preventing future errors, please navigate to http://steamcommunity.com/sharedfiles/filedetails/?id=583429740 and follow the steps under 'In case problems arise'.\", true);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TLM.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"12.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{7422AE58-8B0A-401C-9404-F4A438EFFE10}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>TrafficManager</RootNamespace>\n    <AssemblyName>TrafficManager</AssemblyName>\n    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;PF2;QUEUEDSTATS;PARKINGAI;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>PF2;QUEUEDSTATS;PARKINGAI;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'FullDebug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\DebugGeometry\\</OutputPath>\n    <DefineConstants>DEBUG;PF2;QUEUEDSTATS;DEBUGGEO;DEBUGVSTATE;DEBUGNEWPF;DEBUGCOSTS;DEBUGCONN2;DEBUGTTL;DEBUGHWJUNCTIONROUTING;DEBUGROUTING;DEBUGCONN;DEBUGHK;PARKINGAI;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS</DefineConstants>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <WarningLevel>4</WarningLevel>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Benchmark|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\Benchmark\\</OutputPath>\n    <DefineConstants>DEBUG;QUEUEDSTATS;DEBUGGEO;DEBUGVSTATE;DEBUGNEWPF;DEBUGCOSTS;DEBUGCONN2;DEBUGTTL;DEBUGHWJUNCTIONROUTING;DEBUGROUTING;BENCHMARK</DefineConstants>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <WarningLevel>4</WarningLevel>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'PF2_Debug|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\PF2_Debug\\</OutputPath>\n    <DefineConstants>DEBUG;QUEUEDSTATS;DEBUGGEO;DEBUGVSTATE;DEBUGNEWPF;DEBUGCOSTS;DEBUGCONN2;DEBUGTTL;DEBUGHWJUNCTIONROUTING;DEBUGROUTING;DEBUGHK;PF2;PARKINGAI;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS</DefineConstants>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <WarningLevel>4</WarningLevel>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Assembly-CSharp\">\n      <HintPath>..\\dependencies\\Assembly-CSharp.dll</HintPath>\n    </Reference>\n    <Reference Include=\"ColossalManaged\">\n      <HintPath>..\\dependencies\\ColossalManaged.dll</HintPath>\n    </Reference>\n    <Reference Include=\"ICities\">\n      <HintPath>..\\dependencies\\ICities.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\">\n      <HintPath>..\\dependencies\\System.Core.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System.Runtime.Serialization\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"UnityEngine\">\n      <HintPath>..\\dependencies\\UnityEngine.dll</HintPath>\n    </Reference>\n    <Reference Include=\"UnityEngine.Networking\">\n      <HintPath>..\\dependencies\\UnityEngine.Networking.dll</HintPath>\n    </Reference>\n    <Reference Include=\"UnityEngine.UI\">\n      <HintPath>..\\dependencies\\UnityEngine.UI.dll</HintPath>\n    </Reference>\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"CodeProfiler.cs\" />\n    <Compile Include=\"Constants.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomBuildingAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomBusAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomAmbulanceAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomCitizenAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomCommonBuildingAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomFireTruckAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomPoliceCarAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomResidentAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomShipAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomTaxiAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomTouristAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomTransportLineAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomTramBaseAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomVehicleAI.cs\" />\n    <Compile Include=\"Custom\\Data\\CustomVehicle.cs\" />\n    <Compile Include=\"Custom\\Manager\\CustomNetManager.cs\" />\n    <Compile Include=\"Custom\\Manager\\CustomCitizenManager.cs\" />\n    <Compile Include=\"Custom\\Manager\\CustomVehicleManager.cs\" />\n    <Compile Include=\"Custom\\PathFinding\\CustomPathFind.cs\" />\n    <Compile Include=\"Custom\\PathFinding\\CustomPathFind2.cs\" />\n    <Compile Include=\"Custom\\PathFinding\\StockPathFind.cs\" />\n    <Compile Include=\"Geometry\\GeometryCalculationMode.cs\" />\n    <Compile Include=\"Geometry\\ISegmentEndId.cs\" />\n    <Compile Include=\"Manager\\AbstractCustomManager.cs\" />\n    <Compile Include=\"Manager\\AbstractFeatureManager.cs\" />\n    <Compile Include=\"Manager\\AbstractGeometryObservingManager.cs\" />\n    <Compile Include=\"Manager\\ICustomSegmentLightsManager.cs\" />\n    <Compile Include=\"Manager\\IGeometryManager.cs\" />\n    <Compile Include=\"Manager\\IExtBuildingManager.cs\" />\n    <Compile Include=\"Manager\\IExtCitizenManager.cs\" />\n    <Compile Include=\"Manager\\IExtCitizenInstanceManager.cs\" />\n    <Compile Include=\"Manager\\IFeatureManager.cs\" />\n    <Compile Include=\"Manager\\IJunctionRestrictionsManager.cs\" />\n    <Compile Include=\"Manager\\ILaneArrowManager.cs\" />\n    <Compile Include=\"Manager\\ILaneConnectionManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\ExtCitizenManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\GeometryManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\TurnOnRedManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\ManagerFactory.cs\" />\n    <Compile Include=\"Manager\\Impl\\ParkingRestrictionsManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\RoutingManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\SegmentEndManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\VehicleBehaviorManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\ExtBuildingManager.cs\" />\n    <Compile Include=\"Manager\\ICustomDataManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\AdvancedParkingManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\LaneArrowManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\OptionsManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\TrafficLightManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\TrafficMeasurementManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\JunctionRestrictionsManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\UtilityManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\ExtCitizenInstanceManager.cs\" />\n    <Compile Include=\"Manager\\IAdvancedParkingManager.cs\" />\n    <Compile Include=\"Manager\\IOptionsManager.cs\" />\n    <Compile Include=\"Manager\\IParkingRestrictionsManager.cs\" />\n    <Compile Include=\"Manager\\IRoutingManager.cs\" />\n    <Compile Include=\"Manager\\ISegmentEndManager.cs\" />\n    <Compile Include=\"Manager\\ISpeedLimitManager.cs\" />\n    <Compile Include=\"Manager\\ITrafficLightManager.cs\" />\n    <Compile Include=\"Manager\\ITrafficLightSimulationManager.cs\" />\n    <Compile Include=\"Manager\\ITrafficMeasurementManager.cs\" />\n    <Compile Include=\"Manager\\ITrafficPriorityManager.cs\" />\n    <Compile Include=\"Manager\\ITurnOnRedManager.cs\" />\n    <Compile Include=\"Manager\\IUtilityManager.cs\" />\n    <Compile Include=\"Manager\\IVehicleBehaviorManager.cs\" />\n    <Compile Include=\"Manager\\IVehicleRestrictionsManager.cs\" />\n    <Compile Include=\"Manager\\IVehicleStateManager.cs\" />\n    <Compile Include=\"PrintTransportLines.cs\" />\n    <Compile Include=\"State\\ConfigData\\Debug.cs\" />\n    <Compile Include=\"State\\ConfigData\\DynamicLaneSelection.cs\" />\n    <Compile Include=\"State\\ConfigData\\Main.cs\" />\n    <Compile Include=\"State\\ConfigData\\ParkingAI.cs\" />\n    <Compile Include=\"State\\ConfigData\\PathFinding.cs\" />\n    <Compile Include=\"State\\ConfigData\\PriorityRules.cs\" />\n    <Compile Include=\"State\\ConfigData\\TimedTrafficLights.cs\" />\n    <Compile Include=\"State\\ConfigData\\AdvancedVehicleAI.cs\" />\n    <Compile Include=\"State\\GlobalConfig.cs\" />\n    <Compile Include=\"TrafficLight\\FlowWaitCalcMode.cs\" />\n    <Compile Include=\"TrafficLight\\ICustomSegmentLight.cs\" />\n    <Compile Include=\"TrafficLight\\Impl\\CustomSegmentLights.cs\" />\n    <Compile Include=\"TrafficLight\\ITimedTrafficLightsStep.cs\" />\n    <Compile Include=\"TrafficLight\\ITimedTrafficLights.cs\" />\n    <Compile Include=\"TrafficLight\\Data\\TrafficLightSimulation.cs\" />\n    <Compile Include=\"TrafficLight\\ICustomSegmentLights.cs\" />\n    <Compile Include=\"TrafficLight\\LightMode.cs\" />\n    <Compile Include=\"TrafficLight\\StepChangeMetric.cs\" />\n    <Compile Include=\"TrafficLight\\TrafficLightSimulationType.cs\" />\n    <Compile Include=\"Traffic\\Data\\ExtCitizen.cs\" />\n    <Compile Include=\"Traffic\\Data\\SegmentFlags.cs\" />\n    <Compile Include=\"Traffic\\Data\\SegmentEndFlags.cs\" />\n    <Compile Include=\"Traffic\\Data\\TurnOnRedSegments.cs\" />\n    <Compile Include=\"Traffic\\ISegmentEnd.cs\" />\n    <Compile Include=\"TrafficManager.cs\" />\n    <Compile Include=\"Traffic\\Data\\ExtBuilding.cs\" />\n    <Compile Include=\"Traffic\\Data\\ExtCitizenInstance.cs\" />\n    <Compile Include=\"Traffic\\ExtVehicleType.cs\" />\n    <Compile Include=\"Manager\\Impl\\LaneConnectionManager.cs\" />\n    <Compile Include=\"Geometry\\Impl\\NodeGeometry.cs\" />\n    <Compile Include=\"Geometry\\Impl\\SegmentEndGeometry.cs\" />\n    <Compile Include=\"Manager\\Impl\\VehicleStateManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\VehicleRestrictionsManager.cs\" />\n    <Compile Include=\"Geometry\\Impl\\SegmentEndId.cs\" />\n    <Compile Include=\"Traffic\\VehicleJunctionTransitState.cs\" />\n    <Compile Include=\"State\\Configuration.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomCarAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomCargoTruckAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomHumanAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomPassengerCarAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomRoadAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomTrainAI.cs\" />\n    <Compile Include=\"Custom\\PathFinding\\CustomPathManager.cs\" />\n    <Compile Include=\"Geometry\\Impl\\SegmentGeometry.cs\" />\n    <Compile Include=\"LoadingExtension.cs\" />\n    <Compile Include=\"UI\\CustomKeyHandler.cs\" />\n    <Compile Include=\"UI\\IncompatibleModsPanel.cs\" />\n    <Compile Include=\"UI\\RemoveCitizenInstanceButtonExtender.cs\" />\n    <Compile Include=\"UI\\RemoveVehicleButtonExtender.cs\" />\n    <Compile Include=\"UI\\MainMenu\\LaneConnectorButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\MainMenuPanel.cs\" />\n    <Compile Include=\"UI\\MainMenu\\StatsLabel.cs\" />\n    <Compile Include=\"UI\\MainMenu\\ParkingRestrictionsButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\VersionLabel.cs\" />\n    <Compile Include=\"UI\\MainMenu\\VehicleRestrictionsButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\ToggleTrafficLightsButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\TimedTrafficLightsButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\SpeedLimitsButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\PrioritySignsButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\ManualTrafficLightsButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\LaneArrowsButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\MenuToolModeButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\JunctionRestrictionsButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\DespawnButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\ClearTrafficButton.cs\" />\n    <Compile Include=\"UI\\MainMenu\\MenuButton.cs\" />\n    <Compile Include=\"UI\\LinearSpriteButton.cs\" />\n    <Compile Include=\"UI\\SubTools\\ParkingRestrictionsTool.cs\" />\n    <Compile Include=\"UI\\UIMainMenuButton.cs\" />\n    <Compile Include=\"UI\\SubTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\JunctionRestrictionsTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\SpeedLimitsTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\LaneConnectorTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\VehicleRestrictionsTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\LaneArrowTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\TimedTrafficLightsTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\ManualTrafficLightsTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\PrioritySignsTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\ToggleTrafficLightsTool.cs\" />\n    <Compile Include=\"UI\\Translation.cs\" />\n    <Compile Include=\"State\\Options.cs\" />\n    <Compile Include=\"Manager\\Impl\\SpeedLimitManager.cs\" />\n    <Compile Include=\"Traffic\\Data\\VehicleState.cs\" />\n    <Compile Include=\"State\\Flags.cs\" />\n    <Compile Include=\"Traffic\\Impl\\SegmentEnd.cs\" />\n    <Compile Include=\"UI\\TransportDemandViewMode.cs\" />\n    <Compile Include=\"UI\\UITransportDemand.cs\" />\n    <Compile Include=\"Util\\GenericObservable.cs\" />\n    <Compile Include=\"Util\\GenericUnsubscriber.cs\" />\n    <Compile Include=\"Manager\\ICustomManager.cs\" />\n    <Compile Include=\"Util\\IObservable.cs\" />\n    <Compile Include=\"Util\\IObserver.cs\" />\n    <Compile Include=\"Manager\\IManagerFactory.cs\" />\n    <Compile Include=\"Util\\IVisitor.cs\" />\n    <Compile Include=\"Util\\LoopUtil.cs\" />\n    <Compile Include=\"Util\\ModsCompatibilityChecker.cs\" />\n    <Compile Include=\"Util\\SegmentLaneTraverser.cs\" />\n    <Compile Include=\"Util\\TextureUtil.cs\" />\n    <Compile Include=\"Util\\TinyDictionary.cs\" />\n    <Compile Include=\"Util\\RedirectionHelper.cs\" />\n    <Compile Include=\"State\\SerializableDataExtension.cs\" />\n    <Compile Include=\"ThreadingExtension.cs\" />\n    <Compile Include=\"TrafficLight\\Impl\\CustomSegment.cs\" />\n    <Compile Include=\"TrafficLight\\Impl\\CustomSegmentLight.cs\" />\n    <Compile Include=\"UI\\ToolMode.cs\" />\n    <Compile Include=\"Manager\\Impl\\TrafficLightSimulationManager.cs\" />\n    <Compile Include=\"Manager\\Impl\\CustomSegmentLightsManager.cs\" />\n    <Compile Include=\"TrafficLight\\Impl\\TimedTrafficLights.cs\" />\n    <Compile Include=\"UI\\TrafficManagerTool.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"UI\\TextureResources.cs\" />\n    <Compile Include=\"TrafficManagerMod.cs\" />\n    <Compile Include=\"TrafficManagerMode.cs\" />\n    <Compile Include=\"TrafficLight\\Impl\\TimedTrafficLightsStep.cs\" />\n    <Compile Include=\"Manager\\Impl\\TrafficPriorityManager.cs\" />\n    <Compile Include=\"Traffic\\Data\\PrioritySegment.cs\" />\n    <Compile Include=\"UI\\UIBase.cs\" />\n    <Compile Include=\"UI\\MainMenu\\DebugMenu.cs\" />\n    <Compile Include=\"Util\\SegmentTraverser.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\light_1_1.png\" />\n    <EmbeddedResource Include=\"Resources\\light_1_2.png\" />\n    <EmbeddedResource Include=\"Resources\\light_1_3.png\" />\n    <EmbeddedResource Include=\"Resources\\light_2_1.png\" />\n    <EmbeddedResource Include=\"Resources\\light_2_2.png\" />\n    <EmbeddedResource Include=\"Resources\\light_2_3.png\" />\n    <EmbeddedResource Include=\"Resources\\light_3_1.png\" />\n    <EmbeddedResource Include=\"Resources\\light_3_2.png\" />\n    <EmbeddedResource Include=\"Resources\\light_3_3.png\" />\n    <EmbeddedResource Include=\"Resources\\light_4_1.png\" />\n    <EmbeddedResource Include=\"Resources\\light_4_2.png\" />\n    <EmbeddedResource Include=\"Resources\\light_4_3.png\" />\n    <EmbeddedResource Include=\"Resources\\light_5_1.png\" />\n    <EmbeddedResource Include=\"Resources\\light_5_2.png\" />\n    <EmbeddedResource Include=\"Resources\\light_5_3.png\" />\n    <EmbeddedResource Include=\"Resources\\light_6_1.png\" />\n    <EmbeddedResource Include=\"Resources\\light_6_2.png\" />\n    <EmbeddedResource Include=\"Resources\\light_6_3.png\" />\n    <EmbeddedResource Include=\"Resources\\light_counter.png\" />\n    <EmbeddedResource Include=\"Resources\\light_mode.png\" />\n    <EmbeddedResource Include=\"Resources\\light_yellow.png\" />\n    <EmbeddedResource Include=\"Resources\\pedestrian_light_1.png\" />\n    <EmbeddedResource Include=\"Resources\\pedestrian_light_2.png\" />\n    <EmbeddedResource Include=\"Resources\\pedestrian_mode_1.png\" />\n    <EmbeddedResource Include=\"Resources\\pedestrian_mode_2.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\sign_none.png\" />\n    <EmbeddedResource Include=\"Resources\\sign_priority.png\" />\n    <EmbeddedResource Include=\"Resources\\sign_stop.png\" />\n    <EmbeddedResource Include=\"Resources\\sign_yield.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\lang_zh-tw.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang_es.txt\" />\n    <EmbeddedResource Include=\"Resources\\lanechange_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\lanechange_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\bicycle_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\bus_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\cargotrain_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\cargotruck_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\passengercar_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\passengertrain_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\emergency_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\service_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\taxi_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\tram_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\bus_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\bus_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\cargotrain_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\cargotrain_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\cargotruck_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\cargotruck_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\emergency_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\emergency_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\passengercar_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\passengercar_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\passengertrain_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\passengertrain_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\service_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\service_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\taxi_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\taxi_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\light_counter_pl.png\" />\n    <EmbeddedResource Include=\"Resources\\light_mode_pl.png\" />\n    <EmbeddedResource Include=\"Resources\\pedestrian_mode_2_pl.png\" />\n    <EmbeddedResource Include=\"Resources\\lang_fr.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang_pl.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang_ru.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang_pt.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang_de.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang_ja.txt\" />\n    <EmbeddedResource Include=\"Resources\\light_counter_ja.png\" />\n    <EmbeddedResource Include=\"Resources\\light_mode_ja.png\" />\n    <EmbeddedResource Include=\"Resources\\0.png\" />\n    <EmbeddedResource Include=\"Resources\\10.png\" />\n    <EmbeddedResource Include=\"Resources\\100.png\" />\n    <EmbeddedResource Include=\"Resources\\120.png\" />\n    <EmbeddedResource Include=\"Resources\\130.png\" />\n    <EmbeddedResource Include=\"Resources\\20.png\" />\n    <EmbeddedResource Include=\"Resources\\30.png\" />\n    <EmbeddedResource Include=\"Resources\\40.png\" />\n    <EmbeddedResource Include=\"Resources\\50.png\" />\n    <EmbeddedResource Include=\"Resources\\60.png\" />\n    <EmbeddedResource Include=\"Resources\\70.png\" />\n    <EmbeddedResource Include=\"Resources\\80.png\" />\n    <EmbeddedResource Include=\"Resources\\90.png\" />\n    <EmbeddedResource Include=\"Resources\\clock_pause.png\" />\n    <EmbeddedResource Include=\"Resources\\clock_test.png\" />\n    <EmbeddedResource Include=\"Resources\\clock_play.png\" />\n    <EmbeddedResource Include=\"Resources\\remove_signs.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\lang_nl.txt\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\crossing_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\crossing_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\enterblocked_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\enterblocked_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\uturn_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\uturn_forbidden.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\110.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\MenuButton.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\lang_zh.txt\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\lang_it.txt\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\noimage.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\incompatible_mods.txt\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\CSUtil.CameraControl\\CSUtil.CameraControl\\CSUtil.CameraControl\\CSUtil.CameraControl.csproj\">\n      <Project>{f8759084-df5b-4a54-b73c-824640a8fa3f}</Project>\n      <Name>CSUtil.CameraControl</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\CSUtil.Commons\\CSUtil.Commons.csproj\">\n      <Project>{D3ADE06E-F493-4819-865A-3BB44FEEDF01}</Project>\n      <Name>CSUtil.Commons</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\TMPE.CitiesGameBridge\\TMPE.CitiesGameBridge.csproj\">\n      <Project>{3f2f7926-5d51-4880-a2b7-4594a10d7e54}</Project>\n      <Name>TMPE.CitiesGameBridge</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\TMPE.GenericGameBridge\\TMPE.GenericGameBridge.csproj\">\n      <Project>{663b991f-32a1-46e1-a4d3-540f8ea7f386}</Project>\n      <Name>TMPE.GenericGameBridge</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\mainmenu-btns.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\parking_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\parking_disallowed.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\lang_ko.txt\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\remove-btn.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\WindowBackground.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\left_on_red_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\left_on_red_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\right_on_red_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\right_on_red_forbidden.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.CodeFixes.dll\" />\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.dll\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <PropertyGroup>\n    <PostBuildEvent>mkdir \"$(LOCALAPPDATA)\\Colossal Order\\Cities_Skylines\\Addons\\Mods\\$(TargetName)\"\ndel /Q \"$(LOCALAPPDATA)\\Colossal Order\\Cities_Skylines\\Addons\\Mods\\$(TargetName)\\*\"\nxcopy /y \"$(TargetDir)TrafficManager.dll\" \"$(LOCALAPPDATA)\\Colossal Order\\Cities_Skylines\\Addons\\Mods\\$(TargetName)\"\nxcopy /y \"$(TargetDir)TMPE.CitiesGameBridge.dll\" \"$(LOCALAPPDATA)\\Colossal Order\\Cities_Skylines\\Addons\\Mods\\$(TargetName)\"\nxcopy /y \"$(TargetDir)TMPE.GenericGameBridge.dll\" \"$(LOCALAPPDATA)\\Colossal Order\\Cities_Skylines\\Addons\\Mods\\$(TargetName)\"\nxcopy /y \"$(TargetDir)CSUtil.CameraControl.dll\" \"$(LOCALAPPDATA)\\Colossal Order\\Cities_Skylines\\Addons\\Mods\\$(TargetName)\"\nxcopy /y \"$(TargetDir)CSUtil.Commons.dll\" \"$(LOCALAPPDATA)\\Colossal Order\\Cities_Skylines\\Addons\\Mods\\$(TargetName)\"</PostBuildEvent>\n  </PropertyGroup>\n  <PropertyGroup>\n    <PreBuildEvent>\n    </PreBuildEvent>\n  </PropertyGroup>\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.\n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "TLM/TLM/TMPE.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"12.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{7422AE58-8B0A-401C-9404-F4A438EFFE10}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>TrafficManager</RootNamespace>\n    <AssemblyName>TrafficManager</AssemblyName>\n    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;QUEUEDSTATS</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>0</WarningLevel>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>\n    </DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'QueuedStats|AnyCPU'\">\n    <OutputPath>bin\\QueuedStats\\</OutputPath>\n    <DefineConstants>QUEUEDSTATS</DefineConstants>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Assembly-CSharp\">\n      <HintPath>..\\..\\..\\..\\..\\..\\Program Files (x86)\\Steam\\steamapps\\common\\Cities_Skylines\\Cities_Data\\Managed\\Assembly-CSharp.dll</HintPath>\n    </Reference>\n    <Reference Include=\"ColossalManaged\">\n      <HintPath>..\\..\\..\\..\\..\\..\\Program Files (x86)\\Steam\\steamapps\\common\\Cities_Skylines\\Cities_Data\\Managed\\ColossalManaged.dll</HintPath>\n    </Reference>\n    <Reference Include=\"ICities\">\n      <HintPath>..\\..\\..\\..\\..\\..\\Program Files (x86)\\Steam\\steamapps\\common\\Cities_Skylines\\Cities_Data\\Managed\\ICities.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\">\n      <HintPath>C:\\Program Files (x86)\\Steam\\steamapps\\common\\Cities_Skylines\\Managed\\System.Core.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System.Runtime.Serialization\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"UnityEngine\">\n      <HintPath>..\\..\\..\\..\\..\\..\\Program Files (x86)\\Steam\\steamapps\\common\\Cities_Skylines\\Cities_Data\\Managed\\UnityEngine.dll</HintPath>\n    </Reference>\n    <Reference Include=\"UnityEngine.Networking\">\n      <HintPath>..\\..\\..\\..\\..\\..\\Program Files (x86)\\Steam\\steamapps\\common\\Cities_Skylines\\Cities_Data\\Managed\\UnityEngine.Networking.dll</HintPath>\n    </Reference>\n    <Reference Include=\"UnityEngine.UI\">\n      <HintPath>..\\..\\..\\..\\..\\..\\Program Files (x86)\\Steam\\steamapps\\common\\Cities_Skylines\\Cities_Data\\Managed\\UnityEngine.UI.dll</HintPath>\n    </Reference>\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"CodeProfiler.cs\" />\n    <Compile Include=\"Constants.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomBuildingAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomBusAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomAmbulanceAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomCitizenAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomCommonBuildingAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomFireTruckAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomNetAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomPoliceCarAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomResidentAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomShipAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomTaxiAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomTouristAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomTransportLineAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomTramBaseAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomVehicleAI.cs\" />\n    <Compile Include=\"Custom\\Manager\\CustomNetManager.cs\" />\n    <Compile Include=\"Custom\\Manager\\CustomCitizenManager.cs\" />\n    <Compile Include=\"Custom\\Manager\\CustomVehicleManager.cs\" />\n    <Compile Include=\"Manager\\AbstractCustomManager.cs\" />\n    <Compile Include=\"Manager\\AbstractNodeGeometryObservingManager.cs\" />\n    <Compile Include=\"Manager\\AbstractSegmentGeometryObservingManager.cs\" />\n    <Compile Include=\"Manager\\IExtNetManager.cs\" />\n    <Compile Include=\"Manager\\SegmentEndManager.cs\" />\n    <Compile Include=\"Manager\\VehicleBehaviorManager.cs\" />\n    <Compile Include=\"Manager\\ExtBuildingManager.cs\" />\n    <Compile Include=\"Manager\\ICustomDataManager.cs\" />\n    <Compile Include=\"Manager\\AdvancedParkingManager.cs\" />\n    <Compile Include=\"Manager\\LaneArrowManager.cs\" />\n    <Compile Include=\"Manager\\OptionsManager.cs\" />\n    <Compile Include=\"Manager\\TrafficLightManager.cs\" />\n    <Compile Include=\"Manager\\TrafficMeasurementManager.cs\" />\n    <Compile Include=\"Manager\\JunctionRestrictionsManager.cs\" />\n    <Compile Include=\"Manager\\UtilityManager.cs\" />\n    <Compile Include=\"Manager\\ExtCitizenInstanceManager.cs\" />\n    <Compile Include=\"State\\GlobalConfig.cs\" />\n    <Compile Include=\"TrafficLight\\CustomSegmentLights.cs\" />\n    <Compile Include=\"TrafficLight\\ICustomSegmentLightsManager.cs\" />\n    <Compile Include=\"TrafficLight\\TrafficLightSimulation.cs\" />\n    <Compile Include=\"Traffic\\ExtBuilding.cs\" />\n    <Compile Include=\"Traffic\\ExtCitizenInstance.cs\" />\n    <Compile Include=\"Traffic\\ExtVehicleType.cs\" />\n    <Compile Include=\"Manager\\LaneConnectionManager.cs\" />\n    <Compile Include=\"Geometry\\NodeGeometry.cs\" />\n    <Compile Include=\"Geometry\\SegmentEndGeometry.cs\" />\n    <Compile Include=\"Manager\\VehicleStateManager.cs\" />\n    <Compile Include=\"Manager\\VehicleRestrictionsManager.cs\" />\n    <Compile Include=\"Traffic\\VehicleJunctionTransitState.cs\" />\n    <Compile Include=\"State\\Configuration.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomCarAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomCargoTruckAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomHumanAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomPassengerCarAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomRoadAI.cs\" />\n    <Compile Include=\"Custom\\AI\\CustomTrainAI.cs\" />\n    <Compile Include=\"Custom\\PathFinding\\CustomPathFind.cs\" />\n    <Compile Include=\"Custom\\PathFinding\\CustomPathManager.cs\" />\n    <Compile Include=\"Geometry\\SegmentGeometry.cs\" />\n    <Compile Include=\"LoadingExtension.cs\" />\n    <Compile Include=\"UI\\UIMainMenuButton.cs\" />\n    <Compile Include=\"UI\\SubTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\JunctionRestrictionsTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\SpeedLimitsTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\LaneConnectorTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\VehicleRestrictionsTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\LaneArrowTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\TimedTrafficLightsTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\ManualTrafficLightsTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\PrioritySignsTool.cs\" />\n    <Compile Include=\"UI\\SubTools\\ToggleTrafficLightsTool.cs\" />\n    <Compile Include=\"UI\\Translation.cs\" />\n    <Compile Include=\"State\\Options.cs\" />\n    <Compile Include=\"Traffic\\ArrowDirection.cs\" />\n    <Compile Include=\"Manager\\SpeedLimitManager.cs\" />\n    <Compile Include=\"Traffic\\VehicleState.cs\" />\n    <Compile Include=\"State\\Flags.cs\" />\n    <Compile Include=\"Traffic\\SegmentEnd.cs\" />\n    <Compile Include=\"UI\\TransportDemandViewMode.cs\" />\n    <Compile Include=\"UI\\UITransportDemand.cs\" />\n    <Compile Include=\"Util\\GenericUnsubscriber.cs\" />\n    <Compile Include=\"Manager\\ICustomManager.cs\" />\n    <Compile Include=\"Util\\IExtNetManager.cs\" />\n    <Compile Include=\"Util\\IObservable.cs\" />\n    <Compile Include=\"Util\\IObserver.cs\" />\n    <Compile Include=\"Util\\IVisitor.cs\" />\n    <Compile Include=\"Util\\LoopUtil.cs\" />\n    <Compile Include=\"Util\\SegmentLaneTraverser.cs\" />\n    <Compile Include=\"Util\\TextureUtil.cs\" />\n    <Compile Include=\"Util\\TinyDictionary.cs\" />\n    <Compile Include=\"Util\\VehicleUtil.cs\" />\n    <Compile Include=\"Util\\RedirectionHelper.cs\" />\n    <Compile Include=\"State\\SerializableDataExtension.cs\" />\n    <Compile Include=\"Log.cs\" />\n    <Compile Include=\"ThreadingExtension.cs\" />\n    <Compile Include=\"TrafficLight\\CustomSegment.cs\" />\n    <Compile Include=\"TrafficLight\\CustomSegmentLight.cs\" />\n    <Compile Include=\"UI\\ToolMode.cs\" />\n    <Compile Include=\"Manager\\TrafficLightSimulationManager.cs\" />\n    <Compile Include=\"Manager\\CustomSegmentLightsManager.cs\" />\n    <Compile Include=\"TrafficLight\\TimedTrafficLights.cs\" />\n    <Compile Include=\"UI\\TrafficManagerTool.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"UI\\TrafficLightToolTextureResources.cs\" />\n    <Compile Include=\"TrafficManagerMod.cs\" />\n    <Compile Include=\"TrafficManagerMode.cs\" />\n    <Compile Include=\"TrafficLight\\TimedTrafficLightsStep.cs\" />\n    <Compile Include=\"Manager\\TrafficPriorityManager.cs\" />\n    <Compile Include=\"Traffic\\PrioritySegment.cs\" />\n    <Compile Include=\"UI\\CameraCtrl.cs\" />\n    <Compile Include=\"UI\\UIBase.cs\" />\n    <Compile Include=\"UI\\UITrafficManager.cs\" />\n    <Compile Include=\"Util\\SegmentTraverser.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\light_1_1.png\" />\n    <EmbeddedResource Include=\"Resources\\light_1_2.png\" />\n    <EmbeddedResource Include=\"Resources\\light_1_3.png\" />\n    <EmbeddedResource Include=\"Resources\\light_2_1.png\" />\n    <EmbeddedResource Include=\"Resources\\light_2_2.png\" />\n    <EmbeddedResource Include=\"Resources\\light_2_3.png\" />\n    <EmbeddedResource Include=\"Resources\\light_3_1.png\" />\n    <EmbeddedResource Include=\"Resources\\light_3_2.png\" />\n    <EmbeddedResource Include=\"Resources\\light_3_3.png\" />\n    <EmbeddedResource Include=\"Resources\\light_4_1.png\" />\n    <EmbeddedResource Include=\"Resources\\light_4_2.png\" />\n    <EmbeddedResource Include=\"Resources\\light_4_3.png\" />\n    <EmbeddedResource Include=\"Resources\\light_5_1.png\" />\n    <EmbeddedResource Include=\"Resources\\light_5_2.png\" />\n    <EmbeddedResource Include=\"Resources\\light_5_3.png\" />\n    <EmbeddedResource Include=\"Resources\\light_6_1.png\" />\n    <EmbeddedResource Include=\"Resources\\light_6_2.png\" />\n    <EmbeddedResource Include=\"Resources\\light_6_3.png\" />\n    <EmbeddedResource Include=\"Resources\\light_counter.png\" />\n    <EmbeddedResource Include=\"Resources\\light_mode.png\" />\n    <EmbeddedResource Include=\"Resources\\light_yellow.png\" />\n    <EmbeddedResource Include=\"Resources\\pedestrian_light_1.png\" />\n    <EmbeddedResource Include=\"Resources\\pedestrian_light_2.png\" />\n    <EmbeddedResource Include=\"Resources\\pedestrian_mode_1.png\" />\n    <EmbeddedResource Include=\"Resources\\pedestrian_mode_2.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\sign_none.png\" />\n    <EmbeddedResource Include=\"Resources\\sign_priority.png\" />\n    <EmbeddedResource Include=\"Resources\\sign_stop.png\" />\n    <EmbeddedResource Include=\"Resources\\sign_yield.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\lang_zh-tw.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang_es.txt\" />\n    <EmbeddedResource Include=\"Resources\\lanechange_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\lanechange_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\bicycle_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\bus_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\cargotrain_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\cargotruck_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\passengercar_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\passengertrain_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\emergency_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\service_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\taxi_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\tram_infosign.png\" />\n    <EmbeddedResource Include=\"Resources\\bus_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\bus_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\cargotrain_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\cargotrain_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\cargotruck_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\cargotruck_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\emergency_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\emergency_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\passengercar_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\passengercar_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\passengertrain_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\passengertrain_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\service_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\service_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\taxi_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\taxi_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\light_counter_pl.png\" />\n    <EmbeddedResource Include=\"Resources\\light_mode_pl.png\" />\n    <EmbeddedResource Include=\"Resources\\pedestrian_mode_2_pl.png\" />\n    <EmbeddedResource Include=\"Resources\\lang_fr.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang_pl.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang_ru.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang_pt.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang_de.txt\" />\n    <EmbeddedResource Include=\"Resources\\lang_ja.txt\" />\n    <EmbeddedResource Include=\"Resources\\light_counter_ja.png\" />\n    <EmbeddedResource Include=\"Resources\\light_mode_ja.png\" />\n    <EmbeddedResource Include=\"Resources\\0.png\" />\n    <EmbeddedResource Include=\"Resources\\10.png\" />\n    <EmbeddedResource Include=\"Resources\\100.png\" />\n    <EmbeddedResource Include=\"Resources\\120.png\" />\n    <EmbeddedResource Include=\"Resources\\130.png\" />\n    <EmbeddedResource Include=\"Resources\\20.png\" />\n    <EmbeddedResource Include=\"Resources\\30.png\" />\n    <EmbeddedResource Include=\"Resources\\40.png\" />\n    <EmbeddedResource Include=\"Resources\\50.png\" />\n    <EmbeddedResource Include=\"Resources\\60.png\" />\n    <EmbeddedResource Include=\"Resources\\70.png\" />\n    <EmbeddedResource Include=\"Resources\\80.png\" />\n    <EmbeddedResource Include=\"Resources\\90.png\" />\n    <EmbeddedResource Include=\"Resources\\clock_pause.png\" />\n    <EmbeddedResource Include=\"Resources\\clock_test.png\" />\n    <EmbeddedResource Include=\"Resources\\clock_play.png\" />\n    <EmbeddedResource Include=\"Resources\\remove_signs.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\lang_nl.txt\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\crossing_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\crossing_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\enterblocked_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\enterblocked_forbidden.png\" />\n    <EmbeddedResource Include=\"Resources\\uturn_allowed.png\" />\n    <EmbeddedResource Include=\"Resources\\uturn_forbidden.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\110.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\MenuButton.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\lang_zh.txt\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\lang_kr.txt\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\lang_it.txt\" />\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Resources\\noimage.png\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\CitiesGameBridge\\CitiesGameBridge.csproj\">\n      <Project>{3f2f7926-5d51-4880-a2b7-4594a10d7e54}</Project>\n      <Name>CitiesGameBridge</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\GenericGameBridge\\GenericGameBridge.csproj\">\n      <Project>{663b991f-32a1-46e1-a4d3-540f8ea7f386}</Project>\n      <Name>GenericGameBridge</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\Util\\Util.csproj\">\n      <Project>{d3ade06e-f493-4819-865a-3bb44feedf01}</Project>\n      <Name>Util</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <PropertyGroup>\n    <PostBuildEvent>mkdir \"C:\\Program Files (x86)\\Steam\\steamapps\\workshop\\content\\255710\\583429740\"\ndel /Q \"C:\\Program Files (x86)\\Steam\\steamapps\\workshop\\content\\255710\\583429740\\*\"\nxcopy /y \"$(TargetDir)$(TargetName).*\" \"C:\\Program Files (x86)\\Steam\\steamapps\\workshop\\content\\255710\\583429740\"\n</PostBuildEvent>\n  </PropertyGroup>\n  <PropertyGroup>\n    <PreBuildEvent>\n    </PreBuildEvent>\n  </PropertyGroup>\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.\n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "TLM/TLM/ThreadingExtension.cs",
    "content": "using System;\nusing System.Reflection;\nusing ColossalFramework;\nusing ICities;\nusing TrafficManager.Custom.AI;\nusing UnityEngine;\nusing TrafficManager.State;\nusing TrafficManager.Manager;\nusing TrafficManager.UI;\nusing CSUtil.Commons;\nusing CSUtil.Commons.Benchmark;\nusing TrafficManager.UI.MainMenu;\nusing static TrafficManager.LoadingExtension;\nusing TrafficManager.Util;\nusing System.Collections.Generic;\nusing ColossalFramework.UI;\nusing System.Runtime.InteropServices;\nusing System.Linq;\nusing System.Linq.Expressions;\n\nnamespace TrafficManager {\n    public sealed class ThreadingExtension : ThreadingExtensionBase {\n\t\t//int ticksSinceLastMinuteUpdate = 0;\n\n\t\tITrafficLightSimulationManager tlsMan = Constants.ManagerFactory.TrafficLightSimulationManager;\n\t\tIGeometryManager geoMan = Constants.ManagerFactory.GeometryManager;\n\t\tIRoutingManager routeMan = Constants.ManagerFactory.RoutingManager;\n\t\tIUtilityManager utilMan = Constants.ManagerFactory.UtilityManager;\n\n\t\tbool firstFrame = true;\n\n\t\tpublic override void OnCreated(IThreading threading) {\n\t\t\tbase.OnCreated(threading);\n\n\t\t\t//ticksSinceLastMinuteUpdate = 0;\n\t\t}\n\n\t\tpublic override void OnBeforeSimulationTick() {\n\t\t\tbase.OnBeforeSimulationTick();\n\n\t\t\tgeoMan.SimulationStep();\n\t\t\trouteMan.SimulationStep();\n\t\t}\n\n\t\tpublic override void OnBeforeSimulationFrame() {\n\t\t\tbase.OnBeforeSimulationFrame();\n\n\t\t\tif (firstFrame) {\n\t\t\t\tfirstFrame = false;\n\t\t\t\tLog.Info($\"ThreadingExtension.OnBeforeSimulationFrame: First frame detected. Checking detours.\");\n\n\t\t\t\tList<string> missingDetours = new List<string>();\n\t\t\t\tforeach (Detour detour in LoadingExtension.Detours) {\n\t\t\t\t\tif (! RedirectionHelper.IsRedirected(detour.OriginalMethod, detour.CustomMethod)) {\n\t\t\t\t\t\tmissingDetours.Add($\"{detour.OriginalMethod.DeclaringType.Name}.{detour.OriginalMethod.Name} with {detour.OriginalMethod.GetParameters().Length} parameters ({detour.OriginalMethod.DeclaringType.AssemblyQualifiedName})\");\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tLog.Info($\"ThreadingExtension.OnBeforeSimulationFrame: First frame detected. Detours checked. Result: {missingDetours.Count} missing detours\");\n\n\t\t\t\tif (missingDetours.Count > 0) {\n\t\t\t\t\tstring error = \"Traffic Manager: President Edition detected an incompatibility with another mod! You can continue playing but it's NOT recommended. Traffic Manager will not work as expected. See TMPE.log for technical details.\";\n\t\t\t\t\tLog.Error(error);\n\t\t\t\t\tstring log = \"The following methods were overriden by another mod:\";\n\t\t\t\t\tforeach (string missingDetour in missingDetours) {\n\t\t\t\t\t\tlog += $\"\\n\\t{missingDetour}\";\n\t\t\t\t\t}\n\t\t\t\t\tLog.Info(log);\n\t\t\t\t\tif (GlobalConfig.Instance.Main.ShowCompatibilityCheckErrorMessage) {\n\t\t\t\t\t\tSingleton<SimulationManager>.instance.m_ThreadingWrapper.QueueMainThread(() => {\n\t\t\t\t\t\t\tUIView.library.ShowModal<ExceptionPanel>(\"ExceptionPanel\").SetMessage(\"Incompatibility Issue\", error, true);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (Options.timedLightsEnabled) {\n\t\t\t\ttlsMan.SimulationStep();\n\t\t\t}\n\t\t}\n\n\t\t/*public override void OnAfterSimulationFrame() {\n\t\t\tbase.OnAfterSimulationFrame();\n\n\t\t\trouteMan.SimulationStep();\n\n\t\t\t++ticksSinceLastMinuteUpdate;\n\t\t\tif (ticksSinceLastMinuteUpdate > 60 * 60) {\n\t\t\t\tticksSinceLastMinuteUpdate = 0;\n\t\t\t\tGlobalConfig.Instance.SimulationStep();\n#if DEBUG\n\t\t\t\tDebugMenuPanel.PrintTransportStats();\n#endif\n\t\t\t}\n\t\t}*/\n\n\t\tpublic override void OnUpdate(float realTimeDelta, float simulationTimeDelta) {\n            base.OnUpdate(realTimeDelta, simulationTimeDelta);\n\n#if !TAM\n#if BENCHMARK\n\t\t\tusing (var bm = new Benchmark()) {\n#endif\n\n\t\t\t\tif (ToolsModifierControl.toolController == null || LoadingExtension.BaseUI == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tTrafficManagerTool tmTool = UIBase.GetTrafficManagerTool(false);\n\t\t\t\tif (tmTool != null && ToolsModifierControl.toolController.CurrentTool != tmTool && LoadingExtension.BaseUI.IsVisible()) {\n\t\t\t\t\tLoadingExtension.BaseUI.Close();\n\t\t\t\t}\n\n\t\t\t\tif (Input.GetKeyDown(KeyCode.Escape)) {\n\t\t\t\t\tLoadingExtension.BaseUI.Close();\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t}\n#endif\n#endif\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Traffic/Data/ExtBuilding.cs",
    "content": "﻿using ColossalFramework;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.State;\nusing UnityEngine;\n\nnamespace TrafficManager.Traffic.Data {\n\tpublic struct ExtBuilding {\n\t\t/// <summary>\n\t\t/// Building id\n\t\t/// </summary>\n\t\tpublic ushort buildingId;\n\n\t\t/// <summary>\n\t\t/// Current parking space demand (0-100)\n\t\t/// </summary>\n\t\tpublic byte parkingSpaceDemand;\n\n\t\t/// <summary>\n\t\t/// Current incoming public transport demand (0-100)\n\t\t/// </summary>\n\t\tpublic byte incomingPublicTransportDemand;\n\n\t\t/// <summary>\n\t\t/// Current outgoing public transport demand (0-100)\n\t\t/// </summary>\n\t\tpublic byte outgoingPublicTransportDemand;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[ExtBuilding {base.ToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"buildingId = {buildingId}\\n\" +\n\t\t\t\t\"\\t\" + $\"parkingSpaceDemand = {parkingSpaceDemand}\\n\" +\n\t\t\t\t\"\\t\" + $\"incomingPublicTransportDemand = {incomingPublicTransportDemand}\\n\" +\n\t\t\t\t\"\\t\" + $\"outgoingPublicTransportDemand = {outgoingPublicTransportDemand}\\n\" +\n\t\t\t\t\"ExtBuilding]\";\n\t\t}\n\n\t\tinternal ExtBuilding(ushort buildingId) {\n\t\t\tthis.buildingId = buildingId;\n\t\t\tparkingSpaceDemand = 0;\n\t\t\tincomingPublicTransportDemand = 0;\n\t\t\toutgoingPublicTransportDemand = 0;\n\t\t}\n\n\t\tpublic bool IsValid() {\n\t\t\treturn Constants.ServiceFactory.BuildingService.IsBuildingValid(buildingId);\n\t\t}\n\n\t\tinternal void Reset() {\n\t\t\tparkingSpaceDemand = 0;\n            incomingPublicTransportDemand = 0;\n            outgoingPublicTransportDemand = 0;\n\t\t}\n\n\t\tinternal void AddParkingSpaceDemand(uint delta) {\n\t\t\tparkingSpaceDemand = (byte)Math.Min(100, (int)parkingSpaceDemand + delta);\n\t\t\tRequestColorUpdate();\n\t\t}\n\n\t\tinternal void RemoveParkingSpaceDemand(uint delta) {\n\t\t\tparkingSpaceDemand = (byte)Math.Max(0, (int)parkingSpaceDemand - delta);\n\t\t\tRequestColorUpdate();\n\t\t}\n\n\t\tinternal void ModifyParkingSpaceDemand(Vector3 parkPos, int minDelta=-10, int maxDelta=10) {\n\t\t\tVector3 buildingPos = Singleton<BuildingManager>.instance.m_buildings.m_buffer[buildingId].m_position;\n\t\t\tfloat distance = Mathf.Clamp((parkPos - buildingPos).magnitude, 0f, GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToBuilding);\n\n\t\t\tfloat delta = (float)(maxDelta - minDelta) * (distance / GlobalConfig.Instance.ParkingAI.MaxParkedCarDistanceToBuilding) + (float)minDelta;\n\t\t\tparkingSpaceDemand = (byte)Mathf.Clamp((int)parkingSpaceDemand + (int)Mathf.Round(delta), 0, 100);\n\t\t\tRequestColorUpdate();\n\t\t}\n\n\t\tinternal void AddPublicTransportDemand(uint delta, bool outgoing) {\n            byte oldDemand = outgoing ? outgoingPublicTransportDemand : incomingPublicTransportDemand;\n\t\t\tbyte newDemand = (byte)Math.Min(100, (int)oldDemand + delta);\n            if (outgoing)\n                outgoingPublicTransportDemand = newDemand;\n            else\n                incomingPublicTransportDemand = newDemand;\n\n            RequestColorUpdate();\n\t\t}\n\n\t\tinternal void RemovePublicTransportDemand(uint delta, bool outgoing) {\n            byte oldDemand = outgoing ? outgoingPublicTransportDemand : incomingPublicTransportDemand;\n            byte newDemand = (byte)Math.Max(0, (int)oldDemand - delta);\n            if (outgoing)\n                outgoingPublicTransportDemand = newDemand;\n            else\n                incomingPublicTransportDemand = newDemand;\n\n\t\t\tRequestColorUpdate();\n\t\t}\n\n\t\tprivate void RequestColorUpdate() {\n\t\t\tSingleton<BuildingManager>.instance.UpdateBuildingColors(buildingId);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Traffic/Data/ExtCitizen.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.State;\n\nnamespace TrafficManager.Traffic.Data {\n\tpublic struct ExtCitizen {\n\t\t[Flags]\n\t\tpublic enum ExtTransportMode {\n\t\t\t/// <summary>\n\t\t\t/// No information about which mode of transport is used\n\t\t\t/// </summary>\n\t\t\tNone = 0,\n\t\t\t/// <summary>\n\t\t\t/// Travelling by car\n\t\t\t/// </summary>\n\t\t\tCar = 1,\n\t\t\t/// <summary>\n\t\t\t/// Travelling by means of public transport\n\t\t\t/// </summary>\n\t\t\tPublicTransport = 2\n\t\t}\n\n\t\tpublic uint citizenId;\n\n\t\t/// <summary>\n\t\t/// Mode of transport that is currently used to reach a destination\n\t\t/// </summary>\n\t\tpublic ExtTransportMode transportMode;\n\n\t\t/// <summary>\n\t\t/// Mode of transport that was previously used to reach a destination\n\t\t/// </summary>\n\t\tpublic ExtTransportMode lastTransportMode;\n\n\t\t/// <summary>\n\t\t/// Previous building location\n\t\t/// </summary>\n\t\tpublic Citizen.Location lastLocation;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[ExtCitizen\\n\" +\n\t\t\t\t\"\\t\" + $\"citizenId = {citizenId}\\n\" +\n\t\t\t\t\"\\t\" + $\"transportMode = {transportMode}\\n\" +\n\t\t\t\t\"\\t\" + $\"lastTransportMode = {transportMode}\\n\" +\n\t\t\t\t\"\\t\" + $\"lastLocation = {lastLocation}\\n\" +\n\t\t\t\t\"ExtCitizen]\";\n\t\t}\n\n\t\tinternal ExtCitizen(uint citizenId) {\n\t\t\tthis.citizenId = citizenId;\n\t\t\ttransportMode = ExtTransportMode.None;\n\t\t\tlastTransportMode = ExtTransportMode.None;\n\t\t\tlastLocation = Citizen.Location.Moving;\n\t\t\tResetLastLocation();\n\t\t}\n\n\t\tinternal bool IsValid() {\n\t\t\treturn Constants.ServiceFactory.CitizenService.IsCitizenValid(citizenId);\n\t\t}\n\n\t\tinternal void Reset() {\n#if DEBUG\n\t\t\tbool citDebug = GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == citizenId;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (fineDebug) {\n\t\t\t\tLog.Warning($\"ExtCitizen.Reset({citizenId}): Resetting ext. citizen {citizenId}\");\n\t\t\t}\n#endif\n\t\t\ttransportMode = ExtTransportMode.None;\n\t\t\tlastTransportMode = ExtTransportMode.None;\n\t\t\tResetLastLocation();\n\t\t}\n\n\t\tprivate void ResetLastLocation() {\n\t\t\tCitizen.Location loc = Citizen.Location.Moving;\n\t\t\tConstants.ServiceFactory.CitizenService.ProcessCitizen(citizenId, delegate (uint citId, ref Citizen citizen) {\n\t\t\t\tloc = citizen.CurrentLocation;\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\tlastLocation = loc;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Traffic/Data/ExtCitizenInstance.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.State;\nusing UnityEngine;\nusing static TrafficManager.Custom.PathFinding.CustomPathManager;\n\nnamespace TrafficManager.Traffic.Data {\n\tpublic struct ExtCitizenInstance {\n\t\tpublic enum ExtPathState {\n\t\t\t/// <summary>\n\t\t\t/// No path\n\t\t\t/// </summary>\n\t\t\tNone = 0,\n\t\t\t/// <summary>\n\t\t\t/// Path is currently being calculated\n\t\t\t/// </summary>\n\t\t\tCalculating = 1,\n\t\t\t/// <summary>\n\t\t\t/// Path-finding has succeeded\n\t\t\t/// </summary>\n\t\t\tReady = 2,\n\t\t\t/// <summary>\n\t\t\t/// Path-finding has failed\n\t\t\t/// </summary>\n\t\t\tFailed = 3\n\t\t}\n\n\t\tpublic enum ExtSoftPathState {\n\t\t\t/// <summary>\n\t\t\t/// No path\n\t\t\t/// </summary>\n\t\t\tNone = 0,\n\t\t\t/// <summary>\n\t\t\t/// Path is currently being calculated\n\t\t\t/// </summary>\n\t\t\tCalculating = 1,\n\t\t\t/// <summary>\n\t\t\t/// Path-finding has succeeded and must be handled appropriately\n\t\t\t/// </summary>\n\t\t\tReady = 2,\n\t\t\t/// <summary>\n\t\t\t/// Path-finding has failed and must be handled appropriately\n\t\t\t/// </summary>\n\t\t\tFailedHard = 3,\n\t\t\t/// <summary>\n\t\t\t/// Path-finding must be retried (soft path-find failure)\n\t\t\t/// </summary>\n\t\t\tFailedSoft = 4,\n\t\t\t/// <summary>\n\t\t\t/// Path-finding result must not be handled by the citizen because the path will be transferred to a vehicle\n\t\t\t/// </summary>\n\t\t\tIgnore = 5\n\t\t}\n\n\t\tpublic enum ExtPathType {\n\t\t\t/// <summary>\n\t\t\t/// Mixed path\n\t\t\t/// </summary>\n\t\t\tNone = 0,\n\t\t\t/// <summary>\n\t\t\t/// Walking path\n\t\t\t/// </summary>\n\t\t\tWalkingOnly = 1,\n\t\t\t/// <summary>\n\t\t\t/// Driving path\n\t\t\t/// </summary>\n\t\t\tDrivingOnly = 2\n\t\t}\n\n\t\tpublic enum ExtPathMode {\n\t\t\tNone = 0,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that the citizen requires a walking path to their parked car\n\t\t\t/// </summary>\n\t\t\tRequiresWalkingPathToParkedCar = 1,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that a walking path to the parked car is being calculated\n\t\t\t/// </summary>\n\t\t\tCalculatingWalkingPathToParkedCar = 2,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that the citizen is walking to their parked car\n\t\t\t/// </summary>\n\t\t\tWalkingToParkedCar = 3,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that the citizen is close to their parked car\n\t\t\t/// </summary>\n\t\t\tApproachingParkedCar = 4,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that the citizen has reached their parked car and requires a car path now\n\t\t\t/// </summary>\n\t\t\tRequiresCarPath = 5,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that a direct car path to the target is being calculated\n\t\t\t/// </summary>\n\t\t\tCalculatingCarPathToTarget = 6,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that a car path to a known parking spot near the target is being calculated\n\t\t\t/// </summary>\n\t\t\tCalculatingCarPathToKnownParkPos = 7,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that the citizen is currently driving on a direct path to target\n\t\t\t/// </summary>\n\t\t\tDrivingToTarget = 8,\n\t\t\t/// <summary>\n\t\t\t/// Indiciates that the citizen is currently driving to a known parking spot near the target\n\t\t\t/// </summary>\n\t\t\tDrivingToKnownParkPos = 9,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that the vehicle is being parked on an alternative parking position\n\t\t\t/// </summary>\n\t\t\tRequiresWalkingPathToTarget = 10,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that parking has failed\n\t\t\t/// </summary>\n\t\t\tParkingFailed = 11,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that a path to an alternative parking position is being calculated\n\t\t\t/// </summary>\n\t\t\tCalculatingCarPathToAltParkPos = 12,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that the vehicle is on a path to an alternative parking position\n\t\t\t/// </summary>\n\t\t\tDrivingToAltParkPos = 13,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that a walking path to target is being calculated\n\t\t\t/// </summary>\n\t\t\tCalculatingWalkingPathToTarget = 14,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that the citizen is currently walking to the target\n\t\t\t/// </summary>\n\t\t\tWalkingToTarget = 15,\n\t\t\t/// <summary>\n\t\t\t/// (DEPRECATED) Indicates that the citizen is using public transport (bus/train/tram/subway) to reach the target\n\t\t\t/// </summary>\n\t\t\t__Deprecated__PublicTransportToTarget = 16,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that the citizen is using a taxi to reach the target\n\t\t\t/// </summary>\n\t\t\tTaxiToTarget = 17,\n\t\t\t/// <summary>\n\t\t\t/// Indicates that the driving citizen requires a direct path to target (driving/public transport)\n\t\t\t/// where possible transitions between different modes of transport happen as required (thus no search\n\t\t\t/// for parking spaces is performed beforehand)\n\t\t\t/// </summary>\n\t\t\tRequiresMixedCarPathToTarget = 18,\n\t\t}\n\n\t\tpublic enum ExtParkingSpaceLocation {\n\t\t\t/// <summary>\n\t\t\t/// No parking space location\n\t\t\t/// </summary>\n\t\t\tNone = 0,\n\t\t\t/// <summary>\n\t\t\t/// Road-side parking space\n\t\t\t/// </summary>\n\t\t\tRoadSide = 1,\n\t\t\t/// <summary>\n\t\t\t/// Building parking space\n\t\t\t/// </summary>\n\t\t\tBuilding = 2\n\t\t}\n\n\t\tpublic ushort instanceId;\n\n\t\t/// <summary>\n\t\t/// Citizen path mode (used for Parking AI)\n\t\t/// </summary>\n\t\tpublic ExtPathMode pathMode;\n\n\t\t/// <summary>\n\t\t/// Number of times a formerly found parking space is already occupied after reaching its position\n\t\t/// </summary>\n\t\tpublic int failedParkingAttempts;\n\n\t\t/// <summary>\n\t\t/// Segment id / Building id where a parking space has been found\n\t\t/// </summary>\n\t\tpublic ushort parkingSpaceLocationId;\n\n\t\t/// <summary>\n\t\t/// Type of object (segment/building) where a parking space has been found\n\t\t/// </summary>\n\t\tpublic ExtParkingSpaceLocation parkingSpaceLocation;\n\n\t\t/// <summary>\n\t\t/// Path position that is used as a start position when parking fails\n\t\t/// </summary>\n\t\tpublic PathUnit.Position? parkingPathStartPosition;\n\n\t\t/// <summary>\n\t\t/// Walking path from (alternative) parking spot to target (only used to check if there is a valid walking path, not actually used at the moment)\n\t\t/// </summary>\n\t\tpublic uint returnPathId;\n\n\t\t/// <summary>\n\t\t/// State of the return path\n\t\t/// </summary>\n\t\tpublic ExtPathState returnPathState;\n\n\t\t/// <summary>\n\t\t/// Last known distance to the citizen's parked car\n\t\t/// </summary>\n\t\tpublic float lastDistanceToParkedCar;\n\n\t\t/// <summary>\n\t\t/// Specifies whether the last path-finding started at an outside connection\n\t\t/// </summary>\n\t\tpublic bool atOutsideConnection;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[ExtCitizenInstance\\n\" +\n\t\t\t\t\"\\t\" + $\"instanceId = {instanceId}\\n\" +\n\t\t\t\t\"\\t\" + $\"pathMode = {pathMode}\\n\" +\n\t\t\t\t\"\\t\" + $\"failedParkingAttempts = {failedParkingAttempts}\\n\" +\n\t\t\t\t\"\\t\" + $\"parkingSpaceLocationId = {parkingSpaceLocationId}\\n\" +\n\t\t\t\t\"\\t\" + $\"parkingSpaceLocation = {parkingSpaceLocation}\\n\" +\n\t\t\t\t\"\\t\" + $\"parkingPathStartPosition = {parkingPathStartPosition}\\n\" +\n\t\t\t\t\"\\t\" + $\"returnPathId = {returnPathId}\\n\" +\n\t\t\t\t\"\\t\" + $\"returnPathState = {returnPathState}\\n\" +\n\t\t\t\t\"\\t\" + $\"lastDistanceToParkedCar = {lastDistanceToParkedCar}\\n\" +\n\t\t\t\t\"\\t\" + $\"atOutsideConnection = {atOutsideConnection}\\n\" +\n\t\t\t\t\"ExtCitizenInstance]\";\n\t\t}\n\n\t\tinternal ExtCitizenInstance(ushort instanceId) {\n\t\t\tthis.instanceId = instanceId;\n\t\t\tpathMode = ExtPathMode.None;\n\t\t\tfailedParkingAttempts = 0;\n\t\t\tparkingSpaceLocationId = 0;\n\t\t\tparkingSpaceLocation = ExtParkingSpaceLocation.None;\n\t\t\tparkingPathStartPosition = null;\n\t\t\treturnPathId = 0;\n\t\t\treturnPathState = ExtPathState.None;\n\t\t\tlastDistanceToParkedCar = 0;\n\t\t\tatOutsideConnection = false;\n\t\t}\n\n\t\tinternal bool IsValid() {\n\t\t\treturn Constants.ServiceFactory.CitizenService.IsCitizenInstanceValid(instanceId);\n\t\t}\n\n\t\tpublic uint GetCitizenId() {\n\t\t\tuint ret = 0;\n\t\t\tConstants.ServiceFactory.CitizenService.ProcessCitizenInstance(instanceId, delegate (ushort citInstId, ref CitizenInstance citizenInst) {\n\t\t\t\tret = citizenInst.m_citizen;\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tinternal void Reset() {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == GetCitizenId()) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == Singleton<CitizenManager>.instance.m_instances.m_buffer[instanceId].m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == Singleton<CitizenManager>.instance.m_instances.m_buffer[instanceId].m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (fineDebug) {\n\t\t\t\tLog.Warning($\"ExtCitizenInstance.Reset({instanceId}): Resetting ext. citizen instance {instanceId}\");\n\t\t\t}\n#endif\n\t\t\t//Flags = ExtFlags.None;\n\t\t\tpathMode = ExtPathMode.None;\n\t\t\tfailedParkingAttempts = 0;\n\t\t\tparkingSpaceLocation = ExtParkingSpaceLocation.None;\n\t\t\tparkingSpaceLocationId = 0;\n\t\t\tlastDistanceToParkedCar = float.MaxValue;\n\t\t\tatOutsideConnection = false;\n\t\t\t//ParkedVehiclePosition = default(Vector3);\n\t\t\tReleaseReturnPath();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Releases the return path\n\t\t/// </summary>\n\t\tinternal void ReleaseReturnPath() {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == GetCitizenId()) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == Singleton<CitizenManager>.instance.m_instances.m_buffer[instanceId].m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == Singleton<CitizenManager>.instance.m_instances.m_buffer[instanceId].m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n#endif\n\n\t\t\tif (returnPathId != 0) {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"Releasing return path {returnPathId} of citizen instance {instanceId}. ReturnPathState={returnPathState}\");\n#endif\n\n\t\t\t\tSingleton<PathManager>.instance.ReleasePath(returnPathId);\n\t\t\t\treturnPathId = 0;\n\t\t\t}\n\t\t\treturnPathState = ExtPathState.None;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Checks the calculation state of the return path\n\t\t/// </summary>\n\t\tinternal void UpdateReturnPathState() {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == GetCitizenId()) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == Singleton<CitizenManager>.instance.m_instances.m_buffer[instanceId].m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == Singleton<CitizenManager>.instance.m_instances.m_buffer[instanceId].m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n\n\t\t\tif (fineDebug)\n\t\t\t\tLog._Debug($\"ExtCitizenInstance.UpdateReturnPathState() called for citizen instance {instanceId}\");\n#endif\n\t\t\tif (returnPathId != 0 && returnPathState == ExtPathState.Calculating) {\n\t\t\t\tbyte returnPathFlags = CustomPathManager._instance.m_pathUnits.m_buffer[returnPathId].m_pathFindFlags;\n\t\t\t\tif ((returnPathFlags & PathUnit.FLAG_READY) != 0) {\n\t\t\t\t\treturnPathState = ExtPathState.Ready;\n#if DEBUG\n\t\t\t\t\tif (fineDebug)\n\t\t\t\t\t\tLog._Debug($\"CustomHumanAI.CustomSimulationStep: Return path {returnPathId} SUCCEEDED. Flags={returnPathFlags}. Setting ReturnPathState={returnPathState}\");\n#endif\n\t\t\t\t} else if ((returnPathFlags & PathUnit.FLAG_FAILED) != 0) {\n\t\t\t\t\treturnPathState = ExtPathState.Failed;\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomHumanAI.CustomSimulationStep: Return path {returnPathId} FAILED. Flags={returnPathFlags}. Setting ReturnPathState={returnPathState}\");\n#endif\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Starts path-finding of the walking path from parking position <paramref name=\"parkPos\"/> to target position <paramref name=\"targetPos\"/>.\n\t\t/// </summary>\n\t\t/// <param name=\"parkPos\">Parking position</param>\n\t\t/// <param name=\"targetPos\">Target position</param>\n\t\t/// <returns></returns>\n\t\tinternal bool CalculateReturnPath(Vector3 parkPos, Vector3 targetPos) {\n#if DEBUG\n\t\t\tbool citDebug = (GlobalConfig.Instance.Debug.CitizenInstanceId == 0 || GlobalConfig.Instance.Debug.CitizenInstanceId == instanceId) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == GetCitizenId()) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.SourceBuildingId == 0 || GlobalConfig.Instance.Debug.SourceBuildingId == Singleton<CitizenManager>.instance.m_instances.m_buffer[instanceId].m_sourceBuilding) &&\n\t\t\t\t(GlobalConfig.Instance.Debug.TargetBuildingId == 0 || GlobalConfig.Instance.Debug.TargetBuildingId == Singleton<CitizenManager>.instance.m_instances.m_buffer[instanceId].m_targetBuilding)\n\t\t\t;\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug;\n\t\t\tbool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug;\n#endif\n\n\t\t\tReleaseReturnPath();\n\n\t\t\tPathUnit.Position parkPathPos;\n\t\t\tPathUnit.Position targetPathPos = default(PathUnit.Position);\n\t\t\tbool foundParkPathPos = CustomPathManager.FindCitizenPathPosition(parkPos, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, false, false, out parkPathPos);\n\t\t\tbool foundTargetPathPos = foundParkPathPos && CustomPathManager.FindCitizenPathPosition(targetPos, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, false, false, out targetPathPos);\n\t\t\tif (foundParkPathPos && foundTargetPathPos) {\n\t\t\t\tPathUnit.Position dummyPathPos = default(PathUnit.Position);\n\t\t\t\tuint pathId;\n\t\t\t\tPathCreationArgs args;\n\t\t\t\targs.extPathType = ExtCitizenInstance.ExtPathType.WalkingOnly;\n\t\t\t\targs.extVehicleType = ExtVehicleType.None;\n\t\t\t\targs.vehicleId = 0;\n\t\t\t\targs.spawned = true;\n\t\t\t\targs.buildIndex = Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\t\targs.startPosA = parkPathPos;\n\t\t\t\targs.startPosB = dummyPathPos;\n\t\t\t\targs.endPosA = targetPathPos;\n\t\t\t\targs.endPosB = dummyPathPos;\n\t\t\t\targs.vehiclePosition = dummyPathPos;\n\t\t\t\targs.laneTypes = NetInfo.LaneType.Pedestrian | NetInfo.LaneType.PublicTransport;\n\t\t\t\targs.vehicleTypes = VehicleInfo.VehicleType.None;\n\t\t\t\targs.maxLength = 20000f;\n\t\t\t\targs.isHeavyVehicle = false;\n\t\t\t\targs.hasCombustionEngine = false;\n\t\t\t\targs.ignoreBlocked = false;\n\t\t\t\targs.ignoreFlooded = false;\n\t\t\t\targs.ignoreCosts = false;\n\t\t\t\targs.randomParking = false;\n\t\t\t\targs.stablePath = false;\n\t\t\t\targs.skipQueue = false;\n\n\t\t\t\tif (CustomPathManager._instance.CreatePath(out pathId, ref Singleton<SimulationManager>.instance.m_randomizer, args)) {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"ExtCitizenInstance.CalculateReturnPath: Path-finding starts for return path of citizen instance {instanceId}, path={pathId}, parkPathPos.segment={parkPathPos.m_segment}, parkPathPos.lane={parkPathPos.m_lane}, targetPathPos.segment={targetPathPos.m_segment}, targetPathPos.lane={targetPathPos.m_lane}\");\n#endif\n\n\t\t\t\t\treturnPathId = pathId;\n\t\t\t\t\treturnPathState = ExtPathState.Calculating;\n\t\t\t\t\treturn true;\n\t\t\t\t} else {\n#if DEBUG\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"ExtCitizenInstance.CalculateReturnPath: Could not create return path for citizen instance {instanceId}\");\n#endif\n\t\t\t\t}\n\t\t\t} else {\n#if DEBUG\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"ExtCitizenInstance.CalculateReturnPath: Could not find path position(s) for either the parking position or target position of citizen instance {instanceId}: foundParkPathPos={foundParkPathPos} foundTargetPathPos={foundTargetPathPos}\");\n#endif\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines the path type through evaluating the current path mode.\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic ExtPathType GetPathType() {\n\t\t\tswitch (pathMode) {\n\t\t\t\tcase ExtPathMode.CalculatingCarPathToAltParkPos:\n\t\t\t\tcase ExtPathMode.CalculatingCarPathToKnownParkPos:\n\t\t\t\tcase ExtPathMode.CalculatingCarPathToTarget:\n\t\t\t\tcase ExtPathMode.DrivingToAltParkPos:\n\t\t\t\tcase ExtPathMode.DrivingToKnownParkPos:\n\t\t\t\tcase ExtPathMode.DrivingToTarget:\n\t\t\t\tcase ExtPathMode.RequiresCarPath:\n\t\t\t\tcase ExtPathMode.RequiresMixedCarPathToTarget:\n\t\t\t\tcase ExtPathMode.ParkingFailed:\n\t\t\t\t\treturn ExtPathType.DrivingOnly;\n\t\t\t\tcase ExtPathMode.CalculatingWalkingPathToParkedCar:\n\t\t\t\tcase ExtPathMode.CalculatingWalkingPathToTarget:\n\t\t\t\tcase ExtPathMode.RequiresWalkingPathToParkedCar:\n\t\t\t\tcase ExtPathMode.RequiresWalkingPathToTarget:\n\t\t\t\tcase ExtPathMode.ApproachingParkedCar:\n\t\t\t\tcase ExtPathMode.WalkingToParkedCar:\n\t\t\t\tcase ExtPathMode.WalkingToTarget:\n\t\t\t\t\treturn ExtPathType.WalkingOnly;\n\t\t\t\tdefault:\n\t\t\t\t\treturn ExtPathType.None;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Converts an ExtPathState to a ExtSoftPathState.\n\t\t/// </summary>\n\t\t/// <param name=\"state\"></param>\n\t\t/// <returns></returns>\n\t\tpublic static ExtSoftPathState ConvertPathStateToSoftPathState(ExtPathState state) {\n\t\t\treturn (ExtSoftPathState)((int)state);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Traffic/Data/PrioritySegment.cs",
    "content": "using System;\n\nnamespace TrafficManager.Traffic.Data {\n\t/// <summary>\n\t/// A priority segment specifies the priority signs that are present at each end of a certain segment.\n\t/// </summary>\n\tpublic struct PrioritySegment {\n\t\tpublic enum PriorityType {\n\t\t\tNone = 0,\n\t\t\t/// <summary>\n\t\t\t/// Priority road\n\t\t\t/// </summary>\n\t\t\tMain = 1,\n\t\t\t/// <summary>\n\t\t\t/// Stop sign\n\t\t\t/// </summary>\n\t\t\tStop = 2,\n\t\t\t/// <summary>\n\t\t\t/// Yield sign\n\t\t\t/// </summary>\n\t\t\tYield = 3\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Priority sign at start node (default: None)\n\t\t/// </summary>\n\t\tpublic PriorityType startType;\n\n\t\t/// <summary>\n\t\t/// Priority sign at end node (default: None)\n\t\t/// </summary>\n\t\tpublic PriorityType endType;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[PrioritySegment\\n\" +\n\t\t\t\t\"\\t\" + $\"startType = {startType}\\n\" +\n\t\t\t\t\"\\t\" + $\"endType = {endType}\\n\" +\n\t\t\t\t\"PrioritySegment]\";\n\t\t}\n\n\t\tpublic PrioritySegment(PriorityType startType, PriorityType endType) {\n\t\t\tthis.startType = startType;\n\t\t\tthis.endType = endType;\n\t\t}\n\n\t\tpublic void Reset() {\n\t\t\tstartType = PriorityType.None;\n\t\t\tendType = PriorityType.None;\n\t\t}\n\n\t\tpublic bool IsDefault() {\n\t\t\treturn !HasPrioritySignAtNode(true) && !HasPrioritySignAtNode(false);\n\t\t}\n\n\t\tpublic bool HasPrioritySignAtNode(bool startNode) {\n\t\t\tif (startNode) {\n\t\t\t\treturn startType != PriorityType.None;\n\t\t\t} else {\n\t\t\t\treturn endType != PriorityType.None;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Traffic/Data/SegmentEndFlags.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\n\nnamespace TrafficManager.Traffic.Data {\n\t/// <summary>\n\t/// Segment end flags store junction restrictions\n\t/// </summary>\n\tpublic struct SegmentEndFlags {\n\t\tpublic TernaryBool uturnAllowed;\n        public TernaryBool nearTurnOnRedAllowed;\n\t\tpublic TernaryBool farTurnOnRedAllowed;\n\t\tpublic TernaryBool straightLaneChangingAllowed;\n\t\tpublic TernaryBool enterWhenBlockedAllowed;\n\t\tpublic TernaryBool pedestrianCrossingAllowed;\n\n\t\tbool defaultUturnAllowed;\n        bool defaultNearTurnOnRedAllowed;\n\t\tbool defaultFarTurnOnRedAllowed;\n\t\tbool defaultStraightLaneChangingAllowed;\n\t\tbool defaultEnterWhenBlockedAllowed;\n\t\tbool defaultPedestrianCrossingAllowed;\n\n\t\tpublic void UpdateDefaults(ushort segmentId, bool startNode, ref NetNode node) {\n\t\t\tIJunctionRestrictionsManager junctionRestrictionsManager = Constants.ManagerFactory.JunctionRestrictionsManager;\n\n\t\t\tif (! junctionRestrictionsManager.IsUturnAllowedConfigurable(segmentId, startNode, ref node)) {\n\t\t\t\tuturnAllowed = TernaryBool.Undefined;\n\t\t\t}\n\n            if (! junctionRestrictionsManager.IsNearTurnOnRedAllowedConfigurable(segmentId, startNode, ref node)) {\n                nearTurnOnRedAllowed = TernaryBool.Undefined;\n            }\n\n\t\t\tif (!junctionRestrictionsManager.IsFarTurnOnRedAllowedConfigurable(segmentId, startNode, ref node)) {\n\t\t\t\tfarTurnOnRedAllowed = TernaryBool.Undefined;\n\t\t\t}\n\n\t\t\tif (! junctionRestrictionsManager.IsLaneChangingAllowedWhenGoingStraightConfigurable(segmentId, startNode, ref node)) {\n\t\t\t\tstraightLaneChangingAllowed = TernaryBool.Undefined;\n\t\t\t}\n\n\t\t\tif (! junctionRestrictionsManager.IsEnteringBlockedJunctionAllowedConfigurable(segmentId, startNode, ref node)) {\n\t\t\t\tenterWhenBlockedAllowed = TernaryBool.Undefined;\n\t\t\t}\n\n\t\t\tif (! junctionRestrictionsManager.IsPedestrianCrossingAllowedConfigurable(segmentId, startNode, ref node)) {\n\t\t\t\tpedestrianCrossingAllowed = TernaryBool.Undefined;\n\t\t\t}\n\n\t\t\tdefaultUturnAllowed = junctionRestrictionsManager.GetDefaultUturnAllowed(segmentId, startNode, ref node);\n\t\t\tdefaultNearTurnOnRedAllowed = junctionRestrictionsManager.GetDefaultNearTurnOnRedAllowed(segmentId, startNode, ref node);\n\t\t\tdefaultFarTurnOnRedAllowed = junctionRestrictionsManager.GetDefaultFarTurnOnRedAllowed(segmentId, startNode, ref node);\n\t\t\tdefaultStraightLaneChangingAllowed = junctionRestrictionsManager.GetDefaultLaneChangingAllowedWhenGoingStraight(segmentId, startNode, ref node);\n\t\t\tdefaultEnterWhenBlockedAllowed = junctionRestrictionsManager.GetDefaultEnteringBlockedJunctionAllowed(segmentId, startNode, ref node);\n\t\t\tdefaultPedestrianCrossingAllowed = junctionRestrictionsManager.GetDefaultPedestrianCrossingAllowed(segmentId, startNode, ref node);\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[11])\n\t\t\t\tLog._Debug($\"SegmentEndFlags.UpdateDefaults({segmentId}, {startNode}): Set defaults: defaultUturnAllowed={defaultUturnAllowed}, defaultNearTurnOnRedAllowed={defaultNearTurnOnRedAllowed}, defaultFarTurnOnRedAllowed={defaultFarTurnOnRedAllowed}, defaultStraightLaneChangingAllowed={defaultStraightLaneChangingAllowed}, defaultEnterWhenBlockedAllowed={defaultEnterWhenBlockedAllowed}, defaultPedestrianCrossingAllowed={defaultPedestrianCrossingAllowed}\");\n#endif\n\t\t}\n\n\t\tpublic bool IsUturnAllowed() {\n\t\t\tif (uturnAllowed == TernaryBool.Undefined) {\n\t\t\t\treturn defaultUturnAllowed;\n\t\t\t}\n\n\t\t\treturn TernaryBoolUtil.ToBool(uturnAllowed);\n\t\t}\n\n\t\tpublic bool IsNearTurnOnRedAllowed() {\n\t\t\tif (nearTurnOnRedAllowed == TernaryBool.Undefined) {\n\t\t\t\treturn defaultNearTurnOnRedAllowed;\n\t\t\t}\n\n\t\t\treturn TernaryBoolUtil.ToBool(nearTurnOnRedAllowed);\n\t\t}\n\n\t\tpublic bool IsFarTurnOnRedAllowed() {\n\t\t\tif (farTurnOnRedAllowed == TernaryBool.Undefined) {\n\t\t\t\treturn defaultFarTurnOnRedAllowed;\n\t\t\t}\n\n\t\t\treturn TernaryBoolUtil.ToBool(farTurnOnRedAllowed);\n\t\t}\n\n\t\tpublic bool IsLaneChangingAllowedWhenGoingStraight() {\n\t\t\tif (straightLaneChangingAllowed == TernaryBool.Undefined) {\n\t\t\t\treturn defaultStraightLaneChangingAllowed;\n\t\t\t}\n\n\t\t\treturn TernaryBoolUtil.ToBool(straightLaneChangingAllowed);\n\t\t}\n\n\t\tpublic bool IsEnteringBlockedJunctionAllowed() {\n\t\t\tif (enterWhenBlockedAllowed == TernaryBool.Undefined) {\n\t\t\t\treturn defaultEnterWhenBlockedAllowed;\n\t\t\t}\n\n\t\t\treturn TernaryBoolUtil.ToBool(enterWhenBlockedAllowed);\n\t\t}\n\t\t\n\t\tpublic bool IsPedestrianCrossingAllowed() {\n\t\t\tif (pedestrianCrossingAllowed == TernaryBool.Undefined) {\n\t\t\t\treturn defaultPedestrianCrossingAllowed;\n\t\t\t}\n\n\t\t\treturn TernaryBoolUtil.ToBool(pedestrianCrossingAllowed);\n\t\t}\n\n\t\tpublic void SetUturnAllowed(bool value) {\n\t\t\tuturnAllowed = TernaryBoolUtil.ToTernaryBool(value);\n\t\t}\n\n\t\tpublic void SetNearTurnOnRedAllowed(bool value) {\n\t\t\tnearTurnOnRedAllowed = TernaryBoolUtil.ToTernaryBool(value);\n\t\t}\n\n\t\tpublic void SetFarTurnOnRedAllowed(bool value) {\n\t\t\tfarTurnOnRedAllowed = TernaryBoolUtil.ToTernaryBool(value);\n\t\t}\n\n\t\tpublic void SetLaneChangingAllowedWhenGoingStraight(bool value) {\n\t\t\tstraightLaneChangingAllowed = TernaryBoolUtil.ToTernaryBool(value);\n\t\t}\n\n\t\tpublic void SetEnteringBlockedJunctionAllowed(bool value) {\n\t\t\tenterWhenBlockedAllowed = TernaryBoolUtil.ToTernaryBool(value);\n\t\t}\n\n\t\tpublic void SetPedestrianCrossingAllowed(bool value) {\n\t\t\tpedestrianCrossingAllowed = TernaryBoolUtil.ToTernaryBool(value);\n\t\t}\n\n\t\tpublic bool IsDefault() {\n\t\t\tbool uturnIsDefault = uturnAllowed == TernaryBool.Undefined || TernaryBoolUtil.ToBool(uturnAllowed) == defaultUturnAllowed;\n            bool nearTurnOnRedIsDefault = nearTurnOnRedAllowed == TernaryBool.Undefined || TernaryBoolUtil.ToBool(nearTurnOnRedAllowed) == defaultNearTurnOnRedAllowed;\n\t\t\tbool farTurnOnRedIsDefault = farTurnOnRedAllowed == TernaryBool.Undefined || TernaryBoolUtil.ToBool(farTurnOnRedAllowed) == defaultFarTurnOnRedAllowed;\n\t\t\tbool straightChangeIsDefault = straightLaneChangingAllowed == TernaryBool.Undefined || TernaryBoolUtil.ToBool(straightLaneChangingAllowed) == defaultStraightLaneChangingAllowed;\n\t\t\tbool enterWhenBlockedIsDefault = enterWhenBlockedAllowed == TernaryBool.Undefined || TernaryBoolUtil.ToBool(enterWhenBlockedAllowed) == defaultEnterWhenBlockedAllowed;\n\t\t\tbool pedCrossingIsDefault = pedestrianCrossingAllowed == TernaryBool.Undefined || TernaryBoolUtil.ToBool(pedestrianCrossingAllowed) == defaultPedestrianCrossingAllowed;\n\n\t\t\treturn uturnIsDefault && nearTurnOnRedIsDefault && farTurnOnRedIsDefault && straightChangeIsDefault && enterWhenBlockedIsDefault && pedCrossingIsDefault;\n\t\t}\n\n\t\tpublic void Reset(bool resetDefaults=true) {\n\t\t\tuturnAllowed = TernaryBool.Undefined;\n            nearTurnOnRedAllowed = TernaryBool.Undefined;\n\t\t\tfarTurnOnRedAllowed = TernaryBool.Undefined;\n\t\t\tstraightLaneChangingAllowed = TernaryBool.Undefined;\n\t\t\tenterWhenBlockedAllowed = TernaryBool.Undefined;\n\t\t\tpedestrianCrossingAllowed = TernaryBool.Undefined;\n\n\t\t\tif (resetDefaults) {\n\t\t\t\tdefaultUturnAllowed = false;\n                defaultNearTurnOnRedAllowed = false;\n\t\t\t\tdefaultFarTurnOnRedAllowed = false;\n\t\t\t\tdefaultStraightLaneChangingAllowed = false;\n\t\t\t\tdefaultEnterWhenBlockedAllowed = false;\n\t\t\t\tdefaultPedestrianCrossingAllowed = false;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[SegmentEndFlags\\n\" +\n\t\t\t\t\"\\t\" + $\"uturnAllowed = {uturnAllowed}\\n\" +\n                \"\\t\" + $\"nearTurnOnRedAllowed = {nearTurnOnRedAllowed}\\n\" +\n\t\t\t\t\"\\t\" + $\"farTurnOnRedAllowed = {farTurnOnRedAllowed}\\n\" +\n\t\t\t\t\"\\t\" + $\"straightLaneChangingAllowed = {straightLaneChangingAllowed}\\n\" +\n\t\t\t\t\"\\t\" + $\"enterWhenBlockedAllowed = {enterWhenBlockedAllowed}\\n\" +\n\t\t\t\t\"\\t\" + $\"pedestrianCrossingAllowed = {pedestrianCrossingAllowed}\\n\" +\n\t\t\t\t\"SegmentEndFlags]\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Traffic/Data/SegmentFlags.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.State;\n\nnamespace TrafficManager.Traffic.Data {\n\t/// <summary>\n\t/// Segment flags hold both segment end flags\n\t/// </summary>\n\tpublic struct SegmentFlags {\n\t\tpublic SegmentEndFlags startNodeFlags;\n\t\tpublic SegmentEndFlags endNodeFlags;\n\n\t\tpublic bool IsUturnAllowed(bool startNode) {\n\t\t\treturn startNode ? startNodeFlags.IsUturnAllowed() : endNodeFlags.IsUturnAllowed();\n\t\t}\n\n\t\tpublic bool IsNearTurnOnRedAllowed(bool startNode) {\n\t\t\treturn startNode ? startNodeFlags.IsNearTurnOnRedAllowed() : endNodeFlags.IsNearTurnOnRedAllowed();\n\t\t}\n\n\t\tpublic bool IsFarTurnOnRedAllowed(bool startNode) {\n\t\t\treturn startNode ? startNodeFlags.IsFarTurnOnRedAllowed() : endNodeFlags.IsFarTurnOnRedAllowed();\n\t\t}\n\n\t\tpublic bool IsLaneChangingAllowedWhenGoingStraight(bool startNode) {\n\t\t\treturn startNode ? startNodeFlags.IsLaneChangingAllowedWhenGoingStraight() : endNodeFlags.IsLaneChangingAllowedWhenGoingStraight();\n\t\t}\n\n\t\tpublic bool IsEnteringBlockedJunctionAllowed(bool startNode) {\n\t\t\treturn startNode ? startNodeFlags.IsEnteringBlockedJunctionAllowed() : endNodeFlags.IsEnteringBlockedJunctionAllowed();\n\t\t}\n\n\t\tpublic bool IsPedestrianCrossingAllowed(bool startNode) {\n\t\t\treturn startNode ? startNodeFlags.IsPedestrianCrossingAllowed() : endNodeFlags.IsPedestrianCrossingAllowed();\n\t\t}\n\n\t\tpublic TernaryBool GetUturnAllowed(bool startNode) {\n\t\t\treturn startNode ? startNodeFlags.uturnAllowed : endNodeFlags.uturnAllowed;\n\t\t}\n\n\t\tpublic TernaryBool GetNearTurnOnRedAllowed(bool startNode) {\n\t\t\treturn startNode ? startNodeFlags.nearTurnOnRedAllowed : endNodeFlags.nearTurnOnRedAllowed;\n\t\t}\n\n\t\tpublic TernaryBool GetFarTurnOnRedAllowed(bool startNode) {\n\t\t\treturn startNode ? startNodeFlags.farTurnOnRedAllowed : endNodeFlags.farTurnOnRedAllowed;\n\t\t}\n\n\t\tpublic TernaryBool GetLaneChangingAllowedWhenGoingStraight(bool startNode) {\n\t\t\treturn startNode ? startNodeFlags.straightLaneChangingAllowed : endNodeFlags.straightLaneChangingAllowed;\n\t\t}\n\n\t\tpublic TernaryBool GetEnteringBlockedJunctionAllowed(bool startNode) {\n\t\t\treturn startNode ? startNodeFlags.enterWhenBlockedAllowed : endNodeFlags.enterWhenBlockedAllowed;\n\t\t}\n\n\t\tpublic TernaryBool GetPedestrianCrossingAllowed(bool startNode) {\n\t\t\treturn startNode ? startNodeFlags.pedestrianCrossingAllowed : endNodeFlags.pedestrianCrossingAllowed;\n\t\t}\n\n\t\tpublic void SetUturnAllowed(bool startNode, bool value) {\n\t\t\tif (startNode) {\n\t\t\t\tstartNodeFlags.SetUturnAllowed(value);\n\t\t\t} else {\n\t\t\t\tendNodeFlags.SetUturnAllowed(value);\n\t\t\t}\n\t\t}\n\n\t\tpublic void SetNearTurnOnRedAllowed(bool startNode, bool value) {\n\t\t\tif (startNode) {\n\t\t\t\tstartNodeFlags.SetNearTurnOnRedAllowed(value);\n\t\t\t} else {\n\t\t\t\tendNodeFlags.SetNearTurnOnRedAllowed(value);\n\t\t\t}\n\t\t}\n\n\t\tpublic void SetFarTurnOnRedAllowed(bool startNode, bool value) {\n\t\t\tif (startNode) {\n\t\t\t\tstartNodeFlags.SetFarTurnOnRedAllowed(value);\n\t\t\t} else {\n\t\t\t\tendNodeFlags.SetFarTurnOnRedAllowed(value);\n\t\t\t}\n\t\t}\n\n\t\tpublic void SetLaneChangingAllowedWhenGoingStraight(bool startNode, bool value) {\n\t\t\tif (startNode) {\n\t\t\t\tstartNodeFlags.SetLaneChangingAllowedWhenGoingStraight(value);\n\t\t\t} else {\n\t\t\t\tendNodeFlags.SetLaneChangingAllowedWhenGoingStraight(value);\n\t\t\t}\n\t\t}\n\n\t\tpublic void SetEnteringBlockedJunctionAllowed(bool startNode, bool value) {\n\t\t\tif (startNode) {\n\t\t\t\tstartNodeFlags.SetEnteringBlockedJunctionAllowed(value);\n\t\t\t} else {\n\t\t\t\tendNodeFlags.SetEnteringBlockedJunctionAllowed(value);\n\t\t\t}\n\t\t}\n\n\t\tpublic void SetPedestrianCrossingAllowed(bool startNode, bool value) {\n\t\t\tif (startNode) {\n\t\t\t\tstartNodeFlags.SetPedestrianCrossingAllowed(value);\n\t\t\t} else {\n\t\t\t\tendNodeFlags.SetPedestrianCrossingAllowed(value);\n\t\t\t}\n\t\t}\n\n\t\tpublic bool IsDefault() {\n\t\t\treturn startNodeFlags.IsDefault() && endNodeFlags.IsDefault();\n\t\t}\n\n\t\tpublic void Reset(bool? startNode=null, bool resetDefaults=true) {\n\t\t\tif (startNode == null || (bool)startNode) {\n\t\t\t\tstartNodeFlags.Reset(resetDefaults);\n\t\t\t}\n\n\t\t\tif (startNode == null || ! (bool)startNode) {\n\t\t\t\tendNodeFlags.Reset(resetDefaults);\n\t\t\t}\n\t\t}\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[SegmentFlags\\n\" +\n\t\t\t\t\"\\t\" + $\"startNodeFlags = {startNodeFlags}\\n\" +\n\t\t\t\t\"\\t\" + $\"endNodeFlags = {endNodeFlags}\\n\" +\n\t\t\t\t\"SegmentFlags]\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Traffic/Data/TurnOnRedSegments.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Traffic.Data {\n\t/**\n\t * Holds left/right turn-on-red candidate segments\n\t */\n\tpublic struct TurnOnRedSegments {\n\t\t/**\n\t\t * Left segment id (or 0 if no left turn-on-red candidate segment)\n\t\t */\n\t\tpublic ushort leftSegmentId;\n\n\t\t/**\n\t\t * Right segment id (or 0 if no right turn-on-red candidate segment)\n\t\t */\n\t\tpublic ushort rightSegmentId;\n\n\t\tpublic void Reset() {\n\t\t\tthis.leftSegmentId = 0;\n\t\t\tthis.rightSegmentId = 0;\n\t\t}\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[TurnOnRedSegments {base.ToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"leftSegmentId = {leftSegmentId}\\n\" +\n\t\t\t\t\"\\t\" + $\"rightSegmentId = {rightSegmentId}\\n\" +\n\t\t\t\t\"SegmentEnd]\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Traffic/Data/VehicleState.cs",
    "content": "#define DEBUGVSTATEx\n#define DEBUGREGx\n\nusing System;\nusing ColossalFramework;\nusing UnityEngine;\nusing System.Collections.Generic;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.State;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing ColossalFramework.Math;\n\nnamespace TrafficManager.Traffic.Data {\n\tpublic struct VehicleState {\n\t\tpublic const int STATE_UPDATE_SHIFT = 6;\n\t\tpublic const int JUNCTION_RECHECK_SHIFT = 4;\n\t\tpublic const uint MAX_TIMED_RAND = 100;\n\n\t\t[Flags]\n\t\tpublic enum Flags {\n\t\t\tNone = 0,\n\t\t\tCreated = 1,\n\t\t\tSpawned = 1 << 1\n\t\t}\n\n\t\tpublic VehicleJunctionTransitState JunctionTransitState {\n\t\t\tget { return junctionTransitState; }\n\t\t\tset {\n\t\t\t\tif (value != junctionTransitState) {\n\t\t\t\t\tlastTransitStateUpdate = Now();\n\t\t\t\t}\n\t\t\t\tjunctionTransitState = value;\n\t\t\t}\n\t\t}\n\n\t\tpublic float SqrVelocity {\n\t\t\tget {\n\t\t\t\tfloat ret = 0;\n\t\t\t\tConstants.ServiceFactory.VehicleService.ProcessVehicle(vehicleId, delegate (ushort vehId, ref Vehicle veh) {\n\t\t\t\t\tret = veh.GetLastFrameVelocity().sqrMagnitude;\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t}\n\n\t\tpublic float Velocity {\n\t\t\tget {\n\t\t\t\tfloat ret = 0;\n\t\t\t\tConstants.ServiceFactory.VehicleService.ProcessVehicle(vehicleId, delegate (ushort vehId, ref Vehicle veh) {\n\t\t\t\t\tret = veh.GetLastFrameVelocity().magnitude;\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t}\n\n\t\t/*public bool Valid {\n\t\t\tget {\n\t\t\t\tif ((Singleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId].m_flags & Vehicle.Flags.Created) == 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn valid;\n\t\t\t}\n\t\t\tinternal set { valid = value; }\n\t\t}*/\n\n\t\tpublic ushort vehicleId;\n\t\tpublic uint lastPathId;\n\t\tpublic byte lastPathPositionIndex;\n\t\tpublic uint lastTransitStateUpdate;\n\t\tpublic uint lastPositionUpdate;\n\t\tpublic float totalLength;\n\t\t//public float sqrVelocity;\n\t\t//public float velocity;\n\t\tpublic int waitTime;\n\t\tpublic float reduceSqrSpeedByValueToYield;\n\t\tpublic Flags flags;\n\t\tpublic ExtVehicleType vehicleType;\n\t\tpublic bool heavyVehicle;\n\t\tpublic bool recklessDriver;\n\t\tpublic ushort currentSegmentId;\n\t\tpublic bool currentStartNode;\n\t\tpublic byte currentLaneIndex;\n\t\tpublic ushort nextSegmentId;\n\t\tpublic byte nextLaneIndex;\n\t\tpublic ushort previousVehicleIdOnSegment;\n\t\tpublic ushort nextVehicleIdOnSegment;\n\t\tpublic ushort lastAltLaneSelSegmentId;\n\t\tpublic byte timedRand;\n\t\tprivate VehicleJunctionTransitState junctionTransitState;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[VehicleState\\n\" +\n\t\t\t\t\"\\t\" + $\"vehicleId = {vehicleId}\\n\" +\n\t\t\t\t\"\\t\" + $\"lastPathId = {lastPathId}\\n\" +\n\t\t\t\t\"\\t\" + $\"lastPathPositionIndex = {lastPathPositionIndex}\\n\" +\n\t\t\t\t\"\\t\" + $\"JunctionTransitState = {JunctionTransitState}\\n\" +\n\t\t\t\t\"\\t\" + $\"lastTransitStateUpdate = {lastTransitStateUpdate}\\n\" +\n\t\t\t\t\"\\t\" + $\"lastPositionUpdate = {lastPositionUpdate}\\n\" +\n\t\t\t\t\"\\t\" + $\"totalLength = {totalLength}\\n\" +\n\t\t\t\t//\"\\t\" + $\"velocity = {velocity}\\n\" +\n\t\t\t\t//\"\\t\" + $\"sqrVelocity = {sqrVelocity}\\n\" +\n\t\t\t\t\"\\t\" + $\"waitTime = {waitTime}\\n\" +\n\t\t\t\t\"\\t\" + $\"reduceSqrSpeedByValueToYield = {reduceSqrSpeedByValueToYield}\\n\" +\n\t\t\t\t\"\\t\" + $\"flags = {flags}\\n\" +\n\t\t\t\t\"\\t\" + $\"vehicleType = {vehicleType}\\n\" +\n\t\t\t\t\"\\t\" + $\"heavyVehicle = {heavyVehicle}\\n\" +\n\t\t\t\t\"\\t\" + $\"recklessDriver = {recklessDriver}\\n\" +\n\t\t\t\t\"\\t\" + $\"currentSegmentId = {currentSegmentId}\\n\" +\n\t\t\t\t\"\\t\" + $\"currentStartNode = {currentStartNode}\\n\" +\n\t\t\t\t\"\\t\" + $\"currentLaneIndex = {currentLaneIndex}\\n\" +\n\t\t\t\t\"\\t\" + $\"nextSegmentId = {nextSegmentId}\\n\" +\n\t\t\t\t\"\\t\" + $\"nextLaneIndex = {nextLaneIndex}\\n\" +\n\t\t\t\t\"\\t\" + $\"previousVehicleIdOnSegment = {previousVehicleIdOnSegment}\\n\" +\n\t\t\t\t\"\\t\" + $\"nextVehicleIdOnSegment = {nextVehicleIdOnSegment}\\n\" +\n\t\t\t\t\"\\t\" + $\"lastAltLaneSelSegmentId = {lastAltLaneSelSegmentId}\\n\" +\n\t\t\t\t\"\\t\" + $\"junctionTransitState = {junctionTransitState}\\n\" +\n\t\t\t\t\"\\t\" + $\"timedRand = {timedRand}\\n\" +\n\t\t\t\t\"VehicleState]\";\n\t\t}\n\n\t\tinternal VehicleState(ushort vehicleId) {\n\t\t\tthis.vehicleId = vehicleId;\n\t\t\tlastPathId = 0;\n\t\t\tlastPathPositionIndex = 0;\n\t\t\tlastTransitStateUpdate = Now();\n\t\t\tlastPositionUpdate = Now();\n\t\t\ttotalLength = 0;\n\t\t\twaitTime = 0;\n\t\t\treduceSqrSpeedByValueToYield = 0;\n\t\t\tflags = Flags.None;\n\t\t\tvehicleType = ExtVehicleType.None;\n\t\t\theavyVehicle = false;\n\t\t\trecklessDriver = false;\n\t\t\tcurrentSegmentId = 0;\n\t\t\tcurrentStartNode = false;\n\t\t\tcurrentLaneIndex = 0;\n\t\t\tnextSegmentId = 0;\n\t\t\tnextLaneIndex = 0;\n\t\t\tpreviousVehicleIdOnSegment = 0;\n\t\t\tnextVehicleIdOnSegment = 0;\n\t\t\t//velocity = 0;\n\t\t\t//sqrVelocity = 0;\n\t\t\tlastAltLaneSelSegmentId = 0;\n\t\t\tjunctionTransitState = VehicleJunctionTransitState.None;\n\t\t\ttimedRand = 0;\n\t\t}\n\n\t\t/*private void Reset(bool unlink=true) { // TODO this is called in wrong places!\n\t\t\tif (unlink)\n\t\t\t\tUnlink();\n\n\t\t\tValid = false;\n\t\t\ttotalLength = 0f;\n\t\t\t//VehicleType = ExtVehicleType.None;\n\t\t\twaitTime = 0;\n\t\t\tJunctionTransitState = VehicleJunctionTransitState.None;\n\t\t\tlastStateUpdate = 0;\n\t\t}*/\n\n\t\t/*public ExtCitizenInstance GetDriverExtInstance() {\n\t\t\tushort driverInstanceId = CustomPassengerCarAI.GetDriverInstance(vehicleId, ref Singleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId]);\n\t\t\tif (driverInstanceId != 0) {\n\t\t\t\treturn ExtCitizenInstanceManager.Instance.GetExtInstance(driverInstanceId);\n\t\t\t}\n\t\t\treturn null;\n\t\t}*/\n\n\t\tinternal void Unlink() {\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.Unlink({vehicleId}) called: Unlinking vehicle from all segment ends\\nstate:{this}\");\n#endif\n\n\t\t\tIVehicleStateManager vehStateManager = Constants.ManagerFactory.VehicleStateManager;\n\n\t\t\tlastPositionUpdate = Now();\n\n\t\t\tif (previousVehicleIdOnSegment != 0) {\n\t\t\t\tvehStateManager.SetNextVehicleIdOnSegment(previousVehicleIdOnSegment, nextVehicleIdOnSegment);// VehicleStates[previousVehicleIdOnSegment].nextVehicleIdOnSegment = nextVehicleIdOnSegment;\n\t\t\t} else if (currentSegmentId != 0) {\n\t\t\t\tISegmentEnd curEnd = Constants.ManagerFactory.SegmentEndManager.GetSegmentEnd(currentSegmentId, currentStartNode);\n\t\t\t\tif (curEnd != null && curEnd.FirstRegisteredVehicleId == vehicleId) {\n\t\t\t\t\tcurEnd.FirstRegisteredVehicleId = nextVehicleIdOnSegment;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (nextVehicleIdOnSegment != 0) {\n\t\t\t\tvehStateManager.SetPreviousVehicleIdOnSegment(nextVehicleIdOnSegment, previousVehicleIdOnSegment);// .VehicleStates[nextVehicleIdOnSegment].previousVehicleIdOnSegment = previousVehicleIdOnSegment;\n\t\t\t}\n\n\t\t\tnextVehicleIdOnSegment = 0;\n\t\t\tpreviousVehicleIdOnSegment = 0;\n\n\t\t\tcurrentSegmentId = 0;\n\t\t\tcurrentStartNode = false;\n\t\t\tcurrentLaneIndex = 0;\n\n\t\t\tlastPathId = 0;\n\t\t\tlastPathPositionIndex = 0;\n\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.Unlink({vehicleId}) finished: Unlinked vehicle from all segment ends\\nstate:{this}\");\n#endif\n\t\t}\n\n\t\tprivate void Link(ISegmentEnd end) {\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.Link({vehicleId}) called: Linking vehicle to segment end {end}\\nstate:{this}\");\n#endif\n\n\t\t\tushort oldFirstRegVehicleId = end.FirstRegisteredVehicleId;\n\t\t\tif (oldFirstRegVehicleId != 0) {\n\t\t\t\tConstants.ManagerFactory.VehicleStateManager.SetPreviousVehicleIdOnSegment(oldFirstRegVehicleId, vehicleId);// VehicleStates[oldFirstRegVehicleId].previousVehicleIdOnSegment = vehicleId;\n\t\t\t\tnextVehicleIdOnSegment = oldFirstRegVehicleId;\n\t\t\t}\n\t\t\tend.FirstRegisteredVehicleId = vehicleId;\n\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.Link({vehicleId}) finished: Linked vehicle to segment end {end}\\nstate:{this}\");\n#endif\n\t\t}\n\n\t\tinternal void OnCreate(ref Vehicle vehicleData) {\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.OnCreate({vehicleId}) called: {this}\");\n#endif\n\t\t\t\n\t\t\tif ((flags & Flags.Created) != Flags.None) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleState.OnCreate({vehicleId}): Vehicle is already created.\");\n#endif\n\t\t\t\tOnRelease(ref vehicleData);\n\t\t\t}\n\n\t\t\tDetermineVehicleType(ref vehicleData);\n\t\t\treduceSqrSpeedByValueToYield = UnityEngine.Random.Range(256f, 784f);\n\t\t\trecklessDriver = false;\n\t\t\tflags = Flags.Created;\n\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.OnCreate({vehicleId}) finished: {this}\");\n#endif\n\t\t}\n\n\t\tinternal ExtVehicleType OnStartPathFind(ref Vehicle vehicleData, ExtVehicleType? vehicleType) {\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.OnStartPathFind({vehicleId}, {vehicleType}) called: {this}\");\n#endif\n\n\t\t\tif ((flags & Flags.Created) == Flags.None) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleState.OnStartPathFind({vehicleId}, {vehicleType}): Vehicle has not yet been created.\");\n#endif\n\t\t\t\tOnCreate(ref vehicleData);\n\t\t\t}\n\n\t\t\tif (vehicleType != null) {\n\t\t\t\tthis.vehicleType = (ExtVehicleType)vehicleType;\n\t\t\t}\n\n\t\t\trecklessDriver = Constants.ManagerFactory.VehicleBehaviorManager.IsRecklessDriver(vehicleId, ref vehicleData);\n\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.OnStartPathFind({vehicleId}, {vehicleType}) finished: {this}\");\n#endif\n\n\t\t\treturn this.vehicleType;\n\t\t}\n\n\t\tinternal void OnSpawn(ref Vehicle vehicleData) {\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.OnSpawn({vehicleId}) called: {this}\");\n#endif\n\n\t\t\tif ((flags & Flags.Created) == Flags.None) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleState.OnSpawn({vehicleId}): Vehicle has not yet been created.\");\n#endif\n\t\t\t\tOnCreate(ref vehicleData);\n\t\t\t}\n\n\t\t\tUnlink();\n\n\t\t\t//velocity = 0;\n\t\t\t//sqrVelocity = 0;\n\t\t\tlastPathId = 0;\n\t\t\tlastPathPositionIndex = 0;\n\t\t\tlastAltLaneSelSegmentId = 0;\n\t\t\trecklessDriver = Constants.ManagerFactory.VehicleBehaviorManager.IsRecklessDriver(vehicleId, ref vehicleData);\n\n\t\t\ttry {\n\t\t\t\ttotalLength = vehicleData.CalculateTotalLength(vehicleId);\n\t\t\t} catch (Exception\n#if DEBUG\n\t\t\te\n#endif\n\t\t\t) {\n\t\t\t\ttotalLength = 0;\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleState.OnSpawn({vehicleId}): Error occurred while calculating total length: {e}\\nstate: {this}\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tflags |= Flags.Spawned;\n\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.OnSpawn({vehicleId}) finished: {this}\");\n#endif\n\t\t}\n\n\t\tinternal void UpdatePosition(ref Vehicle vehicleData, ref PathUnit.Position curPos, ref PathUnit.Position nextPos, bool skipCheck = false) {\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.UpdatePosition({vehicleId}) called: {this}\");\n#endif\n\n\t\t\tif ((flags & Flags.Spawned) == Flags.None) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleState.UpdatePosition({vehicleId}): Vehicle is not yet spawned.\");\n#endif\n\t\t\t\tOnSpawn(ref vehicleData);\n\t\t\t}\n\n\t\t\tif (nextSegmentId != nextPos.m_segment || nextLaneIndex != nextPos.m_lane) {\n\t\t\t\tnextSegmentId = nextPos.m_segment;\n\t\t\t\tnextLaneIndex = nextPos.m_lane;\n\t\t\t}\n\n\t\t\tbool startNode = IsTransitNodeCurStartNode(ref curPos, ref nextPos);\n\t\t\tISegmentEnd end = Constants.ManagerFactory.SegmentEndManager.GetSegmentEnd(curPos.m_segment, startNode);\n\n\t\t\tif (end == null || currentSegmentId != end.SegmentId || currentStartNode != end.StartNode || currentLaneIndex != curPos.m_lane) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleState.UpdatePosition({vehicleId}): Current segment end changed. seg. {currentSegmentId}, start {currentStartNode}, lane {currentLaneIndex} -> seg. {end?.SegmentId}, start {end?.StartNode}, lane {curPos.m_lane}\");\n#endif\n\t\t\t\tif (currentSegmentId != 0) {\n#if DEBUG\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\t\tLog._Debug($\"VehicleState.UpdatePosition({vehicleId}): Unlinking from current segment end\");\n#endif\n\t\t\t\t\tUnlink();\n\t\t\t\t}\n\n\t\t\t\tlastPathId = vehicleData.m_path;\n\t\t\t\tlastPathPositionIndex = vehicleData.m_pathPositionIndex;\n\n\t\t\t\tcurrentSegmentId = curPos.m_segment;\n\t\t\t\tcurrentStartNode = startNode;\n\t\t\t\tcurrentLaneIndex = curPos.m_lane;\n\n\t\t\t\twaitTime = 0;\n\t\t\t\tif (end != null) {\n#if DEBUGVSTATE\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\t\tLog._Debug($\"VehicleState.UpdatePosition({vehicleId}): Linking vehicle to segment end {end.SegmentId} @ {end.StartNode} ({end.NodeId}). Current position: Seg. {curPos.m_segment}, lane {curPos.m_lane}, offset {curPos.m_offset} / Next position: Seg. {nextPos.m_segment}, lane {nextPos.m_lane}, offset {nextPos.m_offset}\");\n#endif\n\t\t\t\t\tLink(end);\n\t\t\t\t\tJunctionTransitState = VehicleJunctionTransitState.Approach;\n\t\t\t\t} else {\n\t\t\t\t\tJunctionTransitState = VehicleJunctionTransitState.None;\n\t\t\t\t}\n\t\t\t}\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.UpdatePosition({vehicleId}) finshed: {this}\");\n#endif\n\t\t}\n\n\t\tinternal void OnDespawn() {\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.OnDespawn({vehicleId} called: {this}\");\n#endif\n\t\t\tif ((flags & Flags.Spawned) == Flags.None) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleState.OnDespawn({vehicleId}): Vehicle is not spawned.\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tConstants.ManagerFactory.ExtCitizenInstanceManager.ResetInstance(CustomPassengerCarAI.GetDriverInstanceId(vehicleId, ref Singleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId]));\n\t\t\t\n\t\t\tUnlink();\n\n\t\t\tcurrentSegmentId = 0;\n\t\t\tcurrentStartNode = false;\n\t\t\tcurrentLaneIndex = 0;\n\t\t\tlastAltLaneSelSegmentId = 0;\n\t\t\trecklessDriver = false;\n\n\t\t\tnextSegmentId = 0;\n\t\t\tnextLaneIndex = 0;\n\n\t\t\ttotalLength = 0;\n\n\t\t\tflags &= ~Flags.Spawned;\n\t\t\t\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.OnDespawn({vehicleId}) finished: {this}\");\n#endif\n\t\t}\n\n\t\tinternal void OnRelease(ref Vehicle vehicleData) {\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.OnRelease({vehicleId}) called: {this}\");\n#endif\n\n\t\t\tif ((flags & Flags.Created) == Flags.None) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleState.OnRelease({vehicleId}): Vehicle is not created.\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ((flags & Flags.Spawned) != Flags.None) {\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\t\tLog._Debug($\"VehicleState.OnRelease({vehicleId}): Vehicle is spawned.\");\n#endif\n\t\t\t\tOnDespawn();\n\t\t\t}\n\n\t\t\tlastPathId = 0;\n\t\t\tlastPathPositionIndex = 0;\n\t\t\tlastTransitStateUpdate = Now();\n\t\t\tlastPositionUpdate = Now();\n\t\t\twaitTime = 0;\n\t\t\treduceSqrSpeedByValueToYield = 0;\n\t\t\tflags = Flags.None;\n\t\t\tvehicleType = ExtVehicleType.None;\n\t\t\theavyVehicle = false;\n\t\t\tpreviousVehicleIdOnSegment = 0;\n\t\t\tnextVehicleIdOnSegment = 0;\n\t\t\tlastAltLaneSelSegmentId = 0;\n\t\t\tjunctionTransitState = VehicleJunctionTransitState.None;\n\t\t\trecklessDriver = false;\n\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.OnRelease({vehicleId}) finished: {this}\");\n#endif\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Determines if the junction transit state has been recently modified\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tinternal bool IsJunctionTransitStateNew() {\n\t\t\tuint frame = Constants.ServiceFactory.SimulationService.CurrentFrameIndex;\n\t\t\treturn (lastTransitStateUpdate >> STATE_UPDATE_SHIFT) >= (frame >> STATE_UPDATE_SHIFT);\n\t\t}\n\n\t\tpublic void StepRand() {\n\t\t\tRandomizer rand = Constants.ServiceFactory.SimulationService.Randomizer;\n\t\t\tif (rand.UInt32(20) == 0) {\n\t\t\t\ttimedRand = (byte)(((uint)timedRand + rand.UInt32(25)) % MAX_TIMED_RAND);\n\t\t\t}\n\t\t}\n\n\t\tprivate static ushort GetTransitNodeId(ref PathUnit.Position curPos, ref PathUnit.Position nextPos) {\n\t\t\tbool startNode = IsTransitNodeCurStartNode(ref curPos, ref nextPos);\n\t\t\tushort transitNodeId1 = 0;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(curPos.m_segment, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\ttransitNodeId1 = startNode ? segment.m_startNode : segment.m_endNode;\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tushort transitNodeId2 = 0;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(nextPos.m_segment, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\ttransitNodeId2 = startNode ? segment.m_startNode : segment.m_endNode;\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tif (transitNodeId1 != transitNodeId2) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn transitNodeId1;\n\t\t}\n\n\t\tprivate static bool IsTransitNodeCurStartNode(ref PathUnit.Position curPos, ref PathUnit.Position nextPos) {\n\t\t\t// note: does not check if curPos and nextPos are successive path positions\n\t\t\tbool startNode;\n\t\t\tif (curPos.m_offset == 0) {\n\t\t\t\tstartNode = true;\n\t\t\t} else if (curPos.m_offset == 255) {\n\t\t\t\tstartNode = false;\n\t\t\t} else if (nextPos.m_offset == 0) {\n\t\t\t\tstartNode = true;\n\t\t\t} else {\n\t\t\t\tstartNode = false;\n\t\t\t}\n\t\t\treturn startNode;\n\t\t}\n\n\t\tprivate static uint Now() {\n\t\t\treturn Constants.ServiceFactory.SimulationService.CurrentFrameIndex;\n\t\t}\n\n\t\tprivate void DetermineVehicleType(ref Vehicle vehicleData) {\n\t\t\tVehicleAI ai = vehicleData.Info.m_vehicleAI;\n\n\t\t\tif ((vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0) {\n\t\t\t\tvehicleType = ExtVehicleType.Emergency;\n\t\t\t} else {\n\t\t\t\tExtVehicleType? type = DetermineVehicleTypeFromAIType(ai, false);\n\t\t\t\tif (type != null) {\n\t\t\t\t\tvehicleType = (ExtVehicleType)type;\n\t\t\t\t} else {\n\t\t\t\t\tvehicleType = ExtVehicleType.None;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (vehicleType == ExtVehicleType.CargoTruck) {\n\t\t\t\theavyVehicle = ((CargoTruckAI)ai).m_isHeavyVehicle;\n\t\t\t} else {\n\t\t\t\theavyVehicle = false;\n\t\t\t}\n\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[9])\n\t\t\t\tLog._Debug($\"VehicleState.DetermineVehicleType({vehicleId}): vehicleType={vehicleType}, heavyVehicle={heavyVehicle}. Info={vehicleData.Info?.name}\");\n#endif\n\t\t}\n\n\t\tprivate ExtVehicleType? DetermineVehicleTypeFromAIType(VehicleAI ai, bool emergencyOnDuty) {\n\t\t\tif (emergencyOnDuty)\n\t\t\t\treturn ExtVehicleType.Emergency;\n\n\t\t\tswitch (ai.m_info.m_vehicleType) {\n\t\t\t\tcase VehicleInfo.VehicleType.Bicycle:\n\t\t\t\t\treturn ExtVehicleType.Bicycle;\n\t\t\t\tcase VehicleInfo.VehicleType.Car:\n\t\t\t\t\tif (ai is PassengerCarAI)\n\t\t\t\t\t\treturn ExtVehicleType.PassengerCar;\n\t\t\t\t\tif (ai is AmbulanceAI || ai is FireTruckAI || ai is PoliceCarAI || ai is HearseAI || ai is GarbageTruckAI || ai is MaintenanceTruckAI || ai is SnowTruckAI || ai is WaterTruckAI || ai is DisasterResponseVehicleAI || ai is ParkMaintenanceVehicleAI || ai is PostVanAI) {\n\t\t\t\t\t\treturn ExtVehicleType.Service;\n\t\t\t\t\t}\n\t\t\t\t\tif (ai is CarTrailerAI)\n\t\t\t\t\t\treturn ExtVehicleType.None;\n\t\t\t\t\tif (ai is BusAI)\n\t\t\t\t\t\treturn ExtVehicleType.Bus;\n\t\t\t\t\tif (ai is TaxiAI)\n\t\t\t\t\t\treturn ExtVehicleType.Taxi;\n\t\t\t\t\tif (ai is CargoTruckAI)\n\t\t\t\t\t\treturn ExtVehicleType.CargoTruck;\n\t\t\t\t\tbreak;\n\t\t\t\tcase VehicleInfo.VehicleType.Metro:\n\t\t\t\tcase VehicleInfo.VehicleType.Train:\n\t\t\t\tcase VehicleInfo.VehicleType.Monorail:\n\t\t\t\t\tif (ai is CargoTrainAI)\n\t\t\t\t\t\treturn ExtVehicleType.CargoTrain;\n\t\t\t\t\treturn ExtVehicleType.PassengerTrain;\n\t\t\t\tcase VehicleInfo.VehicleType.Tram:\n\t\t\t\t\treturn ExtVehicleType.Tram;\n\t\t\t\tcase VehicleInfo.VehicleType.Ship:\n\t\t\t\t\tif (ai is PassengerShipAI)\n\t\t\t\t\t\treturn ExtVehicleType.PassengerShip;\n\t\t\t\t\t//if (ai is CargoShipAI)\n\t\t\t\t\treturn ExtVehicleType.CargoShip;\n\t\t\t\t//break;\n\t\t\t\tcase VehicleInfo.VehicleType.Plane:\n\t\t\t\t\tif (ai is PassengerPlaneAI)\n\t\t\t\t\t\treturn ExtVehicleType.PassengerPlane;\n\t\t\t\t\tif (ai is CargoPlaneAI)\n\t\t\t\t\t\treturn ExtVehicleType.CargoPlane;\n\t\t\t\t\tbreak;\n\t\t\t\tcase VehicleInfo.VehicleType.Helicopter:\n\t\t\t\t\t//if (ai is PassengerPlaneAI)\n\t\t\t\t\treturn ExtVehicleType.Helicopter;\n\t\t\t\t//break;\n\t\t\t\tcase VehicleInfo.VehicleType.Ferry:\n\t\t\t\t\treturn ExtVehicleType.Ferry;\n\t\t\t\tcase VehicleInfo.VehicleType.Blimp:\n\t\t\t\t\treturn ExtVehicleType.Blimp;\n\t\t\t\tcase VehicleInfo.VehicleType.CableCar:\n\t\t\t\t\treturn ExtVehicleType.CableCar;\n\t\t\t}\n#if DEBUGVSTATE\n\t\t\tLog._Debug($\"VehicleState.DetermineVehicleType({vehicleId}): Could not determine vehicle type from ai type: {ai.GetType().ToString()}\");\n#endif\n\t\t\treturn null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Traffic/ExtVehicleType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace TrafficManager.Traffic {\n\t[Flags]\n\tpublic enum ExtVehicleType {\n\t\tNone = 0,\n\t\tPassengerCar = 1,\n\t\tBus = 1 << 1,\n\t\tTaxi = 1 << 2,\n\t\tCargoTruck = 1 << 3,\n\t\tService = 1 << 4,\n\t\tEmergency = 1 << 5,\n\t\tPassengerTrain = 1 << 6,\n\t\tCargoTrain = 1 << 7,\n\t\tTram = 1 << 8,\n\t\tBicycle = 1 << 9,\n\t\tPedestrian = 1 << 10,\n\t\tPassengerShip = 1 << 11,\n\t\tCargoShip = 1 << 12,\n\t\tPassengerPlane = 1 << 13,\n\t\tHelicopter = 1 << 14,\n\t\tCableCar = 1 << 15,\n\t\tPassengerFerry = 1 << 16,\n\t\tPassengerBlimp = 1 << 17,\n\t\tCargoPlane = 1 << 18,\n\t\tPlane = PassengerPlane | CargoPlane,\n\t\tShip = PassengerShip | CargoShip,\n\t\tCargoVehicle = CargoTruck | CargoTrain | CargoShip | CargoPlane,\n\t\tPublicTransport = Bus | Taxi | Tram | PassengerTrain,\n\t\tRoadPublicTransport = Bus | Taxi,\n\t\tRoadVehicle = PassengerCar | Bus | Taxi | CargoTruck | Service | Emergency,\n\t\tRailVehicle = PassengerTrain | CargoTrain,\n\t\tNonTransportRoadVehicle = RoadVehicle & ~PublicTransport,\n\t\tFerry = PassengerFerry,\n\t\tBlimp = PassengerBlimp\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Traffic/ISegmentEnd.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Geometry;\n\nnamespace TrafficManager.Traffic {\n\tpublic interface ISegmentEnd : ISegmentEndId {\n\t\t[Obsolete]\n\t\tushort NodeId { get; }\n\t\tushort FirstRegisteredVehicleId { get; set; } // TODO private set\n\n\t\tvoid Update();\n\t\tvoid Destroy();\n\t\tIDictionary<ushort, uint>[] MeasureOutgoingVehicles(bool includeStopped = true, bool debug = false);\n\t\tint GetRegisteredVehicleCount();\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Traffic/Impl/SegmentEnd.cs",
    "content": "#define DEBUGFRONTVEHx\n#define DEBUGREGx\n#define DEBUGMETRIC\n#define DEBUGMETRIC2x\n\nusing System;\nusing System.Collections.Generic;\nusing ColossalFramework;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Util;\nusing System.Threading;\nusing TrafficManager.State;\nusing TrafficManager.UI;\nusing TrafficManager.Manager;\nusing System.Linq;\nusing CSUtil.Commons;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Traffic.Data;\n\n/// <summary>\n/// A segment end describes a directional traffic segment connected to a controlled node\n/// (having custom traffic lights or priority signs).\n/// </summary>\nnamespace TrafficManager.Traffic.Impl {\n\tpublic class SegmentEnd : SegmentEndId, ISegmentEnd {\n\t\t// TODO convert to struct\n\n\t\t[Obsolete]\n\t\tpublic ushort NodeId {\n\t\t\tget {\n\t\t\t\treturn Constants.ServiceFactory.NetService.GetSegmentNodeId(SegmentId, StartNode);\n\t\t\t}\n\t\t}\n\n\t\tprivate int numLanes = 0;\n\n\t\t/// <summary>\n\t\t/// Vehicles that are traversing or will traverse this segment\n\t\t/// </summary>\n\t\tpublic ushort FirstRegisteredVehicleId { get; set; } = 0; // TODO private set\n\n\t\tprivate bool cleanupRequested = false;\n\n\t\t/// <summary>\n\t\t/// Vehicles that are traversing or will traverse this segment\n\t\t/// </summary>\n\t\t//private ushort[] frontVehicleIds;\n\n\t\t/// <summary>\n\t\t/// Number of vehicles / vehicle length going to a certain segment.\n\t\t/// First key: source lane index, second key: target segment id, value: total normalized vehicle length\n\t\t/// </summary>\n\t\tprivate IDictionary<ushort, uint>[] numVehiclesMovingToSegmentId; // minimum speed required\n\t\tprivate IDictionary<ushort, uint>[] numVehiclesGoingToSegmentId; // no minimum speed required\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[SegmentEnd {base.ToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NodeId = {NodeId}\\n\" +\n\t\t\t\t\"\\t\" + $\"numLanes = {numLanes}\\n\" +\n\t\t\t\t\"\\t\" + $\"FirstRegisteredVehicleId = {FirstRegisteredVehicleId}\\n\" +\n\t\t\t\t\"\\t\" + $\"cleanupRequested = {cleanupRequested}\\n\" +\n\t\t\t\t\"\\t\" + $\"numVehiclesMovingToSegmentId = \" + (numVehiclesMovingToSegmentId == null ? \"<null>\" : numVehiclesMovingToSegmentId.ArrayToString()) + \"\\n\" +\n\t\t\t\t\"\\t\" + $\"numVehiclesGoingToSegmentId = \" + (numVehiclesGoingToSegmentId == null ? \"<null>\" : numVehiclesGoingToSegmentId.ArrayToString()) + \"\\n\" +\n\t\t\t\t\"SegmentEnd]\";\n\t\t}\n\n\t\tpublic SegmentEnd(ushort segmentId, bool startNode) : base(segmentId, startNode) {\n\t\t\tFirstRegisteredVehicleId = 0;\n\t\t\tUpdate();\n\t\t}\n\t\t\n\t\t~SegmentEnd() {\n\t\t\tDestroy();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calculates for each segment the number of cars going to this segment.\n\t\t/// We use integer arithmetic for better performance.\n\t\t/// </summary>\n\t\tpublic IDictionary<ushort, uint>[] MeasureOutgoingVehicles(bool includeStopped=true, bool debug = false) {\n\t\t\t//VehicleManager vehicleManager = Singleton<VehicleManager>.instance;\n\t\t\t//NetManager netManager = Singleton<NetManager>.instance;\n\t\t\tVehicleStateManager vehStateManager = VehicleStateManager.Instance;\n\n\t\t\t// TODO pre-calculate this\n\t\t\tuint avgSegLen = 0;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(SegmentId, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\tavgSegLen = (uint)segment.m_averageLength;\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tIDictionary<ushort, uint>[] ret = includeStopped ? numVehiclesGoingToSegmentId : numVehiclesMovingToSegmentId;\n\n\t\t\t// reset\n\t\t\tfor (byte laneIndex = 0; laneIndex < ret.Length; ++laneIndex) {\n\t\t\t\tIDictionary<ushort, uint> laneMetrics = ret[laneIndex];\n\t\t\t\tforeach (KeyValuePair<ushort, uint> e in laneMetrics) {\n\t\t\t\t\tlaneMetrics[e.Key] = 0;\n\t\t\t\t}\n\t\t\t}\n\n#if DEBUGMETRIC\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"GetVehicleMetricGoingToSegment: Segment {SegmentId}, Node {NodeId}, includeStopped={includeStopped}.\");\n#endif\n\n\t\t\tushort vehicleId = FirstRegisteredVehicleId;\n\t\t\tint numProcessed = 0;\n\t\t\twhile (vehicleId != 0) {\n\t\t\t\tMeasureOutgoingVehicle(debug, ret, includeStopped, avgSegLen, vehicleId, ref vehStateManager.VehicleStates[vehicleId], ref numProcessed);\n\n\t\t\t\tif ((Options.simAccuracy >= 3 && numProcessed >= 3) || (Options.simAccuracy == 2 && numProcessed >= 5) || (Options.simAccuracy == 1 && numProcessed >= 10)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tvehicleId = vehStateManager.VehicleStates[vehicleId].nextVehicleIdOnSegment;\n\t\t\t}\n\n#if DEBUGMETRIC\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"GetVehicleMetricGoingToSegment: Calculation completed. {string.Join(\", \", ret.Select(e => \"[\" + string.Join(\", \", e.Select(x => x.Key.ToString() + \"=\" + x.Value.ToString()).ToArray()) + \"]\").ToArray())}\");\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tprotected void MeasureOutgoingVehicle(bool debug, IDictionary<ushort, uint>[] ret, bool includeStopped, uint avgSegmentLength, ushort vehicleId, ref VehicleState state, ref int numProcessed) {\n#if DEBUGMETRIC\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\" MeasureOutgoingVehicle: (Segment {SegmentId}, Node {NodeId} (start={StartNode})) Checking vehicle {vehicleId}. Coming from seg. {state.currentSegmentId}, start {state.currentStartNode}, lane {state.currentLaneIndex} going to seg. {state.nextSegmentId}, lane {state.nextLaneIndex}\");\n#endif\n\n\t\t\tif ((state.flags & VehicleState.Flags.Spawned) == VehicleState.Flags.None) {\n#if DEBUGMETRIC\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\" MeasureOutgoingVehicle: Vehicle {vehicleId} is unspawned. Ignoring.\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n#if DEBUGMETRIC\n\t\t\tif (state.currentSegmentId != SegmentId || state.currentStartNode != StartNode) {\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\" MeasureOutgoingVehicle: (Segment {SegmentId}, Node {NodeId} (start={StartNode})) Vehicle {vehicleId} error: Segment end mismatch! {state.ToString()}\");\n\t\t\t\t//RequestCleanup();\n\t\t\t\treturn;\n\t\t\t}\n#endif\n\n\t\t\tif (state.nextSegmentId == 0) {\n#if DEBUGMETRIC\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\" MeasureOutgoingVehicle: (Segment {SegmentId}, Node {NodeId} (start={StartNode})) Vehicle {vehicleId}: Ignoring vehicle\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (state.currentLaneIndex >= ret.Length || !ret[state.currentLaneIndex].ContainsKey(state.nextSegmentId)) {\n#if DEBUGMETRIC\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\" MeasureOutgoingVehicle: (Segment {SegmentId}, Node {NodeId} (start={StartNode})) Vehicle {vehicleId} is on lane {state.currentLaneIndex} and wants to go to segment {state.nextSegmentId} but one or both are invalid: {ret.CollectionToString()}\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!includeStopped && state.SqrVelocity < GlobalConfig.Instance.PriorityRules.MaxStopVelocity * GlobalConfig.Instance.PriorityRules.MaxStopVelocity) {\n#if DEBUGMETRIC\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"  MeasureOutgoingVehicle: (Segment {SegmentId}, Node {NodeId}) Vehicle {vehicleId}: too slow ({state.SqrVelocity})\");\n#endif\n\t\t\t\t++numProcessed;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t\n\t\t\tuint normLength = 10u;\n\t\t\tif (avgSegmentLength > 0) {\n\t\t\t\tnormLength = Math.Min(100u, (uint)(Math.Max(1u, state.totalLength) * 100u) / avgSegmentLength) + 1; // TODO +1 because the vehicle length calculation for trains/monorail in the method VehicleState.OnVehicleSpawned returns 0 (or a very small number maybe?)\n\t\t\t}\n\n#if DEBUGMETRIC\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"  MeasureOutgoingVehicle: (Segment {SegmentId}, Node {NodeId}) NormLength of vehicle {vehicleId}: {state.totalLength} -> {normLength} (avgSegmentLength={avgSegmentLength})\");\n#endif\n\n\t\t\tret[state.currentLaneIndex][state.nextSegmentId] += normLength;\n\t\t\t++numProcessed;\n\n#if DEBUGMETRIC\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"  MeasureOutgoingVehicle: (Segment {SegmentId}, Node {NodeId}) Vehicle {vehicleId}: ***ADDED*** ({state.currentSegmentId}@{state.currentLaneIndex} -> {state.nextSegmentId}@{state.nextLaneIndex})!\");\n#endif\n\n\t\t\treturn;\n\t\t}\n\n\t\tpublic int GetRegisteredVehicleCount() {\n\t\t\tVehicleStateManager vehStateManager = VehicleStateManager.Instance;\n\n\t\t\tushort vehicleId = FirstRegisteredVehicleId;\n\t\t\tint ret = 0;\n\t\t\twhile (vehicleId != 0) {\n\t\t\t\t++ret;\n\t\t\t\tvehicleId = vehStateManager.VehicleStates[vehicleId].nextVehicleIdOnSegment;\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic void Destroy() {\n\t\t\tUnregisterAllVehicles();\n\t\t}\n\n\t\tprivate void UnregisterAllVehicles() {\n\t\t\tVehicleStateManager vehStateManager = VehicleStateManager.Instance;\n\t\t\twhile (FirstRegisteredVehicleId != 0) {\n\t\t\t\tvehStateManager.VehicleStates[FirstRegisteredVehicleId].Unlink();\n\t\t\t}\n\t\t}\n\n\t\tpublic void Update() {\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(SegmentId, delegate(ushort segmentId, ref NetSegment segment) {\n\t\t\t\tStartNode = segment.m_startNode == NodeId;\n\t\t\t\tnumLanes = segment.Info.m_lanes.Length;\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(SegmentId);\n\n\t\t\tif (segGeo == null) {\n\t\t\t\tLog.Error($\"SegmentEnd.Update: No geometry information available for segment {SegmentId}\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tushort[] outgoingSegmentIds = SegmentGeometry.Get(SegmentId).GetOutgoingSegments(StartNode);\n\t\t\tnumVehiclesMovingToSegmentId = new TinyDictionary<ushort, uint>[numLanes];\n\t\t\tnumVehiclesGoingToSegmentId = new TinyDictionary<ushort, uint>[numLanes];\n\n\t\t\tConstants.ServiceFactory.NetService.IterateSegmentLanes(SegmentId, delegate (uint laneId, ref NetLane lane, NetInfo.Lane laneInfo, ushort segmentId, ref NetSegment segment, byte laneIndex) {\n\t\t\t\tIDictionary<ushort, uint> numVehicleMoving = new TinyDictionary<ushort, uint>();\n\t\t\t\tIDictionary<ushort, uint> numVehicleGoing = new TinyDictionary<ushort, uint>();\n\n\t\t\t\tnumVehiclesMovingToSegmentId[laneIndex] = numVehicleMoving;\n\t\t\t\tnumVehiclesGoingToSegmentId[laneIndex] = numVehicleGoing;\n\n\t\t\t\tforeach (ushort otherSegmentId in outgoingSegmentIds) {\n\t\t\t\t\tnumVehicleMoving[otherSegmentId] = 0;\n\t\t\t\t\tnumVehicleGoing[otherSegmentId] = 0;\n\t\t\t\t}\n\t\t\t\tnumVehicleMoving[SegmentId] = 0;\n\t\t\t\tnumVehicleGoing[SegmentId] = 0;\n\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Traffic/README.md",
    "content": "# TM:PE -- /Traffic\nAuxiliary and traffic related data structures.\n## Classes\n- **ExtBuilding**: Extended building data. Used by Parking AI.\n- **ExtCitizenInstance**: Extended citizen instance data. Used by Parking AI. \n- **ExtVehicleType**: Flag enum. Allows for finer-grained vehicle types.\n- **PrioritySegment**: Data structure that holds priority signs.\n- **SegmentEnd**: Represents the traffic situation at a segment end (only instantiated if either a priority sign or a timed traffic light is present). Holds player-defined priority signs (Type), currently registered vehicles (FirstRegisteredVehicleId; linked list, see **VehicleState**) and allows to count waiting and flowing traffic at the segment end (GetVehicleMetricGoingToSegment)\n- **VehicleJunctionTransitState**: Enum. Allows to describe wheter a vehicle is currently approaching a junction (Approach), stopping in front of it (Stop), leaving it (Leave) or cannot pass the junction due to a traffic jam ahead (Blocked)\n- **VehicleState**: Stores custom information about a vehicle (e.g. its **ExtVehicleType**, its current junction transit state or total length)."
  },
  {
    "path": "TLM/TLM/Traffic/VehicleJunctionTransitState.cs",
    "content": "namespace TrafficManager.Traffic {\n\tpublic enum VehicleJunctionTransitState {\n\t\t/// <summary>\n\t\t/// Represents an unknown/ignored state\n\t\t/// </summary>\n\t\tNone,\n\t\t/// <summary>\n\t\t/// Vehicle is apparoaching at a junction\n\t\t/// </summary>\n\t\tApproach,\n\t\t/// <summary>\n\t\t/// Vehicle must stop at a junction\n\t\t/// </summary>\n\t\tStop,\n\t\t/// <summary>\n\t\t/// Vehicle is leaving the junction\n\t\t/// </summary>\n\t\tLeave,\n\t\t/// <summary>\n\t\t/// Vehicle may leave but is blocked due to traffic ahead\n\t\t/// </summary>\n\t\tBlocked\n\t}\n}"
  },
  {
    "path": "TLM/TLM/TrafficLight/Data/TrafficLightSimulation.cs",
    "content": "using System;\nusing ColossalFramework;\nusing TrafficManager.Geometry;\nusing System.Collections.Generic;\nusing TrafficManager.State;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Util;\nusing TrafficManager.Manager;\nusing CSUtil.Commons;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.TrafficLight.Impl;\n\nnamespace TrafficManager.TrafficLight.Data {\n\tpublic struct TrafficLightSimulation {\n\t\t/// <summary>\n\t\t/// Timed traffic light by node id\n\t\t/// </summary>\n\t\tpublic ITimedTrafficLights TimedLight {\n\t\t\tget; private set;\n\t\t}\n\n\t\tpublic ushort NodeId {\n\t\t\tget; private set;\n\t\t}\n\n\t\tpublic TrafficLightSimulationType Type {\n\t\t\tget; private set;\n\t\t}\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[TrafficLightSimulation\\n\" +\n\t\t\t\t\"\\t\" + $\"NodeId = {NodeId}\\n\" +\n\t\t\t\t\"\\t\" + $\"Type = {Type}\\n\" +\n\t\t\t\t\"\\t\" + $\"TimedLight = {TimedLight}\\n\" +\n\t\t\t\t\"TrafficLightSimulation]\";\n\t\t}\n\n\t\tpublic TrafficLightSimulation(ushort nodeId) {\n\t\t\t//Log._Debug($\"TrafficLightSimulation: Constructor called @ node {nodeId}\");\n\t\t\tthis.NodeId = nodeId;\n\t\t\tTimedLight = null;\n\t\t\tType = TrafficLightSimulationType.None;\n\t\t}\n\n\t\tpublic bool SetUpManualTrafficLight() {\n\t\t\tif (IsTimedLight()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tConstants.ServiceFactory.NetService.ProcessNode(NodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\tConstants.ManagerFactory.TrafficLightManager.AddTrafficLight(nId, ref node);\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tConstants.ManagerFactory.CustomSegmentLightsManager.AddNodeLights(NodeId);\n\t\t\tType = TrafficLightSimulationType.Manual;\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool DestroyManualTrafficLight() {\n\t\t\tif (IsTimedLight()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (! IsManualLight()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tType = TrafficLightSimulationType.None;\n\t\t\tConstants.ManagerFactory.CustomSegmentLightsManager.RemoveNodeLights(NodeId);\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool SetUpTimedTrafficLight(IList<ushort> nodeGroup) {\n\t\t\tif (IsManualLight()) {\n\t\t\t\tDestroyManualTrafficLight();\n\t\t\t}\n\n\t\t\tif (IsTimedLight()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tConstants.ServiceFactory.NetService.ProcessNode(NodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\tConstants.ManagerFactory.TrafficLightManager.AddTrafficLight(nId, ref node);\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tConstants.ManagerFactory.CustomSegmentLightsManager.AddNodeLights(NodeId);\n\t\t\tTimedLight = new TimedTrafficLights(NodeId, nodeGroup);\n\t\t\tType = TrafficLightSimulationType.Timed;\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool DestroyTimedTrafficLight() {\n\t\t\tif (! IsTimedLight()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tType = TrafficLightSimulationType.None;\n\t\t\tvar timedLight = TimedLight;\n\t\t\tTimedLight = null;\n\n\t\t\tif (timedLight != null) {\n\t\t\t\ttimedLight.Destroy();\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic void Destroy() {\n\t\t\tDestroyTimedTrafficLight();\n\t\t\tDestroyManualTrafficLight();\n\t\t}\n\n\t\tpublic bool IsTimedLight() {\n\t\t\treturn Type == TrafficLightSimulationType.Timed && TimedLight != null;\n\t\t}\n\n\t\tpublic bool IsManualLight() {\n\t\t\treturn Type == TrafficLightSimulationType.Manual;\n\t\t}\n\n\t\tpublic bool IsTimedLightRunning() {\n\t\t\treturn IsTimedLight() && TimedLight.IsStarted();\n\t\t}\n\n\t\tpublic bool IsSimulationRunning() {\n\t\t\treturn IsManualLight() || IsTimedLightRunning();\n\t\t}\n\n\t\tpublic bool HasSimulation() {\n\t\t\treturn IsManualLight() || IsTimedLight();\n\t\t}\n\n\t\tpublic void SimulationStep() {\n\t\t\tif (! HasSimulation()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (IsTimedLightRunning()) {\n\t\t\t\tTimedLight.SimulationStep();\n\t\t\t}\n\t\t}\n\n\t\tpublic void Update() {\n\t\t\tLog._Debug($\"TrafficLightSimulation.Update(): called for node {NodeId}\");\n\n\t\t\tif (IsTimedLight()) {\n\t\t\t\tTimedLight.OnGeometryUpdate();\n\t\t\t\tTimedLight.Housekeeping();\n\t\t\t}\n\t\t}\n\n\t\tpublic void Housekeeping() { // TODO improve & remove\n\t\t\tTimedLight?.Housekeeping(); // removes unused step lights\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficLight/FlowWaitCalcMode.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.TrafficLight {\n\tpublic enum FlowWaitCalcMode {\n\t\t/// <summary>\n\t\t/// traffic measurements are averaged\n\t\t/// </summary>\n\t\tMean,\n\t\t/// <summary>\n\t\t/// traffic measurements are summed up\n\t\t/// </summary>\n\t\tTotal\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficLight/ICustomSegmentLight.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.TrafficLight {\n\tpublic interface ICustomSegmentLight : ICloneable {\n\t\t// TODO documentation\n\t\tushort NodeId { get; }\n\t\tushort SegmentId { get; }\n\t\tbool StartNode { get; }\n\t\tshort ClockwiseIndex { get; }\n\t\tLightMode CurrentMode { get; set; }\n\t\tLightMode InternalCurrentMode { get; set; } // TODO should not be defined here\n\t\tRoadBaseAI.TrafficLightState LightLeft { get; }\n\t\tRoadBaseAI.TrafficLightState LightMain { get; }\n\t\tRoadBaseAI.TrafficLightState LightRight { get; }\n\n\t\tRoadBaseAI.TrafficLightState GetLightState(ArrowDirection dir);\n\t\tbool IsAnyGreen();\n\t\tbool IsGreen(ArrowDirection dir);\n\t\tbool IsAnyInTransition();\n\t\tbool IsInTransition(ArrowDirection dir);\n\t\tbool IsLeftGreen();\n\t\tbool IsMainGreen();\n\t\tbool IsRightGreen();\n\t\tbool IsLeftRed();\n\t\tbool IsMainRed();\n\t\tbool IsRightRed();\n\t\tbool IsRed(ArrowDirection dir);\n\t\tvoid UpdateVisuals();\n\t\tvoid MakeRed();\n\t\tvoid MakeRedOrGreen();\n\t\tvoid ToggleMode();\n\t\tvoid ChangeMainLight();\n\t\tvoid ChangeLeftLight();\n\t\tvoid ChangeRightLight();\n\t\tvoid SetStates(RoadBaseAI.TrafficLightState? mainLight, RoadBaseAI.TrafficLightState? leftLight, RoadBaseAI.TrafficLightState? rightLight, bool calcAutoPedLight = true);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficLight/ICustomSegmentLights.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Traffic;\n\nnamespace TrafficManager.TrafficLight {\n\tpublic interface ICustomSegmentLights : ICloneable, ISegmentEndId {\n\t\t// TODO documentation\n\t\tushort NodeId { get; }\n\t\tIDictionary<ExtVehicleType, ICustomSegmentLight> CustomLights { get; }\n\t\tRoadBaseAI.TrafficLightState AutoPedestrianLightState { get; set; } // TODO should not be writable\n\t\tbool InvalidPedestrianLight { get; set; } // TODO improve & remove\n\t\tRoadBaseAI.TrafficLightState? PedestrianLightState { get; set; }\n\t\tRoadBaseAI.TrafficLightState? InternalPedestrianLightState { get; }\n\t\tbool ManualPedestrianMode { get; set; }\n\t\tLinkedList<ExtVehicleType> VehicleTypes { get; } // TODO improve & remove\n\t\tExtVehicleType?[] VehicleTypeByLaneIndex { get; }\n\n\t\tvoid CalculateAutoPedestrianLightState(bool propagate = true);\n\t\tbool IsAnyGreen();\n\t\tbool IsAnyInTransition();\n\t\tbool IsAnyLeftGreen();\n\t\tbool IsAnyMainGreen();\n\t\tbool IsAnyRightGreen();\n\t\tbool IsAllLeftRed();\n\t\tbool IsAllMainRed();\n\t\tbool IsAllRightRed();\n\t\tvoid UpdateVisuals();\n\t\tuint LastChange();\n\t\tvoid MakeRed();\n\t\tvoid MakeRedOrGreen();\n\t\tvoid ChangeLightPedestrian();\n\t\tvoid SetLights(RoadBaseAI.TrafficLightState lightState);\n\t\tvoid SetLights(ICustomSegmentLights otherLights);\n\t\tICustomSegmentLight GetCustomLight(byte laneIndex);\n\t\tICustomSegmentLight GetCustomLight(ExtVehicleType vehicleType);\n\t\tbool Relocate(ushort segmentId, bool startNode, ICustomSegmentLightsManager lightsManager);\n\t\tICustomSegmentLights Clone(ICustomSegmentLightsManager newLightsManager, bool performHousekeeping = true);\n\t\tvoid Housekeeping(bool mayDelete, bool calculateAutoPedLight);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficLight/ITimedTrafficLights.cs",
    "content": "﻿using System.Collections.Generic;\nusing CSUtil.Commons;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.Traffic;\nusing TrafficManager.Util;\n\nnamespace TrafficManager.TrafficLight {\n\tpublic interface ITimedTrafficLights : IObserver<NodeGeometry> {\n\t\tIDictionary<ushort, IDictionary<ushort, ArrowDirection>> Directions { get; }\n\t\tushort NodeId { get; }\n\t\tushort MasterNodeId { get; set; } // TODO private set\n\t\tshort RotationOffset { get; }\n\t\tint CurrentStep { get; set; }\n\t\tbool TestMode { get; set; } // TODO private set\n\t\tIList<ushort> NodeGroup { get; set; } // TODO private set\n\n\t\tITimedTrafficLightsStep AddStep(int minTime, int maxTime, StepChangeMetric changeMetric, float waitFlowBalance, bool makeRed = false);\n\t\tlong CheckNextChange(ushort segmentId, bool startNode, ExtVehicleType vehicleType, int lightType);\n\t\tITimedTrafficLightsStep GetStep(int stepId);\n\t\tbool Housekeeping(); // TODO improve & remove\n\t\tbool IsMasterNode();\n\t\tbool IsStarted();\n\t\tbool IsInTestMode();\n\t\tvoid SetTestMode(bool testMode);\n\t\tvoid Destroy();\n\t\tITimedTrafficLights MasterLights();\n\t\tvoid MoveStep(int oldPos, int newPos);\n\t\tint NumSteps();\n\t\tvoid RemoveStep(int id);\n\t\tvoid ResetSteps();\n\t\tvoid RotateLeft();\n\t\tvoid RotateRight();\n\t\tvoid Join(ITimedTrafficLights otherTimedLight);\n\t\tvoid PasteSteps(ITimedTrafficLights sourceTimedLight);\n\t\tvoid ChangeLightMode(ushort segmentId, ExtVehicleType vehicleType, LightMode mode);\n\t\tvoid SetLights(bool noTransition = false);\n\t\tvoid SimulationStep();\n\t\tvoid SkipStep(bool setLights = true, int prevStepRefIndex = -1);\n\t\tvoid Start();\n\t\tvoid Start(int step);\n\t\tvoid Stop();\n\t\tvoid OnGeometryUpdate();\n\t\tvoid RemoveNodeFromGroup(ushort otherNodeId);\n\t}\n}"
  },
  {
    "path": "TLM/TLM/TrafficLight/ITimedTrafficLightsStep.cs",
    "content": "﻿using System.Collections.Generic;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.Manager;\nusing TrafficManager.Traffic;\n\nnamespace TrafficManager.TrafficLight {\n\tpublic interface ITimedTrafficLightsStep : ICustomSegmentLightsManager {\n\t\t// TODO documentation\n\t\tIDictionary<ushort, ICustomSegmentLights> CustomSegmentLights { get; }\n\t\tLinkedList<ICustomSegmentLights> InvalidSegmentLights { get; }\n\t\tint PreviousStepRefIndex { get; set; }\n\t\tint NextStepRefIndex { get; set; }\n\t\tint MinTime { get; set; }\n\t\tint MaxTime { get; set; }\n\t\tStepChangeMetric ChangeMetric { get; set; }\n\t\tfloat WaitFlowBalance { get; set; }\n\t\tfloat CurrentWait { get; }\n\t\tfloat CurrentFlow { get; }\n\n\t\tvoid CalcWaitFlow(bool countOnlyMovingIfGreen, int stepRefIndex, out float wait, out float flow);\n\t\tRoadBaseAI.TrafficLightState GetLightState(ushort segmentId, ExtVehicleType vehicleType, int lightType);\n\t\tfloat GetMetric(float flow, float wait);\n\t\tICustomSegmentLights GetSegmentLights(ushort segmentId);\n\t\tlong MaxTimeRemaining();\n\t\tlong MinTimeRemaining();\n\t\tbool IsInStartTransition();\n\t\tbool IsInEndTransition();\n\t\tbool IsEndTransitionDone();\n\t\tbool RelocateSegmentLights(ushort sourceSegmentId, ushort targetSegmentId);\n\t\tICustomSegmentLights RemoveSegmentLights(ushort segmentId);\n\t\tbool SetSegmentLights(ushort segmentId, ICustomSegmentLights lights);\n\t\tvoid SetStepDone();\n\t\tbool ShouldGoToNextStep(float flow, float wait, out float metric);\n\t\tvoid Start(int previousStepRefIndex = -1);\n\t\tbool StepDone(bool updateValues);\n\t\tstring ToString();\n\t\tvoid UpdateLights();\n\t\tvoid UpdateLiveLights();\n\t\tvoid UpdateLiveLights(bool noTransition);\n\t}\n}"
  },
  {
    "path": "TLM/TLM/TrafficLight/Impl/CustomSegment.cs",
    "content": "using System.Collections.Generic;\nusing TrafficManager.Geometry;\n\nnamespace TrafficManager.TrafficLight.Impl {\n    class CustomSegment {\n        public ICustomSegmentLights StartNodeLights;\n        public ICustomSegmentLights EndNodeLights;\n\n\t\tpublic override string ToString() {\n\t\t\treturn \"[CustomSegment \\n\" +\n\t\t\t\"\\t\" + $\"StartNodeLights: {StartNodeLights}\\n\" +\n\t\t\t\"\\t\" + $\"EndNodeLights: {EndNodeLights}\\n\" +\n\t\t\t\"CustomSegment]\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficLight/Impl/CustomSegmentLight.cs",
    "content": "﻿#define DEBUGVISUALSx\n\nusing System;\nusing System.Collections.Generic;\nusing ColossalFramework;\nusing TrafficManager.Geometry;\nusing UnityEngine;\nusing TrafficManager.Custom.AI;\nusing CSUtil.Commons;\nusing TrafficManager.State;\nusing TrafficManager.Geometry.Impl;\n\nnamespace TrafficManager.TrafficLight.Impl {\n\t/// <summary>\n\t/// Represents the traffic light (left, forward, right) at a specific segment end\n\t/// </summary>\n\tpublic class CustomSegmentLight : ICustomSegmentLight {\n\t\t[Obsolete]\n\t\tpublic ushort NodeId {\n\t\t\tget {\n\t\t\t\treturn lights.NodeId;\n\t\t\t}\n\t\t}\n\n\t\tpublic ushort SegmentId {\n\t\t\tget {\n\t\t\t\treturn lights.SegmentId;\n\t\t\t}\n\t\t}\n\n\t\tpublic bool StartNode {\n\t\t\tget {\n\t\t\t\treturn lights.StartNode;\n\t\t\t}\n\t\t}\n\n\t\tpublic short ClockwiseIndex {\n\t\t\tget {\n\t\t\t\treturn lights.ClockwiseIndex;\n\t\t\t}\n\t\t}\n\n\t\tpublic LightMode CurrentMode {\n\t\t\tget { return InternalCurrentMode; }\n\t\t\tset {\n\t\t\t\tif (InternalCurrentMode == value)\n\t\t\t\t\treturn;\n\n\t\t\t\tInternalCurrentMode = value;\n\t\t\t\tEnsureModeLights();\n\t\t\t}\n\t\t}\n\t\tpublic LightMode InternalCurrentMode { get; set; } = LightMode.Simple; // TODO should be private\n\n\t\tinternal RoadBaseAI.TrafficLightState leftLight;\n\t\tinternal RoadBaseAI.TrafficLightState mainLight;\n\t\tinternal RoadBaseAI.TrafficLightState rightLight;\n\n\t\tpublic RoadBaseAI.TrafficLightState LightLeft {\n\t\t\tget { return leftLight; }\n\t\t\t/*private set {\n\t\t\t\tif (leftLight == value)\n\t\t\t\t\treturn;\n\n\t\t\t\tleftLight = value;\n\t\t\t\tlights.OnChange();\n\t\t\t}*/\n\t\t}\n\n\t\tpublic RoadBaseAI.TrafficLightState LightMain {\n\t\t\tget { return mainLight; }\n\t\t\t/*private set {\n\t\t\t\tif (mainLight == value)\n\t\t\t\t\treturn;\n\n\t\t\t\tmainLight = value;\n\t\t\t\tlights.OnChange();\n\t\t\t}*/\n\t\t}\n\t\tpublic RoadBaseAI.TrafficLightState LightRight {\n\t\t\tget { return rightLight; }\n\t\t\t/*private set {\n\t\t\t\tif (rightLight == value)\n\t\t\t\t\treturn;\n\n\t\t\t\trightLight = value;\n\t\t\t\tlights.OnChange();\n\t\t\t}*/\n\t\t}\n\n\t\tCustomSegmentLights lights;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[CustomSegmentLight seg. {SegmentId} @ node {NodeId}\\n\" +\n\t\t\t\"\\t\" + $\"CurrentMode: {CurrentMode}\\n\" +\n\t\t\t\"\\t\" + $\"LightLeft: {LightLeft}\\n\" +\n\t\t\t\"\\t\" + $\"LightMain: {LightMain}\\n\" +\n\t\t\t\"\\t\" + $\"LightRight: {LightRight}\\n\" +\n\t\t\t\"CustomSegmentLight]\";\n\t\t}\n\n\t\tprivate void EnsureModeLights() {\n\t\t\tbool changed = false;\n\n\t\t\tswitch (InternalCurrentMode) {\n\t\t\t\tcase LightMode.Simple:\n\t\t\t\t\tif (leftLight != LightMain) {\n\t\t\t\t\t\tleftLight = LightMain;\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (rightLight != LightMain) {\n\t\t\t\t\t\trightLight = LightMain;\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase LightMode.SingleLeft:\n\t\t\t\t\tif (rightLight != LightMain) {\n\t\t\t\t\t\trightLight = LightMain;\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase LightMode.SingleRight:\n\t\t\t\t\tif (leftLight != LightMain) {\n\t\t\t\t\t\tleftLight = LightMain;\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (changed)\n\t\t\t\tlights.OnChange();\n\t\t}\n\n\t\tpublic CustomSegmentLight(CustomSegmentLights lights, RoadBaseAI.TrafficLightState mainLight) {\n\t\t\tthis.lights = lights;\n\n\t\t\tSetStates(mainLight, leftLight, rightLight);\n\t\t\tUpdateVisuals();\n\t\t}\n\n\t\tpublic CustomSegmentLight(CustomSegmentLights lights, RoadBaseAI.TrafficLightState mainLight, RoadBaseAI.TrafficLightState leftLight, RoadBaseAI.TrafficLightState rightLight/*, RoadBaseAI.TrafficLightState pedestrianLight*/) {\n\t\t\tthis.lights = lights;\n\n\t\t\tSetStates(mainLight, leftLight, rightLight);\n\n\t\t\tUpdateVisuals();\n\t\t}\n\n\t\tpublic void ToggleMode() {\n\t\t\tSegmentGeometry geometry = SegmentGeometry.Get(SegmentId);\n\n\t\t\tif (geometry == null) {\n\t\t\t\tLog.Error($\"CustomSegmentLight.ToggleMode: No geometry information available for segment {SegmentId}\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbool startNode = lights.StartNode;\n\t\t\tvar hasLeftSegment = geometry.HasOutgoingLeftSegment(startNode);\n\t\t\tvar hasForwardSegment = geometry.HasOutgoingStraightSegment(startNode);\n\t\t\tvar hasRightSegment = geometry.HasOutgoingRightSegment(startNode);\n\n#if DEBUG\n\t\t\tLog._Debug($\"ChangeMode. segment {SegmentId} @ node {NodeId}, hasOutgoingLeft={hasLeftSegment}, hasOutgoingStraight={hasForwardSegment}, hasOutgoingRight={hasRightSegment}\");\n#endif\n\n\t\t\tLightMode newMode = LightMode.Simple;\n\t\t\tif (CurrentMode == LightMode.Simple) {\n\t\t\t\tif (!hasLeftSegment) {\n\t\t\t\t\tnewMode = LightMode.SingleRight;\n\t\t\t\t} else {\n\t\t\t\t\tnewMode = LightMode.SingleLeft;\n\t\t\t\t}\n\t\t\t} else if (CurrentMode == LightMode.SingleLeft) {\n\t\t\t\tif (!hasForwardSegment || !hasRightSegment) {\n\t\t\t\t\tnewMode = LightMode.Simple;\n\t\t\t\t} else {\n\t\t\t\t\tnewMode = LightMode.SingleRight;\n\t\t\t\t}\n\t\t\t} else if (CurrentMode == LightMode.SingleRight) {\n\t\t\t\tif (!hasLeftSegment) {\n\t\t\t\t\tnewMode = LightMode.Simple;\n\t\t\t\t} else {\n\t\t\t\t\tnewMode = LightMode.All;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnewMode = LightMode.Simple;\n\t\t\t}\n\n\t\t\tCurrentMode = newMode;\n\t\t}\n\n\t\tpublic void ChangeMainLight() {\n\t\t\tvar invertedLight = LightMain == RoadBaseAI.TrafficLightState.Green\n\t\t\t\t? RoadBaseAI.TrafficLightState.Red\n\t\t\t\t: RoadBaseAI.TrafficLightState.Green;\n\n\t\t\tif (CurrentMode == LightMode.Simple) {\n\t\t\t\tSetStates(invertedLight, invertedLight, invertedLight);\n\t\t\t} else if (CurrentMode == LightMode.SingleLeft) {\n\t\t\t\tSetStates(invertedLight, null, invertedLight);\n\t\t\t} else if (CurrentMode == LightMode.SingleRight) {\n\t\t\t\tSetStates(invertedLight, invertedLight, null);\n\t\t\t} else {\n\t\t\t\t//LightMain = invertedLight;\n\t\t\t\tSetStates(invertedLight, null, null);\n\t\t\t}\n\n\t\t\tUpdateVisuals();\n\t\t}\n\n\t\tpublic void ChangeLeftLight() {\n\t\t\tvar invertedLight = LightLeft == RoadBaseAI.TrafficLightState.Green\n\t\t\t\t? RoadBaseAI.TrafficLightState.Red\n\t\t\t\t: RoadBaseAI.TrafficLightState.Green;\n\n\t\t\t//LightLeft = invertedLight;\n\t\t\tSetStates(null, invertedLight, null);\n\n\t\t\tUpdateVisuals();\n\t\t}\n\n\t\tpublic void ChangeRightLight() {\n\t\t\tvar invertedLight = LightRight == RoadBaseAI.TrafficLightState.Green\n\t\t\t\t? RoadBaseAI.TrafficLightState.Red\n\t\t\t\t: RoadBaseAI.TrafficLightState.Green;\n\n\t\t\t//LightRight = invertedLight;\n\t\t\tSetStates(null, null, invertedLight);\n\n\t\t\tUpdateVisuals();\n\t\t}\n\n\t\tpublic RoadBaseAI.TrafficLightState GetLightState(ArrowDirection dir) {\n\t\t\tswitch (dir) {\n\t\t\t\tcase ArrowDirection.Left:\n\t\t\t\t\treturn LightLeft;\n\t\t\t\tcase ArrowDirection.Forward:\n\t\t\t\tdefault:\n\t\t\t\t\treturn LightMain;\n\t\t\t\tcase ArrowDirection.Right:\n\t\t\t\t\treturn LightRight;\n\t\t\t\tcase ArrowDirection.Turn:\n\t\t\t\t\treturn Constants.ServiceFactory.SimulationService.LeftHandDrive ? LightRight : LightLeft;\n\t\t\t}\n\t\t}\n\n\t\tpublic bool IsGreen(ArrowDirection dir) {\n\t\t\treturn GetLightState(dir) == RoadBaseAI.TrafficLightState.Green;\n\t\t}\n\n\t\tpublic bool IsInTransition(ArrowDirection dir) {\n\t\t\tRoadBaseAI.TrafficLightState state = GetLightState(dir);\n\t\t\treturn state == RoadBaseAI.TrafficLightState.GreenToRed || state == RoadBaseAI.TrafficLightState.RedToGreen;\n\t\t}\n\n\t\tpublic bool IsRed(ArrowDirection dir) {\n\t\t\treturn GetLightState(dir) == RoadBaseAI.TrafficLightState.Red;\n\t\t}\n\n\t\tpublic bool IsAnyGreen() {\n\t\t\treturn LightMain == RoadBaseAI.TrafficLightState.Green ||\n\t\t\t\tLightLeft == RoadBaseAI.TrafficLightState.Green ||\n\t\t\t\tLightRight == RoadBaseAI.TrafficLightState.Green;\n\t\t}\n\n\t\tpublic bool IsAnyInTransition() {\n\t\t\treturn LightMain == RoadBaseAI.TrafficLightState.RedToGreen ||\n\t\t\t\tLightLeft == RoadBaseAI.TrafficLightState.RedToGreen ||\n\t\t\t\tLightRight == RoadBaseAI.TrafficLightState.RedToGreen ||\n\t\t\t\tLightMain == RoadBaseAI.TrafficLightState.GreenToRed ||\n\t\t\t\tLightLeft == RoadBaseAI.TrafficLightState.GreenToRed ||\n\t\t\t\tLightRight == RoadBaseAI.TrafficLightState.GreenToRed;\n\t\t}\n\n\t\tpublic bool IsLeftGreen() {\n\t\t\treturn LightLeft == RoadBaseAI.TrafficLightState.Green;\n\t\t}\n\n\t\tpublic bool IsMainGreen() {\n\t\t\treturn LightMain == RoadBaseAI.TrafficLightState.Green;\n\t\t}\n\n\t\tpublic bool IsRightGreen() {\n\t\t\treturn LightRight == RoadBaseAI.TrafficLightState.Green;\n\t\t}\n\n\t\tpublic bool IsLeftRed() {\n\t\t\treturn LightLeft == RoadBaseAI.TrafficLightState.Red;\n\t\t}\n\n\t\tpublic bool IsMainRed() {\n\t\t\treturn LightMain == RoadBaseAI.TrafficLightState.Red;\n\t\t}\n\n\t\tpublic bool IsRightRed() {\n\t\t\treturn LightRight == RoadBaseAI.TrafficLightState.Red;\n\t\t}\n\n\t\tpublic void UpdateVisuals() {\n\t\t\tvar instance = Singleton<NetManager>.instance;\n\n\t\t\tushort nodeId = lights.NodeId;\n\t\t\tuint currentFrameIndex = Singleton<SimulationManager>.instance.m_currentFrameIndex;\n\t\t\tuint num = (uint)(((int)nodeId << 8) / 32768);\n\n\t\t\tRoadBaseAI.TrafficLightState vehicleLightState;\n\t\t\tRoadBaseAI.TrafficLightState pedestrianLightState;\n\n\t\t\tRoadBaseAI.TrafficLightState mainLight = LightMain;\n\t\t\tRoadBaseAI.TrafficLightState leftLight = LightLeft;\n\t\t\tRoadBaseAI.TrafficLightState rightLight = LightRight;\n\n\t\t\tswitch (CurrentMode) {\n\t\t\t\tcase LightMode.Simple:\n\t\t\t\t\tleftLight = mainLight;\n\t\t\t\t\trightLight = mainLight;\n\t\t\t\t\tbreak;\n\t\t\t\tcase LightMode.SingleLeft:\n\t\t\t\t\trightLight = mainLight;\n\t\t\t\t\tbreak;\n\t\t\t\tcase LightMode.SingleRight:\n\t\t\t\t\tleftLight = mainLight;\n\t\t\t\t\tbreak;\n\t\t\t\tcase LightMode.All:\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tvehicleLightState = GetVisualLightState();\n\t\t\tpedestrianLightState = lights.PedestrianLightState == null ? RoadBaseAI.TrafficLightState.Red : (RoadBaseAI.TrafficLightState)lights.PedestrianLightState;\n\n#if DEBUGVISUALS\n\t\t\tLog._Debug($\"Setting visual traffic light state of node {NodeId}, seg. {SegmentId} to vehicleState={vehicleLightState} pedState={pedestrianLightState}\");\n#endif\n\n\t\t\tuint now = ((currentFrameIndex - num) >> 8) & 1;\n\t\t\tCustomRoadAI.OriginalSetTrafficLightState(true, nodeId, ref instance.m_segments.m_buffer[SegmentId], now << 8, vehicleLightState, pedestrianLightState, false, false);\n\t\t\tCustomRoadAI.OriginalSetTrafficLightState(true, nodeId, ref instance.m_segments.m_buffer[SegmentId], (1u - now) << 8, vehicleLightState, pedestrianLightState, false, false);\n\t\t}\n\n\t\tpublic RoadBaseAI.TrafficLightState GetVisualLightState() {\n\t\t\tRoadBaseAI.TrafficLightState vehicleLightState;\n\t\t\t// any green?\n\t\t\tif (LightMain == RoadBaseAI.TrafficLightState.Green ||\n\t\t\t\tLightLeft == RoadBaseAI.TrafficLightState.Green ||\n\t\t\t\tLightRight == RoadBaseAI.TrafficLightState.Green) {\n\t\t\t\tvehicleLightState = RoadBaseAI.TrafficLightState.Green;\n\t\t\t} else // all red?\n\t\t\tif (LightMain == RoadBaseAI.TrafficLightState.Red &&\n\t\t\t\tLightLeft == RoadBaseAI.TrafficLightState.Red &&\n\t\t\t\tLightRight == RoadBaseAI.TrafficLightState.Red) {\n\t\t\t\tvehicleLightState = RoadBaseAI.TrafficLightState.Red;\n\t\t\t} else // any red+yellow?\n\t\t\tif (LightMain == RoadBaseAI.TrafficLightState.RedToGreen ||\n\t\t\t\tLightLeft == RoadBaseAI.TrafficLightState.RedToGreen ||\n\t\t\t\tLightRight == RoadBaseAI.TrafficLightState.RedToGreen) {\n\t\t\t\tvehicleLightState = RoadBaseAI.TrafficLightState.RedToGreen;\n\t\t\t} else {\n\t\t\t\tvehicleLightState = RoadBaseAI.TrafficLightState.GreenToRed;\n\t\t\t}\n\n\t\t\treturn vehicleLightState;\n\t\t}\n\n\t\tprivate RoadBaseAI.TrafficLightState _checkPedestrianLight() {\n\t\t\tif (LightLeft == RoadBaseAI.TrafficLightState.Red && LightMain == RoadBaseAI.TrafficLightState.Red &&\n\t\t\t\tLightRight == RoadBaseAI.TrafficLightState.Red) {\n\t\t\t\treturn RoadBaseAI.TrafficLightState.Green;\n\t\t\t}\n\t\t\treturn RoadBaseAI.TrafficLightState.Red;\n\t\t}\n\n\t\tpublic object Clone() {\n\t\t\treturn MemberwiseClone();\n\t\t}\n\n\t\tpublic void MakeRedOrGreen() {\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId)\n\t\t\t\tLog._Debug($\"CustomSegmentLight.MakeRedOrGreen: called for segment {SegmentId} @ {NodeId}\");\n#endif\n\n\t\t\tRoadBaseAI.TrafficLightState mainState = RoadBaseAI.TrafficLightState.Green;\n\t\t\tRoadBaseAI.TrafficLightState leftState = RoadBaseAI.TrafficLightState.Green;\n\t\t\tRoadBaseAI.TrafficLightState rightState = RoadBaseAI.TrafficLightState.Green;\n\n\t\t\tif (LightLeft != RoadBaseAI.TrafficLightState.Green) {\n\t\t\t\tleftState = RoadBaseAI.TrafficLightState.Red;\n\t\t\t}\n\n\t\t\tif (LightMain != RoadBaseAI.TrafficLightState.Green) {\n\t\t\t\tmainState = RoadBaseAI.TrafficLightState.Red;\n\t\t\t}\n\n\t\t\tif (LightRight != RoadBaseAI.TrafficLightState.Green) {\n\t\t\t\trightState = RoadBaseAI.TrafficLightState.Red;\n\t\t\t}\n\n\t\t\tSetStates(mainState, leftState, rightState);\n\t\t}\n\n\t\tpublic void MakeRed() {\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId)\n\t\t\t\tLog._Debug($\"CustomSegmentLight.MakeRed: called for segment {SegmentId} @ {NodeId}\");\n#endif\n\n\t\t\tSetStates(RoadBaseAI.TrafficLightState.Red, RoadBaseAI.TrafficLightState.Red, RoadBaseAI.TrafficLightState.Red);\n\t\t}\n\n\t\tpublic void SetStates(RoadBaseAI.TrafficLightState? mainLight, RoadBaseAI.TrafficLightState? leftLight, RoadBaseAI.TrafficLightState? rightLight, bool calcAutoPedLight=true) {\n\t\t\tif ((mainLight == null || this.mainLight == mainLight) &&\n\t\t\t\t(leftLight == null || this.leftLight == leftLight) &&\n\t\t\t\t(rightLight == null || this.rightLight == rightLight))\n\t\t\t\treturn;\n\n\t\t\tif (mainLight != null)\n\t\t\t\tthis.mainLight = (RoadBaseAI.TrafficLightState)mainLight;\n\t\t\tif (leftLight != null)\n\t\t\t\tthis.leftLight = (RoadBaseAI.TrafficLightState)leftLight;\n\t\t\tif (rightLight != null)\n\t\t\t\tthis.rightLight = (RoadBaseAI.TrafficLightState)rightLight;\n\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId)\n\t\t\t\tLog._Debug($\"CustomSegmentLight.SetStates({mainLight}, {leftLight}, {rightLight}, {calcAutoPedLight}) for segment {SegmentId} @ {NodeId}: {this.mainLight} {this.leftLight} {this.rightLight}\");\n#endif\n\n\t\t\tlights.OnChange(calcAutoPedLight);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficLight/Impl/CustomSegmentLights.cs",
    "content": "﻿#define DEBUGGETx\n\nusing System;\nusing System.Collections.Generic;\nusing ColossalFramework;\nusing TrafficManager.Geometry;\nusing UnityEngine;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager;\nusing System.Linq;\nusing TrafficManager.Util;\nusing CSUtil.Commons;\nusing TrafficManager.State;\nusing TrafficManager.Geometry.Impl;\n\nnamespace TrafficManager.TrafficLight.Impl {\n\t/// <summary>\n\t/// Represents the set of custom traffic lights located at a node\n\t/// </summary>\n\tpublic class CustomSegmentLights : SegmentEndId, ICustomSegmentLights {\n\t\t//private static readonly ExtVehicleType[] SINGLE_LANE_VEHICLETYPES = new ExtVehicleType[] { ExtVehicleType.Tram, ExtVehicleType.Service, ExtVehicleType.CargoTruck, ExtVehicleType.RoadPublicTransport | ExtVehicleType.Service, ExtVehicleType.RailVehicle };\n\t\tpublic const ExtVehicleType DEFAULT_MAIN_VEHICLETYPE = ExtVehicleType.None;\n\n\t\t[Obsolete]\n\t\tpublic ushort NodeId {\n\t\t\tget {\n\t\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(SegmentId);\n\n\t\t\t\tif (segGeo == null) {\n\t\t\t\t\tLog.Info($\"CustomSegmentLights.NodeId: No geometry information available for segment {SegmentId}\");\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\tif (StartNode)\n\t\t\t\t\treturn segGeo.StartNodeId();\n\t\t\t\telse\n\t\t\t\t\treturn segGeo.EndNodeId();\n\t\t\t}\n\t\t}\n\n\t\tpublic short ClockwiseIndex {\n\t\t\tget {\n\t\t\t\treturn LightsManager.ClockwiseIndexOfSegmentEnd(this);\n\t\t\t}\n\t\t}\n\n\t\tpublic uint LastChangeFrame;\n\n\t\tpublic bool InvalidPedestrianLight { get; set; } = false; // TODO improve & remove\n\n\t\tpublic IDictionary<ExtVehicleType, ICustomSegmentLight> CustomLights {\n\t\t\tget; private set;\n\t\t} = new TinyDictionary<ExtVehicleType, ICustomSegmentLight>();\n\n\t\tpublic LinkedList<ExtVehicleType> VehicleTypes { // TODO replace collection\n\t\t\tget; private set;\n\t\t} = new LinkedList<ExtVehicleType>();\n\n\t\tpublic ExtVehicleType?[] VehicleTypeByLaneIndex {\n\t\t\tget; private set;\n\t\t} = new ExtVehicleType?[0];\n\n\t\t/// <summary>\n\t\t/// Vehicles types that have their own traffic light\n\t\t/// </summary>\n\t\tpublic ExtVehicleType SeparateVehicleTypes {\n\t\t\tget; private set;\n\t\t} = ExtVehicleType.None;\n\n\t\tpublic RoadBaseAI.TrafficLightState AutoPedestrianLightState { get; set; } = RoadBaseAI.TrafficLightState.Green; // TODO set should be private\n\n\t\tpublic RoadBaseAI.TrafficLightState? PedestrianLightState {\n\t\t\tget {\n\t\t\t\tif (InvalidPedestrianLight || InternalPedestrianLightState == null)\n\t\t\t\t\treturn RoadBaseAI.TrafficLightState.Green; // no pedestrian crossing at this point\n\n\t\t\t\tif (ManualPedestrianMode && InternalPedestrianLightState != null)\n\t\t\t\t\treturn (RoadBaseAI.TrafficLightState)InternalPedestrianLightState;\n\t\t\t\telse {\n\t\t\t\t\treturn AutoPedestrianLightState;\n\t\t\t\t}\n\t\t\t}\n\t\t\tset {\n\t\t\t\tif (InternalPedestrianLightState == null) {\n#if DEBUGHK\n\t\t\t\t\tLog._Debug($\"CustomSegmentLights: Refusing to change pedestrian light at segment {SegmentId}\");\n#endif\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t//Log._Debug($\"CustomSegmentLights: Setting pedestrian light at segment {segmentId}\");\n\t\t\t\tInternalPedestrianLightState = value;\n\t\t\t}\n\t\t}\n\n\t\tpublic bool ManualPedestrianMode {\n\t\t\tget { return manualPedestrianMode; }\n\t\t\tset {\n\t\t\t\tif (!manualPedestrianMode && value) {\n\t\t\t\t\tPedestrianLightState = AutoPedestrianLightState;\n\t\t\t\t}\n\t\t\t\tmanualPedestrianMode = value;\n\t\t\t}\n\t\t}\n\n\t\tprivate bool manualPedestrianMode = false;\n\n\t\tpublic RoadBaseAI.TrafficLightState? InternalPedestrianLightState { get; private set; } = null;\n\t\tprivate ExtVehicleType mainVehicleType = ExtVehicleType.None;\n\t\tprotected ICustomSegmentLight MainSegmentLight {\n\t\t\tget {\n\t\t\t\tICustomSegmentLight res = null;\n\t\t\t\tCustomLights.TryGetValue(mainVehicleType, out res);\n\t\t\t\treturn res;\n\t\t\t}\n\t\t}\n\n\t\tpublic ICustomSegmentLightsManager LightsManager {\n\t\t\tget {\n\t\t\t\treturn lightsManager;\n\t\t\t}\n\t\t\tset {\n\t\t\t\tlightsManager = value;\n\t\t\t\tOnChange();\n\t\t\t}\n\t\t}\n\t\tprivate ICustomSegmentLightsManager lightsManager;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[CustomSegmentLights {base.ToString()} @ node {NodeId}\\n\" +\n\t\t\t\"\\t\" + $\"LastChangeFrame: {LastChangeFrame}\\n\" +\n\t\t\t\"\\t\" + $\"InvalidPedestrianLight: {InvalidPedestrianLight}\\n\" +\n\t\t\t\"\\t\" + $\"CustomLights: {CustomLights}\\n\" +\n\t\t\t\"\\t\" + $\"VehicleTypes: {VehicleTypes.CollectionToString()}\\n\" +\n\t\t\t\"\\t\" + $\"VehicleTypeByLaneIndex: {VehicleTypeByLaneIndex.ArrayToString()}\\n\" +\n\t\t\t\"\\t\" + $\"SeparateVehicleTypes: {SeparateVehicleTypes}\\n\" +\n\t\t\t\"\\t\" + $\"AutoPedestrianLightState: {AutoPedestrianLightState}\\n\" +\n\t\t\t\"\\t\" + $\"PedestrianLightState: {PedestrianLightState}\\n\" +\n\t\t\t\"\\t\" + $\"ManualPedestrianMode: {ManualPedestrianMode}\\n\" +\n\t\t\t\"\\t\" + $\"manualPedestrianMode: {manualPedestrianMode}\\n\" +\n\t\t\t\"\\t\" + $\"InternalPedestrianLightState: {InternalPedestrianLightState}\\n\" +\n\t\t\t\"\\t\" + $\"MainSegmentLight: {MainSegmentLight}\\n\" +\n\t\t\t\"CustomSegmentLights]\";\n\t\t}\n\n\t\tpublic bool Relocate(ushort segmentId, bool startNode, ICustomSegmentLightsManager lightsManager) {\n\t\t\tif (Relocate(segmentId, startNode)) {\n\t\t\t\tthis.lightsManager = lightsManager;\n\t\t\t\tHousekeeping(true, true);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t[Obsolete]\n\t\tprotected CustomSegmentLights(ICustomSegmentLightsManager lightsManager, ushort nodeId, ushort segmentId, bool calculateAutoPedLight)\n\t\t\t: this(lightsManager, segmentId, nodeId == SegmentGeometry.Get(segmentId)?.StartNodeId(), calculateAutoPedLight) {\n\n\t\t}\n\n\t\tpublic CustomSegmentLights(ICustomSegmentLightsManager lightsManager, ushort segmentId, bool startNode, bool calculateAutoPedLight) : this(lightsManager, segmentId, startNode, calculateAutoPedLight, true) {\n\t\t\t\n\t\t}\n\n\t\tpublic CustomSegmentLights(ICustomSegmentLightsManager lightsManager, ushort segmentId, bool startNode, bool calculateAutoPedLight, bool performHousekeeping) : base(segmentId, startNode) {\n\t\t\tthis.lightsManager = lightsManager;\n\t\t\tif (performHousekeeping) {\n\t\t\t\tHousekeeping(false, calculateAutoPedLight);\n\t\t\t}\n\t\t}\n\n\t\tpublic bool IsAnyGreen() {\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\tif (e.Value.IsAnyGreen())\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic bool IsAnyInTransition() {\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\tif (e.Value.IsAnyInTransition())\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic bool IsAnyLeftGreen() {\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\tif (e.Value.IsLeftGreen())\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic bool IsAnyMainGreen() {\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\tif (e.Value.IsMainGreen())\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic bool IsAnyRightGreen() {\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\tif (e.Value.IsRightGreen())\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic bool IsAllLeftRed() {\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\tif (!e.Value.IsLeftRed())\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool IsAllMainRed() {\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\tif (!e.Value.IsMainRed())\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool IsAllRightRed() {\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\tif (!e.Value.IsRightRed())\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic void UpdateVisuals() {\n\t\t\tif (MainSegmentLight == null)\n\t\t\t\treturn;\n\n\t\t\tMainSegmentLight.UpdateVisuals();\n\t\t}\n\t\t\n\t\tpublic object Clone() {\n\t\t\treturn Clone(LightsManager, true);\n\t\t}\n\n\t\tpublic ICustomSegmentLights Clone(ICustomSegmentLightsManager newLightsManager, bool performHousekeeping=true) {\n\t\t\tCustomSegmentLights clone = new CustomSegmentLights(newLightsManager != null ? newLightsManager : LightsManager, SegmentId, StartNode, false, false);\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\tclone.CustomLights.Add(e.Key, (ICustomSegmentLight)e.Value.Clone());\n\t\t\t}\n\t\t\tclone.InternalPedestrianLightState = InternalPedestrianLightState;\n\t\t\tclone.manualPedestrianMode = manualPedestrianMode;\n\t\t\tclone.VehicleTypes = new LinkedList<ExtVehicleType>(VehicleTypes);\n\t\t\tclone.LastChangeFrame = LastChangeFrame;\n\t\t\tclone.mainVehicleType = mainVehicleType;\n\t\t\tclone.AutoPedestrianLightState = AutoPedestrianLightState;\n\t\t\tif (performHousekeeping) {\n\t\t\t\tclone.Housekeeping(false, false);\n\t\t\t}\n\t\t\treturn clone;\n\t\t}\n\n\t\tpublic ICustomSegmentLight GetCustomLight(byte laneIndex) {\n\t\t\tif (laneIndex >= VehicleTypeByLaneIndex.Length) {\n#if DEBUGGET\n\t\t\t\tLog._Debug($\"CustomSegmentLights.GetCustomLight({laneIndex}): No vehicle type found for lane index\");\n#endif\n\t\t\t\treturn MainSegmentLight;\n\t\t\t}\n\n\t\t\tExtVehicleType? vehicleType = VehicleTypeByLaneIndex[laneIndex];\n\n\t\t\tif (vehicleType == null) {\n#if DEBUGGET\n\t\t\t\tLog._Debug($\"CustomSegmentLights.GetCustomLight({laneIndex}): No vehicle type found for lane index: lane is invalid\");\n#endif\n\t\t\t\treturn MainSegmentLight;\n\t\t\t}\n\n#if DEBUGGET\n\t\t\tLog._Debug($\"CustomSegmentLights.GetCustomLight({laneIndex}): Vehicle type is {vehicleType}\");\n#endif\n\t\t\tICustomSegmentLight light;\n\t\t\tif (!CustomLights.TryGetValue((ExtVehicleType)vehicleType, out light)) {\n#if DEBUGGET\n\t\t\t\tLog._Debug($\"CustomSegmentLights.GetCustomLight({laneIndex}): No custom light found for vehicle type {vehicleType}\");\n#endif\n\t\t\t\treturn MainSegmentLight;\n\t\t\t}\n#if DEBUGGET\n\t\t\tLog._Debug($\"CustomSegmentLights.GetCustomLight({laneIndex}): Returning custom light for vehicle type {vehicleType}\");\n#endif\n\t\t\treturn light;\n\t\t}\n\n\t\tpublic ICustomSegmentLight GetCustomLight(ExtVehicleType vehicleType) {\n\t\t\tICustomSegmentLight ret = null;\n\t\t\tif (!CustomLights.TryGetValue(vehicleType, out ret)) {\n\t\t\t\tret = MainSegmentLight;\n\t\t\t}\n\n\t\t\treturn ret;\n\n\t\t\t/*if (vehicleType != ExtVehicleType.None)\n\t\t\t\tLog._Debug($\"No traffic light for vehicle type {vehicleType} defined at segment {segmentId}, node {nodeId}.\");*/\n\t\t}\n\n\t\tpublic void MakeRed() {\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\te.Value.MakeRed();\n\t\t\t}\n\t\t}\n\n\t\tpublic void MakeRedOrGreen() {\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\te.Value.MakeRedOrGreen();\n\t\t\t}\n\t\t}\n\n\t\tpublic void SetLights(RoadBaseAI.TrafficLightState lightState) {\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\te.Value.SetStates(lightState, lightState, lightState, false);\n\t\t\t}\n\t\t\tCalculateAutoPedestrianLightState();\n\t\t}\n\n\t\tpublic void SetLights(ICustomSegmentLights otherLights) {\n\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in otherLights.CustomLights) {\n\t\t\t\tICustomSegmentLight ourLight = null;\n\t\t\t\tif (!CustomLights.TryGetValue(e.Key, out ourLight)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tourLight.SetStates(e.Value.LightMain, e.Value.LightLeft, e.Value.LightRight, false);\n\t\t\t\t//ourLight.LightPedestrian = e.Value.LightPedestrian;\n\t\t\t}\n\t\t\tInternalPedestrianLightState = otherLights.InternalPedestrianLightState;\n\t\t\tmanualPedestrianMode = otherLights.ManualPedestrianMode;\n\t\t\tAutoPedestrianLightState = otherLights.AutoPedestrianLightState;\n\t\t}\n\n\t\tpublic void ChangeLightPedestrian() {\n\t\t\tif (PedestrianLightState != null) {\n\t\t\t\tvar invertedLight = PedestrianLightState == RoadBaseAI.TrafficLightState.Green\n\t\t\t\t\t? RoadBaseAI.TrafficLightState.Red\n\t\t\t\t\t: RoadBaseAI.TrafficLightState.Green;\n\n\t\t\t\tPedestrianLightState = invertedLight;\n\t\t\t\tUpdateVisuals();\n\t\t\t}\n\t\t}\n\n\t\tprivate static uint getCurrentFrame() {\n\t\t\treturn Singleton<SimulationManager>.instance.m_currentFrameIndex >> 6;\n\t\t}\n\n\t\tpublic uint LastChange() {\n\t\t\treturn getCurrentFrame() - LastChangeFrame;\n\t\t}\n\n\t\tpublic void OnChange(bool calculateAutoPedLight=true) {\n\t\t\tLastChangeFrame = getCurrentFrame();\n\n\t\t\tif (calculateAutoPedLight)\n\t\t\t\tCalculateAutoPedestrianLightState();\n\t\t}\n\n\t\tpublic void CalculateAutoPedestrianLightState(bool propagate=true) {\n#if DEBUGTTL\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId;\n#endif\n\n#if DEBUGTTL\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Calculating pedestrian light state of seg. {SegmentId} @ node {NodeId}\");\n#endif\n\n\t\t\tSegmentEndGeometry segmentEndGeometry = SegmentGeometry.Get(SegmentId)?.GetEnd(StartNode);\n\n\t\t\tif (segmentEndGeometry == null) {\n\t\t\t\tLog._Debug($\"Could not get SegmentEndGeometry for segment {SegmentId} @ {NodeId}.\");\n\t\t\t\tAutoPedestrianLightState = RoadBaseAI.TrafficLightState.Green;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tushort nodeId = segmentEndGeometry.NodeId();\n\t\t\tif (nodeId != NodeId) {\n\t\t\t\tLog.Warning($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Node id mismatch! segment end node is {nodeId} but we are node {NodeId}. segmentEndGeometry={segmentEndGeometry} this={this}\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (propagate) {\n\t\t\t\tforeach (ushort otherSegmentId in segmentEndGeometry.ConnectedSegments) {\n\t\t\t\t\tif (otherSegmentId == 0)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tICustomSegmentLights otherLights = LightsManager.GetSegmentLights(nodeId, otherSegmentId);\n\t\t\t\t\tif (otherLights == null) {\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Expected other (propagate) CustomSegmentLights at segment {otherSegmentId} @ {NodeId} but there was none. Original segment id: {SegmentId}\");\n#endif\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\totherLights.CalculateAutoPedestrianLightState(false);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (IsAnyGreen()) {\n#if DEBUGTTL\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Any green at seg. {SegmentId} @ {NodeId}\");\n#endif\n\t\t\t\tAutoPedestrianLightState = RoadBaseAI.TrafficLightState.Red;\n\t\t\t\treturn;\n\t\t\t}\n\n#if DEBUGTTL\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Querying incoming segments at seg. {SegmentId} @ {NodeId}\");\n#endif\n\t\t\tRoadBaseAI.TrafficLightState autoPedestrianLightState = RoadBaseAI.TrafficLightState.Green;\n\n\t\t\tItemClass prevConnectionClass = null;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(SegmentId, delegate (ushort prevSegId, ref NetSegment segment) {\n\t\t\t\tprevConnectionClass = segment.Info.GetConnectionClass();\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tif (!segmentEndGeometry.IncomingOneWay) {\n\t\t\t\t// query straight segments\n\t\t\t\tforeach (ushort otherSegmentId in segmentEndGeometry.IncomingStraightSegments) {\n\t\t\t\t\tif (otherSegmentId == 0)\n\t\t\t\t\t\tcontinue;\n#if DEBUGTTL\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Checking incoming straight segment {otherSegmentId} at seg. {SegmentId} @ {NodeId}\");\n#endif\n\n\t\t\t\t\tICustomSegmentLights otherLights = LightsManager.GetSegmentLights(nodeId, otherSegmentId);\n\t\t\t\t\tif (otherLights == null) {\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Expected other (straight) CustomSegmentLights at segment {otherSegmentId} @ {NodeId} but there was none. Original segment id: {SegmentId}\");\n#endif\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tSegmentGeometry otherGeo = SegmentGeometry.Get(otherSegmentId);\n\t\t\t\t\tif (otherGeo == null) {\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Expected other (straight) segment geometry at segment {otherSegmentId} @ {NodeId} (startNode={otherLights.StartNode}) but there was none. Original segment id: {SegmentId}\");\n#endif\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tItemClass nextConnectionClass = null;\n\t\t\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(otherSegmentId, delegate (ushort otherSegId, ref NetSegment segment) {\n\t\t\t\t\t\tnextConnectionClass = segment.Info.GetConnectionClass();\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\n\t\t\t\t\tif (nextConnectionClass.m_service != prevConnectionClass.m_service) {\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Other (straight) segment {otherSegmentId} @ {NodeId} has different connection service than segment {SegmentId} ({nextConnectionClass.m_service} vs. {prevConnectionClass.m_service}). Ignoring traffic light state.\");\n#endif\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!otherLights.IsAllMainRed()) {\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Not all main red at {otherSegmentId} at seg. {SegmentId} @ {NodeId}\");\n#endif\n\t\t\t\t\t\tautoPedestrianLightState = RoadBaseAI.TrafficLightState.Red;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// query left/right segments\n\t\t\t\tif (autoPedestrianLightState == RoadBaseAI.TrafficLightState.Green) {\n\t\t\t\t\tbool lhd = Constants.ServiceFactory.SimulationService.LeftHandDrive;\n\t\t\t\t\tforeach (ushort otherSegmentId in lhd ? segmentEndGeometry.IncomingLeftSegments : segmentEndGeometry.IncomingRightSegments) {\n\t\t\t\t\t\tif (otherSegmentId == 0)\n\t\t\t\t\t\t\tcontinue;\n\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Checking left/right segment {otherSegmentId} at seg. {SegmentId} @ {NodeId}\");\n#endif\n\n\t\t\t\t\t\tICustomSegmentLights otherLights = LightsManager.GetSegmentLights(nodeId, otherSegmentId);\n\t\t\t\t\t\tif (otherLights == null) {\n#if DEBUGTTL\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Expected other (left/right) CustomSegmentLights at segment {otherSegmentId} @ {NodeId} but there was none. Original segment id: {SegmentId}\");\n#endif\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tItemClass nextConnectionClass = null;\n\t\t\t\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(otherSegmentId, delegate (ushort otherSegId, ref NetSegment segment) {\n\t\t\t\t\t\t\tnextConnectionClass = segment.Info.GetConnectionClass();\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (nextConnectionClass.m_service != prevConnectionClass.m_service) {\n#if DEBUGTTL\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Other (left/right) segment {otherSegmentId} @ {NodeId} has different connection service than segment {SegmentId} ({nextConnectionClass.m_service} vs. {prevConnectionClass.m_service}). Ignoring traffic light state.\");\n#endif\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((lhd && !otherLights.IsAllRightRed()) || (!lhd && !otherLights.IsAllLeftRed())) {\n#if DEBUGTTL\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Not all left red at {otherSegmentId} at seg. {SegmentId} @ {NodeId}\");\n#endif\n\t\t\t\t\t\t\tautoPedestrianLightState = RoadBaseAI.TrafficLightState.Red;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tAutoPedestrianLightState = autoPedestrianLightState;\n#if DEBUGTTL\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"CustomSegmentLights.CalculateAutoPedestrianLightState: Calculated AutoPedestrianLightState for segment {SegmentId} @ {NodeId}: {AutoPedestrianLightState}\");\n#endif\n\t\t}\n\n\t\t// TODO improve & remove\n\t\tpublic void Housekeeping(bool mayDelete, bool calculateAutoPedLight) {\n#if DEBUGHK\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId;\n#endif\n\n\t\t\t// we intentionally never delete vehicle types (because we may want to retain traffic light states if a segment is upgraded or replaced)\n\n\t\t\tICustomSegmentLight mainLight = MainSegmentLight;\n\t\t\tushort nodeId = NodeId;\n\t\t\tHashSet<ExtVehicleType> setupLights = new HashSet<ExtVehicleType>();\n\t\t\tIDictionary<byte, ExtVehicleType> allAllowedTypes = Constants.ManagerFactory.VehicleRestrictionsManager.GetAllowedVehicleTypesAsDict(SegmentId, nodeId, VehicleRestrictionsMode.Restricted); // TODO improve\n\t\t\tExtVehicleType allAllowedMask = Constants.ManagerFactory.VehicleRestrictionsManager.GetAllowedVehicleTypes(SegmentId, nodeId, VehicleRestrictionsMode.Restricted);\n\t\t\tSeparateVehicleTypes = ExtVehicleType.None;\n#if DEBUGHK\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping started @ seg. {SegmentId}, node {nodeId}, allAllowedTypes={allAllowedTypes.DictionaryToString()}, allAllowedMask={allAllowedMask}\");\n#endif\n\t\t\t//bool addPedestrianLight = false;\n\t\t\tuint separateLanes = 0;\n\t\t\tint defaultLanes = 0;\n\t\t\tNetInfo segmentInfo = null;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(SegmentId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\tVehicleTypeByLaneIndex = new ExtVehicleType?[segment.Info.m_lanes.Length];\n\t\t\t\tsegmentInfo = segment.Info;\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\tHashSet<byte> laneIndicesWithoutSeparateLights = new HashSet<byte>(allAllowedTypes.Keys); // TODO improve\n\n\t\t\t// check if separate traffic lights are required\n\t\t\tbool separateLightsRequired = false;\n\t\t\tforeach (KeyValuePair<byte, ExtVehicleType> e in allAllowedTypes) {\n\t\t\t\tif (e.Value != allAllowedMask) {\n\t\t\t\t\tseparateLightsRequired = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// set up vehicle-separated traffic lights\n\t\t\tif (separateLightsRequired) {\n\t\t\t\tforeach (KeyValuePair<byte, ExtVehicleType> e in allAllowedTypes) {\n\t\t\t\t\tbyte laneIndex = e.Key;\n\t\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\t\tExtVehicleType allowedTypes = e.Value;\n\t\t\t\t\tExtVehicleType defaultMask = Constants.ManagerFactory.VehicleRestrictionsManager.GetDefaultAllowedVehicleTypes(SegmentId, segmentInfo, laneIndex, laneInfo, VehicleRestrictionsMode.Unrestricted);\n\n#if DEBUGHK\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Processing lane {laneIndex} with allowedTypes={allowedTypes}, defaultMask={defaultMask}\");\n#endif\n\n\t\t\t\t\tif (laneInfo.m_vehicleType == VehicleInfo.VehicleType.Car && allowedTypes == defaultMask) {\n#if DEBUGHK\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}, lane {laneIndex}: Allowed types equal default mask. Ignoring lane.\");\n#endif\n\t\t\t\t\t\t// no vehicle restrictions applied, generic lights are handled further below\n\t\t\t\t\t\t++defaultLanes;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tExtVehicleType mask = allowedTypes & ~ExtVehicleType.Emergency;\n\n#if DEBUGHK\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}, lane {laneIndex}: Trying to add {mask} light\");\n#endif\n\n\t\t\t\t\tICustomSegmentLight segmentLight;\n\t\t\t\t\tif (!CustomLights.TryGetValue(mask, out segmentLight)) {\n\t\t\t\t\t\t// add a new light\n\t\t\t\t\t\tsegmentLight = new CustomSegmentLight(this, RoadBaseAI.TrafficLightState.Red);\n\t\t\t\t\t\tif (mainLight != null) {\n\t\t\t\t\t\t\tsegmentLight.CurrentMode = mainLight.CurrentMode;\n\t\t\t\t\t\t\tsegmentLight.SetStates(mainLight.LightMain, mainLight.LightLeft, mainLight.LightRight, false);\n\t\t\t\t\t\t}\n\n#if DEBUGHK\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}, lane {laneIndex}: Light for mask {mask} does not exist. Created new light: {segmentLight} (mainLight: {mainLight})\");\n#endif\n\n\t\t\t\t\t\tCustomLights.Add(mask, segmentLight);\n\t\t\t\t\t\tVehicleTypes.AddFirst(mask);\n\t\t\t\t\t}\n\n\t\t\t\t\tmainVehicleType = mask;\n\t\t\t\t\tVehicleTypeByLaneIndex[laneIndex] = mask;\n\t\t\t\t\tlaneIndicesWithoutSeparateLights.Remove(laneIndex);\n\t\t\t\t\t++separateLanes;\n\t\t\t\t\t//addPedestrianLight = true;\n\t\t\t\t\tsetupLights.Add(mask);\n\t\t\t\t\tSeparateVehicleTypes |= mask;\n\n#if DEBUGHK\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Finished processing lane {laneIndex}: mainVehicleType={mainVehicleType}, VehicleTypeByLaneIndex={VehicleTypeByLaneIndex.ArrayToString()}, laneIndicesWithoutSeparateLights={laneIndicesWithoutSeparateLights.CollectionToString()}, numLights={separateLanes}, SeparateVehicleTypes={SeparateVehicleTypes}\");\n#endif\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (separateLanes == 0 || defaultLanes > 0) {\n#if DEBUGHK\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Adding default main vehicle light: {DEFAULT_MAIN_VEHICLETYPE}\");\n#endif\n\n\t\t\t\t// generic traffic lights\n\t\t\t\tICustomSegmentLight defaultSegmentLight;\n\t\t\t\tif (!CustomLights.TryGetValue(DEFAULT_MAIN_VEHICLETYPE, out defaultSegmentLight)) {\n\t\t\t\t\tdefaultSegmentLight = new CustomSegmentLight(this, RoadBaseAI.TrafficLightState.Red);\n\t\t\t\t\tif (mainLight != null) {\n\t\t\t\t\t\tdefaultSegmentLight.CurrentMode = mainLight.CurrentMode;\n\t\t\t\t\t\tdefaultSegmentLight.SetStates(mainLight.LightMain, mainLight.LightLeft, mainLight.LightRight, false);\n\t\t\t\t\t}\n\t\t\t\t\tCustomLights.Add(DEFAULT_MAIN_VEHICLETYPE, defaultSegmentLight);\n\t\t\t\t\tVehicleTypes.AddFirst(DEFAULT_MAIN_VEHICLETYPE);\n\t\t\t\t}\n\t\t\t\tmainVehicleType = DEFAULT_MAIN_VEHICLETYPE;\n\t\t\t\tsetupLights.Add(DEFAULT_MAIN_VEHICLETYPE);\n\n\t\t\t\tforeach (byte laneIndex in laneIndicesWithoutSeparateLights) {\n\t\t\t\t\tVehicleTypeByLaneIndex[laneIndex] = ExtVehicleType.None;\n\t\t\t\t}\n\n#if DEBUGHK\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Added default main vehicle light: {defaultSegmentLight}\");\n#endif\n\t\t\t\t//addPedestrianLight = true;\n\t\t\t} else {\n\t\t\t\t//addPedestrianLight = allAllowedMask == ExtVehicleType.None || (allAllowedMask & ~ExtVehicleType.RailVehicle) != ExtVehicleType.None;\n\t\t\t}\n\n#if DEBUGHK\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Created all necessary lights. VehicleTypeByLaneIndex={VehicleTypeByLaneIndex.ArrayToString()}, CustomLights={CustomLights.DictionaryToString()}\");\n#endif\n\n\t\t\tif (mayDelete) {\n\t\t\t\t// delete traffic lights for non-existing vehicle-separated configurations\n\t\t\t\tHashSet<ExtVehicleType> vehicleTypesToDelete = new HashSet<ExtVehicleType>();\n\t\t\t\tforeach (KeyValuePair<ExtVehicleType, ICustomSegmentLight> e in CustomLights) {\n\t\t\t\t\t/*if (e.Key == DEFAULT_MAIN_VEHICLETYPE) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}*/\n\t\t\t\t\tif (!setupLights.Contains(e.Key)) {\n\t\t\t\t\t\tvehicleTypesToDelete.Add(e.Key);\n\t\t\t\t\t}\n\t\t\t\t}\n\n#if DEBUGHK\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Going to delete unnecessary lights now: vehicleTypesToDelete={vehicleTypesToDelete.CollectionToString()}\");\n#endif\n\n\t\t\t\tforeach (ExtVehicleType vehicleType in vehicleTypesToDelete) {\n\t\t\t\t\tCustomLights.Remove(vehicleType);\n\t\t\t\t\tVehicleTypes.Remove(vehicleType);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (CustomLights.ContainsKey(DEFAULT_MAIN_VEHICLETYPE) && VehicleTypes.First.Value != DEFAULT_MAIN_VEHICLETYPE) {\n\t\t\t\tVehicleTypes.Remove(DEFAULT_MAIN_VEHICLETYPE);\n\t\t\t\tVehicleTypes.AddFirst(DEFAULT_MAIN_VEHICLETYPE);\n\t\t\t}\n\n\t\t\t//if (addPedestrianLight) {\n#if DEBUGHK\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: adding pedestrian light\");\n#endif\n\t\t\t\tif (InternalPedestrianLightState == null) {\n\t\t\t\t\tInternalPedestrianLightState = RoadBaseAI.TrafficLightState.Red;\n\t\t\t\t}\n\t\t\t/*} else {\n\t\t\t\tInternalPedestrianLightState = null;\n\t\t\t}*/\n\n\t\t\tOnChange(calculateAutoPedLight);\n#if DEBUGHK\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): housekeeping @ seg. {SegmentId}, node {nodeId}: Housekeeping complete. VehicleTypeByLaneIndex={VehicleTypeByLaneIndex.ArrayToString()} CustomLights={CustomLights.DictionaryToString()}\");\n#endif\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficLight/Impl/TimedTrafficLights.cs",
    "content": "#define DEBUGTTLx\n\nusing System;\nusing System.Collections.Generic;\nusing ColossalFramework;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Geometry;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager;\nusing System.Linq;\nusing TrafficManager.Util;\nusing System.Threading;\nusing TrafficManager.State;\nusing GenericGameBridge.Service;\nusing CSUtil.Commons;\nusing TrafficManager.Geometry.Impl;\nusing CSUtil.Commons.Benchmark;\nusing TrafficManager.Manager.Impl;\n\nnamespace TrafficManager.TrafficLight.Impl {\n\t// TODO define TimedTrafficLights per node group, not per individual nodes\n\tpublic class TimedTrafficLights : ITimedTrafficLights {\n\t\tpublic ushort NodeId {\n\t\t\tget; private set;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// In case the traffic light is set for a group of nodes, the master node decides\n\t\t/// if all member steps are done.\n\t\t/// </summary>\n\t\tpublic ushort MasterNodeId {\n\t\t\tget; set; // TODO private set\n\t\t}\n\n\t\tpublic List<TimedTrafficLightsStep> Steps = new List<TimedTrafficLightsStep>();\n\t\tpublic int CurrentStep { get; set; } = 0;\n\n\t\tpublic IList<ushort> NodeGroup { get; set; } // TODO private set\n\t\tpublic bool TestMode { get; set; } = false; // TODO private set\n\n\t\tprivate bool started = false;\n\n\t\t/// <summary>\n\t\t/// Indicates the total amount and direction of rotation that was applied to this timed traffic light\n\t\t/// </summary>\n\t\tpublic short RotationOffset { get; private set; } = 0;\n\n\t\tpublic IDictionary<ushort, IDictionary<ushort, ArrowDirection>> Directions { get; private set; } = null;\n\n\t\t/// <summary>\n\t\t/// Segment ends that were set up for this timed traffic light\n\t\t/// </summary>\n\t\tprivate ICollection<ISegmentEndId> segmentEndIds = new HashSet<ISegmentEndId>();\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[TimedTrafficLights\\n\" +\n\t\t\t\t\"\\t\" + $\"NodeId = {NodeId}\\n\" +\n\t\t\t\t\"\\t\" + $\"masterNodeId = {MasterNodeId}\\n\" +\n\t\t\t\t\"\\t\" + $\"Steps = {Steps.CollectionToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"NodeGroup = {NodeGroup.CollectionToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"testMode = {TestMode}\\n\" +\n\t\t\t\t\"\\t\" + $\"started = {started}\\n\" +\n\t\t\t\t\"\\t\" + $\"Directions = {Directions.DictionaryToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"segmentEndIds = {segmentEndIds.CollectionToString()}\\n\" +\n\t\t\t\t\"TimedTrafficLights]\";\n\t\t}\n\n\t\tpublic TimedTrafficLights(ushort nodeId, IEnumerable<ushort> nodeGroup) {\n\t\t\tthis.NodeId = nodeId;\n\t\t\tNodeGroup = new List<ushort>(nodeGroup);\n\t\t\tMasterNodeId = NodeGroup[0];\n\n\t\t\tUpdateDirections(NodeGeometry.Get(nodeId));\n\t\t\tUpdateSegmentEnds();\n\n\t\t\tstarted = false;\n\t\t}\n\n\t\tprivate TimedTrafficLights() {\n\n\t\t}\n\n\t\tpublic void PasteSteps(ITimedTrafficLights sourceTimedLight) {\n\t\t\tStop();\n\t\t\tSteps.Clear();\n\t\t\tRotationOffset = 0;\n\n\t\t\tIList<ushort> clockSortedSourceSegmentIds = new List<ushort>();\n\t\t\tConstants.ServiceFactory.NetService.IterateNodeSegments(sourceTimedLight.NodeId, ClockDirection.Clockwise, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\tclockSortedSourceSegmentIds.Add(segmentId);\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tIList<ushort> clockSortedTargetSegmentIds = new List<ushort>();\n\t\t\tConstants.ServiceFactory.NetService.IterateNodeSegments(NodeId, ClockDirection.Clockwise, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\tclockSortedTargetSegmentIds.Add(segmentId);\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tif (clockSortedTargetSegmentIds.Count != clockSortedSourceSegmentIds.Count) {\n\t\t\t\tthrow new Exception($\"TimedTrafficLights.PasteLight: Segment count mismatch -- source node {sourceTimedLight.NodeId}: {clockSortedSourceSegmentIds.CollectionToString()} vs. target node {NodeId}: {clockSortedTargetSegmentIds.CollectionToString()}\");\n\t\t\t}\n\n\t\t\tfor (int stepIndex = 0; stepIndex < sourceTimedLight.NumSteps(); ++stepIndex) {\n\t\t\t\tITimedTrafficLightsStep sourceStep = sourceTimedLight.GetStep(stepIndex);\n\t\t\t\tTimedTrafficLightsStep targetStep = new TimedTrafficLightsStep(this, sourceStep.MinTime, sourceStep.MaxTime, sourceStep.ChangeMetric, sourceStep.WaitFlowBalance);\n\t\t\t\tfor (int i = 0; i < clockSortedSourceSegmentIds.Count; ++i) {\n\t\t\t\t\tushort sourceSegmentId = clockSortedSourceSegmentIds[i];\n\t\t\t\t\tushort targetSegmentId = clockSortedTargetSegmentIds[i];\n\n\t\t\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(targetSegmentId);\n\t\t\t\t\tif (segGeo == null) {\n\t\t\t\t\t\tthrow new Exception($\"TimedTrafficLights.PasteSteps: No geometry information available for segment {targetSegmentId}\");\n\t\t\t\t\t}\n\n\t\t\t\t\tbool targetStartNode = segGeo.StartNodeId() == NodeId;\n\n\t\t\t\t\tICustomSegmentLights sourceLights = sourceStep.CustomSegmentLights[sourceSegmentId];\n\t\t\t\t\tICustomSegmentLights targetLights = sourceLights.Clone(targetStep, false);\n\t\t\t\t\ttargetStep.SetSegmentLights(targetSegmentId, targetLights);\n\t\t\t\t\tConstants.ManagerFactory.CustomSegmentLightsManager.ApplyLightModes(targetSegmentId, targetStartNode, targetLights);\n\t\t\t\t}\n\t\t\t\tSteps.Add(targetStep);\n\t\t\t}\n\n\t\t\tif (sourceTimedLight.IsStarted()) {\n\t\t\t\tStart();\n\t\t\t}\n\t\t}\n\n\t\tprivate object rotateLock = new object();\n\n\t\tprivate void Rotate(ArrowDirection dir) {\n\t\t\tif (! IsMasterNode() || NodeGroup.Count != 1 || Steps.Count <= 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tStop();\n\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(rotateLock);\n\n\t\t\t\tLog._Debug($\"TimedTrafficLights.Rotate({dir}) @ node {NodeId}: Rotating timed traffic light.\");\n\n\t\t\t\tif (dir != ArrowDirection.Left && dir != ArrowDirection.Right) {\n\t\t\t\t\tthrow new NotSupportedException();\n\t\t\t\t}\n\n\t\t\t\tIList<ushort> clockSortedSegmentIds = new List<ushort>();\n\t\t\t\tConstants.ServiceFactory.NetService.IterateNodeSegments(NodeId, dir == ArrowDirection.Right ? ClockDirection.Clockwise : ClockDirection.CounterClockwise, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\t\tclockSortedSegmentIds.Add(segmentId);\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\n\t\t\t\tLog._Debug($\"TimedTrafficLights.Rotate({dir}) @ node {NodeId}: Clock-sorted segment ids: {clockSortedSegmentIds.CollectionToString()}\");\n\n\t\t\t\tif (clockSortedSegmentIds.Count <= 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tint stepIndex = -1;\n\t\t\t\tforeach (TimedTrafficLightsStep step in Steps) {\n\t\t\t\t\t++stepIndex;\n\t\t\t\t\tICustomSegmentLights bufferedLights = null;\n\t\t\t\t\tfor (int sourceIndex = 0; sourceIndex < clockSortedSegmentIds.Count; ++sourceIndex) {\n\t\t\t\t\t\tushort sourceSegmentId = clockSortedSegmentIds[sourceIndex];\n\t\t\t\t\t\tint targetIndex = (sourceIndex + 1) % clockSortedSegmentIds.Count;\n\t\t\t\t\t\tushort targetSegmentId = clockSortedSegmentIds[targetIndex];\n\n\t\t\t\t\t\tSegmentGeometry targetSegGeo = SegmentGeometry.Get(targetSegmentId); // should never fail here\n\n\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.Rotate({dir}) @ node {NodeId}: Moving light @ seg. {sourceSegmentId} to seg. {targetSegmentId} @ step {stepIndex}\");\n\n\t\t\t\t\t\tICustomSegmentLights sourceLights = sourceIndex == 0 ? step.RemoveSegmentLights(sourceSegmentId) : bufferedLights;\n\t\t\t\t\t\tif (sourceLights == null) {\n\t\t\t\t\t\t\tthrow new Exception($\"TimedTrafficLights.Rotate({dir}): Error occurred while copying custom lights from {sourceSegmentId} to {targetSegmentId} @ step {stepIndex}: sourceLights is null @ sourceIndex={sourceIndex}, targetIndex={targetIndex}\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbufferedLights = step.RemoveSegmentLights(targetSegmentId);\n\t\t\t\t\t\tsourceLights.Relocate(targetSegmentId, targetSegGeo.StartNodeId() == NodeId);\n\t\t\t\t\t\tif (!step.SetSegmentLights(targetSegmentId, sourceLights)) {\n\t\t\t\t\t\t\tthrow new Exception($\"TimedTrafficLights.Rotate({dir}): Error occurred while copying custom lights from {sourceSegmentId} to {targetSegmentId} @ step {stepIndex}: could not set lights for target segment @ sourceIndex={sourceIndex}, targetIndex={targetIndex}\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tswitch (dir) {\n\t\t\t\t\tcase ArrowDirection.Left:\n\t\t\t\t\t\tRotationOffset = (short)((RotationOffset + clockSortedSegmentIds.Count - 1) % clockSortedSegmentIds.Count);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ArrowDirection.Right:\n\t\t\t\t\t\tRotationOffset = (short)((RotationOffset + 1) % clockSortedSegmentIds.Count);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tCurrentStep = 0;\n\t\t\t\tSetLights(true);\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(rotateLock);\n\t\t\t}\n\t\t}\n\n\t\tpublic void RotateLeft() {\n\t\t\tRotate(ArrowDirection.Left);\n\t\t}\n\n\t\tpublic void RotateRight() {\n\t\t\tRotate(ArrowDirection.Right);\n\t\t}\n\n\t\tprivate void UpdateDirections(NodeGeometry nodeGeo) {\n\t\t\tLog._Debug($\">>>>> TimedTrafficLights.UpdateDirections: called for node {NodeId}\");\n\t\t\tDirections = new TinyDictionary<ushort, IDictionary<ushort, ArrowDirection>>();\n\t\t\tforeach (SegmentEndGeometry srcSegEndGeo in nodeGeo.SegmentEndGeometries) {\n\t\t\t\tif (srcSegEndGeo == null)\n\t\t\t\t\tcontinue;\n\t\t\t\tLog._Debug($\"TimedTrafficLights.UpdateDirections: Processing source segment {srcSegEndGeo.SegmentId}\");\n\n\t\t\t\tSegmentGeometry srcSegGeo = srcSegEndGeo.GetSegmentGeometry();\n\t\t\t\tif (srcSegGeo == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tIDictionary<ushort, ArrowDirection> dirs = new TinyDictionary<ushort, ArrowDirection>();\n\t\t\t\tDirections.Add(srcSegEndGeo.SegmentId, dirs);\n\t\t\t\tforeach (SegmentEndGeometry trgSegEndGeo in nodeGeo.SegmentEndGeometries) {\n\t\t\t\t\tif (trgSegEndGeo == null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tArrowDirection dir = srcSegGeo.GetDirection(trgSegEndGeo.SegmentId, srcSegEndGeo.StartNode);\n\t\t\t\t\tif (dir == ArrowDirection.None) {\n\t\t\t\t\t\tLog.Error($\"TimedTrafficLights.UpdateDirections: Processing source segment {srcSegEndGeo.SegmentId}, target segment {trgSegEndGeo.SegmentId}: Invalid direction {dir}\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tdirs.Add(trgSegEndGeo.SegmentId, dir);\n\t\t\t\t\tLog._Debug($\"TimedTrafficLights.UpdateDirections: Processing source segment {srcSegEndGeo.SegmentId}, target segment {trgSegEndGeo.SegmentId}: adding dir {dir}\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tLog._Debug($\"<<<<< TimedTrafficLights.UpdateDirections: finished for node {NodeId}: {Directions.DictionaryToString()}\");\n\t\t}\n\n\t\tpublic void OnUpdate(NodeGeometry nodeGeo) {\n\t\t\t// not required since TrafficLightSimulation handles this for us: OnGeometryUpdate() is being called.\n\t\t\t// TODO improve\n\t\t}\n\n\t\tpublic bool IsMasterNode() {\n\t\t\treturn MasterNodeId == NodeId;\n\t\t}\n\n\t\tpublic ITimedTrafficLightsStep AddStep(int minTime, int maxTime, StepChangeMetric changeMetric, float waitFlowBalance, bool makeRed = false) {\n\t\t\t// TODO currently, this method must be called for each node in the node group individually\n\n\t\t\tif (minTime < 0)\n\t\t\t\tminTime = 0;\n\t\t\tif (maxTime <= 0)\n\t\t\t\tmaxTime = 1;\n\t\t\tif (maxTime < minTime)\n\t\t\t\tmaxTime = minTime;\n\n\t\t\tTimedTrafficLightsStep step = new TimedTrafficLightsStep(this, minTime, maxTime, changeMetric, waitFlowBalance, makeRed);\n\t\t\tSteps.Add(step);\n\t\t\treturn step;\n\t\t}\n\n\t\tpublic void Start() {\n\t\t\tStart(0);\n\t\t}\n\n\t\tpublic void Start(int stepIndex) {\n\t\t\t// TODO currently, this method must be called for each node in the node group individually\n\n\t\t\tif (stepIndex < 0 || stepIndex >= Steps.Count) {\n\t\t\t\tstepIndex = 0;\n\t\t\t}\n\n\t\t\t/*if (!housekeeping())\n\t\t\t\treturn;*/\n\n\t\t\tConstants.ServiceFactory.NetService.ProcessNode(NodeId, delegate (ushort nodeId, ref NetNode node) {\n\t\t\t\tConstants.ManagerFactory.TrafficLightManager.AddTrafficLight(NodeId, ref node);\n\t\t\t\treturn true;\n\t\t\t});\n\n\t\t\tforeach (TimedTrafficLightsStep step in Steps) {\n\t\t\t\tforeach (KeyValuePair<ushort, ICustomSegmentLights> e in step.CustomSegmentLights) {\n\t\t\t\t\te.Value.Housekeeping(true, true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tCheckInvalidPedestrianLights();\n\n\t\t\tCurrentStep = stepIndex;\n\t\t\tSteps[stepIndex].Start();\n\t\t\tSteps[stepIndex].UpdateLiveLights();\n\n\t\t\tstarted = true;\n\t\t}\n\n\t\tprivate void CheckInvalidPedestrianLights() {\n\t\t\tICustomSegmentLightsManager customTrafficLightsManager = Constants.ManagerFactory.CustomSegmentLightsManager;\n\t\t\tNodeGeometry nodeGeometry = NodeGeometry.Get(NodeId);\n\n\t\t\t//Log._Debug($\"Checking for invalid pedestrian lights @ {NodeId}.\");\n\t\t\tforeach (SegmentEndGeometry end in nodeGeometry.SegmentEndGeometries) {\n\t\t\t\tif (end == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tICustomSegmentLights lights = customTrafficLightsManager.GetSegmentLights(end.SegmentId, end.StartNode);\n\t\t\t\tif (lights == null) {\n\t\t\t\t\tLog.Warning($\"TimedTrafficLights.CheckInvalidPedestrianLights() @ node {NodeId}: Could not retrieve segment lights for segment {end.SegmentId} @ start {end.StartNode}.\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t//Log._Debug($\"Checking seg. {segmentId} @ {NodeId}.\");\n\t\t\t\tbool needsAlwaysGreenPedestrian = true;\n\t\t\t\tint i = 0;\n\t\t\t\tforeach (TimedTrafficLightsStep step in Steps) {\n\t\t\t\t\t//Log._Debug($\"Checking step {i}, seg. {segmentId} @ {NodeId}.\");\n\t\t\t\t\tif (!step.CustomSegmentLights.ContainsKey(end.SegmentId)) {\n\t\t\t\t\t\t//Log._Debug($\"Step {i} @ {NodeId} does not contain a segment light for seg. {segmentId}.\");\n\t\t\t\t\t\t++i;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t//Log._Debug($\"Checking step {i}, seg. {segmentId} @ {NodeId}: {step.segmentLights[segmentId].PedestrianLightState} (pedestrianLightState={step.segmentLights[segmentId].pedestrianLightState}, ManualPedestrianMode={step.segmentLights[segmentId].ManualPedestrianMode}, AutoPedestrianLightState={step.segmentLights[segmentId].AutoPedestrianLightState}\");\n\t\t\t\t\tif (step.CustomSegmentLights[end.SegmentId].PedestrianLightState == RoadBaseAI.TrafficLightState.Green) {\n\t\t\t\t\t\t//Log._Debug($\"Step {i} @ {NodeId} has a green ped. light @ seg. {segmentId}.\");\n\t\t\t\t\t\tneedsAlwaysGreenPedestrian = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t++i;\n\t\t\t\t}\n\t\t\t\t//Log._Debug($\"Setting InvalidPedestrianLight of seg. {segmentId} @ {NodeId} to {needsAlwaysGreenPedestrian}.\");\n\t\t\t\tlights.InvalidPedestrianLight = needsAlwaysGreenPedestrian;\n\t\t\t}\n\t\t}\n\n\t\tprivate void ClearInvalidPedestrianLights() {\n\t\t\tICustomSegmentLightsManager customTrafficLightsManager = Constants.ManagerFactory.CustomSegmentLightsManager;\n\n\t\t\tNodeGeometry nodeGeometry = NodeGeometry.Get(NodeId);\n\n\t\t\t//Log._Debug($\"Checking for invalid pedestrian lights @ {NodeId}.\");\n\t\t\tforeach (SegmentEndGeometry end in nodeGeometry.SegmentEndGeometries) {\n\t\t\t\tif (end == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tICustomSegmentLights lights = customTrafficLightsManager.GetSegmentLights(end.SegmentId, end.StartNode);\n\t\t\t\tif (lights == null) {\n\t\t\t\t\tLog.Warning($\"TimedTrafficLights.ClearInvalidPedestrianLights() @ node {NodeId}: Could not retrieve segment lights for segment {end.SegmentId} @ start {end.StartNode}.\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlights.InvalidPedestrianLight = false;\n\t\t\t}\n\t\t}\n\n\t\tpublic void RemoveNodeFromGroup(ushort otherNodeId) {\n\t\t\t// TODO currently, this method must be called for each node in the node group individually\n\n\t\t\tNodeGroup.Remove(otherNodeId);\n\t\t\tif (NodeGroup.Count <= 0) {\n\t\t\t\tConstants.ManagerFactory.TrafficLightSimulationManager.RemoveNodeFromSimulation(NodeId, true, false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tMasterNodeId = NodeGroup[0];\n\t\t}\n\n\t\t// TODO improve & remove\n\t\tpublic bool Housekeeping() {\n\t\t\t// TODO currently, this method must be called for each node in the node group individually\n\t\t\t//Log._Debug($\"Housekeeping timed light @ {NodeId}\");\n\n\t\t\tif (NodeGroup == null || NodeGroup.Count <= 0) {\n\t\t\t\tStop();\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t//Log.Warning($\"Timed housekeeping: Setting master node to {NodeGroup[0]}\");\n\t\t\tMasterNodeId = NodeGroup[0];\n\n\t\t\tif (IsStarted())\n\t\t\t\tCheckInvalidPedestrianLights();\n\n\t\t\tint i = 0;\n\t\t\tforeach (TimedTrafficLightsStep step in Steps) {\n\t\t\t\tforeach (CustomSegmentLights lights in step.CustomSegmentLights.Values) {\n\t\t\t\t\t//Log._Debug($\"----- Housekeeping timed light at step {i}, seg. {lights.SegmentId} @ {NodeId}\");\n\t\t\t\t\tConstants.ManagerFactory.CustomSegmentLightsManager.GetOrLiveSegmentLights(lights.SegmentId, lights.StartNode).Housekeeping(true, true);\n\t\t\t\t\tlights.Housekeeping(true, true);\n\t\t\t\t}\n\t\t\t\t++i;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic void MoveStep(int oldPos, int newPos) {\n\t\t\t// TODO currently, this method must be called for each node in the node group individually\n\n\t\t\tvar oldStep = Steps[oldPos];\n\n\t\t\tSteps.RemoveAt(oldPos);\n\t\t\tSteps.Insert(newPos, oldStep);\n\t\t}\n\n\t\tpublic void Stop() {\n\t\t\t// TODO currently, this method must be called for each node in the node group individually\n\n\t\t\tstarted = false;\n\t\t\tforeach (TimedTrafficLightsStep step in Steps) {\n\t\t\t\tstep.Reset();\n\t\t\t}\n\t\t\tClearInvalidPedestrianLights();\n\t\t}\n\n\t\t~TimedTrafficLights() {\n\t\t\tDestroy();\n\t\t}\n\n\t\tpublic void Destroy() {\n\t\t\t// TODO  currently, this method must be called for each node in the node group individually\n\n\t\t\tstarted = false;\n\t\t\tDestroySegmentEnds();\n\t\t\tSteps = null;\n\t\t\tNodeGroup = null;\n\t\t}\n\n\t\tpublic bool IsStarted() {\n\t\t\t// TODO currently, this method must be called for each node in the node group individually\n\n\t\t\treturn started;\n\t\t}\n\n\t\tpublic int NumSteps() {\n\t\t\t// TODO currently, this method must be called for each node in the node group individually\n\n\t\t\treturn Steps.Count;\n\t\t}\n\n\t\tpublic ITimedTrafficLightsStep GetStep(int stepId) {\n\t\t\t// TODO currently, this method must be called for each node in the node group individually\n\n\t\t\treturn Steps[stepId];\n\t\t}\n\n\t\tpublic void SimulationStep() {\n\t\t\t// TODO this method is currently called on each node, but should be called on the master node only\n\n#if DEBUGTTL\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId;\n#endif\n\n\t\t\tif (!IsMasterNode() || !IsStarted()) {\n#if DEBUGTTL\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TimedTrafficLights.SimulationStep(): TTL SimStep: *STOP* NodeId={NodeId} isMasterNode={IsMasterNode()} IsStarted={IsStarted()}\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// we are the master node\n\n\t\t\t/*if (!housekeeping()) {\n#if DEBUGTTL\n\t\t\t\tLog.Warning($\"TTL SimStep: *STOP* NodeId={NodeId} Housekeeping detected that this timed traffic light has become invalid: {NodeId}.\");\n#endif\n\t\t\t\tStop();\n\t\t\t\treturn;\n\t\t\t}*/\n\n#if DEBUGTTL\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TimedTrafficLights.SimulationStep(): TTL SimStep: NodeId={NodeId} Setting lights (1)\");\n#endif\n#if BENCHMARK\n\t\t\t//using (var bm = new Benchmark(null, \"SetLights.1\")) {\n#endif\n\t\t\t\tSetLights();\n#if BENCHMARK\n\t\t\t//}\n#endif\n\n#if BENCHMARK\n\t\t\t//using (var bm = new Benchmark(null, \"StepDone\")) {\n#endif\n\t\t\tif (!Steps[CurrentStep].StepDone(true)) {\n#if DEBUGTTL\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.SimulationStep(): TTL SimStep: *STOP* NodeId={NodeId} current step ({CurrentStep}) is not done.\");\n#endif\n\t\t\t\t\treturn;\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t//}\n#endif\n\n\t\t\t// step is done\n\n#if DEBUGTTL\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TimedTrafficLights.SimulationStep(): TTL SimStep: NodeId={NodeId} Setting lights (2)\");\n#endif\n\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\t\t\tif (Steps[CurrentStep].NextStepRefIndex < 0) {\n#if DEBUGTTL\n\t\t\t\tif (debug) {\n\t\t\t\t\tLog._Debug($\"TimedTrafficLights.SimulationStep(): Step {CurrentStep} is done at timed light {NodeId}. Determining next step.\");\n\t\t\t\t}\n#endif\n\t\t\t\t// next step has not yet identified yet. check for minTime=0 steps\n\t\t\t\tint nextStepIndex = (CurrentStep + 1) % NumSteps();\n\t\t\t\tif (Steps[nextStepIndex].MinTime == 0 && Steps[nextStepIndex].ChangeMetric == Steps[CurrentStep].ChangeMetric) {\n#if BENCHMARK\n\t\t\t\t\t//using (var bm = new Benchmark(null, \"bestNextStepIndex\")) {\n#endif\n\n\t\t\t\t\t// next step has minTime=0. calculate flow/wait ratios and compare.\n\t\t\t\t\tint prevStepIndex = CurrentStep;\n\n\t\t\t\t\t\t// Steps[CurrentStep].minFlow - Steps[CurrentStep].maxWait\n\t\t\t\t\t\tfloat maxWaitFlowDiff = Steps[CurrentStep].GetMetric(Steps[CurrentStep].CurrentFlow, Steps[CurrentStep].CurrentWait);\n\t\t\t\t\t\tif (float.IsNaN(maxWaitFlowDiff))\n\t\t\t\t\t\t\tmaxWaitFlowDiff = float.MinValue;\n\t\t\t\t\t\tint bestNextStepIndex = prevStepIndex;\n\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.SimulationStep(): Next step {nextStepIndex} has minTime = 0 at timed light {NodeId}. Old step {CurrentStep} has waitFlowDiff={maxWaitFlowDiff} (flow={Steps[CurrentStep].CurrentFlow}, wait={Steps[CurrentStep].CurrentWait}).\");\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\twhile (nextStepIndex != prevStepIndex) {\n\t\t\t\t\t\t\tfloat wait;\n\t\t\t\t\t\t\tfloat flow;\n\t\t\t\t\t\t\tSteps[nextStepIndex].CalcWaitFlow(false, nextStepIndex, out wait, out flow);\n\n\t\t\t\t\t\t\t//float flowWaitDiff = flow - wait;\n\t\t\t\t\t\t\tfloat flowWaitDiff = Steps[nextStepIndex].GetMetric(flow, wait);\n\t\t\t\t\t\t\tif (flowWaitDiff > maxWaitFlowDiff) {\n\t\t\t\t\t\t\t\tmaxWaitFlowDiff = flowWaitDiff;\n\t\t\t\t\t\t\t\tbestNextStepIndex = nextStepIndex;\n\t\t\t\t\t\t\t}\n\n#if DEBUGTTL\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.SimulationStep(): Checking upcoming step {nextStepIndex} @ node {NodeId}: flow={flow} wait={wait} minTime={Steps[nextStepIndex].MinTime}. bestWaitFlowDiff={bestNextStepIndex}, bestNextStepIndex={bestNextStepIndex}\");\n\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\tif (Steps[nextStepIndex].MinTime != 0) {\n\t\t\t\t\t\t\t\tint stepAfterPrev = (prevStepIndex + 1) % NumSteps();\n\t\t\t\t\t\t\t\tif (nextStepIndex == stepAfterPrev) {\n\t\t\t\t\t\t\t\t\t// always switch if the next step has a minimum time assigned\n\t\t\t\t\t\t\t\t\tbestNextStepIndex = stepAfterPrev;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tnextStepIndex = (nextStepIndex + 1) % NumSteps();\n\n\t\t\t\t\t\t\tif (Steps[nextStepIndex].ChangeMetric != Steps[CurrentStep].ChangeMetric) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\n\t\t\t\t\t\tif (bestNextStepIndex == CurrentStep) {\n#if DEBUGTTL\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.SimulationStep(): Best next step {bestNextStepIndex} (wait/flow diff = {maxWaitFlowDiff}) equals CurrentStep @ node {NodeId}.\");\n\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t// restart the current step\n\t\t\t\t\t\t\tforeach (ushort slaveNodeId in NodeGroup) {\n\t\t\t\t\t\t\t\tif (! tlsMan.TrafficLightSimulations[slaveNodeId].IsTimedLight()) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tITimedTrafficLights slaveTTL = tlsMan.TrafficLightSimulations[slaveNodeId].TimedLight;\n\t\t\t\t\t\t\t\tslaveTTL.GetStep(CurrentStep).Start(CurrentStep);\n\t\t\t\t\t\t\t\tslaveTTL.GetStep(CurrentStep).UpdateLiveLights();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t} else {\n#if DEBUGTTL\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.SimulationStep(): Best next step {bestNextStepIndex} (wait/flow diff = {maxWaitFlowDiff}) does not equal CurrentStep @ node {NodeId}.\");\n\t\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\t\t// set next step reference index for assuring a correct end transition\n\t\t\t\t\t\t\tforeach (ushort slaveNodeId in NodeGroup) {\n\t\t\t\t\t\t\t\tif (!tlsMan.TrafficLightSimulations[slaveNodeId].IsTimedLight()) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tITimedTrafficLights slaveTTL = tlsMan.TrafficLightSimulations[slaveNodeId].TimedLight;\n\t\t\t\t\t\t\t\tslaveTTL.GetStep(CurrentStep).NextStepRefIndex = bestNextStepIndex;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n#if BENCHMARK\n\t\t\t\t\t//}\n#endif\n\t\t\t\t} else {\n\t\t\t\t\tSteps[CurrentStep].NextStepRefIndex = nextStepIndex;\n\t\t\t\t}\n\t\t\t}\n\n#if BENCHMARK\n\t\t\t//using (var bm = new Benchmark(null, \"SetLights.2\")) {\n#endif\n\t\t\tSetLights(); // check if this is needed\n#if BENCHMARK\n\t\t\t//}\n#endif\n\n#if BENCHMARK\n\t\t\t//using (var bm = new Benchmark(null, \"IsEndTransitionDone\")) {\n#endif\n\t\t\tif (!Steps[CurrentStep].IsEndTransitionDone()) {\n#if DEBUGTTL\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.SimulationStep(): TTL SimStep: *STOP* NodeId={NodeId} current step ({CurrentStep}): end transition is not done.\");\n#endif\n\t\t\t\t\treturn;\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t//}\n#endif\n\n\t\t\t// ending transition (yellow) finished\n\n#if DEBUGTTL\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TimedTrafficLights.SimulationStep(): TTL SimStep: NodeId={NodeId} ending transition done. NodeGroup={string.Join(\", \", NodeGroup.Select(x => x.ToString()).ToArray())}, nodeId={NodeId}, NumSteps={NumSteps()}\");\n#endif\n\n#if BENCHMARK\n\t\t\t//using (var bm = new Benchmark(null, \"ChangeStep\")) {\n#endif\n\t\t\t// change step\n\t\t\tint newStepIndex = Steps[CurrentStep].NextStepRefIndex;\n\t\t\t\tint oldStepIndex = CurrentStep;\n\n\t\t\t\tforeach (ushort slaveNodeId in NodeGroup) {\n\t\t\t\t\tif (!tlsMan.TrafficLightSimulations[slaveNodeId].IsTimedLight()) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tITimedTrafficLights slaveTTL = tlsMan.TrafficLightSimulations[slaveNodeId].TimedLight;\n\t\t\t\t\tslaveTTL.CurrentStep = newStepIndex;\n\n#if DEBUGTTL\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.SimulationStep(): TTL SimStep: NodeId={slaveNodeId} setting lights of next step: {CurrentStep}\");\n#endif\n\n\t\t\t\t\tslaveTTL.GetStep(oldStepIndex).NextStepRefIndex = -1;\n\t\t\t\t\tslaveTTL.GetStep(newStepIndex).Start(oldStepIndex);\n\t\t\t\t\tslaveTTL.GetStep(newStepIndex).UpdateLiveLights();\n\t\t\t\t}\n#if BENCHMARK\n\t\t\t//}\n#endif\n\t\t}\n\n\t\tpublic void SetLights(bool noTransition=false) {\n\t\t\tif (Steps.Count <= 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\n\t\t\t// set lights\n\t\t\tforeach (ushort slaveNodeId in NodeGroup) {\n\t\t\t\tif (!tlsMan.TrafficLightSimulations[slaveNodeId].IsTimedLight()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tITimedTrafficLights slaveTTL = tlsMan.TrafficLightSimulations[slaveNodeId].TimedLight;\n\t\t\t\tslaveTTL.GetStep(CurrentStep).UpdateLiveLights(noTransition);\n\t\t\t}\n\t\t}\n\n\t\tpublic void SkipStep(bool setLights=true, int prevStepRefIndex=-1) {\n\t\t\tif (!IsMasterNode())\n\t\t\t\treturn;\n\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\n\t\t\tvar newCurrentStep = (CurrentStep + 1) % NumSteps();\n\t\t\tforeach (ushort slaveNodeId in NodeGroup) {\n\t\t\t\tif (!tlsMan.TrafficLightSimulations[slaveNodeId].IsTimedLight()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tITimedTrafficLights slaveTTL = tlsMan.TrafficLightSimulations[slaveNodeId].TimedLight;\n\n\t\t\t\tslaveTTL.GetStep(CurrentStep).SetStepDone();\n\t\t\t\tslaveTTL.CurrentStep = newCurrentStep;\n\t\t\t\tslaveTTL.GetStep(newCurrentStep).Start(prevStepRefIndex);\n\t\t\t\tif (setLights) {\n\t\t\t\t\tslaveTTL.GetStep(newCurrentStep).UpdateLiveLights();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic long CheckNextChange(ushort segmentId, bool startNode, ExtVehicleType vehicleType, int lightType) {\n\t\t\tvar curStep = CurrentStep;\n\t\t\tvar nextStep = (CurrentStep + 1) % NumSteps();\n\t\t\tvar numFrames = Steps[CurrentStep].MaxTimeRemaining();\n\n\t\t\tRoadBaseAI.TrafficLightState currentState;\n\t\t\tICustomSegmentLights segmentLights = Constants.ManagerFactory.CustomSegmentLightsManager.GetSegmentLights(segmentId, startNode, false);\n\t\t\tif (segmentLights == null) {\n\t\t\t\tLog._Debug($\"CheckNextChange: No segment lights at node {NodeId}, segment {segmentId}\");\n                return 99;\n\t\t\t}\n\t\t\tICustomSegmentLight segmentLight = segmentLights.GetCustomLight(vehicleType);\n\t\t\tif (segmentLight == null) {\n\t\t\t\tLog._Debug($\"CheckNextChange: No segment light at node {NodeId}, segment {segmentId}\");\n\t\t\t\treturn 99;\n\t\t\t}\n\n\t\t\tif (lightType == 0)\n\t\t\t\tcurrentState = segmentLight.LightMain;\n\t\t\telse if (lightType == 1)\n\t\t\t\tcurrentState = segmentLight.LightLeft;\n\t\t\telse if (lightType == 2)\n\t\t\t\tcurrentState = segmentLight.LightRight;\n\t\t\telse\n\t\t\t\tcurrentState = segmentLights.PedestrianLightState == null ? RoadBaseAI.TrafficLightState.Red : (RoadBaseAI.TrafficLightState)segmentLights.PedestrianLightState;\n\n\n\t\t\twhile (true) {\n\t\t\t\tif (nextStep == curStep) {\n\t\t\t\t\tnumFrames = 99;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tvar light = Steps[nextStep].GetLightState(segmentId, vehicleType, lightType);\n\n\t\t\t\tif (light != currentState) {\n\t\t\t\t\tbreak;\n\t\t\t\t} else {\n\t\t\t\t\tnumFrames += Steps[nextStep].MaxTime;\n\t\t\t\t}\n\n\t\t\t\tnextStep = (nextStep + 1) % NumSteps();\n\t\t\t}\n\n\t\t\treturn numFrames;\n\t\t}\n\n\t\tpublic void ResetSteps() {\n\t\t\tSteps.Clear();\n\t\t}\n\n\t\tpublic void RemoveStep(int id) {\n\t\t\tSteps.RemoveAt(id);\n\t\t}\n\n\t\tpublic void OnGeometryUpdate() {\n\t\t\tNodeGeometry nodeGeometry = NodeGeometry.Get(NodeId);\n\t\t\tLog._Debug($\"TimedTrafficLights.OnGeometryUpdate: called for timed traffic light @ {NodeId}. nodeGeometry={nodeGeometry}\");\n\n\t\t\tUpdateDirections(nodeGeometry);\n\t\t\tUpdateSegmentEnds();\n\n\t\t\tif (NumSteps() <= 0) {\n\t\t\t\tLog._Debug($\"TimedTrafficLights.OnGeometryUpdate: no steps @ {NodeId}\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tBackUpInvalidStepSegments(nodeGeometry);\n\t\t\tHandleNewSegments(nodeGeometry);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Moves all custom segment lights that are associated with an invalid segment to a special container for later reuse\n\t\t/// </summary>\n\t\tprivate void BackUpInvalidStepSegments(NodeGeometry nodeGeo) {\n#if DEBUGTTL\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId;\n\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TimedTrafficLights.BackUpInvalidStepSegments: called for timed traffic light @ {NodeId}\");\n#endif\n\n\t\t\tICollection<ushort> validSegments = new HashSet<ushort>();\n\t\t\tforeach (SegmentEndGeometry end in nodeGeo.SegmentEndGeometries) {\n\t\t\t\tif (end == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tvalidSegments.Add(end.SegmentId);\n\t\t\t}\n\n#if DEBUGTTL\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TimedTrafficLights.BackUpInvalidStepSegments: valid segments @ {NodeId}: {validSegments.CollectionToString()}\");\n#endif\n\n\t\t\tint i = 0;\n\t\t\tforeach (TimedTrafficLightsStep step in Steps) {\n\t\t\t\tICollection<ushort> invalidSegmentIds = new HashSet<ushort>();\n\t\t\t\tforeach (KeyValuePair<ushort, ICustomSegmentLights> e in step.CustomSegmentLights) {\n\t\t\t\t\tif (! validSegments.Contains(e.Key)) {\n\t\t\t\t\t\tstep.InvalidSegmentLights.AddLast(e.Value);\n\t\t\t\t\t\tinvalidSegmentIds.Add(e.Key);\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.BackUpInvalidStepSegments: Detected invalid segment @ step {i}, node {NodeId}: {e.Key}\");\n#endif\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tforeach (ushort invalidSegmentId in invalidSegmentIds) {\n#if DEBUGTTL\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.BackUpInvalidStepSegments: Removing invalid segment {invalidSegmentId} from step {i} @ node {NodeId}\");\n#endif\n\t\t\t\t\tstep.CustomSegmentLights.Remove(invalidSegmentId);\n\t\t\t\t}\n\n#if DEBUGTTL\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TimedTrafficLights.BackUpInvalidStepSegments finished for TTL step {i} @ node {NodeId}: step.CustomSegmentLights={step.CustomSegmentLights.DictionaryToString()} step.InvalidSegmentLights={step.InvalidSegmentLights.CollectionToString()}\");\n#endif\n\n\t\t\t\t++i;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Processes new segments and adds them to the steps. If steps contain a custom light\n\t\t/// for an old invalid segment, this light is being reused for the new segment.\n\t\t/// </summary>\n\t\t/// <param name=\"nodeGeo\"></param>\n\t\tprivate void HandleNewSegments(NodeGeometry nodeGeo) {\n#if DEBUGTTL\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId;\n#endif\n\n\t\t\tICustomSegmentLightsManager customTrafficLightsManager = Constants.ManagerFactory.CustomSegmentLightsManager;\n\t\t\tITrafficPriorityManager prioMan = Constants.ManagerFactory.TrafficPriorityManager;\n\n\t\t\t//Log._Debug($\"Checking for invalid pedestrian lights @ {NodeId}.\");\n\t\t\tforeach (SegmentEndGeometry end in nodeGeo.SegmentEndGeometries) {\n\t\t\t\tif (end == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n#if DEBUGTTL\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TimedTrafficLights.HandleNewSegments: handling existing seg. {end.SegmentId} @ {NodeId}\");\n#endif\n\n\t\t\t\tif (Steps[0].CustomSegmentLights.ContainsKey(end.SegmentId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// segment was created\n\t\t\t\tRotationOffset = 0;\n#if DEBUGTTL\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TimedTrafficLights.HandleNewSegments: New segment detected: {end.SegmentId} @ {NodeId}\");\n#endif\n\n\t\t\t\tint stepIndex = -1;\n\t\t\t\tforeach (TimedTrafficLightsStep step in Steps) {\n\t\t\t\t\t++stepIndex;\n\n\t\t\t\t\tLinkedListNode<ICustomSegmentLights> lightsToReuseNode = step.InvalidSegmentLights.First;\n\t\t\t\t\tif (lightsToReuseNode == null) {\n\t\t\t\t\t\t// no old segment found: create a fresh custom light\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.HandleNewSegments: Adding new segment {end.SegmentId} to node {NodeId} without reusing old segment\");\n#endif\n\t\t\t\t\t\tif (! step.AddSegment(end.SegmentId, end.StartNode, true)) {\n#if DEBUGTTL\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog.Warning($\"TimedTrafficLights.HandleNewSegments: Failed to add segment {end.SegmentId} @ start {end.StartNode} to node {NodeId}\");\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// reuse old lights\n\t\t\t\t\t\tstep.InvalidSegmentLights.RemoveFirst();\n\t\t\t\t\t\tICustomSegmentLights lightsToReuse = lightsToReuseNode.Value;\n\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"Replacing old segment @ {NodeId} with new segment {end.SegmentId}\");\n#endif\n\t\t\t\t\t\tlightsToReuse.Relocate(end.SegmentId, end.StartNode);\n\t\t\t\t\t\tstep.SetSegmentLights(end.SegmentId, lightsToReuse);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic ITimedTrafficLights MasterLights() {\n\t\t\treturn TrafficLightSimulationManager.Instance.TrafficLightSimulations[MasterNodeId].TimedLight;\n\t\t}\n\n\t\tpublic void SetTestMode(bool testMode) {\n\t\t\tthis.TestMode = false;\n\t\t\tif (!IsStarted())\n\t\t\t\treturn;\n\t\t\tthis.TestMode = testMode;\n\t\t}\n\n\t\tpublic bool IsInTestMode() {\n\t\t\tif (!IsStarted()) {\n\t\t\t\tTestMode = false;\n\t\t\t}\n\t\t\treturn TestMode;\n\t\t}\n\n\t\tpublic void ChangeLightMode(ushort segmentId, ExtVehicleType vehicleType, LightMode mode) {\n#if DEBUGTTL\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId;\n#endif\n\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeo == null) {\n#if DEBUGTTL\n\t\t\t\tif (debug)\n\t\t\t\t\tLog.Error($\"TimedTrafficLights.ChangeLightMode: No geometry information available for segment {segmentId}\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tforeach (TimedTrafficLightsStep step in Steps) {\n\t\t\t\tstep.ChangeLightMode(segmentId, vehicleType, mode);\n\t\t\t}\n\t\t\tConstants.ManagerFactory.CustomSegmentLightsManager.SetLightMode(segmentId, segGeo.StartNodeId() == NodeId, vehicleType, mode);\n\t\t}\n\n\t\tpublic void Join(ITimedTrafficLights otherTimedLight) {\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\n\t\t\tif (NumSteps() < otherTimedLight.NumSteps()) {\n\t\t\t\t// increase the number of steps at our timed lights\n\t\t\t\tfor (int i = NumSteps(); i < otherTimedLight.NumSteps(); ++i) {\n\t\t\t\t\tITimedTrafficLightsStep otherStep = otherTimedLight.GetStep(i);\n\t\t\t\t\tforeach (ushort slaveNodeId in NodeGroup) {\n\t\t\t\t\t\tif (!tlsMan.TrafficLightSimulations[slaveNodeId].IsTimedLight()) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tITimedTrafficLights slaveTTL = tlsMan.TrafficLightSimulations[slaveNodeId].TimedLight;\n\t\t\t\t\t\tslaveTTL.AddStep(otherStep.MinTime, otherStep.MaxTime, otherStep.ChangeMetric, otherStep.WaitFlowBalance, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// increase the number of steps at their timed lights\n\t\t\t\tfor (int i = otherTimedLight.NumSteps(); i < NumSteps(); ++i) {\n\t\t\t\t\tITimedTrafficLightsStep ourStep = GetStep(i);\n\t\t\t\t\tforeach (ushort slaveNodeId in otherTimedLight.NodeGroup) {\n\t\t\t\t\t\tif (!tlsMan.TrafficLightSimulations[slaveNodeId].IsTimedLight()) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tITimedTrafficLights slaveTTL = tlsMan.TrafficLightSimulations[slaveNodeId].TimedLight;\n\t\t\t\t\t\tslaveTTL.AddStep(ourStep.MinTime, ourStep.MaxTime, ourStep.ChangeMetric, ourStep.WaitFlowBalance, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// join groups and re-defined master node, determine mean min/max times & mean wait-flow-balances\n\t\t\tHashSet<ushort> newNodeGroupSet = new HashSet<ushort>();\n\t\t\tnewNodeGroupSet.UnionWith(NodeGroup);\n\t\t\tnewNodeGroupSet.UnionWith(otherTimedLight.NodeGroup);\n\t\t\tList<ushort> newNodeGroup = new List<ushort>(newNodeGroupSet);\n\t\t\tushort newMasterNodeId = newNodeGroup[0];\n\n\t\t\tint[] minTimes = new int[NumSteps()];\n\t\t\tint[] maxTimes = new int[NumSteps()];\n\t\t\tfloat[] waitFlowBalances = new float[NumSteps()];\n\t\t\tStepChangeMetric?[] stepChangeMetrics = new StepChangeMetric?[NumSteps()];\n\t\t\t\n\t\t\tforeach (ushort timedNodeId in newNodeGroup) {\n\t\t\t\tif (!tlsMan.TrafficLightSimulations[timedNodeId].IsTimedLight()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tITimedTrafficLights ttl = tlsMan.TrafficLightSimulations[timedNodeId].TimedLight;\n\n\t\t\t\tfor (int i = 0; i < NumSteps(); ++i) {\n\t\t\t\t\tminTimes[i] += ttl.GetStep(i).MinTime;\n\t\t\t\t\tmaxTimes[i] += ttl.GetStep(i).MaxTime;\n\t\t\t\t\twaitFlowBalances[i] += ttl.GetStep(i).WaitFlowBalance;\n\t\t\t\t\tStepChangeMetric metric = ttl.GetStep(i).ChangeMetric;\n\t\t\t\t\tif (metric != StepChangeMetric.Default) {\n\t\t\t\t\t\tif (stepChangeMetrics[i] == null) {\n\t\t\t\t\t\t\t// remember first non-default setting\n\t\t\t\t\t\t\tstepChangeMetrics[i] = metric;\n\t\t\t\t\t\t} else if (stepChangeMetrics[i] != metric) {\n\t\t\t\t\t\t\t// reset back to default on metric mismatch\n\t\t\t\t\t\t\tstepChangeMetrics[i] = StepChangeMetric.Default;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tttl.NodeGroup = newNodeGroup;\n\t\t\t\tttl.MasterNodeId = newMasterNodeId;\n\t\t\t}\n\n\t\t\t// build means\n\t\t\tif (NumSteps() > 0) {\n\t\t\t\tfor (int i = 0; i < NumSteps(); ++i) {\n\t\t\t\t\tminTimes[i] = Math.Max(0, minTimes[i] / newNodeGroup.Count);\n\t\t\t\t\tmaxTimes[i] = Math.Max(1, maxTimes[i] / newNodeGroup.Count);\n\t\t\t\t\twaitFlowBalances[i] = Math.Max(0.001f, waitFlowBalances[i] / (float)newNodeGroup.Count);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// apply means & reset\n\t\t\tforeach (ushort timedNodeId in newNodeGroup) {\n\t\t\t\tif (!tlsMan.TrafficLightSimulations[timedNodeId].IsTimedLight()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tITimedTrafficLights ttl = tlsMan.TrafficLightSimulations[timedNodeId].TimedLight;\n\n\t\t\t\tttl.Stop();\n\t\t\t\tttl.TestMode = false;\n\t\t\t\tfor (int i = 0; i < NumSteps(); ++i) {\n\t\t\t\t\tttl.GetStep(i).MinTime = minTimes[i];\n\t\t\t\t\tttl.GetStep(i).MaxTime = maxTimes[i];\n\t\t\t\t\tttl.GetStep(i).WaitFlowBalance = waitFlowBalances[i];\n\t\t\t\t\tttl.GetStep(i).ChangeMetric = stepChangeMetrics[i] == null ? StepChangeMetric.Default : (StepChangeMetric)stepChangeMetrics[i];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void UpdateSegmentEnds() {\n#if DEBUGTTL\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId;\n\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TimedTrafficLights.UpdateSegmentEnds: called for node {NodeId}\");\n#endif\n\n\t\t\tISegmentEndManager segEndMan = Constants.ManagerFactory.SegmentEndManager;\n\n\t\t\tICollection<SegmentEndId> segmentEndsToDelete = new HashSet<SegmentEndId>();\n\t\t\t// update currently set segment ends\n\t\t\tforeach (SegmentEndId endId in segmentEndIds) {\n#if DEBUGTTL\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TimedTrafficLights.UpdateSegmentEnds: updating existing segment end {endId} for node {NodeId}\");\n#endif\n\t\t\t\tif (!segEndMan.UpdateSegmentEnd(endId)) {\n#if DEBUGTTL\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.UpdateSegmentEnds: segment end {endId} @ node {NodeId} is invalid\");\n#endif\n\t\t\t\t\tsegmentEndsToDelete.Add(endId);\n\t\t\t\t} else {\n\t\t\t\t\tISegmentEnd end = segEndMan.GetSegmentEnd(endId);\n\t\t\t\t\tif (end.NodeId != NodeId) {\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.UpdateSegmentEnds: Segment end {end} is valid and updated but does not belong to TTL node {NodeId} anymore.\");\n#endif\n\t\t\t\t\t\tsegmentEndsToDelete.Add(endId);\n\t\t\t\t\t} else {\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.UpdateSegmentEnds: segment end {endId} @ node {NodeId} is valid\");\n#endif\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// remove all invalid segment ends\n\t\t\tforeach (SegmentEndId endId in segmentEndsToDelete) {\n#if DEBUGTTL\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TimedTrafficLights.UpdateSegmentEnds: Removing invalid segment end {endId} @ node {NodeId}\");\n#endif\n\t\t\t\tsegmentEndIds.Remove(endId);\n\t\t\t}\n\n\t\t\t// set up new segment ends\n\t\t\tNodeGeometry nodeGeo = NodeGeometry.Get(NodeId);\n#if DEBUGTTL\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TimedTrafficLights.UpdateSegmentEnds: Setting up new segment ends @ node {NodeId}. nodeGeo={nodeGeo}\");\n#endif\n\n\t\t\tforeach (SegmentEndGeometry endGeo in nodeGeo.SegmentEndGeometries) {\n\t\t\t\tif (endGeo == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (segmentEndIds.Contains(endGeo)) {\n#if DEBUGTTL\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TimedTrafficLights.UpdateSegmentEnds: Node {NodeId} already knows segment {endGeo.SegmentId}\");\n#endif\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n#if DEBUGTTL\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TimedTrafficLights.UpdateSegmentEnds: Adding segment {endGeo.SegmentId} to node {NodeId}\");\n#endif\n\t\t\t\tISegmentEnd end = segEndMan.GetOrAddSegmentEnd(endGeo.SegmentId, endGeo.StartNode);\n\t\t\t\tif (end != null) {\n\t\t\t\t\tsegmentEndIds.Add(end);\n\t\t\t\t} else {\n#if DEBUGTTL\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog.Warning($\"TimedTrafficLights.UpdateSegmentEnds: Failed to add segment end {endGeo.SegmentId} @ {endGeo.StartNode} to node {NodeId}: GetOrAddSegmentEnd returned null.\");\n#endif\n\t\t\t\t}\n\t\t\t}\n#if DEBUGTTL\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TimedTrafficLights.UpdateSegmentEnds: finished for node {NodeId}: {segmentEndIds.CollectionToString()}\");\n#endif\n\t\t}\n\n\t\tprivate void DestroySegmentEnds() {\n#if DEBUGTTL\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == NodeId;\n\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TimedTrafficLights.DestroySegmentEnds: Destroying segment ends @ node {NodeId}\");\n#endif\n\t\t\tforeach (ISegmentEndId endId in segmentEndIds) {\n#if DEBUGTTL\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TimedTrafficLights.DestroySegmentEnds: Destroying segment end {endId} @ node {NodeId}\");\n#endif\n\t\t\t\t// TODO only remove if no priority sign is located at the segment end (although this is currently not possible)\n\t\t\t\tConstants.ManagerFactory.SegmentEndManager.RemoveSegmentEnd(endId);\n\t\t\t}\n\t\t\tsegmentEndIds.Clear();\n#if DEBUGTTL\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TimedTrafficLights.DestroySegmentEnds: finished for node {NodeId}\");\n#endif\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficLight/Impl/TimedTrafficLightsStep.cs",
    "content": "#define DEBUGSTEPx\n#define DEBUGTTLx\n#define DEBUGMETRICx\n\nusing System;\nusing System.Collections.Generic;\nusing ColossalFramework;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Geometry;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\nusing TrafficManager.Util;\nusing System.Linq;\nusing CSUtil.Commons;\nusing TrafficManager.Geometry.Impl;\nusing CSUtil.Commons.Benchmark;\nusing TrafficManager.Manager.Impl;\n\nnamespace TrafficManager.TrafficLight.Impl {\n\t// TODO class should be completely reworked, approx. in version 1.10\n\tpublic class TimedTrafficLightsStep : ITimedTrafficLightsStep {\n\t\t/// <summary>\n\t\t/// The number of time units this traffic light remains in the current state at least\n\t\t/// </summary>\n\t\tpublic int MinTime { get; set; }\n\n\t\t/// <summary>\n\t\t/// The number of time units this traffic light remains in the current state at most\n\t\t/// </summary>\n\t\tpublic int MaxTime { get; set; }\n\n\t\t/// <summary>\n\t\t/// Indicates if waiting vehicles should be measured\n\t\t/// </summary>\n\t\tpublic StepChangeMetric ChangeMetric { get; set; }\n\n\t\tpublic uint startFrame;\n\n\t\t/// <summary>\n\t\t/// Indicates if the step is done (internal use only)\n\t\t/// </summary>\n\t\tprivate bool stepDone;\n\n\t\t/// <summary>\n\t\t/// Frame when the GreenToRed phase started\n\t\t/// </summary>\n\t\tprivate uint? endTransitionStart;\n\n\t\t/// <summary>\n\t\t/// minimum mean \"number of cars passing through\" / \"average segment length\"\n\t\t/// </summary>\n\t\tpublic float CurrentFlow { get; private set; }\n\n\t\t/// <summary>\n\t\t///\tmaximum mean \"number of cars waiting for green\" / \"average segment length\"\n\t\t/// </summary>\n\t\tpublic float CurrentWait { get; private set; }\n\n\t\tpublic int PreviousStepRefIndex { get; set; } = -1;\n\t\tpublic int NextStepRefIndex { get; set; } = -1;\n\n\t\tpublic uint lastFlowWaitCalc = 0;\n\n\t\tprivate ITimedTrafficLights timedNode;\n\n\t\tpublic IDictionary<ushort, ICustomSegmentLights> CustomSegmentLights { get; private set; } = new TinyDictionary<ushort, ICustomSegmentLights>();\n\t\tpublic LinkedList<ICustomSegmentLights> InvalidSegmentLights { get; private set; } = new LinkedList<ICustomSegmentLights>();\n\n\t\tpublic float WaitFlowBalance { get; set; } = 1f;\n\n\t\tpublic override string ToString() {\n\t\t\treturn $\"[TimedTrafficLightsStep\\n\" +\n\t\t\t\t\"\\t\" + $\"minTime = {MinTime}\\n\" +\n\t\t\t\t\"\\t\" + $\"maxTime = {MaxTime}\\n\" +\n\t\t\t\t\"\\t\" + $\"stepChangeMode = {ChangeMetric}\\n\" +\n\t\t\t\t\"\\t\" + $\"startFrame = {startFrame}\\n\" +\n\t\t\t\t\"\\t\" + $\"stepDone = {stepDone}\\n\" +\n\t\t\t\t\"\\t\" + $\"endTransitionStart = {endTransitionStart}\\n\" +\n\t\t\t\t\"\\t\" + $\"minFlow = {CurrentFlow}\\n\" +\n\t\t\t\t\"\\t\" + $\"maxWait = {CurrentWait}\\n\" +\n\t\t\t\t\"\\t\" + $\"PreviousStepRefIndex = {PreviousStepRefIndex}\\n\" +\n\t\t\t\t\"\\t\" + $\"NextStepRefIndex = {NextStepRefIndex}\\n\" +\n\t\t\t\t\"\\t\" + $\"lastFlowWaitCalc = {lastFlowWaitCalc}\\n\" +\n\t\t\t\t\"\\t\" + $\"CustomSegmentLights = {CustomSegmentLights}\\n\" +\n\t\t\t\t\"\\t\" + $\"InvalidSegmentLights = {InvalidSegmentLights.CollectionToString()}\\n\" +\n\t\t\t\t\"\\t\" + $\"waitFlowBalance = {WaitFlowBalance}\\n\" +\n\t\t\t\t\"TimedTrafficLightsStep]\";\n\t\t}\n\n\t\tpublic TimedTrafficLightsStep(ITimedTrafficLights timedNode, int minTime, int maxTime, StepChangeMetric stepChangeMode, float waitFlowBalance, bool makeRed=false) {\n\t\t\tthis.MinTime = minTime;\n\t\t\tthis.MaxTime = maxTime;\n\t\t\tthis.ChangeMetric = stepChangeMode;\n\t\t\tthis.WaitFlowBalance = waitFlowBalance;\n\t\t\tthis.timedNode = timedNode;\n\n\t\t\tCurrentFlow = Single.NaN;\n\t\t\tCurrentWait = Single.NaN;\n\n\t\t\tendTransitionStart = null;\n\t\t\tstepDone = false;\n\n\t\t\tNodeGeometry nodeGeometry = NodeGeometry.Get(timedNode.NodeId);\n\n\t\t\tforeach (SegmentEndGeometry end in nodeGeometry.SegmentEndGeometries) {\n\t\t\t\tif (end == null)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (! AddSegment(end.SegmentId, end.StartNode, makeRed)) {\n\t\t\t\t\tLog.Warning($\"TimedTrafficLightsStep.ctor: Failed to add segment {end.SegmentId} @ start {end.StartNode} to node {timedNode.NodeId}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate TimedTrafficLightsStep() {\n\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Checks if the green-to-red (=yellow) phase is finished\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic bool IsEndTransitionDone() {\n\t\t\tif (!timedNode.IsMasterNode()) {\n\t\t\t\tITimedTrafficLights masterLights = timedNode.MasterLights();\n\t\t\t\treturn masterLights.GetStep(masterLights.CurrentStep).IsEndTransitionDone();\n\t\t\t}\n\n\t\t\tbool ret = endTransitionStart != null && getCurrentFrame() > endTransitionStart && stepDone; //StepDone(false);\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.isEndTransitionDone() called for master NodeId={timedNode.NodeId}. CurrentStep={timedNode.CurrentStep} getCurrentFrame()={getCurrentFrame()} endTransitionStart={endTransitionStart} stepDone={stepDone} ret={ret}\");\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Checks if the green-to-red (=yellow) phase is currently active\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic bool IsInEndTransition() {\n\t\t\tif (!timedNode.IsMasterNode()) {\n\t\t\t\tITimedTrafficLights masterLights = timedNode.MasterLights();\n\t\t\t\treturn masterLights.GetStep(masterLights.CurrentStep).IsInEndTransition();\n\t\t\t}\n\n\t\t\tbool ret = endTransitionStart != null && getCurrentFrame() <= endTransitionStart && stepDone; //StepDone(false);\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.isInEndTransition() called for master NodeId={timedNode.NodeId}. CurrentStep={timedNode.CurrentStep} getCurrentFrame()={getCurrentFrame()} endTransitionStart={endTransitionStart} stepDone={stepDone} ret={ret}\");\n#endif\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool IsInStartTransition() {\n\t\t\tif (!timedNode.IsMasterNode()) {\n\t\t\t\tITimedTrafficLights masterLights = timedNode.MasterLights();\n\t\t\t\treturn masterLights.GetStep(masterLights.CurrentStep).IsInStartTransition();\n\t\t\t}\n\n\t\t\tbool ret = getCurrentFrame() == startFrame && !stepDone; //!StepDone(false);\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.isInStartTransition() called for master NodeId={timedNode.NodeId}. CurrentStep={timedNode.CurrentStep} getCurrentFrame()={getCurrentFrame()} startFrame={startFrame} stepDone={stepDone} ret={ret}\");\n#endif\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic RoadBaseAI.TrafficLightState GetLightState(ushort segmentId, ExtVehicleType vehicleType, int lightType) {\n\t\t\tICustomSegmentLight segLight = CustomSegmentLights[segmentId].GetCustomLight(vehicleType);\n\t\t\tif (segLight != null) {\n\t\t\t\tswitch (lightType) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn segLight.LightMain;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\treturn segLight.LightLeft;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\treturn segLight.LightRight;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\tRoadBaseAI.TrafficLightState? pedState = CustomSegmentLights[segmentId].PedestrianLightState;\n\t\t\t\t\t\treturn pedState == null ? RoadBaseAI.TrafficLightState.Red : (RoadBaseAI.TrafficLightState)pedState;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn RoadBaseAI.TrafficLightState.Green;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Starts the step.\n\t\t/// </summary>\n\t\tpublic void Start(int previousStepRefIndex=-1) {\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.Start: Starting step {timedNode.CurrentStep} @ {timedNode.NodeId}\");\n#endif\n\n\t\t\tthis.startFrame = getCurrentFrame();\n\t\t\tReset();\n\t\t\tPreviousStepRefIndex = previousStepRefIndex;\n\n#if DEBUG\n\t\t\t/*if (GlobalConfig.Instance.Debug.Switches[2]) {\n\t\t\t\tif (timedNode.NodeId == 31605) {\n\t\t\t\t\tLog._Debug($\"===== Step {timedNode.CurrentStep} @ node {timedNode.NodeId} =====\");\n\t\t\t\t\tLog._Debug($\"minTime: {minTime} maxTime: {maxTime}\");\n\t\t\t\t\tforeach (KeyValuePair<ushort, CustomSegmentLights> e in segmentLights) {\n\t\t\t\t\t\tLog._Debug($\"\\tSegment {e.Key}:\");\n\t\t\t\t\t\tLog._Debug($\"\\t{e.Value.ToString()}\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}*/\n#endif\n\t\t}\n\n\t\tinternal void Reset() {\n\t\t\tthis.endTransitionStart = null;\n\t\t\tCurrentFlow = Single.NaN;\n\t\t\tCurrentWait = Single.NaN;\n\t\t\tlastFlowWaitCalc = 0;\n\t\t\tPreviousStepRefIndex = -1;\n\t\t\tNextStepRefIndex = -1;\n\t\t\tstepDone = false;\n\t\t}\n\n\t\tinternal static uint getCurrentFrame() {\n\t\t\treturn Constants.ServiceFactory.SimulationService.CurrentFrameIndex >> 6;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Updates \"real-world\" traffic light states according to the timed scripts\n\t\t/// </summary>\n\t\tpublic void UpdateLiveLights() {\n\t\t\tUpdateLiveLights(false);\n\t\t}\n\t\t\n\t\tpublic void UpdateLiveLights(bool noTransition) {\n\t\t\ttry {\n\t\t\t\tICustomSegmentLightsManager customTrafficLightsManager = Constants.ManagerFactory.CustomSegmentLightsManager;\n\n\t\t\t\tbool atEndTransition = !noTransition && (IsInEndTransition() || IsEndTransitionDone()); // = yellow\n\t\t\t\tbool atStartTransition = !noTransition && !atEndTransition && IsInStartTransition(); // = red + yellow\n\n#if DEBUGTTL\n\t\t\t\tif (timedNode == null) {\n\t\t\t\t\tLog.Error($\"TimedTrafficLightsStep: timedNode is null!\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (PreviousStepRefIndex >= timedNode.NumSteps())\n\t\t\t\t\tPreviousStepRefIndex = -1;\n\t\t\t\tif (NextStepRefIndex >= timedNode.NumSteps())\n\t\t\t\t\tNextStepRefIndex = -1;\n\t\t\t\tITimedTrafficLightsStep previousStep = timedNode.GetStep(PreviousStepRefIndex >= 0 ? PreviousStepRefIndex : ((timedNode.CurrentStep + timedNode.NumSteps() - 1) % timedNode.NumSteps()));\n\t\t\t\tITimedTrafficLightsStep nextStep = timedNode.GetStep(NextStepRefIndex >= 0 ? NextStepRefIndex : ((timedNode.CurrentStep + 1) % timedNode.NumSteps()));\n\n#if DEBUGTTL\n\t\t\t\tif (previousStep == null) {\n\t\t\t\t\tLog.Error($\"TimedTrafficLightsStep: previousStep is null!\");\n\t\t\t\t\t//return;\n\t\t\t\t}\n\n\t\t\t\tif (nextStep == null) {\n\t\t\t\t\tLog.Error($\"TimedTrafficLightsStep: nextStep is null!\");\n\t\t\t\t\t//return;\n\t\t\t\t}\n\n\t\t\t\tif (previousStep.CustomSegmentLights == null) {\n\t\t\t\t\tLog.Error($\"TimedTrafficLightsStep: previousStep.segmentLights is null!\");\n\t\t\t\t\t//return;\n\t\t\t\t}\n\n\t\t\t\tif (nextStep.CustomSegmentLights == null) {\n\t\t\t\t\tLog.Error($\"TimedTrafficLightsStep: nextStep.segmentLights is null!\");\n\t\t\t\t\t//return;\n\t\t\t\t}\n\n\t\t\t\tif (CustomSegmentLights == null) {\n\t\t\t\t\tLog.Error($\"TimedTrafficLightsStep: segmentLights is null!\");\n\t\t\t\t\t//return;\n\t\t\t\t}\n#endif\n\n#if DEBUG\n\t\t\t\t//Log._Debug($\"TimedTrafficLightsStep.SetLights({noTransition}) called for NodeId={timedNode.NodeId}. atStartTransition={atStartTransition} atEndTransition={atEndTransition}\");\n#endif\n\n\t\t\t\tforeach (KeyValuePair<ushort, ICustomSegmentLights> e in CustomSegmentLights) {\n\t\t\t\t\tvar segmentId = e.Key;\n\t\t\t\t\tvar curStepSegmentLights = e.Value;\n\n#if DEBUG\n\t\t\t\t\t//Log._Debug($\"TimedTrafficLightsStep.SetLights({noTransition})   -> segmentId={segmentId} @ NodeId={timedNode.NodeId}\");\n#endif\n\n\t\t\t\t\tICustomSegmentLights prevStepSegmentLights = null;\n\t\t\t\t\tif (!previousStep.CustomSegmentLights.TryGetValue(segmentId, out prevStepSegmentLights)) {\n#if DEBUGTTL\n\t\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\t\t\t\tLog.Warning($\"TimedTrafficLightsStep: previousStep does not contain lights for segment {segmentId}!\");\n#endif\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tICustomSegmentLights nextStepSegmentLights = null;\n\t\t\t\t\tif (!nextStep.CustomSegmentLights.TryGetValue(segmentId, out nextStepSegmentLights)) {\n#if DEBUGTTL\n\t\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\t\t\t\tLog.Warning($\"TimedTrafficLightsStep: nextStep does not contain lights for segment {segmentId}!\");\n#endif\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t//segLightState.makeRedOrGreen(); // TODO temporary fix\n\n\t\t\t\t\tICustomSegmentLights liveSegmentLights = customTrafficLightsManager.GetSegmentLights(segmentId, curStepSegmentLights.StartNode, false);\n\t\t\t\t\tif (liveSegmentLights == null) {\n\t\t\t\t\t\tLog.Warning($\"TimedTrafficLightsStep.UpdateLights() @ node {timedNode.NodeId}: Could not retrieve live segment lights for segment {segmentId} @ start {curStepSegmentLights.StartNode}.\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tRoadBaseAI.TrafficLightState pedLightState = calcLightState((RoadBaseAI.TrafficLightState)prevStepSegmentLights.PedestrianLightState, (RoadBaseAI.TrafficLightState)curStepSegmentLights.PedestrianLightState, (RoadBaseAI.TrafficLightState)nextStepSegmentLights.PedestrianLightState, atStartTransition, atEndTransition);\n\t\t\t\t\t//Log._Debug($\"TimedStep.SetLights: Setting pedestrian light state @ seg. {segmentId} to {pedLightState} {curStepSegmentLights.ManualPedestrianMode}\");\n                    liveSegmentLights.ManualPedestrianMode = curStepSegmentLights.ManualPedestrianMode;\n\t\t\t\t\tliveSegmentLights.PedestrianLightState = liveSegmentLights.AutoPedestrianLightState = pedLightState;\n\t\t\t\t\t//Log.Warning($\"Step @ {timedNode.NodeId}: Segment {segmentId}: Ped.: {liveSegmentLights.PedestrianLightState.ToString()} / {liveSegmentLights.AutoPedestrianLightState.ToString()}\");\n\n#if DEBUGTTL\n\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\t\t\tif (curStepSegmentLights.VehicleTypes == null) {\n\t\t\t\t\t\t\tLog.Error($\"TimedTrafficLightsStep: curStepSegmentLights.VehicleTypes is null!\");\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\tforeach (ExtVehicleType vehicleType in curStepSegmentLights.VehicleTypes) {\n#if DEBUG\n\t\t\t\t\t\t//Log._Debug($\"TimedTrafficLightsStep.SetLights({noTransition})     -> segmentId={segmentId} @ NodeId={timedNode.NodeId} for vehicle {vehicleType}\");\n#endif\n\n\t\t\t\t\t\tICustomSegmentLight liveSegmentLight = liveSegmentLights.GetCustomLight(vehicleType);\n\t\t\t\t\t\tif (liveSegmentLight == null) {\n#if DEBUGTTL\n\t\t\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\t\t\t\t\tLog._Debug($\"Timed step @ seg. {segmentId}, node {timedNode.NodeId} has a traffic light for {vehicleType} but the live segment does not have one.\");\n#endif\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tICustomSegmentLight curStepSegmentLight = curStepSegmentLights.GetCustomLight(vehicleType);\n\t\t\t\t\t\tICustomSegmentLight prevStepSegmentLight = prevStepSegmentLights.GetCustomLight(vehicleType);\n\t\t\t\t\t\tICustomSegmentLight nextStepSegmentLight = nextStepSegmentLights.GetCustomLight(vehicleType);\n\t\t\t\t\t\t\n#if DEBUGTTL\n\t\t\t\t\t\tif (curStepSegmentLight == null) {\n\t\t\t\t\t\t\tLog.Error($\"TimedTrafficLightsStep: curStepSegmentLight is null!\");\n\t\t\t\t\t\t\t//return;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (prevStepSegmentLight == null) {\n\t\t\t\t\t\t\tLog.Error($\"TimedTrafficLightsStep: prevStepSegmentLight is null!\");\n\t\t\t\t\t\t\t//return;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (nextStepSegmentLight == null) {\n\t\t\t\t\t\t\tLog.Error($\"TimedTrafficLightsStep: nextStepSegmentLight is null!\");\n\t\t\t\t\t\t\t//return;\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tliveSegmentLight.InternalCurrentMode = curStepSegmentLight.CurrentMode; // TODO improve & remove\n\t\t\t\t\t\t/*curStepSegmentLight.EnsureModeLights();\n\t\t\t\t\t\tprevStepSegmentLight.EnsureModeLights();\n\t\t\t\t\t\tnextStepSegmentLight.EnsureModeLights();*/\n\n\t\t\t\t\t\tRoadBaseAI.TrafficLightState mainLight = calcLightState(prevStepSegmentLight.LightMain, curStepSegmentLight.LightMain, nextStepSegmentLight.LightMain, atStartTransition, atEndTransition);\n\t\t\t\t\t\tRoadBaseAI.TrafficLightState leftLight = calcLightState(prevStepSegmentLight.LightLeft, curStepSegmentLight.LightLeft, nextStepSegmentLight.LightLeft, atStartTransition, atEndTransition);\n\t\t\t\t\t\tRoadBaseAI.TrafficLightState rightLight = calcLightState(prevStepSegmentLight.LightRight, curStepSegmentLight.LightRight, nextStepSegmentLight.LightRight, atStartTransition, atEndTransition);\n\t\t\t\t\t\tliveSegmentLight.SetStates(mainLight, leftLight, rightLight, false);\n\n#if DEBUGTTL\n\t\t\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.SetLights({noTransition})     -> *SETTING* LightLeft={liveSegmentLight.LightLeft} LightMain={liveSegmentLight.LightMain} LightRight={liveSegmentLight.LightRight} for segmentId={segmentId} @ NodeId={timedNode.NodeId} for vehicle {vehicleType}\");\n#endif\n\n\t\t\t\t\t\t//Log._Debug($\"Step @ {timedNode.NodeId}: Segment {segmentId} for vehicle type {vehicleType}: L: {liveSegmentLight.LightLeft.ToString()} F: {liveSegmentLight.LightMain.ToString()} R: {liveSegmentLight.LightRight.ToString()}\");\n\t\t\t\t\t}\n\n\t\t\t\t\t/*if (timedNode.NodeId == 20164) {\n\t\t\t\t\t\tLog._Debug($\"Step @ {timedNode.NodeId}: Segment {segmentId}: {segmentLight.LightLeft.ToString()} {segmentLight.LightMain.ToString()} {segmentLight.LightRight.ToString()} {segmentLight.LightPedestrian.ToString()}\");\n                    }*/\n\n\t\t\t\t\tliveSegmentLights.UpdateVisuals();\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"Exception in TimedTrafficStep.UpdateLiveLights for node {timedNode.NodeId}: {e.ToString()}\");\n\t\t\t\t//invalid = true;\n\t\t\t}\n\t\t}\n\n\t\tprivate RoadBaseAI.TrafficLightState calcLightState(RoadBaseAI.TrafficLightState previousState, RoadBaseAI.TrafficLightState currentState, RoadBaseAI.TrafficLightState nextState, bool atStartTransition, bool atEndTransition) {\n\t\t\tif (atStartTransition && currentState == RoadBaseAI.TrafficLightState.Green && previousState == RoadBaseAI.TrafficLightState.Red)\n\t\t\t\treturn RoadBaseAI.TrafficLightState.RedToGreen;\n\t\t\telse if (atEndTransition && currentState == RoadBaseAI.TrafficLightState.Green && nextState == RoadBaseAI.TrafficLightState.Red)\n\t\t\t\treturn RoadBaseAI.TrafficLightState.GreenToRed;\n\t\t\telse\n\t\t\t\treturn currentState;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Updates timed segment lights according to \"real-world\" traffic light states\n\t\t/// </summary>\n\t\tpublic void UpdateLights() {\n\t\t\tLog._Debug($\"TimedTrafficLightsStep.UpdateLights: Updating lights of timed traffic light step @ {timedNode.NodeId}\");\n\t\t\tforeach (KeyValuePair<ushort, ICustomSegmentLights> e in CustomSegmentLights) {\n\t\t\t\tvar segmentId = e.Key;\n\t\t\t\tICustomSegmentLights segLights = e.Value;\n\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.UpdateLights: Updating lights of timed traffic light step at seg. {e.Key} @ {timedNode.NodeId}\");\n\n\t\t\t\t//if (segment == 0) continue;\n\t\t\t\tICustomSegmentLights liveSegLights = Constants.ManagerFactory.CustomSegmentLightsManager.GetSegmentLights(segmentId, segLights.StartNode, false);\n\t\t\t\tif (liveSegLights == null) {\n\t\t\t\t\tLog.Warning($\"TimedTrafficLightsStep.UpdateLights() @ node {timedNode.NodeId}: Could not retrieve live segment lights for segment {segmentId} @ start {segLights.StartNode}.\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tsegLights.SetLights(liveSegLights);\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.UpdateLights: Segment {segmentId} AutoPedState={segLights.AutoPedestrianLightState} live={liveSegLights.AutoPedestrianLightState}\");\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Countdown value for min. time\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic long MinTimeRemaining() {\n\t\t\treturn Math.Max(0, startFrame + MinTime - getCurrentFrame());\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Countdown value for max. time\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic long MaxTimeRemaining() {\n\t\t\treturn Math.Max(0, startFrame + MaxTime - getCurrentFrame());\n\t\t}\n\n\t\tpublic void SetStepDone() {\n\t\t\tstepDone = true;\n\t\t}\n\n\t\tpublic bool StepDone(bool updateValues) {\n#if DEBUGTTL\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId;\n\t\t\tif (debug) {\n\t\t\t\tLog._Debug($\"StepDone: called for node {timedNode.NodeId} @ step {timedNode.CurrentStep}\");\n\t\t\t}\n#endif\n\n\t\t\tif (!timedNode.IsMasterNode()) {\n\t\t\t\tITimedTrafficLights masterLights = timedNode.MasterLights();\n\t\t\t\treturn masterLights.GetStep(masterLights.CurrentStep).StepDone(updateValues);\n\t\t\t}\n\t\t\t// we are the master node\n\n\t\t\tif (timedNode.IsInTestMode()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (stepDone) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (getCurrentFrame() >= startFrame + MaxTime) {\n\t\t\t\t// maximum time reached. switch!\n#if DEBUGTTL\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"StepDone: step finished @ {timedNode.NodeId}\");\n#endif\n\t\t\t\tif (!stepDone && updateValues) {\n\t\t\t\t\tstepDone = true;\n\t\t\t\t\tendTransitionStart = getCurrentFrame();\n\t\t\t\t}\n\t\t\t\treturn stepDone;\n\t\t\t}\n\n\t\t\tif (getCurrentFrame() >= startFrame + MinTime) {\n\t\t\t\tfloat wait, flow;\n\t\t\t\tuint curFrame = getCurrentFrame();\n\t\t\t\t//Log._Debug($\"TTL @ {timedNode.NodeId}: curFrame={curFrame} lastFlowWaitCalc={lastFlowWaitCalc}\");\n\t\t\t\tif (lastFlowWaitCalc < curFrame) {\n\t\t\t\t\t//Log._Debug($\"TTL @ {timedNode.NodeId}: lastFlowWaitCalc<curFrame\");\n#if BENCHMARK\n\t\t\t\t\tusing (var bm = new Benchmark(null, \"CalcWaitFlow\")) {\n#endif\n\t\t\t\t\t\tCalcWaitFlow(true, timedNode.CurrentStep, out wait, out flow);\n#if BENCHMARK\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tif (updateValues) {\n\t\t\t\t\t\tlastFlowWaitCalc = curFrame;\n\t\t\t\t\t\t//Log._Debug($\"TTL @ {timedNode.NodeId}: updated lastFlowWaitCalc=curFrame={curFrame}\");\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tflow = CurrentFlow;\n\t\t\t\t\twait = CurrentWait;\n\t\t\t\t\t//Log._Debug($\"TTL @ {timedNode.NodeId}: lastFlowWaitCalc>=curFrame wait={maxWait} flow={minFlow}\");\n\t\t\t\t}\n\n\t\t\t\tfloat newFlow = CurrentFlow;\n\t\t\t\tfloat newWait = CurrentWait;\n\n#if DEBUGMETRIC\n\t\t\t\tnewFlow = flow;\n\t\t\t\tnewWait = wait;\n#else\n\t\t\t\tif (ChangeMetric != StepChangeMetric.Default || Single.IsNaN(newFlow)) {\n\t\t\t\t\tnewFlow = flow;\n\t\t\t\t} else {\n\t\t\t\t\tnewFlow = GlobalConfig.Instance.TimedTrafficLights.SmoothingFactor * newFlow + (1f - GlobalConfig.Instance.TimedTrafficLights.SmoothingFactor) * flow; // some smoothing\n\t\t\t\t}\n\n\t\t\t\tif (Single.IsNaN(newWait)) {\n\t\t\t\t\tnewWait = 0;\n\t\t\t\t} else if (ChangeMetric != StepChangeMetric.Default) {\n\t\t\t\t\tnewWait = wait;\n\t\t\t\t} else {\n\t\t\t\t\tnewWait = GlobalConfig.Instance.TimedTrafficLights.SmoothingFactor * newWait + (1f - GlobalConfig.Instance.TimedTrafficLights.SmoothingFactor) * wait; // some smoothing\n\t\t\t\t}\n#endif\n\n\t\t\t\t// if more cars are waiting than flowing, we change the step\n\t\t\t\tfloat metric;\n\t\t\t\tbool done = ShouldGoToNextStep(newFlow, newWait, out metric);// newWait > 0 && newFlow < newWait;\n\n\t\t\t\t//Log._Debug($\"TTL @ {timedNode.NodeId}: newWait={newWait} newFlow={newFlow} updateValues={updateValues} stepDone={stepDone} done={done}\");\n\n\t\t\t\tif (updateValues) {\n\t\t\t\t\tCurrentFlow = newFlow;\n\t\t\t\t\tCurrentWait = newWait;\n\t\t\t\t\t//Log._Debug($\"TTL @ {timedNode.NodeId}: updated minFlow=newFlow={minFlow} maxWait=newWait={maxWait}\");\n\t\t\t\t}\n#if DEBUG\n\t\t\t\t//Log.Message(\"step finished (2) @ \" + nodeId);\n#endif\n\t\t\t\tif (updateValues && !stepDone && done) {\n\t\t\t\t\tstepDone = done;\n\t\t\t\t\tendTransitionStart = getCurrentFrame();\n\t\t\t\t}\n\t\t\t\treturn done;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic float GetMetric(float flow, float wait) {\n\t\t\tswitch (ChangeMetric) {\n\t\t\t\tcase StepChangeMetric.Default:\n\t\t\t\tdefault:\n\t\t\t\t\treturn flow - wait;\n\t\t\t\tcase StepChangeMetric.FirstFlow:\n\t\t\t\t\treturn flow <= 0 ? 1f : 0f;\n\t\t\t\tcase StepChangeMetric.FirstWait:\n\t\t\t\t\treturn wait <= 0 ? 1f : 0f;\n\t\t\t\tcase StepChangeMetric.NoFlow:\n\t\t\t\t\treturn flow > 0 ? 1f : 0f;\n\t\t\t\tcase StepChangeMetric.NoWait:\n\t\t\t\t\treturn wait > 0 ? 1f : 0f;\n\t\t\t}\n\t\t}\n\n\t\tpublic bool ShouldGoToNextStep(float flow, float wait, out float metric) {\n\t\t\tmetric = GetMetric(flow, wait);\n\t\t\treturn ChangeMetric == StepChangeMetric.Default ? metric < 0 : metric == 0f;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Calculates the current metrics for flowing and waiting vehicles\n\t\t/// </summary>\n\t\t/// <param name=\"wait\"></param>\n\t\t/// <param name=\"flow\"></param>\n\t\t/// <returns>true if the values could be calculated, false otherwise</returns>\n\t\tpublic void CalcWaitFlow(bool countOnlyMovingIfGreen, int stepRefIndex, out float wait, out float flow) {\n\t\t\tuint numFlows = 0;\n\t\t\tuint numWaits = 0;\n\t\t\tfloat curTotalFlow = 0;\n\t\t\tfloat curTotalWait = 0;\n\n#if DEBUGTTL\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId;\n\t\t\tif (debug) {\n\t\t\t\tLog.Warning($\"calcWaitFlow: called for node {timedNode.NodeId} @ step {stepRefIndex}\");\n\t\t\t}\n#else\n\t\t\tbool debug = false;\n#endif\n\n\t\t\t// TODO checking agains getCurrentFrame() is only valid if this is the current step\n\t\t\tif (countOnlyMovingIfGreen && getCurrentFrame() <= startFrame + MinTime + 1) { // during start phase all vehicles on \"green\" segments are counted as flowing\n\t\t\t\tcountOnlyMovingIfGreen = false;\n\t\t\t}\n\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\t\t\tISegmentEndManager endMan = Constants.ManagerFactory.SegmentEndManager;\n\t\t\tIVehicleRestrictionsManager restrMan = Constants.ManagerFactory.VehicleRestrictionsManager;\n\n\t\t\t// loop over all timed traffic lights within the node group\n\t\t\tforeach (ushort timedNodeId in timedNode.NodeGroup) {\n\t\t\t\tif (!tlsMan.TrafficLightSimulations[timedNodeId].IsTimedLight()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tITimedTrafficLights slaveTTL = tlsMan.TrafficLightSimulations[timedNodeId].TimedLight;\n\t\t\t\tITimedTrafficLightsStep slaveStep = slaveTTL.GetStep(stepRefIndex);\n\n\t\t\t\t// minimum time reached. check traffic! loop over source segments\n\t\t\t\tuint numNodeFlows = 0;\n\t\t\t\tuint numNodeWaits = 0;\n\t\t\t\tfloat curTotalNodeFlow = 0;\n\t\t\t\tfloat curTotalNodeWait = 0;\n\t\t\t\tforeach (KeyValuePair<ushort, ICustomSegmentLights> e in slaveStep.CustomSegmentLights) {\n\t\t\t\t\tvar sourceSegmentId = e.Key;\n\t\t\t\t\tvar segLights = e.Value;\n\n\t\t\t\t\tIDictionary<ushort, ArrowDirection> directions = null;\n\t\t\t\t\tif (!slaveTTL.Directions.TryGetValue(sourceSegmentId, out directions)) {\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\tLog._Debug($\"calcWaitFlow: No arrow directions defined for segment {sourceSegmentId} @ {timedNodeId}\");\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// one of the traffic lights at this segment is green: count minimum traffic flowing through\n\t\t\t\t\tISegmentEnd sourceSegmentEnd = endMan.GetSegmentEnd(sourceSegmentId, segLights.StartNode);\n\t\t\t\t\tif (sourceSegmentEnd == null) {\n\t\t\t\t\t\tLog.Error($\"TimedTrafficLightsStep.calcWaitFlow: No segment end @ seg. {sourceSegmentId} found!\");\n\t\t\t\t\t\tcontinue; // skip invalid segment\n\t\t\t\t\t}\n\n\t\t\t\t\tIDictionary<ushort, uint>[] movingVehiclesMetrics = null;\n\t\t\t\t\tbool countOnlyMovingIfGreenOnSegment = false;\n\t\t\t\t\tif (ChangeMetric == StepChangeMetric.Default) {\n\t\t\t\t\t\tcountOnlyMovingIfGreenOnSegment = countOnlyMovingIfGreen;\n\t\t\t\t\t\tif (countOnlyMovingIfGreenOnSegment) {\n\t\t\t\t\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(sourceSegmentId, delegate (ushort srcSegId, ref NetSegment segment) {\n\t\t\t\t\t\t\t\tif (restrMan.IsRailSegment(segment.Info)) {\n\t\t\t\t\t\t\t\t\tcountOnlyMovingIfGreenOnSegment = false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmovingVehiclesMetrics =\tcountOnlyMovingIfGreenOnSegment ? sourceSegmentEnd.MeasureOutgoingVehicles(false, debug) : null;\n\t\t\t\t\t}\n\t\t\t\t\tIDictionary<ushort, uint>[] allVehiclesMetrics = sourceSegmentEnd.MeasureOutgoingVehicles(true, debug);\n\t\t\t\t\t\n\t\t\t\t\tExtVehicleType?[] vehTypeByLaneIndex = segLights.VehicleTypeByLaneIndex;\n#if DEBUGTTL\n\t\t\t\t\tif (debug) {\n\t\t\t\t\t\tLog._Debug($\"calcWaitFlow: Seg. {sourceSegmentId} @ {timedNodeId}, vehTypeByLaneIndex={string.Join(\", \", vehTypeByLaneIndex.Select(x => x == null ? \"null\" : x.ToString()).ToArray())}\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tuint numSegFlows = 0;\n\t\t\t\t\tuint numSegWaits = 0;\n\t\t\t\t\tfloat curTotalSegFlow = 0;\n\t\t\t\t\tfloat curTotalSegWait = 0;\n\t\t\t\t\t// loop over source lanes\n\t\t\t\t\tfor (byte laneIndex = 0; laneIndex < vehTypeByLaneIndex.Length; ++laneIndex) {\n\t\t\t\t\t\tExtVehicleType? vehicleType = vehTypeByLaneIndex[laneIndex];\n\t\t\t\t\t\tif (vehicleType == null) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tICustomSegmentLight segLight = segLights.GetCustomLight(laneIndex);\n\t\t\t\t\t\tif (segLight == null) {\n#if DEBUGTTL\n\t\t\t\t\t\t\tLog.Warning($\"Timed traffic light step: Failed to get custom light for vehicleType {vehicleType} @ seg. {sourceSegmentId}, node {timedNode.NodeId}!\");\n#endif\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tIDictionary<ushort, uint> movingVehiclesMetric = countOnlyMovingIfGreenOnSegment ? movingVehiclesMetrics[laneIndex] : null;\n\t\t\t\t\t\tIDictionary<ushort, uint> allVehiclesMetric = allVehiclesMetrics[laneIndex];\n\t\t\t\t\t\tif (allVehiclesMetrics == null) {\n#if DEBUGTTL\n\t\t\t\t\t\t\tif (debug) {\n\t\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.calcWaitFlow: No cars on lane {laneIndex} @ seg. {sourceSegmentId}. Vehicle types: {vehicleType}\");\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.calcWaitFlow: Checking lane {laneIndex} @ seg. {sourceSegmentId}. Vehicle types: {vehicleType}\");\n#endif\n\n\t\t\t\t\t\t// loop over target segment: calculate waiting/moving traffic\n\t\t\t\t\t\tuint numLaneFlows = 0;\n\t\t\t\t\t\tuint numLaneWaits = 0;\n\t\t\t\t\t\tuint curTotalLaneFlow = 0;\n\t\t\t\t\t\tuint curTotalLaneWait = 0;\n\t\t\t\t\t\tforeach (KeyValuePair<ushort, uint> f in allVehiclesMetric) {\n\t\t\t\t\t\t\tushort targetSegmentId = f.Key;\n\t\t\t\t\t\t\tuint numVehicles = f.Value;\n\n\t\t\t\t\t\t\tArrowDirection dir;\n\t\t\t\t\t\t\tif (!directions.TryGetValue(targetSegmentId, out dir)) {\n\t\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.calcWaitFlow: Direction undefined for target segment {targetSegmentId} @ {timedNodeId}\");\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tuint numMovingVehicles = countOnlyMovingIfGreenOnSegment ? movingVehiclesMetric[f.Key] : numVehicles;\n\n#if DEBUGTTL\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.calcWaitFlow: Total num of cars on seg. {sourceSegmentId}, lane {laneIndex} going to seg. {targetSegmentId}: {numMovingVehicles} (all: {numVehicles})\");\n#endif\n\n\t\t\t\t\t\t\tbool addToFlow = false;\n\t\t\t\t\t\t\tswitch (dir) {\n\t\t\t\t\t\t\t\tcase ArrowDirection.Turn:\n\t\t\t\t\t\t\t\t\taddToFlow = Constants.ServiceFactory.SimulationService.LeftHandDrive ? segLight.IsRightGreen() : segLight.IsLeftGreen();\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase ArrowDirection.Left:\n\t\t\t\t\t\t\t\t\taddToFlow = segLight.IsLeftGreen();\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase ArrowDirection.Right:\n\t\t\t\t\t\t\t\t\taddToFlow = segLight.IsRightGreen();\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase ArrowDirection.Forward:\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\taddToFlow = segLight.IsMainGreen();\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (addToFlow) {\n\t\t\t\t\t\t\t\tcurTotalLaneFlow += numMovingVehicles;\n\t\t\t\t\t\t\t\t++numLaneFlows;\n#if DEBUGTTL\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.calcWaitFlow: ## Vehicles @ lane {laneIndex}, seg. {sourceSegmentId} going to seg. {targetSegmentId}: COUTING as FLOWING -- numMovingVehicles={numMovingVehicles}\");\n#endif\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcurTotalLaneWait += numVehicles;\n\t\t\t\t\t\t\t\t++numLaneWaits;\n#if DEBUGTTL\n\t\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.calcWaitFlow: ## Vehicles @ lane {laneIndex}, seg. {sourceSegmentId} going to seg. {targetSegmentId}: COUTING as WAITING -- numVehicles={numVehicles}\");\n#endif\n\t\t\t\t\t\t\t}\n\n#if DEBUGTTL\n\t\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.calcWaitFlow: >>>>> Vehicles @ lane {laneIndex}, seg. {sourceSegmentId} going to seg. {targetSegmentId}: curTotalLaneFlow={curTotalLaneFlow}, curTotalLaneWait={curTotalLaneWait}, numLaneFlows={numLaneFlows}, numLaneWaits={numLaneWaits}\");\n#endif\n\t\t\t\t\t\t} // foreach target segment\n\n\t\t\t\t\t\tfloat meanLaneFlow = 0;\n\t\t\t\t\t\tif (numLaneFlows > 0) {\n\t\t\t\t\t\t\tswitch (GlobalConfig.Instance.TimedTrafficLights.FlowWaitCalcMode) {\n\t\t\t\t\t\t\t\tcase FlowWaitCalcMode.Mean:\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t++numSegFlows;\n\t\t\t\t\t\t\t\t\tmeanLaneFlow = (float)curTotalLaneFlow / (float)numLaneFlows;\n\t\t\t\t\t\t\t\t\tcurTotalSegFlow += meanLaneFlow;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase FlowWaitCalcMode.Total:\n\t\t\t\t\t\t\t\t\tnumSegFlows += numLaneFlows;\n\t\t\t\t\t\t\t\t\tcurTotalSegFlow += curTotalLaneFlow;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfloat meanLaneWait = 0;\n\t\t\t\t\t\tif (numLaneWaits > 0) {\n\t\t\t\t\t\t\tswitch (GlobalConfig.Instance.TimedTrafficLights.FlowWaitCalcMode) {\n\t\t\t\t\t\t\t\tcase FlowWaitCalcMode.Mean:\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t++numSegWaits;\n\t\t\t\t\t\t\t\t\tmeanLaneWait = (float)curTotalLaneWait / (float)numLaneWaits;\n\t\t\t\t\t\t\t\t\tcurTotalSegWait += meanLaneWait;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase FlowWaitCalcMode.Total:\n\t\t\t\t\t\t\t\t\tnumSegWaits += numLaneWaits;\n\t\t\t\t\t\t\t\t\tcurTotalSegWait += curTotalLaneWait;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n#if DEBUGTTL\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.calcWaitFlow: >>>> Vehicles @ lane {laneIndex}, seg. {sourceSegmentId}: meanLaneFlow={meanLaneFlow}, meanLaneWait={meanLaneWait} // curTotalSegFlow={curTotalSegFlow}, curTotalSegWait={curTotalSegWait}, numSegFlows={numSegFlows}, numSegWaits={numSegWaits}\");\n#endif\n\n\t\t\t\t\t} // foreach source lane\n\n\t\t\t\t\tfloat meanSegFlow = 0;\n\t\t\t\t\tif (numSegFlows > 0) {\n\t\t\t\t\t\tswitch (GlobalConfig.Instance.TimedTrafficLights.FlowWaitCalcMode) {\n\t\t\t\t\t\t\tcase FlowWaitCalcMode.Mean:\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t++numNodeFlows;\n\t\t\t\t\t\t\t\tmeanSegFlow = (float)curTotalSegFlow / (float)numSegFlows;\n\t\t\t\t\t\t\t\tcurTotalNodeFlow += meanSegFlow;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase FlowWaitCalcMode.Total:\n\t\t\t\t\t\t\t\tnumNodeFlows += numSegFlows;\n\t\t\t\t\t\t\t\tcurTotalNodeFlow += curTotalSegFlow;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat meanSegWait = 0;\n\t\t\t\t\tif (numSegWaits > 0) {\n\t\t\t\t\t\tswitch (GlobalConfig.Instance.TimedTrafficLights.FlowWaitCalcMode) {\n\t\t\t\t\t\t\tcase FlowWaitCalcMode.Mean:\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t++numNodeWaits;\n\t\t\t\t\t\t\t\tmeanSegWait = (float)curTotalSegWait / (float)numSegWaits;\n\t\t\t\t\t\t\t\tcurTotalNodeWait += meanSegWait;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase FlowWaitCalcMode.Total:\n\t\t\t\t\t\t\t\tnumNodeWaits += numSegWaits;\n\t\t\t\t\t\t\t\tcurTotalNodeWait += curTotalSegWait;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n#if DEBUGTTL\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.calcWaitFlow: >>> Vehicles @ seg. {sourceSegmentId}: meanSegFlow={meanSegFlow}, meanSegWait={meanSegWait} // curTotalNodeFlow={curTotalNodeFlow}, curTotalNodeWait={curTotalNodeWait}, numNodeFlows={numNodeFlows}, numNodeWaits={numNodeWaits}\");\n#endif\n\n\t\t\t\t} // foreach source segment\n\n\t\t\t\tfloat meanNodeFlow = 0;\n\t\t\t\tif (numNodeFlows > 0) {\n\t\t\t\t\tswitch (GlobalConfig.Instance.TimedTrafficLights.FlowWaitCalcMode) {\n\t\t\t\t\t\tcase FlowWaitCalcMode.Mean:\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t++numFlows;\n\t\t\t\t\t\t\tmeanNodeFlow = (float)curTotalNodeFlow / (float)numNodeFlows;\n\t\t\t\t\t\t\tcurTotalFlow += meanNodeFlow;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase FlowWaitCalcMode.Total:\n\t\t\t\t\t\t\tnumFlows += numNodeFlows;\n\t\t\t\t\t\t\tcurTotalFlow += curTotalNodeFlow;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfloat meanNodeWait = 0;\n\t\t\t\tif (numNodeWaits > 0) {\n\t\t\t\t\tswitch (GlobalConfig.Instance.TimedTrafficLights.FlowWaitCalcMode) {\n\t\t\t\t\t\tcase FlowWaitCalcMode.Mean:\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t++numWaits;\n\t\t\t\t\t\t\tmeanNodeWait = (float)curTotalNodeWait / (float)numNodeWaits;\n\t\t\t\t\t\t\tcurTotalWait += meanNodeWait;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase FlowWaitCalcMode.Total:\n\t\t\t\t\t\t\tnumWaits += numNodeWaits;\n\t\t\t\t\t\t\tcurTotalWait += curTotalNodeWait;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n#if DEBUGTTL\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.calcWaitFlow: Calculated flow for source node {timedNodeId}: meanNodeFlow={meanNodeFlow} meanNodeWait={meanNodeWait} // curTotalFlow={curTotalFlow}, curTotalWait={curTotalWait}, numFlows={numFlows}, numWaits={numWaits}\");\n#endif\n\t\t\t} // foreach timed node\n\n\t\t\tfloat meanFlow = numFlows > 0 ? (float)curTotalFlow / (float)numFlows : 0;\n\t\t\tfloat meanWait = numWaits > 0 ? (float)curTotalWait / (float)numWaits : 0;\n\t\t\tmeanFlow /= WaitFlowBalance; // a value smaller than 1 rewards steady traffic currents\n\n\t\t\twait = (float)meanWait;\n\t\t\tflow = meanFlow;\n\n#if DEBUGTTL\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.calcWaitFlow: ***CALCULATION FINISHED*** for master node {timedNode.NodeId}: flow={flow} wait={wait}\");\n#endif\n\t\t}\n\n\t\tinternal void ChangeLightMode(ushort segmentId, ExtVehicleType vehicleType, LightMode mode) {\n\t\t\tICustomSegmentLight light = CustomSegmentLights[segmentId].GetCustomLight(vehicleType);\n\t\t\tif (light != null) {\n\t\t\t\tlight.CurrentMode = mode;\n\t\t\t}\n\t\t}\n\n\t\tpublic ICustomSegmentLights RemoveSegmentLights(ushort segmentId) {\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.RemoveSegmentLights({segmentId}) called.\");\n#endif\n\n\t\t\tICustomSegmentLights ret = null;\n\t\t\tif (CustomSegmentLights.TryGetValue(segmentId, out ret)) {\n\t\t\t\tCustomSegmentLights.Remove(segmentId);\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic ICustomSegmentLights GetSegmentLights(ushort segmentId) {\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.GetSegmentLights({segmentId}) called.\");\n#endif\n\n\t\t\treturn GetSegmentLights(timedNode.NodeId, segmentId);\n\t\t}\n\n\t\tpublic ICustomSegmentLights GetSegmentLights(ushort nodeId, ushort segmentId) {\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.GetSegmentLights({nodeId}, {segmentId}) called.\");\n#endif\n\n\t\t\tif (nodeId != timedNode.NodeId) {\n\t\t\t\tLog.Warning($\"TimedTrafficLightsStep.GetSegmentLights({nodeId}, {segmentId}): TTL @ node {timedNode.NodeId} does not handle custom traffic lights for node {nodeId}\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tICustomSegmentLights customLights;\n\t\t\tif (CustomSegmentLights.TryGetValue(segmentId, out customLights)) {\n\t\t\t\treturn customLights;\n\t\t\t} else {\n\t\t\t\tLog.Info($\"TimedTrafficLightsStep.GetSegmentLights({nodeId}, {segmentId}): TTL @ node {timedNode.NodeId} does not know segment {segmentId}\");\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tpublic bool RelocateSegmentLights(ushort sourceSegmentId, ushort targetSegmentId) {\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.RelocateSegmentLights({sourceSegmentId}, {targetSegmentId}) called.\");\n#endif\n\n\t\t\tICustomSegmentLights sourceLights = null;\n\t\t\tif (! CustomSegmentLights.TryGetValue(sourceSegmentId, out sourceLights)) {\n\t\t\t\tLog.Error($\"TimedTrafficLightsStep.RelocateSegmentLights({sourceSegmentId}, {targetSegmentId}): Timed traffic light does not know source segment {sourceSegmentId}. Cannot relocate to {targetSegmentId}.\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(targetSegmentId);\n\t\t\tif (segGeo == null) {\n\t\t\t\tLog.Error($\"TimedTrafficLightsStep.RelocateSegmentLights({sourceSegmentId}, {targetSegmentId}): No geometry information available for target segment {targetSegmentId}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (segGeo.StartNodeId() != timedNode.NodeId && segGeo.EndNodeId() != timedNode.NodeId) {\n\t\t\t\tLog.Error($\"TimedTrafficLightsStep.RelocateSegmentLights({sourceSegmentId}, {targetSegmentId}): Target segment {targetSegmentId} is not connected to node {timedNode.NodeId}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool startNode = segGeo.StartNodeId() == timedNode.NodeId;\n\t\t\tCustomSegmentLights.Remove(sourceSegmentId);\n\t\t\tConstants.ManagerFactory.CustomSegmentLightsManager.GetOrLiveSegmentLights(targetSegmentId, startNode).Housekeeping(true, true);\n\t\t\tsourceLights.Relocate(targetSegmentId, startNode, this);\n\t\t\tCustomSegmentLights[targetSegmentId] = sourceLights;\n\n\t\t\tLog._Debug($\"TimedTrafficLightsStep.RelocateSegmentLights({sourceSegmentId}, {targetSegmentId}): Relocated lights: {sourceSegmentId} -> {targetSegmentId} @ node {timedNode.NodeId}\");\n\t\t\treturn true;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Adds a new segment to this step. It is cloned from the live custom traffic light.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\"></param>\n\t\tinternal bool AddSegment(ushort segmentId, bool startNode, bool makeRed) {\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.AddSegment({segmentId}, {startNode}, {makeRed}) called @ node {timedNode.NodeId}.\");\n#endif\n\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeo == null) {\n\t\t\t\tLog.Error($\"TimedTrafficLightsStep.AddSegment({segmentId}, {startNode}, {makeRed}): No geometry information available for segment {segmentId}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegmentEndGeometry endGeo = segGeo.GetEnd(startNode);\n\t\t\tif (endGeo == null) {\n\t\t\t\tLog.Error($\"TimedTrafficLightsStep.AddSegment({segmentId}, {startNode}, {makeRed}): No end geometry information available for segment {segmentId} @ {startNode}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (endGeo.NodeId() != timedNode.NodeId) {\n\t\t\t\tLog.Error($\"TimedTrafficLightsStep.AddSegment({segmentId}, {startNode}, {makeRed}): Segment {segmentId} is not connected to node {timedNode.NodeId} @ start {startNode}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tICustomSegmentLightsManager customSegLightsMan = Constants.ManagerFactory.CustomSegmentLightsManager;\n\n\t\t\tICustomSegmentLights liveLights = customSegLightsMan.GetOrLiveSegmentLights(segmentId, startNode);\n\t\t\tliveLights.Housekeeping(true, true);\n\n\t\t\tICustomSegmentLights clonedLights = liveLights.Clone(this);\n\n\t\t\tCustomSegmentLights.Add(segmentId, clonedLights);\n\t\t\tif (makeRed)\n\t\t\t\tCustomSegmentLights[segmentId].MakeRed();\n\t\t\telse\n\t\t\t\tCustomSegmentLights[segmentId].MakeRedOrGreen();\n\t\t\treturn customSegLightsMan.ApplyLightModes(segmentId, startNode, clonedLights);\n\t\t}\n\n\t\tpublic bool SetSegmentLights(ushort nodeId, ushort segmentId, ICustomSegmentLights lights) {\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.SetSegmentLights({nodeId}, {segmentId}, {lights}) called.\");\n#endif\n\n\t\t\tif (nodeId != timedNode.NodeId) {\n\t\t\t\tLog.Warning($\"TimedTrafficLightsStep.SetSegmentLights({nodeId}, {segmentId}, {lights}): TTL @ node {timedNode.NodeId} does not handle custom traffic lights for node {nodeId}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn SetSegmentLights(segmentId, lights);\n\t\t}\n\n\t\tpublic bool SetSegmentLights(ushort segmentId, ICustomSegmentLights lights) {\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.SetSegmentLights({segmentId}, {lights}) called.\");\n#endif\n\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeo == null) {\n\t\t\t\tLog.Error($\"TimedTrafficLightsStep.SetSegmentLights: No geometry information available for target segment {segmentId}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (segGeo.StartNodeId() != timedNode.NodeId && segGeo.EndNodeId() != timedNode.NodeId) {\n\t\t\t\tLog.Error($\"TimedTrafficLightsStep.SetSegmentLights: Segment {segmentId} is not connected to node {timedNode.NodeId}\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool startNode = segGeo.StartNodeId() == timedNode.NodeId;\n\t\t\tConstants.ManagerFactory.CustomSegmentLightsManager.GetOrLiveSegmentLights(segmentId, startNode).Housekeeping(true, true);\n\t\t\tlights.Relocate(segmentId, startNode, this);\n\t\t\tCustomSegmentLights[segmentId] = lights;\n\t\t\tLog._Debug($\"TimedTrafficLightsStep.SetSegmentLights: Set lights @ seg. {segmentId}, node {timedNode.NodeId}\");\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic short ClockwiseIndexOfSegmentEnd(ISegmentEndId endId) {\n#if DEBUGTTL\n\t\t\tif (GlobalConfig.Instance.Debug.Switches[7] && GlobalConfig.Instance.Debug.NodeId == timedNode.NodeId)\n\t\t\t\tLog._Debug($\"TimedTrafficLightsStep.ClockwiseIndexOfSegmentEnd({endId}) called.\");\n#endif\n\t\t\tSegmentEndGeometry endGeo = SegmentEndGeometry.Get(endId);\n\n\t\t\tif (endGeo == null) {\n\t\t\t\tLog.Warning($\"TimedTrafficLightsStep.ClockwiseIndexOfSegmentEnd: @ node {timedNode.NodeId}: No segment end geometry found for end id {endId}\");\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tif (endGeo.NodeId() != timedNode.NodeId) {\n\t\t\t\tLog.Warning($\"TimedTrafficLightsStep.ClockwiseIndexOfSegmentEnd: @ node {timedNode.NodeId} does not handle custom traffic lights for node {endGeo.NodeId()}\");\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tif (CustomSegmentLights.ContainsKey(endId.SegmentId)) {\n\t\t\t\tLog.Warning($\"TimedTrafficLightsStep.ClockwiseIndexOfSegmentEnd: @ node {timedNode.NodeId} does not handle custom traffic lights for segment {endId.SegmentId}\");\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tshort index = Constants.ManagerFactory.CustomSegmentLightsManager.ClockwiseIndexOfSegmentEnd(endId);\n\t\t\tindex += timedNode.RotationOffset;\n\t\t\treturn (short)(index % (endGeo.NumConnectedSegments + 1));\n\t\t}\n\n\t\t// TODO IMPROVE THIS! Liskov substitution principle must hold.\n\t\tpublic ICustomSegmentLights GetSegmentLights(ushort segmentId, bool startNode, bool add = true, RoadBaseAI.TrafficLightState lightState = RoadBaseAI.TrafficLightState.Red) {\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\t\t// TODO IMPROVE THIS! Liskov substitution principle must hold.\n\t\tpublic ICustomSegmentLights GetOrLiveSegmentLights(ushort segmentId, bool startNode) {\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\t\t// TODO IMPROVE THIS! Liskov substitution principle must hold.\n\t\tpublic bool ApplyLightModes(ushort segmentId, bool startNode, ICustomSegmentLights otherLights) {\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\t\t// TODO IMPROVE THIS! Liskov substitution principle must hold.\n\t\tpublic void SetLightMode(ushort segmentId, bool startNode, ExtVehicleType vehicleType, LightMode mode) {\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\t\t// TODO IMPROVE THIS! Liskov substitution principle must hold.\n\t\tpublic void AddNodeLights(ushort nodeId) {\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\t\t// TODO IMPROVE THIS! Liskov substitution principle must hold.\n\t\tpublic void RemoveNodeLights(ushort nodeId) {\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\t\t// TODO IMPROVE THIS! Liskov substitution principle must hold.\n\t\tvoid ICustomSegmentLightsManager.RemoveSegmentLights(ushort segmentId) {\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\t\t// TODO IMPROVE THIS! Liskov substitution principle must hold.\n\t\tpublic void RemoveSegmentLight(ushort segmentId, bool startNode) {\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\n\t\t// TODO IMPROVE THIS! Liskov substitution principle must hold.\n\t\tpublic bool IsSegmentLight(ushort segmentId, bool startNode) {\n\t\t\tthrow new NotImplementedException();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficLight/LightMode.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.TrafficLight {\n\tpublic enum LightMode {\n\t\tSimple = 1, // <^>\n\t\tSingleLeft = 2, // <, ^>\n\t\tSingleRight = 3, // <^, >\n\t\tAll = 4 // <, ^, >\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficLight/README.md",
    "content": "# TM:PE -- /TrafficLight\nCustom traffic light data structures.\n## Classes\n- **CustomSegment**: Holds references to both possible custom traffic lights at a segment\n- **CustomSegmentLight**: Stores a fully-directional traffic light state (red, yellow, green for left, forward, right) for one lane at a specific segment end.\n- **CustomSegmentLights**: Holds a set of fully-directional traffic light states for all incoming lanes at a segment end.\n- **TimedTrafficLights**: Holds the timed traffic light program for one specific junction or a set of junctions. Holds all timed steps that are defined by the player (Steps). Implements the timed traffic light program simulation (SimulationStep). (TODO: rework needed)\n- **TimedTrafficLightsStep**: Represents one player-defined timed step for one junction, including minimum/maximum time (minTime, maxTime) and cached number of flowing/waiting vehicles (minFlow, maxWait).\n- **TrafficLightSimulation**: Main class representing either a manual or timed traffic light at a junction."
  },
  {
    "path": "TLM/TLM/TrafficLight/StepChangeMetric.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.TrafficLight {\n\tpublic enum StepChangeMetric {\n\t\t/// <summary>\n\t\t/// Step is changed based on flow/wait comparison\n\t\t/// </summary>\n\t\tDefault,\n\t\t/// <summary>\n\t\t/// Step is changed on first flow detection\n\t\t/// </summary>\n\t\tFirstFlow,\n\t\t/// <summary>\n\t\t/// Step is changed on first wait detection\n\t\t/// </summary>\n\t\tFirstWait,\n\t\t/// <summary>\n\t\t/// Step is changed if no vehicle is moving\n\t\t/// </summary>\n\t\tNoFlow,\n\t\t/// <summary>\n\t\t/// Step is changed if no vehicle is waiting\n\t\t/// </summary>\n\t\tNoWait,\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficLight/TrafficLightSimulationType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.TrafficLight {\n\tpublic enum TrafficLightSimulationType {\n\t\tNone,\n\t\tManual,\n\t\tTimed\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficManager.cs",
    "content": "﻿using GenericGameBridge.Factory;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Manager;\nusing UnityEngine;\n\nnamespace TrafficManager {\n\t/// <summary>\n\t/// Helper class to make Traffic Manager services available in-game\n\t/// </summary>\n\tpublic class TrafficManager : MonoBehaviour {\n\t\tprivate const string GameObjectName = \"TMPE\";\n\n\t\tpublic IServiceFactory ServiceFactory {\n\t\t\tget {\n\t\t\t\treturn Constants.ServiceFactory;\n\t\t\t}\n\t\t}\n\n\t\tpublic IManagerFactory ManagerFactory {\n\t\t\tget {\n\t\t\t\treturn Constants.ManagerFactory;\n\t\t\t}\n\t\t}\n\n\t\t/*public void Initialize() {\n\t\t\tGameObject gameObject = new GameObject(GameObjectName);\n\t\t}\n\n\t\tpublic static void Dispose() {\n\t\t\tvar gameObject = GameObject.Find(GameObjectName);\n\t\t\tif (gameObject != null) {\n\t\t\t\tDestroy(gameObject);\n\t\t\t}\n\t\t}*/\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficManagerMod.cs",
    "content": "using CSUtil.Commons;\nusing ICities;\nusing System.Reflection;\nusing System.Runtime.CompilerServices;\nusing ColossalFramework;\nusing ColossalFramework.UI;\nusing TrafficManager.State;\nusing TrafficManager.Util;\nusing UnityEngine;\n\nnamespace TrafficManager {\n\tpublic class TrafficManagerMod : IUserMod {\n\n\t\tpublic static readonly string Version = \"10.18\";\n\n\t\tpublic static readonly uint GameVersion = 180609552u;\n\t\tpublic static readonly uint GameVersionA = 1u;\n\t\tpublic static readonly uint GameVersionB = 11u;\n\t\tpublic static readonly uint GameVersionC = 1u;\n\t\tpublic static readonly uint GameVersionBuild = 2u;\n\n\t\tpublic string Name => \"TM:PE \" + Version;\n\n\t\tpublic string Description => \"Manage your city's traffic\";\n\n\t\tpublic void OnEnabled() {\n\t\t\tLog.Info($\"TM:PE enabled. Version {Version}, Build {Assembly.GetExecutingAssembly().GetName().Version} for game version {GameVersionA}.{GameVersionB}.{GameVersionC}-f{GameVersionBuild}\");\n\t\t\tif (UIView.GetAView() != null) {\n\t\t\t\tOnGameIntroLoaded();\n\t\t\t} else {\n\t\t\t\tLoadingManager.instance.m_introLoaded += OnGameIntroLoaded;\n\t\t\t}\n\t\t}\n\n\t\tpublic void OnDisabled() {\n\t\t\tLog.Info(\"TM:PE disabled.\");\n\t\t\tLoadingManager.instance.m_introLoaded -= OnGameIntroLoaded;\n\t\t}\n\n\t\tpublic void OnSettingsUI(UIHelperBase helper) {\n\t\t\tOptions.makeSettings(helper);\n\t\t}\n\n\t\tprivate static void OnGameIntroLoaded() {\n\t\t\tModsCompatibilityChecker mcc = new ModsCompatibilityChecker();\n\t\t\tmcc.PerformModCheck();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/TrafficManagerMode.cs",
    "content": "namespace TrafficManager\n{\n    public enum TrafficManagerMode\n    {\n        None = 0,\n        Activated = 1\n    }\n}\n"
  },
  {
    "path": "TLM/TLM/UI/CustomKeyHandler.cs",
    "content": "using ColossalFramework.UI;\nusing CSUtil.Commons;\nusing UnityEngine;\n\nnamespace TrafficManager.UI {\n    public class CustomKeyHandler : UICustomControl {\n        //TODO add more key bindings or refactor to mod key shortcut manager\n        \n        public void OnKeyDown(UIComponent comp, UIKeyEventParameter p)\n        {\n            if (p.used || p.keycode != KeyCode.Escape)\n                return;\n            Log._Debug(\"CustomKeyHandler::OnKeyDown(KeyCode.Escape) call\");\n            p.Use();\n        }\n    }\n}"
  },
  {
    "path": "TLM/TLM/UI/IncompatibleModsPanel.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing ColossalFramework;\nusing ColossalFramework.Globalization;\nusing ColossalFramework.PlatformServices;\nusing ColossalFramework.Plugins;\nusing ColossalFramework.UI;\nusing CSUtil.Commons;\nusing UnityEngine;\n\nnamespace TrafficManager.UI {\n    public class IncompatibleModsPanel : UIPanel {\n        private UILabel title;\n        private UIButton closeButton;\n        private UISprite warningIcon;\n        private UIPanel mainPanel;\n        private UIComponent blurEffect;\n        private static IncompatibleModsPanel _instance;\n\n        public Dictionary<ulong, string> IncompatibleMods { get; set; }\n\n        public void Initialize() {\n            Log._Debug(\"IncompatibleModsPanel initialize\");\n            if (mainPanel != null) {\n                mainPanel.OnDestroy();\n            }\n\n            isVisible = true;\n\n            mainPanel = AddUIComponent<UIPanel>();\n            mainPanel.backgroundSprite = \"UnlockingPanel2\";\n            mainPanel.color = new Color32(75, 75, 135, 255);\n            width = 600;\n            height = 440;\n            mainPanel.width = 600;\n            mainPanel.height = 440;\n\n            Vector2 resolution = UIView.GetAView().GetScreenResolution();\n            relativePosition = new Vector3(resolution.x / 2 - 300, resolution.y / 3);\n            mainPanel.relativePosition = Vector3.zero;\n\n            warningIcon = mainPanel.AddUIComponent<UISprite>();\n            warningIcon.size = new Vector2(40f, 40f);\n            warningIcon.spriteName = \"IconWarning\";\n            warningIcon.relativePosition = new Vector3(15, 15);\n            warningIcon.zOrder = 0;\n\n            title = mainPanel.AddUIComponent<UILabel>();\n            title.autoSize = true;\n            title.padding = new RectOffset(10, 10, 15, 15);\n            title.relativePosition = new Vector2(60, 12);\n            title.text = \"Traffic Manager detected incompatible mods\";\n\n            closeButton = mainPanel.AddUIComponent<UIButton>();\n            closeButton.eventClick += CloseButtonClick;\n            closeButton.relativePosition = new Vector3(width - closeButton.width - 45, 15f);\n            closeButton.normalBgSprite = \"buttonclose\";\n            closeButton.hoveredBgSprite = \"buttonclosehover\";\n            closeButton.pressedBgSprite = \"buttonclosepressed\";\n\n            UIPanel panel = mainPanel.AddUIComponent<UIPanel>();\n            panel.relativePosition = new Vector2(20, 70);\n            panel.size = new Vector2(565, 320);\n\n            UIScrollablePanel scrollablePanel = panel.AddUIComponent<UIScrollablePanel>();\n            scrollablePanel.backgroundSprite = \"\";\n            scrollablePanel.size = new Vector2(550, 340);\n            scrollablePanel.relativePosition = new Vector3(0, 0);\n            scrollablePanel.clipChildren = true;\n            if (IncompatibleMods.Count != 0) {\n                int acc = 0;\n                UIPanel item;\n                IncompatibleMods.ForEach((pair) => {\n                    item = CreateEntry(ref scrollablePanel, pair.Value, pair.Key);\n                    item.relativePosition = new Vector2(0, acc);\n                    item.size = new Vector2(560, 50);\n                    acc += 50;\n                });\n                item = null;\n            }\n\n            scrollablePanel.FitTo(panel);\n            scrollablePanel.scrollWheelDirection = UIOrientation.Vertical;\n            scrollablePanel.builtinKeyNavigation = true;\n\n            UIScrollbar verticalScroll = panel.AddUIComponent<UIScrollbar>();\n            verticalScroll.stepSize = 1;\n            verticalScroll.relativePosition = new Vector2(panel.width - 15, 0);\n            verticalScroll.orientation = UIOrientation.Vertical;\n            verticalScroll.size = new Vector2(20, 320);\n            verticalScroll.incrementAmount = 25;\n            verticalScroll.scrollEasingType = EasingType.BackEaseOut;\n\n            scrollablePanel.verticalScrollbar = verticalScroll;\n\n            UISlicedSprite track = verticalScroll.AddUIComponent<UISlicedSprite>();\n            track.spriteName = \"ScrollbarTrack\";\n            track.relativePosition = Vector3.zero;\n            track.size = new Vector2(16, 320);\n\n            verticalScroll.trackObject = track;\n\n            UISlicedSprite thumb = track.AddUIComponent<UISlicedSprite>();\n            thumb.spriteName = \"ScrollbarThumb\";\n            thumb.autoSize = true;\n            thumb.relativePosition = Vector3.zero;\n            verticalScroll.thumbObject = thumb;\n\n\n            blurEffect = GameObject.Find(\"ModalEffect\").GetComponent<UIComponent>();\n            AttachUIComponent(blurEffect.gameObject);\n            blurEffect.size = new Vector2(resolution.x, resolution.y);\n            blurEffect.absolutePosition = new Vector3(0, 0);\n            blurEffect.SendToBack();\n            if (blurEffect != null) {\n                blurEffect.isVisible = true;\n                ValueAnimator.Animate(\"ModalEffect\", delegate(float val) { blurEffect.opacity = val; }, new AnimatedFloat(0f, 1f, 0.7f, EasingType.CubicEaseOut));\n            }\n\n            BringToFront();\n        }\n\n        private void CloseButtonClick(UIComponent component, UIMouseEventParameter eventparam) {\n            closeButton.eventClick -= CloseButtonClick;\n            TryPopModal();\n            Hide();\n            Unfocus();\n            eventparam.Use();\n        }\n\n        private UIPanel CreateEntry(ref UIScrollablePanel parent, string name, ulong steamId) {\n            UIPanel panel = parent.AddUIComponent<UIPanel>();\n            panel.size = new Vector2(560, 50);\n            panel.backgroundSprite = \"ContentManagerItemBackground\";\n            UILabel label = panel.AddUIComponent<UILabel>();\n            label.text = name;\n            label.textAlignment = UIHorizontalAlignment.Left;\n            label.relativePosition = new Vector2(10, 15);\n            CreateButton(panel, \"Unsubscribe\", (int) panel.width - 170, 10, delegate(UIComponent component, UIMouseEventParameter param) { UnsubscribeClick(component, param, steamId); });\n            return panel;\n        }\n\n        private void UnsubscribeClick(UIComponent component, UIMouseEventParameter eventparam, ulong steamId) {\n            Log.Info(\"Trying to unsubscribe workshop item \" + steamId);\n            component.isEnabled = false;\n            if (PlatformService.workshop.Unsubscribe(new PublishedFileId(steamId))) {\n                IncompatibleMods.Remove(steamId);\n                component.parent.Disable();\n                component.isVisible = false;\n                Log.Info(\"Workshop item \" + steamId + \" unsubscribed\");\n            } else {\n                Log.Warning(\"Failed unsubscribing workshop item \" + steamId);\n                component.isEnabled = true;\n            }\n        }\n\n        private UIButton CreateButton(UIComponent parent, string text, int x, int y, MouseEventHandler eventClick) {\n            var button = parent.AddUIComponent<UIButton>();\n            button.textScale = 0.8f;\n            button.width = 150f;\n            button.height = 30;\n            button.normalBgSprite = \"ButtonMenu\";\n            button.disabledBgSprite = \"ButtonMenuDisabled\";\n            button.hoveredBgSprite = \"ButtonMenuHovered\";\n            button.focusedBgSprite = \"ButtonMenu\";\n            button.pressedBgSprite = \"ButtonMenuPressed\";\n            button.textColor = new Color32(255, 255, 255, 255);\n            button.playAudioEvents = true;\n            button.text = text;\n            button.relativePosition = new Vector3(x, y);\n            button.eventClick += eventClick;\n\n            return button;\n        }\n\n        private void OnEnable() {\n            Log._Debug(\"IncompatibleModsPanel enabled\");\n            PlatformService.workshop.eventUGCQueryCompleted += OnQueryCompleted;\n            Singleton<PluginManager>.instance.eventPluginsChanged += OnPluginsChanged;\n            Singleton<PluginManager>.instance.eventPluginsStateChanged += OnPluginsChanged;\n            LocaleManager.eventLocaleChanged += OnLocaleChanged;\n        }\n\n        private void OnQueryCompleted(UGCDetails result, bool ioerror) {\n            Log._Debug(\"IncompatibleModsPanel.OnQueryCompleted() - \" + result.result.ToString(\"D\") + \" IO error?:\" + ioerror);\n        }\n\n        private void OnPluginsChanged() {\n            Log._Debug(\"IncompatibleModsPanel.OnPluginsChanged() - Plugins changed\");\n        }\n\n        private void OnDisable() {\n            Log._Debug(\"IncompatibleModsPanel disabled\");\n            PlatformService.workshop.eventUGCQueryCompleted -= this.OnQueryCompleted;\n            Singleton<PluginManager>.instance.eventPluginsChanged -= this.OnPluginsChanged;\n            Singleton<PluginManager>.instance.eventPluginsStateChanged -= this.OnPluginsChanged;\n            LocaleManager.eventLocaleChanged -= this.OnLocaleChanged;\n        }\n\n        protected override void OnKeyDown(UIKeyEventParameter p) {\n            if (Input.GetKey(KeyCode.Escape) || Input.GetKey(KeyCode.Return)) {\n                TryPopModal();\n                p.Use();\n                Hide();\n                Unfocus();\n            }\n\n            base.OnKeyDown(p);\n        }\n\n        private void TryPopModal() {\n            if (UIView.HasModalInput()) {\n                UIView.PopModal();\n                UIComponent component = UIView.GetModalComponent();\n                if (component != null) {\n                    UIView.SetFocus(component);\n                }\n            }\n\n            if (blurEffect != null && UIView.ModalInputCount() == 0) {\n                ValueAnimator.Animate(\"ModalEffect\", delegate(float val) { blurEffect.opacity = val; }, new AnimatedFloat(1f, 0f, 0.7f, EasingType.CubicEaseOut), delegate() { blurEffect.Hide(); });\n            }\n        }\n    }\n}"
  },
  {
    "path": "TLM/TLM/UI/LinearSpriteButton.cs",
    "content": "﻿using ColossalFramework.Math;\nusing ColossalFramework.UI;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.State;\nusing TrafficManager.Util;\nusing UnityEngine;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic abstract class LinearSpriteButton : UIButton {\n\t\tpublic enum ButtonMouseState {\n\t\t\tBase,\n\t\t\tHovered,\n\t\t\tMouseDown\n\t\t}\n\n\t\tpublic const string MENU_BUTTON_BACKGROUND = \"Bg\";\n\t\tpublic const string MENU_BUTTON_FOREGROUND = \"Fg\";\n\n\t\tpublic const string MENU_BUTTON_BASE = \"Base\";\n\t\tpublic const string MENU_BUTTON_HOVERED = \"Hovered\";\n\t\tpublic const string MENU_BUTTON_MOUSEDOWN = \"MouseDown\";\n\n\t\tpublic const string MENU_BUTTON_DEFAULT = \"Default\";\n\t\tpublic const string MENU_BUTTON_ACTIVE = \"Active\";\n\n\t\tprotected static string GetButtonBackgroundTextureId(string prefix, ButtonMouseState state, bool active) {\n\t\t\tstring ret = prefix + MENU_BUTTON_BACKGROUND;\n\n\t\t\tswitch (state) {\n\t\t\t\tcase ButtonMouseState.Base:\n\t\t\t\t\tret += MENU_BUTTON_BASE;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ButtonMouseState.Hovered:\n\t\t\t\t\tret += MENU_BUTTON_HOVERED;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ButtonMouseState.MouseDown:\n\t\t\t\t\tret += MENU_BUTTON_MOUSEDOWN;\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tret += active ? MENU_BUTTON_ACTIVE : MENU_BUTTON_DEFAULT;\n\t\t\treturn ret;\n\t\t}\n\n\t\tprotected static string GetButtonForegroundTextureId(string prefix, string function, bool active) {\n\t\t\tstring ret = prefix + MENU_BUTTON_FOREGROUND + function;\n\t\t\tret += active ? MENU_BUTTON_ACTIVE : MENU_BUTTON_DEFAULT;\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic abstract bool CanActivate();\n\t\tpublic abstract string ButtonName { get; }\n\t\tpublic abstract string FunctionName { get; }\n\t\tpublic abstract string[] FunctionNames { get; }\n\t\tpublic abstract Texture2D AtlasTexture { get; }\n\n\t\tpublic abstract int Width { get; }\n\t\tpublic abstract int Height { get; }\n\n\t\tpublic override void Start() {\n\t\t\tstring[] textureIds = new string[Enum.GetValues(typeof(ButtonMouseState)).Length * (CanActivate() ? 2 : 1) + FunctionNames.Length * 2];\n\n\t\t\tint i = 0;\n\t\t\tforeach (ButtonMouseState mouseState in EnumUtil.GetValues<ButtonMouseState>()) {\n\t\t\t\tif (CanActivate()) {\n\t\t\t\t\ttextureIds[i++] = GetButtonBackgroundTextureId(ButtonName, mouseState, true);\n\t\t\t\t}\n\t\t\t\ttextureIds[i++] = GetButtonBackgroundTextureId(ButtonName, mouseState, false);\n\t\t\t}\n\n\t\t\tforeach (string function in FunctionNames) {\n\t\t\t\ttextureIds[i++] = GetButtonForegroundTextureId(ButtonName, function, false);\n\t\t\t}\n\n\t\t\tforeach (string function in FunctionNames) {\n\t\t\t\ttextureIds[i++] = GetButtonForegroundTextureId(ButtonName, function, true);\n\t\t\t}\n\n\t\t\t// Set the atlases for background/foreground\n\t\t\tatlas = TextureUtil.GenerateLinearAtlas(\"TMPE_\" + ButtonName + \"Atlas\", AtlasTexture, textureIds.Length, textureIds);\n\n\t\t\tm_ForegroundSpriteMode = UIForegroundSpriteMode.Scale;\n\t\t\tUpdateProperties();\n\n\t\t\t// Enable button sounds.\n\t\t\tplayAudioEvents = true;\n\t\t}\n\n\t\tpublic abstract bool Active { get; }\n\t\tpublic abstract string Tooltip { get; }\n\t\tpublic abstract bool Visible { get; }\n\t\tpublic abstract void HandleClick(UIMouseEventParameter p);\n\n\t\tprotected override void OnClick(UIMouseEventParameter p) {\n\t\t\tHandleClick(p);\n\t\t\tUpdateProperties();\n\t\t}\n\n\t\tinternal void UpdateProperties() {\n\t\t\tbool active = CanActivate() ? Active : false;\n\n\t\t\tm_BackgroundSprites.m_Normal = m_BackgroundSprites.m_Disabled = m_BackgroundSprites.m_Focused = GetButtonBackgroundTextureId(ButtonName, ButtonMouseState.Base, active);\n\t\t\tm_BackgroundSprites.m_Hovered = GetButtonBackgroundTextureId(ButtonName, ButtonMouseState.Hovered, active);\n\t\t\tm_PressedBgSprite = GetButtonBackgroundTextureId(ButtonName, ButtonMouseState.MouseDown, active);\n\n\t\t\tm_ForegroundSprites.m_Normal = m_ForegroundSprites.m_Disabled = m_ForegroundSprites.m_Focused = GetButtonForegroundTextureId(ButtonName, FunctionName, active);\n\t\t\tm_ForegroundSprites.m_Hovered = m_PressedFgSprite = GetButtonForegroundTextureId(ButtonName, FunctionName, true);\n\n\t\t\ttooltip = Translation.GetString(Tooltip);\n\t\t\tisVisible = Visible;\n\t\t\tthis.Invalidate();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/ClearTrafficButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class ClearTrafficButton : MenuButton {\n\t\tpublic override bool Active {\n\t\t\tget {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tpublic override ButtonFunction Function {\n\t\t\tget {\n\t\t\t\treturn ButtonFunction.ClearTraffic;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string Tooltip {\n\t\t\tget {\n\t\t\t\treturn \"Clear_Traffic\";\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool Visible {\n\t\t\tget {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnClickInternal(UIMouseEventParameter p) {\n\t\t\tConfirmPanel.ShowModal(Translation.GetString(\"Clear_Traffic\"), Translation.GetString(\"Clear_Traffic\") + \"?\", delegate (UIComponent comp, int ret) {\n\t\t\t\tif (ret == 1) {\n\t\t\t\t\tConstants.ServiceFactory.SimulationService.AddAction(() => {\n\t\t\t\t\t\tUtilityManager.Instance.ClearTraffic();\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tUIBase.GetTrafficManagerTool(true).SetToolMode(ToolMode.None);\n\t\t\t});\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/DebugMenu.cs",
    "content": "#define QUEUEDSTATSx\n\nusing System;\nusing System.Linq;\nusing ColossalFramework;\nusing ColossalFramework.UI;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing UnityEngine;\nusing TrafficManager.State;\nusing TrafficManager.Custom.PathFinding;\nusing System.Collections.Generic;\nusing TrafficManager.Manager;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Util;\nusing CSUtil.Commons.Benchmark;\n\nnamespace TrafficManager.UI {\n#if DEBUG\n\tpublic class DebugMenuPanel : UIPanel {\n\t\t//private static UIState _uiState = UIState.None;\n\n#if QUEUEDSTATS\n\t\tprivate static bool showPathFindStats = false;\n#endif\n\n\t\t/*private static UIButton _buttonSwitchTraffic;\n\t\tprivate static UIButton _buttonPrioritySigns;\n\t\tprivate static UIButton _buttonManualControl;\n\t\tprivate static UIButton _buttonTimedMain;\n\t\tprivate static UIButton _buttonLaneChange;\n\t\tprivate static UIButton _buttonLaneConnector;\n\t\tprivate static UIButton _buttonVehicleRestrictions;\n\t\tprivate static UIButton _buttonJunctionRestrictions;\n\t\tprivate static UIButton _buttonSpeedLimits;\n\t\tprivate static UIButton _buttonClearTraffic;\n\t\tprivate static UIButton _buttonToggleDespawn;*/\n#if DEBUG\n\t\tprivate static UITextField _goToField = null;\n\t\tprivate static UIButton _goToSegmentButton = null;\n\t\tprivate static UIButton _goToNodeButton = null;\n\t\tprivate static UIButton _goToVehicleButton = null;\n\t\tprivate static UIButton _goToParkedVehicleButton = null;\n\t\tprivate static UIButton _goToBuildingButton = null;\n\t\tprivate static UIButton _goToCitizenInstanceButton = null;\n\t\tprivate static UIButton _goToPosButton = null;\n\t\tprivate static UIButton _printDebugInfoButton = null;\n\t\tprivate static UIButton _reloadConfigButton = null;\n\t\tprivate static UIButton _recalcLinesButton = null;\n\t\tprivate static UIButton _checkDetoursButton = null;\n\t\tprivate static UIButton _noneToVehicleButton = null;\n\t\tprivate static UIButton _vehicleToNoneButton = null;\n\t\tprivate static UIButton _printFlagsDebugInfoButton = null;\n\t\tprivate static UIButton _printBenchmarkReportButton = null;\n\t\tprivate static UIButton _resetBenchmarksButton = null;\n#endif\n\n#if QUEUEDSTATS\n\t\tprivate static UIButton _togglePathFindStatsButton = null;\n#endif\n\n\t\tpublic static UILabel title;\n\n\t\tpublic override void Start() {\n\t\t\tisVisible = false;\n\n\t\t\tbackgroundSprite = \"GenericPanel\";\n\t\t\tcolor = new Color32(75, 75, 135, 255);\n\t\t\twidth = Translation.getMenuWidth();\n\t\t\theight = 30;\n\n\t\t\t//height = LoadingExtension.IsPathManagerCompatible ? 430 : 230;\n\t\t\tVector2 resolution = UIView.GetAView().GetScreenResolution();\n\t\t\trelativePosition = new Vector3(resolution.x - Translation.getMenuWidth() - 30f, 65f);\n\n\t\t\ttitle = AddUIComponent<UILabel>();\n\t\t\ttitle.text = \"Version \" + TrafficManagerMod.Version;\n\t\t\ttitle.relativePosition = new Vector3(50.0f, 5.0f);\n\n\t\t\tint y = 30;\n\n\t\t\t/*_buttonSwitchTraffic = _createButton(Translation.GetString(\"Switch_traffic_lights\"), y, clickSwitchTraffic);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\n\t\t\tif (Options.prioritySignsEnabled) {\n\t\t\t\t_buttonPrioritySigns = _createButton(Translation.GetString(\"Add_priority_signs\"), y, clickAddPrioritySigns);\n\t\t\t\ty += 40;\n\t\t\t\theight += 40;\n\t\t\t}\n\n\t\t\t_buttonManualControl = _createButton(Translation.GetString(\"Manual_traffic_lights\"), y, clickManualControl);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\n\t\t\tif (Options.timedLightsEnabled) {\n\t\t\t\t_buttonTimedMain = _createButton(Translation.GetString(\"Timed_traffic_lights\"), y, clickTimedAdd);\n\t\t\t\ty += 40;\n\t\t\t\theight += 40;\n\t\t\t}\n\n\t\t\t\n\t\t\t_buttonLaneChange = _createButton(Translation.GetString(\"Change_lane_arrows\"), y, clickChangeLanes);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\n\t\t\tif (Options.laneConnectorEnabled) {\n\t\t\t\t_buttonLaneConnector = _createButton(Translation.GetString(\"Lane_connector\"), y, clickLaneConnector);\n\t\t\t\ty += 40;\n\t\t\t\theight += 40;\n\t\t\t}\n\n\t\t\tif (Options.customSpeedLimitsEnabled) {\n\t\t\t\t_buttonSpeedLimits = _createButton(Translation.GetString(\"Speed_limits\"), y, clickSpeedLimits);\n\t\t\t\ty += 40;\n\t\t\t\theight += 40;\n\t\t\t}\n\n\t\t\tif (Options.vehicleRestrictionsEnabled) {\n\t\t\t\t_buttonVehicleRestrictions = _createButton(Translation.GetString(\"Vehicle_restrictions\"), y, clickVehicleRestrictions);\n\t\t\t\ty += 40;\n\t\t\t\theight += 40;\n\t\t\t}\n\n\t\t\tif (Options.junctionRestrictionsEnabled) {\n\t\t\t\t_buttonJunctionRestrictions = _createButton(Translation.GetString(\"Junction_restrictions\"), y, clickJunctionRestrictions);\n\t\t\t\ty += 40;\n\t\t\t\theight += 40;\n\t\t\t}\n\n\t\t\t_buttonClearTraffic = _createButton(Translation.GetString(\"Clear_Traffic\"), y, clickClearTraffic);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\n\t\t\t\n\t\t\t_buttonToggleDespawn = _createButton(Options.enableDespawning ? Translation.GetString(\"Disable_despawning\") : Translation.GetString(\"Enable_despawning\"), y, ClickToggleDespawn);\n\t\t\ty += 40;\n\t\t\theight += 40;*/\n\n#if DEBUG\n\t\t\t_goToField = CreateTextField(\"\", y);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_goToPosButton = _createButton(\"Goto position\", y, clickGoToPos);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_goToSegmentButton = _createButton(\"Goto segment\", y, clickGoToSegment);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_goToNodeButton = _createButton(\"Goto node\", y, clickGoToNode);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_goToVehicleButton = _createButton(\"Goto vehicle\", y, clickGoToVehicle);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_goToParkedVehicleButton = _createButton(\"Goto parked vehicle\", y, clickGoToParkedVehicle);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_goToBuildingButton = _createButton(\"Goto building\", y, clickGoToBuilding);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_goToCitizenInstanceButton = _createButton(\"Goto citizen inst.\", y, clickGoToCitizenInstance);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_printDebugInfoButton = _createButton(\"Print debug info\", y, clickPrintDebugInfo);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_reloadConfigButton = _createButton(\"Reload configuration\", y, clickReloadConfig);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_recalcLinesButton = _createButton(\"Recalculate transport lines\", y, clickRecalcLines);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_checkDetoursButton = _createButton(\"Remove all parked vehicles\", y, clickCheckDetours);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t/*_noneToVehicleButton = _createButton(\"None -> Vehicle\", y, clickNoneToVehicle);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_vehicleToNoneButton = _createButton(\"Vehicle -> None\", y, clickVehicleToNone);\n\t\t\ty += 40;\n\t\t\theight += 40;*/\n#endif\n#if QUEUEDSTATS\n\t\t\t_togglePathFindStatsButton = _createButton(\"Toggle PathFind stats\", y, clickTogglePathFindStats);\n\t\t\ty += 40;\n\t\t\theight += 40;\n#endif\n#if DEBUG\n\t\t\t_printFlagsDebugInfoButton = _createButton(\"Print flags debug info\", y, clickPrintFlagsDebugInfo);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_printBenchmarkReportButton = _createButton(\"Print benchmark report\", y, clickPrintBenchmarkReport);\n\t\t\ty += 40;\n\t\t\theight += 40;\n\t\t\t_resetBenchmarksButton = _createButton(\"Reset benchmarks\", y, clickResetBenchmarks);\n\t\t\ty += 40;\n\t\t\theight += 40;\n#endif\n\t\t}\n\n\t\tprivate UITextField CreateTextField(string str, int y) {\n\t\t\tUITextField textfield = AddUIComponent<UITextField>();\n\t\t\ttextfield.relativePosition = new Vector3(15f, y);\n\t\t\ttextfield.horizontalAlignment = UIHorizontalAlignment.Left;\n\t\t\ttextfield.text = str;\n\t\t\ttextfield.textScale = 0.8f;\n\t\t\ttextfield.color = Color.black;\n\t\t\ttextfield.cursorBlinkTime = 0.45f;\n\t\t\ttextfield.cursorWidth = 1;\n\t\t\ttextfield.selectionBackgroundColor = new Color(233, 201, 148, 255);\n\t\t\ttextfield.selectionSprite = \"EmptySprite\";\n\t\t\ttextfield.verticalAlignment = UIVerticalAlignment.Middle;\n\t\t\ttextfield.padding = new RectOffset(5, 0, 5, 0);\n\t\t\ttextfield.foregroundSpriteMode = UIForegroundSpriteMode.Fill;\n\t\t\ttextfield.normalBgSprite = \"TextFieldPanel\";\n\t\t\ttextfield.hoveredBgSprite = \"TextFieldPanelHovered\";\n\t\t\ttextfield.focusedBgSprite = \"TextFieldPanel\";\n\t\t\ttextfield.size = new Vector3(190, 30);\n\t\t\ttextfield.isInteractive = true;\n\t\t\ttextfield.enabled = true;\n\t\t\ttextfield.readOnly = false;\n\t\t\ttextfield.builtinKeyNavigation = true;\n\t\t\ttextfield.width = Translation.getMenuWidth() - 30;\n\t\t\treturn textfield;\n\t\t}\n\n\t\tprivate UIButton _createButton(string text, int y, MouseEventHandler eventClick) {\n\t\t\tvar button = AddUIComponent<UIButton>();\n\t\t\tbutton.textScale = 0.8f;\n\t\t\tbutton.width = Translation.getMenuWidth()-30;\n\t\t\tbutton.height = 30;\n\t\t\tbutton.normalBgSprite = \"ButtonMenu\";\n\t\t\tbutton.disabledBgSprite = \"ButtonMenuDisabled\";\n\t\t\tbutton.hoveredBgSprite = \"ButtonMenuHovered\";\n\t\t\tbutton.focusedBgSprite = \"ButtonMenu\";\n\t\t\tbutton.pressedBgSprite = \"ButtonMenuPressed\";\n\t\t\tbutton.textColor = new Color32(255, 255, 255, 255);\n\t\t\tbutton.playAudioEvents = true;\n\t\t\tbutton.text = text;\n\t\t\tbutton.relativePosition = new Vector3(15f, y);\n\t\t\tbutton.eventClick += delegate (UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\t\teventClick(component, eventParam);\n\t\t\t\tbutton.Invalidate();\n\t\t\t};\n\n\t\t\treturn button;\n\t\t}\n\n#if DEBUG\n\t\tprivate void clickGoToPos(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tstring[] vectorElms = _goToField.text.Split(',');\n\t\t\tif (vectorElms.Length < 2)\n\t\t\t\treturn;\n\n\t\t\tCSUtil.CameraControl.CameraController.Instance.GoToPos(new Vector3(float.Parse(vectorElms[0]), Camera.main.transform.position.y, float.Parse(vectorElms[1])));\n\t\t}\n\n\t\tprivate void clickGoToSegment(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tushort segmentId = Convert.ToUInt16(_goToField.text);\n\t\t\tif ((Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Created) != NetSegment.Flags.None) {\n\t\t\t\tCSUtil.CameraControl.CameraController.Instance.GoToSegment(segmentId);\n\t\t\t}\n\t\t}\n\n\t\tprivate void clickGoToNode(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tushort nodeId = Convert.ToUInt16(_goToField.text);\n\t\t\tif ((Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_flags & NetNode.Flags.Created) != NetNode.Flags.None) {\n\t\t\t\tCSUtil.CameraControl.CameraController.Instance.GoToNode(nodeId);\n\t\t\t}\n\t\t}\n\n\t\tprivate void clickPrintDebugInfo(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tConstants.ServiceFactory.SimulationService.AddAction(() => {\n\t\t\t\tUtilityManager.Instance.PrintDebugInfo();\n\t\t\t});\n\t\t}\n\n\t\tprivate void clickReloadConfig(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tGlobalConfig.Reload();\n\t\t}\n\n\t\tprivate void clickRecalcLines(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tSimulationManager.instance.AddAction(() => {\n\t\t\t\tfor (int i = 0; i < TransportManager.MAX_LINE_COUNT; ++i) {\n\t\t\t\t\tif (TransportManager.instance.m_lines.m_buffer[i].m_flags == TransportLine.Flags.None) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t//Log.Message(\"\\tTransport line is not created.\");\n\t\t\t\t\t}\n\t\t\t\t\tLog.Info($\"Recalculating transport line {i} now.\");\n\t\t\t\t\tif (TransportManager.instance.m_lines.m_buffer[i].UpdatePaths((ushort)i) &&\n\t\t\t\t\t\tTransportManager.instance.m_lines.m_buffer[i].UpdateMeshData((ushort)i)\n\t\t\t\t\t) {\n\t\t\t\t\t\tLog.Info($\"Transport line {i} recalculated.\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tprivate void clickCheckDetours(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tSimulationManager.instance.AddAction(() => {\n\t\t\t\tSimulationManager.instance.ForcedSimulationPaused = true;\n\t\t\t\tfor (uint i = 0; i < VehicleManager.instance.m_parkedVehicles.m_buffer.Length; ++i) {\n\t\t\t\t\tVehicleManager.instance.ReleaseParkedVehicle((ushort)i);\n\t\t\t\t}\n\t\t\t\tSimulationManager.instance.ForcedSimulationPaused = false;\n\t\t\t});\n\n\t\t\t/*Log.Info($\"Screen.width: {Screen.width} Screen.height: {Screen.height}\");\n\t\t\tLog.Info($\"Screen.currentResolution.width: {Screen.currentResolution.width} Screen.currentResolution.height: {Screen.currentResolution.height}\");\n\t\t\tVector2 resolution = UIView.GetAView().GetScreenResolution();\n\t\t\tLog.Info($\"UIView.screenResolution.width: {resolution.x} UIView.screenResolution.height: {resolution.y}\");\n\t\t\t*/\n\n\t\t\t/*SimulationManager.instance.AddAction(() => {\n\t\t\t\tPrintTransportStats();\n\t\t\t});*/\n\t\t}\n\n\t\tpublic static void PrintTransportStats() {\n\t\t\tfor (int i = 0; i < TransportManager.MAX_LINE_COUNT; ++i) {\n\t\t\t\tLog.Info(\"Transport line \" + i + \":\");\n\t\t\t\tif ((TransportManager.instance.m_lines.m_buffer[i].m_flags & TransportLine.Flags.Created) == TransportLine.Flags.None) {\n\t\t\t\t\tLog.Info(\"\\tTransport line is not created.\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tLog.Info(\"\\tFlags: \" + TransportManager.instance.m_lines.m_buffer[i].m_flags + \", cat: \" + TransportManager.instance.m_lines.m_buffer[i].Info.category + \", type: \" + TransportManager.instance.m_lines.m_buffer[i].Info.m_transportType + \", name: \" + TransportManager.instance.GetLineName((ushort)i));\n\t\t\t\tushort firstStopNodeId = TransportManager.instance.m_lines.m_buffer[i].m_stops;\n\t\t\t\tushort stopNodeId = firstStopNodeId;\n\t\t\t\tVector3 lastNodePos = Vector3.zero;\n\t\t\t\tint index = 1;\n\t\t\t\twhile (stopNodeId != 0) {\n\t\t\t\t\tVector3 pos = NetManager.instance.m_nodes.m_buffer[stopNodeId].m_position;\n\t\t\t\t\tLog.Info(\"\\tStop node #\" + index + \" -- \" + stopNodeId + \": Flags: \" + NetManager.instance.m_nodes.m_buffer[stopNodeId].m_flags + \", Transport line: \" + NetManager.instance.m_nodes.m_buffer[stopNodeId].m_transportLine + \", Problems: \" + NetManager.instance.m_nodes.m_buffer[stopNodeId].m_problems + \" Pos: \" + pos + \", Dist. to lat pos: \" + (lastNodePos - pos).magnitude);\n\t\t\t\t\tif (NetManager.instance.m_nodes.m_buffer[stopNodeId].m_problems != Notification.Problem.None) {\n\t\t\t\t\t\tLog.Warning(\"\\t*** PROBLEMS DETECTED ***\");\n\t\t\t\t\t}\n\t\t\t\t\tlastNodePos = pos;\n\n\t\t\t\t\tushort nextSegment = TransportLine.GetNextSegment(stopNodeId);\n\t\t\t\t\tif (nextSegment != 0) {\n\t\t\t\t\t\tstopNodeId = NetManager.instance.m_segments.m_buffer[(int)nextSegment].m_endNode;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t++index;\n\n\t\t\t\t\tif (stopNodeId == firstStopNodeId) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (index > 10000) {\n\t\t\t\t\t\tLog.Error(\"Too many iterations!\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static Dictionary<string, List<byte>> customEmergencyLanes = new Dictionary<string, List<byte>>();\n\n\t\tprivate void clickNoneToVehicle(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tDictionary<NetInfo, ushort> ret = new Dictionary<NetInfo, ushort>();\n\t\t\tint numLoaded = PrefabCollection<NetInfo>.LoadedCount();\n\t\t\tfor (uint i = 0; i < numLoaded; ++i) {\n\t\t\t\tNetInfo info = PrefabCollection<NetInfo>.GetLoaded(i);\n\t\t\t\tif (!(info.m_netAI is RoadBaseAI))\n\t\t\t\t\tcontinue;\n\t\t\t\tRoadBaseAI ai = (RoadBaseAI)info.m_netAI;\n\t\t\t\tif (!ai.m_highwayRules)\n\t\t\t\t\tcontinue;\n\t\t\t\tNetInfo.Lane[] laneInfos = info.m_lanes;\n\n\t\t\t\tfor (byte k = 0; k < Math.Min(2, laneInfos.Length); ++k) {\n\t\t\t\t\tNetInfo.Lane laneInfo = laneInfos[k];\n\t\t\t\t\tif (laneInfo.m_vehicleType == VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\tlaneInfo.m_vehicleType = VehicleInfo.VehicleType.Car;\n\t\t\t\t\t\tlaneInfo.m_laneType = NetInfo.LaneType.Vehicle;\n\t\t\t\t\t\tLog._Debug($\"Changing vehicle type of lane {k} @ {info.name} from None to Car, lane type from None to Vehicle\");\n\n\t\t\t\t\t\tif (!customEmergencyLanes.ContainsKey(info.name))\n\t\t\t\t\t\t\tcustomEmergencyLanes.Add(info.name, new List<byte>());\n\t\t\t\t\t\tcustomEmergencyLanes[info.name].Add(k);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\n#if QUEUEDSTATS\n\t\tprivate void clickTogglePathFindStats(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tUpdate();\n\t\t\tshowPathFindStats = !showPathFindStats;\n\t\t}\n#endif\n\n#if DEBUG\n\n\t\tprivate void clickPrintFlagsDebugInfo(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tFlags.PrintDebugInfo();\n\t\t}\n\n\t\tprivate void clickPrintBenchmarkReport(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tConstants.ServiceFactory.SimulationService.AddAction(() => {\n\t\t\t\tLog.Info(BenchmarkProfileProvider.Instance.CreateReport());\n\t\t\t});\n\t\t}\n\n\t\tprivate void clickResetBenchmarks(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tConstants.ServiceFactory.SimulationService.AddAction(() => {\n\t\t\t\tBenchmarkProfileProvider.Instance.ClearProfiles();\n\t\t\t});\n\t\t}\n\n\t\tprivate void clickVehicleToNone(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tforeach (KeyValuePair<string, List<byte>> e in customEmergencyLanes) {\n\t\t\t\tNetInfo info = PrefabCollection<NetInfo>.FindLoaded(e.Key);\n\t\t\t\tif (info == null) {\n\t\t\t\t\tLog.Warning($\"Could not find NetInfo by name {e.Key}\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tforeach (byte index in e.Value) {\n\t\t\t\t\tif (index < 0 || index >= info.m_lanes.Length) {\n\t\t\t\t\t\tLog.Warning($\"Illegal lane index {index} for NetInfo {e.Key}\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tLog._Debug($\"Resetting vehicle type of lane {index} @ {info.name}\");\n\n\t\t\t\t\tinfo.m_lanes[index].m_vehicleType = VehicleInfo.VehicleType.None;\n\t\t\t\t\tinfo.m_lanes[index].m_laneType = NetInfo.LaneType.None;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcustomEmergencyLanes.Clear();\n\t\t}\n\n\t\tprivate void clickGoToVehicle(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tushort vehicleId = Convert.ToUInt16(_goToField.text);\n\t\t\tVehicle vehicle = Singleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId];\n\t\t\tif ((vehicle.m_flags & Vehicle.Flags.Created) != 0) {\n\t\t\t\tCSUtil.CameraControl.CameraController.Instance.GoToVehicle(vehicleId);\n\t\t\t}\n\t\t}\n\n\t\tprivate void clickGoToParkedVehicle(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tushort parkedVehicleId = Convert.ToUInt16(_goToField.text);\n\t\t\tVehicleParked parkedVehicle = Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId];\n\t\t\tif ((parkedVehicle.m_flags & (ushort)VehicleParked.Flags.Created) != 0) {\n\t\t\t\tCSUtil.CameraControl.CameraController.Instance.GoToParkedVehicle(parkedVehicleId);\n\t\t\t}\n\t\t}\n\n\t\tprivate void clickGoToBuilding(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tushort buildingId = Convert.ToUInt16(_goToField.text);\n\t\t\tBuilding building = Singleton<BuildingManager>.instance.m_buildings.m_buffer[buildingId];\n\t\t\tif ((building.m_flags & Building.Flags.Created) != 0) {\n\t\t\t\tCSUtil.CameraControl.CameraController.Instance.GoToBuilding(buildingId);\n\n\t\t\t\t/*for (int index = 0; index < BuildingManager.BUILDINGGRID_RESOLUTION * BuildingManager.BUILDINGGRID_RESOLUTION; ++index) {\n\t\t\t\t\tushort bid = Singleton<BuildingManager>.instance.m_buildingGrid[index];\n\t\t\t\t\twhile (bid != 0) {\n\t\t\t\t\t\tif (bid == buildingId) {\n\t\t\t\t\t\t\tint i = index / BuildingManager.BUILDINGGRID_RESOLUTION;\n\t\t\t\t\t\t\tint j = index % BuildingManager.BUILDINGGRID_RESOLUTION;\n\t\t\t\t\t\t\tLog._Debug($\"Found building {buildingId} in building grid @ {index}. i={i}, j={j}\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbid = Singleton<BuildingManager>.instance.m_buildings.m_buffer[bid].m_nextGridBuilding;\n\t\t\t\t\t}\n\t\t\t\t}*/\n\t\t\t}\n\t\t}\n\n\t\tprivate void clickGoToCitizenInstance(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tushort citizenInstanceId = Convert.ToUInt16(_goToField.text);\n\t\t\tCitizenInstance citizenInstance = Singleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId];\n\t\t\tif ((citizenInstance.m_flags & CitizenInstance.Flags.Created) != 0) {\n\t\t\t\tCSUtil.CameraControl.CameraController.Instance.GoToCitizenInstance(citizenInstanceId);\n\t\t\t}\n\t\t}\n#endif\n\n\t\tpublic override void Update() {\n#if QUEUEDSTATS\n\t\t\tif (showPathFindStats && title != null) {\n\t\t\t\ttitle.text = CustomPathManager.TotalQueuedPathFinds.ToString();\n\t\t\t}\n#endif\n\t\t}\n\t}\n#endif\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/DespawnButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class DespawnButton : MenuButton {\n\t\tpublic override bool Active {\n\t\t\tget {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tpublic override ButtonFunction Function {\n\t\t\tget {\n\t\t\t\treturn Options.disableDespawning ? ButtonFunction.DespawnDisabled : ButtonFunction.DespawnEnabled;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string Tooltip {\n\t\t\tget {\n\t\t\t\treturn Options.disableDespawning ? \"Enable_despawning\" : \"Disable_despawning\";\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool Visible {\n\t\t\tget {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnClickInternal(UIMouseEventParameter p) {\n\t\t\tUIBase.GetTrafficManagerTool(true).SetToolMode(ToolMode.None);\n\t\t\tOptions.setDisableDespawning(!Options.disableDespawning);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/JunctionRestrictionsButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class JunctionRestrictionsButton : MenuToolModeButton {\n\t\tpublic override ToolMode ToolMode {\n\t\t\tget {\n\t\t\t\treturn ToolMode.JunctionRestrictions;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic override ButtonFunction Function {\n\t\t\tget {\n\t\t\t\treturn ButtonFunction.JunctionRestrictions;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string Tooltip {\n\t\t\tget {\n\t\t\t\treturn \"Junction_restrictions\";\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool Visible {\n\t\t\tget {\n\t\t\t\treturn Options.junctionRestrictionsEnabled;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/LaneArrowsButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class LaneArrowsButton : MenuToolModeButton {\n\t\tpublic override ToolMode ToolMode {\n\t\t\tget {\n\t\t\t\treturn ToolMode.LaneChange;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic override ButtonFunction Function {\n\t\t\tget {\n\t\t\t\treturn ButtonFunction.LaneArrows;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string Tooltip {\n\t\t\tget {\n\t\t\t\treturn \"Change_lane_arrows\";\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool Visible {\n\t\t\tget {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/LaneConnectorButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class LaneConnectorButton : MenuToolModeButton {\n\t\tpublic override ToolMode ToolMode {\n\t\t\tget {\n\t\t\t\treturn ToolMode.LaneConnector;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic override ButtonFunction Function {\n\t\t\tget {\n\t\t\t\treturn ButtonFunction.LaneConnector;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string Tooltip {\n\t\t\tget {\n\t\t\t\treturn \"Lane_connector\";\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool Visible {\n\t\t\tget {\n\t\t\t\treturn Options.laneConnectorEnabled;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/MainMenuPanel.cs",
    "content": "#define QUEUEDSTATSx\n\nusing System;\nusing System.Linq;\nusing ColossalFramework;\nusing ColossalFramework.UI;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing UnityEngine;\nusing TrafficManager.State;\nusing TrafficManager.Custom.PathFinding;\nusing System.Collections.Generic;\nusing TrafficManager.Manager;\nusing CSUtil.Commons;\nusing TrafficManager.Util;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class MainMenuPanel : UIPanel, IObserver<GlobalConfig> {\n\t\tprivate static readonly Type[] MENU_BUTTON_TYPES = new Type[] {\n\t\t\t// first row\n\t\t\ttypeof(ToggleTrafficLightsButton),\n\t\t\ttypeof(ManualTrafficLightsButton),\n\t\t\ttypeof(LaneArrowsButton),\n\t\t\ttypeof(LaneConnectorButton),\n\t\t\ttypeof(DespawnButton),\n\t\t\ttypeof(ClearTrafficButton),\n\t\t\t// second row\n\t\t\ttypeof(PrioritySignsButton),\n\t\t\ttypeof(TimedTrafficLightsButton),\n\t\t\ttypeof(JunctionRestrictionsButton),\n\t\t\ttypeof(SpeedLimitsButton),\n\t\t\ttypeof(VehicleRestrictionsButton),\n\t\t\ttypeof(ParkingRestrictionsButton),\n\t\t};\n\n\t\tpublic class SizeProfile {\n\t\t\tpublic int NUM_BUTTONS_PER_ROW { get; set; }\n\t\t\tpublic int NUM_ROWS { get; set; }\n\n\t\t\tpublic int VSPACING { get; set; }\n\t\t\tpublic int HSPACING { get; set; }\n\t\t\tpublic int TOP_BORDER { get; set; }\n\t\t\tpublic int BUTTON_SIZE { get; set; }\n\n\t\t\tpublic int MENU_WIDTH { get; set; }\n\t\t\tpublic int MENU_HEIGHT { get; set; }\n\t\t}\n\n\t\tpublic static readonly SizeProfile[] SIZE_PROFILES = new SizeProfile[] {\n\t\t\tnew SizeProfile() {\n\t\t\t\tNUM_BUTTONS_PER_ROW = 6,\n\t\t\t\tNUM_ROWS = 2,\n\n\t\t\t\tVSPACING = 5,\n\t\t\t\tHSPACING = 5,\n\t\t\t\tTOP_BORDER = 25,\n\t\t\t\tBUTTON_SIZE = 30,\n\n\t\t\t\tMENU_WIDTH = 215,\n\t\t\t\tMENU_HEIGHT = 95\n\t\t\t},\n\t\t\tnew SizeProfile() {\n\t\t\t\tNUM_BUTTONS_PER_ROW = 6,\n\t\t\t\tNUM_ROWS = 2,\n\n\t\t\t\tVSPACING = 5,\n\t\t\t\tHSPACING = 5,\n\t\t\t\tTOP_BORDER = 25,\n\t\t\t\tBUTTON_SIZE = 50,\n\n\t\t\t\tMENU_WIDTH = 335,\n\t\t\t\tMENU_HEIGHT = 135\n\t\t\t}\n\t\t};\n\n\t\tpublic const int DEFAULT_MENU_X = 85;\n\t\tpublic const int DEFAULT_MENU_Y = 60;\n\n\t\tpublic MenuButton[] Buttons { get; private set; }\n\t\tpublic UILabel VersionLabel { get; private set; }\n\t\tpublic UILabel StatsLabel { get; private set; }\n\n\t\tpublic UIDragHandle Drag { get; private set; }\n\n\t\tIDisposable confDisposable;\n\n\t\tprivate SizeProfile activeProfile = null;\n\t\tprivate bool started = false;\n\n\t\t//private UILabel optionsLabel;\n\n\t\tpublic override void Start() {\n\t\t\tGlobalConfig conf = GlobalConfig.Instance;\n\t\t\tDetermineProfile(conf);\n\n\t\t\tOnUpdate(conf);\n\n\t\t\tconfDisposable = conf.Subscribe(this);\n\n\t\t\tisVisible = false;\n\n\t\t\tbackgroundSprite = \"GenericPanel\";\n\t\t\tcolor = new Color32(64, 64, 64, 240);\n\n\t\t\tVersionLabel = AddUIComponent<VersionLabel>();\n\t\t\tStatsLabel = AddUIComponent<StatsLabel>();\n\n\t\t\tButtons = new MenuButton[MENU_BUTTON_TYPES.Length];\n\t\t\tfor (int i = 0; i < MENU_BUTTON_TYPES.Length; ++i) {\n\t\t\t\tButtons[i] = AddUIComponent(MENU_BUTTON_TYPES[i]) as MenuButton;\n\t\t\t}\n\n\t\t\tvar dragHandler = new GameObject(\"TMPE_Menu_DragHandler\");\n\t\t\tdragHandler.transform.parent = transform;\n\t\t\tdragHandler.transform.localPosition = Vector3.zero;\n\t\t\tDrag = dragHandler.AddComponent<UIDragHandle>();\n\t\t\tDrag.enabled = !GlobalConfig.Instance.Main.MainMenuPosLocked;\n\n\t\t\tUpdateAllSizes();\n\t\t\tstarted = true;\n\t\t}\n\n\t\tpublic override void OnDestroy() {\n\t\t\tif (confDisposable != null) {\n\t\t\t\tconfDisposable.Dispose();\n\t\t\t}\n\t\t}\n\n\t\tinternal void SetPosLock(bool lck) {\n\t\t\tDrag.enabled = !lck;\n\t\t}\n\n\t\tprotected override void OnPositionChanged() {\n\t\t\tGlobalConfig config = GlobalConfig.Instance;\n\n\t\t\tbool posChanged = (config.Main.MainMenuX != (int)absolutePosition.x || config.Main.MainMenuY != (int)absolutePosition.y);\n\n\t\t\tif (posChanged) {\n\t\t\t\tLog._Debug($\"Menu position changed to {absolutePosition.x}|{absolutePosition.y}\");\n\n\t\t\t\tconfig.Main.MainMenuX = (int)absolutePosition.x;\n\t\t\t\tconfig.Main.MainMenuY = (int)absolutePosition.y;\n\n\t\t\t\tGlobalConfig.WriteConfig();\n\t\t\t}\n\t\t\tbase.OnPositionChanged();\n\t\t}\n\n\t\tpublic void OnUpdate(GlobalConfig config) {\n\t\t\tUpdatePosition(new Vector2(config.Main.MainMenuX, config.Main.MainMenuY));\n\t\t\tif (started) {\n\t\t\t\tDetermineProfile(config);\n\t\t\t\tUpdateAllSizes();\n\t\t\t\tInvalidate();\n\t\t\t}\n\t\t}\n\n\t\tprivate void DetermineProfile(GlobalConfig conf) {\n\t\t\tint profileIndex = conf.Main.TinyMainMenu ? 0 : 1;\n\t\t\tactiveProfile = SIZE_PROFILES[profileIndex];\n\t\t}\n\n\t\tpublic void UpdateAllSizes() {\n\t\t\tUpdateSize();\n\t\t\tUpdateDragSize();\n\t\t\tUpdateButtons();\n\t\t}\n\n\t\tprivate void UpdateSize() {\n\t\t\twidth = activeProfile.MENU_WIDTH;\n\t\t\theight = activeProfile.MENU_HEIGHT;\n\t\t}\n\n\t\tprivate void UpdateDragSize() {\n\t\t\tDrag.width = width;\n\t\t\tDrag.height = activeProfile.TOP_BORDER;\n\t\t}\n\n\t\tprivate void UpdateButtons() {\n\t\t\tint i = 0;\n\t\t\tint y = activeProfile.TOP_BORDER;\n\t\t\tfor (int row = 0; row < activeProfile.NUM_ROWS; ++row) {\n\t\t\t\tint x = activeProfile.HSPACING;\n\t\t\t\tfor (int col = 0; col < activeProfile.NUM_BUTTONS_PER_ROW; ++col) {\n\t\t\t\t\tif (i >= Buttons.Length) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tMenuButton button = Buttons[i];\n\t\t\t\t\tbutton.relativePosition = new Vector3(x, y);\n\t\t\t\t\tbutton.width = activeProfile.BUTTON_SIZE;\n\t\t\t\t\tbutton.height = activeProfile.BUTTON_SIZE;\n\t\t\t\t\tbutton.Invalidate();\n\t\t\t\t\tButtons[i++] = button;\n\t\t\t\t\tx += activeProfile.BUTTON_SIZE + activeProfile.HSPACING;\n\t\t\t\t}\n\t\t\t\ty += activeProfile.BUTTON_SIZE + activeProfile.VSPACING;\n\t\t\t}\n\t\t}\n\n\t\tpublic void UpdatePosition(Vector2 pos) {\n\t\t\tRect rect = new Rect(pos.x, pos.y, activeProfile.MENU_WIDTH, activeProfile.MENU_HEIGHT);\n\t\t\tVector2 resolution = UIView.GetAView().GetScreenResolution();\n\t\t\tVectorUtil.ClampRectToScreen(ref rect, resolution);\n\t\t\tLog.Info($\"Setting main menu position to [{pos.x},{pos.y}]\");\n\t\t\tabsolutePosition = rect.position;\n\t\t\tInvalidate();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/ManualTrafficLightsButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class ManualTrafficLightsButton : MenuToolModeButton {\n\t\tpublic override ToolMode ToolMode {\n\t\t\tget {\n\t\t\t\treturn ToolMode.ManualSwitch;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic override ButtonFunction Function {\n\t\t\tget {\n\t\t\t\treturn ButtonFunction.ManualTrafficLights;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string Tooltip {\n\t\t\tget {\n\t\t\t\treturn \"Manual_traffic_lights\";\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool Visible {\n\t\t\tget {\n\t\t\t\treturn Options.timedLightsEnabled;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/MenuButton.cs",
    "content": "﻿using ColossalFramework.Math;\nusing ColossalFramework.UI;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.State;\nusing TrafficManager.Util;\nusing UnityEngine;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic abstract class MenuButton : LinearSpriteButton {\n\t\tpublic enum ButtonFunction {\n\t\t\tLaneConnector,\n\t\t\tClearTraffic,\n\t\t\tDespawnDisabled,\n\t\t\tDespawnEnabled,\n\t\t\tJunctionRestrictions,\n\t\t\tLaneArrows,\n\t\t\tManualTrafficLights,\n\t\t\tPrioritySigns,\n\t\t\tSpeedLimits,\n\t\t\tTimedTrafficLights,\n\t\t\tToggleTrafficLights,\n\t\t\tVehicleRestrictions,\n\t\t\tParkingRestrictions\n\t\t}\n\n\t\tpublic const string MENU_BUTTON = \"TMPE_MenuButton\";\n\t\tpublic const int BUTTON_SIZE = 30;\n\t\tpublic override void HandleClick(UIMouseEventParameter p) { }\n\n\t\tprotected override void OnClick(UIMouseEventParameter p) {\n\t\t\tOnClickInternal(p);\n\t\t\tforeach (MenuButton button in LoadingExtension.BaseUI.MainMenu.Buttons) {\n\t\t\t\tbutton.UpdateProperties();\n\t\t\t}\n\t\t}\n\n\t\tpublic abstract void OnClickInternal(UIMouseEventParameter p);\n\t\tpublic abstract ButtonFunction Function { get; }\n\n\t\tpublic override bool CanActivate() {\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic override string ButtonName {\n\t\t\tget {\n\t\t\t\treturn MENU_BUTTON;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string FunctionName {\n\t\t\tget {\n\t\t\t\treturn Function.ToString();\n\t\t\t}\n\t\t}\n\n\t\tpublic override string[] FunctionNames {\n\t\t\tget {\n\t\t\t\tvar functions = Enum.GetValues(typeof(ButtonFunction));\n\t\t\t\tstring[] ret = new string[functions.Length];\n\t\t\t\tfor (int i = 0; i < functions.Length; ++i) {\n\t\t\t\t\tret[i] = functions.GetValue(i).ToString();\n\t\t\t\t}\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t}\n\n\t\tpublic override Texture2D AtlasTexture {\n\t\t\tget {\n\t\t\t\treturn TextureResources.MainMenuButtonsTexture2D;\n\t\t\t}\n\t\t}\n\n\t\tpublic override int Width {\n\t\t\tget {\n\t\t\t\treturn 50;\n\t\t\t}\n\t\t}\n\n\t\tpublic override int Height {\n\t\t\tget {\n\t\t\t\treturn 50;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/MenuToolModeButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic abstract class MenuToolModeButton : MenuButton {\n\t\tpublic abstract ToolMode ToolMode { get; }\n\n\t\tpublic override bool Active {\n\t\t\tget {\n\t\t\t\treturn this.ToolMode.Equals(UIBase.GetTrafficManagerTool(false)?.GetToolMode());\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnClickInternal(UIMouseEventParameter p) {\n\t\t\tif (Active) {\n\t\t\t\tUIBase.GetTrafficManagerTool(true).SetToolMode(ToolMode.None);\n\t\t\t} else {\n\t\t\t\tUIBase.GetTrafficManagerTool(true).SetToolMode(this.ToolMode);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/ParkingRestrictionsButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class ParkingRestrictionsButton : MenuToolModeButton {\n\t\tpublic override ToolMode ToolMode {\n\t\t\tget {\n\t\t\t\treturn ToolMode.ParkingRestrictions;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic override ButtonFunction Function {\n\t\t\tget {\n\t\t\t\treturn ButtonFunction.ParkingRestrictions;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string Tooltip {\n\t\t\tget {\n\t\t\t\treturn \"Parking_restrictions\";\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool Visible {\n\t\t\tget {\n\t\t\t\treturn Options.parkingRestrictionsEnabled;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/PrioritySignsButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class PrioritySignsButton : MenuToolModeButton {\n\t\tpublic override ToolMode ToolMode {\n\t\t\tget {\n\t\t\t\treturn ToolMode.AddPrioritySigns;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic override ButtonFunction Function {\n\t\t\tget {\n\t\t\t\treturn ButtonFunction.PrioritySigns;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string Tooltip {\n\t\t\tget {\n\t\t\t\treturn \"Add_priority_signs\";\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool Visible {\n\t\t\tget {\n\t\t\t\treturn Options.prioritySignsEnabled;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/README.md",
    "content": "# TM:PE -- /UI/MainMenu\nMain menu related classes.\n## Classes\n- **ClearTrafficButton**: Triggers deletion of all spawned vehicles\n- **DebugMenu**: Debugging menu (originally the old main menu)\n- **DespawnButton**: For toggling despawn\n- **JunctionRestrictionsButton**: For controlling junction restrictions\n- **LaneArrowsButton**: For setting up custom lane arrows\n- **LaneConnectorButton**: For setting up custom lane connections\n- **MainMenuPanel**: the new (v1.9) compact main menu\n- **ManualTrafficLightsButton**: For setting up manual traffic lights\n- **MenuButton**: abstract main menu button\n- **MenuToolModeButton**: abstract main menu button supporting association with a specific tool mode\n- **OptionsLabel**: currently unused\n- **ParkingRestrictionsButton**: For controlling parking restrictions\n- **SpeedLimitsButton**: For setting up custom speed limits\n- **TimedTrafficLightsButton**: For setting up timed traffic lights\n- **ToggleTrafficLightsButton**: For adding/removing traffic lights at junctions\n- **VehicleRestrictionsButton**: For setting up vehicle restrictions\n- **VersionLabel**: Displays the current version\n "
  },
  {
    "path": "TLM/TLM/UI/MainMenu/SpeedLimitsButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class SpeedLimitsButton : MenuToolModeButton {\n\t\tpublic override ToolMode ToolMode {\n\t\t\tget {\n\t\t\t\treturn ToolMode.SpeedLimits;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic override ButtonFunction Function {\n\t\t\tget {\n\t\t\t\treturn ButtonFunction.SpeedLimits;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string Tooltip {\n\t\t\tget {\n\t\t\t\treturn \"Speed_limits\";\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool Visible {\n\t\t\tget {\n\t\t\t\treturn Options.customSpeedLimitsEnabled;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/StatsLabel.cs",
    "content": "﻿using ColossalFramework.UI;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Custom.PathFinding;\nusing TrafficManager.State;\nusing UnityEngine;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class StatsLabel : UILabel {\n\t\tpublic override void Start() {\n\t\t\tsize = new Vector2(MainMenuPanel.SIZE_PROFILES[0].MENU_WIDTH / 2, MainMenuPanel.SIZE_PROFILES[0].TOP_BORDER); // TODO use current size profile\n\t\t\ttext = \"\";\n\t\t\trelativePosition = new Vector3(5f, -20f);\n\t\t\ttextAlignment = UIHorizontalAlignment.Left;\n\t\t\tanchor = UIAnchorStyle.Top | UIAnchorStyle.Left;\n\t\t}\n\n#if QUEUEDSTATS\n\t\tpublic override void Update() {\n\t\t\tif (Options.showPathFindStats) {\n\t\t\t\tuint queued = CustomPathManager.TotalQueuedPathFinds;\n\t\t\t\tif (queued < 1000) {\n\t\t\t\t\ttextColor = Color.Lerp(Color.green, Color.yellow, (float)queued / 1000f);\n\t\t\t\t} else if (queued < 2500) {\n\t\t\t\t\ttextColor = Color.Lerp(Color.yellow, Color.red, (float)(queued - 1000f) / 1500f);\n\t\t\t\t} else {\n\t\t\t\t\ttextColor = Color.red;\n\t\t\t\t}\n\n\t\t\t\ttext = CustomPathManager.TotalQueuedPathFinds.ToString() + \" PFs\";\n\t\t\t} else {\n\t\t\t\ttext = \"\";\n\t\t\t\tm_TextColor = Color.white;\n\t\t\t}\n\t\t}\n#endif\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/TimedTrafficLightsButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class TimedTrafficLightsButton : MenuToolModeButton {\n\t\tpublic override ToolMode ToolMode {\n\t\t\tget {\n\t\t\t\treturn ToolMode.TimedLightsSelectNode;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic override ButtonFunction Function {\n\t\t\tget {\n\t\t\t\treturn ButtonFunction.TimedTrafficLights;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string Tooltip {\n\t\t\tget {\n\t\t\t\treturn \"Timed_traffic_lights\";\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool Visible {\n\t\t\tget {\n\t\t\t\treturn Options.timedLightsEnabled;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/ToggleTrafficLightsButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class ToggleTrafficLightsButton : MenuToolModeButton {\n\t\tpublic override ToolMode ToolMode {\n\t\t\tget {\n\t\t\t\treturn ToolMode.SwitchTrafficLight;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic override ButtonFunction Function {\n\t\t\tget {\n\t\t\t\treturn ButtonFunction.ToggleTrafficLights;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string Tooltip {\n\t\t\tget {\n\t\t\t\treturn \"Switch_traffic_lights\";\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool Visible {\n\t\t\tget {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/VehicleRestrictionsButton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ColossalFramework.UI;\nusing TrafficManager.Manager;\nusing TrafficManager.State;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class VehicleRestrictionsButton : MenuToolModeButton {\n\t\tpublic override ToolMode ToolMode {\n\t\t\tget {\n\t\t\t\treturn ToolMode.VehicleRestrictions;\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic override ButtonFunction Function {\n\t\t\tget {\n\t\t\t\treturn ButtonFunction.VehicleRestrictions;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string Tooltip {\n\t\t\tget {\n\t\t\t\treturn \"Vehicle_restrictions\";\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool Visible {\n\t\t\tget {\n\t\t\t\treturn Options.vehicleRestrictionsEnabled;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/MainMenu/VersionLabel.cs",
    "content": "﻿using ColossalFramework.UI;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing UnityEngine;\n\nnamespace TrafficManager.UI.MainMenu {\n\tpublic class VersionLabel : UILabel {\n\t\tpublic override void Start() {\n\t\t\tsize = new Vector2(MainMenuPanel.SIZE_PROFILES[0].MENU_WIDTH, MainMenuPanel.SIZE_PROFILES[0].TOP_BORDER); // TODO use current size profile\n\t\t\ttext = \"TM:PE \" + TrafficManagerMod.Version;\n\t\t\trelativePosition = new Vector3(5f, 5f);\n\t\t\ttextAlignment = UIHorizontalAlignment.Left;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/README.md",
    "content": "# TM:PE -- /UI\nUser interface classes.\n## Classes\n- **CameraCtrl**: Allows to move the player camera to certain objects (used for debugging)\n- **SubTool**: Represents a tool that can be selected through the main menu.\n- **TextureResources**: Holds references to all required textures.\n- **ToolMode**: Enum. Allowes to describe which sub tool is currently active.\n- **TrafficManagerTool**: Central UI controller. Manages active sub tools and forwards UI update calls to the sub tools.\n- **Translation**: Internationalization functions.\n- **TransportDemandViewMode**: Enum. Represents the currently active demand mode when the public transport info view is active together with the Parking AI\n- **UIBase**: Holds an instance of the main menu and main menu button\n- **UIMainMenuButton**: the main menu button\n- **UITransportDemand**: transport demand view mode toggle window"
  },
  {
    "path": "TLM/TLM/UI/RemoveCitizenInstanceButtonExtender.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.UI;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.UI.MainMenu;\nusing UnityEngine;\n\nnamespace TrafficManager.UI {\n\tpublic class RemoveCitizenInstanceButtonExtender : MonoBehaviour {\n\t\tprivate IList<UIButton> buttons;\n\n\t\tpublic void Start() {\n\t\t\tbuttons = new List<UIButton>();\n\n\t\t\tvar citizenInfoPanel = GameObject.Find(\"(Library) CitizenWorldInfoPanel\").GetComponent<CitizenWorldInfoPanel>();\n\t\t\tif (citizenInfoPanel != null) {\n\t\t\t\tbuttons.Add(AddRemoveCitizenInstanceButton(citizenInfoPanel));\n\t\t\t}\n\n\t\t\tvar touristInfoPanel = GameObject.Find(\"(Library) TouristWorldInfoPanel\").GetComponent<TouristWorldInfoPanel>();\n\t\t\tif (touristInfoPanel != null) {\n\t\t\t\tbuttons.Add(AddRemoveCitizenInstanceButton(touristInfoPanel));\n\t\t\t}\n\t\t}\n\n\t\tpublic void OnDestroy() {\n\t\t\tif (buttons == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tforeach (UIButton button in buttons) {\n\t\t\t\tDestroy(button.gameObject);\n\t\t\t}\n\t\t}\n\n\t\tprotected UIButton AddRemoveCitizenInstanceButton(WorldInfoPanel panel) {\n\t\t\tUIButton button = UIView.GetAView().AddUIComponent(typeof(RemoveCitizenInstanceButton)) as RemoveCitizenInstanceButton;\n\t\t\t\n\t\t\tbutton.AlignTo(panel.component, UIAlignAnchor.TopRight);\n\t\t\tbutton.relativePosition += new Vector3(- button.width - 80f, 50f);\n\n\t\t\treturn button;\n\t\t}\n\n\t\tpublic class RemoveCitizenInstanceButton : LinearSpriteButton {\n\t\t\tpublic override void Start() {\n\t\t\t\tbase.Start();\n\t\t\t\twidth = Width;\n\t\t\t\theight = Height;\n\t\t\t}\n\n\t\t\tpublic override void HandleClick(UIMouseEventParameter p) {\n\t\t\t\tInstanceID instance = WorldInfoPanel.GetCurrentInstanceID();\n\t\t\t\tLog._Debug($\"Current citizen: {instance.Citizen}\");\n\t\t\t\tif (instance.Citizen != 0) {\n\t\t\t\t\tushort citizenInstanceId = 0;\n\t\t\t\t\tConstants.ServiceFactory.CitizenService.ProcessCitizen(instance.Citizen, delegate (uint citId, ref Citizen cit) {\n\t\t\t\t\t\tcitizenInstanceId = cit.m_instance;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\n\t\t\t\t\tLog._Debug($\"Current citizen: {instance.Citizen} Instance: {citizenInstanceId}\");\n\t\t\t\t\tif (citizenInstanceId != 0) {\n\t\t\t\t\t\tConstants.ServiceFactory.SimulationService.AddAction(() => Constants.ServiceFactory.CitizenService.ReleaseCitizenInstance(citizenInstanceId));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override bool Active {\n\t\t\t\tget {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override Texture2D AtlasTexture {\n\t\t\t\tget {\n\t\t\t\t\treturn TextureResources.RemoveButtonTexture2D;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override string ButtonName {\n\t\t\t\tget {\n\t\t\t\t\treturn \"RemoveCitizenInstance\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override string FunctionName {\n\t\t\t\tget {\n\t\t\t\t\treturn \"RemoveCitizenInstanceNow\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override string[] FunctionNames {\n\t\t\t\tget {\n\t\t\t\t\treturn new string[] { \"RemoveCitizenInstanceNow\" };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override string Tooltip {\n\t\t\t\tget {\n\t\t\t\t\treturn Translation.GetString(\"Remove_this_citizen\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override bool Visible {\n\t\t\t\tget {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override int Width {\n\t\t\t\tget {\n\t\t\t\t\treturn 30;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override int Height {\n\t\t\t\tget {\n\t\t\t\t\treturn 30;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override bool CanActivate() {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/RemoveVehicleButtonExtender.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.UI;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.UI.MainMenu;\nusing UnityEngine;\n\nnamespace TrafficManager.UI {\n\tpublic class RemoveVehicleButtonExtender : MonoBehaviour {\n\t\tprivate IList<UIButton> buttons;\n\n\t\tpublic void Start() {\n\t\t\tbuttons = new List<UIButton>();\n\n\t\t\tvar citizenVehicleInfoPanel = GameObject.Find(\"(Library) CitizenVehicleWorldInfoPanel\").GetComponent<CitizenVehicleWorldInfoPanel>();\n\t\t\tif (citizenVehicleInfoPanel != null) {\n\t\t\t\tbuttons.Add(AddRemoveVehicleButton(citizenVehicleInfoPanel));\n\t\t\t}\n\n\t\t\tvar cityServiceVehicleInfoPanel = GameObject.Find(\"(Library) CityServiceVehicleWorldInfoPanel\").GetComponent<CityServiceVehicleWorldInfoPanel>();\n\t\t\tif (cityServiceVehicleInfoPanel != null) {\n\t\t\t\tbuttons.Add(AddRemoveVehicleButton(cityServiceVehicleInfoPanel));\n\t\t\t}\n\n\t\t\tvar publicTransportVehicleInfoPanel = GameObject.Find(\"(Library) PublicTransportVehicleWorldInfoPanel\").GetComponent<PublicTransportVehicleWorldInfoPanel>();\n\t\t\tif (publicTransportVehicleInfoPanel != null) {\n\t\t\t\tbuttons.Add(AddRemoveVehicleButton(publicTransportVehicleInfoPanel));\n\t\t\t}\n\t\t}\n\n\t\tpublic void OnDestroy() {\n\t\t\tif (buttons == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tforeach (UIButton button in buttons) {\n\t\t\t\tDestroy(button.gameObject);\n\t\t\t}\n\t\t}\n\n\t\tprotected UIButton AddRemoveVehicleButton(WorldInfoPanel panel) {\n\t\t\tUIButton button = UIView.GetAView().AddUIComponent(typeof(RemoveVehicleButton)) as RemoveVehicleButton;\n\t\t\t\n\t\t\tbutton.AlignTo(panel.component, UIAlignAnchor.TopRight);\n\t\t\tbutton.relativePosition += new Vector3(- button.width - 80f, 50f);\n\n\t\t\treturn button;\n\t\t}\n\n\t\tpublic class RemoveVehicleButton : LinearSpriteButton {\n\t\t\tpublic override void Start() {\n\t\t\t\tbase.Start();\n\t\t\t\twidth = Width;\n\t\t\t\theight = Height;\n\t\t\t}\n\n\t\t\tpublic override void HandleClick(UIMouseEventParameter p) {\n\t\t\t\tInstanceID instance = WorldInfoPanel.GetCurrentInstanceID();\n\t\t\t\tLog._Debug($\"Current vehicle instance: {instance.Vehicle}\");\n\t\t\t\tif (instance.Vehicle != 0) {\n\t\t\t\t\tConstants.ServiceFactory.SimulationService.AddAction(() => Constants.ServiceFactory.VehicleService.ReleaseVehicle(instance.Vehicle));\n\t\t\t\t} else if (instance.ParkedVehicle != 0) {\n\t\t\t\t\tConstants.ServiceFactory.SimulationService.AddAction(() => Constants.ServiceFactory.VehicleService.ReleaseParkedVehicle(instance.ParkedVehicle));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override bool Active {\n\t\t\t\tget {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override Texture2D AtlasTexture {\n\t\t\t\tget {\n\t\t\t\t\treturn TextureResources.RemoveButtonTexture2D;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override string ButtonName {\n\t\t\t\tget {\n\t\t\t\t\treturn \"RemoveVehicle\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override string FunctionName {\n\t\t\t\tget {\n\t\t\t\t\treturn \"RemoveVehicleNow\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override string[] FunctionNames {\n\t\t\t\tget {\n\t\t\t\t\treturn new string[] { \"RemoveVehicleNow\" };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override string Tooltip {\n\t\t\t\tget {\n\t\t\t\t\treturn Translation.GetString(\"Remove_this_vehicle\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override bool Visible {\n\t\t\t\tget {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override int Width {\n\t\t\t\tget {\n\t\t\t\t\treturn 30;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override int Height {\n\t\t\t\tget {\n\t\t\t\t\treturn 30;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic override bool CanActivate() {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/SubTool.cs",
    "content": "﻿using ColossalFramework.UI;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing UnityEngine;\n\nnamespace TrafficManager.UI {\n\tpublic abstract class SubTool {\n\t\tpublic TrafficManagerTool MainTool { get; set; }\n\n\t\tprotected Texture2D WindowTexture {\n\t\t\tget {\n\t\t\t\tif (windowTexture == null) {\n\t\t\t\t\twindowTexture = TrafficManagerTool.AdjustAlpha(TextureResources.WindowBackgroundTexture2D, MainTool.GetWindowAlpha());\n\t\t\t\t}\n\t\t\t\treturn windowTexture;\n\t\t\t}\n\t\t}\n\t\tprivate Texture2D windowTexture = null;\n\n\t\tprotected GUIStyle WindowStyle {\n\t\t\tget {\n\t\t\t\tif (windowStyle == null) {\n\t\t\t\t\twindowStyle = new GUIStyle {\n\t\t\t\t\t\tnormal = {\n\t\t\t\t\t\t\tbackground = WindowTexture,\n\t\t\t\t\t\t\ttextColor = Color.white\n\t\t\t\t\t\t},\n\t\t\t\t\t\talignment = TextAnchor.UpperCenter,\n\t\t\t\t\t\tfontSize = 20,\n\t\t\t\t\t\tborder = {\n\t\t\t\t\t\t\tleft = 4,\n\t\t\t\t\t\t\ttop = 41,\n\t\t\t\t\t\t\tright = 4,\n\t\t\t\t\t\t\tbottom = 8\n\t\t\t\t\t\t},\n\t\t\t\t\t\toverflow = {\n\t\t\t\t\t\t\tbottom = 0,\n\t\t\t\t\t\t\ttop = 0,\n\t\t\t\t\t\t\tright = 12,\n\t\t\t\t\t\t\tleft = 12\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcontentOffset = new Vector2(0, -44),\n\t\t\t\t\t\tpadding = {\n\t\t\t\t\t\t\ttop = 55\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn windowStyle;\n\t\t\t}\n\t\t}\n\t\tprivate GUIStyle windowStyle = null;\n\n\t\tprotected Texture2D BorderlessTexture {\n\t\t\tget {\n\t\t\t\tif (borderlessTexture == null) {\n\t\t\t\t\tborderlessTexture = TrafficManagerTool.MakeTex(1, 1, new Color(0.5f, 0.5f, 0.5f, MainTool.GetWindowAlpha()));\n\t\t\t\t}\n\t\t\t\treturn borderlessTexture;\n\t\t\t}\n\t\t}\n\t\tprivate Texture2D borderlessTexture = null;\n\n\t\tprotected GUIStyle BorderlessStyle {\n\t\t\tget {\n\t\t\t\tif (borderlessStyle == null) {\n\t\t\t\t\tborderlessStyle = new GUIStyle {\n\t\t\t\t\t\tnormal = { background = BorderlessTexture },\n\t\t\t\t\t\talignment = TextAnchor.MiddleCenter,\n\t\t\t\t\t\tborder = {\n\t\t\t\t\t\t\tbottom = 2,\n\t\t\t\t\t\t\ttop = 2,\n\t\t\t\t\t\t\tright = 2,\n\t\t\t\t\t\t\tleft = 2\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn borderlessStyle;\n\t\t\t}\n\t\t}\n\t\tprivate GUIStyle borderlessStyle = null;\n\n\t\tprotected ushort HoveredNodeId {\n\t\t\tget { return TrafficManagerTool.HoveredNodeId; }\n\t\t\tset { TrafficManagerTool.HoveredNodeId = value; }\n\t\t}\n\n\t\tprotected ushort HoveredSegmentId {\n\t\t\tget { return TrafficManagerTool.HoveredSegmentId; }\n\t\t\tset { TrafficManagerTool.HoveredSegmentId = value; }\n\t\t}\n\n\t\tprotected ushort SelectedNodeId {\n\t\t\tget { return TrafficManagerTool.SelectedNodeId; }\n\t\t\tset { TrafficManagerTool.SelectedNodeId = value; }\n\t\t}\n\n\t\tprotected ushort SelectedSegmentId {\n\t\t\tget { return TrafficManagerTool.SelectedSegmentId; }\n\t\t\tset { TrafficManagerTool.SelectedSegmentId = value; }\n\t\t}\n\n\t\tpublic SubTool(TrafficManagerTool mainTool) {\n\t\t\tMainTool = mainTool;\n\t\t}\n\n\t\tpublic void OnToolUpdate() {\n\t\t\t//OnLeftClickOverlay();\n\t\t}\n\n\t\tfloat nativeWidth = 1920;\n\t\tfloat nativeHeight = 1200;\n\n\t\t/// <summary>\n\t\t/// Called whenever the \n\t\t/// </summary>\n\t\tpublic abstract void OnPrimaryClickOverlay();\n\t\tpublic virtual void OnSecondaryClickOverlay() { }\n\t\tpublic virtual void OnToolGUI(Event e) {\n\t\t\t//set up scaling\n\t\t\t/*Vector2 resolution = UIView.GetAView().GetScreenResolution();\n\t\t\tfloat rx = resolution.x / nativeWidth;\n\t\t\tfloat ry = resolution.y / nativeHeight;\n\t\t\tGUI.matrix = Matrix4x4.TRS(new Vector3(0, 0, 0), Quaternion.identity, new Vector3(rx, ry, 1));*/\n\t\t}\n\t\tpublic abstract void RenderOverlay(RenderManager.CameraInfo cameraInfo);\n\t\tpublic virtual void Initialize() {\n\t\t\tborderlessTexture = null;\n\t\t\tborderlessStyle = null;\n\t\t\twindowTexture = null;\n\t\t\twindowStyle = null;\n\t\t}\n\t\tpublic virtual void Cleanup() { }\n\t\tpublic virtual void OnActivate() { }\n\t\tpublic virtual void RenderInfoOverlay(RenderManager.CameraInfo cameraInfo) { }\n\t\tpublic virtual void ShowGUIOverlay(ToolMode toolMode, bool viewOnly) { }\n\t\tpublic virtual bool IsCursorInPanel() {\n\t\t\treturn LoadingExtension.BaseUI.GetMenu().containsMouse\n#if DEBUG\n\t\t\t\t|| LoadingExtension.BaseUI.GetDebugMenu().containsMouse\n#endif\n\t\t\t\t;\n\t\t}\n\t\tpublic virtual string GetTutorialKey() {\n\t\t\treturn this.GetType().Name;\n\t\t}\n\n\t\tprotected void DragWindow(ref Rect window) {\n\t\t\tVector2 resolution = UIView.GetAView().GetScreenResolution();\n\t\t\twindow.x = Mathf.Clamp(window.x, 0, resolution.x - window.width);\n\t\t\twindow.y = Mathf.Clamp(window.y, 0, resolution.y - window.height);\n\n\t\t\tbool primaryMouseDown = Input.GetMouseButton(0);\n\t\t\tif (primaryMouseDown) {\n\t\t\t\tGUI.DragWindow();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/SubTools/JunctionRestrictionsTool.cs",
    "content": "﻿#define DEBUGCONNx\n\nusing ColossalFramework;\nusing ColossalFramework.Math;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Util;\nusing UnityEngine;\nusing TrafficManager.Manager;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Geometry.Impl;\nusing ColossalFramework.UI;\n\nnamespace TrafficManager.UI.SubTools {\n\tpublic class JunctionRestrictionsTool : SubTool {\n\n\t\tprivate HashSet<ushort> currentRestrictedNodeIds;\n\t\tprivate bool overlayHandleHovered;\n\t\tprivate readonly float junctionRestrictionsSignSize = 80f;\n\n\t\tpublic JunctionRestrictionsTool(TrafficManagerTool mainTool) : base(mainTool) {\n\t\t\tcurrentRestrictedNodeIds = new HashSet<ushort>();\n\t\t}\n\n\t\tpublic override void OnToolGUI(Event e) {\n\t\t\t/*if (SelectedNodeId != 0) {\n\t\t\t\toverlayHandleHovered = false;\n\t\t\t}\n\t\t\tShowSigns(false);*/\n\t\t}\n\n\t\tpublic override void RenderInfoOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\t\n\t\t}\n\n\t\tpublic override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\tif (SelectedNodeId != 0) {\n\t\t\t\t// draw selected node\n\t\t\t\tMainTool.DrawNodeCircle(cameraInfo, SelectedNodeId, true);\n\t\t\t}\n\n\t\t\tif (HoveredNodeId != 0 && HoveredNodeId != SelectedNodeId && (Singleton<NetManager>.instance.m_nodes.m_buffer[HoveredNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Bend)) != NetNode.Flags.None) {\n\t\t\t\t// draw hovered node\n\t\t\t\tMainTool.DrawNodeCircle(cameraInfo, HoveredNodeId, Input.GetMouseButton(0));\n\t\t\t}\n\t\t}\n\n\t\tpublic override void ShowGUIOverlay(ToolMode toolMode, bool viewOnly) {\n\t\t\tif (viewOnly && !Options.junctionRestrictionsOverlay)\n\t\t\t\treturn;\n\n\t\t\tif (SelectedNodeId != 0) {\n\t\t\t\toverlayHandleHovered = false;\n\t\t\t}\n\n\t\t\tShowSigns(viewOnly);\n\t\t}\n\n\t\tprivate void ShowSigns(bool viewOnly) {\n\t\t\tbool debug = false;\n#if DEBUG\n\t\t\tdebug = !viewOnly && GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tvar camPos = Singleton<SimulationManager>.instance.m_simulationView.m_position;\n\n\t\t\tif (!viewOnly && SelectedNodeId != 0) {\n\t\t\t\tcurrentRestrictedNodeIds.Add(SelectedNodeId);\n\t\t\t}\n\n\t\t\tushort updatedNodeId = 0;\n\t\t\tbool handleHovered = false;\n\t\t\tbool cursorInPanel = this.IsCursorInPanel();\n\t\t\tforeach (ushort nodeId in currentRestrictedNodeIds) {\n\t\t\t\tif (!Constants.ServiceFactory.NetService.IsNodeValid(nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tVector3 nodePos = netManager.m_nodes.m_buffer[nodeId].m_position;\n\t\t\t\tvar diff = nodePos - camPos;\n\n\t\t\t\tif (diff.magnitude > TrafficManagerTool.MaxOverlayDistance)\n\t\t\t\t\tcontinue; // do not draw if too distant\n\n\t\t\t\tVector3 screenPos;\n\t\t\t\tbool visible = MainTool.WorldToScreenPoint(nodePos, out screenPos);\n\n\t\t\t\tif (! visible)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tbool viewOnlyNode = true;\n\t\t\t\tif (!viewOnly && nodeId == SelectedNodeId)\n\t\t\t\t\tviewOnlyNode = false;\n\n\t\t\t\t// draw junction restrictions\n\t\t\t\tbool update;\n\t\t\t\tif (drawSignHandles(debug, nodeId, ref netManager.m_nodes.m_buffer[nodeId], viewOnlyNode, !cursorInPanel, ref camPos, out update))\n\t\t\t\t\thandleHovered = true;\n\n\t\t\t\tif (update) {\n\t\t\t\t\tupdatedNodeId = nodeId;\n\t\t\t\t}\n\t\t\t}\n\t\t\toverlayHandleHovered = handleHovered;\n\n\t\t\tif (updatedNodeId != 0) {\n\t\t\t\tRefreshCurrentRestrictedNodeIds(updatedNodeId);\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnPrimaryClickOverlay() {\n\t\t\tbool debug = false;\n#if DEBUG\n\t\t\tdebug = GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\t\t\tif (HoveredNodeId == 0) return;\n\t\t\tif (overlayHandleHovered) return;\n\t\t\tif (!debug && (Singleton<NetManager>.instance.m_nodes.m_buffer[HoveredNodeId].m_flags & (NetNode.Flags.Junction | NetNode.Flags.Bend)) == NetNode.Flags.None)\n\t\t\t\treturn;\n\n\t\t\tSelectedNodeId = HoveredNodeId;\n\t\t\tMainTool.CheckClicked(); // prevent accidential activation of signs on node selection (TODO improve this!)\n\t\t}\n\n\t\tpublic override void OnSecondaryClickOverlay() {\n\t\t\tSelectedNodeId = 0;\n\t\t}\n\n\t\tpublic override void OnActivate() {\n#if DEBUGCONN\n\t\t\tLog._Debug(\"TppLaneConnectorTool: OnActivate\");\n#endif\n\t\t\tSelectedNodeId = 0;\n\t\t\tRefreshCurrentRestrictedNodeIds();\n\t\t}\n\n\t\tpublic override void Cleanup() {\n#if DEBUG\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[11];\n#endif\n\t\t\tforeach (ushort nodeId in currentRestrictedNodeIds) {\n\t\t\t\tJunctionRestrictionsManager.Instance.RemoveJunctionRestrictionsIfNecessary(nodeId);\n\t\t\t}\n\t\t\tRefreshCurrentRestrictedNodeIds();\n\t\t}\n\n\t\tpublic override void Initialize() {\n\t\t\tbase.Initialize();\n\t\t\tCleanup();\n\t\t\tif (Options.junctionRestrictionsOverlay) {\n\t\t\t\tRefreshCurrentRestrictedNodeIds();\n\t\t\t} else {\n\t\t\t\tcurrentRestrictedNodeIds.Clear();\n\t\t\t}\n\t\t}\n\n\t\tprivate void RefreshCurrentRestrictedNodeIds(ushort forceNodeId=0) {\n\t\t\tif (forceNodeId == 0) {\n\t\t\t\tcurrentRestrictedNodeIds.Clear();\n\t\t\t} else {\n\t\t\t\tcurrentRestrictedNodeIds.Remove(forceNodeId);\n\t\t\t}\n\n\t\t\tfor (uint nodeId = (forceNodeId == 0 ? 1u : forceNodeId); nodeId <= (forceNodeId == 0 ? NetManager.MAX_NODE_COUNT - 1 : forceNodeId); ++nodeId) {\n\t\t\t\tif (!Constants.ServiceFactory.NetService.IsNodeValid((ushort)nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (JunctionRestrictionsManager.Instance.HasJunctionRestrictions((ushort)nodeId)) {\n\t\t\t\t\tcurrentRestrictedNodeIds.Add((ushort)nodeId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate bool drawSignHandles(bool debug, ushort nodeId, ref NetNode node, bool viewOnly, bool handleClick, ref Vector3 camPos, out bool stateUpdated) {\n\t\t\tbool hovered = false;\n\t\t\tstateUpdated = false;\n\n\t\t\tif (viewOnly && !Options.junctionRestrictionsOverlay && MainTool.GetToolMode() != ToolMode.JunctionRestrictions)\n\t\t\t\treturn false;\n\n\t\t\t//NetManager netManager = Singleton<NetManager>.instance;\n\t\t\tvar guiColor = GUI.color;\n\n\t\t\tVector3 nodePos = Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_position;\n\n\t\t\tfor (int i = 0; i < 8; ++i) {\n\t\t\t\tushort segmentId = node.GetSegment(i);\n\t\t\t\tif (segmentId == 0)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tSegmentGeometry geometry = SegmentGeometry.Get(segmentId);\n\t\t\t\tif (geometry == null) {\n\t\t\t\t\tLog.Error($\"JunctionRestrictionsTool.drawSignHandles: No geometry information available for segment {segmentId}\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbool startNode = geometry.StartNodeId() == nodeId;\n\t\t\t\tbool incoming = geometry.IsIncoming(startNode);\n\n\t\t\t\tint numSignsPerRow = incoming ? 2 : 1;\n\n\t\t\t\tNetInfo segmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].Info;\n\n\t\t\t\tItemClass connectionClass = segmentInfo.GetConnectionClass();\n\t\t\t\tif (connectionClass.m_service != ItemClass.Service.Road)\n\t\t\t\t\tcontinue; // only for road junctions\n\n\t\t\t\t// draw all junction restriction signs\n\t\t\t\tVector3 segmentCenterPos = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId].m_bounds.center;\n\t\t\t\tVector3 yu = (segmentCenterPos - nodePos).normalized;\n\t\t\t\tVector3 xu = Vector3.Cross(yu, new Vector3(0, 1f, 0)).normalized;\n\t\t\t\tfloat f = viewOnly ? 6f : 7f; // reserved sign size in game coordinates\n\n\t\t\t\tVector3 centerStart = nodePos + yu * (viewOnly ? 5f : 14f);\n\t\t\t\tVector3 zero = centerStart - 0.5f * (float)(numSignsPerRow-1) * f * xu; // \"top left\"\n\t\t\t\tif (viewOnly) {\n\t\t\t\t\tif (Constants.ServiceFactory.SimulationService.LeftHandDrive)\n\t\t\t\t\t\tzero -= xu * 8f;\n\t\t\t\t\telse\n\t\t\t\t\t\tzero += xu * 8f;\n\t\t\t\t}\n\n\t\t\t\tbool signHovered;\n\t\t\t\tint x = 0;\n\t\t\t\tint y = 0;\n\t\t\t\tbool hasSignInPrevRow = false;\n\n\t\t\t\t// draw \"lane-changing when going straight allowed\" sign at (0; 0)\n\t\t\t\tbool allowed = JunctionRestrictionsManager.Instance.IsLaneChangingAllowedWhenGoingStraight(segmentId, startNode);\n\t\t\t\tbool configurable = Constants.ManagerFactory.JunctionRestrictionsManager.IsLaneChangingAllowedWhenGoingStraightConfigurable(segmentId, startNode, ref node);\n\t\t\t\tif (\n\t\t\t\t\tdebug ||\n\t\t\t\t\t(configurable &&\n\t\t\t\t\t(!viewOnly || allowed != Constants.ManagerFactory.JunctionRestrictionsManager.GetDefaultLaneChangingAllowedWhenGoingStraight(segmentId, startNode, ref node)))\n\t\t\t\t) {\n\t\t\t\t\tDrawSign(viewOnly, !configurable, ref camPos, ref xu, ref yu, f, ref zero, x, y, guiColor, allowed ? TextureResources.LaneChangeAllowedTexture2D : TextureResources.LaneChangeForbiddenTexture2D, out signHovered);\n\t\t\t\t\tif (signHovered && handleClick) {\n\t\t\t\t\t\thovered = true;\n\t\t\t\t\t\tif (MainTool.CheckClicked()) {\n\t\t\t\t\t\t\tJunctionRestrictionsManager.Instance.ToggleLaneChangingAllowedWhenGoingStraight(segmentId, startNode);\n\t\t\t\t\t\t\tstateUpdated = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t++x;\n\t\t\t\t\thasSignInPrevRow = true;\n\t\t\t\t}\n\n\t\t\t\t// draw \"u-turns allowed\" sign at (1; 0)\n\t\t\t\tallowed = JunctionRestrictionsManager.Instance.IsUturnAllowed(segmentId, startNode);\n\t\t\t\tconfigurable = Constants.ManagerFactory.JunctionRestrictionsManager.IsUturnAllowedConfigurable(segmentId, startNode, ref node);\n\t\t\t\tif (\n\t\t\t\t\tdebug ||\n\t\t\t\t\t(configurable &&\n\t\t\t\t\t(!viewOnly || allowed != Constants.ManagerFactory.JunctionRestrictionsManager.GetDefaultUturnAllowed(segmentId, startNode, ref node)))\n\t\t\t\t) {\n\t\t\t\t\tDrawSign(viewOnly, !configurable, ref camPos, ref xu, ref yu, f, ref zero, x, y, guiColor, allowed ? TextureResources.UturnAllowedTexture2D : TextureResources.UturnForbiddenTexture2D, out signHovered);\n\t\t\t\t\tif (signHovered && handleClick) {\n\t\t\t\t\t\thovered = true;\n\n\t\t\t\t\t\tif (MainTool.CheckClicked()) {\n\t\t\t\t\t\t\tif (!JunctionRestrictionsManager.Instance.ToggleUturnAllowed(segmentId, startNode)) {\n\t\t\t\t\t\t\t\t// TODO MainTool.ShowTooltip(Translation.GetString(\"...\"), Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_position);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tstateUpdated = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tx++;\n\t\t\t\t\thasSignInPrevRow = true;\n\t\t\t\t}\n\n                x = 0;\n\t\t\t\tif (hasSignInPrevRow) {\n\t\t\t\t\t++y;\n\t\t\t\t\thasSignInPrevRow = false;\n\t\t\t\t}\n\n\t\t\t\t// draw \"entering blocked junctions allowed\" sign at (0; 1)\n\t\t\t\tallowed = JunctionRestrictionsManager.Instance.IsEnteringBlockedJunctionAllowed(segmentId, startNode);\n\t\t\t\tconfigurable = Constants.ManagerFactory.JunctionRestrictionsManager.IsEnteringBlockedJunctionAllowedConfigurable(segmentId, startNode, ref node);\n\t\t\t\tif (\n\t\t\t\t\tdebug ||\n\t\t\t\t\t(configurable &&\n\t\t\t\t\t(!viewOnly || allowed != Constants.ManagerFactory.JunctionRestrictionsManager.GetDefaultEnteringBlockedJunctionAllowed(segmentId, startNode, ref node)))\n\t\t\t\t) {\n\t\t\t\t\tDrawSign(viewOnly, !configurable, ref camPos, ref xu, ref yu, f, ref zero, x, y, guiColor, allowed ? TextureResources.EnterBlockedJunctionAllowedTexture2D : TextureResources.EnterBlockedJunctionForbiddenTexture2D, out signHovered);\n\t\t\t\t\tif (signHovered && handleClick) {\n\t\t\t\t\t\thovered = true;\n\n\t\t\t\t\t\tif (MainTool.CheckClicked()) {\n\t\t\t\t\t\t\tJunctionRestrictionsManager.Instance.ToggleEnteringBlockedJunctionAllowed(segmentId, startNode);\n\t\t\t\t\t\t\tstateUpdated = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t++x;\n\t\t\t\t\thasSignInPrevRow = true;\n\t\t\t\t}\n\n\t\t\t\t// draw \"pedestrian crossing allowed\" sign at (1; 1)\n\t\t\t\tallowed = JunctionRestrictionsManager.Instance.IsPedestrianCrossingAllowed(segmentId, startNode);\n\t\t\t\tconfigurable = Constants.ManagerFactory.JunctionRestrictionsManager.IsPedestrianCrossingAllowedConfigurable(segmentId, startNode, ref node);\n\t\t\t\tif (\n\t\t\t\t\tdebug ||\n\t\t\t\t\t(configurable &&\n\t\t\t\t\t(!viewOnly || !allowed))\n\t\t\t\t) {\n\t\t\t\t\tDrawSign(viewOnly, !configurable, ref camPos, ref xu, ref yu, f, ref zero, x, y, guiColor, allowed ? TextureResources.PedestrianCrossingAllowedTexture2D : TextureResources.PedestrianCrossingForbiddenTexture2D, out signHovered);\n\t\t\t\t\tif (signHovered && handleClick) {\n\t\t\t\t\t\thovered = true;\n\n\t\t\t\t\t\tif (MainTool.CheckClicked()) {\n\t\t\t\t\t\t\tJunctionRestrictionsManager.Instance.TogglePedestrianCrossingAllowed(segmentId, startNode);\n\t\t\t\t\t\t\tstateUpdated = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tx++;\n\t\t\t\t\thasSignInPrevRow = true;\n\t\t\t\t}\n\n\t\t\t\tx = 0;\n\t\t\t\tif (hasSignInPrevRow) {\n\t\t\t\t\t++y;\n\t\t\t\t\thasSignInPrevRow = false;\n\t\t\t\t}\n\n\t\t\t\tif (Options.turnOnRedEnabled) {\n\t\t\t\t\tIJunctionRestrictionsManager junctionRestrictionsManager = Constants.ManagerFactory.JunctionRestrictionsManager;\n\t\t\t\t\tbool lhd = Constants.ServiceFactory.SimulationService.LeftHandDrive;\n\n\t\t\t\t\t// draw \"turn-left-on-red allowed\" sign at (2; 0)\n\t\t\t\t\tallowed = junctionRestrictionsManager.IsTurnOnRedAllowed(lhd, segmentId, startNode);\n\t\t\t\t\tconfigurable = junctionRestrictionsManager.IsTurnOnRedAllowedConfigurable(lhd, segmentId, startNode, ref node);\n\t\t\t\t\tif (\n\t\t\t\t\t\tdebug ||\n\t\t\t\t\t\t(configurable &&\n\t\t\t\t\t\t(!viewOnly || allowed != junctionRestrictionsManager.GetDefaultTurnOnRedAllowed(lhd, segmentId, startNode, ref node)))\n\t\t\t\t\t) {\n\t\t\t\t\t\tDrawSign(viewOnly, !configurable, ref camPos, ref xu, ref yu, f, ref zero, x, y, guiColor, allowed ? TextureResources.LeftOnRedAllowedTexture2D : TextureResources.LeftOnRedForbiddenTexture2D, out signHovered);\n\n\t\t\t\t\t\tif (signHovered && handleClick) {\n\t\t\t\t\t\t\thovered = true;\n\n\t\t\t\t\t\t\tif (MainTool.CheckClicked()) {\n\t\t\t\t\t\t\t\tjunctionRestrictionsManager.ToggleTurnOnRedAllowed(lhd, segmentId, startNode);\n\t\t\t\t\t\t\t\tstateUpdated = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\thasSignInPrevRow = true;\n\t\t\t\t\t}\n\t\t\t\t\tx++;\n\n\t\t\t\t\t// draw \"turn-right-on-red allowed\" sign at (2; 1)\n\t\t\t\t\tallowed = junctionRestrictionsManager.IsTurnOnRedAllowed(!lhd, segmentId, startNode);\n\t\t\t\t\tconfigurable = junctionRestrictionsManager.IsTurnOnRedAllowedConfigurable(!lhd, segmentId, startNode, ref node);\n\t\t\t\t\tif (\n\t\t\t\t\t\tdebug ||\n\t\t\t\t\t\t(configurable &&\n\t\t\t\t\t\t(!viewOnly || allowed != junctionRestrictionsManager.GetDefaultTurnOnRedAllowed(!lhd, segmentId, startNode, ref node)))\n\t\t\t\t\t) {\n\t\t\t\t\t\tDrawSign(viewOnly, !configurable, ref camPos, ref xu, ref yu, f, ref zero, x, y, guiColor, allowed ? TextureResources.RightOnRedAllowedTexture2D : TextureResources.RightOnRedForbiddenTexture2D, out signHovered);\n\n\t\t\t\t\t\tif (signHovered && handleClick) {\n\t\t\t\t\t\t\thovered = true;\n\n\t\t\t\t\t\t\tif (MainTool.CheckClicked()) {\n\t\t\t\t\t\t\t\tjunctionRestrictionsManager.ToggleTurnOnRedAllowed(!lhd, segmentId, startNode);\n\t\t\t\t\t\t\t\tstateUpdated = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\thasSignInPrevRow = true;\n\t\t\t\t\t}\n\t\t\t\t\tx++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tguiColor.a = 1f;\n\t\t\tGUI.color = guiColor;\n\n\t\t\treturn hovered;\n\t\t}\n\n\t\tprivate void DrawSign(bool viewOnly, bool small, ref Vector3 camPos, ref Vector3 xu, ref Vector3 yu, float f, ref Vector3 zero, int x, int y, Color guiColor, Texture2D signTexture, out bool hoveredHandle) {\n\t\t\tVector3 signCenter = zero + f * (float)x * xu + f * (float)y * yu; // in game coordinates\n\n\t\t\tVector3 signScreenPos;\n\t\t\tbool visible = MainTool.WorldToScreenPoint(signCenter, out signScreenPos);\n\n\t\t\tif (!visible) {\n\t\t\t\thoveredHandle = false;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tVector3 diff = signCenter - camPos;\n\n\t\t\tvar zoom = 1.0f / diff.magnitude * 100f * MainTool.GetBaseZoom();\n\t\t\tvar size = (small ? 0.75f : 1f) * (viewOnly ? 0.8f : 1f) * junctionRestrictionsSignSize * zoom;\n\n\t\t\tvar boundingBox = new Rect(signScreenPos.x - size / 2, signScreenPos.y - size / 2, size, size);\n\t\t\thoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox);\n\t\t\tguiColor.a = MainTool.GetHandleAlpha(hoveredHandle);\n\n\t\t\tGUI.color = guiColor;\n\t\t\tGUI.DrawTexture(boundingBox, signTexture);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/SubTools/LaneArrowTool.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing ColossalFramework.UI;\nusing CSUtil.Commons;\nusing GenericGameBridge.Service;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Geometry;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.State;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Util;\nusing UnityEngine;\n\nnamespace TrafficManager.UI.SubTools {\n\tpublic class LaneArrowTool : SubTool {\n\t\tprivate bool _cursorInSecondaryPanel;\n\n\t\tpublic LaneArrowTool(TrafficManagerTool mainTool) : base(mainTool) {\n\t\t\t\n\t\t}\n\n\t\tpublic override bool IsCursorInPanel() {\n\t\t\treturn base.IsCursorInPanel() || _cursorInSecondaryPanel;\n\t\t}\n\n\t\tpublic override void OnPrimaryClickOverlay() {\n\t\t\tif (HoveredNodeId == 0 || HoveredSegmentId == 0) return;\n\n\t\t\tvar netFlags = Singleton<NetManager>.instance.m_nodes.m_buffer[HoveredNodeId].m_flags;\n\n\t\t\tif ((netFlags & NetNode.Flags.Junction) == NetNode.Flags.None) return;\n\n\t\t\tif (Singleton<NetManager>.instance.m_segments.m_buffer[HoveredSegmentId].m_startNode != HoveredNodeId &&\n\t\t\t\tSingleton<NetManager>.instance.m_segments.m_buffer[HoveredSegmentId].m_endNode != HoveredNodeId)\n\t\t\t\treturn;\n\n\t\t\tSelectedSegmentId = HoveredSegmentId;\n\t\t\tSelectedNodeId = HoveredNodeId;\n\t\t}\n\n\t\tpublic override void OnSecondaryClickOverlay() {\n\t\t\tif (!IsCursorInPanel()) {\n\t\t\t\tSelectedSegmentId = 0;\n\t\t\t\tSelectedNodeId = 0;\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnToolGUI(Event e) {\n\t\t\t//base.OnToolGUI(e);\n\t\t\t_cursorInSecondaryPanel = false;\n\n\t\t\tif (SelectedNodeId == 0 || SelectedSegmentId == 0) return;\n\n\t\t\tint numDirections;\n\t\t\tint numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(SelectedSegmentId, SelectedNodeId, out numDirections, LaneArrowManager.VEHICLE_TYPES);\n\t\t\tif (numLanes <= 0) {\n\t\t\t\tSelectedNodeId = 0;\n\t\t\t\tSelectedSegmentId = 0;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tVector3 nodePos = Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNodeId].m_position;\n\n\t\t\tVector3 screenPos;\n\t\t\tbool visible = MainTool.WorldToScreenPoint(nodePos, out screenPos);\n\n\t\t\tif (!visible)\n\t\t\t\treturn;\n\n\t\t\tvar camPos = Singleton<SimulationManager>.instance.m_simulationView.m_position;\n\t\t\tvar diff = nodePos - camPos;\n\n\t\t\tif (diff.magnitude > TrafficManagerTool.MaxOverlayDistance)\n\t\t\t\treturn; // do not draw if too distant\n\n\t\t\tint width = numLanes * 128;\n\t\t\tvar windowRect3 = new Rect(screenPos.x - width / 2, screenPos.y - 70, width, 50);\n\t\t\tGUILayout.Window(250, windowRect3, _guiLaneChangeWindow, \"\", BorderlessStyle);\n\t\t\t_cursorInSecondaryPanel = windowRect3.Contains(Event.current.mousePosition);\n\t\t}\n\n\t\tpublic override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\t//Log._Debug($\"LaneArrow Overlay: {HoveredNodeId} {HoveredSegmentId} {SelectedNodeId} {SelectedSegmentId}\");\n\t\t\tif (!_cursorInSecondaryPanel && HoveredSegmentId != 0 && HoveredNodeId != 0 && (HoveredSegmentId != SelectedSegmentId || HoveredNodeId != SelectedNodeId)) {\n\t\t\t\tvar nodeFlags = netManager.m_nodes.m_buffer[HoveredNodeId].m_flags;\n\t\t\t\t\n\t\t\t\tif ((netManager.m_segments.m_buffer[HoveredSegmentId].m_startNode == HoveredNodeId || netManager.m_segments.m_buffer[HoveredSegmentId].m_endNode == HoveredNodeId) && (nodeFlags & NetNode.Flags.Junction) != NetNode.Flags.None) {\n\t\t\t\t\tNetTool.RenderOverlay(cameraInfo, ref Singleton<NetManager>.instance.m_segments.m_buffer[HoveredSegmentId], MainTool.GetToolColor(false, false),\n\t\t\t\t\t\tMainTool.GetToolColor(false, false));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (SelectedSegmentId == 0) return;\n\n\t\t\tNetTool.RenderOverlay(cameraInfo, ref Singleton<NetManager>.instance.m_segments.m_buffer[SelectedSegmentId], MainTool.GetToolColor(true, false), MainTool.GetToolColor(true, false));\n\t\t}\n\n\t\tprivate void _guiLaneChangeWindow(int num) {\n\t\t\tvar info = Singleton<NetManager>.instance.m_segments.m_buffer[SelectedSegmentId].Info;\n\n\t\t\tIList<LanePos> laneList = Constants.ServiceFactory.NetService.GetSortedLanes(SelectedSegmentId, ref Singleton<NetManager>.instance.m_segments.m_buffer[SelectedSegmentId], Singleton<NetManager>.instance.m_segments.m_buffer[SelectedSegmentId].m_startNode == SelectedNodeId, LaneArrowManager.LANE_TYPES, LaneArrowManager.VEHICLE_TYPES, true);\n\t\t\tSegmentGeometry geometry = SegmentGeometry.Get(SelectedSegmentId);\n\t\t\tif (geometry == null) {\n\t\t\t\tLog.Error($\"LaneArrowTool._guiLaneChangeWindow: No geometry information available for segment {SelectedSegmentId}\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbool startNode = geometry.StartNodeId() == SelectedNodeId;\n\n\t\t\tGUILayout.BeginHorizontal();\n\n\t\t\tfor (var i = 0; i < laneList.Count; i++) {\n\t\t\t\tvar flags = (NetLane.Flags)Singleton<NetManager>.instance.m_lanes.m_buffer[laneList[i].laneId].m_flags;\n\n\t\t\t\tvar style1 = new GUIStyle(\"button\");\n\t\t\t\tvar style2 = new GUIStyle(\"button\") {\n\t\t\t\t\tnormal = { textColor = new Color32(255, 0, 0, 255) },\n\t\t\t\t\thover = { textColor = new Color32(255, 0, 0, 255) },\n\t\t\t\t\tfocused = { textColor = new Color32(255, 0, 0, 255) }\n\t\t\t\t};\n\n\t\t\t\tvar laneStyle = new GUIStyle { contentOffset = new Vector2(12f, 0f) };\n\n\t\t\t\tvar laneTitleStyle = new GUIStyle {\n\t\t\t\t\tcontentOffset = new Vector2(36f, 2f),\n\t\t\t\t\tnormal = { textColor = new Color(1f, 1f, 1f) }\n\t\t\t\t};\n\n\t\t\t\tGUILayout.BeginVertical(laneStyle);\n\t\t\t\tGUILayout.Label(Translation.GetString(\"Lane\") + \" \" + (i + 1), laneTitleStyle);\n\t\t\t\tGUILayout.BeginVertical();\n\t\t\t\tGUILayout.BeginHorizontal();\n\t\t\t\tif (!Flags.applyLaneArrowFlags(laneList[i].laneId)) {\n\t\t\t\t\tFlags.removeLaneArrowFlags(laneList[i].laneId);\n\t\t\t\t}\n\t\t\t\tFlags.LaneArrowChangeResult res = Flags.LaneArrowChangeResult.Invalid;\n\t\t\t\tbool buttonClicked = false;\n\t\t\t\tif (GUILayout.Button(\"←\", ((flags & NetLane.Flags.Left) == NetLane.Flags.Left ? style1 : style2), GUILayout.Width(35), GUILayout.Height(25))) {\n\t\t\t\t\tbuttonClicked = true;\n\t\t\t\t\tLaneArrowManager.Instance.ToggleLaneArrows(laneList[i].laneId, startNode, Flags.LaneArrows.Left, out res);\n\t\t\t\t}\n\t\t\t\tif (GUILayout.Button(\"↑\", ((flags & NetLane.Flags.Forward) == NetLane.Flags.Forward ? style1 : style2), GUILayout.Width(25), GUILayout.Height(35))) {\n\t\t\t\t\tbuttonClicked = true;\n\t\t\t\t\tLaneArrowManager.Instance.ToggleLaneArrows(laneList[i].laneId, startNode, Flags.LaneArrows.Forward, out res);\n\t\t\t\t}\n\t\t\t\tif (GUILayout.Button(\"→\", ((flags & NetLane.Flags.Right) == NetLane.Flags.Right ? style1 : style2), GUILayout.Width(35), GUILayout.Height(25))) {\n\t\t\t\t\tbuttonClicked = true;\n\t\t\t\t\tLaneArrowManager.Instance.ToggleLaneArrows(laneList[i].laneId, startNode, Flags.LaneArrows.Right, out res);\n\t\t\t\t}\n\n\t\t\t\tif (buttonClicked) {\n\t\t\t\t\tswitch (res) {\n\t\t\t\t\t\tcase Flags.LaneArrowChangeResult.Invalid:\n\t\t\t\t\t\tcase Flags.LaneArrowChangeResult.Success:\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase Flags.LaneArrowChangeResult.HighwayArrows:\n\t\t\t\t\t\t\tMainTool.ShowTooltip(Translation.GetString(\"Lane_Arrow_Changer_Disabled_Highway\"));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase Flags.LaneArrowChangeResult.LaneConnection:\n\t\t\t\t\t\t\tMainTool.ShowTooltip(Translation.GetString(\"Lane_Arrow_Changer_Disabled_Connection\"));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tGUILayout.EndHorizontal();\n\t\t\t\tGUILayout.EndVertical();\n\t\t\t\tGUILayout.EndVertical();\n\t\t\t}\n\n\t\t\tGUILayout.EndHorizontal();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/SubTools/LaneConnectorTool.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Util;\nusing UnityEngine;\nusing TrafficManager.Manager;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\n\nnamespace TrafficManager.UI.SubTools {\n\tpublic class LaneConnectorTool : SubTool {\n\t\tenum MarkerSelectionMode {\n\t\t\tNone,\n\t\t\tSelectSource,\n\t\t\tSelectTarget\n\t\t}\n\n\t\tenum StayInLaneMode {\n\t\t\tNone,\n\t\t\tBoth,\n\t\t\tForward,\n\t\t\tBackward\n\t\t}\n\n\t\tprivate static readonly Color DefaultNodeMarkerColor = new Color(1f, 1f, 1f, 0.4f);\n\t\tprivate NodeLaneMarker selectedMarker = null;\n\t\tprivate NodeLaneMarker hoveredMarker = null;\n\t\tprivate Dictionary<ushort, List<NodeLaneMarker>> currentNodeMarkers;\n\t\tprivate StayInLaneMode stayInLaneMode = StayInLaneMode.None;\n\t\t//private bool initDone = false;\n\n\t\tclass NodeLaneMarker {\n\t\t\tinternal ushort segmentId;\n\t\t\tinternal ushort nodeId;\n\t\t\tinternal bool startNode;\n\t\t\tinternal Vector3 position;\n\t\t\tinternal Vector3 secondaryPosition;\n\t\t\tinternal bool isSource;\n\t\t\tinternal bool isTarget;\n\t\t\tinternal uint laneId;\n\t\t\tinternal int innerSimilarLaneIndex;\n\t\t\tinternal int segmentIndex;\n\t\t\tinternal float radius = 1f;\n\t\t\tinternal Color color;\n\t\t\tinternal List<NodeLaneMarker> connectedMarkers = new List<NodeLaneMarker>();\n\t\t\tinternal NetInfo.LaneType laneType;\n\t\t\tinternal VehicleInfo.VehicleType vehicleType;\n\t\t}\n\n\t\tpublic LaneConnectorTool(TrafficManagerTool mainTool) : base(mainTool) {\n\t\t\t//Log._Debug($\"TppLaneConnectorTool: Constructor called\");\n\t\t\tcurrentNodeMarkers = new Dictionary<ushort, List<NodeLaneMarker>>();\n\t\t}\n\n\t\tpublic override void OnToolGUI(Event e) {\n\t\t\t//Log._Debug($\"TppLaneConnectorTool: OnToolGUI. SelectedNodeId={SelectedNodeId} SelectedSegmentId={SelectedSegmentId} HoveredNodeId={HoveredNodeId} HoveredSegmentId={HoveredSegmentId} IsInsideUI={MainTool.GetToolController().IsInsideUI}\");\n\t\t}\n\n\t\tpublic override void RenderInfoOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\tShowOverlay(true, cameraInfo);\n\t\t}\n\n\t\tprivate void ShowOverlay(bool viewOnly, RenderManager.CameraInfo cameraInfo) {\n\t\t\tif (viewOnly && !Options.connectedLanesOverlay)\n\t\t\t\treturn;\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tvar camPos = Singleton<SimulationManager>.instance.m_simulationView.m_position;\n\t\t\t//Bounds bounds = new Bounds(Vector3.zero, Vector3.one);\n\t\t\tRay mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);\n\n\t\t\tfor (uint nodeId = 1; nodeId < NetManager.MAX_NODE_COUNT; ++nodeId) {\n\t\t\t\tif (!Constants.ServiceFactory.NetService.IsNodeValid((ushort)nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// TODO refactor connection class check\n\t\t\t\tItemClass connectionClass = NetManager.instance.m_nodes.m_buffer[nodeId].Info.GetConnectionClass();\n\t\t\t\tif (connectionClass == null ||\n\t\t\t\t\t!(connectionClass.m_service == ItemClass.Service.Road || (connectionClass.m_service == ItemClass.Service.PublicTransport &&\n\t\t\t\t\t(connectionClass.m_subService == ItemClass.SubService.PublicTransportTrain ||\n\t\t\t\t\tconnectionClass.m_subService == ItemClass.SubService.PublicTransportMetro ||\n\t\t\t\t\tconnectionClass.m_subService == ItemClass.SubService.PublicTransportMonorail)))) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tvar diff = NetManager.instance.m_nodes.m_buffer[nodeId].m_position - camPos;\n\t\t\t\tif (diff.magnitude > TrafficManagerTool.MaxOverlayDistance)\n\t\t\t\t\tcontinue; // do not draw if too distant\n\n\t\t\t\tList<NodeLaneMarker> nodeMarkers;\n\t\t\t\tbool hasMarkers = currentNodeMarkers.TryGetValue((ushort)nodeId, out nodeMarkers);\n\n\t\t\t\tif (!viewOnly && GetMarkerSelectionMode() == MarkerSelectionMode.None) {\n\t\t\t\t\tMainTool.DrawNodeCircle(cameraInfo, (ushort)nodeId, DefaultNodeMarkerColor, true);\n\t\t\t\t}\n\n\t\t\t\tif (hasMarkers) {\n\t\t\t\t\tforeach (NodeLaneMarker laneMarker in nodeMarkers) {\n\t\t\t\t\t\tif (!Constants.ServiceFactory.NetService.IsLaneValid(laneMarker.laneId)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tforeach (NodeLaneMarker targetLaneMarker in laneMarker.connectedMarkers) {\n\t\t\t\t\t\t\t// render lane connection from laneMarker to targetLaneMarker\n\t\t\t\t\t\t\tif (!Constants.ServiceFactory.NetService.IsLaneValid(targetLaneMarker.laneId)) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tRenderLane(cameraInfo, laneMarker.position, targetLaneMarker.position, NetManager.instance.m_nodes.m_buffer[nodeId].m_position, laneMarker.color);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!viewOnly && nodeId == SelectedNodeId) {\n\t\t\t\t\t\t\t//bounds.center = laneMarker.position;\n\t\t\t\t\t\t\tbool markerIsHovered = IsLaneMarkerHovered(laneMarker, ref mouseRay);// bounds.IntersectRay(mouseRay);\n\n\t\t\t\t\t\t\t// draw source marker in source selection mode,\n\t\t\t\t\t\t\t// draw target marker (if segment turning angles are within bounds) and selected source marker in target selection mode\n\t\t\t\t\t\t\tbool drawMarker = (GetMarkerSelectionMode() == MarkerSelectionMode.SelectSource && laneMarker.isSource) ||\n\t\t\t\t\t\t\t\t(GetMarkerSelectionMode() == MarkerSelectionMode.SelectTarget && (\n\t\t\t\t\t\t\t\t(laneMarker.isTarget &&\n\t\t\t\t\t\t\t\t(laneMarker.vehicleType & selectedMarker.vehicleType) != VehicleInfo.VehicleType.None &&\n\t\t\t\t\t\t\t\tCheckSegmentsTurningAngle(selectedMarker.segmentId, ref netManager.m_segments.m_buffer[selectedMarker.segmentId], selectedMarker.startNode, laneMarker.segmentId, ref netManager.m_segments.m_buffer[laneMarker.segmentId], laneMarker.startNode)\n\t\t\t\t\t\t\t\t) || laneMarker == selectedMarker));\n\t\t\t\t\t\t\t// highlight hovered marker and selected marker\n\t\t\t\t\t\t\tbool highlightMarker = drawMarker && (laneMarker == selectedMarker || markerIsHovered);\n\n\t\t\t\t\t\t\tif (drawMarker) {\n\t\t\t\t\t\t\t\tif (highlightMarker) {\n\t\t\t\t\t\t\t\t\tlaneMarker.radius = 2f;\n\t\t\t\t\t\t\t\t} else\n\t\t\t\t\t\t\t\t\tlaneMarker.radius = 1f;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tmarkerIsHovered = false;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (markerIsHovered) {\n\t\t\t\t\t\t\t\t/*if (hoveredMarker != sourceLaneMarker)\n\t\t\t\t\t\t\t\t\tLog._Debug($\"Marker @ lane {sourceLaneMarker.laneId} hovered\");*/\n\t\t\t\t\t\t\t\thoveredMarker = laneMarker;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (drawMarker) {\n\t\t\t\t\t\t\t\t//DrawLaneMarker(laneMarker, cameraInfo);\n\t\t\t\t\t\t\t\tRenderManager.instance.OverlayEffect.DrawCircle(cameraInfo, laneMarker.color, laneMarker.position, laneMarker.radius, laneMarker.position.y - 100f, laneMarker.position.y + 100f, false, true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate bool IsLaneMarkerHovered(NodeLaneMarker laneMarker, ref Ray mouseRay) {\n\t\t\tBounds bounds = new Bounds(Vector3.zero, Vector3.one);\n\t\t\tbounds.center = laneMarker.position;\n\t\t\tif (bounds.IntersectRay(mouseRay))\n\t\t\t\treturn true;\n\n\t\t\tbounds = new Bounds(Vector3.zero, Vector3.one);\n\t\t\tbounds.center = laneMarker.secondaryPosition;\n\t\t\treturn bounds.IntersectRay(mouseRay);\n\t\t}\n\n\t\tpublic override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\t//Log._Debug($\"TppLaneConnectorTool: RenderOverlay. SelectedNodeId={SelectedNodeId} SelectedSegmentId={SelectedSegmentId} HoveredNodeId={HoveredNodeId} HoveredSegmentId={HoveredSegmentId} IsInsideUI={MainTool.GetToolController().IsInsideUI}\");\n\t\t\t// draw lane markers and connections\n\n\t\t\thoveredMarker = null;\n\n\t\t\tShowOverlay(false, cameraInfo);\n\n\t\t\t// draw bezier from source marker to mouse position in target marker selection \n\t\t\tif (SelectedNodeId != 0) {\n\t\t\t\tif (GetMarkerSelectionMode() == MarkerSelectionMode.SelectTarget) {\n\t\t\t\t\tVector3 selNodePos = NetManager.instance.m_nodes.m_buffer[SelectedNodeId].m_position;\n\n\t\t\t\t\tToolBase.RaycastOutput output;\n\t\t\t\t\tif (RayCastSegmentAndNode(out output)) {\n\t\t\t\t\t\tRenderLane(cameraInfo, selectedMarker.position, output.m_hitPos, selNodePos, selectedMarker.color);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbool deleteAll = Input.GetKeyDown(KeyCode.Delete) || Input.GetKeyDown(KeyCode.Backspace);\n\t\t\t\tbool stayInLane = Input.GetKeyDown(KeyCode.S) && (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) && Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNodeId].CountSegments() == 2;\n\t\t\t\tif (stayInLane)\n\t\t\t\t\tdeleteAll = true;\n\n\t\t\t\tif (deleteAll) {\n\t\t\t\t\t// remove all connections at selected node\n\t\t\t\t\tLaneConnectionManager.Instance.RemoveLaneConnectionsFromNode(SelectedNodeId);\n\t\t\t\t\tRefreshCurrentNodeMarkers(SelectedNodeId);\n\t\t\t\t}\n\n\t\t\t\tif (stayInLane) {\n\t\t\t\t\t// \"stay in lane\"\n\t\t\t\t\tswitch (stayInLaneMode) {\n\t\t\t\t\t\tcase StayInLaneMode.None:\n\t\t\t\t\t\t\tstayInLaneMode = StayInLaneMode.Both;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase StayInLaneMode.Both:\n\t\t\t\t\t\t\tstayInLaneMode = StayInLaneMode.Forward;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase StayInLaneMode.Forward:\n\t\t\t\t\t\t\tstayInLaneMode = StayInLaneMode.Backward;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase StayInLaneMode.Backward:\n\t\t\t\t\t\t\tstayInLaneMode = StayInLaneMode.None;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (stayInLaneMode != StayInLaneMode.None) {\n\t\t\t\t\t\tList<NodeLaneMarker> nodeMarkers = GetNodeMarkers(SelectedNodeId, ref Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNodeId]);\n\t\t\t\t\t\tif (nodeMarkers != null) {\n\t\t\t\t\t\t\tselectedMarker = null;\n\t\t\t\t\t\t\tforeach (NodeLaneMarker sourceLaneMarker in nodeMarkers) {\n\t\t\t\t\t\t\t\tif (!sourceLaneMarker.isSource)\n\t\t\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\t\t\tif (stayInLaneMode == StayInLaneMode.Forward || stayInLaneMode == StayInLaneMode.Backward) {\n\t\t\t\t\t\t\t\t\tif (sourceLaneMarker.segmentIndex == 0 ^ stayInLaneMode == StayInLaneMode.Backward) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tforeach (NodeLaneMarker targetLaneMarker in nodeMarkers) {\n\t\t\t\t\t\t\t\t\tif (!targetLaneMarker.isTarget || targetLaneMarker.segmentId == sourceLaneMarker.segmentId)\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\t\t\t\tif (targetLaneMarker.innerSimilarLaneIndex == sourceLaneMarker.innerSimilarLaneIndex) {\n\t\t\t\t\t\t\t\t\t\tLog._Debug($\"Adding lane connection {sourceLaneMarker.laneId} -> {targetLaneMarker.laneId}\");\n\t\t\t\t\t\t\t\t\t\tLaneConnectionManager.Instance.AddLaneConnection(sourceLaneMarker.laneId, targetLaneMarker.laneId, sourceLaneMarker.startNode);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tRefreshCurrentNodeMarkers(SelectedNodeId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (GetMarkerSelectionMode() == MarkerSelectionMode.None && HoveredNodeId != 0) {\n\t\t\t\t// draw hovered node\n\t\t\t\tMainTool.DrawNodeCircle(cameraInfo, HoveredNodeId, Input.GetMouseButton(0));\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnPrimaryClickOverlay() {\n#if DEBUGCONN\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[23];\n\t\t\tif (debug)\n\t\t\t\tLog._Debug($\"TppLaneConnectorTool: OnPrimaryClickOverlay. SelectedNodeId={SelectedNodeId} SelectedSegmentId={SelectedSegmentId} HoveredNodeId={HoveredNodeId} HoveredSegmentId={HoveredSegmentId}\");\n#endif\n\n\t\t\tif (IsCursorInPanel())\n\t\t\t\treturn;\n\n\t\t\tif (GetMarkerSelectionMode() == MarkerSelectionMode.None) {\n\t\t\t\tif (HoveredNodeId != 0) {\n#if DEBUGCONN\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TppLaneConnectorTool: HoveredNode != 0\");\n#endif\n\n\t\t\t\t\tif (NetManager.instance.m_nodes.m_buffer[HoveredNodeId].CountSegments() < 2) {\n\t\t\t\t\t\t// this node cannot be configured (dead end)\n#if DEBUGCONN\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"TppLaneConnectorTool: Node is a dead end\");\n#endif\n\t\t\t\t\t\tSelectedNodeId = 0;\n\t\t\t\t\t\tselectedMarker = null;\n\t\t\t\t\t\tstayInLaneMode = StayInLaneMode.None;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (SelectedNodeId != HoveredNodeId) {\n#if DEBUGCONN\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"Node {HoveredNodeId} has been selected. Creating markers.\");\n#endif\n\n\t\t\t\t\t\t// selected node has changed. create markers\n\t\t\t\t\t\tList<NodeLaneMarker> markers = GetNodeMarkers(HoveredNodeId, ref Singleton<NetManager>.instance.m_nodes.m_buffer[HoveredNodeId]);\n\t\t\t\t\t\tif (markers != null) {\n\t\t\t\t\t\t\tSelectedNodeId = HoveredNodeId;\n\t\t\t\t\t\t\tselectedMarker = null;\n\t\t\t\t\t\t\tstayInLaneMode = StayInLaneMode.None;\n\n\t\t\t\t\t\t\tcurrentNodeMarkers[SelectedNodeId] = markers;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//this.allNodeMarkers[SelectedNodeId] = GetNodeMarkers(SelectedNodeId);\n\t\t\t\t\t}\n\t\t\t\t} else {\n#if DEBUGCONN\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TppLaneConnectorTool: Node {SelectedNodeId} has been deselected.\");\n#endif\n\n\t\t\t\t\t// click on free spot. deselect node\n\t\t\t\t\tSelectedNodeId = 0;\n\t\t\t\t\tselectedMarker = null;\n\t\t\t\t\tstayInLaneMode = StayInLaneMode.None;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (hoveredMarker != null) {\n\t\t\t\tstayInLaneMode = StayInLaneMode.None;\n\n#if DEBUGCONN\n\t\t\t\tif (debug)\n\t\t\t\t\tLog._Debug($\"TppLaneConnectorTool: hoveredMarker != null. selMode={GetMarkerSelectionMode()}\");\n#endif\n\n\t\t\t\t// hovered marker has been clicked\n\t\t\t\tif (GetMarkerSelectionMode() == MarkerSelectionMode.SelectSource) {\n\t\t\t\t\t// select source marker\n\t\t\t\t\tselectedMarker = hoveredMarker;\n#if DEBUGCONN\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TppLaneConnectorTool: set selected marker\");\n#endif\n\t\t\t\t} else if (GetMarkerSelectionMode() == MarkerSelectionMode.SelectTarget) {\n\t\t\t\t\t// select target marker\n\t\t\t\t\t//bool success = false;\n\t\t\t\t\tif (LaneConnectionManager.Instance.RemoveLaneConnection(selectedMarker.laneId, hoveredMarker.laneId, selectedMarker.startNode)) { // try to remove connection\n\t\t\t\t\t\tselectedMarker.connectedMarkers.Remove(hoveredMarker);\n#if DEBUGCONN\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"TppLaneConnectorTool: removed lane connection: {selectedMarker.laneId}, {hoveredMarker.laneId}\");\n#endif\n\t\t\t\t\t\t//success = true;\n\t\t\t\t\t} else if (LaneConnectionManager.Instance.AddLaneConnection(selectedMarker.laneId, hoveredMarker.laneId, selectedMarker.startNode)) { // try to add connection\n\t\t\t\t\t\tselectedMarker.connectedMarkers.Add(hoveredMarker);\n#if DEBUGCONN\n\t\t\t\t\t\tif (debug)\n\t\t\t\t\t\t\tLog._Debug($\"TppLaneConnectorTool: added lane connection: {selectedMarker.laneId}, {hoveredMarker.laneId}\");\n#endif\n\t\t\t\t\t\t//success = true;\n\t\t\t\t\t}\n\n\t\t\t\t\t/*if (success) {\n\t\t\t\t\t\t// connection has been modified. switch back to source marker selection\n\t\t\t\t\t\tLog._Debug($\"TppLaneConnectorTool: switch back to source marker selection\");\n\t\t\t\t\t\tselectedMarker = null;\n\t\t\t\t\t\tselMode = MarkerSelectionMode.SelectSource;\n\t\t\t\t\t}*/\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnSecondaryClickOverlay() {\n#if DEBUGCONN\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[23];\n#endif\n\n\t\t\tif (IsCursorInPanel())\n\t\t\t\treturn;\n\n\t\t\tswitch (GetMarkerSelectionMode()) {\n\t\t\t\tcase MarkerSelectionMode.None:\n\t\t\t\tdefault:\n#if DEBUGCONN\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TppLaneConnectorTool: OnSecondaryClickOverlay: nothing to do\");\n#endif\n\t\t\t\t\tstayInLaneMode = StayInLaneMode.None;\n\t\t\t\t\tbreak;\n\t\t\t\tcase MarkerSelectionMode.SelectSource:\n\t\t\t\t\t// deselect node\n#if DEBUGCONN\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TppLaneConnectorTool: OnSecondaryClickOverlay: selected node id = 0\");\n#endif\n\t\t\t\t\tSelectedNodeId = 0;\n\t\t\t\t\tbreak;\n\t\t\t\tcase MarkerSelectionMode.SelectTarget:\n\t\t\t\t\t// deselect source marker\n#if DEBUGCONN\n\t\t\t\t\tif (debug)\n\t\t\t\t\t\tLog._Debug($\"TppLaneConnectorTool: OnSecondaryClickOverlay: switch to selected source mode\");\n#endif\n\t\t\t\t\tselectedMarker = null;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnActivate() {\n#if DEBUGCONN\n\t\t\tbool debug = GlobalConfig.Instance.Debug.Switches[23];\n\t\t\tif (debug)\n\t\t\t\tLog._Debug(\"TppLaneConnectorTool: OnActivate\");\n#endif\n\t\t\tSelectedNodeId = 0;\n\t\t\tselectedMarker = null;\n\t\t\thoveredMarker = null;\n\t\t\tstayInLaneMode = StayInLaneMode.None;\n\t\t\tRefreshCurrentNodeMarkers();\n\t\t}\n\n\t\tprivate void RefreshCurrentNodeMarkers(ushort forceNodeId=0) {\n\t\t\tif (forceNodeId == 0) {\n\t\t\t\tcurrentNodeMarkers.Clear();\n\t\t\t} else {\n\t\t\t\tcurrentNodeMarkers.Remove(forceNodeId);\n\t\t\t}\n\n\t\t\tfor (uint nodeId = (forceNodeId == 0 ? 1u : forceNodeId); nodeId <= (forceNodeId == 0 ? NetManager.MAX_NODE_COUNT-1 : forceNodeId); ++nodeId) {\n\t\t\t\tif (!Constants.ServiceFactory.NetService.IsNodeValid((ushort)nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (! LaneConnectionManager.Instance.HasNodeConnections((ushort)nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tList<NodeLaneMarker> nodeMarkers = GetNodeMarkers((ushort)nodeId, ref Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId]);\n\t\t\t\tif (nodeMarkers == null)\n\t\t\t\t\tcontinue;\n\t\t\t\tcurrentNodeMarkers[(ushort)nodeId] = nodeMarkers;\n\t\t\t}\n\t\t}\n\n\t\tprivate MarkerSelectionMode GetMarkerSelectionMode() {\n\t\t\tif (SelectedNodeId == 0)\n\t\t\t\treturn MarkerSelectionMode.None;\n\t\t\tif (selectedMarker == null)\n\t\t\t\treturn MarkerSelectionMode.SelectSource;\n\t\t\treturn MarkerSelectionMode.SelectTarget;\n\t\t}\n\n\t\tpublic override void Cleanup() {\n\t\t\t\n\t\t}\n\n\t\tpublic override void Initialize() {\n\t\t\tbase.Initialize();\n\t\t\tCleanup();\n\t\t\tif (Options.connectedLanesOverlay) {\n\t\t\t\tRefreshCurrentNodeMarkers();\n\t\t\t} else {\n\t\t\t\tcurrentNodeMarkers.Clear();\n\t\t\t}\n\t\t}\n\n\t\tprivate List<NodeLaneMarker> GetNodeMarkers(ushort nodeId, ref NetNode node) {\n\t\t\tif (nodeId == 0)\n\t\t\t\treturn null;\n\t\t\tif ((node.m_flags & NetNode.Flags.Created) == NetNode.Flags.None)\n\t\t\t\treturn null;\n\n\t\t\tList<NodeLaneMarker> nodeMarkers = new List<NodeLaneMarker>();\n\t\t\tLaneConnectionManager connManager = LaneConnectionManager.Instance;\n\n\t\t\tint offsetMultiplier = node.CountSegments() <= 2 ? 3 : 1;\n\t\t\tfor (int i = 0; i < 8; i++) {\n\t\t\t\tushort segmentId = node.GetSegment(i);\n\t\t\t\tif (segmentId == 0)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tbool isEndNode = NetManager.instance.m_segments.m_buffer[segmentId].m_endNode == nodeId;\n\t\t\t\tVector3 offset = NetManager.instance.m_segments.m_buffer[segmentId].FindDirection(segmentId, nodeId) * offsetMultiplier;\n\t\t\t\tNetInfo.Lane[] lanes = NetManager.instance.m_segments.m_buffer[segmentId].Info.m_lanes;\n\t\t\t\tuint laneId = NetManager.instance.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\t\tfor (byte laneIndex = 0; laneIndex < lanes.Length && laneId != 0; laneIndex++) {\n\t\t\t\t\tNetInfo.Lane laneInfo = lanes[laneIndex];\n\t\t\t\t\tif ((laneInfo.m_laneType & LaneConnectionManager.LANE_TYPES) != NetInfo.LaneType.None &&\n\t\t\t\t\t\t(laneInfo.m_vehicleType & LaneConnectionManager.VEHICLE_TYPES) != VehicleInfo.VehicleType.None) {\n\n\t\t\t\t\t\tVector3? pos = null;\n\t\t\t\t\t\tbool isSource = false;\n\t\t\t\t\t\tbool isTarget = false;\n\t\t\t\t\t\tif (connManager.GetLaneEndPoint(segmentId, !isEndNode, laneIndex, laneId, laneInfo, out isSource, out isTarget, out pos)) {\n\n\t\t\t\t\t\t\tpos = (Vector3)pos + offset;\n\t\t\t\t\t\t\tfloat terrainY = Singleton<TerrainManager>.instance.SampleDetailHeightSmooth(((Vector3)pos));\n\t\t\t\t\t\t\tVector3 finalPos = new Vector3(((Vector3)pos).x, terrainY, ((Vector3)pos).z);\n\n\t\t\t\t\t\t\tnodeMarkers.Add(new NodeLaneMarker() {\n\t\t\t\t\t\t\t\tsegmentId = segmentId,\n\t\t\t\t\t\t\t\tlaneId = laneId,\n\t\t\t\t\t\t\t\tnodeId = nodeId,\n\t\t\t\t\t\t\t\tstartNode = !isEndNode,\n\t\t\t\t\t\t\t\tposition = finalPos,\n\t\t\t\t\t\t\t\tsecondaryPosition = (Vector3)pos,\n\t\t\t\t\t\t\t\tcolor = colors[nodeMarkers.Count % colors.Length],\n\t\t\t\t\t\t\t\tisSource = isSource,\n\t\t\t\t\t\t\t\tisTarget = isTarget,\n\t\t\t\t\t\t\t\tlaneType = laneInfo.m_laneType,\n\t\t\t\t\t\t\t\tvehicleType = laneInfo.m_vehicleType,\n\t\t\t\t\t\t\t\tinnerSimilarLaneIndex = ((byte)(laneInfo.m_direction & NetInfo.Direction.Forward) != 0) ? laneInfo.m_similarLaneIndex : laneInfo.m_similarLaneCount - laneInfo.m_similarLaneIndex - 1,\n\t\t\t\t\t\t\t\tsegmentIndex = i\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tlaneId = NetManager.instance.m_lanes.m_buffer[laneId].m_nextLane;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (nodeMarkers.Count == 0)\n\t\t\t\treturn null;\n\n\t\t\tforeach (NodeLaneMarker laneMarker1 in nodeMarkers) {\n\t\t\t\tif (!laneMarker1.isSource)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tuint[] connections = LaneConnectionManager.Instance.GetLaneConnections(laneMarker1.laneId, laneMarker1.startNode);\n\t\t\t\tif (connections == null || connections.Length == 0)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tforeach (NodeLaneMarker laneMarker2 in nodeMarkers) {\n\t\t\t\t\tif (!laneMarker2.isTarget)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tif (connections.Contains(laneMarker2.laneId))\n\t\t\t\t\t\tlaneMarker1.connectedMarkers.Add(laneMarker2);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn nodeMarkers;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Checks if the turning angle between two segments at the given node is within bounds.\n\t\t/// </summary>\n\t\t/// <param name=\"sourceSegmentId\"></param>\n\t\t/// <param name=\"sourceSegment\"></param>\n\t\t/// <param name=\"sourceStartNode\"></param>\n\t\t/// <param name=\"targetSegmentId\"></param>\n\t\t/// <param name=\"targetSegment\"></param>\n\t\t/// <param name=\"targetStartNode\"></param>\n\t\t/// <returns></returns>\n\t\tprivate bool CheckSegmentsTurningAngle(ushort sourceSegmentId, ref NetSegment sourceSegment, bool sourceStartNode, ushort targetSegmentId, ref NetSegment targetSegment, bool targetStartNode) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tNetInfo sourceSegmentInfo = netManager.m_segments.m_buffer[sourceSegmentId].Info;\n\t\t\tNetInfo targetSegmentInfo = netManager.m_segments.m_buffer[targetSegmentId].Info;\n\n\t\t\tfloat turningAngle = 0.01f - Mathf.Min(sourceSegmentInfo.m_maxTurnAngleCos, targetSegmentInfo.m_maxTurnAngleCos);\n\t\t\tif (turningAngle < 1f) {\n\t\t\t\tVector3 sourceDirection;\n\t\t\t\tif (sourceStartNode) {\n\t\t\t\t\tsourceDirection = sourceSegment.m_startDirection;\n\t\t\t\t} else {\n\t\t\t\t\tsourceDirection = sourceSegment.m_endDirection;\n\t\t\t\t}\n\n\t\t\t\tVector3 targetDirection;\n\t\t\t\tif (targetStartNode) {\n\t\t\t\t\ttargetDirection = targetSegment.m_startDirection;\n\t\t\t\t} else {\n\t\t\t\t\ttargetDirection = targetSegment.m_endDirection;\n\t\t\t\t}\n\t\t\t\tfloat dirDotProd = sourceDirection.x * targetDirection.x + sourceDirection.z * targetDirection.z;\n\t\t\t\treturn dirDotProd < turningAngle;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate void RenderLane(RenderManager.CameraInfo cameraInfo, Vector3 start, Vector3 end, Vector3 middlePoint, Color color, float size = 0.1f) {\n\t\t\tBezier3 bezier;\n\t\t\tbezier.a = start;\n\t\t\tbezier.d = end;\n\t\t\tNetSegment.CalculateMiddlePoints(bezier.a, (middlePoint - bezier.a).normalized, bezier.d, (middlePoint - bezier.d).normalized, false, false, out bezier.b, out bezier.c);\n\n\t\t\tRenderManager.instance.OverlayEffect.DrawBezier(cameraInfo, color, bezier, size, 0, 0, -1f, 1280f, false, true);\n\t\t}\n\n\t\tprivate bool RayCastSegmentAndNode(out ToolBase.RaycastOutput output) {\n\t\t\tToolBase.RaycastInput input = new ToolBase.RaycastInput(Camera.main.ScreenPointToRay(Input.mousePosition), Camera.main.farClipPlane);\n\t\t\tinput.m_netService.m_service = ItemClass.Service.Road;\n\t\t\tinput.m_netService.m_itemLayers = ItemClass.Layer.Default | ItemClass.Layer.MetroTunnels;\n\t\t\tinput.m_ignoreSegmentFlags = NetSegment.Flags.None;\n\t\t\tinput.m_ignoreNodeFlags = NetNode.Flags.None;\n\t\t\tinput.m_ignoreTerrain = true;\n\n\t\t\treturn MainTool.DoRayCast(input, out output);\n\t\t}\n\n\t\tprivate static readonly Color32[] colors = new Color32[]\n\t\t{\n\t\t\tnew Color32(161, 64, 206, 255),\n\t\t\tnew Color32(79, 251, 8, 255),\n\t\t\tnew Color32(243, 96, 44, 255),\n\t\t\tnew Color32(45, 106, 105, 255),\n\t\t\tnew Color32(253, 165, 187, 255),\n\t\t\tnew Color32(90, 131, 14, 255),\n\t\t\tnew Color32(58, 20, 70, 255),\n\t\t\tnew Color32(248, 246, 183, 255),\n\t\t\tnew Color32(255, 205, 29, 255),\n\t\t\tnew Color32(91, 50, 18, 255),\n\t\t\tnew Color32(76, 239, 155, 255),\n\t\t\tnew Color32(241, 25, 130, 255),\n\t\t\tnew Color32(125, 197, 240, 255),\n\t\t\tnew Color32(57, 102, 187, 255),\n\t\t\tnew Color32(160, 27, 61, 255),\n\t\t\tnew Color32(167, 251, 107, 255),\n\t\t\tnew Color32(165, 94, 3, 255),\n\t\t\tnew Color32(204, 18, 161, 255),\n\t\t\tnew Color32(208, 136, 237, 255),\n\t\t\tnew Color32(232, 211, 202, 255),\n\t\t\tnew Color32(45, 182, 15, 255),\n\t\t\tnew Color32(8, 40, 47, 255),\n\t\t\tnew Color32(249, 172, 142, 255),\n\t\t\tnew Color32(248, 99, 101, 255),\n\t\t\tnew Color32(180, 250, 208, 255),\n\t\t\tnew Color32(126, 25, 77, 255),\n\t\t\tnew Color32(243, 170, 55, 255),\n\t\t\tnew Color32(47, 69, 126, 255),\n\t\t\tnew Color32(50, 105, 70, 255),\n\t\t\tnew Color32(156, 49, 1, 255),\n\t\t\tnew Color32(233, 231, 255, 255),\n\t\t\tnew Color32(107, 146, 253, 255),\n\t\t\tnew Color32(127, 35, 26, 255),\n\t\t\tnew Color32(240, 94, 222, 255),\n\t\t\tnew Color32(58, 28, 24, 255),\n\t\t\tnew Color32(165, 179, 240, 255),\n\t\t\tnew Color32(239, 93, 145, 255),\n\t\t\tnew Color32(47, 110, 138, 255),\n\t\t\tnew Color32(57, 195, 101, 255),\n\t\t\tnew Color32(124, 88, 213, 255),\n\t\t\tnew Color32(252, 220, 144, 255),\n\t\t\tnew Color32(48, 106, 224, 255),\n\t\t\tnew Color32(90, 109, 28, 255),\n\t\t\tnew Color32(56, 179, 208, 255),\n\t\t\tnew Color32(239, 73, 177, 255),\n\t\t\tnew Color32(84, 60, 2, 255),\n\t\t\tnew Color32(169, 104, 238, 255),\n\t\t\tnew Color32(97, 201, 238, 255),\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/SubTools/ManualTrafficLightsTool.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing UnityEngine;\nusing TrafficManager.Manager;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Geometry.Impl;\nusing ColossalFramework.UI;\n\nnamespace TrafficManager.UI.SubTools {\n\tpublic class ManualTrafficLightsTool : SubTool {\n\t\tprivate readonly int[] _hoveredButton = new int[2];\n\t\tprivate readonly GUIStyle _counterStyle = new GUIStyle();\n\n\t\tpublic ManualTrafficLightsTool(TrafficManagerTool mainTool) : base(mainTool) {\n\t\t\t\n\t\t}\n\n\t\tpublic override void OnSecondaryClickOverlay() {\n\t\t\tif (IsCursorInPanel())\n\t\t\t\treturn;\n\t\t\tCleanup();\n\t\t\tSelectedNodeId = 0;\n\t\t}\n\n\t\tpublic override void OnPrimaryClickOverlay() {\n\t\t\tif (IsCursorInPanel())\n\t\t\t\treturn;\n\t\t\tif (SelectedNodeId != 0) return;\n\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\t\t\tTrafficPriorityManager prioMan = TrafficPriorityManager.Instance;\n\n\t\t\tif (!tlsMan.TrafficLightSimulations[HoveredNodeId].IsTimedLight()) {\n\t\t\t\tif ((Singleton<NetManager>.instance.m_nodes.m_buffer[HoveredNodeId].m_flags & NetNode.Flags.TrafficLights) == NetNode.Flags.None) {\n\t\t\t\t\tprioMan.RemovePrioritySignsFromNode(HoveredNodeId);\n\t\t\t\t\tTrafficLightManager.Instance.AddTrafficLight(HoveredNodeId, ref Singleton<NetManager>.instance.m_nodes.m_buffer[HoveredNodeId]);\n\t\t\t\t}\n\n\t\t\t\tif (tlsMan.SetUpManualTrafficLight(HoveredNodeId)) {\n\t\t\t\t\tSelectedNodeId = HoveredNodeId;\n\t\t\t\t}\n\n\t\t\t\t/*for (var s = 0; s < 8; s++) {\n\t\t\t\t\tvar segment = Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNodeId].GetSegment(s);\n\t\t\t\t\tif (segment != 0 && !TrafficPriority.IsPrioritySegment(SelectedNodeId, segment)) {\n\t\t\t\t\t\tTrafficPriority.AddPrioritySegment(SelectedNodeId, segment, SegmentEnd.PriorityType.None);\n\t\t\t\t\t}\n\t\t\t\t}*/\n\t\t\t} else {\n\t\t\t\tMainTool.ShowTooltip(Translation.GetString(\"NODE_IS_TIMED_LIGHT\"));\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnToolGUI(Event e) {\n\t\t\tvar hoveredSegment = false;\n\n\t\t\tif (SelectedNodeId != 0) {\n\t\t\t\tCustomSegmentLightsManager customTrafficLightsManager = CustomSegmentLightsManager.Instance;\n\t\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\t\t\t\tJunctionRestrictionsManager junctionRestrictionsManager = JunctionRestrictionsManager.Instance;\n\n\t\t\t\tif (!tlsMan.HasManualSimulation(SelectedNodeId)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttlsMan.TrafficLightSimulations[SelectedNodeId].Housekeeping();\n\n\t\t\t\t/*if (Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNode].CountSegments() == 2) {\n\t\t\t\t\t_guiManualTrafficLightsCrosswalk(ref Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNode]);\n\t\t\t\t\treturn;\n\t\t\t\t}*/ // TODO check\n\n\t\t\t\tNodeGeometry nodeGeometry = NodeGeometry.Get(SelectedNodeId);\n\t\t\t\tforeach (SegmentEndGeometry end in nodeGeometry.SegmentEndGeometries) {\n\t\t\t\t\tif (end == null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tvar position = CalculateNodePositionForSegment(Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNodeId], ref Singleton<NetManager>.instance.m_segments.m_buffer[end.SegmentId]);\n\t\t\t\t\tvar segmentLights = customTrafficLightsManager.GetSegmentLights(end.SegmentId, end.StartNode, false);\n\t\t\t\t\tif (segmentLights == null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tbool showPedLight = segmentLights.PedestrianLightState != null && junctionRestrictionsManager.IsPedestrianCrossingAllowed(segmentLights.SegmentId, segmentLights.StartNode);\n\n\t\t\t\t\tVector3 screenPos;\n\t\t\t\t\tbool visible = MainTool.WorldToScreenPoint(position, out screenPos);\n\n\t\t\t\t\tif (!visible)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tvar diff = position - Camera.main.transform.position;\n\t\t\t\t\tvar zoom = 1.0f / diff.magnitude * 100f;\n\n\t\t\t\t\t// original / 2.5\n\t\t\t\t\tvar lightWidth = 41f * zoom;\n\t\t\t\t\tvar lightHeight = 97f * zoom;\n\n\t\t\t\t\tvar pedestrianWidth = 36f * zoom;\n\t\t\t\t\tvar pedestrianHeight = 61f * zoom;\n\n\t\t\t\t\t// SWITCH MODE BUTTON\n\t\t\t\t\tvar modeWidth = 41f * zoom;\n\t\t\t\t\tvar modeHeight = 38f * zoom;\n\n\t\t\t\t\tvar guiColor = GUI.color;\n\n\t\t\t\t\tif (showPedLight) {\n\t\t\t\t\t\t// pedestrian light\n\n\t\t\t\t\t\t// SWITCH MANUAL PEDESTRIAN LIGHT BUTTON\n\t\t\t\t\t\thoveredSegment = RenderManualPedestrianLightSwitch(zoom, end.SegmentId, screenPos, lightWidth, segmentLights, hoveredSegment);\n\n\t\t\t\t\t\t// SWITCH PEDESTRIAN LIGHT\n\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == end.SegmentId && _hoveredButton[1] == 2 && segmentLights.ManualPedestrianMode);\n\t\t\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\t\t\tvar myRect3 = new Rect(screenPos.x - pedestrianWidth / 2 - lightWidth + 5f * zoom, screenPos.y - pedestrianHeight / 2 + 22f * zoom, pedestrianWidth, pedestrianHeight);\n\n\t\t\t\t\t\tswitch (segmentLights.PedestrianLightState) {\n\t\t\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\t\t\t\tGUI.DrawTexture(myRect3, TextureResources.PedestrianGreenLightTexture2D);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tGUI.DrawTexture(myRect3, TextureResources.PedestrianRedLightTexture2D);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\thoveredSegment = IsPedestrianLightHovered(myRect3, end.SegmentId, hoveredSegment, segmentLights);\n\t\t\t\t\t}\n\n\t\t\t\t\tint lightOffset = -1;\n\t\t\t\t\tforeach (ExtVehicleType vehicleType in segmentLights.VehicleTypes) {\n\t\t\t\t\t\t++lightOffset;\n\t\t\t\t\t\tICustomSegmentLight segmentLight = segmentLights.GetCustomLight(vehicleType);\n\n\t\t\t\t\t\tVector3 offsetScreenPos = screenPos;\n\t\t\t\t\t\toffsetScreenPos.y -= (lightHeight + 10f * zoom) * lightOffset;\n\n\t\t\t\t\t\tSetAlpha(end.SegmentId, -1);\n\n\t\t\t\t\t\tvar myRect1 = new Rect(offsetScreenPos.x - modeWidth / 2, offsetScreenPos.y - modeHeight / 2 + modeHeight - 7f * zoom, modeWidth, modeHeight);\n\n\t\t\t\t\t\tGUI.DrawTexture(myRect1, TextureResources.LightModeTexture2D);\n\n\t\t\t\t\t\thoveredSegment = GetHoveredSegment(myRect1, end.SegmentId, hoveredSegment, segmentLight);\n\n\t\t\t\t\t\t// COUNTER\n\t\t\t\t\t\thoveredSegment = RenderCounter(end.SegmentId, offsetScreenPos, modeWidth, modeHeight, zoom, segmentLights, hoveredSegment);\n\n\t\t\t\t\t\tif (vehicleType != ExtVehicleType.None) {\n\t\t\t\t\t\t\t// Info sign\n\t\t\t\t\t\t\tvar infoWidth = 56.125f * zoom;\n\t\t\t\t\t\t\tvar infoHeight = 51.375f * zoom;\n\n\t\t\t\t\t\t\tint numInfos = 0;\n\t\t\t\t\t\t\tfor (int k = 0; k < TrafficManagerTool.InfoSignsToDisplay.Length; ++k) {\n\t\t\t\t\t\t\t\tif ((TrafficManagerTool.InfoSignsToDisplay[k] & vehicleType) == ExtVehicleType.None)\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\tvar infoRect = new Rect(offsetScreenPos.x + modeWidth / 2f + 7f * zoom * (float)(numInfos + 1) + infoWidth * (float)numInfos, offsetScreenPos.y - infoHeight / 2f, infoWidth, infoHeight);\n\t\t\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(false);\n\t\t\t\t\t\t\t\tGUI.DrawTexture(infoRect, TextureResources.VehicleInfoSignTextures[TrafficManagerTool.InfoSignsToDisplay[k]]);\n\t\t\t\t\t\t\t\t++numInfos;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (end.OutgoingOneWay) continue;\n\n\t\t\t\t\t\tvar hasLeftSegment = end.NumLeftSegments > 0;\n\t\t\t\t\t\tvar hasForwardSegment = end.NumStraightSegments > 0;\n\t\t\t\t\t\tvar hasRightSegment = end.NumRightSegments > 0;\n\n\t\t\t\t\t\tswitch (segmentLight.CurrentMode) {\n\t\t\t\t\t\t\tcase LightMode.Simple:\n\t\t\t\t\t\t\t\thoveredSegment = SimpleManualSegmentLightMode(end.SegmentId, offsetScreenPos, lightWidth, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase LightMode.SingleLeft:\n\t\t\t\t\t\t\t\thoveredSegment = LeftForwardRManualSegmentLightMode(hasLeftSegment, end.SegmentId, offsetScreenPos, lightWidth, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment, hasForwardSegment, hasRightSegment);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase LightMode.SingleRight:\n\t\t\t\t\t\t\t\thoveredSegment = RightForwardLSegmentLightMode(end.SegmentId, offsetScreenPos, lightWidth, pedestrianWidth, zoom, lightHeight, hasForwardSegment, hasLeftSegment, segmentLight, hasRightSegment, hoveredSegment);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t// left arrow light\n\t\t\t\t\t\t\t\tif (hasLeftSegment)\n\t\t\t\t\t\t\t\t\thoveredSegment = LeftArrowLightMode(end.SegmentId, lightWidth, hasRightSegment, hasForwardSegment, offsetScreenPos, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment);\n\n\t\t\t\t\t\t\t\t// forward arrow light\n\t\t\t\t\t\t\t\tif (hasForwardSegment)\n\t\t\t\t\t\t\t\t\thoveredSegment = ForwardArrowLightMode(end.SegmentId, lightWidth, hasRightSegment, offsetScreenPos, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment);\n\n\t\t\t\t\t\t\t\t// right arrow light\n\t\t\t\t\t\t\t\tif (hasRightSegment)\n\t\t\t\t\t\t\t\t\thoveredSegment = RightArrowLightMode(end.SegmentId, offsetScreenPos, lightWidth, pedestrianWidth, zoom, lightHeight, segmentLight, hoveredSegment);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (hoveredSegment) return;\n\t\t\t_hoveredButton[0] = 0;\n\t\t\t_hoveredButton[1] = 0;\n\t\t}\n\n\t\tpublic override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\tif (SelectedNodeId != 0) {\n\t\t\t\tRenderManualNodeOverlays(cameraInfo);\n\t\t\t} else {\n\t\t\t\tRenderManualSelectionOverlay(cameraInfo);\n\t\t\t}\n\t\t}\n\n\t\tprivate bool RenderManualPedestrianLightSwitch(float zoom, int segmentId, Vector3 screenPos, float lightWidth,\n\t\t\tICustomSegmentLights segmentLights, bool hoveredSegment) {\n\t\t\tif (segmentLights.PedestrianLightState == null)\n\t\t\t\treturn false;\n\n\t\t\tvar guiColor = GUI.color;\n\t\t\tvar manualPedestrianWidth = 36f * zoom;\n\t\t\tvar manualPedestrianHeight = 35f * zoom;\n\n\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == segmentId && (_hoveredButton[1] == 1 || _hoveredButton[1] == 2));\n\n\t\t\tGUI.color = guiColor;\n\n\t\t\tvar myRect2 = new Rect(screenPos.x - manualPedestrianWidth / 2 - lightWidth + 5f * zoom,\n\t\t\t\tscreenPos.y - manualPedestrianHeight / 2 - 9f * zoom, manualPedestrianWidth, manualPedestrianHeight);\n\n\t\t\tGUI.DrawTexture(myRect2, segmentLights.ManualPedestrianMode ? TextureResources.PedestrianModeManualTexture2D : TextureResources.PedestrianModeAutomaticTexture2D);\n\n\t\t\tif (!myRect2.Contains(Event.current.mousePosition))\n\t\t\t\treturn hoveredSegment;\n\n\t\t\t_hoveredButton[0] = segmentId;\n\t\t\t_hoveredButton[1] = 1;\n\n\t\t\tif (!MainTool.CheckClicked())\n\t\t\t\treturn true;\n\n\t\t\tsegmentLights.ManualPedestrianMode = !segmentLights.ManualPedestrianMode;\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate bool IsPedestrianLightHovered(Rect myRect3, int segmentId, bool hoveredSegment, ICustomSegmentLights segmentLights) {\n\t\t\tif (!myRect3.Contains(Event.current.mousePosition))\n\t\t\t\treturn hoveredSegment;\n\t\t\tif (segmentLights.PedestrianLightState == null)\n\t\t\t\treturn false;\n\n\t\t\t_hoveredButton[0] = segmentId;\n\t\t\t_hoveredButton[1] = 2;\n\n\t\t\tif (!MainTool.CheckClicked())\n\t\t\t\treturn true;\n\n\t\t\tif (!segmentLights.ManualPedestrianMode) {\n\t\t\t\tsegmentLights.ManualPedestrianMode = true;\n\t\t\t} else {\n\t\t\t\tsegmentLights.ChangeLightPedestrian();\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate bool GetHoveredSegment(Rect myRect1, int segmentId, bool hoveredSegment, ICustomSegmentLight segmentDict) {\n\t\t\tif (!myRect1.Contains(Event.current.mousePosition))\n\t\t\t\treturn hoveredSegment;\n\n\t\t\t//Log.Message(\"mouse in myRect1\");\n\t\t\t_hoveredButton[0] = segmentId;\n\t\t\t_hoveredButton[1] = -1;\n\n\t\t\tif (!MainTool.CheckClicked())\n\t\t\t\treturn true;\n\t\t\tsegmentDict.ToggleMode();\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate bool RenderCounter(int segmentId, Vector3 screenPos, float modeWidth, float modeHeight, float zoom,\n\t\t\tICustomSegmentLights segmentLights, bool hoveredSegment) {\n\t\t\tSetAlpha(segmentId, 0);\n\n\t\t\tvar myRectCounter = new Rect(screenPos.x - modeWidth / 2, screenPos.y - modeHeight / 2 - 6f * zoom, modeWidth, modeHeight);\n\n\t\t\tGUI.DrawTexture(myRectCounter, TextureResources.LightCounterTexture2D);\n\n\t\t\tvar counterSize = 20f * zoom;\n\n\t\t\tvar counter = segmentLights.LastChange();\n\n\t\t\tvar myRectCounterNum = new Rect(screenPos.x - counterSize + 15f * zoom + (counter >= 10 ? -5 * zoom : 0f),\n\t\t\t\tscreenPos.y - counterSize + 11f * zoom, counterSize, counterSize);\n\n\t\t\t_counterStyle.fontSize = (int)(18f * zoom);\n\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 1f);\n\n\t\t\tGUI.Label(myRectCounterNum, counter.ToString(), _counterStyle);\n\n\t\t\tif (!myRectCounter.Contains(Event.current.mousePosition))\n\t\t\t\treturn hoveredSegment;\n\t\t\t_hoveredButton[0] = segmentId;\n\t\t\t_hoveredButton[1] = 0;\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate bool SimpleManualSegmentLightMode(int segmentId, Vector3 screenPos, float lightWidth, float pedestrianWidth,\n\t\t\tfloat zoom, float lightHeight, ICustomSegmentLight segmentDict, bool hoveredSegment) {\n\t\t\tSetAlpha(segmentId, 3);\n\n\t\t\tvar myRect4 =\n\t\t\t\tnew Rect(screenPos.x - lightWidth / 2 - lightWidth - pedestrianWidth + 5f * zoom,\n\t\t\t\t\tscreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\tswitch (segmentDict.LightMain) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tGUI.DrawTexture(myRect4, TextureResources.GreenLightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\tGUI.DrawTexture(myRect4, TextureResources.RedLightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!myRect4.Contains(Event.current.mousePosition))\n\t\t\t\treturn hoveredSegment;\n\t\t\t_hoveredButton[0] = segmentId;\n\t\t\t_hoveredButton[1] = 3;\n\n\t\t\tif (!MainTool.CheckClicked())\n\t\t\t\treturn true;\n\t\t\tsegmentDict.ChangeMainLight();\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate bool LeftForwardRManualSegmentLightMode(bool hasLeftSegment, int segmentId, Vector3 screenPos, float lightWidth,\n\t\t\tfloat pedestrianWidth, float zoom, float lightHeight, ICustomSegmentLight segmentDict, bool hoveredSegment,\n\t\t\tbool hasForwardSegment, bool hasRightSegment) {\n\t\t\tif (hasLeftSegment) {\n\t\t\t\t// left arrow light\n\t\t\t\tSetAlpha(segmentId, 3);\n\n\t\t\t\tvar myRect4 =\n\t\t\t\t\tnew Rect(screenPos.x - lightWidth / 2 - lightWidth * 2 - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\tscreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\t\tswitch (segmentDict.LightLeft) {\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\t\tGUI.DrawTexture(myRect4, TextureResources.GreenLightLeftTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\t\tGUI.DrawTexture(myRect4, TextureResources.RedLightLeftTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (myRect4.Contains(Event.current.mousePosition)) {\n\t\t\t\t\t_hoveredButton[0] = segmentId;\n\t\t\t\t\t_hoveredButton[1] = 3;\n\t\t\t\t\thoveredSegment = true;\n\n\t\t\t\t\tif (MainTool.CheckClicked()) {\n\t\t\t\t\t\tsegmentDict.ChangeLeftLight();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// forward-right arrow light\n\t\t\tSetAlpha(segmentId, 4);\n\n\t\t\tvar myRect5 =\n\t\t\t\tnew Rect(screenPos.x - lightWidth / 2 - lightWidth - pedestrianWidth + 5f * zoom,\n\t\t\t\t\tscreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\tif (hasForwardSegment && hasRightSegment) {\n\t\t\t\tswitch (segmentDict.LightMain) {\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\t\tGUI.DrawTexture(myRect5, TextureResources.GreenLightForwardRightTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\t\tGUI.DrawTexture(myRect5, TextureResources.RedLightForwardRightTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else if (!hasRightSegment) {\n\t\t\t\tswitch (segmentDict.LightMain) {\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\t\tGUI.DrawTexture(myRect5, TextureResources.GreenLightStraightTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\t\tGUI.DrawTexture(myRect5, TextureResources.RedLightStraightTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tswitch (segmentDict.LightMain) {\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\t\tGUI.DrawTexture(myRect5, TextureResources.GreenLightRightTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\t\tGUI.DrawTexture(myRect5, TextureResources.RedLightRightTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!myRect5.Contains(Event.current.mousePosition))\n\t\t\t\treturn hoveredSegment;\n\t\t\t_hoveredButton[0] = segmentId;\n\t\t\t_hoveredButton[1] = 4;\n\n\t\t\tif (!MainTool.CheckClicked())\n\t\t\t\treturn true;\n\t\t\tsegmentDict.ChangeMainLight();\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate bool RightForwardLSegmentLightMode(int segmentId, Vector3 screenPos, float lightWidth, float pedestrianWidth,\n\t\t\tfloat zoom, float lightHeight, bool hasForwardSegment, bool hasLeftSegment, ICustomSegmentLight segmentDict,\n\t\t\tbool hasRightSegment, bool hoveredSegment) {\n\t\t\tSetAlpha(segmentId, 3);\n\n\t\t\tvar myRect4 = new Rect(screenPos.x - lightWidth / 2 - lightWidth * 2 - pedestrianWidth + 5f * zoom,\n\t\t\t\tscreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\tif (hasForwardSegment && hasLeftSegment) {\n\t\t\t\tswitch (segmentDict.LightLeft) {\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\t\tGUI.DrawTexture(myRect4, TextureResources.GreenLightForwardLeftTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\t\tGUI.DrawTexture(myRect4, TextureResources.RedLightForwardLeftTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else if (!hasLeftSegment) {\n\t\t\t\tif (!hasRightSegment) {\n\t\t\t\t\tmyRect4 = new Rect(screenPos.x - lightWidth / 2 - lightWidth - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\tscreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\t\t\t\t}\n\n\t\t\t\tswitch (segmentDict.LightMain) {\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\t\tGUI.DrawTexture(myRect4, TextureResources.GreenLightStraightTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\t\tGUI.DrawTexture(myRect4, TextureResources.RedLightStraightTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (!hasRightSegment) {\n\t\t\t\t\tmyRect4 = new Rect(screenPos.x - lightWidth / 2 - lightWidth - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\tscreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\t\t\t\t}\n\n\t\t\t\tswitch (segmentDict.LightMain) {\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\t\tGUI.DrawTexture(myRect4, TextureResources.GreenLightLeftTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\t\tGUI.DrawTexture(myRect4, TextureResources.RedLightLeftTexture2D);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tif (myRect4.Contains(Event.current.mousePosition)) {\n\t\t\t\t_hoveredButton[0] = segmentId;\n\t\t\t\t_hoveredButton[1] = 3;\n\t\t\t\thoveredSegment = true;\n\n\t\t\t\tif (MainTool.CheckClicked()) {\n\t\t\t\t\tsegmentDict.ChangeMainLight();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar guiColor = GUI.color;\n\t\t\t// right arrow light\n\t\t\tif (hasRightSegment)\n\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == segmentId && _hoveredButton[1] == 4);\n\n\t\t\tGUI.color = guiColor;\n\n\t\t\tvar myRect5 =\n\t\t\t\tnew Rect(screenPos.x - lightWidth / 2 - lightWidth - pedestrianWidth + 5f * zoom,\n\t\t\t\t\tscreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\tswitch (segmentDict.LightRight) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tGUI.DrawTexture(myRect5, TextureResources.GreenLightRightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\tGUI.DrawTexture(myRect5, TextureResources.RedLightRightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\n\t\t\tif (!myRect5.Contains(Event.current.mousePosition))\n\t\t\t\treturn hoveredSegment;\n\n\t\t\t_hoveredButton[0] = segmentId;\n\t\t\t_hoveredButton[1] = 4;\n\n\t\t\tif (!MainTool.CheckClicked())\n\t\t\t\treturn true;\n\t\t\tsegmentDict.ChangeRightLight();\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate bool LeftArrowLightMode(int segmentId, float lightWidth, bool hasRightSegment,\n\t\t\tbool hasForwardSegment, Vector3 screenPos, float pedestrianWidth, float zoom, float lightHeight,\n\t\t\tICustomSegmentLight segmentDict, bool hoveredSegment) {\n\t\t\tSetAlpha(segmentId, 3);\n\n\t\t\tvar offsetLight = lightWidth;\n\n\t\t\tif (hasRightSegment)\n\t\t\t\toffsetLight += lightWidth;\n\n\t\t\tif (hasForwardSegment)\n\t\t\t\toffsetLight += lightWidth;\n\n\t\t\tvar myRect4 =\n\t\t\t\tnew Rect(screenPos.x - lightWidth / 2 - offsetLight - pedestrianWidth + 5f * zoom,\n\t\t\t\t\tscreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\tswitch (segmentDict.LightLeft) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tGUI.DrawTexture(myRect4, TextureResources.GreenLightLeftTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\tGUI.DrawTexture(myRect4, TextureResources.RedLightLeftTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!myRect4.Contains(Event.current.mousePosition))\n\t\t\t\treturn hoveredSegment;\n\t\t\t_hoveredButton[0] = segmentId;\n\t\t\t_hoveredButton[1] = 3;\n\n\t\t\tif (!MainTool.CheckClicked())\n\t\t\t\treturn true;\n\t\t\tsegmentDict.ChangeLeftLight();\n\n\t\t\tif (!hasForwardSegment) {\n\t\t\t\tsegmentDict.ChangeMainLight();\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate bool ForwardArrowLightMode(int segmentId, float lightWidth, bool hasRightSegment,\n\t\t\tVector3 screenPos, float pedestrianWidth, float zoom, float lightHeight, ICustomSegmentLight segmentDict,\n\t\t\tbool hoveredSegment) {\n\t\t\tSetAlpha(segmentId, 4);\n\n\t\t\tvar offsetLight = lightWidth;\n\n\t\t\tif (hasRightSegment)\n\t\t\t\toffsetLight += lightWidth;\n\n\t\t\tvar myRect6 =\n\t\t\t\tnew Rect(screenPos.x - lightWidth / 2 - offsetLight - pedestrianWidth + 5f * zoom,\n\t\t\t\t\tscreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\tswitch (segmentDict.LightMain) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tGUI.DrawTexture(myRect6, TextureResources.GreenLightStraightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\tGUI.DrawTexture(myRect6, TextureResources.RedLightStraightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!myRect6.Contains(Event.current.mousePosition))\n\t\t\t\treturn hoveredSegment;\n\n\t\t\t_hoveredButton[0] = segmentId;\n\t\t\t_hoveredButton[1] = 4;\n\n\t\t\tif (!MainTool.CheckClicked())\n\t\t\t\treturn true;\n\t\t\tsegmentDict.ChangeMainLight();\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate bool RightArrowLightMode(int segmentId, Vector3 screenPos, float lightWidth,\n\t\t\tfloat pedestrianWidth, float zoom, float lightHeight, ICustomSegmentLight segmentDict, bool hoveredSegment) {\n\t\t\tSetAlpha(segmentId, 5);\n\n\t\t\tvar myRect5 =\n\t\t\t\tnew Rect(screenPos.x - lightWidth / 2 - lightWidth - pedestrianWidth + 5f * zoom,\n\t\t\t\t\tscreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\tswitch (segmentDict.LightRight) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tGUI.DrawTexture(myRect5, TextureResources.GreenLightRightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\tGUI.DrawTexture(myRect5, TextureResources.RedLightRightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!myRect5.Contains(Event.current.mousePosition))\n\t\t\t\treturn hoveredSegment;\n\n\t\t\t_hoveredButton[0] = segmentId;\n\t\t\t_hoveredButton[1] = 5;\n\n\t\t\tif (!MainTool.CheckClicked())\n\t\t\t\treturn true;\n\t\t\tsegmentDict.ChangeRightLight();\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate Vector3 CalculateNodePositionForSegment(NetNode node, int segmentId) {\n\t\t\tvar position = node.m_position;\n\n\t\t\tvar segment = Singleton<NetManager>.instance.m_segments.m_buffer[segmentId];\n\t\t\tif (segment.m_startNode == SelectedNodeId) {\n\t\t\t\tposition.x += segment.m_startDirection.x * 10f;\n\t\t\t\tposition.y += segment.m_startDirection.y * 10f;\n\t\t\t\tposition.z += segment.m_startDirection.z * 10f;\n\t\t\t} else {\n\t\t\t\tposition.x += segment.m_endDirection.x * 10f;\n\t\t\t\tposition.y += segment.m_endDirection.y * 10f;\n\t\t\t\tposition.z += segment.m_endDirection.z * 10f;\n\t\t\t}\n\t\t\treturn position;\n\t\t}\n\n\t\tprivate Vector3 CalculateNodePositionForSegment(NetNode node, ref NetSegment segment) {\n\t\t\tvar position = node.m_position;\n\n\t\t\tconst float offset = 25f;\n\n\t\t\tif (segment.m_startNode == SelectedNodeId) {\n\t\t\t\tposition.x += segment.m_startDirection.x * offset;\n\t\t\t\tposition.y += segment.m_startDirection.y * offset;\n\t\t\t\tposition.z += segment.m_startDirection.z * offset;\n\t\t\t} else {\n\t\t\t\tposition.x += segment.m_endDirection.x * offset;\n\t\t\t\tposition.y += segment.m_endDirection.y * offset;\n\t\t\t\tposition.z += segment.m_endDirection.z * offset;\n\t\t\t}\n\n\t\t\treturn position;\n\t\t}\n\n\t\tprivate void SetAlpha(int segmentId, int buttonId) {\n\t\t\tvar guiColor = GUI.color;\n\n\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == segmentId && _hoveredButton[1] == buttonId);\n\n\t\t\tGUI.color = guiColor;\n\t\t}\n\n\t\tprivate void RenderManualSelectionOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\tif (HoveredNodeId == 0) return;\n\n\t\t\tMainTool.DrawNodeCircle(cameraInfo, HoveredNodeId, false, false);\n\t\t\t/*var segment = Singleton<NetManager>.instance.m_segments.m_buffer[Singleton<NetManager>.instance.m_nodes.m_buffer[HoveredNodeId].m_segment0];\n\n\t\t\t//if ((node.m_flags & NetNode.Flags.TrafficLights) == NetNode.Flags.None) return;\n\t\t\tBezier3 bezier;\n\t\t\tbezier.a = Singleton<NetManager>.instance.m_nodes.m_buffer[HoveredNodeId].m_position;\n\t\t\tbezier.d = Singleton<NetManager>.instance.m_nodes.m_buffer[HoveredNodeId].m_position;\n\n\t\t\tvar color = MainTool.GetToolColor(false, false);\n\n\t\t\tNetSegment.CalculateMiddlePoints(bezier.a, segment.m_startDirection, bezier.d,\n\t\t\t\tsegment.m_endDirection, false, false, out bezier.b, out bezier.c);\n\t\t\tMainTool.DrawOverlayBezier(cameraInfo, bezier, color);*/\n\t\t}\n\n\t\tprivate void RenderManualNodeOverlays(RenderManager.CameraInfo cameraInfo) {\n\t\t\tif (!TrafficLightSimulationManager.Instance.HasManualSimulation(SelectedNodeId)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tMainTool.DrawNodeCircle(cameraInfo, SelectedNodeId, true, false);\n\t\t}\n\n\t\tpublic override void Cleanup() {\n\t\t\tif (SelectedNodeId == 0) return;\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\n\t\t\tif (!tlsMan.HasManualSimulation(SelectedNodeId)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttlsMan.RemoveNodeFromSimulation(SelectedNodeId, true, false);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/SubTools/ParkingRestrictionsTool.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing ColossalFramework.UI;\nusing CSUtil.Commons;\nusing GenericGameBridge.Service;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Util;\nusing UnityEngine;\nusing static ColossalFramework.UI.UITextureAtlas;\nusing static TrafficManager.Util.SegmentLaneTraverser;\nusing static TrafficManager.Util.SegmentTraverser;\n\nnamespace TrafficManager.UI.SubTools {\n\tpublic class ParkingRestrictionsTool : SubTool {\n\t\tprivate bool overlayHandleHovered;\n\t\tprivate Dictionary<ushort, Dictionary<NetInfo.Direction, Vector3>> segmentCenterByDir = new Dictionary<ushort, Dictionary<NetInfo.Direction, Vector3>>();\n\t\tprivate readonly float signSize = 80f;\n\t\tprivate HashSet<ushort> currentlyVisibleSegmentIds;\n\t\t\n\t\tpublic ParkingRestrictionsTool(TrafficManagerTool mainTool) : base(mainTool) {\n\t\t\tcurrentlyVisibleSegmentIds = new HashSet<ushort>();\n\t\t}\n\n\t\tpublic override void OnActivate() {\n\t\t\t\n\t\t}\n\n\t\tpublic override void OnPrimaryClickOverlay() {\n\t\t\t\n\t\t}\n\n\t\tpublic override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\t\n\t\t}\n\n\t\tpublic override void ShowGUIOverlay(ToolMode toolMode, bool viewOnly) {\n\t\t\tif (viewOnly && !Options.parkingRestrictionsOverlay)\n\t\t\t\treturn;\n\n\t\t\toverlayHandleHovered = false;\n\t\t\tShowSigns(viewOnly);\n\t\t}\n\n\t\tpublic override void Cleanup() {\n\t\t\tsegmentCenterByDir.Clear();\n\t\t\tcurrentlyVisibleSegmentIds.Clear();\n\t\t\tlastCamPos = null;\n\t\t\tlastCamRot = null;\n\t\t}\n\n\t\tprivate Quaternion? lastCamRot = null;\n\t\tprivate Vector3? lastCamPos = null;\n\n\t\tprivate void ShowSigns(bool viewOnly) {\n\t\t\tQuaternion camRot = Camera.main.transform.rotation;\n\t\t\tVector3 camPos = Camera.main.transform.position;\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tParkingRestrictionsManager parkingManager = ParkingRestrictionsManager.Instance;\n\n\t\t\tif (lastCamPos == null || lastCamRot == null || !lastCamRot.Equals(camRot) || !lastCamPos.Equals(camPos)) {\n\t\t\t\t// cache visible segments\n\t\t\t\tcurrentlyVisibleSegmentIds.Clear();\n\n\t\t\t\tfor (uint segmentId = 1; segmentId < NetManager.MAX_SEGMENT_COUNT; ++segmentId) {\n\t\t\t\t\tif (!Constants.ServiceFactory.NetService.IsSegmentValid((ushort)segmentId)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t/*if ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Untouchable) != NetSegment.Flags.None)\n\t\t\t\t\t\tcontinue;*/\n\n\t\t\t\t\tif ((netManager.m_segments.m_buffer[segmentId].m_bounds.center - camPos).magnitude > TrafficManagerTool.MaxOverlayDistance)\n\t\t\t\t\t\tcontinue; // do not draw if too distant\n\n\t\t\t\t\tVector3 screenPos;\n\t\t\t\t\tbool visible = MainTool.WorldToScreenPoint(netManager.m_segments.m_buffer[segmentId].m_bounds.center, out screenPos);\n\n\t\t\t\t\tif (!visible)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tif (!parkingManager.MayHaveParkingRestriction((ushort)segmentId))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tcurrentlyVisibleSegmentIds.Add((ushort)segmentId);\n\t\t\t\t}\n\n\t\t\t\tlastCamPos = camPos;\n\t\t\t\tlastCamRot = camRot;\n\t\t\t}\n\n\t\t\tbool handleHovered = false;\n\t\t\tbool clicked = !viewOnly && MainTool.CheckClicked();\n\t\t\tforeach (ushort segmentId in currentlyVisibleSegmentIds) {\n\t\t\t\tVector3 screenPos;\n\t\t\t\tbool visible = MainTool.WorldToScreenPoint(netManager.m_segments.m_buffer[segmentId].m_bounds.center, out screenPos);\n\n\t\t\t\tif (!visible)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tNetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info;\n\n\t\t\t\t// draw parking restrictions\n\t\t\t\tif (MainTool.GetToolMode() != ToolMode.SpeedLimits && (MainTool.GetToolMode() != ToolMode.VehicleRestrictions || segmentId != SelectedSegmentId)) { // no parking restrictions overlay on selected segment when in vehicle restrictions mode\n\t\t\t\t\tif (drawParkingRestrictionHandles((ushort)segmentId, clicked, ref netManager.m_segments.m_buffer[segmentId], viewOnly, ref camPos))\n\t\t\t\t\t\thandleHovered = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\toverlayHandleHovered = handleHovered;\n\t\t}\n\n\t\tprivate bool drawParkingRestrictionHandles(ushort segmentId, bool clicked, ref NetSegment segment, bool viewOnly, ref Vector3 camPos) {\n\t\t\tif (viewOnly && !Options.parkingRestrictionsOverlay)\n\t\t\t\treturn false;\n\n\t\t\tVector3 center = segment.m_bounds.center;\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tParkingRestrictionsManager parkingManager = ParkingRestrictionsManager.Instance;\n\n\t\t\tbool hovered = false;\n\t\t\t\n\t\t\t// draw parking restriction signs over mean middle points of lane beziers\n\t\t\tDictionary<NetInfo.Direction, Vector3> segCenter;\n\t\t\tif (!segmentCenterByDir.TryGetValue(segmentId, out segCenter)) {\n\t\t\t\tsegCenter = new Dictionary<NetInfo.Direction, Vector3>();\n\t\t\t\tsegmentCenterByDir.Add(segmentId, segCenter);\n\t\t\t\tTrafficManagerTool.CalculateSegmentCenterByDir(segmentId, segCenter);\n\t\t\t}\n\n\t\t\tforeach (KeyValuePair<NetInfo.Direction, Vector3> e in segCenter) {\n\t\t\t\tbool allowed = parkingManager.IsParkingAllowed(segmentId, e.Key);\n\t\t\t\tif (allowed && viewOnly) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tVector3 screenPos;\n\t\t\t\tbool visible = MainTool.WorldToScreenPoint(e.Value, out screenPos);\n\n\t\t\t\tif (!visible)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tfloat zoom = 1.0f / (e.Value - camPos).magnitude * 100f * MainTool.GetBaseZoom();\n\t\t\t\tfloat size = (viewOnly ? 0.8f : 1f) * signSize * zoom;\n\t\t\t\tColor guiColor = GUI.color;\n\t\t\t\tRect boundingBox = new Rect(screenPos.x - size / 2, screenPos.y - size / 2, size, size);\n\t\t\t\tif (Options.speedLimitsOverlay) {\n\t\t\t\t\tboundingBox.y -= size + 10f;\n\t\t\t\t}\n\t\t\t\tbool hoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox);\n\n\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(hoveredHandle);\n\t\t\t\tif (hoveredHandle) {\n\t\t\t\t\t// mouse hovering over sign\n\t\t\t\t\thovered = true;\n\t\t\t\t}\n\n\t\t\t\tGUI.color = guiColor;\n\t\t\t\tGUI.DrawTexture(boundingBox, TextureResources.ParkingRestrictionTextures[allowed]);\n\n\t\t\t\tif (hoveredHandle && clicked && !IsCursorInPanel()) {\n\t\t\t\t\tif (parkingManager.ToggleParkingAllowed(segmentId, e.Key)) {\n\t\t\t\t\t\tallowed = !allowed;\n\n\t\t\t\t\t\tif (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {\n\n\t\t\t\t\t\t\tNetInfo.Direction normDir = e.Key;\n\t\t\t\t\t\t\tif ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\t\t\t\t\tnormDir = NetInfo.InvertDirection(normDir);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tSegmentLaneTraverser.Traverse(segmentId, SegmentTraverser.TraverseDirection.AnyDirection, SegmentTraverser.TraverseSide.AnySide, SegmentLaneTraverser.LaneStopCriterion.LaneCount, SegmentTraverser.SegmentStopCriterion.Junction, ParkingRestrictionsManager.LANE_TYPES, ParkingRestrictionsManager.VEHICLE_TYPES, delegate (SegmentLaneVisitData data) {\n\t\t\t\t\t\t\t\tif (data.segVisitData.initial) {\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbool reverse = data.segVisitData.viaStartNode == data.segVisitData.viaInitialStartNode;\n\n\t\t\t\t\t\t\t\tushort otherSegmentId = data.segVisitData.curGeo.SegmentId;\n\t\t\t\t\t\t\t\tNetInfo otherSegmentInfo = netManager.m_segments.m_buffer[otherSegmentId].Info;\n\t\t\t\t\t\t\t\tuint laneId = data.curLanePos.laneId;\n\t\t\t\t\t\t\t\tbyte laneIndex = data.curLanePos.laneIndex;\n\t\t\t\t\t\t\t\tNetInfo.Lane laneInfo = otherSegmentInfo.m_lanes[laneIndex];\n\n\t\t\t\t\t\t\t\tNetInfo.Direction otherNormDir = laneInfo.m_finalDirection;\n\t\t\t\t\t\t\t\tif ((netManager.m_segments.m_buffer[otherSegmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None ^\n\t\t\t\t\t\t\t\t\treverse) {\n\t\t\t\t\t\t\t\t\totherNormDir = NetInfo.InvertDirection(otherNormDir);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (otherNormDir == normDir) {\n\t\t\t\t\t\t\t\t\tparkingManager.SetParkingAllowed(otherSegmentId, laneInfo.m_finalDirection, allowed);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tguiColor.a = 1f;\n\t\t\t\tGUI.color = guiColor;\n\t\t\t}\n\t\t\treturn hovered;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/SubTools/PrioritySignsTool.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing UnityEngine;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager;\nusing TrafficManager.Util;\nusing CSUtil.Commons;\nusing TrafficManager.Geometry.Impl;\nusing TrafficManager.Manager.Impl;\nusing static TrafficManager.Traffic.Data.PrioritySegment;\nusing static TrafficManager.Util.SegmentTraverser;\nusing ColossalFramework.UI;\n\nnamespace TrafficManager.UI.SubTools {\n\tpublic class PrioritySignsTool : SubTool {\n\t\tpublic enum PrioritySignsMassEditMode {\n\t\t\tMainYield = 0,\n\t\t\tMainStop = 1,\n\t\t\tYieldMain = 2,\n\t\t\tStopMain = 3,\n\t\t\tDelete = 4\n\t\t}\n\n\t\tprivate HashSet<ushort> currentPriorityNodeIds;\n\t\tprivate PrioritySignsMassEditMode massEditMode = PrioritySignsMassEditMode.MainYield;\n\n\t\tpublic PrioritySignsTool(TrafficManagerTool mainTool) : base(mainTool) {\n\t\t\tcurrentPriorityNodeIds = new HashSet<ushort>();\n\t\t}\n\n\t\tpublic override void OnPrimaryClickOverlay() {\n\t\t\tif (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {\n\t\t\t\tif (HoveredSegmentId != 0) {\n\t\t\t\t\tSelectedNodeId = 0;\n\n\t\t\t\t\tPriorityType primaryPrioType = PriorityType.None;\n\t\t\t\t\tPriorityType secondaryPrioType = PriorityType.None;\n\t\t\t\t\tswitch (massEditMode) {\n\t\t\t\t\t\tcase PrioritySignsMassEditMode.MainYield:\n\t\t\t\t\t\t\tprimaryPrioType = PriorityType.Main;\n\t\t\t\t\t\t\tsecondaryPrioType = PriorityType.Yield;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase PrioritySignsMassEditMode.MainStop:\n\t\t\t\t\t\t\tprimaryPrioType = PriorityType.Main;\n\t\t\t\t\t\t\tsecondaryPrioType = PriorityType.Stop;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase PrioritySignsMassEditMode.YieldMain:\n\t\t\t\t\t\t\tprimaryPrioType = PriorityType.Yield;\n\t\t\t\t\t\t\tsecondaryPrioType = PriorityType.Main;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase PrioritySignsMassEditMode.StopMain:\n\t\t\t\t\t\t\tprimaryPrioType = PriorityType.Stop;\n\t\t\t\t\t\t\tsecondaryPrioType = PriorityType.Main;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase PrioritySignsMassEditMode.Delete:\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tSegmentTraverser.Traverse(HoveredSegmentId, TraverseDirection.AnyDirection, TraverseSide.Straight, SegmentStopCriterion.None, delegate (SegmentVisitData data) {\n\t\t\t\t\t\tforeach (bool startNode in Constants.ALL_BOOL) {\n\t\t\t\t\t\t\tTrafficPriorityManager.Instance.SetPrioritySign(data.curGeo.SegmentId, startNode, primaryPrioType);\n\n\t\t\t\t\t\t\tforeach (ushort otherSegmentId in data.curGeo.GetConnectedSegments(startNode)) {\n\t\t\t\t\t\t\t\tif (!data.curGeo.IsStraightSegment(otherSegmentId, startNode)) {\n\t\t\t\t\t\t\t\t\tSegmentGeometry otherGeo = SegmentGeometry.Get(otherSegmentId);\n\t\t\t\t\t\t\t\t\tif (otherGeo == null) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tTrafficPriorityManager.Instance.SetPrioritySign(otherSegmentId, otherGeo.StartNodeId() == data.curGeo.GetNodeId(startNode), secondaryPrioType);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\n\t\t\t\t\t// cycle mass edit mode\n\t\t\t\t\tmassEditMode = (PrioritySignsMassEditMode)(((int)massEditMode + 1) % Enum.GetValues(typeof(PrioritySignsMassEditMode)).GetLength(0));\n\n\t\t\t\t\t// update priority node cache\n\t\t\t\t\tRefreshCurrentPriorityNodeIds();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (TrafficPriorityManager.Instance.HasNodePrioritySign(HoveredNodeId)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (! MayNodeHavePrioritySigns(HoveredNodeId)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tSelectedNodeId = HoveredNodeId;\n\t\t\tLog._Debug($\"PrioritySignsTool.OnPrimaryClickOverlay: SelectedNodeId={SelectedNodeId}\");\n\t\t\t// update priority node cache\n\t\t\tRefreshCurrentPriorityNodeIds();\n\t\t}\n\n\t\tpublic override void OnToolGUI(Event e) {\n\t\t\t\n\t\t}\n\n\t\tpublic override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\tif (MainTool.GetToolController().IsInsideUI || !Cursor.visible) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {\n\t\t\t\t// draw hovered segments\n\t\t\t\tif (HoveredSegmentId != 0) {\n\t\t\t\t\tColor color = MainTool.GetToolColor(Input.GetMouseButton(0), false);\n\t\t\t\t\tSegmentTraverser.Traverse(HoveredSegmentId, TraverseDirection.AnyDirection, TraverseSide.Straight, SegmentStopCriterion.None, delegate (SegmentVisitData data) {\n\t\t\t\t\t\tNetTool.RenderOverlay(cameraInfo, ref Singleton<NetManager>.instance.m_segments.m_buffer[data.curGeo.SegmentId], color, color);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tmassEditMode = PrioritySignsMassEditMode.MainYield;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmassEditMode = PrioritySignsMassEditMode.MainYield;\n\n\t\t\tif (HoveredNodeId == SelectedNodeId) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// no highlight for existing priority node in sign mode\n\t\t\tif (TrafficPriorityManager.Instance.HasNodePrioritySign(HoveredNodeId)) {\n\t\t\t\t//Log._Debug($\"PrioritySignsTool.RenderOverlay: HasNodePrioritySign({HoveredNodeId})=true\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (! TrafficPriorityManager.Instance.MayNodeHavePrioritySigns(HoveredNodeId)) {\n\t\t\t\t//Log._Debug($\"PrioritySignsTool.RenderOverlay: MayNodeHavePrioritySigns({HoveredNodeId})=false\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tMainTool.DrawNodeCircle(cameraInfo, HoveredNodeId, Input.GetMouseButton(0));\n\t\t}\n\n\t\tprivate void RefreshCurrentPriorityNodeIds() {\n\t\t\tTrafficPriorityManager tpm = TrafficPriorityManager.Instance;\n\n\t\t\tcurrentPriorityNodeIds.Clear();\n\t\t\tfor (uint nodeId = 0; nodeId < NetManager.MAX_NODE_COUNT; ++nodeId) {\n\t\t\t\tif (!Constants.ServiceFactory.NetService.IsNodeValid((ushort)nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (!tpm.MayNodeHavePrioritySigns((ushort)nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (!tpm.HasNodePrioritySign((ushort)nodeId) && nodeId != SelectedNodeId) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t/*if (! MainTool.IsNodeWithinViewDistance(nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}*/\n\n\t\t\t\tcurrentPriorityNodeIds.Add((ushort)nodeId);\n\t\t\t}\n\t\t\t//Log._Debug($\"PrioritySignsTool.RefreshCurrentPriorityNodeIds: currentPriorityNodeIds={string.Join(\", \", currentPriorityNodeIds.Select(x => x.ToString()).ToArray())}\");\n\t\t}\n\n\t\tpublic override void ShowGUIOverlay(ToolMode toolMode, bool viewOnly) {\n\t\t\tif (viewOnly && !Options.prioritySignsOverlay)\n\t\t\t\treturn;\n\n\t\t\tif (UIBase.GetTrafficManagerTool(false)?.GetToolMode() == ToolMode.JunctionRestrictions)\n\t\t\t\treturn;\n\n\t\t\tShowGUI(viewOnly);\n\t\t}\n\n\t\tpublic void ShowGUI(bool viewOnly) {\n\t\t\ttry {\n\t\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\t\t\t\tTrafficPriorityManager prioMan = TrafficPriorityManager.Instance;\n\t\t\t\tTrafficLightManager tlm = TrafficLightManager.Instance;\n\n\t\t\t\tVector3 camPos = Constants.ServiceFactory.SimulationService.CameraPosition;\n\n\t\t\t\tbool clicked = !viewOnly ? MainTool.CheckClicked() : false;\n\n\t\t\t\tushort removedNodeId = 0;\n\t\t\t\tbool showRemoveButton = false;\n\t\t\t\tforeach (ushort nodeId in currentPriorityNodeIds) {\n\t\t\t\t\tif (! Constants.ServiceFactory.NetService.IsNodeValid(nodeId)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!MainTool.IsNodeWithinViewDistance(nodeId)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tNodeGeometry nodeGeo = NodeGeometry.Get(nodeId);\n\n\t\t\t\t\tVector3 nodePos = default(Vector3);\n\t\t\t\t\tConstants.ServiceFactory.NetService.ProcessNode(nodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\t\t\tnodePos = node.m_position;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\n\t\t\t\t\tforeach (SegmentEndGeometry endGeo in nodeGeo.SegmentEndGeometries) {\n\t\t\t\t\t\tif (endGeo == null) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (endGeo.OutgoingOneWay) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tushort segmentId = endGeo.SegmentId;\n\t\t\t\t\t\tbool startNode = endGeo.StartNode;\n\n\t\t\t\t\t\t// calculate sign position\n\t\t\t\t\t\tVector3 signPos = nodePos;\n\n\t\t\t\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(segmentId, delegate (ushort sId, ref NetSegment segment) {\n\t\t\t\t\t\t\tsignPos += 10f * (startNode ? segment.m_startDirection : segment.m_endDirection);\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tVector3 signScreenPos;\n\t\t\t\t\t\tif (! MainTool.WorldToScreenPoint(signPos, out signScreenPos)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// draw sign and handle input\n\t\t\t\t\t\tPriorityType sign = prioMan.GetPrioritySign(segmentId, startNode);\n\t\t\t\t\t\tif (viewOnly && sign == PriorityType.None) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!viewOnly && sign != PriorityType.None) {\n\t\t\t\t\t\t\tshowRemoveButton = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (MainTool.DrawGenericSquareOverlayTexture(TextureResources.PrioritySignTextures[sign], camPos, signPos, 90f, !viewOnly) && clicked) {\n\t\t\t\t\t\t\tPriorityType? newSign = null;\n\t\t\t\t\t\t\tswitch (sign) {\n\t\t\t\t\t\t\t\tcase PriorityType.Main:\n\t\t\t\t\t\t\t\t\tnewSign = PriorityType.Yield;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase PriorityType.Yield:\n\t\t\t\t\t\t\t\t\tnewSign = PriorityType.Stop;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase PriorityType.Stop:\n\t\t\t\t\t\t\t\t\tnewSign = PriorityType.Main;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase PriorityType.None:\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\tnewSign = prioMan.CountPrioritySignsAtNode(nodeId, PriorityType.Main) >= 2 ? PriorityType.Yield : PriorityType.Main;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (newSign != null) {\n\t\t\t\t\t\t\t\tSetPrioritySign(segmentId, startNode, (PriorityType)newSign);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} // draw sign\n\t\t\t\t\t} // foreach segment end\n\n\t\t\t\t\tif (viewOnly) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// draw remove button and handle click\n\t\t\t\t\tif (showRemoveButton && MainTool.DrawHoverableSquareOverlayTexture(TextureResources.SignRemoveTexture2D, camPos, nodePos, 90f) && clicked) {\n\t\t\t\t\t\tprioMan.RemovePrioritySignsFromNode(nodeId);\n\t\t\t\t\t\tLog._Debug($\"PrioritySignsTool.ShowGUI: Removed priority signs from node {nodeId}\");\n\t\t\t\t\t\tremovedNodeId = nodeId;\n\t\t\t\t\t}\n\t\t\t\t} // foreach node\n\n\t\t\t\tif (removedNodeId != 0) {\n\t\t\t\t\tcurrentPriorityNodeIds.Remove(removedNodeId);\n\t\t\t\t\tSelectedNodeId = 0;\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error(e.ToString());\n\t\t\t}\n\t\t}\n\n\t\tpublic bool SetPrioritySign(ushort segmentId, bool startNode, PriorityType sign) {\n\t\t\tSegmentGeometry segGeo = SegmentGeometry.Get(segmentId);\n\t\t\tif (segGeo == null) {\n\t\t\t\tLog.Error($\"PrioritySignsTool.SetPrioritySign: No geometry information available for segment {segmentId}\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tushort nodeId = segGeo.GetNodeId(startNode);\n\n\t\t\t// check for restrictions\n\t\t\tif (!MayNodeHavePrioritySigns(nodeId)) {\n\t\t\t\tLog._Debug($\"PrioritySignsTool.SetPrioritySign: MayNodeHavePrioritySigns({nodeId})=false\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool success = TrafficPriorityManager.Instance.SetPrioritySign(segmentId, startNode, sign);\n\t\t\tLog._Debug($\"PrioritySignsTool.SetPrioritySign: SetPrioritySign({segmentId}, {startNode}, {sign})={success}\");\n\t\t\tif (success && (sign == PriorityType.Stop || sign == PriorityType.Yield)) {\n\t\t\t\t// make all undefined segments a main road\n\t\t\t\tLog._Debug($\"PrioritySignsTool.SetPrioritySign: flagging remaining segments at node {nodeId} as main road.\");\n\t\t\t\tNodeGeometry nodeGeo = NodeGeometry.Get(nodeId);\n\t\t\t\tforeach (SegmentEndGeometry endGeo in nodeGeo.SegmentEndGeometries) {\n\t\t\t\t\tif (endGeo == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (endGeo.SegmentId == segmentId) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (TrafficPriorityManager.Instance.GetPrioritySign(endGeo.SegmentId, endGeo.StartNode) == PriorityType.None) {\n\t\t\t\t\t\tLog._Debug($\"PrioritySignsTool.SetPrioritySign: setting main priority sign for segment {endGeo.SegmentId} @ {nodeId}\");\n\t\t\t\t\t\tTrafficPriorityManager.Instance.SetPrioritySign(endGeo.SegmentId, endGeo.StartNode, PriorityType.Main);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn success;\n\t\t}\n\n\t\tpublic override void Cleanup() {\n\t\t\t//TrafficPriorityManager prioMan = TrafficPriorityManager.Instance;\n\n\t\t\t//foreach (PrioritySegment trafficSegment in prioMan.PrioritySegments) {\n\t\t\t//\ttry {\n\t\t\t//\t\ttrafficSegment?.Instance1?.Reset();\n\t\t\t//\t\ttrafficSegment?.Instance2?.Reset();\n\t\t\t//\t} catch (Exception e) {\n\t\t\t//\t\tLog.Error($\"Error occured while performing PrioritySignsTool.Cleanup: {e.ToString()}\");\n\t\t\t//\t}\n\t\t\t//}\n\n\t\t\t\n\t\t}\n\n\t\tpublic override void OnActivate() {\n\t\t\tRefreshCurrentPriorityNodeIds();\n\t\t}\n\n\t\tpublic override void Initialize() {\n\t\t\tbase.Initialize();\n\t\t\tCleanup();\n\t\t\tif (Options.prioritySignsOverlay) {\n\t\t\t\tRefreshCurrentPriorityNodeIds();\n\t\t\t} else {\n\t\t\t\tcurrentPriorityNodeIds.Clear();\n\t\t\t}\n\t\t}\n\n\t\tprivate bool MayNodeHavePrioritySigns(ushort nodeId) {\n\t\t\tTrafficPriorityManager.UnableReason reason;\n\t\t\t//Log._Debug($\"PrioritySignsTool.MayNodeHavePrioritySigns: Checking if node {nodeId} may have priority signs.\");\n\t\t\tif (!TrafficPriorityManager.Instance.MayNodeHavePrioritySigns(nodeId, out reason)) {\n\t\t\t\t//Log._Debug($\"PrioritySignsTool.MayNodeHavePrioritySigns: Node {nodeId} does not allow priority signs: {reason}\");\n\t\t\t\tif (reason == TrafficPriorityManager.UnableReason.HasTimedLight) {\n\t\t\t\t\tMainTool.ShowTooltip(Translation.GetString(\"NODE_IS_TIMED_LIGHT\"));\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t//Log._Debug($\"PrioritySignsTool.MayNodeHavePrioritySigns: Node {nodeId} allows priority signs\");\n\t\t\treturn true;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/SubTools/README.md",
    "content": "# TM:PE -- /UI/SubTool\nTools that are selectable through the main menu. \n## Classes\n- **JunctionRestrictionsTool**: Junction restrictions\n- **LaneArrowTool**: Lane arrow changer\n- **LaneConnectorTool**: Lane connector\n- **ManualTrafficLightsTool**: Manual traffic lights\n- **ParkingRestrictionsTool**: Parking restrictions\n- **PrioritySignsTool**: Priority signs\n- **SpeedLimitTool**: Speed limits\n- **TimedTrafficLightTool**: Timed traffic lights\n- **ToggleTrafficLightsTool**: Switch traffic lights\n- **VehicleRestrictionsTool**: Vehicle restrictions"
  },
  {
    "path": "TLM/TLM/UI/SubTools/SpeedLimitsTool.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing ColossalFramework.UI;\nusing CSUtil.Commons;\nusing GenericGameBridge.Service;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Util;\nusing UnityEngine;\nusing static ColossalFramework.UI.UITextureAtlas;\nusing static TrafficManager.Util.SegmentLaneTraverser;\n\nnamespace TrafficManager.UI.SubTools {\n\tpublic class SpeedLimitsTool : SubTool {\n\t\tprivate bool _cursorInSecondaryPanel;\n\t\tprivate int curSpeedLimitIndex = 0;\n\t\tprivate bool overlayHandleHovered;\n\t\tprivate Dictionary<ushort, Dictionary<NetInfo.Direction, Vector3>> segmentCenterByDir = new Dictionary<ushort, Dictionary<NetInfo.Direction, Vector3>>();\n\t\tprivate readonly float speedLimitSignSize = 80f;\n\t\tprivate readonly int guiSpeedSignSize = 100;\n\t\tprivate Rect windowRect = TrafficManagerTool.MoveGUI(new Rect(0, 0, 7 * 105, 225));\n\t\tprivate Rect defaultsWindowRect = TrafficManagerTool.MoveGUI(new Rect(0, 280, 400, 400));\n\t\tprivate HashSet<ushort> currentlyVisibleSegmentIds;\n\t\tprivate bool defaultsWindowVisible = false;\n\t\tprivate int currentInfoIndex = -1;\n\t\tprivate int currentSpeedLimitIndex = -1;\n\t\tprivate Texture2D RoadTexture {\n\t\t\tget {\n\t\t\t\tif (roadTexture == null) {\n\t\t\t\t\troadTexture = new Texture2D(guiSpeedSignSize, guiSpeedSignSize);\n\t\t\t\t}\n\t\t\t\treturn roadTexture;\n\t\t\t}\n\t\t}\n\t\tprivate Texture2D roadTexture = null;\n\t\tprivate bool showLimitsPerLane = false;\n\n\t\tpublic SpeedLimitsTool(TrafficManagerTool mainTool) : base(mainTool) {\n\t\t\tcurrentlyVisibleSegmentIds = new HashSet<ushort>();\n\t\t}\n\n\t\tpublic override bool IsCursorInPanel() {\n\t\t\treturn base.IsCursorInPanel() || _cursorInSecondaryPanel;\n\t\t}\n\n\t\tpublic override void OnActivate() {\n\t\t\t\n\t\t}\n\n\t\tpublic override void OnPrimaryClickOverlay() {\n\t\t\t\n\t\t}\n\n\t\tpublic override void OnToolGUI(Event e) {\n\t\t\tbase.OnToolGUI(e);\n\n\t\t\twindowRect = GUILayout.Window(254, windowRect, _guiSpeedLimitsWindow, Translation.GetString(\"Speed_limits\"), WindowStyle);\n\t\t\tif (defaultsWindowVisible) {\n\t\t\t\tdefaultsWindowRect = GUILayout.Window(258, defaultsWindowRect, _guiDefaultsWindow, Translation.GetString(\"Default_speed_limits\"), WindowStyle);\n\t\t\t}\n\t\t\t_cursorInSecondaryPanel = windowRect.Contains(Event.current.mousePosition) || (defaultsWindowVisible && defaultsWindowRect.Contains(Event.current.mousePosition));\n\n\t\t\t//overlayHandleHovered = false;\n\t\t\t//ShowSigns(false);\n\t\t}\n\n\t\tpublic override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\t\n\t\t}\n\n\t\tpublic override void ShowGUIOverlay(ToolMode toolMode, bool viewOnly) {\n\t\t\tif (viewOnly && !Options.speedLimitsOverlay)\n\t\t\t\treturn;\n\n\t\t\toverlayHandleHovered = false;\n\t\t\tShowSigns(viewOnly);\n\t\t}\n\n\t\tpublic override void Cleanup() {\n\t\t\tsegmentCenterByDir.Clear();\n\t\t\tcurrentlyVisibleSegmentIds.Clear();\n\t\t\tlastCamPos = null;\n\t\t\tlastCamRot = null;\n\t\t\tcurrentInfoIndex = -1;\n\t\t\tcurrentSpeedLimitIndex = -1;\n\t\t}\n\n\t\tprivate Quaternion? lastCamRot = null;\n\t\tprivate Vector3? lastCamPos = null;\n\n\t\tprivate void ShowSigns(bool viewOnly) {\n\t\t\tQuaternion camRot = Camera.main.transform.rotation;\n\t\t\tVector3 camPos = Camera.main.transform.position;\n\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tSpeedLimitManager speedLimitManager = SpeedLimitManager.Instance;\n\n\t\t\tif (lastCamPos == null || lastCamRot == null || !lastCamRot.Equals(camRot) || !lastCamPos.Equals(camPos)) {\n\t\t\t\t// cache visible segments\n\t\t\t\tcurrentlyVisibleSegmentIds.Clear();\n\n\t\t\t\tfor (uint segmentId = 1; segmentId < NetManager.MAX_SEGMENT_COUNT; ++segmentId) {\n\t\t\t\t\tif (!Constants.ServiceFactory.NetService.IsSegmentValid((ushort)segmentId)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t/*if ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Untouchable) != NetSegment.Flags.None)\n\t\t\t\t\t\tcontinue;*/\n\n\t\t\t\t\tif ((netManager.m_segments.m_buffer[segmentId].m_bounds.center - camPos).magnitude > TrafficManagerTool.MaxOverlayDistance)\n\t\t\t\t\t\tcontinue; // do not draw if too distant\n\n\t\t\t\t\tVector3 screenPos;\n\t\t\t\t\tbool visible = MainTool.WorldToScreenPoint(netManager.m_segments.m_buffer[segmentId].m_bounds.center, out screenPos);\n\t\t\t\t\t\n\t\t\t\t\tif (! visible)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tif (!speedLimitManager.MayHaveCustomSpeedLimits((ushort)segmentId, ref netManager.m_segments.m_buffer[segmentId]))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tcurrentlyVisibleSegmentIds.Add((ushort)segmentId);\n\t\t\t\t}\n\n\t\t\t\tlastCamPos = camPos;\n\t\t\t\tlastCamRot = camRot;\n\t\t\t}\n\n\t\t\tbool handleHovered = false;\n\t\t\tforeach (ushort segmentId in currentlyVisibleSegmentIds) {\n\t\t\t\tVector3 screenPos;\n\t\t\t\tbool visible = MainTool.WorldToScreenPoint(netManager.m_segments.m_buffer[segmentId].m_bounds.center, out screenPos);\n\n\t\t\t\tif (!visible)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tNetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info;\n\n\t\t\t\t// draw speed limits\n\t\t\t\tif (MainTool.GetToolMode() != ToolMode.VehicleRestrictions || segmentId != SelectedSegmentId) { // no speed limit overlay on selected segment when in vehicle restrictions mode\n\t\t\t\t\tif (drawSpeedLimitHandles((ushort)segmentId, ref netManager.m_segments.m_buffer[segmentId], viewOnly, ref camPos))\n\t\t\t\t\t\thandleHovered = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\toverlayHandleHovered = handleHovered;\n\t\t}\n\n\t\tprivate void _guiDefaultsWindow(int num) {\n\t\t\tList<NetInfo> mainNetInfos = SpeedLimitManager.Instance.GetCustomizableNetInfos();\n\n\t\t\tif (mainNetInfos == null || mainNetInfos.Count <= 0) {\n\t\t\t\tLog._Debug($\"mainNetInfos={mainNetInfos?.Count}\");\n\t\t\t\tDragWindow(ref defaultsWindowRect);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbool updateRoadTex = false;\n\t\t\tif (currentInfoIndex < 0 || currentInfoIndex >= mainNetInfos.Count) {\n\t\t\t\tcurrentInfoIndex = 0;\n\t\t\t\tupdateRoadTex = true;\n\t\t\t\tLog._Debug($\"set currentInfoIndex to 0\");\n\t\t\t}\n\n\t\t\tNetInfo info = mainNetInfos[currentInfoIndex];\n\t\t\tif (updateRoadTex)\n\t\t\t\tUpdateRoadTex(info);\n\n\t\t\tif (currentSpeedLimitIndex < 0) {\n\t\t\t\tcurrentSpeedLimitIndex = SpeedLimitManager.Instance.GetCustomNetInfoSpeedLimitIndex(info);\n\t\t\t\tLog._Debug($\"set currentSpeedLimitIndex to {currentSpeedLimitIndex}\");\n\t\t\t}\n\t\t\t//Log._Debug($\"currentInfoIndex={currentInfoIndex} currentSpeedLimitIndex={currentSpeedLimitIndex}\");\n\n\t\t\tGUILayout.BeginHorizontal();\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tif (GUILayout.Button(\"X\", GUILayout.Width(25))) {\n\t\t\t\tdefaultsWindowVisible = false;\n\t\t\t}\n\t\t\tGUILayout.EndHorizontal();\n\n\t\t\t// Road type label\n\t\t\tGUILayout.BeginVertical();\n\t\t\tGUILayout.Space(10);\n\t\t\tGUILayout.Label(Translation.GetString(\"Road_type\") + \":\");\n\t\t\tGUILayout.EndVertical();\n\n\t\t\t// switch between NetInfos\n\t\t\tGUILayout.BeginHorizontal();\n\t\t\t\n\t\t\tGUILayout.BeginVertical();\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tif (GUILayout.Button(\"←\", GUILayout.Width(50))) {\n\t\t\t\tcurrentInfoIndex = (currentInfoIndex + mainNetInfos.Count - 1) % mainNetInfos.Count;\n\t\t\t\tinfo = mainNetInfos[currentInfoIndex];\n\t\t\t\tcurrentSpeedLimitIndex = SpeedLimitManager.Instance.GetCustomNetInfoSpeedLimitIndex(info);\n\t\t\t\tUpdateRoadTex(info);\n\t\t\t}\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tGUILayout.EndVertical();\n\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tGUILayout.BeginVertical();\n\t\t\tGUILayout.FlexibleSpace();\n\n\t\t\t// NetInfo thumbnail\n\t\t\tGUILayout.Box(RoadTexture, GUILayout.Height(guiSpeedSignSize));\n\t\t\tGUILayout.FlexibleSpace();\n\n\t\t\tGUILayout.EndVertical();\n\t\t\tGUILayout.FlexibleSpace();\n\n\t\t\tGUILayout.BeginVertical();\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tif (GUILayout.Button(\"→\", GUILayout.Width(50))) {\n\t\t\t\tcurrentInfoIndex = (currentInfoIndex + 1) % mainNetInfos.Count;\n\t\t\t\tinfo = mainNetInfos[currentInfoIndex];\n\t\t\t\tcurrentSpeedLimitIndex = SpeedLimitManager.Instance.GetCustomNetInfoSpeedLimitIndex(info);\n\t\t\t\tUpdateRoadTex(info);\n\t\t\t}\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tGUILayout.EndVertical();\n\n\t\t\tGUILayout.EndHorizontal();\n\n\t\t\tGUIStyle centeredTextStyle = new GUIStyle(\"label\");\n\t\t\tcenteredTextStyle.alignment = TextAnchor.MiddleCenter;\n\n\t\t\t// NetInfo name\n\t\t\tGUILayout.Label(info.name, centeredTextStyle);\n\n\t\t\t// Default speed limit label\n\t\t\tGUILayout.BeginVertical();\n\t\t\tGUILayout.Space(10);\n\t\t\tGUILayout.Label(Translation.GetString(\"Default_speed_limit\") + \":\");\n\t\t\tGUILayout.EndVertical();\n\n\t\t\t// switch between speed limits\n\t\t\tGUILayout.BeginHorizontal();\n\n\t\t\tGUILayout.BeginVertical();\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tif (GUILayout.Button(\"←\", GUILayout.Width(50))) {\n\t\t\t\tcurrentSpeedLimitIndex = (currentSpeedLimitIndex + SpeedLimitManager.Instance.AvailableSpeedLimits.Count - 1) % SpeedLimitManager.Instance.AvailableSpeedLimits.Count;\n\t\t\t}\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tGUILayout.EndVertical();\n\n\t\t\tGUILayout.FlexibleSpace();\n\n\t\t\tGUILayout.BeginVertical();\n\t\t\tGUILayout.FlexibleSpace();\n\n\t\t\t// speed limit sign\n\t\t\tGUILayout.Box(TextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.AvailableSpeedLimits[currentSpeedLimitIndex]], GUILayout.Width(guiSpeedSignSize), GUILayout.Height(guiSpeedSignSize));\n\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tGUILayout.EndVertical();\n\n\t\t\tGUILayout.FlexibleSpace();\n\n\t\t\tGUILayout.BeginVertical();\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tif (GUILayout.Button(\"→\", GUILayout.Width(50))) {\n\t\t\t\tcurrentSpeedLimitIndex = (currentSpeedLimitIndex + 1) % SpeedLimitManager.Instance.AvailableSpeedLimits.Count;\n\t\t\t}\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tGUILayout.EndVertical();\n\n\t\t\tGUILayout.EndHorizontal();\n\n\t\t\t// Save & Apply\n\t\t\tGUILayout.BeginVertical();\n\t\t\tGUILayout.Space(10);\n\n\t\t\tGUILayout.BeginHorizontal();\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tif (GUILayout.Button(Translation.GetString(\"Save\"), GUILayout.Width(70))) {\n\t\t\t\tSpeedLimitManager.Instance.FixCurrentSpeedLimits(info);\n\t\t\t\tSpeedLimitManager.Instance.SetCustomNetInfoSpeedLimitIndex(info, currentSpeedLimitIndex);\n\t\t\t}\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tif (GUILayout.Button(Translation.GetString(\"Save\") + \" & \" + Translation.GetString(\"Apply\"), GUILayout.Width(160))) {\n\t\t\t\tSpeedLimitManager.Instance.SetCustomNetInfoSpeedLimitIndex(info, currentSpeedLimitIndex);\n\t\t\t\tSpeedLimitManager.Instance.ClearCurrentSpeedLimits(info);\n\t\t\t}\n\t\t\tGUILayout.FlexibleSpace();\n\t\t\tGUILayout.EndHorizontal();\n\n\t\t\tGUILayout.EndVertical();\n\n\t\t\tDragWindow(ref defaultsWindowRect);\n\t\t}\n\n\t\tprivate void UpdateRoadTex(NetInfo info) {\n\t\t\tif (info != null) {\n\t\t\t\tif (info.m_Atlas != null && info.m_Atlas.material != null && info.m_Atlas.material.mainTexture != null && info.m_Atlas.material.mainTexture is Texture2D) {\n\t\t\t\t\tTexture2D mainTex = (Texture2D)info.m_Atlas.material.mainTexture;\n\t\t\t\t\tSpriteInfo spriteInfo = info.m_Atlas[info.m_Thumbnail];\n\n\t\t\t\t\tif (spriteInfo != null && spriteInfo.texture != null && spriteInfo.texture.width > 0 && spriteInfo.texture.height > 0) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\troadTexture = new Texture2D((int)spriteInfo.texture.width, (int)spriteInfo.texture.height, TextureFormat.ARGB32, false);\n\t\t\t\t\t\t\troadTexture.SetPixels(0, 0, roadTexture.width, roadTexture.height, mainTex.GetPixels((int)(spriteInfo.region.x * mainTex.width), (int)(spriteInfo.region.y * mainTex.height), (int)(spriteInfo.region.width * mainTex.width), (int)(spriteInfo.region.height * mainTex.height)));\n\t\t\t\t\t\t\troadTexture.Apply();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tLog.Warning($\"Could not get texture from NetInfo {info.name}: {e.ToString()}\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// fallback to \"noimage\" texture\n\t\t\troadTexture = TextureResources.NoImageTexture2D;\n\t\t}\n\n\t\tprivate void _guiSpeedLimitsWindow(int num) {\n\t\t\tGUILayout.BeginHorizontal();\n\n\t\t\tColor oldColor = GUI.color;\n\t\t\tfor (int i = 0; i < SpeedLimitManager.Instance.AvailableSpeedLimits.Count; ++i) {\n\t\t\t\tif (curSpeedLimitIndex != i)\n\t\t\t\t\tGUI.color = Color.gray;\n\t\t\t\tfloat signSize = TrafficManagerTool.AdaptWidth(guiSpeedSignSize);\n\t\t\t\tif (GUILayout.Button(TextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.AvailableSpeedLimits[i]], GUILayout.Width(signSize), GUILayout.Height(signSize))) {\n\t\t\t\t\tcurSpeedLimitIndex = i;\n\t\t\t\t}\n\t\t\t\tGUI.color = oldColor;\n\n\t\t\t\tif (i == 6) {\n\t\t\t\t\tGUILayout.EndHorizontal();\n\t\t\t\t\tGUILayout.BeginHorizontal();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tGUILayout.EndHorizontal();\n\n\t\t\tif (GUILayout.Button(Translation.GetString(\"Default_speed_limits\"))) {\n\t\t\t\tTrafficManagerTool.ShowAdvisor(this.GetType().Name + \"_Defaults\");\n\t\t\t\tdefaultsWindowVisible = true;\n\t\t\t}\n\n\t\t\tshowLimitsPerLane = GUILayout.Toggle(showLimitsPerLane, Translation.GetString(\"Show_lane-wise_speed_limits\"));\n\n\t\t\tDragWindow(ref windowRect);\n\t\t}\n\n\t\tprivate bool drawSpeedLimitHandles(ushort segmentId, ref NetSegment segment, bool viewOnly, ref Vector3 camPos) {\n\t\t\tif (viewOnly && !Options.speedLimitsOverlay)\n\t\t\t\treturn false;\n\n\t\t\tVector3 center = segment.m_bounds.center;\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tbool hovered = false;\n\t\t\tushort speedLimitToSet = viewOnly ? (ushort)0 : SpeedLimitManager.Instance.AvailableSpeedLimits[curSpeedLimitIndex];\n\n\t\t\tbool showPerLane = showLimitsPerLane;\n\t\t\tif (!viewOnly) {\n\t\t\t\tshowPerLane = showLimitsPerLane ^ (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl));\n\t\t\t}\n\t\t\tif (showPerLane) {\n\t\t\t\t// show individual speed limit handle per lane\n\t\t\t\tint numDirections;\n\t\t\t\tint numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(segmentId, null, out numDirections, SpeedLimitManager.VEHICLE_TYPES);\n\n\t\t\t\tNetInfo segmentInfo = segment.Info;\n\t\t\t\tVector3 yu = (segment.m_endDirection - segment.m_startDirection).normalized;\n\t\t\t\tVector3 xu = Vector3.Cross(yu, new Vector3(0, 1f, 0)).normalized;\n\t\t\t\t/*if ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) {\n\t\t\t\t\txu = -xu;\n\t\t\t\t}*/\n\t\t\t\tfloat f = viewOnly ? 4f : 7f; // reserved sign size in game coordinates\n\t\t\t\tVector3 zero = center - 0.5f * (float)(numLanes - 1 + numDirections - 1) * f * xu;\n\n\t\t\t\tuint x = 0;\n\t\t\t\tvar guiColor = GUI.color;\n\t\t\t\tIList<LanePos> sortedLanes = Constants.ServiceFactory.NetService.GetSortedLanes(segmentId, ref segment, null, SpeedLimitManager.LANE_TYPES, SpeedLimitManager.VEHICLE_TYPES);\n\t\t\t\tbool onlyMonorailLanes = sortedLanes.Count > 0;\n\t\t\t\tif (!viewOnly) {\n\t\t\t\t\tforeach (LanePos laneData in sortedLanes) {\n\t\t\t\t\t\tbyte laneIndex = laneData.laneIndex;\n\t\t\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\n\t\t\t\t\t\tif ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) == VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\t\tonlyMonorailLanes = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tHashSet<NetInfo.Direction> directions = new HashSet<NetInfo.Direction>();\n\t\t\t\tint sortedLaneIndex = -1;\n\t\t\t\tforeach (LanePos laneData in sortedLanes) {\n\t\t\t\t\t++sortedLaneIndex;\n\t\t\t\t\tuint laneId = laneData.laneId;\n\t\t\t\t\tbyte laneIndex = laneData.laneIndex;\n\n\t\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\t\tif (!directions.Contains(laneInfo.m_finalDirection)) {\n\t\t\t\t\t\tif (directions.Count > 0)\n\t\t\t\t\t\t\t++x; // space between different directions\n\t\t\t\t\t\tdirections.Add(laneInfo.m_finalDirection);\n\t\t\t\t\t}\n\n\t\t\t\t\tbool hoveredHandle = MainTool.DrawGenericSquareOverlayGridTexture(TextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.GetCustomSpeedLimit(laneId)], camPos, zero, f, xu, yu, x, 0, speedLimitSignSize, !viewOnly);\n\t\t\t\t\tif (!viewOnly && !onlyMonorailLanes && (laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) != VehicleInfo.VehicleType.None) {\n\t\t\t\t\t\tMainTool.DrawStaticSquareOverlayGridTexture(TextureResources.VehicleInfoSignTextures[ExtVehicleType.PassengerTrain], camPos, zero, f, xu, yu, x, 1, speedLimitSignSize);\n\t\t\t\t\t}\n\t\t\t\t\tif (hoveredHandle)\n\t\t\t\t\t\thovered = true;\n\n\t\t\t\t\tif (hoveredHandle && Input.GetMouseButton(0) && !IsCursorInPanel()) {\n\t\t\t\t\t\tSpeedLimitManager.Instance.SetSpeedLimit(segmentId, laneIndex, laneInfo, laneId, speedLimitToSet);\n\n\t\t\t\t\t\tif (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {\n\t\t\t\t\t\t\tSegmentLaneTraverser.Traverse(segmentId, SegmentTraverser.TraverseDirection.AnyDirection, SegmentTraverser.TraverseSide.AnySide, SegmentLaneTraverser.LaneStopCriterion.LaneCount, SegmentTraverser.SegmentStopCriterion.Junction, SpeedLimitManager.LANE_TYPES, SpeedLimitManager.VEHICLE_TYPES, delegate (SegmentLaneVisitData data) {\n\t\t\t\t\t\t\t\tif (data.segVisitData.initial) {\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif (sortedLaneIndex != data.sortedLaneIndex) {\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(data.segVisitData.curGeo.SegmentId, delegate (ushort curSegmentId, ref NetSegment curSegment) {\n\t\t\t\t\t\t\t\t\tNetInfo.Lane curLaneInfo = curSegment.Info.m_lanes[data.curLanePos.laneIndex];\n\t\t\t\t\t\t\t\t\tSpeedLimitManager.Instance.SetSpeedLimit(curSegmentId, data.curLanePos.laneIndex, curLaneInfo, data.curLanePos.laneId, speedLimitToSet);\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t++x;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// draw speedlimits over mean middle points of lane beziers\n\t\t\t\tDictionary<NetInfo.Direction, Vector3> segCenter;\n\t\t\t\tif (!segmentCenterByDir.TryGetValue(segmentId, out segCenter)) {\n\t\t\t\t\tsegCenter = new Dictionary<NetInfo.Direction, Vector3>();\n\t\t\t\t\tsegmentCenterByDir.Add(segmentId, segCenter);\n\t\t\t\t\tTrafficManagerTool.CalculateSegmentCenterByDir(segmentId, segCenter);\n\t\t\t\t}\n\n\t\t\t\tforeach (KeyValuePair<NetInfo.Direction, Vector3> e in segCenter) {\n\t\t\t\t\tVector3 screenPos;\n\t\t\t\t\tbool visible = MainTool.WorldToScreenPoint(e.Value, out screenPos);\n\n\t\t\t\t\tif (!visible)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tfloat zoom = 1.0f / (e.Value - camPos).magnitude * 100f * MainTool.GetBaseZoom();\n\t\t\t\t\tfloat size = (viewOnly ? 0.8f : 1f) * speedLimitSignSize * zoom;\n\t\t\t\t\tColor guiColor = GUI.color;\n\t\t\t\t\tRect boundingBox = new Rect(screenPos.x - size / 2, screenPos.y - size / 2, size, size);\n\t\t\t\t\tbool hoveredHandle = !viewOnly && TrafficManagerTool.IsMouseOver(boundingBox);\n\n\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(hoveredHandle);\n\t\t\t\t\tif (hoveredHandle) {\n\t\t\t\t\t\t// mouse hovering over sign\n\t\t\t\t\t\thovered = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tGUI.color = guiColor;\n\t\t\t\t\tGUI.DrawTexture(boundingBox, TextureResources.SpeedLimitTextures[SpeedLimitManager.Instance.GetCustomSpeedLimit(segmentId, e.Key)]);\n\n\t\t\t\t\tif (hoveredHandle && Input.GetMouseButton(0) && !IsCursorInPanel()) {\n\t\t\t\t\t\t// change the speed limit to the selected one\n\t\t\t\t\t\t//Log._Debug($\"Setting speed limit of segment {segmentId}, dir {e.Key.ToString()} to {speedLimitToSet}\");\n\t\t\t\t\t\tSpeedLimitManager.Instance.SetSpeedLimit(segmentId, e.Key, speedLimitToSet);\n\n\t\t\t\t\t\tif (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {\n\n\t\t\t\t\t\t\tNetInfo.Direction normDir = e.Key;\n\t\t\t\t\t\t\tif ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) {\n\t\t\t\t\t\t\t\tnormDir = NetInfo.InvertDirection(normDir);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tSegmentLaneTraverser.Traverse(segmentId, SegmentTraverser.TraverseDirection.AnyDirection, SegmentTraverser.TraverseSide.AnySide, SegmentLaneTraverser.LaneStopCriterion.LaneCount, SegmentTraverser.SegmentStopCriterion.Junction, SpeedLimitManager.LANE_TYPES, SpeedLimitManager.VEHICLE_TYPES, delegate (SegmentLaneVisitData data) {\n\t\t\t\t\t\t\t\tif (data.segVisitData.initial) {\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbool reverse = data.segVisitData.viaStartNode == data.segVisitData.viaInitialStartNode;\n\n\t\t\t\t\t\t\t\tushort otherSegmentId = data.segVisitData.curGeo.SegmentId;\n\t\t\t\t\t\t\t\tNetInfo otherSegmentInfo = netManager.m_segments.m_buffer[otherSegmentId].Info;\n\t\t\t\t\t\t\t\tuint laneId = data.curLanePos.laneId;\n\t\t\t\t\t\t\t\tbyte laneIndex = data.curLanePos.laneIndex;\n\t\t\t\t\t\t\t\tNetInfo.Lane laneInfo = otherSegmentInfo.m_lanes[laneIndex];\n\n\t\t\t\t\t\t\t\tNetInfo.Direction otherNormDir = laneInfo.m_finalDirection;\n\t\t\t\t\t\t\t\tif ((netManager.m_segments.m_buffer[otherSegmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None ^\n\t\t\t\t\t\t\t\t\treverse) {\n\t\t\t\t\t\t\t\t\totherNormDir = NetInfo.InvertDirection(otherNormDir);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (otherNormDir == normDir) {\n\t\t\t\t\t\t\t\t\tSpeedLimitManager.Instance.SetSpeedLimit(otherSegmentId, laneInfo.m_finalDirection, speedLimitToSet);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tguiColor.a = 1f;\n\t\t\t\t\tGUI.color = guiColor;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn hovered;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/SubTools/TimedTrafficLightsTool.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing UnityEngine;\nusing TrafficManager.Manager;\nusing TrafficManager.Traffic;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Geometry.Impl;\nusing ColossalFramework.UI;\n\nnamespace TrafficManager.UI.SubTools {\n\tpublic class TimedTrafficLightsTool : SubTool {\n\t\tprivate readonly GUIStyle _counterStyle = new GUIStyle();\n\t\tprivate readonly int[] _hoveredButton = new int[2];\n\t\tprivate bool nodeSelectionLocked = false;\n\t\tprivate List<ushort> SelectedNodeIds = new List<ushort>();\n\t\tprivate bool _cursorInSecondaryPanel;\n\t\tprivate Rect _windowRect = TrafficManagerTool.MoveGUI(new Rect(0, 0, 480, 350));\n\t\tprivate Rect _windowRect2 = TrafficManagerTool.MoveGUI(new Rect(0, 0, 300, 150));\n\t\tprivate bool _timedPanelAdd = false;\n\t\tprivate int _timedEditStep = -1;\n\t\tprivate ushort _hoveredNode = 0;\n\t\tprivate bool _timedShowNumbers = false;\n\t\tprivate int _timedViewedStep = -1;\n\t\tprivate int _stepMinValue = 1;\n\t\tprivate int _stepMaxValue = 1;\n\t\tprivate StepChangeMetric _stepMetric = StepChangeMetric.Default;\n\t\tprivate float _waitFlowBalance = GlobalConfig.Instance.TimedTrafficLights.FlowToWaitRatio;\n\t\tprivate string _stepMinValueStr = \"1\";\n\t\tprivate string _stepMaxValueStr = \"1\";\n\t\tprivate bool timedLightActive = false;\n\t\tprivate int currentStep = -1;\n\t\tprivate int numSteps = 0;\n\t\tprivate bool inTestMode = false;\n\t\tprivate ushort nodeIdToCopy = 0;\n\t\tprivate HashSet<ushort> currentTimedNodeIds;\n\n\t\tprivate GUIStyle layout = new GUIStyle { normal = { textColor = new Color(1f, 1f, 1f) } };\n\t\tprivate GUIStyle layoutRed = new GUIStyle { normal = { textColor = new Color(1f, 0f, 0f) } };\n\t\tprivate GUIStyle layoutGreen = new GUIStyle { normal = { textColor = new Color(0f, 1f, 0f) } };\n\t\tprivate GUIStyle layoutYellow = new GUIStyle { normal = { textColor = new Color(1f, 1f, 0f) } };\n\n\t\tpublic TimedTrafficLightsTool(TrafficManagerTool mainTool) : base(mainTool) {\n\t\t\tcurrentTimedNodeIds = new HashSet<ushort>();\n\t\t}\n\n\t\tpublic override bool IsCursorInPanel() {\n\t\t\treturn base.IsCursorInPanel() || _cursorInSecondaryPanel;\n\t\t}\n\n\t\tprivate void RefreshCurrentTimedNodeIds(ushort forceNodeId=0) {\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\n\t\t\tif (forceNodeId == 0) {\n\t\t\t\tcurrentTimedNodeIds.Clear();\n\t\t\t} else {\n\t\t\t\tcurrentTimedNodeIds.Remove(forceNodeId);\n\t\t\t}\n\n\t\t\tfor (uint nodeId = (forceNodeId == 0 ? 1u : forceNodeId); nodeId <= (forceNodeId == 0 ? NetManager.MAX_NODE_COUNT - 1 : forceNodeId); ++nodeId) {\n\t\t\t\tif (!Constants.ServiceFactory.NetService.IsNodeValid((ushort)nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (tlsMan.HasTimedSimulation((ushort)nodeId)) {\n\t\t\t\t\tcurrentTimedNodeIds.Add((ushort)nodeId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnActivate() {\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\n\t\t\tRefreshCurrentTimedNodeIds();\n\n\t\t\tnodeSelectionLocked = false;\n\t\t\tforeach (ushort nodeId in currentTimedNodeIds) {\n\t\t\t\tif (!Constants.ServiceFactory.NetService.IsNodeValid(nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttlsMan.TrafficLightSimulations[nodeId].Housekeeping();\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnSecondaryClickOverlay() {\n\t\t\tif (!IsCursorInPanel()) {\n\t\t\t\tCleanup();\n\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsSelectNode);\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnPrimaryClickOverlay() {\n\t\t\tif (HoveredNodeId <= 0 || nodeSelectionLocked)\n\t\t\t\treturn;\n\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\n\t\t\tswitch (MainTool.GetToolMode()) {\n\t\t\t\tcase ToolMode.TimedLightsSelectNode:\n\t\t\t\tcase ToolMode.TimedLightsShowLights:\n\t\t\t\t\tif (MainTool.GetToolMode() == ToolMode.TimedLightsShowLights) {\n\t\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsSelectNode);\n\t\t\t\t\t\tClearSelectedNodes();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (! tlsMan.HasTimedSimulation(HoveredNodeId)) {\n\t\t\t\t\t\tif (IsNodeSelected(HoveredNodeId)) {\n\t\t\t\t\t\t\tRemoveSelectedNode(HoveredNodeId);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tAddSelectedNode(HoveredNodeId);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (SelectedNodeIds.Count == 0) {\n\t\t\t\t\t\t\t//timedSim.housekeeping();\n\t\t\t\t\t\t\tvar timedLight = tlsMan.TrafficLightSimulations[HoveredNodeId].TimedLight;\n\n\t\t\t\t\t\t\tif (timedLight != null) {\n\t\t\t\t\t\t\t\tSelectedNodeIds = new List<ushort>(timedLight.NodeGroup);\n\t\t\t\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsShowLights);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tMainTool.ShowTooltip(Translation.GetString(\"NODE_IS_TIMED_LIGHT\"));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase ToolMode.TimedLightsAddNode:\n\t\t\t\t\tif (SelectedNodeIds.Count <= 0) {\n\t\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsSelectNode);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (SelectedNodeIds.Contains(HoveredNodeId))\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t//bool mayEnterBlocked = Options.mayEnterBlockedJunctions;\n\t\t\t\t\tITimedTrafficLights existingTimedLight = null;\n\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\tif (!tlsMan.HasTimedSimulation(nodeId)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t//mayEnterBlocked = timedNode.vehiclesMayEnterBlockedJunctions;\n\t\t\t\t\t\texistingTimedLight = tlsMan.TrafficLightSimulations[nodeId].TimedLight;\n\t\t\t\t\t}\n\n\t\t\t\t\t/*if (timedSim2 != null)\n\t\t\t\t\t\ttimedSim2.housekeeping();*/\n\t\t\t\t\tITimedTrafficLights timedLight2 = null;\n\t\t\t\t\tif (! tlsMan.HasTimedSimulation(HoveredNodeId)) {\n\t\t\t\t\t\tvar nodeGroup = new List<ushort>();\n\t\t\t\t\t\tnodeGroup.Add(HoveredNodeId);\n\t\t\t\t\t\ttlsMan.SetUpTimedTrafficLight(HoveredNodeId, nodeGroup);\n\t\t\t\t\t}\n\t\t\t\t\ttimedLight2 = tlsMan.TrafficLightSimulations[HoveredNodeId].TimedLight;\n\n\t\t\t\t\ttimedLight2.Join(existingTimedLight);\n\t\t\t\t\tClearSelectedNodes();\n\t\t\t\t\tforeach (ushort nodeId in timedLight2.NodeGroup) {\n\t\t\t\t\t\tRefreshCurrentTimedNodeIds(nodeId);\n\t\t\t\t\t\tAddSelectedNode(nodeId);\n\t\t\t\t\t}\n\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsShowLights);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ToolMode.TimedLightsRemoveNode:\n\t\t\t\t\tif (SelectedNodeIds.Count <= 0) {\n\t\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsSelectNode);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (SelectedNodeIds.Contains(HoveredNodeId)) {\n\t\t\t\t\t\ttlsMan.RemoveNodeFromSimulation(HoveredNodeId, false, false);\n\t\t\t\t\t\tRefreshCurrentTimedNodeIds(HoveredNodeId);\n\t\t\t\t\t}\n\t\t\t\t\tRemoveSelectedNode(HoveredNodeId);\n\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsShowLights);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ToolMode.TimedLightsCopyLights:\n\t\t\t\t\tif (nodeIdToCopy == 0 || !tlsMan.HasTimedSimulation(nodeIdToCopy)) {\n\t\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsSelectNode);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// compare geometry\n\t\t\t\t\tNodeGeometry sourceNodeGeo = NodeGeometry.Get(nodeIdToCopy);\n\t\t\t\t\tNodeGeometry targetNodeGeo = NodeGeometry.Get(HoveredNodeId);\n\n\t\t\t\t\tif (sourceNodeGeo.NumSegmentEnds != targetNodeGeo.NumSegmentEnds) {\n\t\t\t\t\t\tMainTool.ShowTooltip(Translation.GetString(\"The_chosen_traffic_light_program_is_incompatible_to_this_junction\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// check for existing simulation\n\t\t\t\t\tif (tlsMan.HasTimedSimulation(HoveredNodeId)) {\n\t\t\t\t\t\tMainTool.ShowTooltip(Translation.GetString(\"NODE_IS_TIMED_LIGHT\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tITimedTrafficLights sourceTimedLights = tlsMan.TrafficLightSimulations[nodeIdToCopy].TimedLight;\n\n\t\t\t\t\t// copy `nodeIdToCopy` to `HoveredNodeId`\n\t\t\t\t\ttlsMan.SetUpTimedTrafficLight(HoveredNodeId, new List<ushort> { HoveredNodeId });\n\n\t\t\t\t\ttlsMan.TrafficLightSimulations[HoveredNodeId].TimedLight.PasteSteps(sourceTimedLights);\n\t\t\t\t\tRefreshCurrentTimedNodeIds(HoveredNodeId);\n\n\t\t\t\t\tCleanup();\n\t\t\t\t\tAddSelectedNode(HoveredNodeId);\n\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsShowLights);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnToolGUI(Event e) {\n\t\t\tbase.OnToolGUI(e);\n\n\t\t\tswitch (MainTool.GetToolMode()) {\n\t\t\t\tcase ToolMode.TimedLightsSelectNode:\n\t\t\t\t\t_guiTimedTrafficLightsNode();\n\t\t\t\t\tbreak;\n\t\t\t\tcase ToolMode.TimedLightsShowLights:\n\t\t\t\tcase ToolMode.TimedLightsAddNode:\n\t\t\t\tcase ToolMode.TimedLightsRemoveNode:\n\t\t\t\t\t_guiTimedTrafficLights();\n\t\t\t\t\tbreak;\n\t\t\t\tcase ToolMode.TimedLightsCopyLights:\n\t\t\t\t\t_guiTimedTrafficLightsCopy();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tpublic override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\tbool onlySelected = MainTool.GetToolMode() == ToolMode.TimedLightsRemoveNode;\n\t\t\t//Log._Debug($\"nodeSelLocked={nodeSelectionLocked} HoveredNodeId={HoveredNodeId} IsNodeSelected={IsNodeSelected(HoveredNodeId)} onlySelected={onlySelected} isinsideui={MainTool.GetToolController().IsInsideUI} cursorVis={Cursor.visible}\");\n            if (!nodeSelectionLocked &&\n\t\t\t\tHoveredNodeId != 0 &&\n\t\t\t\t(!IsNodeSelected(HoveredNodeId) ^ onlySelected) &&\n\t\t\t\t!MainTool.GetToolController().IsInsideUI &&\n\t\t\t\tCursor.visible &&\n\t\t\t\tFlags.mayHaveTrafficLight(HoveredNodeId)\n\t\t\t) {\n\t\t\t\tMainTool.DrawNodeCircle(cameraInfo, HoveredNodeId, false, false);\n\t\t\t}\n\n\t\t\tif (SelectedNodeIds.Count <= 0) return;\n\n\t\t\tforeach (var index in SelectedNodeIds) {\n\t\t\t\tMainTool.DrawNodeCircle(cameraInfo, index, true, false);\n\t\t\t}\n\t\t}\n\n\t\tprivate void _guiTimedControlPanel(int num) {\n\t\t\t//Log._Debug(\"guiTimedControlPanel\");\n\t\t\ttry {\n\t\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\n\t\t\t\tif (MainTool.GetToolMode() == ToolMode.TimedLightsAddNode || MainTool.GetToolMode() == ToolMode.TimedLightsRemoveNode) {\n\t\t\t\t\tGUILayout.Label(Translation.GetString(\"Select_junction\"));\n\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Cancel\"))) {\n\t\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsShowLights);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tDragWindow(ref _windowRect);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (! tlsMan.HasTimedSimulation(SelectedNodeIds[0])) {\n\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsSelectNode);\n\t\t\t\t\t//Log._Debug(\"nodesim or timednodemain is null\");\n\t\t\t\t\tDragWindow(ref _windowRect);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tvar timedNodeMain = tlsMan.TrafficLightSimulations[SelectedNodeIds[0]].TimedLight;\n\n\t\t\t\tif (Event.current.type == EventType.Layout) {\n\t\t\t\t\ttimedLightActive = tlsMan.HasActiveTimedSimulation(SelectedNodeIds[0]);\n\t\t\t\t\tcurrentStep = timedNodeMain.CurrentStep;\n\t\t\t\t\tinTestMode = timedNodeMain.IsInTestMode();\n\t\t\t\t\tnumSteps = timedNodeMain.NumSteps();\n\t\t\t\t}\n\n\t\t\t\tif (!timedLightActive && numSteps > 0 && !_timedPanelAdd && _timedEditStep < 0 && _timedViewedStep < 0) {\n\t\t\t\t\t_timedViewedStep = 0;\n\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\ttlsMan.TrafficLightSimulations[nodeId].TimedLight?.GetStep(_timedViewedStep).UpdateLiveLights(true);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (var i = 0; i < timedNodeMain.NumSteps(); i++) {\n\t\t\t\t\tGUILayout.BeginHorizontal();\n\n\t\t\t\t\tif (_timedEditStep != i) {\n\t\t\t\t\t\tif (timedLightActive) {\n\t\t\t\t\t\t\tif (i == currentStep) {\n\t\t\t\t\t\t\t\tGUILayout.BeginVertical();\n\t\t\t\t\t\t\t\tGUILayout.Space(5);\n\t\t\t\t\t\t\t\tString labelStr = Translation.GetString(\"State\") + \" \" + (i + 1) + \": (\" + Translation.GetString(\"min/max\") + \")\" + timedNodeMain.GetStep(i).MinTimeRemaining() + \"/\" + timedNodeMain.GetStep(i).MaxTimeRemaining();\n\t\t\t\t\t\t\t\tfloat flow = Single.NaN;\n\t\t\t\t\t\t\t\tfloat wait = Single.NaN;\n\t\t\t\t\t\t\t\tif (inTestMode) {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\ttimedNodeMain.GetStep(timedNodeMain.CurrentStep).CalcWaitFlow(true, timedNodeMain.CurrentStep, out wait, out flow);\n\t\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\t\tLog.Warning(\"calcWaitFlow in UI: This is not thread-safe: \" + e.ToString());\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\twait = timedNodeMain.GetStep(i).CurrentWait;\n\t\t\t\t\t\t\t\t\tflow = timedNodeMain.GetStep(i).CurrentFlow;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (!Single.IsNaN(flow) && !Single.IsNaN(wait))\n\t\t\t\t\t\t\t\t\tlabelStr += \" \" + Translation.GetString(\"avg._flow\") + \": \" + String.Format(\"{0:0.##}\", flow) + \" \" + Translation.GetString(\"avg._wait\") + \": \" + String.Format(\"{0:0.##}\", wait);\n\t\t\t\t\t\t\t\tGUIStyle labelLayout = layout;\n\t\t\t\t\t\t\t\tif (inTestMode && !Single.IsNaN(wait) && !Single.IsNaN(flow)) {\n\t\t\t\t\t\t\t\t\tfloat metric;\n\t\t\t\t\t\t\t\t\tif (timedNodeMain.GetStep(i).ShouldGoToNextStep(flow, wait, out metric))\n\t\t\t\t\t\t\t\t\t\tlabelLayout = layoutRed;\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tlabelLayout = layoutGreen;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tbool inEndTransition = false;\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tinEndTransition = timedNodeMain.GetStep(i).IsInEndTransition();\n\t\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\t\tLog.Error(\"Error while determining if timed traffic light is in end transition: \" + e.ToString());\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tlabelLayout = inEndTransition ? layoutYellow : layoutGreen;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tGUILayout.Label(labelStr, labelLayout);\n\t\t\t\t\t\t\t\tGUILayout.Space(5);\n\t\t\t\t\t\t\t\tGUILayout.EndVertical();\n\t\t\t\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Skip\"), GUILayout.Width(80))) {\n\t\t\t\t\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\t\t\t\t\ttlsMan.TrafficLightSimulations[nodeId].TimedLight?.SkipStep();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tGUILayout.Label(Translation.GetString(\"State\") + \" \" + (i + 1) + \": \" + timedNodeMain.GetStep(i).MinTime + \" - \" + timedNodeMain.GetStep(i).MaxTime, layout);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tGUIStyle labelLayout = layout;\n\t\t\t\t\t\t\tif (_timedViewedStep == i) {\n\t\t\t\t\t\t\t\tlabelLayout = layoutGreen;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tGUILayout.Label(Translation.GetString(\"State\") + \" \" + (i + 1) + \": \" + timedNodeMain.GetStep(i).MinTime + \" - \" + timedNodeMain.GetStep(i).MaxTime, labelLayout);\n\n\t\t\t\t\t\t\tif (_timedEditStep < 0) {\n\t\t\t\t\t\t\t\tGUILayout.BeginHorizontal(GUILayout.Width(100));\n\n\t\t\t\t\t\t\t\tif (i > 0) {\n\t\t\t\t\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"up\"), GUILayout.Width(48))) {\n\t\t\t\t\t\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\t\t\t\t\t\ttlsMan.TrafficLightSimulations[nodeId].TimedLight?.MoveStep(i, i - 1);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t_timedViewedStep = i - 1;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tGUILayout.Space(50);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (i < numSteps - 1) {\n\t\t\t\t\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"down\"), GUILayout.Width(48))) {\n\t\t\t\t\t\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\t\t\t\t\t\ttlsMan.TrafficLightSimulations[nodeId].TimedLight?.MoveStep(i, i + 1);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t_timedViewedStep = i + 1;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tGUILayout.Space(50);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tGUILayout.EndHorizontal();\n\n\t\t\t\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"View\"), GUILayout.Width(70))) {\n\t\t\t\t\t\t\t\t\t_timedPanelAdd = false;\n\t\t\t\t\t\t\t\t\t_timedViewedStep = i;\n\n\t\t\t\t\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\t\t\t\t\ttlsMan.TrafficLightSimulations[nodeId].TimedLight?.GetStep(i).UpdateLiveLights(true);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Edit\"), GUILayout.Width(65))) {\n\t\t\t\t\t\t\t\t\t_timedPanelAdd = false;\n\t\t\t\t\t\t\t\t\t_timedEditStep = i;\n\t\t\t\t\t\t\t\t\t_timedViewedStep = -1;\n\t\t\t\t\t\t\t\t\t_stepMinValue = timedNodeMain.GetStep(i).MinTime;\n\t\t\t\t\t\t\t\t\t_stepMaxValue = timedNodeMain.GetStep(i).MaxTime;\n\t\t\t\t\t\t\t\t\t_stepMetric = timedNodeMain.GetStep(i).ChangeMetric;\n\t\t\t\t\t\t\t\t\t_waitFlowBalance = timedNodeMain.GetStep(i).WaitFlowBalance;\n\t\t\t\t\t\t\t\t\t_stepMinValueStr = _stepMinValue.ToString();\n\t\t\t\t\t\t\t\t\t_stepMaxValueStr = _stepMaxValue.ToString();\n\t\t\t\t\t\t\t\t\tnodeSelectionLocked = true;\n\n\t\t\t\t\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\t\t\t\t\ttlsMan.TrafficLightSimulations[nodeId].TimedLight?.GetStep(i).UpdateLiveLights(true);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Delete\"), GUILayout.Width(70))) {\n\t\t\t\t\t\t\t\t\t_timedPanelAdd = false;\n\t\t\t\t\t\t\t\t\t_timedViewedStep = -1;\n\n\t\t\t\t\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\t\t\t\t\ttlsMan.TrafficLightSimulations[nodeId].TimedLight?.RemoveStep(i);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnodeSelectionLocked = true;\n\t\t\t\t\t\tint oldStepMinValue = _stepMinValue;\n\t\t\t\t\t\tint oldStepMaxValue = _stepMaxValue;\n\n\t\t\t\t\t\t// Editing step\n\t\t\t\t\t\tGUILayout.Label(Translation.GetString(\"Min._Time:\"), GUILayout.Width(75));\n\t\t\t\t\t\t_stepMinValueStr = GUILayout.TextField(_stepMinValueStr, GUILayout.Height(20));\n\t\t\t\t\t\tif (!Int32.TryParse(_stepMinValueStr, out _stepMinValue))\n\t\t\t\t\t\t\t_stepMinValue = oldStepMinValue;\n\n\t\t\t\t\t\tGUILayout.Label(Translation.GetString(\"Max._Time:\"), GUILayout.Width(75));\n\t\t\t\t\t\t_stepMaxValueStr = GUILayout.TextField(_stepMaxValueStr, GUILayout.Height(20));\n\t\t\t\t\t\tif (!Int32.TryParse(_stepMaxValueStr, out _stepMaxValue))\n\t\t\t\t\t\t\t_stepMaxValue = oldStepMaxValue;\n\n\t\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Save\"), GUILayout.Width(70))) {\n\t\t\t\t\t\t\tif (_stepMinValue < 0)\n\t\t\t\t\t\t\t\t_stepMinValue = 0;\n\t\t\t\t\t\t\tif (_stepMaxValue <= 0)\n\t\t\t\t\t\t\t\t_stepMaxValue = 1;\n\t\t\t\t\t\t\tif (_stepMaxValue < _stepMinValue)\n\t\t\t\t\t\t\t\t_stepMaxValue = _stepMinValue;\n\t\t\t\t\t\t\tif (_waitFlowBalance <= 0)\n\t\t\t\t\t\t\t\t_waitFlowBalance = GlobalConfig.Instance.TimedTrafficLights.FlowToWaitRatio;\n\n\t\t\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\t\t\tvar step = tlsMan.TrafficLightSimulations[nodeId].TimedLight?.GetStep(_timedEditStep);\n\n\t\t\t\t\t\t\t\tif (step != null) {\n\t\t\t\t\t\t\t\t\tstep.MinTime = _stepMinValue;\n\t\t\t\t\t\t\t\t\tstep.MaxTime = _stepMaxValue;\n\t\t\t\t\t\t\t\t\tstep.ChangeMetric = _stepMetric;\n\t\t\t\t\t\t\t\t\tstep.WaitFlowBalance = _waitFlowBalance;\n\t\t\t\t\t\t\t\t\tstep.UpdateLights();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t_timedViewedStep = _timedEditStep;\n\t\t\t\t\t\t\t_timedEditStep = -1;\n\t\t\t\t\t\t\tnodeSelectionLocked = false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tGUILayout.EndHorizontal();\n\n\t\t\t\t\t\tBuildStepChangeMetricDisplay(true);\n\t\t\t\t\t\tBuildFlowPolicyDisplay(true);\n\t\t\t\t\t\tGUILayout.BeginHorizontal();\n\t\t\t\t\t}\n\n\t\t\t\t\tGUILayout.EndHorizontal();\n\t\t\t\t} // foreach step\n\n\t\t\t\tGUILayout.BeginHorizontal();\n\n\t\t\t\tif (_timedEditStep < 0 && !timedLightActive) {\n\t\t\t\t\tif (_timedPanelAdd) {\n\t\t\t\t\t\tnodeSelectionLocked = true;\n\t\t\t\t\t\t// new step\n\t\t\t\t\t\tint oldStepMinValue = _stepMinValue;\n\t\t\t\t\t\tint oldStepMaxValue = _stepMaxValue;\n\n\t\t\t\t\t\tGUILayout.Label(Translation.GetString(\"Min._Time:\"), GUILayout.Width(65));\n\t\t\t\t\t\t_stepMinValueStr = GUILayout.TextField(_stepMinValueStr, GUILayout.Height(20));\n\t\t\t\t\t\tif (!Int32.TryParse(_stepMinValueStr, out _stepMinValue))\n\t\t\t\t\t\t\t_stepMinValue = oldStepMinValue;\n\n\t\t\t\t\t\tGUILayout.Label(Translation.GetString(\"Max._Time:\"), GUILayout.Width(65));\n\t\t\t\t\t\t_stepMaxValueStr = GUILayout.TextField(_stepMaxValueStr, GUILayout.Height(20));\n\t\t\t\t\t\tif (!Int32.TryParse(_stepMaxValueStr, out _stepMaxValue))\n\t\t\t\t\t\t\t_stepMaxValue = oldStepMaxValue;\n\n\t\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Add\"), GUILayout.Width(70))) {\n\t\t\t\t\t\t\tTrafficManagerTool.ShowAdvisor(this.GetType().Name + \"_AddStep\");\n\t\t\t\t\t\t\tif (_stepMinValue < 0)\n\t\t\t\t\t\t\t\t_stepMinValue = 0;\n\t\t\t\t\t\t\tif (_stepMaxValue <= 0)\n\t\t\t\t\t\t\t\t_stepMaxValue = 1;\n\t\t\t\t\t\t\tif (_stepMaxValue < _stepMinValue)\n\t\t\t\t\t\t\t\t_stepMaxValue = _stepMinValue;\n\t\t\t\t\t\t\tif (_waitFlowBalance <= 0)\n\t\t\t\t\t\t\t\t_waitFlowBalance = 1f;\n\n\t\t\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\t\t\ttlsMan.TrafficLightSimulations[nodeId].TimedLight?.AddStep(_stepMinValue, _stepMaxValue, _stepMetric, _waitFlowBalance);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t_timedPanelAdd = false;\n\t\t\t\t\t\t\t_timedViewedStep = timedNodeMain.NumSteps() - 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (GUILayout.Button(\"X\", GUILayout.Width(22))) {\n\t\t\t\t\t\t\t_timedPanelAdd = false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tGUILayout.EndHorizontal();\n\n\t\t\t\t\t\tBuildStepChangeMetricDisplay(true);\n\t\t\t\t\t\tBuildFlowPolicyDisplay(true);\n\t\t\t\t\t\tGUILayout.BeginHorizontal();\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (_timedEditStep < 0) {\n\t\t\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Add_step\"))) {\n\t\t\t\t\t\t\t\tTrafficManagerTool.ShowAdvisor(this.GetType().Name + \"_AddStep\");\n\t\t\t\t\t\t\t\t_timedPanelAdd = true;\n\t\t\t\t\t\t\t\tnodeSelectionLocked = true;\n\t\t\t\t\t\t\t\t_timedViewedStep = -1;\n\t\t\t\t\t\t\t\t_timedEditStep = -1;\n\t\t\t\t\t\t\t\t_stepMetric = StepChangeMetric.Default;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tGUILayout.EndHorizontal();\n\n\t\t\t\tGUILayout.Space(5);\n\n\t\t\t\tif (numSteps > 1 && _timedEditStep < 0) {\n\t\t\t\t\tif (timedLightActive) {\n\t\t\t\t\t\tif (GUILayout.Button(_timedShowNumbers ? Translation.GetString(\"Hide_counters\") : Translation.GetString(\"Show_counters\"))) {\n\t\t\t\t\t\t\t_timedShowNumbers = !_timedShowNumbers;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Stop\"))) {\n\t\t\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\t\t\ttlsMan.TrafficLightSimulations[nodeId].TimedLight?.Stop();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/*bool isInTestMode = false;\n\t\t\t\t\t\tforeach (var sim in SelectedNodeIndexes.Select(tlsMan.GetNodeSimulation)) {\n\t\t\t\t\t\t\tif (sim.TimedLight.IsInTestMode()) {\n\t\t\t\t\t\t\t\tisInTestMode = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}*/\n\n\n\t\t\t\t\t\tvar curStep = timedNodeMain.CurrentStep;\n\t\t\t\t\t\tITimedTrafficLightsStep currentStep = timedNodeMain.GetStep(curStep);\n\t\t\t\t\t\t_stepMetric = currentStep.ChangeMetric;\n\t\t\t\t\t\tif (currentStep.MaxTime > currentStep.MinTime) {\n\t\t\t\t\t\t\tBuildStepChangeMetricDisplay(false);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t_waitFlowBalance = timedNodeMain.GetStep(curStep).WaitFlowBalance;\n\t\t\t\t\t\tBuildFlowPolicyDisplay(inTestMode);\n\t\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\t\tvar step = tlsMan.TrafficLightSimulations[nodeId].TimedLight?.GetStep(curStep);\n\t\t\t\t\t\t\tif (step != null) {\n\t\t\t\t\t\t\t\tstep.WaitFlowBalance = _waitFlowBalance;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t//var mayEnterIfBlocked = GUILayout.Toggle(timedNodeMain.vehiclesMayEnterBlockedJunctions, Translation.GetString(\"Vehicles_may_enter_blocked_junctions\"), new GUILayoutOption[] { });\n\t\t\t\t\t\tvar testMode = GUILayout.Toggle(inTestMode, Translation.GetString(\"Enable_test_mode_(stay_in_current_step)\"), new GUILayoutOption[] { });\n\t\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\t\ttlsMan.TrafficLightSimulations[nodeId].TimedLight?.SetTestMode(testMode);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (_timedEditStep < 0 && !_timedPanelAdd) {\n\t\t\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Start\"))) {\n\t\t\t\t\t\t\t\t_timedPanelAdd = false;\n\t\t\t\t\t\t\t\tnodeSelectionLocked = false;\n\n\t\t\t\t\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\t\t\t\t\ttlsMan.TrafficLightSimulations[nodeId].TimedLight?.Start();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (_timedEditStep >= 0) {\n\t\t\t\t\tDragWindow(ref _windowRect);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tGUILayout.Space(30);\n\n\t\t\t\tif (SelectedNodeIds.Count == 1 && timedNodeMain.NumSteps() > 0) {\n\t\t\t\t\tGUILayout.BeginHorizontal();\n\n\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Rotate_left\"))) {\n\t\t\t\t\t\ttimedNodeMain.RotateLeft();\n\t\t\t\t\t\t_timedViewedStep = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Copy\"))) {\n\t\t\t\t\t\tTrafficManagerTool.ShowAdvisor(this.GetType().Name + \"_Copy\");\n\t\t\t\t\t\tnodeIdToCopy = SelectedNodeIds[0];\n\t\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsCopyLights);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Rotate_right\"))) {\n\t\t\t\t\t\ttimedNodeMain.RotateRight();\n\t\t\t\t\t\t_timedViewedStep = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tGUILayout.EndHorizontal();\n\t\t\t\t}\n\n\t\t\t\tif (!timedLightActive) {\n\t\t\t\t\tGUILayout.Space(30);\n\n\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Add_junction_to_timed_light\"))) {\n\t\t\t\t\t\tTrafficManagerTool.ShowAdvisor(this.GetType().Name + \"_AddJunction\");\n\t\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsAddNode);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (SelectedNodeIds.Count > 1) {\n\t\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Remove_junction_from_timed_light\"))) {\n\t\t\t\t\t\t\tTrafficManagerTool.ShowAdvisor(this.GetType().Name + \"_RemoveJunction\");\n\t\t\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsRemoveNode);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tGUILayout.Space(30);\n\n\t\t\t\t\tif (GUILayout.Button(Translation.GetString(\"Remove_timed_traffic_light\"))) {\n\t\t\t\t\t\tDisableTimed();\n\t\t\t\t\t\tClearSelectedNodes();\n\t\t\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsSelectNode);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tDragWindow(ref _windowRect);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error($\"TimedTrafficLightsTool._guiTimedControlPanel: {e}\");\n\t\t\t}\n\t\t}\n\n\t\tpublic override void Cleanup() {\n\t\t\tSelectedNodeId = 0;\n\t\t\tClearSelectedNodes();\n\n\t\t\t_timedShowNumbers = false;\n\t\t\t_timedPanelAdd = false;\n\t\t\t_timedEditStep = -1;\n\t\t\t_hoveredNode = 0;\n\t\t\t_timedShowNumbers = false;\n\t\t\t_timedViewedStep = -1;\n\t\t\ttimedLightActive = false;\n\t\t\tnodeIdToCopy = 0;\n\t\t}\n\n\t\tpublic override void Initialize() {\n\t\t\tbase.Initialize();\n\t\t\tCleanup();\n\t\t\tif (Options.timedLightsOverlay) {\n\t\t\t\tRefreshCurrentTimedNodeIds();\n\t\t\t} else {\n\t\t\t\tcurrentTimedNodeIds.Clear();\n\t\t\t}\n\t\t}\n\n\t\tprivate void BuildStepChangeMetricDisplay(bool editable) {\n\t\t\tGUILayout.BeginVertical();\n\n\t\t\tif (editable) {\n\t\t\t\tGUILayout.Label(Translation.GetString(\"After_min._time_has_elapsed_switch_to_next_step_if\") + \":\");\n\n\t\t\t\tif (GUILayout.Toggle(_stepMetric == StepChangeMetric.Default, GetStepChangeMetricDescription(StepChangeMetric.Default))) {\n\t\t\t\t\t_stepMetric = StepChangeMetric.Default;\n\t\t\t\t}\n\n\t\t\t\tif (GUILayout.Toggle(_stepMetric == StepChangeMetric.FirstFlow, GetStepChangeMetricDescription(StepChangeMetric.FirstFlow))) {\n\t\t\t\t\t_stepMetric = StepChangeMetric.FirstFlow;\n\t\t\t\t}\n\n\t\t\t\tif (GUILayout.Toggle(_stepMetric == StepChangeMetric.FirstWait, GetStepChangeMetricDescription(StepChangeMetric.FirstWait))) {\n\t\t\t\t\t_stepMetric = StepChangeMetric.FirstWait;\n\t\t\t\t}\n\n\t\t\t\tif (GUILayout.Toggle(_stepMetric == StepChangeMetric.NoFlow, GetStepChangeMetricDescription(StepChangeMetric.NoFlow))) {\n\t\t\t\t\t_stepMetric = StepChangeMetric.NoFlow;\n\t\t\t\t}\n\n\t\t\t\tif (GUILayout.Toggle(_stepMetric == StepChangeMetric.NoWait, GetStepChangeMetricDescription(StepChangeMetric.NoWait))) {\n\t\t\t\t\t_stepMetric = StepChangeMetric.NoWait;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tGUILayout.Label(Translation.GetString(\"Adaptive_step_switching\") + \": \" + GetStepChangeMetricDescription(_stepMetric));\n\t\t\t}\n\n\t\t\tGUILayout.EndVertical();\n\t\t}\n\n\t\tprivate void BuildFlowPolicyDisplay(bool editable) {\n\t\t\tstring formatStr;\n\t\t\tif (_waitFlowBalance < 0.01f)\n\t\t\t\tformatStr = \"{0:0.###}\";\n\t\t\telse if (_waitFlowBalance < 0.1f)\n\t\t\t\tformatStr = \"{0:0.##}\";\n\t\t\telse\n\t\t\t\tformatStr = \"{0:0.#}\";\n\n\t\t\tGUILayout.BeginHorizontal();\n\t\t\tif (editable) {\n\t\t\t\tGUILayout.Label(Translation.GetString(\"Sensitivity\") + \" (\" + String.Format(formatStr, _waitFlowBalance) + \", \" + getWaitFlowBalanceInfo() + \"):\");\n\t\t\t\tif (_waitFlowBalance <= 0.01f) {\n\t\t\t\t\tif (_waitFlowBalance >= 0) {\n\t\t\t\t\t\tif (GUILayout.Button(\"-.001\")) {\n\t\t\t\t\t\t\t_waitFlowBalance -= 0.001f;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (_waitFlowBalance < 0.01f) {\n\t\t\t\t\t\tif (GUILayout.Button(\"+.001\")) {\n\t\t\t\t\t\t\t_waitFlowBalance += 0.001f;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (_waitFlowBalance <= 0.1f) {\n\t\t\t\t\tif (GUILayout.Button(\"-.01\")) {\n\t\t\t\t\t\t_waitFlowBalance -= 0.01f;\n\t\t\t\t\t}\n\t\t\t\t\tif (_waitFlowBalance < 0.1f) {\n\t\t\t\t\t\tif (GUILayout.Button(\"+.01\")) {\n\t\t\t\t\t\t\t_waitFlowBalance += 0.01f;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (_waitFlowBalance < 0)\n\t\t\t\t\t_waitFlowBalance = 0;\n\t\t\t\tif (_waitFlowBalance > 10)\n\t\t\t\t\t_waitFlowBalance = 10;\n\t\t\t\tGUILayout.EndHorizontal();\n\n\t\t\t\t_waitFlowBalance = GUILayout.HorizontalSlider(_waitFlowBalance, 0.001f, 10f);\n\n\t\t\t\t// step snapping\n\t\t\t\tif (_waitFlowBalance < 0.001f) {\n\t\t\t\t\t_waitFlowBalance = 0.001f;\n\t\t\t\t} else if (_waitFlowBalance < 0.01f) {\n\t\t\t\t\t_waitFlowBalance = Mathf.Round(_waitFlowBalance * 1000f) * 0.001f;\n\t\t\t\t} else if (_waitFlowBalance < 0.1f) {\n\t\t\t\t\t_waitFlowBalance = Mathf.Round(_waitFlowBalance * 100f) * 0.01f;\n\t\t\t\t} else if (_waitFlowBalance < 10f) {\n\t\t\t\t\t_waitFlowBalance = Mathf.Round(_waitFlowBalance * 10f) * 0.1f;\n\t\t\t\t} else {\n\t\t\t\t\t_waitFlowBalance = 10f;\n\t\t\t\t}\n\n\t\t\t\tGUILayout.BeginHorizontal();\n\t\t\t\tGUIStyle style = new GUIStyle();\n\t\t\t\tstyle.normal.textColor = Color.white;\n\t\t\t\tstyle.alignment = TextAnchor.LowerLeft;\n\t\t\t\tGUILayout.Label(Translation.GetString(\"Low\"), style, new GUILayoutOption[] { GUILayout.Height(10) });\n\t\t\t\tstyle.alignment = TextAnchor.LowerRight;\n\t\t\t\tGUILayout.Label(Translation.GetString(\"High\"), style, new GUILayoutOption[] { GUILayout.Height(10) });\n\t\t\t} else {\n\t\t\t\tGUILayout.Label(Translation.GetString(\"Sensitivity\") + \": \" + String.Format(formatStr, _waitFlowBalance) + \" (\" + getWaitFlowBalanceInfo() + \")\");\n\t\t\t}\n\t\t\tGUILayout.EndHorizontal();\n\n\t\t\tGUILayout.Space(5);\n\t\t}\n\n\t\tprivate string GetStepChangeMetricDescription(StepChangeMetric metric) {\n\t\t\tswitch (metric) {\n\t\t\t\tcase StepChangeMetric.Default:\n\t\t\t\tdefault:\n\t\t\t\t\treturn Translation.GetString(\"flow_ratio\") + \" < \" + Translation.GetString(\"wait_ratio\") + \" (\" + Translation.GetString(\"default\") + \")\";\n\t\t\t\tcase StepChangeMetric.FirstFlow:\n\t\t\t\t\treturn Translation.GetString(\"flow_ratio\") + \" > 0\";\n\t\t\t\tcase StepChangeMetric.FirstWait:\n\t\t\t\t\treturn Translation.GetString(\"wait_ratio\") + \" > 0\";\n\t\t\t\tcase StepChangeMetric.NoFlow:\n\t\t\t\t\treturn Translation.GetString(\"flow_ratio\") + \" = 0\";\n\t\t\t\tcase StepChangeMetric.NoWait:\n\t\t\t\t\treturn Translation.GetString(\"wait_ratio\") + \" = 0\";\n\t\t\t}\n\t\t}\n\n\t\tprivate void _guiTimedTrafficLightsNode() {\n\t\t\t_cursorInSecondaryPanel = false;\n\n\t\t\t_windowRect2 = GUILayout.Window(252, _windowRect2, _guiTimedTrafficLightsNodeWindow, Translation.GetString(\"Select_nodes_windowTitle\"), WindowStyle);\n\n\t\t\t_cursorInSecondaryPanel = _windowRect2.Contains(Event.current.mousePosition);\n\t\t}\n\n\t\tprivate void _guiTimedTrafficLights() {\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\t\t\tCustomSegmentLightsManager customTrafficLightsManager = CustomSegmentLightsManager.Instance;\n\t\t\tTrafficPriorityManager prioMan = TrafficPriorityManager.Instance;\n\n\t\t\t_cursorInSecondaryPanel = false;\n\n\t\t\t_windowRect = GUILayout.Window(253, _windowRect, _guiTimedControlPanel, Translation.GetString(\"Timed_traffic_lights_manager\"), WindowStyle);\n\n\t\t\t_cursorInSecondaryPanel = _windowRect.Contains(Event.current.mousePosition);\n\n\t\t\tGUI.matrix = Matrix4x4.TRS(new Vector3(0, 0, 0), Quaternion.identity, new Vector3(1, 1, 1)); // revert scaling\n\t\t\tShowGUI();\n\t\t}\n\n\t\tprivate void _guiTimedTrafficLightsCopy() {\n\t\t\t_cursorInSecondaryPanel = false;\n\n\t\t\t_windowRect2 = GUILayout.Window(255, _windowRect2, _guiTimedTrafficLightsPasteWindow, Translation.GetString(\"Paste\"), WindowStyle);\n\n\t\t\t_cursorInSecondaryPanel = _windowRect2.Contains(Event.current.mousePosition);\n\t\t}\n\n\t\tprivate void _guiTimedTrafficLightsPasteWindow(int num) {\n\t\t\tGUILayout.Label(Translation.GetString(\"Select_junction\"));\n\t\t}\n\n\t\tprivate void _guiTimedTrafficLightsNodeWindow(int num) {\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\n\t\t\tif (SelectedNodeIds.Count < 1) {\n\t\t\t\tGUILayout.Label(Translation.GetString(\"Select_nodes\"));\n\t\t\t} else {\n\t\t\t\tvar txt = SelectedNodeIds.Aggregate(\"\", (current, t) => current + (Translation.GetString(\"Node\") + \" \" + t + \"\\n\"));\n\n\t\t\t\tGUILayout.Label(txt);\n\n\t\t\t\tif (SelectedNodeIds.Count > 0 && GUILayout.Button(Translation.GetString(\"Deselect_all_nodes\"))) {\n\t\t\t\t\tClearSelectedNodes();\n\t\t\t\t}\n\t\t\t\tif (!GUILayout.Button(Translation.GetString(\"Setup_timed_traffic_light\"))) return;\n\n\t\t\t\t_waitFlowBalance = GlobalConfig.Instance.TimedTrafficLights.FlowToWaitRatio;\n\t\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\t\ttlsMan.SetUpTimedTrafficLight(nodeId, SelectedNodeIds);\n\t\t\t\t\tRefreshCurrentTimedNodeIds(nodeId);\n\t\t\t\t}\n\n\t\t\t\tMainTool.SetToolMode(ToolMode.TimedLightsShowLights);\n\t\t\t}\n\n\t\t\tDragWindow(ref _windowRect2);\n\t\t}\n\n\t\tprivate string getWaitFlowBalanceInfo() {\n\t\t\tif (_waitFlowBalance < 0.1f) {\n\t\t\t\treturn Translation.GetString(\"Extreme_long_green/red_phases\");\n\t\t\t} else if (_waitFlowBalance < 0.5f) {\n\t\t\t\treturn Translation.GetString(\"Very_long_green/red_phases\");\n\t\t\t} else if (_waitFlowBalance < 0.75f) {\n\t\t\t\treturn Translation.GetString(\"Long_green/red_phases\");\n\t\t\t} else if (_waitFlowBalance < 1.25f) {\n\t\t\t\treturn Translation.GetString(\"Moderate_green/red_phases\");\n\t\t\t} else if (_waitFlowBalance < 1.5f) {\n\t\t\t\treturn Translation.GetString(\"Short_green/red_phases\");\n\t\t\t} else if (_waitFlowBalance < 2.5f) {\n\t\t\t\treturn Translation.GetString(\"Very_short_green/red_phases\");\n\t\t\t} else {\n\t\t\t\treturn Translation.GetString(\"Extreme_short_green/red_phases\");\n\t\t\t}\n\t\t}\n\n\t\tprivate void DisableTimed() {\n\t\t\tif (SelectedNodeIds.Count <= 0) return;\n\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\n\t\t\tforeach (var selectedNodeId in SelectedNodeIds) {\n\t\t\t\ttlsMan.RemoveNodeFromSimulation(selectedNodeId, true, false);\n\t\t\t\tRefreshCurrentTimedNodeIds(selectedNodeId);\n\t\t\t}\n\t\t}\n\n\t\tprivate void AddSelectedNode(ushort node) {\n\t\t\tSelectedNodeIds.Add(node);\n\t\t}\n\n\t\tprivate bool IsNodeSelected(ushort node) {\n\t\t\treturn SelectedNodeIds.Contains(node);\n\t\t}\n\n\t\tprivate void RemoveSelectedNode(ushort node) {\n\t\t\tSelectedNodeIds.Remove(node);\n\t\t}\n\n\t\tprivate void ClearSelectedNodes() {\n\t\t\tSelectedNodeIds.Clear();\n\t\t}\n\n\t\tprivate void drawStraightLightTexture(RoadBaseAI.TrafficLightState state, Rect rect) {\n\t\t\tswitch (state) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.GreenLightStraightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.GreenToRed:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.YellowLightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\tdefault:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.RedLightStraightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.RedToGreen:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.YellowLightStraightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tprivate void drawForwardLeftLightTexture(RoadBaseAI.TrafficLightState state, Rect rect) {\n\t\t\tswitch (state) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.GreenLightForwardLeftTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.GreenToRed:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.YellowLightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\tdefault:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.RedLightForwardLeftTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.RedToGreen:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.YellowLightForwardLeftTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tprivate void drawForwardRightLightTexture(RoadBaseAI.TrafficLightState state, Rect rect) {\n\t\t\tswitch (state) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.GreenLightForwardRightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.GreenToRed:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.YellowLightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\tdefault:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.RedLightForwardRightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.RedToGreen:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.YellowLightForwardRightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tprivate void drawLeftLightTexture(RoadBaseAI.TrafficLightState state, Rect rect) {\n\t\t\tswitch (state) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.GreenLightLeftTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.GreenToRed:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.YellowLightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\tdefault:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.RedLightLeftTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.RedToGreen:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.YellowLightLeftTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tprivate void drawRightLightTexture(RoadBaseAI.TrafficLightState state, Rect rect) {\n\t\t\tswitch (state) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.GreenLightRightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.GreenToRed:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.YellowLightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\tdefault:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.RedLightRightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.RedToGreen:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.YellowLightRightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tprivate void drawMainLightTexture(RoadBaseAI.TrafficLightState state, Rect rect) {\n\t\t\tswitch (state) {\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.GreenLightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.GreenToRed:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.YellowLightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\tdefault:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.RedLightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t\tcase RoadBaseAI.TrafficLightState.RedToGreen:\n\t\t\t\t\tGUI.DrawTexture(rect, TextureResources.YellowRedLightTexture2D);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tpublic override void ShowGUIOverlay(ToolMode toolMode, bool viewOnly) {\n\t\t\tif (! ToolMode.TimedLightsShowLights.Equals(toolMode) &&\n\t\t\t\t! ToolMode.TimedLightsSelectNode.Equals(toolMode) &&\n\t\t\t\t! ToolMode.TimedLightsAddNode.Equals(toolMode) &&\n\t\t\t\t! ToolMode.TimedLightsRemoveNode.Equals(toolMode) &&\n\t\t\t\t! ToolMode.TimedLightsCopyLights.Equals(toolMode)) {\n\t\t\t\t// TODO refactor timed light related tool modes to sub tool modes\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (viewOnly && !Options.timedLightsOverlay)\n\t\t\t\treturn;\n\n\t\t\tVector3 camPos = Singleton<SimulationManager>.instance.m_simulationView.m_position;\n\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\n\t\t\tforeach (ushort nodeId in currentTimedNodeIds) {\n\t\t\t\tif (!Constants.ServiceFactory.NetService.IsNodeValid((ushort)nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (SelectedNodeIds.Contains((ushort)nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (tlsMan.HasTimedSimulation((ushort)nodeId)) {\n\t\t\t\t\tITimedTrafficLights timedNode = tlsMan.TrafficLightSimulations[nodeId].TimedLight;\n\n\t\t\t\t\tvar nodePos = Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_position;\n\n\t\t\t\t\tTexture2D tex = timedNode.IsStarted() ? (timedNode.IsInTestMode() ? TextureResources.ClockTestTexture2D : TextureResources.ClockPlayTexture2D) : TextureResources.ClockPauseTexture2D;\n\t\t\t\t\tMainTool.DrawGenericSquareOverlayTexture(tex, camPos, nodePos, 120f, false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void ShowGUI() {\n\t\t\tTrafficLightSimulationManager tlsMan = TrafficLightSimulationManager.Instance;\n\t\t\tCustomSegmentLightsManager customTrafficLightsManager = CustomSegmentLightsManager.Instance;\n\t\t\tJunctionRestrictionsManager junctionRestrictionsManager = JunctionRestrictionsManager.Instance;\n\n\t\t\tvar hoveredSegment = false;\n\n\t\t\tforeach (var nodeId in SelectedNodeIds) {\n\t\t\t\tif (!tlsMan.HasTimedSimulation(nodeId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tITimedTrafficLights timedNode = tlsMan.TrafficLightSimulations[nodeId].TimedLight;\n\n\t\t\t\tvar nodePos = Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_position;\n\n\t\t\t\tVector3 nodeScreenPos;\n\t\t\t\tbool nodeVisible = MainTool.WorldToScreenPoint(nodePos, out nodeScreenPos);\n\n\t\t\t\tif (!nodeVisible)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tvar diff = nodePos - Camera.main.transform.position;\n\t\t\t\tvar zoom = 1.0f / diff.magnitude * 100f * MainTool.GetBaseZoom();\n\n\t\t\t\tNodeGeometry nodeGeometry = NodeGeometry.Get(nodeId);\n\t\t\t\tforeach (SegmentEndGeometry end in nodeGeometry.SegmentEndGeometries) {\n\t\t\t\t\tif (end == null)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tushort srcSegmentId = end.SegmentId; // source segment\n\n\t\t\t\t\tICustomSegmentLights liveSegmentLights = customTrafficLightsManager.GetSegmentLights(srcSegmentId, end.StartNode, false);\n\t\t\t\t\tif (liveSegmentLights == null)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tbool showPedLight = liveSegmentLights.PedestrianLightState != null && junctionRestrictionsManager.IsPedestrianCrossingAllowed(liveSegmentLights.SegmentId, liveSegmentLights.StartNode);\n\n\t\t\t\t\tvar timedActive = timedNode.IsStarted();\n\t\t\t\t\tif (! timedActive) {\n\t\t\t\t\t\tliveSegmentLights.MakeRedOrGreen();\n\t\t\t\t\t}\n\n\t\t\t\t\tvar offset = 17f;\n\t\t\t\t\tVector3 segmentLightPos = nodePos;\n\n\t\t\t\t\tif (Singleton<NetManager>.instance.m_segments.m_buffer[srcSegmentId].m_startNode == nodeId) {\n\t\t\t\t\t\tsegmentLightPos.x += Singleton<NetManager>.instance.m_segments.m_buffer[srcSegmentId].m_startDirection.x * offset;\n\t\t\t\t\t\tsegmentLightPos.y += Singleton<NetManager>.instance.m_segments.m_buffer[srcSegmentId].m_startDirection.y;\n\t\t\t\t\t\tsegmentLightPos.z += Singleton<NetManager>.instance.m_segments.m_buffer[srcSegmentId].m_startDirection.z * offset;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsegmentLightPos.x += Singleton<NetManager>.instance.m_segments.m_buffer[srcSegmentId].m_endDirection.x * offset;\n\t\t\t\t\t\tsegmentLightPos.y += Singleton<NetManager>.instance.m_segments.m_buffer[srcSegmentId].m_endDirection.y;\n\t\t\t\t\t\tsegmentLightPos.z += Singleton<NetManager>.instance.m_segments.m_buffer[srcSegmentId].m_endDirection.z * offset;\n\t\t\t\t\t}\n\n\t\t\t\t\tVector3 screenPos;\n\t\t\t\t\tbool segmentLightVisible = MainTool.WorldToScreenPoint(segmentLightPos, out screenPos);\n\n\t\t\t\t\tif (!segmentLightVisible)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tvar guiColor = GUI.color;\n\n\t\t\t\t\tvar manualPedestrianWidth = 36f * zoom;\n\t\t\t\t\tvar manualPedestrianHeight = 35f * zoom;\n\n\t\t\t\t\tvar pedestrianWidth = 36f * zoom;\n\t\t\t\t\tvar pedestrianHeight = 61f * zoom;\n\n\t\t\t\t\t// original / 2.5\n\t\t\t\t\tvar lightWidth = 41f * zoom;\n\t\t\t\t\tvar lightHeight = 97f * zoom;\n\n\t\t\t\t\t// SWITCH MODE BUTTON\n\t\t\t\t\tvar modeWidth = 41f * zoom;\n\t\t\t\t\tvar modeHeight = 38f * zoom;\n\n\t\t\t\t\tif (showPedLight) {\n\t\t\t\t\t\t// pedestrian light\n\n\t\t\t\t\t\t// SWITCH MANUAL PEDESTRIAN LIGHT BUTTON\n\t\t\t\t\t\tif (!timedActive && (_timedPanelAdd || _timedEditStep >= 0)) {\n\t\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == srcSegmentId &&\n\t\t\t\t\t\t\t\t\t\t (_hoveredButton[1] == 1 || _hoveredButton[1] == 2) &&\n\t\t\t\t\t\t\t\t\t\t _hoveredNode == nodeId);\n\t\t\t\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\t\t\t\tvar myRect2 = new Rect(screenPos.x - manualPedestrianWidth / 2 - (_timedPanelAdd || _timedEditStep >= 0 ? lightWidth : 0) + 5f * zoom,\n\t\t\t\t\t\t\t\tscreenPos.y - manualPedestrianHeight / 2 - 9f * zoom, manualPedestrianWidth,\n\t\t\t\t\t\t\t\tmanualPedestrianHeight);\n\n\t\t\t\t\t\t\tGUI.DrawTexture(myRect2, liveSegmentLights.ManualPedestrianMode ? TextureResources.PedestrianModeManualTexture2D : TextureResources.PedestrianModeAutomaticTexture2D);\n\n\t\t\t\t\t\t\tif (myRect2.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t_hoveredButton[1] = 1;\n\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\thoveredSegment = true;\n\n\t\t\t\t\t\t\t\tif (MainTool.CheckClicked()) {\n\t\t\t\t\t\t\t\t\tliveSegmentLights.ManualPedestrianMode = !liveSegmentLights.ManualPedestrianMode;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// SWITCH PEDESTRIAN LIGHT\n\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == srcSegmentId && _hoveredButton[1] == 2 && _hoveredNode == nodeId);\n\n\t\t\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\t\t\tvar myRect3 = new Rect(screenPos.x - pedestrianWidth / 2 - (_timedPanelAdd || _timedEditStep >= 0 ? lightWidth : 0) + 5f * zoom, screenPos.y - pedestrianHeight / 2 + 22f * zoom, pedestrianWidth, pedestrianHeight);\n\n\t\t\t\t\t\tswitch (liveSegmentLights.PedestrianLightState) {\n\t\t\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Green:\n\t\t\t\t\t\t\t\tGUI.DrawTexture(myRect3, TextureResources.PedestrianGreenLightTexture2D);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase RoadBaseAI.TrafficLightState.Red:\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tGUI.DrawTexture(myRect3, TextureResources.PedestrianRedLightTexture2D);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (myRect3.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t_hoveredButton[1] = 2;\n\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\thoveredSegment = true;\n\n\t\t\t\t\t\t\tif (MainTool.CheckClicked() && !timedActive && (_timedPanelAdd || _timedEditStep >= 0)) {\n\t\t\t\t\t\t\t\tif (!liveSegmentLights.ManualPedestrianMode) {\n\t\t\t\t\t\t\t\t\tliveSegmentLights.ManualPedestrianMode = true;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tliveSegmentLights.ChangeLightPedestrian();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tint lightOffset = -1;\n\t\t\t\t\tforeach (ExtVehicleType vehicleType in liveSegmentLights.VehicleTypes) {\n\t\t\t\t\t\tHashSet<byte> laneIndices = new HashSet<byte>();\n\t\t\t\t\t\tfor (byte laneIndex = 0; laneIndex < liveSegmentLights.VehicleTypeByLaneIndex.Length; ++laneIndex) {\n\t\t\t\t\t\t\tif (liveSegmentLights.VehicleTypeByLaneIndex[laneIndex] == vehicleType) {\n\t\t\t\t\t\t\t\tlaneIndices.Add(laneIndex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//Log._Debug($\"Traffic light @ seg. {srcSegmentId} node {nodeId}. Lane indices for vehicleType {vehicleType}: {string.Join(\",\", laneIndices.Select(x => x.ToString()).ToArray())}\");\n\n\t\t\t\t\t\t++lightOffset;\n\t\t\t\t\t\tICustomSegmentLight liveSegmentLight = liveSegmentLights.GetCustomLight(vehicleType);\n\n\t\t\t\t\t\tVector3 offsetScreenPos = screenPos;\n\t\t\t\t\t\toffsetScreenPos.y -= (lightHeight + 10f * zoom) * lightOffset;\n\n\t\t\t\t\t\tif (!timedActive && (_timedPanelAdd || _timedEditStep >= 0)) {\n\t\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == srcSegmentId && _hoveredButton[1] == -1 &&\n\t\t\t\t\t\t\t\t\t\t _hoveredNode == nodeId);\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\t\t\t\tvar myRect1 = new Rect(offsetScreenPos.x - modeWidth / 2,\n\t\t\t\t\t\t\t\toffsetScreenPos.y - modeHeight / 2 + modeHeight - 7f * zoom, modeWidth, modeHeight);\n\n\t\t\t\t\t\t\tGUI.DrawTexture(myRect1, TextureResources.LightModeTexture2D);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (myRect1.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t_hoveredButton[1] = -1;\n\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\thoveredSegment = true;\n\n\t\t\t\t\t\t\t\tif (MainTool.CheckClicked()) {\n\t\t\t\t\t\t\t\t\tliveSegmentLight.ToggleMode();\n\t\t\t\t\t\t\t\t\ttimedNode.ChangeLightMode(srcSegmentId, vehicleType, liveSegmentLight.CurrentMode);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (vehicleType != ExtVehicleType.None) {\n\t\t\t\t\t\t\t// Info sign\n\t\t\t\t\t\t\tvar infoWidth = 56.125f * zoom;\n\t\t\t\t\t\t\tvar infoHeight = 51.375f * zoom;\n\n\t\t\t\t\t\t\tint numInfos = 0;\n\t\t\t\t\t\t\tfor (int k = 0; k < TrafficManagerTool.InfoSignsToDisplay.Length; ++k) {\n\t\t\t\t\t\t\t\tif ((TrafficManagerTool.InfoSignsToDisplay[k] & vehicleType) == ExtVehicleType.None)\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\tvar infoRect = new Rect(offsetScreenPos.x + modeWidth / 2f + 7f * zoom * (float)(numInfos + 1) + infoWidth * (float)numInfos, offsetScreenPos.y - infoHeight / 2f, infoWidth, infoHeight);\n\t\t\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(false);\n\t\t\t\t\t\t\t\tGUI.DrawTexture(infoRect, TextureResources.VehicleInfoSignTextures[TrafficManagerTool.InfoSignsToDisplay[k]]);\n\t\t\t\t\t\t\t\t++numInfos;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Draw light index\n\t\t\t\t\t\t/*if (!timedActive && _timedEditStep < 0 && lightOffset == 0) {\n\t\t\t\t\t\t\tvar indexSize = 20f * zoom;\n\t\t\t\t\t\t\tvar yOffset = indexSize + 77f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t//var carNumRect = new Rect(offsetScreenPos.x, offsetScreenPos.y - yOffset, counterSize, counterSize);\n\t\t\t\t\t\t\tvar segIndexRect = new Rect(offsetScreenPos.x, offsetScreenPos.y - yOffset - indexSize - 2f, indexSize, indexSize);\n\n\t\t\t\t\t\t\t_counterStyle.fontSize = (int)(15f * zoom);\n\t\t\t\t\t\t\t_counterStyle.normal.textColor = new Color(0f, 0f, 1f);\n\t\t\t\t\t\t\tGUI.Label(segIndexRect, $\"#{liveSegmentLight.ClockwiseIndex+1}\", _counterStyle);\n\t\t\t\t\t\t}*/\n\n#if DEBUG\n\t\t\t\t\t\tif (timedActive /*&& _timedShowNumbers*/) {\n\t\t\t\t\t\t\t//var prioSeg = TrafficPriorityManager.Instance.GetPrioritySegment(nodeId, srcSegmentId);\n\n\t\t\t\t\t\t\tvar counterSize = 20f * zoom;\n\t\t\t\t\t\t\tvar yOffset = counterSize + 77f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t//var carNumRect = new Rect(offsetScreenPos.x, offsetScreenPos.y - yOffset, counterSize, counterSize);\n\t\t\t\t\t\t\tvar segIdRect = new Rect(offsetScreenPos.x, offsetScreenPos.y - yOffset - counterSize - 2f, counterSize, counterSize);\n\n\t\t\t\t\t\t\t_counterStyle.fontSize = (int)(15f * zoom);\n\t\t\t\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 0f, 0f);\n\n\t\t\t\t\t\t\t/*String labelStr = \"n/a\";\n\t\t\t\t\t\t\tif (prioSeg != null) {\n\t\t\t\t\t\t\t\tlabelStr = prioSeg.GetRegisteredVehicleCount(laneIndices).ToString() + \" \" + Translation.GetString(\"incoming\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tGUI.Label(carNumRect, labelStr, _counterStyle);*/\n\n\t\t\t\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 0f, 0f);\n\t\t\t\t\t\t\tGUI.Label(segIdRect, Translation.GetString(\"Segment\") + \" \" + srcSegmentId, _counterStyle);\n\t\t\t\t\t\t}\n#endif\n\n\t\t\t\t\t\tif (lightOffset == 0 && showPedLight) {\n\t\t\t\t\t\t\t// PEDESTRIAN COUNTER\n\t\t\t\t\t\t\tif (timedActive && _timedShowNumbers) {\n\t\t\t\t\t\t\t\tvar counterSize = 20f * zoom;\n\n\t\t\t\t\t\t\t\tvar counter = timedNode.CheckNextChange(srcSegmentId, end.StartNode, vehicleType, 3);\n\n\t\t\t\t\t\t\t\tfloat numOffset;\n\n\t\t\t\t\t\t\t\tif (liveSegmentLights.PedestrianLightState == RoadBaseAI.TrafficLightState.Red) { // TODO check this\n\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 53f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 29f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tvar myRectCounterNum =\n\t\t\t\t\t\t\t\t\tnew Rect(offsetScreenPos.x - counterSize + 15f * zoom + (counter >= 10 ? (counter >= 100 ? -10 * zoom : -5 * zoom) : 1f) + 24f * zoom - pedestrianWidth / 2,\n\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - numOffset, counterSize, counterSize);\n\n\t\t\t\t\t\t\t\t_counterStyle.fontSize = (int)(15f * zoom);\n\t\t\t\t\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 1f);\n\n\t\t\t\t\t\t\t\tGUI.Label(myRectCounterNum, counter.ToString(), _counterStyle);\n\n\t\t\t\t\t\t\t\tif (myRectCounterNum.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 2;\n\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tSegmentGeometry geometry = SegmentGeometry.Get(srcSegmentId);\n\t\t\t\t\t\tif (geometry == null) {\n\t\t\t\t\t\t\tLog.Error($\"TimedTrafficLightsTool.ShowGUI: No geometry information available for segment {srcSegmentId}\");\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbool startNode = geometry.StartNodeId() == nodeId;\n\t\t\t\t\t\tif (geometry.IsOutgoingOneWay(startNode))\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\tvar hasOutgoingLeftSegment = geometry.HasOutgoingLeftSegment(startNode);\n\t\t\t\t\t\tvar hasOutgoingForwardSegment = geometry.HasOutgoingStraightSegment(startNode);\n\t\t\t\t\t\tvar hasOutgoingRightSegment = geometry.HasOutgoingRightSegment(startNode);\n\n\t\t\t\t\t\t/*var hasLeftSegment = geometry.HasLeftSegment(startNode);\n\t\t\t\t\t\tvar hasForwardSegment = geometry.HasStraightSegment(startNode);\n\t\t\t\t\t\tvar hasRightSegment = geometry.HasRightSegment(startNode);*/\n\n\t\t\t\t\t\tbool hasOtherLight = false;\n\t\t\t\t\t\tswitch (liveSegmentLight.CurrentMode) {\n\t\t\t\t\t\t\tcase LightMode.Simple: {\n\t\t\t\t\t\t\t\t\t// no arrow light\n\t\t\t\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == srcSegmentId && _hoveredButton[1] == 3 && _hoveredNode == nodeId);\n\n\t\t\t\t\t\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\t\t\t\t\t\tvar myRect4 =\n\t\t\t\t\t\t\t\t\t\tnew Rect(offsetScreenPos.x - lightWidth / 2 - (_timedPanelAdd || _timedEditStep >= 0 ? lightWidth : 0) - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\t\t\t\t\t\t\tdrawMainLightTexture(liveSegmentLight.LightMain, myRect4);\n\n\t\t\t\t\t\t\t\t\tif (myRect4.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 3;\n\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\n\t\t\t\t\t\t\t\t\t\tif (MainTool.CheckClicked() && !timedActive && (_timedPanelAdd || _timedEditStep >= 0)) {\n\t\t\t\t\t\t\t\t\t\t\tliveSegmentLight.ChangeMainLight();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// COUNTER\n\t\t\t\t\t\t\t\t\tif (timedActive && _timedShowNumbers) {\n\t\t\t\t\t\t\t\t\t\tvar counterSize = 20f * zoom;\n\n\t\t\t\t\t\t\t\t\t\tvar counter = timedNode.CheckNextChange(srcSegmentId, end.StartNode, vehicleType, 0);\n\n\t\t\t\t\t\t\t\t\t\tfloat numOffset;\n\n\t\t\t\t\t\t\t\t\t\tif (liveSegmentLight.LightMain == RoadBaseAI.TrafficLightState.Red) {\n\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 96f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 40f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tvar myRectCounterNum =\n\t\t\t\t\t\t\t\t\t\t\tnew Rect(offsetScreenPos.x - counterSize + 15f * zoom + (counter >= 10 ? (counter >= 100 ? -10 * zoom : -5 * zoom) : 0f) - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - numOffset, counterSize, counterSize);\n\n\t\t\t\t\t\t\t\t\t\t_counterStyle.fontSize = (int)(18f * zoom);\n\t\t\t\t\t\t\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 1f);\n\n\t\t\t\t\t\t\t\t\t\tGUI.Label(myRectCounterNum, counter.ToString(), _counterStyle);\n\n\t\t\t\t\t\t\t\t\t\tif (myRectCounterNum.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 3;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tGUI.color = guiColor;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase LightMode.SingleLeft:\n\t\t\t\t\t\t\t\tif (hasOutgoingLeftSegment) {\n\t\t\t\t\t\t\t\t\t// left arrow light\n\t\t\t\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == srcSegmentId && _hoveredButton[1] == 3 && _hoveredNode == nodeId);\n\n\t\t\t\t\t\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\t\t\t\t\t\tvar myRect4 =\n\t\t\t\t\t\t\t\t\t\tnew Rect(offsetScreenPos.x - lightWidth / 2 - (_timedPanelAdd || _timedEditStep >= 0 ? lightWidth * 2 : lightWidth) - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\t\t\t\t\t\t\tdrawLeftLightTexture(liveSegmentLight.LightLeft, myRect4);\n\n\t\t\t\t\t\t\t\t\tif (myRect4.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 3;\n\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\n\t\t\t\t\t\t\t\t\t\tif (MainTool.CheckClicked() && !timedActive && (_timedPanelAdd || _timedEditStep >= 0)) {\n\t\t\t\t\t\t\t\t\t\t\tliveSegmentLight.ChangeLeftLight();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// COUNTER\n\t\t\t\t\t\t\t\t\tif (timedActive && _timedShowNumbers) {\n\t\t\t\t\t\t\t\t\t\tvar counterSize = 20f * zoom;\n\n\t\t\t\t\t\t\t\t\t\tvar counter = timedNode.CheckNextChange(srcSegmentId, end.StartNode, vehicleType, 1);\n\n\t\t\t\t\t\t\t\t\t\tfloat numOffset;\n\n\t\t\t\t\t\t\t\t\t\tif (liveSegmentLight.LightLeft == RoadBaseAI.TrafficLightState.Red) {\n\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 96f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 40f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tvar myRectCounterNum =\n\t\t\t\t\t\t\t\t\t\t\tnew Rect(offsetScreenPos.x - counterSize + 15f * zoom + (counter >= 10 ? (counter >= 100 ? -10 * zoom : -5 * zoom) : 0f) - pedestrianWidth + 5f * zoom - (_timedPanelAdd || _timedEditStep >= 0 ? lightWidth * 2 : lightWidth),\n\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - numOffset, counterSize, counterSize);\n\n\t\t\t\t\t\t\t\t\t\t_counterStyle.fontSize = (int)(18f * zoom);\n\t\t\t\t\t\t\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 1f);\n\n\t\t\t\t\t\t\t\t\t\tGUI.Label(myRectCounterNum, counter.ToString(), _counterStyle);\n\n\t\t\t\t\t\t\t\t\t\tif (myRectCounterNum.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 3;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// forward-right arrow light\n\t\t\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == srcSegmentId && _hoveredButton[1] == 4 && _hoveredNode == nodeId);\n\n\t\t\t\t\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\t\t\t\t\tvar myRect5 =\n\t\t\t\t\t\t\t\t\tnew Rect(offsetScreenPos.x - lightWidth / 2 - pedestrianWidth - (_timedPanelAdd || _timedEditStep >= 0 ? lightWidth : 0f) + 5f * zoom,\n\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\t\t\t\t\t\tif (hasOutgoingForwardSegment && hasOutgoingRightSegment) {\n\t\t\t\t\t\t\t\t\tdrawForwardRightLightTexture(liveSegmentLight.LightMain, myRect5);\n\t\t\t\t\t\t\t\t\thasOtherLight = true;\n\t\t\t\t\t\t\t\t} else if (hasOutgoingForwardSegment) {\n\t\t\t\t\t\t\t\t\tdrawStraightLightTexture(liveSegmentLight.LightMain, myRect5);\n\t\t\t\t\t\t\t\t\thasOtherLight = true;\n\t\t\t\t\t\t\t\t} else if (hasOutgoingRightSegment) {\n\t\t\t\t\t\t\t\t\tdrawRightLightTexture(liveSegmentLight.LightMain, myRect5);\n\t\t\t\t\t\t\t\t\thasOtherLight = true;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (hasOtherLight && myRect5.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 4;\n\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\n\t\t\t\t\t\t\t\t\tif (MainTool.CheckClicked() && !timedActive && (_timedPanelAdd || _timedEditStep >= 0)) {\n\t\t\t\t\t\t\t\t\t\tliveSegmentLight.ChangeMainLight();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// COUNTER\n\t\t\t\t\t\t\t\tif (timedActive && _timedShowNumbers) {\n\t\t\t\t\t\t\t\t\tvar counterSize = 20f * zoom;\n\t\t\t\t\t\t\t\t\tvar counter = timedNode.CheckNextChange(srcSegmentId, end.StartNode, vehicleType, 0);\n\t\t\t\t\n\t\t\t\t\t\t\t\t\tfloat numOffset;\n\t\t\t\t\t\t\t\t\tif (liveSegmentLight.LightMain == RoadBaseAI.TrafficLightState.Red) {\n\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 96f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 40f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tvar myRectCounterNum =\n\t\t\t\t\t\t\t\t\t\tnew Rect(offsetScreenPos.x - counterSize + 15f * zoom + (counter >= 10 ? (counter >= 100 ? -10 * zoom : -5 * zoom) : 0f) - pedestrianWidth + 5f * zoom - (_timedPanelAdd || _timedEditStep >= 0 ? lightWidth : 0f),\n\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - numOffset, counterSize, counterSize);\n\n\t\t\t\t\t\t\t\t\t_counterStyle.fontSize = (int)(18f * zoom);\n\t\t\t\t\t\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 1f);\n\n\t\t\t\t\t\t\t\t\tGUI.Label(myRectCounterNum, counter.ToString(), _counterStyle);\n\n\t\t\t\t\t\t\t\t\tif (myRectCounterNum.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 4;\n\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase LightMode.SingleRight: {\n\t\t\t\t\t\t\t\t\t// forward-left light\n\t\t\t\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == srcSegmentId && _hoveredButton[1] == 3 && _hoveredNode == nodeId);\n\n\t\t\t\t\t\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\t\t\t\t\t\tvar myRect4 = new Rect(offsetScreenPos.x - lightWidth / 2 - (_timedPanelAdd || _timedEditStep >= 0 ? lightWidth * 2 : lightWidth) - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\t\t\t\t\t\t\tvar lightType = 0;\n\n\t\t\t\t\t\t\t\t\thasOtherLight = false;\n\t\t\t\t\t\t\t\t\tif (hasOutgoingForwardSegment && hasOutgoingLeftSegment) {\n\t\t\t\t\t\t\t\t\t\thasOtherLight = true;\n\t\t\t\t\t\t\t\t\t\tdrawForwardLeftLightTexture(liveSegmentLight.LightMain, myRect4);\n\t\t\t\t\t\t\t\t\t\tlightType = 1;\n\t\t\t\t\t\t\t\t\t} else if (hasOutgoingForwardSegment) {\n\t\t\t\t\t\t\t\t\t\thasOtherLight = true;\n\t\t\t\t\t\t\t\t\t\tif (!hasOutgoingRightSegment) {\n\t\t\t\t\t\t\t\t\t\t\tmyRect4 = new Rect(offsetScreenPos.x - lightWidth / 2 - (_timedPanelAdd || _timedEditStep >= 0 ? lightWidth : 0f) - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tdrawStraightLightTexture(liveSegmentLight.LightMain, myRect4);\n\t\t\t\t\t\t\t\t\t} else if (hasOutgoingLeftSegment) {\n\t\t\t\t\t\t\t\t\t\thasOtherLight = true;\n\t\t\t\t\t\t\t\t\t\tif (!hasOutgoingRightSegment) {\n\t\t\t\t\t\t\t\t\t\t\tmyRect4 = new Rect(offsetScreenPos.x - lightWidth / 2 - (_timedPanelAdd || _timedEditStep >= 0 ? lightWidth : 0f) - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tdrawLeftLightTexture(liveSegmentLight.LightMain, myRect4);\n\t\t\t\t\t\t\t\t\t}\n\n\n\t\t\t\t\t\t\t\t\tif (hasOtherLight && myRect4.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 3;\n\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\n\t\t\t\t\t\t\t\t\t\tif (MainTool.CheckClicked() && !timedActive && (_timedPanelAdd || _timedEditStep >= 0)) {\n\t\t\t\t\t\t\t\t\t\t\tliveSegmentLight.ChangeMainLight();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// COUNTER\n\t\t\t\t\t\t\t\t\tif (timedActive && _timedShowNumbers) {\n\t\t\t\t\t\t\t\t\t\tvar counterSize = 20f * zoom;\n\n\t\t\t\t\t\t\t\t\t\tvar counter = timedNode.CheckNextChange(srcSegmentId, end.StartNode, vehicleType, lightType);\n\n\t\t\t\t\t\t\t\t\t\tfloat numOffset;\n\n\t\t\t\t\t\t\t\t\t\tif (liveSegmentLight.LightMain == RoadBaseAI.TrafficLightState.Red) {\n\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 96f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 40f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tvar myRectCounterNum =\n\t\t\t\t\t\t\t\t\t\t\tnew Rect(offsetScreenPos.x - counterSize + 15f * zoom + (counter >= 10 ? (counter >= 100 ? -10 * zoom : -5 * zoom) : 0f) - pedestrianWidth + 5f * zoom - (_timedPanelAdd || _timedEditStep >= 0 ? (hasOutgoingRightSegment ? lightWidth * 2 : lightWidth) : (hasOutgoingRightSegment ? lightWidth : 0f)),\n\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - numOffset, counterSize, counterSize);\n\n\t\t\t\t\t\t\t\t\t\t_counterStyle.fontSize = (int)(18f * zoom);\n\t\t\t\t\t\t\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 1f);\n\n\t\t\t\t\t\t\t\t\t\tGUI.Label(myRectCounterNum, counter.ToString(), _counterStyle);\n\n\t\t\t\t\t\t\t\t\t\tif (myRectCounterNum.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 3;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// right arrow light\n\t\t\t\t\t\t\t\t\tif (hasOutgoingRightSegment) {\n\t\t\t\t\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == srcSegmentId && _hoveredButton[1] == 4 &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t _hoveredNode == nodeId);\n\n\t\t\t\t\t\t\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\t\t\t\t\t\t\tvar rect5 =\n\t\t\t\t\t\t\t\t\t\t\tnew Rect(offsetScreenPos.x - lightWidth / 2 - (_timedPanelAdd || _timedEditStep >= 0 ? lightWidth : 0f) - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\t\t\t\t\t\t\t\tdrawRightLightTexture(liveSegmentLight.LightRight, rect5);\n\n\t\t\t\t\t\t\t\t\t\tif (rect5.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 4;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\n\t\t\t\t\t\t\t\t\t\t\tif (MainTool.CheckClicked() && !timedActive &&\n\t\t\t\t\t\t\t\t\t\t\t\t(_timedPanelAdd || _timedEditStep >= 0)) {\n\t\t\t\t\t\t\t\t\t\t\t\tliveSegmentLight.ChangeRightLight();\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// COUNTER\n\t\t\t\t\t\t\t\t\t\tif (timedActive && _timedShowNumbers) {\n\t\t\t\t\t\t\t\t\t\t\tvar counterSize = 20f * zoom;\n\n\t\t\t\t\t\t\t\t\t\t\tvar counter = timedNode.CheckNextChange(srcSegmentId, end.StartNode, vehicleType, 2);\n\n\t\t\t\t\t\t\t\t\t\t\tfloat numOffset;\n\n\t\t\t\t\t\t\t\t\t\t\tif (liveSegmentLight.LightRight == RoadBaseAI.TrafficLightState.Red) {\n\t\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 96f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 40f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\tvar myRectCounterNum =\n\t\t\t\t\t\t\t\t\t\t\t\tnew Rect(\n\t\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.x - counterSize + 15f * zoom + (counter >= 10 ? (counter >= 100 ? -10 * zoom : -5 * zoom) : 0f) -\n\t\t\t\t\t\t\t\t\t\t\t\t\tpedestrianWidth + 5f * zoom -\n\t\t\t\t\t\t\t\t\t\t\t\t\t(_timedPanelAdd || _timedEditStep >= 0 ? lightWidth : 0f),\n\t\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - numOffset, counterSize, counterSize);\n\n\t\t\t\t\t\t\t\t\t\t\t_counterStyle.fontSize = (int)(18f * zoom);\n\t\t\t\t\t\t\t\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 1f);\n\n\t\t\t\t\t\t\t\t\t\t\tGUI.Label(myRectCounterNum, counter.ToString(), _counterStyle);\n\n\t\t\t\t\t\t\t\t\t\t\tif (myRectCounterNum.Contains(Event.current.mousePosition) &&\n\t\t\t\t\t\t\t\t\t\t\t\t!IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 4;\n\t\t\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t// left arrow light\n\t\t\t\t\t\t\t\tif (hasOutgoingLeftSegment) {\n\t\t\t\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == srcSegmentId && _hoveredButton[1] == 3 && _hoveredNode == nodeId);\n\n\t\t\t\t\t\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\t\t\t\t\t\tvar offsetLight = lightWidth;\n\n\t\t\t\t\t\t\t\t\tif (hasOutgoingRightSegment)\n\t\t\t\t\t\t\t\t\t\toffsetLight += lightWidth;\n\n\t\t\t\t\t\t\t\t\tif (hasOutgoingForwardSegment)\n\t\t\t\t\t\t\t\t\t\toffsetLight += lightWidth;\n\n\t\t\t\t\t\t\t\t\tvar myRect4 =\n\t\t\t\t\t\t\t\t\t\tnew Rect(offsetScreenPos.x - lightWidth / 2 - (_timedPanelAdd || _timedEditStep >= 0 ? offsetLight : offsetLight - lightWidth) - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\t\t\t\t\t\t\tdrawLeftLightTexture(liveSegmentLight.LightLeft, myRect4);\n\n\t\t\t\t\t\t\t\t\tif (myRect4.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 3;\n\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\n\t\t\t\t\t\t\t\t\t\tif (MainTool.CheckClicked() && !timedActive && (_timedPanelAdd || _timedEditStep >= 0)) {\n\t\t\t\t\t\t\t\t\t\t\tliveSegmentLight.ChangeLeftLight();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// COUNTER\n\t\t\t\t\t\t\t\t\tif (timedActive && _timedShowNumbers) {\n\t\t\t\t\t\t\t\t\t\tvar counterSize = 20f * zoom;\n\n\t\t\t\t\t\t\t\t\t\tvar counter = timedNode.CheckNextChange(srcSegmentId, end.StartNode, vehicleType, 1);\n\n\t\t\t\t\t\t\t\t\t\tfloat numOffset;\n\n\t\t\t\t\t\t\t\t\t\tif (liveSegmentLight.LightLeft == RoadBaseAI.TrafficLightState.Red) {\n\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 96f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 40f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tvar myRectCounterNum =\n\t\t\t\t\t\t\t\t\t\t\tnew Rect(\n\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.x - counterSize + 15f * zoom + (counter >= 10 ? (counter >= 100 ? -10 * zoom : -5 * zoom) : 0f) -\n\t\t\t\t\t\t\t\t\t\t\t\tpedestrianWidth + 5f * zoom -\n\t\t\t\t\t\t\t\t\t\t\t\t(_timedPanelAdd || _timedEditStep >= 0 ? offsetLight : offsetLight - lightWidth),\n\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - numOffset, counterSize, counterSize);\n\n\t\t\t\t\t\t\t\t\t\t_counterStyle.fontSize = (int)(18f * zoom);\n\t\t\t\t\t\t\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 1f);\n\n\t\t\t\t\t\t\t\t\t\tGUI.Label(myRectCounterNum, counter.ToString(), _counterStyle);\n\n\t\t\t\t\t\t\t\t\t\tif (myRectCounterNum.Contains(Event.current.mousePosition) &&\n\t\t\t\t\t\t\t\t\t\t\t!IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 3;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// forward arrow light\n\t\t\t\t\t\t\t\tif (hasOutgoingForwardSegment) {\n\t\t\t\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == srcSegmentId && _hoveredButton[1] == 4 && _hoveredNode == nodeId);\n\n\t\t\t\t\t\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\t\t\t\t\t\tvar offsetLight = lightWidth;\n\n\t\t\t\t\t\t\t\t\tif (hasOutgoingRightSegment)\n\t\t\t\t\t\t\t\t\t\toffsetLight += lightWidth;\n\n\t\t\t\t\t\t\t\t\tvar myRect6 =\n\t\t\t\t\t\t\t\t\t\tnew Rect(offsetScreenPos.x - lightWidth / 2 - (_timedPanelAdd || _timedEditStep >= 0 ? offsetLight : offsetLight - lightWidth) - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\t\t\t\t\t\t\tdrawStraightLightTexture(liveSegmentLight.LightMain, myRect6);\n\n\t\t\t\t\t\t\t\t\tif (myRect6.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 4;\n\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\n\t\t\t\t\t\t\t\t\t\tif (MainTool.CheckClicked() && !timedActive && (_timedPanelAdd || _timedEditStep >= 0)) {\n\t\t\t\t\t\t\t\t\t\t\tliveSegmentLight.ChangeMainLight();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// COUNTER\n\t\t\t\t\t\t\t\t\tif (timedActive && _timedShowNumbers) {\n\t\t\t\t\t\t\t\t\t\tvar counterSize = 20f * zoom;\n\n\t\t\t\t\t\t\t\t\t\tvar counter = timedNode.CheckNextChange(srcSegmentId, end.StartNode, vehicleType, 0);\n\n\t\t\t\t\t\t\t\t\t\tfloat numOffset;\n\n\t\t\t\t\t\t\t\t\t\tif (liveSegmentLight.LightMain == RoadBaseAI.TrafficLightState.Red) {\n\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 96f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 40f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tvar myRectCounterNum =\n\t\t\t\t\t\t\t\t\t\t\tnew Rect(\n\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.x - counterSize + 15f * zoom + (counter >= 10 ? (counter >= 100 ? -10 * zoom : -5 * zoom) : 0f) -\n\t\t\t\t\t\t\t\t\t\t\t\tpedestrianWidth + 5f * zoom -\n\t\t\t\t\t\t\t\t\t\t\t\t(_timedPanelAdd || _timedEditStep >= 0 ? offsetLight : offsetLight - lightWidth),\n\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - numOffset, counterSize, counterSize);\n\n\t\t\t\t\t\t\t\t\t\t_counterStyle.fontSize = (int)(18f * zoom);\n\t\t\t\t\t\t\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 1f);\n\n\t\t\t\t\t\t\t\t\t\tGUI.Label(myRectCounterNum, counter.ToString(), _counterStyle);\n\n\t\t\t\t\t\t\t\t\t\tif (myRectCounterNum.Contains(Event.current.mousePosition) &&\n\t\t\t\t\t\t\t\t\t\t\t!IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 4;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// right arrow light\n\t\t\t\t\t\t\t\tif (hasOutgoingRightSegment) {\n\t\t\t\t\t\t\t\t\tguiColor.a = MainTool.GetHandleAlpha(_hoveredButton[0] == srcSegmentId && _hoveredButton[1] == 5 && _hoveredNode == nodeId);\n\n\t\t\t\t\t\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\t\t\t\t\t\tvar rect6 =\n\t\t\t\t\t\t\t\t\t\tnew Rect(offsetScreenPos.x - lightWidth / 2 - (_timedPanelAdd || _timedEditStep >= 0 ? lightWidth : 0f) - pedestrianWidth + 5f * zoom,\n\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - lightHeight / 2, lightWidth, lightHeight);\n\n\t\t\t\t\t\t\t\t\tdrawRightLightTexture(liveSegmentLight.LightRight, rect6);\n\n\t\t\t\t\t\t\t\t\tif (rect6.Contains(Event.current.mousePosition) && !IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 5;\n\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\n\t\t\t\t\t\t\t\t\t\tif (MainTool.CheckClicked() && !timedActive && (_timedPanelAdd || _timedEditStep >= 0)) {\n\t\t\t\t\t\t\t\t\t\t\tliveSegmentLight.ChangeRightLight();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// COUNTER\n\t\t\t\t\t\t\t\t\tif (timedActive && _timedShowNumbers) {\n\t\t\t\t\t\t\t\t\t\tvar counterSize = 20f * zoom;\n\n\t\t\t\t\t\t\t\t\t\tvar counter = timedNode.CheckNextChange(srcSegmentId, end.StartNode, vehicleType, 2);\n\n\t\t\t\t\t\t\t\t\t\tfloat numOffset;\n\n\t\t\t\t\t\t\t\t\t\tif (liveSegmentLight.LightRight == RoadBaseAI.TrafficLightState.Red) {\n\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 96f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tnumOffset = counterSize + 40f * zoom - modeHeight * 2;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tvar myRectCounterNum =\n\t\t\t\t\t\t\t\t\t\t\tnew Rect(\n\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.x - counterSize + 15f * zoom + (counter >= 10 ? (counter >= 100 ? -10 * zoom : -5 * zoom) : 0f) -\n\t\t\t\t\t\t\t\t\t\t\t\tpedestrianWidth + 5f * zoom -\n\t\t\t\t\t\t\t\t\t\t\t\t(_timedPanelAdd || _timedEditStep >= 0 ? lightWidth : 0f),\n\t\t\t\t\t\t\t\t\t\t\t\toffsetScreenPos.y - numOffset, counterSize, counterSize);\n\n\t\t\t\t\t\t\t\t\t\t_counterStyle.fontSize = (int)(18f * zoom);\n\t\t\t\t\t\t\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 1f);\n\n\t\t\t\t\t\t\t\t\t\tGUI.Label(myRectCounterNum, counter.ToString(), _counterStyle);\n\n\t\t\t\t\t\t\t\t\t\tif (myRectCounterNum.Contains(Event.current.mousePosition) &&\n\t\t\t\t\t\t\t\t\t\t\t!IsCursorInPanel()) {\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[0] = srcSegmentId;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredButton[1] = 5;\n\t\t\t\t\t\t\t\t\t\t\t_hoveredNode = nodeId;\n\t\t\t\t\t\t\t\t\t\t\thoveredSegment = true;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} // end switch liveSegmentLight.CurrentMode\n\t\t\t\t\t} // end foreach light\n\t\t\t\t} // end foreach segment\n\t\t\t} // end foreach node\n\n\t\t\tif (!hoveredSegment) {\n\t\t\t\t_hoveredButton[0] = 0;\n\t\t\t\t_hoveredButton[1] = 0;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/SubTools/ToggleTrafficLightsTool.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.State;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing UnityEngine;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\n\nnamespace TrafficManager.UI.SubTools {\n\tpublic class ToggleTrafficLightsTool : SubTool {\n\t\tpublic ToggleTrafficLightsTool(TrafficManagerTool mainTool) : base(mainTool) {\n\t\t\t\n\t\t}\n\n\t\tpublic override void OnPrimaryClickOverlay() {\n\t\t\tif (IsCursorInPanel())\n\t\t\t\treturn;\n\t\t\tif (HoveredNodeId == 0)\n\t\t\t\treturn;\n\t\t\t\n\t\t\tConstants.ServiceFactory.NetService.ProcessNode(HoveredNodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\tToggleTrafficLight(HoveredNodeId, ref node);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\tpublic void ToggleTrafficLight(ushort nodeId, ref NetNode node, bool showMessageOnError=true) {\n\t\t\tUnableReason reason;\n\t\t\tif (!TrafficLightManager.Instance.IsTrafficLightToggleable(nodeId, !TrafficLightManager.Instance.HasTrafficLight(nodeId, ref node), ref node, out reason)) {\n\t\t\t\tif (showMessageOnError) {\n\t\t\t\t\tswitch (reason) {\n\t\t\t\t\t\tcase UnableReason.HasTimedLight:\n\t\t\t\t\t\t\tMainTool.ShowTooltip(Translation.GetString(\"NODE_IS_TIMED_LIGHT\"));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase UnableReason.IsLevelCrossing:\n\t\t\t\t\t\t\tMainTool.ShowTooltip(Translation.GetString(\"Node_is_level_crossing\"));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tTrafficPriorityManager.Instance.RemovePrioritySignsFromNode(nodeId);\n\t\t\tTrafficLightManager.Instance.ToggleTrafficLight(nodeId, ref node);\n\t\t}\n\n\t\tpublic override void OnToolGUI(Event e) {\n\t\t\t\n\t\t}\n\n\t\tpublic override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\tif (MainTool.GetToolController().IsInsideUI || !Cursor.visible) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (HoveredNodeId == 0) return;\n\n\t\t\tif (!Flags.mayHaveTrafficLight(HoveredNodeId)) return;\n\n\t\t\tMainTool.DrawNodeCircle(cameraInfo, HoveredNodeId, Input.GetMouseButton(0), false);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Math;\nusing ColossalFramework.UI;\nusing GenericGameBridge.Service;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.State;\nusing TrafficManager.Traffic;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.Util;\nusing UnityEngine;\nusing static TrafficManager.UI.TrafficManagerTool;\nusing static TrafficManager.Util.SegmentLaneTraverser;\n\nnamespace TrafficManager.UI.SubTools {\n\tpublic class VehicleRestrictionsTool : SubTool {\n\t\tprivate static ExtVehicleType[] roadVehicleTypes = new ExtVehicleType[] { ExtVehicleType.PassengerCar, ExtVehicleType.Bus, ExtVehicleType.Taxi, ExtVehicleType.CargoTruck, ExtVehicleType.Service, ExtVehicleType.Emergency };\n\t\tprivate static ExtVehicleType[] railVehicleTypes = new ExtVehicleType[] { ExtVehicleType.PassengerTrain, ExtVehicleType.CargoTrain };\n\t\tprivate readonly float vehicleRestrictionsSignSize = 80f;\n\t\tprivate bool _cursorInSecondaryPanel;\n\t\tprivate bool overlayHandleHovered;\n\t\tprivate Rect windowRect = TrafficManagerTool.MoveGUI(new Rect(0, 0, 620, 100));\n\t\tprivate HashSet<ushort> currentRestrictedSegmentIds;\n\n\t\tpublic VehicleRestrictionsTool(TrafficManagerTool mainTool) : base(mainTool) {\n\t\t\tcurrentRestrictedSegmentIds = new HashSet<ushort>();\n\t\t}\n\n\t\tpublic override void OnActivate() {\n\t\t\t_cursorInSecondaryPanel = false;\n\t\t\tRefreshCurrentRestrictedSegmentIds();\n\t\t}\n\n\t\tprivate void RefreshCurrentRestrictedSegmentIds(ushort forceSegmentId=0) {\n\t\t\tif (forceSegmentId == 0) {\n\t\t\t\tcurrentRestrictedSegmentIds.Clear();\n\t\t\t} else {\n\t\t\t\tcurrentRestrictedSegmentIds.Remove(forceSegmentId);\n\t\t\t}\n\n\t\t\tfor (uint segmentId = (forceSegmentId == 0 ? 1u : forceSegmentId); segmentId <= (forceSegmentId == 0 ? NetManager.MAX_SEGMENT_COUNT - 1 : forceSegmentId); ++segmentId) {\n\t\t\t\tif (!Constants.ServiceFactory.NetService.IsSegmentValid((ushort)segmentId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (VehicleRestrictionsManager.Instance.HasSegmentRestrictions((ushort)segmentId))\n\t\t\t\t\tcurrentRestrictedSegmentIds.Add((ushort)segmentId);\n\t\t\t}\n\t\t}\n\n\t\tpublic override void Cleanup() {\n\t\t\t\n\t\t}\n\n\t\tpublic override void Initialize() {\n\t\t\tbase.Initialize();\n\t\t\tCleanup();\n\t\t\tif (Options.vehicleRestrictionsOverlay) {\n\t\t\t\tRefreshCurrentRestrictedSegmentIds();\n\t\t\t} else {\n\t\t\t\tcurrentRestrictedSegmentIds.Clear();\n\t\t\t}\n\t\t}\n\n\t\tpublic override bool IsCursorInPanel() {\n\t\t\treturn base.IsCursorInPanel() || _cursorInSecondaryPanel;\n\t\t}\n\n\t\tpublic override void OnPrimaryClickOverlay() {\n\t\t\t//Log._Debug($\"Restrictions: {HoveredSegmentId} {overlayHandleHovered}\");\n\t\t\tif (HoveredSegmentId == 0) return;\n\t\t\tif (overlayHandleHovered) return;\n\n\t\t\tSelectedSegmentId = HoveredSegmentId;\n\t\t\tcurrentRestrictedSegmentIds.Add(SelectedSegmentId);\n\t\t\tMainTool.CheckClicked(); // TODO do we need that?\n\t\t}\n\n\t\tpublic override void OnSecondaryClickOverlay() {\n\t\t\tif (!IsCursorInPanel()) {\n\t\t\t\tSelectedSegmentId = 0;\n\t\t\t}\n\t\t}\n\n\t\tpublic override void OnToolGUI(Event e) {\n\t\t\tbase.OnToolGUI(e);\n\n\t\t\tif (SelectedSegmentId != 0) {\n\t\t\t\t_cursorInSecondaryPanel = false;\n\n\t\t\t\twindowRect = GUILayout.Window(255, windowRect, _guiVehicleRestrictionsWindow, Translation.GetString(\"Vehicle_restrictions\"), WindowStyle);\n\t\t\t\t_cursorInSecondaryPanel = windowRect.Contains(Event.current.mousePosition);\n\n\t\t\t\t//overlayHandleHovered = false;\n\t\t\t}\n\t\t\t//ShowSigns(false);\n\t\t}\n\n\t\tpublic override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\t//Log._Debug($\"Restrictions overlay {_cursorInSecondaryPanel} {HoveredNodeId} {SelectedNodeId} {HoveredSegmentId} {SelectedSegmentId}\");\n\n\t\t\tif (SelectedSegmentId != 0)\n\t\t\t\tNetTool.RenderOverlay(cameraInfo, ref Singleton<NetManager>.instance.m_segments.m_buffer[SelectedSegmentId], MainTool.GetToolColor(true, false), MainTool.GetToolColor(true, false));\n\n\t\t\tif (_cursorInSecondaryPanel)\n\t\t\t\treturn;\n\n\t\t\tif (HoveredSegmentId != 0 && HoveredSegmentId != SelectedSegmentId && !overlayHandleHovered) {\n\t\t\t\tNetTool.RenderOverlay(cameraInfo, ref Singleton<NetManager>.instance.m_segments.m_buffer[HoveredSegmentId], MainTool.GetToolColor(false, false), MainTool.GetToolColor(false, false));\n\t\t\t}\n\t\t}\n\n\t\tpublic override void ShowGUIOverlay(ToolMode toolMode, bool viewOnly) {\n\t\t\tif (viewOnly && !Options.vehicleRestrictionsOverlay)\n\t\t\t\treturn;\n\n\t\t\tShowSigns(viewOnly);\n\t\t}\n\n\t\tprivate void ShowSigns(bool viewOnly) {\n\t\t\tVector3 camPos = Camera.main.transform.position;\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tushort updatedSegmentId = 0;\n\t\t\tbool handleHovered = false;\n\t\t\tforeach (ushort segmentId in currentRestrictedSegmentIds) {\n\t\t\t\tif (!Constants.ServiceFactory.NetService.IsSegmentValid(segmentId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tvar segmentInfo = netManager.m_segments.m_buffer[segmentId].Info;\n\n\t\t\t\tVector3 centerPos = netManager.m_segments.m_buffer[segmentId].m_bounds.center;\n\t\t\t\tVector3 screenPos;\n\t\t\t\tbool visible = MainTool.WorldToScreenPoint(centerPos, out screenPos);\n\n\t\t\t\tif (!visible)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif ((netManager.m_segments.m_buffer[segmentId].m_bounds.center - camPos).magnitude > TrafficManagerTool.MaxOverlayDistance)\n\t\t\t\t\tcontinue; // do not draw if too distant\n\n\t\t\t\t// draw vehicle restrictions\n\t\t\t\tbool update;\n\t\t\t\tif (drawVehicleRestrictionHandles(segmentId, ref netManager.m_segments.m_buffer[segmentId], viewOnly || segmentId != SelectedSegmentId, out update))\n\t\t\t\t\thandleHovered = true;\n\n\t\t\t\tif (update) {\n\t\t\t\t\tupdatedSegmentId = segmentId;\n\t\t\t\t}\n\t\t\t}\n\t\t\toverlayHandleHovered = handleHovered;\n\n\t\t\tif (updatedSegmentId != 0) {\n\t\t\t\tRefreshCurrentRestrictedSegmentIds(updatedSegmentId);\n\t\t\t}\n\t\t}\n\n\t\tprivate void _guiVehicleRestrictionsWindow(int num) {\n\t\t\tif (GUILayout.Button(Translation.GetString(\"Invert\"))) {\n\t\t\t\t// invert pattern\n\n\t\t\t\tNetInfo selectedSegmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[SelectedSegmentId].Info;\n\t\t\t\tIList<LanePos> sortedLanes = Constants.ServiceFactory.NetService.GetSortedLanes(SelectedSegmentId, ref Singleton<NetManager>.instance.m_segments.m_buffer[SelectedSegmentId], null, VehicleRestrictionsManager.LANE_TYPES, VehicleRestrictionsManager.VEHICLE_TYPES); // TODO does not need to be sorted, but every lane should be a vehicle lane\n\t\t\t\tforeach (LanePos laneData in sortedLanes) {\n\t\t\t\t\tuint laneId = laneData.laneId;\n\t\t\t\t\tbyte laneIndex = laneData.laneIndex;\n\t\t\t\t\tNetInfo.Lane laneInfo = selectedSegmentInfo.m_lanes[laneIndex];\n\n\t\t\t\t\tExtVehicleType baseMask = VehicleRestrictionsManager.Instance.GetBaseMask(laneInfo, VehicleRestrictionsMode.Configured);\n\n\t\t\t\t\tif (baseMask == ExtVehicleType.None)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tExtVehicleType allowedTypes = VehicleRestrictionsManager.Instance.GetAllowedVehicleTypes(SelectedSegmentId, selectedSegmentInfo, laneIndex, laneInfo, VehicleRestrictionsMode.Configured);\n\t\t\t\t\tallowedTypes = ~(allowedTypes & VehicleRestrictionsManager.EXT_VEHICLE_TYPES) & baseMask;\n\t\t\t\t\tVehicleRestrictionsManager.Instance.SetAllowedVehicleTypes(SelectedSegmentId, selectedSegmentInfo, laneIndex, laneInfo, laneId, allowedTypes);\n\t\t\t\t}\n\t\t\t\tRefreshCurrentRestrictedSegmentIds(SelectedSegmentId);\n\t\t\t}\n\n\t\t\tGUILayout.BeginHorizontal();\n\t\t\tif (GUILayout.Button(Translation.GetString(\"Allow_all_vehicles\"))) {\n\t\t\t\t// allow all vehicle types\n\n\t\t\t\tNetInfo selectedSegmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[SelectedSegmentId].Info;\n\t\t\t\tIList<LanePos> sortedLanes = Constants.ServiceFactory.NetService.GetSortedLanes(SelectedSegmentId, ref Singleton<NetManager>.instance.m_segments.m_buffer[SelectedSegmentId], null, VehicleRestrictionsManager.LANE_TYPES, VehicleRestrictionsManager.VEHICLE_TYPES); // TODO does not need to be sorted, but every lane should be a vehicle lane\n\t\t\t\tforeach (LanePos laneData in sortedLanes) {\n\t\t\t\t\tuint laneId = laneData.laneId;\n\t\t\t\t\tbyte laneIndex = laneData.laneIndex;\n\t\t\t\t\tNetInfo.Lane laneInfo = selectedSegmentInfo.m_lanes[laneIndex];\n\n\t\t\t\t\tExtVehicleType baseMask = VehicleRestrictionsManager.Instance.GetBaseMask(laneInfo, VehicleRestrictionsMode.Configured);\n\n\t\t\t\t\tif (baseMask == ExtVehicleType.None)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tVehicleRestrictionsManager.Instance.SetAllowedVehicleTypes(SelectedSegmentId, selectedSegmentInfo, laneIndex, laneInfo, laneId, baseMask);\n\t\t\t\t}\n\t\t\t\tRefreshCurrentRestrictedSegmentIds(SelectedSegmentId);\n\t\t\t}\n\n\t\t\tif (GUILayout.Button(Translation.GetString(\"Ban_all_vehicles\"))) {\n\t\t\t\t// ban all vehicle types\n\n\t\t\t\tNetInfo selectedSegmentInfo = Singleton<NetManager>.instance.m_segments.m_buffer[SelectedSegmentId].Info;\n\t\t\t\tIList<LanePos> sortedLanes = Constants.ServiceFactory.NetService.GetSortedLanes(SelectedSegmentId, ref Singleton<NetManager>.instance.m_segments.m_buffer[SelectedSegmentId], null, VehicleRestrictionsManager.LANE_TYPES, VehicleRestrictionsManager.VEHICLE_TYPES); // TODO does not need to be sorted, but every lane should be a vehicle lane\n\t\t\t\tforeach (LanePos laneData in sortedLanes) {\n\t\t\t\t\tuint laneId = laneData.laneId;\n\t\t\t\t\tbyte laneIndex = laneData.laneIndex;\n\t\t\t\t\tNetInfo.Lane laneInfo = selectedSegmentInfo.m_lanes[laneIndex];\n\n\t\t\t\t\tExtVehicleType baseMask = VehicleRestrictionsManager.Instance.GetBaseMask(laneInfo, VehicleRestrictionsMode.Configured);\n\n\t\t\t\t\tif (baseMask == ExtVehicleType.None)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tVehicleRestrictionsManager.Instance.SetAllowedVehicleTypes(SelectedSegmentId, selectedSegmentInfo, laneIndex, laneInfo, laneId, ~VehicleRestrictionsManager.EXT_VEHICLE_TYPES & baseMask);\n\t\t\t\t}\n\t\t\t\tRefreshCurrentRestrictedSegmentIds(SelectedSegmentId);\n\t\t\t}\n\t\t\tGUILayout.EndHorizontal();\n\n\t\t\tif (GUILayout.Button(Translation.GetString(\"Apply_vehicle_restrictions_to_all_road_segments_between_two_junctions\"))) {\n\t\t\t\tApplyRestrictionsToAllSegments();\n\t\t\t}\n\n\t\t\tDragWindow(ref windowRect);\n\t\t}\n\n\t\tprivate void ApplyRestrictionsToAllSegments(int? sortedLaneIndex=null) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tNetInfo selectedSegmentInfo = netManager.m_segments.m_buffer[SelectedSegmentId].Info;\n\t\t\tushort selectedStartNodeId = netManager.m_segments.m_buffer[SelectedSegmentId].m_startNode;\n\t\t\tushort selectedEndNodeId = netManager.m_segments.m_buffer[SelectedSegmentId].m_endNode;\n\n\t\t\tSegmentLaneTraverser.Traverse(SelectedSegmentId, SegmentTraverser.TraverseDirection.AnyDirection, SegmentTraverser.TraverseSide.AnySide, SegmentLaneTraverser.LaneStopCriterion.LaneCount, SegmentTraverser.SegmentStopCriterion.Junction, VehicleRestrictionsManager.LANE_TYPES, VehicleRestrictionsManager.VEHICLE_TYPES, delegate (SegmentLaneVisitData data) {\n\t\t\t\tif (data.segVisitData.initial) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tif (sortedLaneIndex != null && data.sortedLaneIndex != sortedLaneIndex) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tushort segmentId = data.segVisitData.curGeo.SegmentId;\n\t\t\t\tNetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info;\n\n\t\t\t\tuint selectedLaneId = data.initLanePos.laneId;\n\t\t\t\tbyte selectedLaneIndex = data.initLanePos.laneIndex;\n\t\t\t\tNetInfo.Lane selectedLaneInfo = selectedSegmentInfo.m_lanes[selectedLaneIndex];\n\n\t\t\t\tuint laneId = data.curLanePos.laneId;\n\t\t\t\tbyte laneIndex = data.curLanePos.laneIndex;\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\n\t\t\t\tExtVehicleType baseMask = VehicleRestrictionsManager.Instance.GetBaseMask(laneInfo, VehicleRestrictionsMode.Configured);\n\t\t\t\tif (baseMask == ExtVehicleType.None) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// apply restrictions of selected segment & lane\n\t\t\t\tExtVehicleType mask = ~VehicleRestrictionsManager.EXT_VEHICLE_TYPES & baseMask; // ban all possible controllable vehicles\n\t\t\t\tmask |= VehicleRestrictionsManager.EXT_VEHICLE_TYPES & VehicleRestrictionsManager.Instance.GetAllowedVehicleTypes(SelectedSegmentId, selectedSegmentInfo, selectedLaneIndex, selectedLaneInfo, VehicleRestrictionsMode.Configured); // allow all enabled and controllable vehicles\n\n\t\t\t\tVehicleRestrictionsManager.Instance.SetAllowedVehicleTypes(segmentId, segmentInfo, laneIndex, laneInfo, laneId, mask);\n\n\t\t\t\tRefreshCurrentRestrictedSegmentIds(segmentId);\n\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\tprivate bool drawVehicleRestrictionHandles(ushort segmentId, ref NetSegment segment, bool viewOnly, out bool stateUpdated) {\n\t\t\tstateUpdated = false;\n\n\t\t\tif (viewOnly && !Options.vehicleRestrictionsOverlay && MainTool.GetToolMode() != ToolMode.VehicleRestrictions)\n\t\t\t\treturn false;\n\n\t\t\tVector3 center = segment.m_bounds.center;\n\n\t\t\tVector3 screenPos;\n\t\t\tbool visible = MainTool.WorldToScreenPoint(center, out screenPos);\n\n\t\t\tif (!visible)\n\t\t\t\treturn false;\n\n\t\t\tvar camPos = Singleton<SimulationManager>.instance.m_simulationView.m_position;\n\t\t\tvar diff = center - camPos;\n\n\t\t\tif (diff.magnitude > TrafficManagerTool.MaxOverlayDistance)\n\t\t\t\treturn false; // do not draw if too distant\n\n\t\t\tint numDirections;\n\t\t\tint numLanes = TrafficManagerTool.GetSegmentNumVehicleLanes(segmentId, null, out numDirections, VehicleRestrictionsManager.VEHICLE_TYPES);\n\n\t\t\t// draw vehicle restrictions over each lane\n\t\t\tNetInfo segmentInfo = segment.Info;\n\t\t\tVector3 yu = (segment.m_endDirection - segment.m_startDirection).normalized;\n\t\t\t/*if ((segment.m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None)\n\t\t\t\tyu = -yu;*/\n\t\t\tVector3 xu = Vector3.Cross(yu, new Vector3(0, 1f, 0)).normalized;\n\t\t\tfloat f = viewOnly ? 4f : 7f; // reserved sign size in game coordinates\n\t\t\tint maxNumSigns = 0;\n\t\t\tif (VehicleRestrictionsManager.Instance.IsRoadSegment(segmentInfo))\n\t\t\t\tmaxNumSigns = roadVehicleTypes.Length;\n\t\t\telse if (VehicleRestrictionsManager.Instance.IsRailSegment(segmentInfo))\n\t\t\t\tmaxNumSigns = railVehicleTypes.Length;\n\t\t\t//Vector3 zero = center - 0.5f * (float)(numLanes + numDirections - 1) * f * (xu + yu); // \"bottom left\"\n\t\t\tVector3 zero = center - 0.5f * (float)(numLanes - 1 + numDirections - 1) * f * xu - 0.5f * (float)maxNumSigns * f * yu; // \"bottom left\"\n\n\t\t\t/*if (!viewOnly)\n\t\t\t\tLog._Debug($\"xu: {xu.ToString()} yu: {yu.ToString()} center: {center.ToString()} zero: {zero.ToString()} numLanes: {numLanes} numDirections: {numDirections}\");*/\n\n\t\t\tuint x = 0;\n\t\t\tvar guiColor = GUI.color;\n\t\t\tIList<LanePos> sortedLanes = Constants.ServiceFactory.NetService.GetSortedLanes(segmentId, ref segment, null, VehicleRestrictionsManager.LANE_TYPES, VehicleRestrictionsManager.VEHICLE_TYPES);\n\t\t\tbool hovered = false;\n\t\t\tHashSet<NetInfo.Direction> directions = new HashSet<NetInfo.Direction>();\n\t\t\tint sortedLaneIndex = -1;\n\t\t\tforeach (LanePos laneData in sortedLanes) {\n\t\t\t\t++sortedLaneIndex;\n\t\t\t\tuint laneId = laneData.laneId;\n\t\t\t\tbyte laneIndex = laneData.laneIndex;\n\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\tif (!directions.Contains(laneInfo.m_finalDirection)) {\n\t\t\t\t\tif (directions.Count > 0)\n\t\t\t\t\t\t++x; // space between different directions\n\t\t\t\t\tdirections.Add(laneInfo.m_finalDirection);\n\t\t\t\t}\n\n\t\t\t\tExtVehicleType[] possibleVehicleTypes = null;\n\t\t\t\tif (VehicleRestrictionsManager.Instance.IsRoadLane(laneInfo)) {\n\t\t\t\t\tpossibleVehicleTypes = roadVehicleTypes;\n\t\t\t\t} else if (VehicleRestrictionsManager.Instance.IsRailLane(laneInfo)) {\n\t\t\t\t\tpossibleVehicleTypes = railVehicleTypes;\n\t\t\t\t} else {\n\t\t\t\t\t++x;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tExtVehicleType allowedTypes = VehicleRestrictionsManager.Instance.GetAllowedVehicleTypes(segmentId, segmentInfo, laneIndex, laneInfo, VehicleRestrictionsMode.Configured);\n\n\t\t\t\tuint y = 0;\n#if DEBUGx\n\t\t\t\tVector3 labelCenter = zero + f * (float)x * xu + f * (float)y * yu; // in game coordinates\n\n\t\t\t\tVector3 labelScreenPos;\n\t\t\t\tbool visible = MainTool.WorldToScreenPoint(labelCenter, out labelScreenPos);\n\t\t\t\tlabelScreenPos.y = Screen.height - labelScreenPos.y;\n\t\t\t\tdiff = labelCenter - camPos;\n\n\t\t\t\tvar labelZoom = 1.0f / diff.magnitude * 100f;\n\t\t\t\t_counterStyle.fontSize = (int)(11f * labelZoom);\n\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 0f);\n\n\t\t\t\tstring labelStr = $\"Idx {laneIndex}\";\n\t\t\t\tVector2 dim = _counterStyle.CalcSize(new GUIContent(labelStr));\n\t\t\t\tRect labelRect = new Rect(labelScreenPos.x - dim.x / 2f, labelScreenPos.y, dim.x, dim.y);\n\t\t\t\tGUI.Label(labelRect, labelStr, _counterStyle);\n\n\t\t\t\t++y;\n#endif\n\t\t\t\tforeach (ExtVehicleType vehicleType in possibleVehicleTypes) {\n\t\t\t\t\tbool allowed = VehicleRestrictionsManager.Instance.IsAllowed(allowedTypes, vehicleType);\n\t\t\t\t\tif (allowed && viewOnly)\n\t\t\t\t\t\tcontinue; // do not draw allowed vehicles in view-only mode\n\n\t\t\t\t\tbool hoveredHandle = MainTool.DrawGenericSquareOverlayGridTexture(TextureResources.VehicleRestrictionTextures[vehicleType][allowed], camPos, zero, f, xu, yu, x, y, vehicleRestrictionsSignSize, !viewOnly);\n\t\t\t\t\tif (hoveredHandle)\n\t\t\t\t\t\thovered = true;\n\n\t\t\t\t\tif (hoveredHandle && MainTool.CheckClicked()) {\n\t\t\t\t\t\t// toggle vehicle restrictions\n\t\t\t\t\t\t//Log._Debug($\"Setting vehicle restrictions of segment {segmentId}, lane idx {laneIndex}, {vehicleType.ToString()} to {!allowed}\");\n\t\t\t\t\t\tVehicleRestrictionsManager.Instance.ToggleAllowedType(segmentId, segmentInfo, laneIndex, laneId, laneInfo, vehicleType, !allowed);\n\t\t\t\t\t\tstateUpdated = true;\n\n\t\t\t\t\t\tif (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {\n\t\t\t\t\t\t\tApplyRestrictionsToAllSegments(sortedLaneIndex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t++y;\n\t\t\t\t}\n\n\t\t\t\t++x;\n\t\t\t}\n\n\t\t\tguiColor.a = 1f;\n\t\t\tGUI.color = guiColor;\n\n\t\t\treturn hovered;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/TextureResources.cs",
    "content": "using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Reflection;\nusing TrafficManager.Geometry;\nusing TrafficManager.Manager;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Traffic;\nusing TrafficManager.UI;\nusing TrafficManager.Util;\nusing UnityEngine;\nusing static TrafficManager.Traffic.Data.PrioritySegment;\n\nnamespace TrafficManager.UI\n{\n    public class TextureResources\n    {\n        public static readonly Texture2D RedLightTexture2D;\n        public static readonly Texture2D YellowRedLightTexture2D;\n        public static readonly Texture2D YellowLightTexture2D;\n        public static readonly Texture2D GreenLightTexture2D;\n        public static readonly Texture2D RedLightStraightTexture2D;\n        public static readonly Texture2D YellowLightStraightTexture2D;\n        public static readonly Texture2D GreenLightStraightTexture2D;\n        public static readonly Texture2D RedLightRightTexture2D;\n        public static readonly Texture2D YellowLightRightTexture2D;\n        public static readonly Texture2D GreenLightRightTexture2D;\n        public static readonly Texture2D RedLightLeftTexture2D;\n        public static readonly Texture2D YellowLightLeftTexture2D;\n        public static readonly Texture2D GreenLightLeftTexture2D;\n        public static readonly Texture2D RedLightForwardRightTexture2D;\n        public static readonly Texture2D YellowLightForwardRightTexture2D;\n        public static readonly Texture2D GreenLightForwardRightTexture2D;\n        public static readonly Texture2D RedLightForwardLeftTexture2D;\n        public static readonly Texture2D YellowLightForwardLeftTexture2D;\n        public static readonly Texture2D GreenLightForwardLeftTexture2D;\n        public static readonly Texture2D PedestrianRedLightTexture2D;\n        public static readonly Texture2D PedestrianGreenLightTexture2D;\n        public static readonly Texture2D LightModeTexture2D;\n        public static readonly Texture2D LightCounterTexture2D;\n        public static readonly Texture2D PedestrianModeAutomaticTexture2D;\n        public static readonly Texture2D PedestrianModeManualTexture2D;\n\t\tpublic static readonly IDictionary<PriorityType, Texture2D> PrioritySignTextures;\n\t\tpublic static readonly Texture2D SignRemoveTexture2D;\n\t\tpublic static readonly Texture2D ClockPlayTexture2D;\n\t\tpublic static readonly Texture2D ClockPauseTexture2D;\n\t\tpublic static readonly Texture2D ClockTestTexture2D;\n\t\tpublic static readonly IDictionary<ushort, Texture2D> SpeedLimitTextures;\n\t\tpublic static readonly IDictionary<ExtVehicleType, IDictionary<bool, Texture2D>> VehicleRestrictionTextures;\n\t\tpublic static readonly IDictionary<ExtVehicleType, Texture2D> VehicleInfoSignTextures;\n\t\tpublic static readonly IDictionary<bool, Texture2D> ParkingRestrictionTextures;\n\t\tpublic static readonly Texture2D LaneChangeForbiddenTexture2D;\n\t\tpublic static readonly Texture2D LaneChangeAllowedTexture2D;\n\t\tpublic static readonly Texture2D UturnAllowedTexture2D;\n\t\tpublic static readonly Texture2D UturnForbiddenTexture2D;\n        public static readonly Texture2D RightOnRedForbiddenTexture2D;\n        public static readonly Texture2D RightOnRedAllowedTexture2D;\n        public static readonly Texture2D LeftOnRedForbiddenTexture2D;\n        public static readonly Texture2D LeftOnRedAllowedTexture2D;\n\t\tpublic static readonly Texture2D EnterBlockedJunctionAllowedTexture2D;\n\t\tpublic static readonly Texture2D EnterBlockedJunctionForbiddenTexture2D;\n\t\tpublic static readonly Texture2D PedestrianCrossingAllowedTexture2D;\n\t\tpublic static readonly Texture2D PedestrianCrossingForbiddenTexture2D;\n\t\tpublic static readonly Texture2D MainMenuButtonTexture2D;\n\t\tpublic static readonly Texture2D MainMenuButtonsTexture2D;\n\t\tpublic static readonly Texture2D NoImageTexture2D;\n\t\tpublic static readonly Texture2D RemoveButtonTexture2D;\n\t\tpublic static readonly Texture2D WindowBackgroundTexture2D;\n\n\t\tstatic TextureResources()\n        {\n\t\t\t// missing image\n\t\t\tNoImageTexture2D = LoadDllResource(\"noimage.png\", 64, 64);\n\n\t\t\t// main menu icon\n\t\t\tMainMenuButtonTexture2D = LoadDllResource(\"MenuButton.png\", 300, 50);\n\t\t\tMainMenuButtonTexture2D.name = \"TMPE_MainMenuButtonIcon\";\n\n\t\t\t// main menu buttons\n\t\t\tMainMenuButtonsTexture2D = LoadDllResource(\"mainmenu-btns.png\", 960, 30);\n\t\t\tMainMenuButtonsTexture2D.name = \"TMPE_MainMenuButtons\";\n\n\t\t\t// simple\n\t\t\tRedLightTexture2D = LoadDllResource(\"light_1_1.png\", 103, 243);\n            YellowRedLightTexture2D = LoadDllResource(\"light_1_2.png\", 103, 243);\n            GreenLightTexture2D = LoadDllResource(\"light_1_3.png\", 103, 243);\n            // forward\n            RedLightStraightTexture2D = LoadDllResource(\"light_2_1.png\", 103, 243);\n            YellowLightStraightTexture2D = LoadDllResource(\"light_2_2.png\", 103, 243);\n            GreenLightStraightTexture2D = LoadDllResource(\"light_2_3.png\", 103, 243);\n            // right\n            RedLightRightTexture2D = LoadDllResource(\"light_3_1.png\", 103, 243);\n            YellowLightRightTexture2D = LoadDllResource(\"light_3_2.png\", 103, 243);\n            GreenLightRightTexture2D = LoadDllResource(\"light_3_3.png\", 103, 243);\n            // left\n            RedLightLeftTexture2D = LoadDllResource(\"light_4_1.png\", 103, 243);\n            YellowLightLeftTexture2D = LoadDllResource(\"light_4_2.png\", 103, 243);\n            GreenLightLeftTexture2D = LoadDllResource(\"light_4_3.png\", 103, 243);\n            // forwardright\n            RedLightForwardRightTexture2D = LoadDllResource(\"light_5_1.png\", 103, 243);\n            YellowLightForwardRightTexture2D = LoadDllResource(\"light_5_2.png\", 103, 243);\n            GreenLightForwardRightTexture2D = LoadDllResource(\"light_5_3.png\", 103, 243);\n            // forwardleft\n            RedLightForwardLeftTexture2D = LoadDllResource(\"light_6_1.png\", 103, 243);\n            YellowLightForwardLeftTexture2D = LoadDllResource(\"light_6_2.png\", 103, 243);\n            GreenLightForwardLeftTexture2D = LoadDllResource(\"light_6_3.png\", 103, 243);\n            // yellow\n            YellowLightTexture2D = LoadDllResource(\"light_yellow.png\", 103, 243);\n            // pedestrian\n            PedestrianRedLightTexture2D = LoadDllResource(\"pedestrian_light_1.png\", 73, 123);\n            PedestrianGreenLightTexture2D = LoadDllResource(\"pedestrian_light_2.png\", 73, 123);\n            // light mode\n            LightModeTexture2D = LoadDllResource(Translation.GetTranslatedFileName(\"light_mode.png\"), 103, 95);\n            LightCounterTexture2D = LoadDllResource(Translation.GetTranslatedFileName(\"light_counter.png\"), 103, 95);\n            // pedestrian mode\n            PedestrianModeAutomaticTexture2D = LoadDllResource(\"pedestrian_mode_1.png\", 73, 70);\n            PedestrianModeManualTexture2D = LoadDllResource(\"pedestrian_mode_2.png\", 73, 73);\n\n\t\t\t// priority signs\n\t\t\tPrioritySignTextures = new TinyDictionary<PriorityType, Texture2D>();\n\t\t\tPrioritySignTextures[PriorityType.None] = LoadDllResource(\"sign_none.png\", 200, 200);\n\t\t\tPrioritySignTextures[PriorityType.Main] = LoadDllResource(\"sign_priority.png\", 200, 200);\n\t\t\tPrioritySignTextures[PriorityType.Stop] = LoadDllResource(\"sign_stop.png\", 200, 200);\n\t\t\tPrioritySignTextures[PriorityType.Yield] = LoadDllResource(\"sign_yield.png\", 200, 200);\n\t\t\t\n\t\t\t// delete priority sign\n\t\t\tSignRemoveTexture2D = LoadDllResource(\"remove_signs.png\", 256, 256);\n\n\t\t\t// timer\n\t\t\tClockPlayTexture2D = LoadDllResource(\"clock_play.png\", 512, 512);\n\t\t\tClockPauseTexture2D = LoadDllResource(\"clock_pause.png\", 512, 512);\n\t\t\tClockTestTexture2D = LoadDllResource(\"clock_test.png\", 512, 512);\n\n\t\t\tSpeedLimitTextures = new TinyDictionary<ushort, Texture2D>();\n\t\t\tforeach (ushort speedLimit in SpeedLimitManager.Instance.AvailableSpeedLimits) {\n\t\t\t\tSpeedLimitTextures.Add(speedLimit, LoadDllResource(speedLimit.ToString() + \".png\", 200, 200));\n\t\t\t}\n\n\t\t\tVehicleRestrictionTextures = new TinyDictionary<ExtVehicleType, IDictionary<bool, Texture2D>>();\n\t\t\tVehicleRestrictionTextures[ExtVehicleType.Bus] = new TinyDictionary<bool, Texture2D>();\n\t\t\tVehicleRestrictionTextures[ExtVehicleType.CargoTrain] = new TinyDictionary<bool, Texture2D>();\n\t\t\tVehicleRestrictionTextures[ExtVehicleType.CargoTruck] = new TinyDictionary<bool, Texture2D>();\n\t\t\tVehicleRestrictionTextures[ExtVehicleType.Emergency] = new TinyDictionary<bool, Texture2D>();\n\t\t\tVehicleRestrictionTextures[ExtVehicleType.PassengerCar] = new TinyDictionary<bool, Texture2D>();\n\t\t\tVehicleRestrictionTextures[ExtVehicleType.PassengerTrain] = new TinyDictionary<bool, Texture2D>();\n\t\t\tVehicleRestrictionTextures[ExtVehicleType.Service] = new TinyDictionary<bool, Texture2D>();\n\t\t\tVehicleRestrictionTextures[ExtVehicleType.Taxi] = new TinyDictionary<bool, Texture2D>();\n\n\t\t\tforeach (KeyValuePair<ExtVehicleType, IDictionary<bool, Texture2D>> e in VehicleRestrictionTextures) {\n\t\t\t\tforeach (bool b in new bool[]{false, true}) {\n\t\t\t\t\tstring suffix = b ? \"allowed\" : \"forbidden\";\n\t\t\t\t\te.Value[b] = LoadDllResource(e.Key.ToString().ToLower() + \"_\" + suffix + \".png\", 200, 200);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tParkingRestrictionTextures = new TinyDictionary<bool, Texture2D>();\n\t\t\tParkingRestrictionTextures[true] = LoadDllResource(\"parking_allowed.png\", 200, 200);\n\t\t\tParkingRestrictionTextures[false] = LoadDllResource(\"parking_disallowed.png\", 200, 200);\n\n\t\t\tLaneChangeAllowedTexture2D = LoadDllResource(\"lanechange_allowed.png\", 200, 200);\n\t\t\tLaneChangeForbiddenTexture2D = LoadDllResource(\"lanechange_forbidden.png\", 200, 200);\n\n\t\t\tUturnAllowedTexture2D = LoadDllResource(\"uturn_allowed.png\", 200, 200);\n\t\t\tUturnForbiddenTexture2D = LoadDllResource(\"uturn_forbidden.png\", 200, 200);\n\n            RightOnRedAllowedTexture2D = LoadDllResource(\"right_on_red_allowed.png\", 200, 200);\n            RightOnRedForbiddenTexture2D = LoadDllResource(\"right_on_red_forbidden.png\", 200, 200);\n            LeftOnRedAllowedTexture2D = LoadDllResource(\"left_on_red_allowed.png\", 200, 200);\n            LeftOnRedForbiddenTexture2D = LoadDllResource(\"left_on_red_forbidden.png\", 200, 200);\n\n            EnterBlockedJunctionAllowedTexture2D = LoadDllResource(\"enterblocked_allowed.png\", 200, 200);\n\t\t\tEnterBlockedJunctionForbiddenTexture2D = LoadDllResource(\"enterblocked_forbidden.png\", 200, 200);\n\n\t\t\tPedestrianCrossingAllowedTexture2D = LoadDllResource(\"crossing_allowed.png\", 200, 200);\n\t\t\tPedestrianCrossingForbiddenTexture2D = LoadDllResource(\"crossing_forbidden.png\", 200, 200);\n\n\t\t\tVehicleInfoSignTextures = new TinyDictionary<ExtVehicleType, Texture2D>();\n\t\t\tVehicleInfoSignTextures[ExtVehicleType.Bicycle] = LoadDllResource(\"bicycle_infosign.png\", 449, 411);\n\t\t\tVehicleInfoSignTextures[ExtVehicleType.Bus] = LoadDllResource(\"bus_infosign.png\", 449, 411);\n\t\t\tVehicleInfoSignTextures[ExtVehicleType.CargoTrain] = LoadDllResource(\"cargotrain_infosign.png\", 449, 411);\n\t\t\tVehicleInfoSignTextures[ExtVehicleType.CargoTruck] = LoadDllResource(\"cargotruck_infosign.png\", 449, 411);\n\t\t\tVehicleInfoSignTextures[ExtVehicleType.Emergency] = LoadDllResource(\"emergency_infosign.png\", 449, 411);\n\t\t\tVehicleInfoSignTextures[ExtVehicleType.PassengerCar] = LoadDllResource(\"passengercar_infosign.png\", 449, 411);\n\t\t\tVehicleInfoSignTextures[ExtVehicleType.PassengerTrain] = LoadDllResource(\"passengertrain_infosign.png\", 449, 411);\n\t\t\tVehicleInfoSignTextures[ExtVehicleType.RailVehicle] = VehicleInfoSignTextures[ExtVehicleType.PassengerTrain];\n\t\t\tVehicleInfoSignTextures[ExtVehicleType.Service] = LoadDllResource(\"service_infosign.png\", 449, 411);\n\t\t\tVehicleInfoSignTextures[ExtVehicleType.Taxi] = LoadDllResource(\"taxi_infosign.png\", 449, 411);\n\t\t\tVehicleInfoSignTextures[ExtVehicleType.Tram] = LoadDllResource(\"tram_infosign.png\", 449, 411);\n\n\t\t\tRemoveButtonTexture2D = LoadDllResource(\"remove-btn.png\", 150, 30);\n\n\t\t\tWindowBackgroundTexture2D = LoadDllResource(\"WindowBackground.png\", 16, 60);\n\t\t}\n\n        private static Texture2D LoadDllResource(string resourceName, int width, int height)\n        {\n#if DEBUG\n            bool debug = State.GlobalConfig.Instance.Debug.Switches[11];\n#endif\n            try {\n#if DEBUG\n                if (debug)\n                    Log._Debug($\"Loading DllResource {resourceName}\");\n#endif\n                var myAssembly = Assembly.GetExecutingAssembly();\n                var myStream = myAssembly.GetManifestResourceStream(\"TrafficManager.Resources.\" + resourceName);\n\n                var texture = new Texture2D(width, height, TextureFormat.ARGB32, false);\n\n                texture.LoadImage(ReadToEnd(myStream));\n\n                return texture;\n            } catch (Exception e) {\n                Log.Error(e.StackTrace.ToString());\n                return null;\n            }\n        }\n\n        static byte[] ReadToEnd(Stream stream)\n        {\n            var originalPosition = stream.Position;\n            stream.Position = 0;\n\n            try\n            {\n                var readBuffer = new byte[4096];\n\n                var totalBytesRead = 0;\n                int bytesRead;\n\n                while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)\n                {\n                    totalBytesRead += bytesRead;\n\n                    if (totalBytesRead != readBuffer.Length)\n                        continue;\n\n                    var nextByte = stream.ReadByte();\n                    if (nextByte == -1)\n                        continue;\n\n                    var temp = new byte[readBuffer.Length * 2];\n                    Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);\n                    Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);\n                    readBuffer = temp;\n                    totalBytesRead++;\n                }\n\n                var buffer = readBuffer;\n                if (readBuffer.Length == totalBytesRead)\n                    return buffer;\n\n                buffer = new byte[totalBytesRead];\n                Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);\n                return buffer;\n            }\n            catch (Exception e) {\n                Log.Error(e.StackTrace.ToString());\n                return null;\n            }\n            finally\n            {\n                stream.Position = originalPosition;\n            }\n        }\n\t}\n}"
  },
  {
    "path": "TLM/TLM/UI/ToolMode.cs",
    "content": "namespace TrafficManager.UI\n{\n    public enum ToolMode\n    {\n        None = 0,\n        SwitchTrafficLight = 1,\n        AddPrioritySigns = 2,\n        ManualSwitch = 3,\n        TimedLightsSelectNode = 4,\n        TimedLightsShowLights = 5,\n        LaneChange = 6,\n\t\tTimedLightsAddNode = 7,\n\t\tTimedLightsRemoveNode = 8,\n\t\tTimedLightsCopyLights = 9,\n\t\tSpeedLimits = 10,\n\t\tVehicleRestrictions = 11,\n\t\tLaneConnector = 12,\n\t\tJunctionRestrictions = 13,\n\t\tParkingRestrictions = 14\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/TrafficManagerTool.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing ColossalFramework;\nusing ColossalFramework.Math;\nusing ColossalFramework.UI;\nusing JetBrains.Annotations;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Geometry;\nusing TrafficManager.UI;\nusing UnityEngine;\nusing TrafficManager.State;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.UI.SubTools;\nusing TrafficManager.Traffic;\nusing TrafficManager.Manager;\nusing TrafficManager.Util;\nusing TrafficManager.UI.MainMenu;\nusing CSUtil.Commons;\nusing TrafficManager.Manager.Impl;\nusing TrafficManager.Traffic.Data;\nusing static TrafficManager.Traffic.Data.ExtCitizenInstance;\nusing System.Collections;\n\nnamespace TrafficManager.UI {\n\t[UsedImplicitly]\n\tpublic class TrafficManagerTool : DefaultTool, IObserver<GlobalConfig> {\n\t\tpublic struct NodeVisitItem {\n\t\t\tpublic ushort nodeId;\n\t\t\tpublic bool startNode;\n\n\t\t\tpublic NodeVisitItem(ushort nodeId, bool startNode) {\n\t\t\t\tthis.nodeId = nodeId;\n\t\t\t\tthis.startNode = startNode;\n\t\t\t}\n\t\t}\n\n\t\tprivate ToolMode _toolMode;\n\n\t\tinternal static ushort HoveredNodeId;\n\t\tinternal static ushort HoveredSegmentId;\n\n\t\tprivate static bool mouseClickProcessed;\n\n\t\tpublic static readonly float DebugCloseLod = 300f;\n\t\tpublic static readonly float MaxOverlayDistance = 450f;\n\n\t\tprivate IDictionary<ToolMode, SubTool> subTools = new TinyDictionary<ToolMode, SubTool>();\n\n\t\tpublic static ushort SelectedNodeId { get; internal set; }\n\n\t\tpublic static ushort SelectedSegmentId { get; internal set; }\n\n        public static TransportDemandViewMode CurrentTransportDemandViewMode { get; internal set; } = TransportDemandViewMode.Outgoing;\n\n        internal static ExtVehicleType[] InfoSignsToDisplay = new ExtVehicleType[] { ExtVehicleType.PassengerCar, ExtVehicleType.Bicycle, ExtVehicleType.Bus, ExtVehicleType.Taxi, ExtVehicleType.Tram, ExtVehicleType.CargoTruck, ExtVehicleType.Service, ExtVehicleType.RailVehicle };\n\n\t\tprivate static SubTool activeSubTool = null;\n\n\t\tprivate static IDisposable confDisposable;\n\n\t\tstatic TrafficManagerTool() {\n\t\t\t\n\t\t}\n\n\t\tinternal ToolController GetToolController() {\n\t\t\treturn m_toolController;\n\t\t}\n\n\t\tinternal static Rect MoveGUI(Rect rect) {\n\t\t\t// x := main menu x + rect.x\n\t\t\t// y := main menu y + main menu height + rect.y\n\t\t\treturn new Rect(MainMenuPanel.DEFAULT_MENU_X + rect.x, MainMenuPanel.DEFAULT_MENU_Y + MainMenuPanel.SIZE_PROFILES[1].MENU_HEIGHT + rect.y, rect.width, rect.height); // TODO use current size profile\n\t\t}\n\n\t\tinternal bool IsNodeWithinViewDistance(ushort nodeId) {\n\t\t\tbool ret = false;\n\t\t\tConstants.ServiceFactory.NetService.ProcessNode(nodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\tret = IsPosWithinOverlayDistance(node.m_position);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tinternal bool IsSegmentWithinViewDistance(ushort segmentId) {\n\t\t\tbool ret = false;\n\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(segmentId, delegate (ushort segId, ref NetSegment segment) {\n\t\t\t\tVector3 centerPos = segment.m_bounds.center;\n\t\t\t\tret = IsPosWithinOverlayDistance(centerPos);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tinternal bool IsPosWithinOverlayDistance(Vector3 position) {\n\t\t\treturn (position - Singleton<SimulationManager>.instance.m_simulationView.m_position).magnitude <= TrafficManagerTool.MaxOverlayDistance;\n\t\t}\n\n\t\tinternal static float AdaptWidth(float originalWidth) {\n\t\t\treturn originalWidth;\n\t\t\t//return originalWidth * ((float)Screen.width / 1920f);\n\t\t}\n\n\t\tinternal float GetBaseZoom() {\n\t\t\treturn (float)Screen.height / 1200f;\n\t\t}\n\n\t\tinternal float GetWindowAlpha() {\n\t\t\treturn TransparencyToAlpha(GlobalConfig.Instance.Main.GuiTransparency);\n\t\t}\n\n\t\tinternal float GetHandleAlpha(bool hovered) {\n\t\t\tbyte transparency = GlobalConfig.Instance.Main.OverlayTransparency;\n\t\t\tif (hovered) {\n\t\t\t\t// reduce transparency when handle is hovered\n\t\t\t\ttransparency = (byte)Math.Min(20, transparency >> 2);\n\t\t\t}\n\t\t\treturn TransparencyToAlpha(transparency);\n\t\t}\n\n\t\tprivate static float TransparencyToAlpha(byte transparency) {\n\t\t\treturn Mathf.Clamp(100 - (int)transparency, 0f, 100f) / 100f;\n\t\t}\n\n\t\tinternal void Initialize() {\n\t\t\tLog.Info(\"TrafficManagerTool: Initialization running now.\");\n\t\t\tsubTools.Clear();\n\t\t\tsubTools[ToolMode.SwitchTrafficLight] = new ToggleTrafficLightsTool(this);\n\t\t\tsubTools[ToolMode.AddPrioritySigns] = new PrioritySignsTool(this);\n\t\t\tsubTools[ToolMode.ManualSwitch] = new ManualTrafficLightsTool(this);\n\t\t\tSubTool timedLightsTool = new TimedTrafficLightsTool(this);\n\t\t\tsubTools[ToolMode.TimedLightsAddNode] = timedLightsTool;\n\t\t\tsubTools[ToolMode.TimedLightsRemoveNode] = timedLightsTool;\n\t\t\tsubTools[ToolMode.TimedLightsSelectNode] = timedLightsTool;\n\t\t\tsubTools[ToolMode.TimedLightsShowLights] = timedLightsTool;\n\t\t\tsubTools[ToolMode.TimedLightsCopyLights] = timedLightsTool;\n\t\t\tsubTools[ToolMode.VehicleRestrictions] = new VehicleRestrictionsTool(this);\n\t\t\tsubTools[ToolMode.SpeedLimits] = new SpeedLimitsTool(this);\n\t\t\tsubTools[ToolMode.LaneChange] = new LaneArrowTool(this);\n\t\t\tsubTools[ToolMode.LaneConnector] = new LaneConnectorTool(this);\n\t\t\tsubTools[ToolMode.JunctionRestrictions] = new JunctionRestrictionsTool(this);\n\t\t\tsubTools[ToolMode.ParkingRestrictions] = new ParkingRestrictionsTool(this);\n\n\t\t\tInitializeSubTools();\n\n\t\t\tSetToolMode(ToolMode.None);\n\n\t\t\tif (confDisposable != null) {\n\t\t\t\tconfDisposable.Dispose();\n\t\t\t}\n\t\t\tconfDisposable = GlobalConfig.Instance.Subscribe(this);\n\n\t\t\tLog.Info(\"TrafficManagerTool: Initialization completed.\");\n\t\t}\n\n\t\tpublic void OnUpdate(GlobalConfig config) {\n\t\t\tInitializeSubTools();\n\t\t}\n\n\t\tinternal void InitializeSubTools() {\n\t\t\tforeach (KeyValuePair<ToolMode, SubTool> e in subTools) {\n\t\t\t\te.Value.Initialize();\n\t\t\t}\n\t\t}\n\n\t\tprotected override void Awake() {\n\t\t\tLog._Debug($\"TrafficLightTool: Awake {this.GetHashCode()}\");\n\t\t\tbase.Awake();\n\t\t}\n\n\t\tpublic SubTool GetSubTool(ToolMode mode) {\n\t\t\tSubTool ret;\n\t\t\tif (subTools.TryGetValue(mode, out ret)) {\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tpublic ToolMode GetToolMode() {\n\t\t\treturn _toolMode;\n\t\t}\n\n\t\tpublic void SetToolMode(ToolMode mode) {\n\t\t\tLog._Debug($\"SetToolMode: {mode}\");\n\t\t\t\n\t\t\tbool toolModeChanged = (mode != _toolMode);\n\t\t\tvar oldToolMode = _toolMode;\n\t\t\tSubTool oldSubTool = null;\n\t\t\tsubTools.TryGetValue(oldToolMode, out oldSubTool);\n\t\t\t_toolMode = mode;\n\t\t\tif (!subTools.TryGetValue(_toolMode, out activeSubTool)) {\n\t\t\t\tactiveSubTool = null;\n\t\t\t}\n\t\t\tbool realToolChange = toolModeChanged;\n\n\t\t\tif (oldSubTool != null) {\n\t\t\t\tif ((oldToolMode == ToolMode.TimedLightsSelectNode || oldToolMode == ToolMode.TimedLightsShowLights || oldToolMode == ToolMode.TimedLightsAddNode || oldToolMode == ToolMode.TimedLightsRemoveNode || oldToolMode == ToolMode.TimedLightsCopyLights)) { // TODO refactor to SubToolMode\n\t\t\t\t\tif (mode != ToolMode.TimedLightsSelectNode && mode != ToolMode.TimedLightsShowLights && mode != ToolMode.TimedLightsAddNode && mode != ToolMode.TimedLightsRemoveNode && mode != ToolMode.TimedLightsCopyLights) {\n\t\t\t\t\t\toldSubTool.Cleanup();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\toldSubTool.Cleanup();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (toolModeChanged && activeSubTool != null) {\n\t\t\t\tif ((oldToolMode == ToolMode.TimedLightsSelectNode || oldToolMode == ToolMode.TimedLightsShowLights || oldToolMode == ToolMode.TimedLightsAddNode || oldToolMode == ToolMode.TimedLightsRemoveNode || oldToolMode == ToolMode.TimedLightsCopyLights)) { // TODO refactor to SubToolMode\n\n\t\t\t\t\tif (mode != ToolMode.TimedLightsSelectNode && mode != ToolMode.TimedLightsShowLights && mode != ToolMode.TimedLightsAddNode && mode != ToolMode.TimedLightsRemoveNode && mode != ToolMode.TimedLightsCopyLights) {\n\t\t\t\t\t\tactiveSubTool.Cleanup();\n\t\t\t\t\t} else {\n\t\t\t\t\t\trealToolChange = false;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tactiveSubTool.Cleanup();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSelectedNodeId = 0;\n\t\t\tSelectedSegmentId = 0;\n\n\t\t\t//Log._Debug($\"Getting activeSubTool for mode {_toolMode} {subTools.Count}\");\n\n\t\t\t//subTools.TryGetValue((int)_toolMode, out activeSubTool);\n\t\t\t//Log._Debug($\"activeSubTool is now {activeSubTool}\");\n\n\t\t\tif (toolModeChanged && activeSubTool != null) {\n\t\t\t\tactiveSubTool.OnActivate();\n\t\t\t\tif (realToolChange) {\n\t\t\t\t\tShowAdvisor(activeSubTool.GetTutorialKey());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Overridden to disable base class behavior\n\t\tprotected override void OnEnable() {\n\t\t\tLog._Debug($\"TrafficManagerTool.OnEnable(): Performing cleanup\");\n\t\t\tforeach (KeyValuePair<ToolMode, SubTool> e in subTools) {\n\t\t\t\te.Value.Cleanup();\n\t\t\t}\n\t\t}\n\n\t\t// Overridden to disable base class behavior\n\t\tprotected override void OnDisable() {\n\t\t}\n\n\t\tpublic override void RenderGeometry(RenderManager.CameraInfo cameraInfo) {\n\t\t\tif (HoveredNodeId != 0) {\n\t\t\t\tm_toolController.RenderCollidingNotifications(cameraInfo, 0, 0);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t///\tRenders overlays (node selection, segment selection, etc.)\n\t\t/// </summary>\n\t\t/// <param name=\"cameraInfo\"></param>\n\t\tpublic override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {\n\t\t\t//Log._Debug($\"RenderOverlay\");\n\t\t\t//Log._Debug($\"RenderOverlay: {_toolMode} {activeSubTool} {this.GetHashCode()}\");\n\n\t\t\tif (!this.isActiveAndEnabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (activeSubTool != null) {\n\t\t\t\t//Log._Debug($\"Rendering overlay in {_toolMode}\");\n\t\t\t\tactiveSubTool.RenderOverlay(cameraInfo);\n\t\t\t}\n\n\t\t\tforeach (KeyValuePair<ToolMode, SubTool> e in subTools) {\n\t\t\t\tif (e.Key == GetToolMode())\n\t\t\t\t\tcontinue;\n\t\t\t\te.Value.RenderInfoOverlay(cameraInfo);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Primarily handles click events on hovered nodes/segments\n\t\t/// </summary>\n\t\tprotected override void OnToolUpdate() {\n\t\t\tbase.OnToolUpdate();\n\t\t\t//Log._Debug($\"OnToolUpdate\");\n\n\t\t\tif (Input.GetKeyUp(KeyCode.PageDown)) {\n\t\t\t\tInfoManager.instance.SetCurrentMode(InfoManager.InfoMode.Traffic, InfoManager.SubInfoMode.Default);\n\t\t\t\tUIView.library.Hide(\"TrafficInfoViewPanel\");\n\t\t\t} else if (Input.GetKeyUp(KeyCode.PageUp))\n\t\t\t\tInfoManager.instance.SetCurrentMode(InfoManager.InfoMode.None, InfoManager.SubInfoMode.Default);\n\n\t\t\tbool primaryMouseClicked = Input.GetMouseButtonDown(0);\n\t\t\tbool secondaryMouseClicked = Input.GetMouseButtonDown(1);\n\n\t\t\t// check if clicked\n\t\t\tif (!primaryMouseClicked && !secondaryMouseClicked)\n\t\t\t\treturn;\n\n\t\t\t// check if mouse is inside panel\n\t\t\tif (LoadingExtension.BaseUI.GetMenu().containsMouse\n#if DEBUG\n\t\t\t\t|| LoadingExtension.BaseUI.GetDebugMenu().containsMouse\n#endif\n\t\t\t\t) {\n#if DEBUG\n\t\t\t\tLog._Debug($\"TrafficManagerTool: OnToolUpdate: Menu contains mouse. Ignoring click.\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (/*!elementsHovered || (*/activeSubTool != null && activeSubTool.IsCursorInPanel()/*)*/) {\n#if DEBUG\n\t\t\t\tLog._Debug($\"TrafficManagerTool: OnToolUpdate: Subtool contains mouse. Ignoring click.\");\n#endif\n\t\t\t\t//Log.Message(\"inside ui: \" + m_toolController.IsInsideUI + \" visible: \" + Cursor.visible + \" in secondary panel: \" + _cursorInSecondaryPanel);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t/*if (HoveredSegmentId == 0 && HoveredNodeId == 0) {\n\t\t\t\t//Log.Message(\"no hovered segment\");\n\t\t\t\treturn;\n\t\t\t}*/\n\n\t\t\tif (activeSubTool != null) {\n\t\t\t\tdetermineHoveredElements();\n\n\t\t\t\tif (primaryMouseClicked)\n\t\t\t\t\tactiveSubTool.OnPrimaryClickOverlay();\n\n\t\t\t\tif (secondaryMouseClicked)\n\t\t\t\t\tactiveSubTool.OnSecondaryClickOverlay();\n\t\t\t}\n\t\t}\n\n\t\tprotected override void OnToolGUI(Event e) {\n\t\t\ttry {\n\t\t\t\tif (!Input.GetMouseButtonDown(0)) {\n\t\t\t\t\tmouseClickProcessed = false;\n\t\t\t\t}\n\n\t\t\t\tif (Options.nodesOverlay) {\n\t\t\t\t\t_guiSegments();\n\t\t\t\t\t_guiNodes();\n\t\t\t\t}\n\n//#if DEBUG\n\t\t\t\tif (Options.vehicleOverlay) {\n\t\t\t\t\t_guiVehicles();\n\t\t\t\t}\n\n\t\t\t\tif (Options.citizenOverlay) {\n\t\t\t\t\t_guiCitizens();\n\t\t\t\t}\n\n\t\t\t\tif (Options.buildingOverlay) {\n\t\t\t\t\t_guiBuildings();\n\t\t\t\t}\n\t\t\t\t//#endif\n\n\t\t\t\tforeach (KeyValuePair<ToolMode, SubTool> en in subTools) {\n\t\t\t\t\ten.Value.ShowGUIOverlay(en.Key, en.Key != GetToolMode());\n\t\t\t\t}\n\n\t\t\t\tvar guiColor = GUI.color;\n\t\t\t\tguiColor.a = 1f;\n\t\t\t\tGUI.color = guiColor;\n\n\t\t\t\tif (activeSubTool != null)\n\t\t\t\t\tactiveSubTool.OnToolGUI(e);\n\t\t\t\telse\n\t\t\t\t\tbase.OnToolGUI(e);\n\t\t\t} catch (Exception ex) {\n\t\t\t\tLog.Error(\"GUI Error: \" + ex.ToString());\n\t\t\t}\n\t\t}\n\n\t\tpublic void DrawNodeCircle(RenderManager.CameraInfo cameraInfo, ushort nodeId, bool warning=false, bool alpha=false) {\n\t\t\tDrawNodeCircle(cameraInfo, nodeId, GetToolColor(warning, false), alpha);\n\t\t}\n\n\t\tpublic void DrawNodeCircle(RenderManager.CameraInfo cameraInfo, ushort nodeId, Color color, bool alpha = false) {\n\t\t\tvar segment = Singleton<NetManager>.instance.m_segments.m_buffer[Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_segment0];\n\n\t\t\tVector3 pos = Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId].m_position;\n\t\t\tfloat terrainY = Singleton<TerrainManager>.instance.SampleDetailHeightSmooth(pos);\n\t\t\tif (terrainY > pos.y)\n\t\t\t\tpos.y = terrainY;\n\n\t\t\tBezier3 bezier;\n\t\t\tbezier.a = pos;\n\t\t\tbezier.d = pos;\n\t\t\t\n\t\t\tNetSegment.CalculateMiddlePoints(bezier.a, segment.m_startDirection, bezier.d, segment.m_endDirection, false, false, out bezier.b, out bezier.c);\n\n\t\t\tDrawOverlayBezier(cameraInfo, bezier, color, alpha);\n\t\t}\n\n\t\tprivate void DrawOverlayBezier(RenderManager.CameraInfo cameraInfo, Bezier3 bezier, Color color, bool alpha=false) {\n\t\t\tconst float width = 8f; // 8 - small roads; 16 - big roads\n\t\t\tSingleton<ToolManager>.instance.m_drawCallData.m_overlayCalls++;\n\t\t\tSingleton<RenderManager>.instance.OverlayEffect.DrawBezier(cameraInfo, color, bezier, width * 2f, width, width, -1f, 1280f, false, alpha);\n\t\t}\n\n\t\tprivate void DrawOverlayCircle(RenderManager.CameraInfo cameraInfo, Color color, Vector3 position, float width, bool alpha) {\n\t\t\tSingleton<ToolManager>.instance.m_drawCallData.m_overlayCalls++;\n\t\t\tSingleton<RenderManager>.instance.OverlayEffect.DrawCircle(cameraInfo, color, position, width, position.y - 100f, position.y + 100f, false, alpha);\n\t\t}\n\n\t\tpublic void DrawStaticSquareOverlayGridTexture(Texture2D texture, Vector3 camPos, Vector3 gridOrigin, float cellSize, Vector3 xu, Vector3 yu, uint x, uint y,\n\t\t\tfloat size) {\n\t\t\tDrawGenericSquareOverlayGridTexture(texture, camPos, gridOrigin, cellSize, xu, yu, x, y, size, false);\n\t\t}\n\n\t\tpublic bool DrawHoverableSquareOverlayGridTexture(Texture2D texture, Vector3 camPos, Vector3 gridOrigin, float cellSize, Vector3 xu, Vector3 yu, uint x, uint y,\n\t\t\tfloat size) {\n\t\t\treturn DrawGenericSquareOverlayGridTexture(texture, camPos, gridOrigin, cellSize, xu, yu, x, y, size, true);\n\t\t}\n\n\t\tpublic bool DrawGenericSquareOverlayGridTexture(Texture2D texture, Vector3 camPos, Vector3 gridOrigin, float cellSize, Vector3 xu, Vector3 yu, uint x, uint y,\n\t\t\tfloat size, bool canHover) {\n\t\t\treturn DrawGenericOverlayGridTexture(texture, camPos, gridOrigin, cellSize, cellSize, xu, yu, x, y, size, size, canHover);\n\t\t}\n\n\t\tpublic void DrawStaticOverlayGridTexture(Texture2D texture, Vector3 camPos, Vector3 gridOrigin, float cellWidth, float cellHeight, Vector3 xu, Vector3 yu, uint x, uint y,\n\t\t\tfloat width, float height) {\n\t\t\tDrawGenericOverlayGridTexture(texture, camPos, gridOrigin, cellWidth, cellHeight, xu, yu, x, y, width, height, false);\n\t\t}\n\n\t\tpublic bool DrawHoverableOverlayGridTexture(Texture2D texture, Vector3 camPos, Vector3 gridOrigin, float cellWidth, float cellHeight, Vector3 xu, Vector3 yu, uint x, uint y,\n\t\t\tfloat width, float height) {\n\t\t\treturn DrawGenericOverlayGridTexture(texture, camPos, gridOrigin, cellWidth, cellHeight, xu, yu, x, y, width, height, true);\n\t\t}\n\n\t\tpublic bool DrawGenericOverlayGridTexture(Texture2D texture, Vector3 camPos, Vector3 gridOrigin, float cellWidth, float cellHeight, Vector3 xu, Vector3 yu, uint x, uint y,\n\t\t\tfloat width, float height, bool canHover) {\n\t\t\tVector3 worldPos = gridOrigin + cellWidth * (float)x * xu + cellHeight * (float)y * yu; // grid position in game coordinates\n\t\t\treturn DrawGenericOverlayTexture(texture, camPos, worldPos, width, height, canHover);\n\t\t}\n\n\t\tpublic void DrawStaticSquareOverlayTexture(Texture2D texture, Vector3 camPos, Vector3 worldPos, float size) {\n\t\t\tDrawGenericOverlayTexture(texture, camPos, worldPos, size, size, false);\n\t\t}\n\n\t\tpublic bool DrawHoverableSquareOverlayTexture(Texture2D texture, Vector3 camPos, Vector3 worldPos, float size) {\n\t\t\treturn DrawGenericOverlayTexture(texture, camPos, worldPos, size, size, true);\n\t\t}\n\n\t\tpublic bool DrawGenericSquareOverlayTexture(Texture2D texture, Vector3 camPos, Vector3 worldPos, float size, bool canHover) {\n\t\t\treturn DrawGenericOverlayTexture(texture, camPos, worldPos, size, size, canHover);\n\t\t}\n\n\t\tpublic void DrawStaticOverlayTexture(Texture2D texture, Vector3 camPos, Vector3 worldPos, float width, float height) {\n\t\t\tDrawGenericOverlayTexture(texture, camPos, worldPos, width, height, false);\n\t\t}\n\n\t\tpublic bool DrawHoverableOverlayTexture(Texture2D texture, Vector3 camPos, Vector3 worldPos, float width, float height) {\n\t\t\treturn DrawGenericOverlayTexture(texture, camPos, worldPos, width, height, true);\n\t\t}\n\n\t\tpublic bool DrawGenericOverlayTexture(Texture2D texture, Vector3 camPos, Vector3 worldPos, float width, float height, bool canHover) {\n\t\t\tVector3 screenPos;\n\t\t\tif (! WorldToScreenPoint(worldPos, out screenPos)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfloat zoom = 1.0f / (worldPos - camPos).magnitude * 100f * GetBaseZoom();\n\t\t\twidth *= zoom;\n\t\t\theight *= zoom;\n\n\t\t\tRect boundingBox = new Rect(screenPos.x - width / 2f, screenPos.y - height / 2f, width, height);\n\n\t\t\tColor guiColor = GUI.color;\n\n\t\t\tbool hovered = false;\n\t\t\tif (canHover) {\n\t\t\t\thovered = IsMouseOver(boundingBox);\n\t\t\t}\n\t\t\tguiColor.a = GetHandleAlpha(hovered);\n\n\t\t\tGUI.color = guiColor;\n\t\t\tGUI.DrawTexture(boundingBox, texture);\n\n\t\t\treturn hovered;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Transforms a world point into a screen point\n\t\t/// </summary>\n\t\t/// <param name=\"worldPos\"></param>\n\t\t/// <param name=\"screenPos\"></param>\n\t\t/// <returns></returns>\n\t\tpublic bool WorldToScreenPoint(Vector3 worldPos, out Vector3 screenPos) {\n\t\t\tscreenPos = Camera.main.WorldToScreenPoint(worldPos);\n\t\t\tscreenPos.y = Screen.height - screenPos.y;\n\n\t\t\treturn screenPos.z >= 0;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Shows a tutorial message. Must be called by a Unity thread.\n\t\t/// </summary>\n\t\t/// <param name=\"localeKey\"></param>\n\t\tpublic static void ShowAdvisor(string localeKey) {\n\t\t\tif (! GlobalConfig.Instance.Main.EnableTutorial) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (! Translation.HasString(Translation.TUTORIAL_BODY_KEY_PREFIX + localeKey)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLog._Debug($\"TrafficManagerTool.ShowAdvisor({localeKey}) called.\");\n\t\t\tTutorialAdvisorPanel tutorialPanel = ToolsModifierControl.advisorPanel;\n\t\t\tstring key = Translation.TUTORIAL_KEY_PREFIX + localeKey;\n\t\t\tif (GlobalConfig.Instance.Main.DisplayedTutorialMessages.Contains(localeKey)) {\n\t\t\t\ttutorialPanel.Refresh(key, \"ToolbarIconZoomOutGlobe\", string.Empty);\n\t\t\t} else {\n\t\t\t\ttutorialPanel.Show(key, \"ToolbarIconZoomOutGlobe\", string.Empty, 0f);\n\t\t\t\tGlobalConfig.Instance.Main.AddDisplayedTutorialMessage(localeKey);\n\t\t\t\tGlobalConfig.WriteConfig();\n\t\t\t}\n\t\t}\n\n\t\tpublic override void SimulationStep() {\n\t\t\tbase.SimulationStep();\n\n\t\t\t/*currentFrame = Singleton<SimulationManager>.instance.m_currentFrameIndex >> 2;\n\n\t\t\tstring displayToolTipText = tooltipText;\n\t\t\tif (displayToolTipText != null) {\n\t\t\t\tif (currentFrame <= tooltipStartFrame + 50) {\n\t\t\t\t\tShowToolInfo(true, displayToolTipText, (Vector3)tooltipWorldPos);\n\t\t\t\t} else {\n\t\t\t\t\t//ShowToolInfo(false, tooltipText, (Vector3)tooltipWorldPos);\n\t\t\t\t\t//ShowToolInfo(false, null, Vector3.zero);\n\t\t\t\t\ttooltipStartFrame = 0;\n\t\t\t\t\ttooltipText = null;\n\t\t\t\t\ttooltipWorldPos = null;\n\t\t\t\t}\n\t\t\t}*/\n\n\t\t\tif (GetToolMode() == ToolMode.None) {\n\t\t\t\tToolCursor = null;\n\t\t\t} else {\n\t\t\t\tbool elementsHovered = determineHoveredElements();\n\n\t\t\t\tvar netTool = ToolsModifierControl.toolController.Tools.OfType<NetTool>().FirstOrDefault(nt => nt.m_prefab != null);\n\n\t\t\t\tif (netTool != null && elementsHovered) {\n\t\t\t\t\tToolCursor = netTool.m_upgradeCursor;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic bool DoRayCast(RaycastInput input, out RaycastOutput output) {\n\t\t\treturn RayCast(input, out output);\n\t\t}\n\n\t\tprivate bool determineHoveredElements() {\n\t\t\tvar mouseRayValid = !UIView.IsInsideUI() && Cursor.visible && (activeSubTool == null || !activeSubTool.IsCursorInPanel());\n\n\t\t\tif (mouseRayValid) {\n\t\t\t\tushort oldHoveredSegmentId = HoveredSegmentId;\n\t\t\t\tushort oldHoveredNodeId = HoveredNodeId;\n\n\t\t\t\tHoveredSegmentId = 0;\n\t\t\t\tHoveredNodeId = 0;\n\n\t\t\t\t// find currently hovered node\n\t\t\t\tvar nodeInput = new RaycastInput(this.m_mouseRay, this.m_mouseRayLength);\n\t\t\t\t// find road nodes\n\t\t\t\tnodeInput.m_netService.m_itemLayers = ItemClass.Layer.Default | ItemClass.Layer.MetroTunnels;\n\t\t\t\tnodeInput.m_netService.m_service = ItemClass.Service.Road;\n\t\t\t\t/*nodeInput.m_netService2.m_itemLayers = ItemClass.Layer.Default | ItemClass.Layer.PublicTransport | ItemClass.Layer.MetroTunnels;\n\t\t\t\tnodeInput.m_netService2.m_service = ItemClass.Service.PublicTransport;\n\t\t\t\tnodeInput.m_netService2.m_subService = ItemClass.SubService.PublicTransportTrain;*/\n\t\t\t\tnodeInput.m_ignoreTerrain = true;\n\t\t\t\tnodeInput.m_ignoreNodeFlags = NetNode.Flags.None;\n\t\t\t\t//nodeInput.m_ignoreNodeFlags = NetNode.Flags.Untouchable;\n\n\t\t\t\tRaycastOutput nodeOutput;\n\t\t\t\tif (RayCast(nodeInput, out nodeOutput)) {\n\t\t\t\t\tHoveredNodeId = nodeOutput.m_netNode;\n\t\t\t\t} else {\n\t\t\t\t\t// find train nodes\n\t\t\t\t\tnodeInput.m_netService.m_itemLayers = ItemClass.Layer.Default | ItemClass.Layer.MetroTunnels;\n\t\t\t\t\tnodeInput.m_netService.m_service = ItemClass.Service.PublicTransport;\n\t\t\t\t\tnodeInput.m_netService.m_subService = ItemClass.SubService.PublicTransportTrain;\n\t\t\t\t\tnodeInput.m_ignoreTerrain = true;\n\t\t\t\t\tnodeInput.m_ignoreNodeFlags = NetNode.Flags.None;\n\t\t\t\t\t//nodeInput.m_ignoreNodeFlags = NetNode.Flags.Untouchable;\n\n\t\t\t\t\tif (RayCast(nodeInput, out nodeOutput)) {\n\t\t\t\t\t\tHoveredNodeId = nodeOutput.m_netNode;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// find metro nodes\n\t\t\t\t\t\tnodeInput.m_netService.m_itemLayers = ItemClass.Layer.Default | ItemClass.Layer.MetroTunnels;\n\t\t\t\t\t\tnodeInput.m_netService.m_service = ItemClass.Service.PublicTransport;\n\t\t\t\t\t\tnodeInput.m_netService.m_subService = ItemClass.SubService.PublicTransportMetro;\n\t\t\t\t\t\tnodeInput.m_ignoreTerrain = true;\n\t\t\t\t\t\tnodeInput.m_ignoreNodeFlags = NetNode.Flags.None;\n\t\t\t\t\t\t//nodeInput.m_ignoreNodeFlags = NetNode.Flags.Untouchable;\n\n\t\t\t\t\t\tif (RayCast(nodeInput, out nodeOutput)) {\n\t\t\t\t\t\t\tHoveredNodeId = nodeOutput.m_netNode;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// find currently hovered segment\n\t\t\t\tvar segmentInput = new RaycastInput(this.m_mouseRay, this.m_mouseRayLength);\n\t\t\t\t// find road segments\n\t\t\t\tsegmentInput.m_netService.m_itemLayers = ItemClass.Layer.Default | ItemClass.Layer.MetroTunnels;\n\t\t\t\tsegmentInput.m_netService.m_service = ItemClass.Service.Road;\n\t\t\t\tsegmentInput.m_ignoreTerrain = true;\n\t\t\t\tsegmentInput.m_ignoreSegmentFlags = NetSegment.Flags.None;\n\t\t\t\t//segmentInput.m_ignoreSegmentFlags = NetSegment.Flags.Untouchable;\n\n\t\t\t\tRaycastOutput segmentOutput;\n\t\t\t\tif (RayCast(segmentInput, out segmentOutput)) {\n\t\t\t\t\tHoveredSegmentId = segmentOutput.m_netSegment;\n\t\t\t\t} else {\n\t\t\t\t\t// find train segments\n\t\t\t\t\tsegmentInput.m_netService.m_itemLayers = ItemClass.Layer.Default | ItemClass.Layer.MetroTunnels;\n\t\t\t\t\tsegmentInput.m_netService.m_service = ItemClass.Service.PublicTransport;\n\t\t\t\t\tsegmentInput.m_netService.m_subService = ItemClass.SubService.PublicTransportTrain;\n\t\t\t\t\tsegmentInput.m_ignoreTerrain = true;\n\t\t\t\t\tsegmentInput.m_ignoreSegmentFlags = NetSegment.Flags.None;\n\t\t\t\t\t//segmentInput.m_ignoreSegmentFlags = NetSegment.Flags.Untouchable;\n\n\t\t\t\t\tif (RayCast(segmentInput, out segmentOutput)) {\n\t\t\t\t\t\tHoveredSegmentId = segmentOutput.m_netSegment;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// find metro segments\n\t\t\t\t\t\tsegmentInput.m_netService.m_itemLayers = ItemClass.Layer.Default | ItemClass.Layer.MetroTunnels;\n\t\t\t\t\t\tsegmentInput.m_netService.m_service = ItemClass.Service.PublicTransport;\n\t\t\t\t\t\tsegmentInput.m_netService.m_subService = ItemClass.SubService.PublicTransportMetro;\n\t\t\t\t\t\tsegmentInput.m_ignoreTerrain = true;\n\t\t\t\t\t\tsegmentInput.m_ignoreSegmentFlags = NetSegment.Flags.None;\n\t\t\t\t\t\t//segmentInput.m_ignoreSegmentFlags = NetSegment.Flags.Untouchable;\n\n\t\t\t\t\t\tif (RayCast(segmentInput, out segmentOutput)) {\n\t\t\t\t\t\t\tHoveredSegmentId = segmentOutput.m_netSegment;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (HoveredNodeId <= 0 && HoveredSegmentId > 0) {\n\t\t\t\t\t// alternative way to get a node hit: check distance to start and end nodes of the segment\n\t\t\t\t\tushort startNodeId = Singleton<NetManager>.instance.m_segments.m_buffer[HoveredSegmentId].m_startNode;\n\t\t\t\t\tushort endNodeId = Singleton<NetManager>.instance.m_segments.m_buffer[HoveredSegmentId].m_endNode;\n\n\t\t\t\t\tfloat startDist = (segmentOutput.m_hitPos - Singleton<NetManager>.instance.m_nodes.m_buffer[startNodeId].m_position).magnitude;\n\t\t\t\t\tfloat endDist = (segmentOutput.m_hitPos - Singleton<NetManager>.instance.m_nodes.m_buffer[endNodeId].m_position).magnitude;\n\t\t\t\t\tif (startDist < endDist && startDist < 75f)\n\t\t\t\t\t\tHoveredNodeId = startNodeId;\n\t\t\t\t\telse if (endDist < startDist && endDist < 75f)\n\t\t\t\t\t\tHoveredNodeId = endNodeId;\n\t\t\t\t}\n\n\t\t\t\t/*if (oldHoveredNodeId != HoveredNodeId || oldHoveredSegmentId != HoveredSegmentId) {\n\t\t\t\t\tLog._Debug($\"*** Mouse ray @ node {HoveredNodeId}, segment {HoveredSegmentId}, toolMode={GetToolMode()}\");\n                }*/\n\n\t\t\t\treturn (HoveredNodeId != 0 || HoveredSegmentId != 0);\n\t\t\t} else {\n\t\t\t\t//Log._Debug($\"Mouse ray invalid: {UIView.IsInsideUI()} {Cursor.visible} {activeSubTool == null} {activeSubTool.IsCursorInPanel()}\");\n            }\n\n\t\t\treturn mouseRayValid;\n\t\t}\n\t\t\n\t\t/// <summary>\n\t\t/// Displays lane ids over lanes\n\t\t/// </summary>\n\t\tprivate void _guiLanes(ushort segmentId, ref NetSegment segment, ref NetInfo segmentInfo) {\n\t\t\tGUIStyle _counterStyle = new GUIStyle();\n\t\t\tVector3 centerPos = segment.m_bounds.center;\n\t\t\tVector3 screenPos;\n\t\t\tbool visible = WorldToScreenPoint(centerPos, out screenPos);\n\t\t\t\n\t\t\tif (! visible) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tscreenPos.y -= 200;\n\n\t\t\tif (screenPos.z < 0)\n\t\t\t\treturn;\n\n\t\t\tvar camPos = Singleton<SimulationManager>.instance.m_simulationView.m_position;\n\t\t\tvar diff = centerPos - camPos;\n\t\t\tif (diff.magnitude > DebugCloseLod)\n\t\t\t\treturn; // do not draw if too distant\n\n\t\t\tvar zoom = 1.0f / diff.magnitude * 150f;\n\n\t\t\t_counterStyle.fontSize = (int)(11f * zoom);\n\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 0f);\n\n\t\t\t/*uint totalDensity = 0u;\n\t\t\tfor (int i = 0; i < segmentInfo.m_lanes.Length; ++i) {\n\t\t\t\tif (CustomRoadAI.currentLaneDensities[segmentId] != null && i < CustomRoadAI.currentLaneDensities[segmentId].Length)\n\t\t\t\t\ttotalDensity += CustomRoadAI.currentLaneDensities[segmentId][i];\n\t\t\t}*/\n\n\t\t\tuint curLaneId = segment.m_lanes;\n\t\t\tString labelStr = \"\";\n\t\t\tfor (int i = 0; i < segmentInfo.m_lanes.Length; ++i) {\n\t\t\t\tif (curLaneId == 0)\n\t\t\t\t\tbreak;\n\n\t\t\t\tTrafficMeasurementManager.LaneTrafficData laneTrafficData;\n\t\t\t\tbool laneTrafficDataLoaded = TrafficMeasurementManager.Instance.GetLaneTrafficData(segmentId, (byte)i, out laneTrafficData);\n\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[i];\n\n#if PFTRAFFICSTATS\n\t\t\t\tuint pfTrafficBuf = TrafficMeasurementManager.Instance.segmentDirTrafficData[TrafficMeasurementManager.Instance.GetDirIndex(segmentId, laneInfo.m_finalDirection)].totalPathFindTrafficBuffer;\n#endif\n\t\t\t\t//TrafficMeasurementManager.Instance.GetTrafficData(segmentId, laneInfo.m_finalDirection, out dirTrafficData);\n\n\t\t\t\t//int dirIndex = laneInfo.m_finalDirection == NetInfo.Direction.Backward ? 1 : 0;\n\n\t\t\t\tlabelStr += \"L idx \" + i + \", id \" + curLaneId;\n#if DEBUG\n\t\t\t\tlabelStr += \", in: \" + RoutingManager.Instance.CalcInnerSimilarLaneIndex(segmentId, i) + \", out: \" + RoutingManager.Instance.CalcOuterSimilarLaneIndex(segmentId, i) + \", f: \" + ((NetLane.Flags)Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_flags).ToString() + \", l: \" + SpeedLimitManager.Instance.GetCustomSpeedLimit(curLaneId) + \" km/h, rst: \" + VehicleRestrictionsManager.Instance.GetAllowedVehicleTypes(segmentId, segmentInfo, (uint)i, laneInfo, VehicleRestrictionsMode.Configured) + \", dir: \" + laneInfo.m_direction + \", fnl: \" + laneInfo.m_finalDirection + \", pos: \" + String.Format(\"{0:0.##}\", laneInfo.m_position) + \", sim: \" + laneInfo.m_similarLaneIndex + \" for \" + laneInfo.m_vehicleType + \"/\" + laneInfo.m_laneType;\n#endif\n\t\t\t\tif (laneTrafficDataLoaded) {\n\t\t\t\t\tlabelStr += \", sp: \" + (TrafficMeasurementManager.Instance.CalcLaneRelativeMeanSpeed(segmentId, (byte)i, curLaneId, laneInfo) / 100) + \"%\";\n#if DEBUG\n\t\t\t\t\tlabelStr += \", buf: \" + laneTrafficData.trafficBuffer + \", max: \" + laneTrafficData.maxTrafficBuffer + \", acc: \" + laneTrafficData.accumulatedSpeeds;\n#if PFTRAFFICSTATS\n\t\t\t\t\tlabelStr += \", pfBuf: \" + laneTrafficData.pathFindTrafficBuffer + \"/\" + laneTrafficData.lastPathFindTrafficBuffer + \", (\" + (pfTrafficBuf > 0 ? \"\" + ((laneTrafficData.lastPathFindTrafficBuffer * 100u) / pfTrafficBuf) : \"n/a\") + \" %)\";\n#endif\n#endif\n#if MEASUREDENSITY\n\t\t\t\t\tif (dirTrafficDataLoaded) {\n\t\t\t\t\t\tlabelStr += \", rel. dens.: \" + (dirTrafficData.accumulatedDensities > 0 ? \"\" + Math.Min(laneTrafficData[i].accumulatedDensities * 100 / dirTrafficData.accumulatedDensities, 100) : \"?\") + \"%\";\n\t\t\t\t\t}\n\t\t\t\t\tlabelStr += \", acc: \" + laneTrafficData[i].accumulatedDensities;\n#endif\n\t\t\t\t}\n\n\t\t\t\tlabelStr += \", nd: \" + Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nodes;\n#if DEBUG\n\t\t\t\t//labelStr += \" (\" + (CustomRoadAI.currentLaneDensities[segmentId] != null && i < CustomRoadAI.currentLaneDensities[segmentId].Length ? \"\" + CustomRoadAI.currentLaneDensities[segmentId][i] : \"?\") + \"/\" + (CustomRoadAI.maxLaneDensities[segmentId] != null && i < CustomRoadAI.maxLaneDensities[segmentId].Length ? \"\" + CustomRoadAI.maxLaneDensities[segmentId][i] : \"?\") + \"/\" + totalDensity + \")\";\n\t\t\t\t//labelStr += \" (\" + (CustomRoadAI.currentLaneDensities[segmentId] != null && i < CustomRoadAI.currentLaneDensities[segmentId].Length ? \"\" + CustomRoadAI.currentLaneDensities[segmentId][i] : \"?\") + \"/\" + totalDensity + \")\";\n#endif\n\t\t\t\t//labelStr += \", abs. dens.: \" + (CustomRoadAI.laneMeanAbsDensities[segmentId] != null && i < CustomRoadAI.laneMeanAbsDensities[segmentId].Length ? \"\" + CustomRoadAI.laneMeanAbsDensities[segmentId][i] : \"?\") + \" %\";\n\t\t\t\tlabelStr += \"\\n\";\n\n\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t}\n\t\t\t\n\t\t\tVector2 dim = _counterStyle.CalcSize(new GUIContent(labelStr));\n\t\t\tRect labelRect = new Rect(screenPos.x - dim.x / 2f, screenPos.y, dim.x, dim.y);\n\n\t\t\tGUI.Label(labelRect, labelStr, _counterStyle);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Displays segment ids over segments\n\t\t/// </summary>\n\t\tprivate void _guiSegments() {\n\t\t\tTrafficMeasurementManager trafficMeasurementManager = TrafficMeasurementManager.Instance;\n\n\t\t\tGUIStyle _counterStyle = new GUIStyle();\n\t\t\tSegmentEndManager endMan = SegmentEndManager.Instance;\n\t\t\tArray16<NetSegment> segments = Singleton<NetManager>.instance.m_segments;\n\t\t\tfor (int i = 1; i < segments.m_size; ++i) {\n\t\t\t\tif (segments.m_buffer[i].m_flags == NetSegment.Flags.None) // segment is unused\n\t\t\t\t\tcontinue;\n\t\t\t\tItemClass.Service service = segments.m_buffer[i].Info.GetService();\n\t\t\t\tItemClass.SubService subService = segments.m_buffer[i].Info.GetSubService();\n\t\t\t\t/*if (service != ItemClass.Service.Road) {\n\t\t\t\t\tif (service != ItemClass.Service.PublicTransport) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (subService != ItemClass.SubService.PublicTransportBus && subService != ItemClass.SubService.PublicTransportCableCar &&\n\t\t\t\t\t\t\tsubService != ItemClass.SubService.PublicTransportMetro && subService != ItemClass.SubService.PublicTransportMonorail &&\n\t\t\t\t\t\t\tsubService != ItemClass.SubService.PublicTransportTrain) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}*/\n#if !DEBUG\n\t\t\t\tif ((segments.m_buffer[i].m_flags & NetSegment.Flags.Untouchable) != NetSegment.Flags.None)\n\t\t\t\t\tcontinue;\n#endif\n\t\t\t\tvar segmentInfo = segments.m_buffer[i].Info;\n\n\t\t\t\tVector3 centerPos = segments.m_buffer[i].m_bounds.center;\n\t\t\t\tVector3 screenPos;\n\t\t\t\tbool visible = WorldToScreenPoint(centerPos, out screenPos);\n\n\t\t\t\tif (! visible)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tvar camPos = Singleton<SimulationManager>.instance.m_simulationView.m_position;\n\t\t\t\tvar diff = centerPos - camPos;\n\t\t\t\tif (diff.magnitude > DebugCloseLod)\n\t\t\t\t\tcontinue; // do not draw if too distant\n\t\t\t\t\n\t\t\t\tvar zoom = 1.0f / diff.magnitude * 150f;\n\n\t\t\t\t_counterStyle.fontSize = (int)(12f * zoom);\n\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 0f, 0f);\n\n\t\t\t\tString labelStr = \"Segment \" + i;\n#if DEBUG\n\t\t\t\tlabelStr += \", flags: \" + segments.m_buffer[i].m_flags.ToString(); // + \", condition: \" + segments.m_buffer[i].m_condition;\n#endif\n#if DEBUG\n\t\t\t\tlabelStr += \"\\nsvc: \" + service + \", sub: \" + subService;\n\t\t\t\tISegmentEnd startEnd = endMan.GetSegmentEnd((ushort)i, true);\n\t\t\t\tISegmentEnd endEnd = endMan.GetSegmentEnd((ushort)i, false);\n\t\t\t\tlabelStr += \"\\nstart? \" + (startEnd != null) + \" veh.: \" + startEnd?.GetRegisteredVehicleCount() + \", end? \" + (endEnd != null) + \" veh.: \" + endEnd?.GetRegisteredVehicleCount();\n#endif\n\t\t\t\tlabelStr += \"\\nTraffic: \" + segments.m_buffer[i].m_trafficDensity + \" %\";\n\n#if DEBUG\n\n\t\t\t\tint fwdSegIndex = trafficMeasurementManager.GetDirIndex((ushort)i, NetInfo.Direction.Forward);\n\t\t\t\tint backSegIndex = trafficMeasurementManager.GetDirIndex((ushort)i, NetInfo.Direction.Backward);\n\n\t\t\t\tlabelStr += \"\\n\";\n#if MEASURECONGESTION\n\t\t\t\tfloat fwdCongestionRatio = trafficMeasurementManager.segmentDirTrafficData[fwdSegIndex].numCongestionMeasurements > 0 ? ((uint)trafficMeasurementManager.segmentDirTrafficData[fwdSegIndex].numCongested * 100u) / (uint)trafficMeasurementManager.segmentDirTrafficData[fwdSegIndex].numCongestionMeasurements : 0; // now in %\n\t\t\t\tfloat backCongestionRatio = trafficMeasurementManager.segmentDirTrafficData[backSegIndex].numCongestionMeasurements > 0 ? ((uint)trafficMeasurementManager.segmentDirTrafficData[backSegIndex].numCongested * 100u) / (uint)trafficMeasurementManager.segmentDirTrafficData[backSegIndex].numCongestionMeasurements : 0; // now in %\n\n\n\t\t\t\tlabelStr += \"min speeds: \";\n\t\t\t\tlabelStr += \" \" + (trafficMeasurementManager.segmentDirTrafficData[fwdSegIndex].minSpeed / 100) + \"%/\" + (trafficMeasurementManager.segmentDirTrafficData[backSegIndex].minSpeed / 100) + \"%\";\n\t\t\t\tlabelStr += \", \";\n#endif\n\t\t\t\tlabelStr += \"mean speeds: \";\n\t\t\t\tlabelStr += \" \" + (trafficMeasurementManager.segmentDirTrafficData[fwdSegIndex].meanSpeed / 100) + \"%/\" + (trafficMeasurementManager.segmentDirTrafficData[backSegIndex].meanSpeed / 100) + \"%\";\n#if PFTRAFFICSTATS || MEASURECONGESTION\n\t\t\t\tlabelStr += \"\\n\";\n#endif\n#if PFTRAFFICSTATS\n\t\t\t\tlabelStr += \"pf bufs: \";\n\t\t\t\tlabelStr += \" \" + (trafficMeasurementManager.segmentDirTrafficData[fwdSegIndex].totalPathFindTrafficBuffer) + \"/\" + (trafficMeasurementManager.segmentDirTrafficData[backSegIndex].totalPathFindTrafficBuffer);\n#endif\n#if PFTRAFFICSTATS && MEASURECONGESTION\n\t\t\t\tlabelStr += \", \";\n#endif\n#if MEASURECONGESTION\n\t\t\t\tlabelStr += \"cong: \";\n\t\t\t\tlabelStr += \" \" + fwdCongestionRatio + \"% (\" + trafficMeasurementManager.segmentDirTrafficData[fwdSegIndex].numCongested + \"/\" + trafficMeasurementManager.segmentDirTrafficData[fwdSegIndex].numCongestionMeasurements + \")/\" + backCongestionRatio + \"% (\" + trafficMeasurementManager.segmentDirTrafficData[backSegIndex].numCongested + \"/\" + trafficMeasurementManager.segmentDirTrafficData[backSegIndex].numCongestionMeasurements + \")\";\n#endif\n\t\t\t\tlabelStr += \"\\nstart: \" + segments.m_buffer[i].m_startNode + \", end: \" + segments.m_buffer[i].m_endNode;\n#endif\n\n\t\t\t\tVector2 dim = _counterStyle.CalcSize(new GUIContent(labelStr));\n\t\t\t\tRect labelRect = new Rect(screenPos.x - dim.x / 2f, screenPos.y, dim.x, dim.y);\n\n\t\t\t\tGUI.Label(labelRect, labelStr, _counterStyle);\n\n\t\t\t\tif (Options.showLanes)\n\t\t\t\t\t_guiLanes((ushort)i, ref segments.m_buffer[i], ref segmentInfo);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Displays node ids over nodes\n\t\t/// </summary>\n\t\tprivate void _guiNodes() {\n\t\t\tGUIStyle _counterStyle = new GUIStyle();\n\t\t\tArray16<NetNode> nodes = Singleton<NetManager>.instance.m_nodes;\n\t\t\t\n\t\t\tfor (int i = 1; i < nodes.m_size; ++i) {\n\t\t\t\tif ((nodes.m_buffer[i].m_flags & NetNode.Flags.Created) == NetNode.Flags.None) // node is unused\n\t\t\t\t\tcontinue;\n\n\t\t\t\tVector3 pos = nodes.m_buffer[i].m_position;\n\t\t\t\tVector3 screenPos;\n\t\t\t\tbool visible = WorldToScreenPoint(pos, out screenPos);\n\t\t\t\t\n\t\t\t\tif (! visible)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tvar camPos = Singleton<SimulationManager>.instance.m_simulationView.m_position;\n\t\t\t\tvar diff = pos - camPos;\n\t\t\t\tif (diff.magnitude > DebugCloseLod)\n\t\t\t\t\tcontinue; // do not draw if too distant\n\t\t\t\t\n\t\t\t\tvar zoom = 1.0f / diff.magnitude * 150f;\n\n\t\t\t\t_counterStyle.fontSize = (int)(15f * zoom);\n\t\t\t\t_counterStyle.normal.textColor = new Color(0f, 0f, 1f);\n\n\t\t\t\tString labelStr = \"Node \" + i;\n#if DEBUG\n\t\t\t\tlabelStr += $\"\\nflags: {nodes.m_buffer[i].m_flags}\";\n\t\t\t\tlabelStr += $\"\\nlane: {nodes.m_buffer[i].m_lane}\";\n#endif\n\t\t\t\tVector2 dim = _counterStyle.CalcSize(new GUIContent(labelStr));\n\t\t\t\tRect labelRect = new Rect(screenPos.x - dim.x / 2f, screenPos.y, dim.x, dim.y);\n\n\t\t\t\tGUI.Label(labelRect, labelStr, _counterStyle);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Displays vehicle ids over vehicles\n\t\t/// </summary>\n\t\tprivate void _guiVehicles() {\n\t\t\tGUIStyle _counterStyle = new GUIStyle();\n\t\t\tArray16<Vehicle> vehicles = Singleton<VehicleManager>.instance.m_vehicles;\n\t\t\tLaneConnectionManager connManager = LaneConnectionManager.Instance;\n\t\t\tSimulationManager simManager = Singleton<SimulationManager>.instance;\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tVehicleStateManager vehStateManager = VehicleStateManager.Instance;\n\n\n\t\t\tint startVehicleId = 1;\n\t\t\tint endVehicleId = (int)(vehicles.m_size - 1);\n#if DEBUG\n\t\t\tif (GlobalConfig.Instance.Debug.VehicleId != 0) {\n\t\t\t\tstartVehicleId = endVehicleId = GlobalConfig.Instance.Debug.VehicleId;\n\t\t\t}\n#endif\n\n\t\t\tfor (int i = startVehicleId; i <= endVehicleId; ++i) {\n\t\t\t\tVehicle vehicle = vehicles.m_buffer[i];\n\t\t\t\tif (vehicle.m_flags == 0) // node is unused\n\t\t\t\t\tcontinue;\n\n\t\t\t\tVector3 vehPos = vehicle.GetSmoothPosition((ushort)i);\n\t\t\t\tVector3 screenPos;\n\t\t\t\tbool visible = WorldToScreenPoint(vehPos, out screenPos);\n\t\t\t\t\n\t\t\t\tif (! visible)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tvar camPos = simManager.m_simulationView.m_position;\n\t\t\t\tvar diff = vehPos - camPos;\n\t\t\t\tif (diff.magnitude > DebugCloseLod)\n\t\t\t\t\tcontinue; // do not draw if too distant\n\n\t\t\t\tvar zoom = 1.0f / diff.magnitude * 150f;\n\n\t\t\t\t_counterStyle.fontSize = (int)(10f * zoom);\n\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 1f, 1f);\n\t\t\t\t//_counterStyle.normal.background = MakeTex(1, 1, new Color(0f, 0f, 0f, 0.4f));\n\n\t\t\t\tVehicleState vState = vehStateManager.VehicleStates[(ushort)i];\n\t\t\t\tExtCitizenInstance driverInst = ExtCitizenInstanceManager.Instance.ExtInstances[CustomPassengerCarAI.GetDriverInstanceId((ushort)i, ref Singleton<VehicleManager>.instance.m_vehicles.m_buffer[i])];\n\t\t\t\tbool startNode = vState.currentStartNode;\n\t\t\t\tushort segmentId = vState.currentSegmentId;\n\t\t\t\tushort vehSpeed = SpeedLimitManager.Instance.VehicleToCustomSpeed(vehicle.GetLastFrameVelocity().magnitude);\n\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.ExtPathMode != ExtPathMode.None && driverInst.pathMode != GlobalConfig.Instance.Debug.ExtPathMode) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n#endif\n\n\t\t\t\tString labelStr = \"V #\" + i + \" is a \" + (vState.recklessDriver ? \"reckless \" : \"\") + vState.flags + \" \" + vState.vehicleType + \" @ ~\" + vehSpeed + \" km/h [^2=\" + vState.SqrVelocity + \"] (len: \" + vState.totalLength + \", \" + vState.JunctionTransitState + \" @ \" + vState.currentSegmentId + \" (\" + vState.currentStartNode + \"), l. \" + vState.currentLaneIndex + \" -> \" + vState.nextSegmentId + \", l. \" + vState.nextLaneIndex + \"), w: \" + vState.waitTime + \"\\n\" +\n\t\t\t\t\t\"di: \" + driverInst.instanceId + \" dc: \" + driverInst.GetCitizenId() + \" m: \" + driverInst.pathMode.ToString() + \" f: \" + driverInst.failedParkingAttempts + \" l: \" + driverInst.parkingSpaceLocation + \" lid: \" + driverInst.parkingSpaceLocationId + \" ltsu: \" + vState.lastTransitStateUpdate + \" lpu: \" + vState.lastPositionUpdate + \" als: \" + vState.lastAltLaneSelSegmentId + \" srnd: \" + Constants.ManagerFactory.VehicleBehaviorManager.GetStaticVehicleRand((ushort)i) + \" trnd: \" + Constants.ManagerFactory.VehicleBehaviorManager.GetTimedVehicleRand((ushort)i);\n\n\t\t\t\tVector2 dim = _counterStyle.CalcSize(new GUIContent(labelStr));\n\t\t\t\tRect labelRect = new Rect(screenPos.x - dim.x / 2f, screenPos.y - dim.y - 50f, dim.x, dim.y);\n\n\t\t\t\tGUI.Box(labelRect, labelStr, _counterStyle);\n\n\t\t\t\t//_counterStyle.normal.background = null;\n\t\t\t}\n\t\t}\n\n\t\tprivate void _guiCitizens() {\n\t\t\tGUIStyle _counterStyle = new GUIStyle();\n\t\t\tArray16<CitizenInstance> citizenInstances = Singleton<CitizenManager>.instance.m_instances;\n\t\t\tfor (int i = 1; i < citizenInstances.m_size; ++i) {\n\t\t\t\tCitizenInstance citizenInstance = citizenInstances.m_buffer[i];\n\t\t\t\tif (citizenInstance.m_flags == CitizenInstance.Flags.None)\n\t\t\t\t\tcontinue;\n\t\t\t\tif ((citizenInstance.m_flags & CitizenInstance.Flags.Character) == CitizenInstance.Flags.None)\n\t\t\t\t\tcontinue;\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.Switches[14]) {\n#endif\n\t\t\t\t\tif (citizenInstance.m_path != 0) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n#if DEBUG\n\t\t\t\t}\n#endif\n\n\t\t\t\tVector3 pos = citizenInstance.GetSmoothPosition((ushort)i);\n\t\t\t\tVector3 screenPos;\n\t\t\t\tbool visible = WorldToScreenPoint(pos, out screenPos);\n\t\t\t\t\n\t\t\t\tif (! visible)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tvar camPos = Singleton<SimulationManager>.instance.m_simulationView.m_position;\n\t\t\t\tvar diff = pos - camPos;\n\t\t\t\tif (diff.magnitude > DebugCloseLod)\n\t\t\t\t\tcontinue; // do not draw if too distant\n\n\t\t\t\tvar zoom = 1.0f / diff.magnitude * 150f;\n\n\t\t\t\t_counterStyle.fontSize = (int)(10f * zoom);\n\t\t\t\t_counterStyle.normal.textColor = new Color(1f, 0f, 1f);\n\t\t\t\t//_counterStyle.normal.background = MakeTex(1, 1, new Color(0f, 0f, 0f, 0.4f));\n\n#if DEBUG\n\t\t\t\tif (GlobalConfig.Instance.Debug.ExtPathMode != ExtPathMode.None && ExtCitizenInstanceManager.Instance.ExtInstances[i].pathMode != GlobalConfig.Instance.Debug.ExtPathMode) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n#endif\n\n\t\t\t\tString labelStr = \"Inst. \" + i + \", Cit. \" + citizenInstance.m_citizen + \",\\nm: \" + ExtCitizenInstanceManager.Instance.ExtInstances[i].pathMode.ToString() + \", tm: \" + ExtCitizenManager.Instance.ExtCitizens[citizenInstance.m_citizen].transportMode + \", ltm: \" + ExtCitizenManager.Instance.ExtCitizens[citizenInstance.m_citizen].lastTransportMode + \", ll: \" + ExtCitizenManager.Instance.ExtCitizens[citizenInstance.m_citizen].lastLocation;\n\t\t\t\tif (citizenInstance.m_citizen != 0) {\n\t\t\t\t\tCitizen citizen = Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenInstance.m_citizen];\n\t\t\t\t\tif (citizen.m_parkedVehicle != 0) {\n\t\t\t\t\t\tlabelStr += \"\\nparked: \" + citizen.m_parkedVehicle + \" dist: \" + (Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[citizen.m_parkedVehicle].m_position - pos).magnitude;\n\t\t\t\t\t}\n\t\t\t\t\tif (citizen.m_vehicle != 0) {\n\t\t\t\t\t\tlabelStr += \"\\nveh: \" + citizen.m_vehicle + \" dist: \" + (Singleton<VehicleManager>.instance.m_vehicles.m_buffer[citizen.m_vehicle].GetLastFramePosition() - pos).magnitude;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tVector2 dim = _counterStyle.CalcSize(new GUIContent(labelStr));\n\t\t\t\tRect labelRect = new Rect(screenPos.x - dim.x / 2f, screenPos.y - dim.y - 50f, dim.x, dim.y);\n\n\t\t\t\tGUI.Box(labelRect, labelStr, _counterStyle);\n\t\t\t}\n\t\t}\n\n\t\tprivate void _guiBuildings() {\n\t\t\tGUIStyle _counterStyle = new GUIStyle();\n\t\t\tArray16<Building> buildings = Singleton<BuildingManager>.instance.m_buildings;\n\t\t\tfor (int i = 1; i < buildings.m_size; ++i) {\n\t\t\t\tBuilding building = buildings.m_buffer[i];\n\t\t\t\tif (building.m_flags == Building.Flags.None)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tVector3 pos = building.m_position;\n\t\t\t\tVector3 screenPos;\n\t\t\t\tbool visible = WorldToScreenPoint(pos, out screenPos);\n\t\t\t\t\n\t\t\t\tif (! visible)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tvar camPos = Singleton<SimulationManager>.instance.m_simulationView.m_position;\n\t\t\t\tvar diff = pos - camPos;\n\t\t\t\tif (diff.magnitude > DebugCloseLod)\n\t\t\t\t\tcontinue; // do not draw if too distant\n\n\t\t\t\tvar zoom = 1.0f / diff.magnitude * 150f;\n\n\t\t\t\t_counterStyle.fontSize = (int)(10f * zoom);\n\t\t\t\t_counterStyle.normal.textColor = new Color(0f, 1f, 0f);\n\t\t\t\t//_counterStyle.normal.background = MakeTex(1, 1, new Color(0f, 0f, 0f, 0.4f));\n\n\t\t\t\tExtBuilding extBuilding = ExtBuildingManager.Instance.ExtBuildings[i];\n\n\t\t\t\tString labelStr = \"Building \" + i + \", PDemand: \" + extBuilding.parkingSpaceDemand + \", IncTDem: \" + extBuilding.incomingPublicTransportDemand + \", OutTDem: \" + extBuilding.outgoingPublicTransportDemand;\n\n\t\t\t\tVector2 dim = _counterStyle.CalcSize(new GUIContent(labelStr));\n\t\t\t\tRect labelRect = new Rect(screenPos.x - dim.x / 2f, screenPos.y - dim.y - 50f, dim.x, dim.y);\n\n\t\t\t\tGUI.Box(labelRect, labelStr, _counterStyle);\n\t\t\t}\n\t\t}\n\n\t\tnew internal Color GetToolColor(bool warning, bool error) {\n\t\t\treturn base.GetToolColor(warning, error);\n\t\t}\n\n\t\tinternal static int GetSegmentNumVehicleLanes(ushort segmentId, ushort? nodeId, out int numDirections, VehicleInfo.VehicleType vehicleTypeFilter) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tvar info = netManager.m_segments.m_buffer[segmentId].Info;\n\t\t\tvar curLaneId = netManager.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tvar laneIndex = 0;\n\n\t\t\tNetInfo.Direction? dir = null;\n\t\t\tNetInfo.Direction? dir2 = null;\n\t\t\t//NetInfo.Direction? dir3 = null;\n\n\t\t\tnumDirections = 0;\n\t\t\tHashSet<NetInfo.Direction> directions = new HashSet<NetInfo.Direction>();\n\n\t\t\tif (nodeId != null) {\n\t\t\t\tdir = (netManager.m_segments.m_buffer[segmentId].m_startNode == nodeId) ? NetInfo.Direction.Backward : NetInfo.Direction.Forward;\n\t\t\t\tdir2 = ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) == NetSegment.Flags.None) ? dir : NetInfo.InvertDirection((NetInfo.Direction)dir);\n\t\t\t\t//dir3 = TrafficPriorityManager.IsLeftHandDrive() ? NetInfo.InvertDirection((NetInfo.Direction)dir2) : dir2;\n\t\t\t}\n\n\t\t\tvar numLanes = 0;\n\n\t\t\twhile (laneIndex < info.m_lanes.Length && curLaneId != 0u) {\n\t\t\t\tif (((info.m_lanes[laneIndex].m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) != NetInfo.LaneType.None &&\n\t\t\t\t\t(info.m_lanes[laneIndex].m_vehicleType & vehicleTypeFilter) != VehicleInfo.VehicleType.None) &&\n\t\t\t\t\t(dir2 == null || info.m_lanes[laneIndex].m_finalDirection == dir2)) {\n\n\t\t\t\t\tif (!directions.Contains(info.m_lanes[laneIndex].m_finalDirection)) {\n\t\t\t\t\t\tdirections.Add(info.m_lanes[laneIndex].m_finalDirection);\n\t\t\t\t\t\t++numDirections;\n\t\t\t\t\t}\n\t\t\t\t\tnumLanes++;\n\t\t\t\t}\n\n\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\tlaneIndex++;\n\t\t\t}\n\n\t\t\treturn numLanes;\n\t\t}\n\t\t\n\t\tinternal static void CalculateSegmentCenterByDir(ushort segmentId, Dictionary<NetInfo.Direction, Vector3> segmentCenterByDir) {\n\t\t\tsegmentCenterByDir.Clear();\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tNetInfo segmentInfo = netManager.m_segments.m_buffer[segmentId].Info;\n\t\t\tuint curLaneId = netManager.m_segments.m_buffer[segmentId].m_lanes;\n\t\t\tDictionary<NetInfo.Direction, int> numCentersByDir = new Dictionary<NetInfo.Direction, int>();\n\t\t\tuint laneIndex = 0;\n\t\t\twhile (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) {\n\t\t\t\tif ((segmentInfo.m_lanes[laneIndex].m_laneType & (NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle)) == NetInfo.LaneType.None)\n\t\t\t\t\tgoto nextIter;\n\n\t\t\t\tNetInfo.Direction dir = segmentInfo.m_lanes[laneIndex].m_finalDirection;\n\t\t\t\tVector3 bezierCenter = netManager.m_lanes.m_buffer[curLaneId].m_bezier.Position(0.5f);\n\n\t\t\t\tif (!segmentCenterByDir.ContainsKey(dir)) {\n\t\t\t\t\tsegmentCenterByDir[dir] = bezierCenter;\n\t\t\t\t\tnumCentersByDir[dir] = 1;\n\t\t\t\t} else {\n\t\t\t\t\tsegmentCenterByDir[dir] += bezierCenter;\n\t\t\t\t\tnumCentersByDir[dir]++;\n\t\t\t\t}\n\n\t\t\t\tnextIter:\n\n\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\tlaneIndex++;\n\t\t\t}\n\n\t\t\tforeach (KeyValuePair<NetInfo.Direction, int> e in numCentersByDir) {\n\t\t\t\tsegmentCenterByDir[e.Key] /= (float)e.Value;\n\t\t\t}\n\t\t}\n\n\t\tpublic static Texture2D MakeTex(int width, int height, Color col) {\n\t\t\tvar pix = new Color[width * height];\n\n\t\t\tfor (var i = 0; i < pix.Length; i++)\n\t\t\t\tpix[i] = col;\n\n\t\t\tvar result = new Texture2D(width, height);\n\t\t\tresult.SetPixels(pix);\n\t\t\tresult.Apply();\n\n\t\t\treturn result;\n\t\t}\n\n\t\tpublic static Texture2D AdjustAlpha(Texture2D tex, float alpha) {\n\t\t\tColor[] texColors = tex.GetPixels();\n\t\t\tColor[] retPixels = new Color[texColors.Length];\n\n\t\t\tfor (int i = 0; i < texColors.Length; ++i) {\n\t\t\t\tretPixels[i] = new Color(texColors[i].r, texColors[i].g, texColors[i].b, texColors[i].a * alpha);\n\t\t\t}\n\n\t\t\tTexture2D ret = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false);\n\n\t\t\tret.SetPixels(retPixels);\n\t\t\tret.Apply();\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tinternal static bool IsMouseOver(Rect boundingBox) {\n\t\t\treturn boundingBox.Contains(Event.current.mousePosition);\n\t\t}\n\n\t\tinternal bool CheckClicked() {\n\t\t\tif (Input.GetMouseButtonDown(0) && !mouseClickProcessed) {\n\t\t\t\tmouseClickProcessed = true;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic void ShowTooltip(String text) {\n\t\t\tif (text == null)\n\t\t\t\treturn;\n\n\t\t\tUIView.library.ShowModal<ExceptionPanel>(\"ExceptionPanel\").SetMessage(\"Info\", text, false);\n\n\t\t\t/*tooltipStartFrame = currentFrame;\n\t\t\ttooltipText = text;\n\t\t\ttooltipWorldPos = position;*/\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/Translation.cs",
    "content": "﻿using ColossalFramework;\nusing ColossalFramework.Globalization;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing TrafficManager.State;\nusing TrafficManager.Util;\n\nnamespace TrafficManager.UI {\n\tpublic class Translation {\n\t\tpublic static readonly IDictionary<string, string> LANGUAGE_LABELS = new TinyDictionary<string, string>();\n\t\tpublic static readonly IList<string> AVAILABLE_LANGUAGE_CODES = new List<string>();\n\t\tpublic const string DEFAULT_LANGUAGE_CODE = \"en\";\n\n\t\tpublic const string TUTORIAL_KEY_PREFIX = \"TMPE_TUTORIAL_\";\n\t\tpublic const string TUTORIAL_HEAD_KEY_PREFIX = TUTORIAL_KEY_PREFIX + \"HEAD_\";\n\t\tpublic const string TUTORIAL_BODY_KEY_PREFIX = TUTORIAL_KEY_PREFIX + \"BODY_\";\n\n\t\tstatic Translation() {\n\t\t\tAVAILABLE_LANGUAGE_CODES.Clear();\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"de\");\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"en\");\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"es\");\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"fr\");\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"it\");\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"ja\");\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"ko\");\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"nl\");\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"pl\");\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"pt\");\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"ru\");\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"zh-tw\");\n\t\t\tAVAILABLE_LANGUAGE_CODES.Add(\"zh\");\n\n\t\t\tLANGUAGE_LABELS.Clear();\n\t\t\tLANGUAGE_LABELS[\"de\"] = \"Deutsch\";\n\t\t\tLANGUAGE_LABELS[\"en\"] = \"English\";\n\t\t\tLANGUAGE_LABELS[\"es\"] = \"Español\";\n\t\t\tLANGUAGE_LABELS[\"fr\"] = \"Français\";\n\t\t\tLANGUAGE_LABELS[\"it\"] = \"Italiano\";\n\t\t\tLANGUAGE_LABELS[\"ja\"] = \"日本語\";\n\t\t\tLANGUAGE_LABELS[\"ko\"] = \"한국의\";\n\t\t\tLANGUAGE_LABELS[\"nl\"] = \"Nederlands\";\n\t\t\tLANGUAGE_LABELS[\"pl\"] = \"Polski\";\n\t\t\tLANGUAGE_LABELS[\"pt\"] = \"Português\";\n\t\t\tLANGUAGE_LABELS[\"ru\"] = \"русский язык\";\n\t\t\tLANGUAGE_LABELS[\"zh-tw\"] = \"中文 (繁體)\";\n\t\t\tLANGUAGE_LABELS[\"zh\"] = \"中文 (简体)\";\n\t\t}\n\n\t\tprivate const string RESOURCES_PREFIX = \"TrafficManager.Resources.\";\n\t\tprivate static readonly string DEFAULT_TRANSLATION_FILENAME = \"lang.txt\";\n\n\t\tprivate static Dictionary<string, string> translations;\n\t\tprivate static string loadedLanguage = null;\n\n\t\tpublic static string GetString(string key) {\n\t\t\tLoadTranslations();\n\n\t\t\tstring ret = null;\n\t\t\tif (translations.TryGetValue(key, out ret)) {\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\treturn key;\n\t\t}\n\n\t\tpublic static bool HasString(string key) {\n\t\t\tLoadTranslations();\n\t\t\treturn translations.ContainsKey(key);\n\t\t}\n\n\t\tpublic static string GetTranslatedFileName(string filename) {\n\t\t\tstring language = GetCurrentLanguage();\n\t\t\tswitch (language) {\n\t\t\t\tcase \"jaex\":\n\t\t\t\t\tlanguage = \"ja\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"zh-cn\":\n\t\t\t\t\tlanguage = \"zh\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"kr\":\n\t\t\t\t\tlanguage = \"ko\";\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tstring translatedFilename = filename;\n\t\t\tif (language != DEFAULT_LANGUAGE_CODE) {\n\t\t\t\tint delimiterIndex = filename.Trim().LastIndexOf('.'); // file extension\n\n\t\t\t\ttranslatedFilename = \"\";\n\t\t\t\tif (delimiterIndex >= 0)\n\t\t\t\t\ttranslatedFilename = filename.Substring(0, delimiterIndex);\n\t\t\t\ttranslatedFilename += \"_\" + language.Trim().ToLower();\n\t\t\t\tif (delimiterIndex >= 0)\n\t\t\t\t\ttranslatedFilename += filename.Substring(delimiterIndex);\n\t\t\t}\n\n\t\t\tif (Assembly.GetExecutingAssembly().GetManifestResourceNames().Contains(RESOURCES_PREFIX + translatedFilename)) {\n\t\t\t\tLog._Debug($\"Translated file {translatedFilename} found\");\n                return translatedFilename;\n\t\t\t} else {\n\t\t\t\tif (language != null && !DEFAULT_LANGUAGE_CODE.Equals(language))\n\t\t\t\t\tLog.Warning($\"Translated file {translatedFilename} not found!\");\n\t\t\t\treturn filename;\n\t\t\t}\n\t\t}\n\n\t\tpublic static string GetCurrentLanguage() {\n\t\t\tstring lang = GlobalConfig.Instance.LanguageCode;\n\t\t\tif (lang != null) {\n\t\t\t\treturn lang;\n\t\t\t}\n\n\t\t\treturn LocaleManager.instance.language;\n\t\t}\n\n\t\tpublic static void SetCurrentLanguage(string lang) {\n\t\t\tif (lang != null && !LANGUAGE_LABELS.ContainsKey(lang)) {\n\t\t\t\tLog.Warning($\"Translation.SetCurrentLanguage: Invalid language code: {lang}\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tGlobalConfig.Instance.LanguageCode = lang;\n\t\t}\n\n\t\tprivate static void LoadTranslations() {\n\t\t\tstring currentLang = GetCurrentLanguage();\n\t\t\tif (translations == null || loadedLanguage == null || ! loadedLanguage.Equals(currentLang)) {\n\t\t\t\ttry {\n\t\t\t\t\tstring filename = RESOURCES_PREFIX + GetTranslatedFileName(DEFAULT_TRANSLATION_FILENAME);\n\t\t\t\t\tLog._Debug($\"Loading translations from file '{filename}'. Language={currentLang}\");\n\t\t\t\t\tstring[] lines;\n\t\t\t\t\tusing (Stream st = Assembly.GetExecutingAssembly().GetManifestResourceStream(filename)) {\n\t\t\t\t\t\tusing (StreamReader sr = new StreamReader(st)) {\n\t\t\t\t\t\t\tlines = sr.ReadToEnd().Split(new string[] { \"\\n\", \"\\r\\n\" }, StringSplitOptions.None);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ttranslations = new Dictionary<string, string>();\n\t\t\t\t\tforeach (string line in lines) {\n\t\t\t\t\t\tif (line == null || line.Trim().Length == 0) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tint delimiterIndex = line.Trim().IndexOf(' ');\n\t\t\t\t\t\tif (delimiterIndex > 0) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tstring translationKey = line.Substring(0, delimiterIndex);\n\t\t\t\t\t\t\t\tstring translationValue = line.Substring(delimiterIndex + 1).Trim().Replace(\"\\\\n\", \"\\n\");\n\t\t\t\t\t\t\t\ttranslations.Add(translationKey, translationValue);\n\t\t\t\t\t\t\t} catch (Exception) {\n\t\t\t\t\t\t\t\tLog.Warning($\"Failed to add translation for key {line.Substring(0, delimiterIndex)}, language {currentLang}. Possible duplicate?\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tloadedLanguage = currentLang;\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"Error while loading translations: {e.ToString()}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic static void ReloadTutorialTranslations() {\n\t\t\tLocale locale = (Locale)typeof(LocaleManager).GetField(\"m_Locale\", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(SingletonLite<LocaleManager>.instance);\n\t\t\tforeach (KeyValuePair<string, string> entry in translations) {\n\t\t\t\tif (!entry.Key.StartsWith(TUTORIAL_KEY_PREFIX)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tstring identifier;\n\t\t\t\tstring tutorialKey;\n\t\t\t\tif (entry.Key.StartsWith(TUTORIAL_HEAD_KEY_PREFIX)) {\n\t\t\t\t\tidentifier = \"TUTORIAL_ADVISER_TITLE\";\n\t\t\t\t\ttutorialKey = TUTORIAL_KEY_PREFIX + entry.Key.Substring(TUTORIAL_HEAD_KEY_PREFIX.Length);\n\t\t\t\t} else if (entry.Key.StartsWith(TUTORIAL_BODY_KEY_PREFIX)) {\n\t\t\t\t\tidentifier = \"TUTORIAL_ADVISER\";\n\t\t\t\t\ttutorialKey = TUTORIAL_KEY_PREFIX + entry.Key.Substring(TUTORIAL_BODY_KEY_PREFIX.Length);\n\t\t\t\t} else {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//Log._Debug($\"Adding tutorial translation for id {identifier}, key={tutorialKey} value={entry.Value}\");\n\t\t\t\tLocale.Key key = new Locale.Key() {\n\t\t\t\t\tm_Identifier = identifier,\n\t\t\t\t\tm_Key = tutorialKey\n\t\t\t\t};\n\n\t\t\t\tif (!locale.Exists(key)) {\n\t\t\t\t\tlocale.AddLocalizedString(key, entry.Value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tinternal static int getMenuWidth() {\n\t\t\tswitch (GetCurrentLanguage()) {\n\t\t\t\tcase null:\n\t\t\t\tcase \"en\":\n\t\t\t\tcase \"de\":\n\t\t\t\tdefault:\n\t\t\t\t\treturn 220;\n\t\t\t\tcase \"ru\":\n\t\t\t\tcase \"pl\":\n\t\t\t\t\treturn 260;\n\t\t\t\tcase \"es\":\n\t\t\t\tcase \"fr\":\n\t\t\t\tcase \"it\":\n\t\t\t\t\treturn 240;\n\t\t\t\tcase \"nl\":\n\t\t\t\t\treturn 270;\n\t\t\t}\n\t\t}\n\n\t\tinternal static void OnLevelUnloading() {\n\t\t\ttranslations = null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/TransportDemandViewMode.cs",
    "content": "﻿namespace TrafficManager.UI {\n    public enum TransportDemandViewMode {\n        Incoming,\n        Outgoing\n    }\n}"
  },
  {
    "path": "TLM/TLM/UI/UIBase.cs",
    "content": "using ColossalFramework.UI;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing TrafficManager.State;\nusing TrafficManager.TrafficLight;\nusing TrafficManager.UI.MainMenu;\nusing TrafficManager.Util;\nusing UnityEngine;\n\nnamespace TrafficManager.UI {\n\tpublic class UIBase : UICustomControl {\n\n\t\tpublic UIMainMenuButton MainMenuButton { get; private set; }\n\t\tpublic MainMenuPanel MainMenu { get; private set; }\n#if DEBUG\n\t\tpublic DebugMenuPanel DebugMenu { get; private set; }\n#endif\n\t\tpublic static TrafficManagerTool GetTrafficManagerTool(bool createIfRequired=true) {\n\t\t\tif (tool == null && createIfRequired) {\n\t\t\t\tLog.Info(\"Initializing traffic manager tool...\");\n\t\t\t\ttool = ToolsModifierControl.toolController.gameObject.GetComponent<TrafficManagerTool>() ??\n\t\t\t\t\t\t\t\t   ToolsModifierControl.toolController.gameObject.AddComponent<TrafficManagerTool>();\n\t\t\t\ttool.Initialize();\n\t\t\t}\n\n\t\t\treturn tool;\n\t\t}\n\t\tprivate static TrafficManagerTool tool = null;\n\t\tpublic static TrafficManagerMode ToolMode { get; set; }\n\n\t\tprivate bool _uiShown = false;\n\n\t\tpublic UIBase() {\n\t\t\tLog._Debug(\"##### Initializing UIBase.\");\n\n\t\t\t// Get the UIView object. This seems to be the top-level object for most\n\t\t\t// of the UI.\n\t\t\tvar uiView = UIView.GetAView();\n\n\t\t\t// Add a new button to the view.\n\t\t\tMainMenuButton = (UIMainMenuButton)uiView.AddUIComponent(typeof(UIMainMenuButton));\n\n\t\t\t// add the menu\n\t\t\tMainMenu = (MainMenuPanel)uiView.AddUIComponent(typeof(MainMenuPanel));\n\t\t\tMainMenu.gameObject.AddComponent<CustomKeyHandler>();\n#if DEBUG\n\t\t\tDebugMenu = (DebugMenuPanel)uiView.AddUIComponent(typeof(DebugMenuPanel));\n#endif\n\n\t\t\tToolMode = TrafficManagerMode.None;\n\t\t}\n\n\t\t~UIBase() {\n\t\t\tUnityEngine.Object.Destroy(MainMenuButton);\n\t\t\tUnityEngine.Object.Destroy(MainMenu);\n\t\t}\n\n\t\tpublic bool IsVisible() {\n\t\t\treturn _uiShown;\n\t\t}\n\n\t\tpublic void ToggleMainMenu() {\n\t\t\tif (IsVisible())\n\t\t\t\tClose();\n\t\t\telse\n\t\t\t\tShow();\n\t\t}\n\n\t\tinternal void RebuildMenu() {\n\t\t\tClose();\n\t\t\tif (MainMenu != null) {\n\t\t\t\tCustomKeyHandler keyHandler = MainMenu.GetComponent<CustomKeyHandler>();\n\t\t\t\tif(keyHandler != null)\n\t\t\t\tUnityEngine.Object.Destroy(keyHandler);\n\t\t\t\t\n\t\t\t\tUnityEngine.Object.Destroy(MainMenu);\n#if DEBUG\n\t\t\t\tUnityEngine.Object.Destroy(DebugMenu);\n#endif\n\t\t\t}\n\t\t\tvar uiView = UIView.GetAView();\n\t\t\tMainMenu = (MainMenuPanel)uiView.AddUIComponent(typeof(MainMenuPanel));\n\t\t\tMainMenu.gameObject.AddComponent<CustomKeyHandler>();\n#if DEBUG\n\t\t\tDebugMenu = (DebugMenuPanel)uiView.AddUIComponent(typeof(DebugMenuPanel));\n#endif\n\t\t}\n\n\t\tpublic void Show() {\n\t\t\ttry {\n\t\t\t\tToolsModifierControl.mainToolbar.CloseEverything();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.Error(\"Error on Show(): \" + e.ToString());\n\t\t\t}\n\n\t\t\tforeach (MenuButton button in GetMenu().Buttons) {\n\t\t\t\tbutton.UpdateProperties();\n\t\t\t}\n\t\t\tGetMenu().Show();\n\t\t\tTranslation.ReloadTutorialTranslations();\n\t\t\tTrafficManagerTool.ShowAdvisor(\"MainMenu\");\n#if DEBUG\n\t\t\tGetDebugMenu().Show();\n#endif\n\t\t\tSetToolMode(TrafficManagerMode.Activated);\n\t\t\t_uiShown = true;\n\t\t\tMainMenuButton.UpdateSprites();\n\t\t\tUIView.SetFocus(MainMenu);\n\t\t}\n\n\t\tpublic void Close() {\n\t\t\tvar uiView = UIView.GetAView();\n\t\t\tGetMenu().Hide();\n#if DEBUG\n\t\t\tGetDebugMenu().Hide();\n#endif\n\t\t\tTrafficManagerTool tmTool = GetTrafficManagerTool(false);\n\t\t\tif (tmTool != null) {\n\t\t\t\ttmTool.SetToolMode(UI.ToolMode.None);\n\t\t\t}\n\t\t\tSetToolMode(TrafficManagerMode.None);\n\t\t\t_uiShown = false;\n\t\t\tMainMenuButton.UpdateSprites();\n\t\t}\n\n\t\tinternal MainMenuPanel GetMenu() {\n\t\t\treturn MainMenu;\n\t\t}\n\n#if DEBUG\n\t\tinternal DebugMenuPanel GetDebugMenu() {\n\t\t\treturn DebugMenu;\n\t\t}\n#endif\n\n\t\tpublic static void SetToolMode(TrafficManagerMode mode) {\n\t\t\tif (mode == ToolMode) return;\n\n\t\t\tToolMode = mode;\n\n\t\t\tif (mode != TrafficManagerMode.None) {\n\t\t\t\tEnableTool();\n\t\t\t} else {\n\t\t\t\tDisableTool();\n\t\t\t}\n\t\t}\n\n\t\tpublic static void EnableTool() {\n\t\t\tLog._Debug(\"LoadingExtension.EnableTool: called\");\n\t\t\tTrafficManagerTool tmTool = GetTrafficManagerTool(true);\n\n\t\t\tToolsModifierControl.toolController.CurrentTool = tmTool;\n\t\t\tToolsModifierControl.SetTool<TrafficManagerTool>();\n\t\t}\n\n\t\tpublic static void DisableTool() {\n\t\t\tLog._Debug(\"LoadingExtension.DisableTool: called\");\n\t\t\tToolsModifierControl.toolController.CurrentTool = ToolsModifierControl.GetTool<DefaultTool>();\n\t\t\tToolsModifierControl.SetTool<DefaultTool>();\n\t\t}\n\n\t\tinternal static void ReleaseTool() {\n\t\t\tif (ToolMode != TrafficManagerMode.None) {\n\t\t\t\tToolMode = TrafficManagerMode.None;\n\t\t\t\tDestroyTool();\n\t\t\t}\n\t\t}\n\n\t\tprivate static void DestroyTool() {\n\t\t\tif (ToolsModifierControl.toolController != null) {\n\t\t\t\tToolsModifierControl.toolController.CurrentTool = ToolsModifierControl.GetTool<DefaultTool>();\n\t\t\t\tToolsModifierControl.SetTool<DefaultTool>();\n\n\t\t\t\tif (tool != null) {\n\t\t\t\t\tUnityEngine.Object.Destroy(tool);\n\t\t\t\t\ttool = null;\n\t\t\t\t}\n\t\t\t} else\n\t\t\t\tLog.Warning(\"LoadingExtensions.DestroyTool: ToolsModifierControl.toolController is null!\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/UIMainMenuButton.cs",
    "content": "﻿using ColossalFramework.Math;\nusing ColossalFramework.UI;\nusing CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.State;\nusing TrafficManager.Util;\nusing UnityEngine;\n\nnamespace TrafficManager.UI {\n\tpublic class UIMainMenuButton : UIButton, IObserver<GlobalConfig> {\n\t\tpublic const string MAIN_MENU_BUTTON_BG_BASE = \"TMPE_MainMenuButtonBgBase\";\n\t\tpublic const string MAIN_MENU_BUTTON_BG_HOVERED = \"TMPE_MainMenuButtonBgHovered\";\n\t\tpublic const string MAIN_MENU_BUTTON_BG_ACTIVE = \"TMPE_MainMenuButtonBgActive\";\n\t\tpublic const string MAIN_MENU_BUTTON_FG_BASE = \"TMPE_MainMenuButtonFgBase\";\n\t\tpublic const string MAIN_MENU_BUTTON_FG_HOVERED = \"TMPE_MainMenuButtonFgHovered\";\n\t\tpublic const string MAIN_MENU_BUTTON_FG_ACTIVE = \"TMPE_MainMenuButtonFgActive\";\n\n\t\tpublic const int BUTTON_WIDTH = 50;\n\t\tpublic const int BUTTON_HEIGHT = 50;\n\n\t\tpublic UIDragHandle Drag { get; private set; }\n\n\t\tIDisposable confDisposable;\n\n\t\tpublic override void Start() {\n\t\t\t// Place the button.\n\t\t\tOnUpdate(GlobalConfig.Instance);\n\n\t\t\tconfDisposable = GlobalConfig.Instance.Subscribe(this);\n\n\t\t\t// Set the atlas and background/foreground\n\t\t\tatlas = TextureUtil.GenerateLinearAtlas(\"TMPE_MainMenuButtonAtlas\", TextureResources.MainMenuButtonTexture2D, 6, new string[] {\n\t\t\t\tMAIN_MENU_BUTTON_BG_BASE,\n\t\t\t\tMAIN_MENU_BUTTON_BG_HOVERED,\n\t\t\t\tMAIN_MENU_BUTTON_BG_ACTIVE,\n\t\t\t\tMAIN_MENU_BUTTON_FG_BASE,\n\t\t\t\tMAIN_MENU_BUTTON_FG_HOVERED,\n\t\t\t\tMAIN_MENU_BUTTON_FG_ACTIVE\n\t\t\t});\n\t\t\t\n\t\t\tUpdateSprites();\n\n\t\t\t// Set the button dimensions.\n\t\t\twidth = BUTTON_WIDTH;\n\t\t\theight = BUTTON_HEIGHT;\n\n\t\t\t// Enable button sounds.\n\t\t\tplayAudioEvents = true;\n\n\t\t\tvar dragHandler = new GameObject(\"TMPE_MainButton_DragHandler\");\n\t\t\tdragHandler.transform.parent = transform;\n\t\t\tdragHandler.transform.localPosition = Vector3.zero;\n\t\t\tDrag = dragHandler.AddComponent<UIDragHandle>();\n\n\t\t\tDrag.width = width;\n\t\t\tDrag.height = height;\n\t\t\tDrag.enabled = !GlobalConfig.Instance.Main.MainMenuButtonPosLocked;\n        }\n\n\t\tpublic override void OnDestroy() {\n\t\t\tif (confDisposable != null) {\n\t\t\t\tconfDisposable.Dispose();\n\t\t\t}\n\t\t}\n\n\t\tinternal void SetPosLock(bool lck) {\n\t\t\tDrag.enabled = !lck;\n\t\t}\n\n\t\tprotected override void OnClick(UIMouseEventParameter p) {\n\t\t\tLog._Debug($\"Current tool: {ToolManager.instance.m_properties.CurrentTool}\");\n\t\t\tLoadingExtension.BaseUI.ToggleMainMenu();\n\t\t\tUpdateSprites();\n\t\t}\n\n\t\tprotected override void OnPositionChanged() {\n\t\t\tGlobalConfig config = GlobalConfig.Instance;\n\n\t\t\tbool posChanged = (config.Main.MainMenuButtonX != (int)absolutePosition.x || config.Main.MainMenuButtonY != (int)absolutePosition.y);\n\n\t\t\tif (posChanged) {\n\t\t\t\tLog._Debug($\"Button position changed to {absolutePosition.x}|{absolutePosition.y}\");\n\n\t\t\t\tconfig.Main.MainMenuButtonX = (int)absolutePosition.x;\n\t\t\t\tconfig.Main.MainMenuButtonY = (int)absolutePosition.y;\n\n\t\t\t\tGlobalConfig.WriteConfig();\n\t\t\t}\n\t\t\tbase.OnPositionChanged();\n\t\t}\n\n\t\tinternal void UpdateSprites() {\n\t\t\tif (! LoadingExtension.BaseUI.IsVisible()) {\n\t\t\t\tm_BackgroundSprites.m_Normal = m_BackgroundSprites.m_Disabled = m_BackgroundSprites.m_Focused = MAIN_MENU_BUTTON_BG_BASE;\n\t\t\t\tm_BackgroundSprites.m_Hovered = MAIN_MENU_BUTTON_BG_HOVERED;\n\t\t\t\tm_PressedBgSprite = MAIN_MENU_BUTTON_BG_ACTIVE;\n\n\t\t\t\tm_ForegroundSprites.m_Normal = m_ForegroundSprites.m_Disabled = m_ForegroundSprites.m_Focused = MAIN_MENU_BUTTON_FG_BASE;\n\t\t\t\tm_ForegroundSprites.m_Hovered = MAIN_MENU_BUTTON_FG_HOVERED;\n\t\t\t\tm_PressedFgSprite = MAIN_MENU_BUTTON_FG_ACTIVE;\n\t\t\t} else {\n\t\t\t\tm_BackgroundSprites.m_Normal = m_BackgroundSprites.m_Disabled = m_BackgroundSprites.m_Focused = m_BackgroundSprites.m_Hovered = MAIN_MENU_BUTTON_BG_ACTIVE;\n\t\t\t\tm_PressedBgSprite = MAIN_MENU_BUTTON_BG_HOVERED;\n\n\t\t\t\tm_ForegroundSprites.m_Normal = m_ForegroundSprites.m_Disabled = m_ForegroundSprites.m_Focused = m_ForegroundSprites.m_Hovered = MAIN_MENU_BUTTON_FG_ACTIVE;\n\t\t\t\tm_PressedFgSprite = MAIN_MENU_BUTTON_FG_HOVERED;\n\t\t\t}\n\t\t\tthis.Invalidate();\n\t\t}\n\n\t\tpublic void OnUpdate(GlobalConfig config) {\n\t\t\tUpdatePosition(new Vector2(config.Main.MainMenuButtonX, config.Main.MainMenuButtonY));\n\t\t}\n\n\t\tpublic void UpdatePosition(Vector2 pos) {\n\t\t\tRect rect = new Rect(pos.x, pos.y, BUTTON_WIDTH, BUTTON_HEIGHT);\n\t\t\tVector2 resolution = UIView.GetAView().GetScreenResolution();\n\t\t\tVectorUtil.ClampRectToScreen(ref rect, resolution);\n\t\t\tLog.Info($\"Setting main menu button position to [{pos.x},{pos.y}]\");\n\t\t\tabsolutePosition = rect.position;\n\t\t\tInvalidate();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/UI/UITransportDemand.cs",
    "content": "using System;\nusing System.Linq;\nusing ColossalFramework;\nusing ColossalFramework.UI;\nusing TrafficManager.Geometry;\nusing TrafficManager.TrafficLight;\nusing UnityEngine;\nusing TrafficManager.State;\nusing TrafficManager.Custom.PathFinding;\nusing System.Collections.Generic;\nusing TrafficManager.Manager;\nusing CSUtil.Commons;\n\nnamespace TrafficManager.UI {\n\tpublic class UITransportDemand : UIPanel {\n\t\tprivate UIButton switchViewModeButton;\n\t\tprivate UILabel viewModeLabel;\n\n\t\tpublic override void Start() {\n\t\t\tbase.Start();\n\n            var transportInfoViewPanel = GameObject.Find(\"(Library) PublicTransportInfoViewPanel\").GetComponent<PublicTransportInfoViewPanel>();\n            if (transportInfoViewPanel != null) {\n                Log._Debug($\"Public transport info view panel found.\");\n                transportInfoViewPanel.component.eventVisibilityChanged += new PropertyChangedEventHandler<bool>(this.ParentVisibilityChanged);\n            } else {\n                Log.Warning($\"Public transport info view panel NOT found.\");\n            }\n\n            isInteractive = true;\n            isVisible = false;\n\n            backgroundSprite = \"GenericPanel\";\n\t\t\tcolor = new Color32(75, 75, 135, 255);\n\t\t\twidth = 156;\n\t\t\theight = 48;\n            \n            relativePosition = new Vector3(540f, 10f);\n\n            viewModeLabel = AddUIComponent<UILabel>();\n            viewModeLabel.text = Translation.GetString(\"Outgoing_demand\");\n            viewModeLabel.relativePosition = new Vector3(3f, 33f);\n            viewModeLabel.textScale = 0.75f;\n\n            switchViewModeButton = _createButton(Translation.GetString(\"Switch_view\"), 3, 3, clickSwitchViewMode);\n\t\t}\n\n\t\tprivate UIButton _createButton(string text, int x, int y, MouseEventHandler eventClick) {\n\t\t\tvar button = AddUIComponent<UIButton>();\n\t\t\tbutton.textScale = 0.8f;\n\t\t\tbutton.width = 150f;\n\t\t\tbutton.height = 30;\n\t\t\tbutton.normalBgSprite = \"ButtonMenu\";\n\t\t\tbutton.disabledBgSprite = \"ButtonMenuDisabled\";\n\t\t\tbutton.hoveredBgSprite = \"ButtonMenuHovered\";\n\t\t\tbutton.focusedBgSprite = \"ButtonMenu\";\n\t\t\tbutton.pressedBgSprite = \"ButtonMenuPressed\";\n\t\t\tbutton.textColor = new Color32(255, 255, 255, 255);\n\t\t\tbutton.playAudioEvents = true;\n\t\t\tbutton.text = text;\n\t\t\tbutton.relativePosition = new Vector3(x, y);\n\t\t\tbutton.eventClick += eventClick;\n\n\t\t\treturn button;\n\t\t}\n\n\t\tprivate void clickSwitchViewMode(UIComponent component, UIMouseEventParameter eventParam) {\n\t\t\tif (TrafficManagerTool.CurrentTransportDemandViewMode == TransportDemandViewMode.Outgoing) {\n                viewModeLabel.text = Translation.GetString(\"Incoming_demand\");\n                TrafficManagerTool.CurrentTransportDemandViewMode = TransportDemandViewMode.Incoming;\n\t\t\t} else {\n                viewModeLabel.text = Translation.GetString(\"Outgoing_demand\");\n                TrafficManagerTool.CurrentTransportDemandViewMode = TransportDemandViewMode.Outgoing;\n            }\n\t\t}\n\n        private void ParentVisibilityChanged(UIComponent component, bool value) {\n            Log._Debug($\"Public transport info view panel changed visibility: {value}\");\n            if (value && Options.prohibitPocketCars) {\n\t\t\t\tTrafficManagerTool.CurrentTransportDemandViewMode = TransportDemandViewMode.Outgoing;\n\t\t\t\tif (viewModeLabel != null)\n\t\t\t\t\tviewModeLabel.text = Translation.GetString(\"Outgoing_demand\");\n\t\t\t\tif (switchViewModeButton != null)\n\t\t\t\t\tswitchViewModeButton.text = Translation.GetString(\"Switch_view\");\n\t\t\t\tthis.Show();\n\t\t\t} else\n                this.Hide();\n        }\n    }\n}\n"
  },
  {
    "path": "TLM/TLM/Util/GenericObservable.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\n\nnamespace TrafficManager.Util {\n\tpublic abstract class GenericObservable<T> : IObservable<T> {\n\t\t/// <summary>\n\t\t/// Holds a list of observers which are being notified as soon as the managed node's geometry is updated (but not neccessarily modified)\n\t\t/// </summary>\n\t\tprotected List<IObserver<T>> Observers = new List<IObserver<T>>();\n\n\t\t/// <summary>\n\t\t/// Lock object. Acquire this before accessing the HashSets.\n\t\t/// </summary>\n\t\tprotected object ObserverLock = new object();\n\n\t\t/// <summary>\n\t\t/// Registers an observer.\n\t\t/// </summary>\n\t\t/// <param name=\"observer\"></param>\n\t\t/// <returns>An unsubscriber</returns>\n\t\tpublic IDisposable Subscribe(IObserver<T> observer) {\n\t\t\t//Log._Debug($\"GenericObserable.Subscribe: Subscribing observer {observer} to observable {this}\");\n\t\t\ttry {\n\t\t\t\tMonitor.Enter(ObserverLock);\n\t\t\t\tObservers.Add(observer);\n\t\t\t} finally {\n\t\t\t\tMonitor.Exit(ObserverLock);\n\t\t\t}\n\t\t\treturn new GenericUnsubscriber<T>(Observers, observer, ObserverLock);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Notifies all observers that the observable object' state has changed\n\t\t/// </summary>\n\t\tpublic virtual void NotifyObservers(T subject) {\n\t\t\t//Log._Debug($\"GenericObserable.NotifyObservers: Notifying observers of observable {this}\");\n\n\t\t\tList<IObserver<T>> myObservers = new List<IObserver<T>>(Observers); // in case somebody unsubscribes while iterating over subscribers\n\t\t\tforeach (IObserver<T> observer in myObservers) {\n\t\t\t\ttry {\n\t\t\t\t\t//Log._Debug($\"GenericObserable.NotifyObservers: Notifying observer {observer} of observable {this}\");\n\t\t\t\t\tobserver.OnUpdate(subject);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.Error($\"GenericObserable.NotifyObservers: An exception occured while notifying an observer of observable {this}: {e}\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Util/GenericUnsubscriber.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Threading;\n\nnamespace TrafficManager.Util {\n\tpublic class GenericUnsubscriber<T> : IDisposable {\n\t\tprivate List<IObserver<T>> observers;\n\t\tprivate IObserver<T> observer;\n\t\tpublic object lck;\n\n\t\tpublic GenericUnsubscriber(List<IObserver<T>> observers, IObserver<T> observer, object lck) {\n\t\t\tthis.observers = observers;\n\t\t\tthis.observer = observer;\n\t\t\tthis.lck = lck;\n\t\t}\n\n\t\tpublic void Dispose() {\n\t\t\tif (observer != null) {\n\t\t\t\ttry {\n\t\t\t\t\tMonitor.Enter(lck);\n\t\t\t\t\tobservers.Remove(observer);\n\t\t\t\t} finally {\n\t\t\t\t\tMonitor.Exit(lck);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Util/IObservable.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace TrafficManager.Util {\n\tpublic interface IObservable<T> {\n\t\tIDisposable Subscribe(IObserver<T> observer); \n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Util/IObserver.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace TrafficManager.Util {\n\tpublic interface IObserver<T> {\n\t\tvoid OnUpdate(T subject);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Util/IVisitor.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace TrafficManager.Util {\n\tpublic interface IVisitor<Target> {\n\t\tbool Visit(Target target);\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Util/LoopUtil.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Util {\n\tpublic static class LoopUtil {\n\t\tpublic delegate bool TwoDimLoopHandler(int x, int y);\n\n\t\tpublic static void SpiralLoop(int xCenter, int yCenter, int width, int height, TwoDimLoopHandler handler) {\n\t\t\tSpiralLoop(width, height, delegate(int x, int y) {\n\t\t\t\treturn handler(xCenter + x, yCenter + y);\n\t\t\t});\n\t\t}\n\n\t\tpublic static void SpiralLoop(int width, int height, TwoDimLoopHandler handler) {\n\t\t\tint x, y, dx, dy;\n\t\t\tx = y = dx = 0;\n\t\t\tdy = -1;\n\t\t\tint t = width > height ? width : height;\n\t\t\tint maxI = t * t;\n\t\t\tfor (int i = 0; i < maxI; i++) {\n\t\t\t\tif ((-width / 2 <= x) && (x <= width / 2) && (-height / 2 <= y) && (y <= height / 2)) {\n\t\t\t\t\tif (! handler(x, y))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif ((x == y) || ((x < 0) && (x == -y)) || ((x > 0) && (x == 1 - y))) {\n\t\t\t\t\tt = dx;\n\t\t\t\t\tdx = -dy;\n\t\t\t\t\tdy = t;\n\t\t\t\t}\n\t\t\t\tx += dx;\n\t\t\t\ty += dy;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Util/ModsCompatibilityChecker.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing ColossalFramework.PlatformServices;\nusing ColossalFramework.UI;\nusing CSUtil.Commons;\nusing TrafficManager.UI;\nusing UnityEngine;\n\nnamespace TrafficManager.Util {\n    public class ModsCompatibilityChecker {\n        //TODO include %APPDATA% mods folder\n\n        private const string RESOURCES_PREFIX = \"TrafficManager.Resources.\";\n        private const string DEFAULT_INCOMPATIBLE_MODS_FILENAME = \"incompatible_mods.txt\";\n        private readonly ulong[] userModList;\n        private readonly Dictionary<ulong, string> incompatibleModList;\n\n        public ModsCompatibilityChecker() {\n            incompatibleModList = LoadIncompatibleModList();\n            userModList = GetUserModsList();\n        }\n\n        public void PerformModCheck() {\n            Log.Info(\"Performing incompatible mods check\");\n            Dictionary<ulong, string> incompatibleMods = new Dictionary<ulong, string>();\n            for (int i = 0; i < userModList.Length; i++) {\n                string incompatibleModName;\n                if (incompatibleModList.TryGetValue(userModList[i], out incompatibleModName)) {\n                    incompatibleMods.Add(userModList[i], incompatibleModName);\n                }\n            }\n\n            if (incompatibleMods.Count > 0) {\n                Log.Warning(\"Incompatible mods detected! Count: \" + incompatibleMods.Count);\n                IncompatibleModsPanel panel = UIView.GetAView().AddUIComponent(typeof(IncompatibleModsPanel)) as IncompatibleModsPanel;\n                panel.IncompatibleMods = incompatibleMods;\n                panel.Initialize();\n                UIView.PushModal(panel);\n                UIView.SetFocus(panel);\n            } else {\n                Log.Info(\"No incompatible mods detected\");\n            }\n        }\n\n        private Dictionary<ulong, string> LoadIncompatibleModList() {\n            Dictionary<ulong, string> incompatibleMods = new Dictionary<ulong, string>();\n            string[] lines;\n            using (Stream st = Assembly.GetExecutingAssembly().GetManifestResourceStream(RESOURCES_PREFIX + DEFAULT_INCOMPATIBLE_MODS_FILENAME)) {\n                using (StreamReader sr = new StreamReader(st)) {\n                    lines = sr.ReadToEnd().Split(new string[] {\"\\n\", \"\\r\\n\"}, StringSplitOptions.None);\n                }\n            }\n\n            for (int i = 0; i < lines.Length; i++) {\n                string[] strings = lines[i].Split(';');\n                ulong steamId;\n                if (ulong.TryParse(strings[0], out steamId)) {\n                    incompatibleMods.Add(steamId, strings[1]);\n                }\n            }\n\n            return incompatibleMods;\n        }\n\n        private ulong[] GetUserModsList() {\n            PublishedFileId[] ids = ContentManagerPanel.subscribedItemsTable.ToArray();\n            return ids.Select(id => id.AsUInt64).ToArray();\n        }\n    }\n}"
  },
  {
    "path": "TLM/TLM/Util/README.md",
    "content": "# TM:PE -- /Util\nUtility classes.\n## Classes\n- **GenericUnsubscriber**: Allows an observer to unsubscribe from an observed object.\n- **IObservable**: Describes an object that can be observed.\n- **IObserver**: Describes an object that can observe observable objects.\n- **IVisitor**: Generic visitor\n- **RedirectionHelper**: Helper methods for detouring.\n- **SegmentLaneTraverser**: Allows a visitor to traverse segment lanes.\n- **SegmentTraverser**: Allows a visitor to traverse segments.\n- **TextureUtil**: Texture utilities.\n- **TinyDictionary**: A dictionary implementation for tiny mappings. Not thread-safe."
  },
  {
    "path": "TLM/TLM/Util/RedirectionHelper.cs",
    "content": "﻿/*\nThe MIT License (MIT)\nCopyright (c) 2015 Sebastian Schöner\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\nusing CSUtil.Commons;\nusing System;\nusing System.Reflection;\n\nnamespace TrafficManager.Util\n{\n    public struct RedirectCallsState\n    {\n        public byte A, B, C, D, E;\n        public ulong F;\n    }\n\n    /// <summary>\n    /// Helper class to deal with detours. This version is for Unity 5 x64 on Windows.\n    /// We provide three different methods of detouring.\n    /// </summary>\n    public static class RedirectionHelper\n    {\n        /// <summary>\n        /// Redirects all calls from method 'from' to method 'to'.\n        /// </summary>\n        /// <param name=\"from\"></param>\n        /// <param name=\"to\"></param>\n        public static RedirectCallsState RedirectCalls(MethodInfo from, MethodInfo to)\n        {\n\t\t\t// GetFunctionPointer enforces compilation of the method.\n\t\t\tvar fptr1 = from.MethodHandle.GetFunctionPointer();\n            var fptr2 = to.MethodHandle.GetFunctionPointer();\n\n\t\t\tLog._Debug($\"RedirectionHeler.RedirectCalls({from.Name}, {to.Name}) called. IsRedirected1={IsRedirected(fptr1, fptr2)} IsRedirected2={IsRedirected(from.MethodHandle.GetFunctionPointer(), to.MethodHandle.GetFunctionPointer())}\");\n\n\t\t\tLog._Debug($\"RedirectionHeler.RedirectCalls({from.Name}, {to.Name}): before redir. fptr1_fnc={GetRedir(fptr1).ArrayToString()} fptr1={(ulong)fptr1.ToInt64()} fptr2={(ulong)fptr2.ToInt64()}\");\n\n\t\t\tRedirectCallsState ret = PatchJumpTo(fptr1, fptr2);\n\n\t\t\tLog._Debug($\"RedirectionHeler.RedirectCalls({from.Name}, {to.Name}): after redir. fptr1_fnc={GetRedir(fptr1).ArrayToString()} fptr1={(ulong)fptr1.ToInt64()} fptr2={(ulong)fptr2.ToInt64()}\");\n\n\t\t\tLog._Debug($\"RedirectionHeler.RedirectCalls({from.Name}, {to.Name}) finished. IsRedirected1={IsRedirected(fptr1, fptr2)} IsRedirected2={IsRedirected(from.MethodHandle.GetFunctionPointer(), to.MethodHandle.GetFunctionPointer())}\");\n\n\t\t\treturn ret;\n\t\t}\n\n        public static void RevertRedirect(MethodInfo from, RedirectCallsState state)\n        {\n            var fptr1 = from.MethodHandle.GetFunctionPointer();\n            RevertJumpTo(fptr1, state);\n        }\n\n\t\tpublic static bool IsRedirected(MethodInfo from, MethodInfo to) {\n\t\t\tvar fptr1 = from.MethodHandle.GetFunctionPointer();\n\t\t\tvar fptr2 = to.MethodHandle.GetFunctionPointer();\n\t\t\treturn IsRedirected(fptr1, fptr2);\n\t\t}\n\n\t\tprivate static byte[] GetRedir(IntPtr ptr) {\n\t\t\tbyte[] ret = new byte[13];\n\t\t\tunsafe\n\t\t\t{\n\t\t\t\tbyte* pointer = (byte*)ptr.ToPointer();\n\t\t\t\tfor (int i = 0; i < 13; ++i) {\n\t\t\t\t\tret[i] = *(pointer + i);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\n\t\tprivate static bool IsRedirected(IntPtr site, IntPtr target) {\n\t\t\tunsafe {\n\t\t\t\tbyte* sitePtr = (byte*)site.ToPointer();\n\t\t\t\treturn *sitePtr == 0x49 &&\n\t\t\t\t\t*(sitePtr + 1) == 0xBB &&\n\t\t\t\t\t*((ulong*)(sitePtr + 2)) == (ulong)target.ToInt64() &&\n\t\t\t\t\t*(sitePtr + 10) == 0x41 &&\n\t\t\t\t\t*(sitePtr + 11) == 0xFF &&\n\t\t\t\t\t*(sitePtr + 12) == 0xE3;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Primitive patching. Inserts a jump to 'target' at 'site'. Works even if both methods'\n\t\t/// callers have already been compiled.\n\t\t/// </summary>\n\t\t/// <param name=\"site\"></param>\n\t\t/// <param name=\"target\"></param>\n\t\tprivate static RedirectCallsState PatchJumpTo(IntPtr site, IntPtr target)\n        {\n\t\t\tRedirectCallsState state = new RedirectCallsState();\n\n            // R11 is volatile.\n            unsafe\n            {\n                byte* sitePtr = (byte*)site.ToPointer();\n                state.A = *sitePtr;\n                state.B = *(sitePtr + 1);\n                state.C = *(sitePtr + 10);\n                state.D = *(sitePtr + 11);\n                state.E = *(sitePtr + 12);\n                state.F = *((ulong*)(sitePtr + 2));\n\n                *sitePtr = 0x49; // mov r11, target\n                *(sitePtr + 1) = 0xBB;\n                *((ulong*)(sitePtr + 2)) = (ulong)target.ToInt64();\n                *(sitePtr + 10) = 0x41; // jmp r11\n                *(sitePtr + 11) = 0xFF;\n                *(sitePtr + 12) = 0xE3;\n            }\n\n            return state;\n        }\n\n        private static void RevertJumpTo(IntPtr site, RedirectCallsState state)\n        {\n            unsafe\n            {\n                byte* sitePtr = (byte*)site.ToPointer();\n                *sitePtr = state.A; // mov r11, target\n                *(sitePtr + 1) = state.B;\n                *((ulong*)(sitePtr + 2)) = state.F;\n                *(sitePtr + 10) = state.C; // jmp r11\n                *(sitePtr + 11) = state.D;\n                *(sitePtr + 12) = state.E;\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "TLM/TLM/Util/SegmentLaneTraverser.cs",
    "content": "﻿using CSUtil.Commons;\nusing GenericGameBridge.Service;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing TrafficManager.Geometry;\nusing static TrafficManager.Util.SegmentTraverser;\n\nnamespace TrafficManager.Util {\n\tpublic class SegmentLaneTraverser {\n\t\tpublic delegate bool SegmentLaneVisitor(SegmentLaneVisitData data);\n\n\t\t[Flags]\n\t\tpublic enum LaneStopCriterion {\n\t\t\t/// <summary>\n\t\t\t/// Traversal stops when the whole network has been visited\n\t\t\t/// </summary>\n\t\t\tNone = 0,\n\t\t\t/// <summary>\n\t\t\t/// Traversal stops when a segment consists of a different number of filtered lanes than the initial segment\n\t\t\t/// </summary>\n\t\t\tLaneCount = 1\n\t\t}\n\n\t\tpublic class SegmentLaneVisitData {\n\t\t\t/// <summary>\n\t\t\t/// Segment visit data\n\t\t\t/// </summary>\n\t\t\tpublic SegmentVisitData segVisitData;\n\n\t\t\t/// <summary>\n\t\t\t/// Iteration index\n\t\t\t/// </summary>\n\t\t\tpublic int sortedLaneIndex;\n\n\t\t\t/// <summary>\n\t\t\t/// current traversed lane position\n\t\t\t/// </summary>\n\t\t\tpublic LanePos curLanePos;\n\n\t\t\t/// <summary>\n\t\t\t/// matching initial lane position\n\t\t\t/// </summary>\n\t\t\tpublic LanePos initLanePos;\n\n\t\t\tpublic SegmentLaneVisitData(SegmentVisitData segVisitData, int sortedLaneIndex, LanePos curLanePos, LanePos initLanePos) {\n\t\t\t\tthis.segVisitData = segVisitData;\n\t\t\t\tthis.sortedLaneIndex = sortedLaneIndex;\n\t\t\t\tthis.curLanePos = curLanePos;\n\t\t\t\tthis.initLanePos = initLanePos;\n\t\t\t}\n\t\t}\n\n\t\tpublic static void Traverse(ushort initialSegmentId, TraverseDirection direction, TraverseSide side, LaneStopCriterion laneStopCrit, SegmentStopCriterion segStopCrit, NetInfo.LaneType? laneTypeFilter, VehicleInfo.VehicleType? vehicleTypeFilter, SegmentLaneVisitor laneVisitor) {\n\t\t\tIList<LanePos> initialSortedLanes = null;\n\n\t\t\tSegmentTraverser.Traverse(initialSegmentId, direction, side, segStopCrit, delegate(SegmentVisitData segData) {\n\t\t\t\tbool isInitialSeg = segData.initial;\n\t\t\t\tbool reverse = !isInitialSeg && segData.viaStartNode == segData.viaInitialStartNode;\n\t\t\t\tbool ret = false;\n\t\t\t\tConstants.ServiceFactory.NetService.ProcessSegment(segData.curGeo.SegmentId, delegate (ushort segmentId, ref NetSegment segment) {\n\t\t\t\t\tLog._Debug($\"SegmentLaneTraverser: Reached segment {segmentId}: isInitialSeg={isInitialSeg} viaStartNode={segData.viaStartNode} viaInitialStartNode={segData.viaInitialStartNode} reverse={reverse}\");\n\t\t\t\t\tIList <LanePos> sortedLanes = Constants.ServiceFactory.NetService.GetSortedLanes(segmentId, ref segment, null, laneTypeFilter, vehicleTypeFilter, reverse);\n\n\t\t\t\t\tif (isInitialSeg) {\n\t\t\t\t\t\tinitialSortedLanes = sortedLanes;\n\t\t\t\t\t} else if (initialSortedLanes == null) {\n\t\t\t\t\t\tthrow new ApplicationException(\"Initial list of sorted lanes not set.\");\n\t\t\t\t\t} else if (sortedLanes.Count != initialSortedLanes.Count && (laneStopCrit & LaneStopCriterion.LaneCount) != LaneStopCriterion.None) {\n\t\t\t\t\t\tLog._Debug($\"SegmentLaneTraverser: Stop criterion reached @ {segmentId}: {sortedLanes.Count} current vs. {initialSortedLanes.Count} initial lanes\");\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (int i = 0; i < sortedLanes.Count; ++i) {\n\t\t\t\t\t\tLog._Debug($\"SegmentLaneTraverser: Traversing segment lane {sortedLanes[i].laneIndex} @ {segmentId} (id {sortedLanes[i].laneId}, pos {sortedLanes[i].position})\");\n\n\t\t\t\t\t\tif (!laneVisitor(new SegmentLaneVisitData(segData, i, sortedLanes[i], initialSortedLanes[i]))) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tret = true;\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t\treturn ret;\n\t\t\t});\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Util/SegmentTraverser.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\nusing TrafficManager.Custom.AI;\nusing TrafficManager.Geometry;\nusing TrafficManager.Geometry.Impl;\n\nnamespace TrafficManager.Util {\n\tpublic class SegmentTraverser {\n\t\t[Flags]\n\t\tpublic enum TraverseDirection {\n\t\t\tNone = 0,\n\t\t\tIncoming = 1,\n\t\t\tOutgoing = 1 << 1,\n\t\t\tAnyDirection = Incoming | Outgoing\n\t\t}\n\n\t\t[Flags]\n\t\tpublic enum TraverseSide {\n\t\t\tNone = 0,\n\t\t\tLeft = 1,\n\t\t\tStraight = 1 << 1,\n\t\t\tRight = 1 << 2,\n\t\t\tAnySide = Left | Straight | Right\n\t\t}\n\n\t\tpublic delegate bool SegmentVisitor(SegmentVisitData data);\n\n\t\t[Flags]\n\t\tpublic enum SegmentStopCriterion {\n\t\t\t/// <summary>\n\t\t\t/// Traversal stops when the whole network has been visited\n\t\t\t/// </summary>\n\t\t\tNone = 0,\n\t\t\t/// <summary>\n\t\t\t/// Traversal stops when a node with more than two connected segments has been reached\n\t\t\t/// </summary>\n\t\t\tJunction = 1,\n\t\t}\n\n\t\tpublic class SegmentVisitData {\n\t\t\t/// <summary>\n\t\t\t/// Previously visited segment geometry\n\t\t\t/// </summary>\n\t\t\tpublic SegmentGeometry prevGeo;\n\n\t\t\t/// <summary>\n\t\t\t/// Current segment geometry\n\t\t\t/// </summary>\n\t\t\tpublic SegmentGeometry curGeo;\n\n\t\t\t/// <summary>\n\t\t\t/// If true the current segment geometry has been reached on a path via the initial segment's start node\n\t\t\t/// </summary>\n\t\t\tpublic bool viaInitialStartNode;\n\n\t\t\t/// <summary>\n\t\t\t/// If true the current segment geometry has been reached on a path via the current segment's start node\n\t\t\t/// </summary>\n\t\t\tpublic bool viaStartNode;\n\n\t\t\t/// <summary>\n\t\t\t/// If true this is the initial segment\n\t\t\t/// </summary>\n\t\t\tpublic bool initial;\n\n\t\t\tpublic SegmentVisitData(SegmentGeometry prevGeo, SegmentGeometry curGeo, bool viaInitialStartNode, bool viaStartNode, bool initial) {\n\t\t\t\tthis.prevGeo = prevGeo;\n\t\t\t\tthis.curGeo = curGeo;\n\t\t\t\tthis.viaInitialStartNode = viaInitialStartNode;\n\t\t\t\tthis.viaStartNode = viaStartNode;\n\t\t\t\tthis.initial = initial;\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Performs a Depth-First traversal over the cached segment geometry structure. At each traversed segment, the given `visitor` is notified. It then can update the current `state`.\n\t\t/// </summary>\n\t\t/// <param name=\"initialSegmentGeometry\">Specifies the segment at which the traversal should start.</param>\n\t\t/// <param name=\"nextNodeIsStartNode\">Specifies if the next node to traverse is the start node of the initial segment.</param>\n\t\t/// <param name=\"direction\">Specifies if traffic should be able to flow towards the initial segment (Incoming) or should be able to flow from the initial segment (Outgoing) or in both directions (Both).</param>\n\t\t/// <param name=\"maximumDepth\">Specifies the maximum depth to explore. At a depth of 0, no segment will be traversed (event the initial segment will be omitted).</param>\n\t\t/// <param name=\"visitor\">Specifies the stateful visitor that should be notified as soon as a traversable segment (which has not been traversed before) is found.</param>\n\t\tpublic static void Traverse(ushort initialSegmentId, TraverseDirection direction, TraverseSide side, SegmentStopCriterion stopCrit, SegmentVisitor visitor) {\n\t\t\tSegmentGeometry initialSegGeometry = SegmentGeometry.Get(initialSegmentId);\n\t\t\tif (initialSegGeometry == null)\n\t\t\t\treturn;\n\n\t\t\tLog._Debug($\"SegmentTraverser: Traversing initial segment {initialSegGeometry.SegmentId}\");\n\t\t\tif (visitor(new SegmentVisitData(initialSegGeometry, initialSegGeometry, false, false, true))) {\n\t\t\t\tHashSet<ushort> visitedSegmentIds = new HashSet<ushort>();\n\t\t\t\tvisitedSegmentIds.Add(initialSegmentId);\n\n\t\t\t\tTraverseRec(initialSegGeometry, true, true, direction, side, stopCrit, visitor, visitedSegmentIds);\n\t\t\t\tTraverseRec(initialSegGeometry, false, false, direction, side, stopCrit, visitor, visitedSegmentIds);\n\t\t\t}\n\t\t\tLog._Debug($\"SegmentTraverser: Traversal finished.\");\n\t\t}\n\n\t\tprivate static void TraverseRec(SegmentGeometry prevSegGeometry, bool exploreStartNode, bool viaInitialStartNode, TraverseDirection direction, TraverseSide side, SegmentStopCriterion stopCrit, SegmentVisitor visitor, HashSet<ushort> visitedSegmentIds) {\n\t\t\tLog._Debug($\"SegmentTraverser: Traversing segment {prevSegGeometry.SegmentId}\");\n\n\t\t\t// collect next segment ids to traverse\n\n\t\t\tif (direction == TraverseDirection.None) {\n\t\t\t\tthrow new ArgumentException($\"Invalid direction {direction} given.\");\n\t\t\t}\n\t\t\t\n\t\t\tif (side == TraverseSide.None) {\n\t\t\t\tthrow new ArgumentException($\"Invalid side {side} given.\");\n\t\t\t}\n\n\t\t\tHashSet<ushort> nextSegmentIds = new HashSet<ushort>();\n\t\t\tswitch (direction) {\n\t\t\t\tcase TraverseDirection.AnyDirection:\n\t\t\t\tdefault:\n\t\t\t\t\tif (side == TraverseSide.AnySide) {\n\t\t\t\t\t\tnextSegmentIds.UnionWith(prevSegGeometry.GetConnectedSegments(exploreStartNode));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif ((side & TraverseSide.Left) != TraverseSide.None) {\n\t\t\t\t\t\t\tnextSegmentIds.UnionWith(prevSegGeometry.GetLeftSegments(exploreStartNode));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((side & TraverseSide.Straight) != TraverseSide.None) {\n\t\t\t\t\t\t\tnextSegmentIds.UnionWith(prevSegGeometry.GetStraightSegments(exploreStartNode));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((side & TraverseSide.Right) != TraverseSide.None) {\n\t\t\t\t\t\t\tnextSegmentIds.UnionWith(prevSegGeometry.GetRightSegments(exploreStartNode));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase TraverseDirection.Incoming:\n\t\t\t\t\tif (side == TraverseSide.AnySide) {\n\t\t\t\t\t\tnextSegmentIds.UnionWith(prevSegGeometry.GetIncomingSegments(exploreStartNode));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif ((side & TraverseSide.Left) != TraverseSide.None) {\n\t\t\t\t\t\t\tnextSegmentIds.UnionWith(prevSegGeometry.GetIncomingLeftSegments(exploreStartNode));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((side & TraverseSide.Straight) != TraverseSide.None) {\n\t\t\t\t\t\t\tnextSegmentIds.UnionWith(prevSegGeometry.GetIncomingStraightSegments(exploreStartNode));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((side & TraverseSide.Right) != TraverseSide.None) {\n\t\t\t\t\t\t\tnextSegmentIds.UnionWith(prevSegGeometry.GetIncomingRightSegments(exploreStartNode));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase TraverseDirection.Outgoing:\n\t\t\t\t\tif (side == TraverseSide.AnySide) {\n\t\t\t\t\t\tnextSegmentIds.UnionWith(prevSegGeometry.GetOutgoingSegments(exploreStartNode));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif ((side & TraverseSide.Left) != TraverseSide.None) {\n\t\t\t\t\t\t\tnextSegmentIds.UnionWith(prevSegGeometry.GetOutgoingLeftSegments(exploreStartNode));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((side & TraverseSide.Straight) != TraverseSide.None) {\n\t\t\t\t\t\t\tnextSegmentIds.UnionWith(prevSegGeometry.GetOutgoingStraightSegments(exploreStartNode));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((side & TraverseSide.Right) != TraverseSide.None) {\n\t\t\t\t\t\t\tnextSegmentIds.UnionWith(prevSegGeometry.GetOutgoingRightSegments(exploreStartNode));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tnextSegmentIds.Remove(0);\n\n\t\t\tLog._Debug($\"SegmentTraverser: Fetched next segments to traverse: {nextSegmentIds.CollectionToString()}\");\n\n\t\t\tif (nextSegmentIds.Count >= 2 && (stopCrit & SegmentStopCriterion.Junction) != SegmentStopCriterion.None) {\n\t\t\t\tLog._Debug($\"SegmentTraverser: Stop criterion reached @ {prevSegGeometry.SegmentId}: {nextSegmentIds.Count} connected segments\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tushort prevNodeId = prevSegGeometry.GetNodeId(exploreStartNode);\n\n\t\t\t// explore next segments\n\t\t\tforeach (ushort nextSegmentId in nextSegmentIds) {\n\t\t\t\tif (nextSegmentId == 0 || visitedSegmentIds.Contains(nextSegmentId))\n\t\t\t\t\tcontinue;\n\t\t\t\tvisitedSegmentIds.Add(nextSegmentId);\n\n\t\t\t\tSegmentGeometry nextSegGeometry = SegmentGeometry.Get(nextSegmentId);\n\t\t\t\tif (nextSegGeometry != null) {\n\t\t\t\t\tLog._Debug($\"SegmentTraverser: Traversing segment {nextSegGeometry.SegmentId}\");\n\n\t\t\t\t\tif (visitor(new SegmentVisitData(prevSegGeometry, nextSegGeometry, viaInitialStartNode, prevNodeId == nextSegGeometry.StartNodeId(), false))) {\n\t\t\t\t\t\tbool nextNodeIsStartNode = nextSegGeometry.StartNodeId() != prevNodeId;\n\t\t\t\t\t\tTraverseRec(nextSegGeometry, nextNodeIsStartNode, viaInitialStartNode, direction, side, stopCrit, visitor, visitedSegmentIds);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Util/TextureUtil.cs",
    "content": "﻿using ColossalFramework.UI;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing UnityEngine;\n\nnamespace TrafficManager.Util {\n\tpublic static class TextureUtil {\n\t\tpublic static UITextureAtlas GenerateLinearAtlas(string name, Texture2D texture, int numSprites, string[] spriteNames) {\n\t\t\treturn Generate2DAtlas(name, texture, numSprites, 1, spriteNames);\n\t\t}\n\n\t\tpublic static UITextureAtlas Generate2DAtlas(string name, Texture2D texture, int numX, int numY, string[] spriteNames) {\n\t\t\tif (spriteNames.Length != numX * numY) {\n\t\t\t\tthrow new ArgumentException($\"Number of sprite name does not match dimensions (expected {numX} x {numY}, was {spriteNames.Length})\");\n\t\t\t}\n\n\t\t\tUITextureAtlas atlas = ScriptableObject.CreateInstance<UITextureAtlas>();\n\t\t\tatlas.padding = 0;\n\t\t\tatlas.name = name;\n\n\t\t\tvar shader = Shader.Find(\"UI/Default UI Shader\");\n\t\t\tif (shader != null)\n\t\t\t\tatlas.material = new Material(shader);\n\t\t\tatlas.material.mainTexture = texture;\n\n\t\t\tint spriteWidth = Mathf.RoundToInt((float)texture.width / (float)numX);\n\t\t\tint spriteHeight = Mathf.RoundToInt((float)texture.height / (float)numY);\n\n\t\t\tint k = 0;\n\t\t\tfor (int i = 0; i < numX; ++i) {\n\t\t\t\tfloat x = (float)i / (float)numX;\n\t\t\t\tfor (int j = 0; j < numY; ++j) {\n\t\t\t\t\tfloat y = (float)j / (float)numY;\n\n\t\t\t\t\tvar sprite = new UITextureAtlas.SpriteInfo {\n\t\t\t\t\t\tname = spriteNames[k],\n\t\t\t\t\t\tregion = new Rect(x, y, (float)spriteWidth / (float)texture.width, (float)spriteHeight / (float)texture.height)\n\t\t\t\t\t};\n\n\t\t\t\t\tvar spriteTexture = new Texture2D(spriteWidth, spriteHeight);\n\t\t\t\t\tspriteTexture.SetPixels(texture.GetPixels((int)((float)texture.width * sprite.region.x), (int)((float)texture.height * sprite.region.y), spriteWidth, spriteHeight));\n\t\t\t\t\tsprite.texture = spriteTexture;\n\n\t\t\t\t\tatlas.AddSprite(sprite);\n\n\t\t\t\t\t++k;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn atlas;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/Util/TinyDictionary.cs",
    "content": "﻿using CSUtil.Commons;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace TrafficManager.Util {\n\t/// <summary>\n\t/// Dictionary for use cases with a small number of entries and arbitrary keys\n\t/// </summary>\n\t/// <typeparam name=\"TKey\">key type</typeparam>\n\t/// <typeparam name=\"TValue\">value type</typeparam>\n\tpublic class TinyDictionary<TKey, TValue> : IDictionary<TKey, TValue> {\n\t\tprivate TKey[] keys;\n\t\tprivate TValue[] values;\n\t\tprivate KeyValuePair<TKey, TValue>[] keyValuePairs;\n\n\t\tpublic TinyDictionary() {\n\t\t\tClear();\n\t\t}\n\n\t\tpublic ICollection<TKey> Keys {\n\t\t\tget {\n\t\t\t\treturn keys.ToList();\n\t\t\t}\n\t\t}\n\n\t\tpublic ICollection<TValue> Values {\n\t\t\tget {\n\t\t\t\treturn values.ToList();\n\t\t\t}\n\t\t}\n\n\t\tpublic int Count {\n\t\t\tget {\n\t\t\t\treturn values.Length;\n\t\t\t}\n\t\t}\n\n\t\tpublic bool IsReadOnly {\n\t\t\tget {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tpublic override string ToString() {\n\t\t\treturn this.DictionaryToString();\n\t\t}\n\n\t\tpublic TValue this[TKey key] {\n\t\t\tget {\n\t\t\t\tif (key == null) {\n\t\t\t\t\tthrow new ArgumentNullException();\n\t\t\t\t}\n\n\t\t\t\tint keyIndex = IndexOfKey(key);\n\t\t\t\tif (keyIndex < 0) {\n\t\t\t\t\tthrow new KeyNotFoundException($\"Key '{key}' not found in dictionary: {string.Join(\", \", keyValuePairs.Select(x => x.Key.ToString() + \" => \" + ToStringExt.ToString(x.Value)).ToArray())}\");\n\t\t\t\t}\n\n\t\t\t\treturn values[keyIndex];\n\t\t\t}\n\n\t\t\tset {\n\t\t\t\tif (key == null) {\n\t\t\t\t\tthrow new ArgumentNullException();\n\t\t\t\t}\n\n\t\t\t\tAdd(key, value);\n\t\t\t}\n\t\t}\n\n\t\tpublic bool ContainsKey(TKey key) {\n\t\t\treturn IndexOfKey(key) >= 0;\n\t\t}\n\n\t\tpublic void Add(TKey key, TValue value) {\n\t\t\tint keyIndex = IndexOfKey(key);\n\t\t\tif (keyIndex >= 0) {\n\t\t\t\tvalues[keyIndex] = value;\n\t\t\t\tkeyValuePairs[keyIndex] = new KeyValuePair<TKey, TValue>(key, value);\n\t\t\t} else {\n\t\t\t\tint len = Count;\n\t\t\t\tint newLen = len + 1;\n\n\t\t\t\tTKey[] newKeys = new TKey[newLen];\n\t\t\t\tTValue[] newValues = new TValue[newLen];\n\t\t\t\tKeyValuePair<TKey, TValue>[] newKeyValuePairs = new KeyValuePair<TKey, TValue>[newLen];\n\n\t\t\t\tArray.Copy(keys, newKeys, len);\n\t\t\t\tArray.Copy(values, newValues, len);\n\t\t\t\tArray.Copy(keyValuePairs, newKeyValuePairs, len);\n\n\t\t\t\tnewKeys[len] = key;\n\t\t\t\tnewValues[len] = value;\n\t\t\t\tnewKeyValuePairs[len] = new KeyValuePair<TKey, TValue>(key, value);\n\n\t\t\t\tkeys = newKeys;\n\t\t\t\tvalues = newValues;\n\t\t\t\tkeyValuePairs = newKeyValuePairs;\n\t\t\t}\n\t\t}\n\n\t\tpublic bool Remove(TKey key) {\n\t\t\tint keyIndex = IndexOfKey(key);\n\t\t\tif (keyIndex < 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tint len = Count;\n\t\t\tint newLen = len - 1;\n\n\t\t\tTKey[] newKeys = new TKey[newLen];\n\t\t\tTValue[] newValues = new TValue[newLen];\n\t\t\tKeyValuePair<TKey, TValue>[] newKeyValuePairs = new KeyValuePair<TKey, TValue>[newLen];\n\n\t\t\tif (keyIndex > 0) {\n\t\t\t\t// copy 0..keyIndex-1\n\t\t\t\tArray.Copy(keys, 0, newKeys, 0, keyIndex);\n\t\t\t\tArray.Copy(values, 0, newValues, 0, keyIndex);\n\t\t\t\tArray.Copy(keyValuePairs, 0, newKeyValuePairs, 0, keyIndex);\n\t\t\t}\n\n\t\t\tif (keyIndex < newLen) {\n\t\t\t\t// copy keyIndex+1..newLen-1\n\t\t\t\tint remLen = newLen - keyIndex;\n\t\t\t\tArray.Copy(keys, keyIndex + 1, newKeys, keyIndex, remLen);\n\t\t\t\tArray.Copy(values, keyIndex + 1, newValues, keyIndex, remLen);\n\t\t\t\tArray.Copy(keyValuePairs, keyIndex + 1, newKeyValuePairs, keyIndex, remLen);\n\t\t\t}\n\t\t\t\n\t\t\tkeys = newKeys;\n\t\t\tvalues = newValues;\n\t\t\tkeyValuePairs = newKeyValuePairs;\n\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic bool TryGetValue(TKey key, out TValue value) {\n\t\t\tint keyIndex = IndexOfKey(key);\n\t\t\tif (keyIndex < 0) {\n\t\t\t\tvalue = default(TValue);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tvalue = values[keyIndex];\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic void Add(KeyValuePair<TKey, TValue> item) {\n\t\t\tAdd(item.Key, item.Value);\n\t\t}\n\n\t\tpublic void Clear() {\n\t\t\tkeys = new TKey[0];\n\t\t\tvalues = new TValue[0];\n\t\t\tkeyValuePairs = new KeyValuePair<TKey, TValue>[0];\n\t\t}\n\n\t\tpublic bool Contains(KeyValuePair<TKey, TValue> item) {\n\t\t\treturn ContainsKey(item.Key);\n\t\t}\n\n\t\tpublic void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {\n\t\t\tkeyValuePairs.CopyTo(array, arrayIndex);\n\t\t}\n\n\t\tpublic bool Remove(KeyValuePair<TKey, TValue> item) {\n\t\t\treturn Remove(item.Key);\n\t\t}\n\n\t\tpublic IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {\n\t\t\treturn new TinyDictionaryEnumerator<TKey, TValue>(this);\n\t\t}\n\n\t\tIEnumerator IEnumerable.GetEnumerator() {\n\t\t\treturn new TinyDictionaryEnumerator<TKey, TValue>(this);\n\t\t}\n\n\t\tprotected int IndexOfKey(TKey key) {\n\t\t\tif (key == null) {\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tfor (int i = 0; i < keys.Length; ++i) {\n\t\t\t\tif (key.Equals(keys[i])) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\n\t\tprotected class TinyDictionaryEnumerator<TKey, TValue> : IEnumerator<KeyValuePair<TKey, TValue>> {\n\t\t\tprivate int currentIndex = -1;\n\t\t\tprivate TinyDictionary<TKey, TValue> dict;\n\n\t\t\tpublic TinyDictionaryEnumerator(TinyDictionary<TKey, TValue> dict) {\n\t\t\t\tthis.dict = dict;\n\t\t\t}\n\n\t\t\tpublic KeyValuePair<TKey, TValue> Current {\n\t\t\t\tget {\n\t\t\t\t\treturn dict.keyValuePairs[currentIndex];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tobject IEnumerator.Current {\n\t\t\t\tget {\n\t\t\t\t\treturn dict.keyValuePairs[currentIndex];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic void Dispose() {\n\t\t\t\tdict = null;\n\t\t\t}\n\n\t\t\tpublic bool MoveNext() {\n\t\t\t\treturn ++currentIndex < dict.Count;\n\t\t\t}\n\n\t\t\tpublic void Reset() {\n\t\t\t\tcurrentIndex = -1;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TLM/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"StyleCop.Analyzers\" version=\"1.0.2\" targetFramework=\"net35\" developmentDependency=\"true\" />\n</packages>"
  },
  {
    "path": "TLM/TLM.sln.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:String x:Key=\"/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue\">AI</s:String>\n\t<s:String x:Key=\"/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GUI/@EntryIndexedValue\">GUI</s:String></wpf:ResourceDictionary>"
  },
  {
    "path": "TLM/TMPE.CitiesGameBridge/Factory/ServiceFactory.cs",
    "content": "﻿using System;\nusing GenericGameBridge.Factory;\nusing GenericGameBridge.Service;\n\nnamespace CitiesGameBridge.Factory {\n\tpublic class ServiceFactory : IServiceFactory {\n\t\tpublic static readonly IServiceFactory Instance = new ServiceFactory();\n\n\t\tprivate ServiceFactory() {\n\n\t\t}\n\n\t\tpublic IBuildingService BuildingService {\n\t\t\tget {\n\t\t\t\treturn Service.BuildingService.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic ICitizenService CitizenService {\n\t\t\tget {\n\t\t\t\treturn Service.CitizenService.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic INetService NetService {\n\t\t\tget {\n\t\t\t\treturn Service.NetService.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IPathService PathService {\n\t\t\tget {\n\t\t\t\treturn Service.PathService.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic ISimulationService SimulationService {\n\t\t\tget {\n\t\t\t\treturn Service.SimulationService.Instance;\n\t\t\t}\n\t\t}\n\n\t\tpublic IVehicleService VehicleService {\n\t\t\tget {\n\t\t\t\treturn Service.VehicleService.Instance;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.CitiesGameBridge/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following \n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"CitiesGameBridge\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"CitiesGameBridge\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2017\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible \n// to COM components.  If you need to access a type in this assembly from \n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"3f2f7926-5d51-4880-a2b7-4594a10d7e54\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version \n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers \n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.*\")]"
  },
  {
    "path": "TLM/TMPE.CitiesGameBridge/Service/BuildingService.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing GenericGameBridge.Service;\n\nnamespace CitiesGameBridge.Service {\n\tpublic class BuildingService : IBuildingService {\n\t\tpublic static readonly IBuildingService Instance = new BuildingService();\n\n\t\tprivate BuildingService() {\n\n\t\t}\n\n\t\tpublic bool IsBuildingValid(ushort buildingId) {\n\t\t\treturn CheckBuildingFlags(buildingId, Building.Flags.Created | Building.Flags.Deleted, Building.Flags.Created);\n\t\t}\n\n\t\tpublic bool CheckBuildingFlags(ushort buildingId, Building.Flags flagMask, Building.Flags? expectedResult = default(Building.Flags?)) {\n\t\t\tbool ret = false;\n\t\t\tProcessBuilding(buildingId, delegate (ushort bId, ref Building building) {\n\t\t\t\tret = LogicUtil.CheckFlags((uint)building.m_flags, (uint)flagMask, (uint?)expectedResult);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic void ProcessBuilding(ushort buildingId, BuildingHandler handler) {\n\t\t\tProcessBuilding(buildingId, ref Singleton<BuildingManager>.instance.m_buildings.m_buffer[buildingId], handler);\n\t\t}\n\n\t\tpublic void ProcessBuilding(ushort buildingId, ref Building building, BuildingHandler handler) {\n\t\t\thandler(buildingId, ref building);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.CitiesGameBridge/Service/CitizenService.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing GenericGameBridge.Service;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace CitiesGameBridge.Service {\n\tpublic class CitizenService : ICitizenService {\n\t\tpublic static readonly ICitizenService Instance = new CitizenService();\n\n\t\tprivate CitizenService() {\n\n\t\t}\n\n\t\tpublic bool CheckCitizenFlags(uint citizenId, Citizen.Flags flagMask, Citizen.Flags? expectedResult = default(Citizen.Flags?)) {\n\t\t\tbool ret = false;\n\t\t\tProcessCitizen(citizenId, delegate (uint cId, ref Citizen citizen) {\n\t\t\t\tret = LogicUtil.CheckFlags((uint)citizen.m_flags, (uint)flagMask, (uint?)expectedResult);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool CheckCitizenInstanceFlags(ushort citizenInstanceId, CitizenInstance.Flags flagMask, CitizenInstance.Flags? expectedResult = default(CitizenInstance.Flags?)) {\n\t\t\tbool ret = false;\n\t\t\tProcessCitizenInstance(citizenInstanceId, delegate (ushort ciId, ref CitizenInstance citizenInstance) {\n\t\t\t\tret = LogicUtil.CheckFlags((uint)citizenInstance.m_flags, (uint)flagMask, (uint?)expectedResult);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool IsCitizenInstanceValid(ushort citizenInstanceId) {\n\t\t\treturn CheckCitizenInstanceFlags(citizenInstanceId, CitizenInstance.Flags.Created | CitizenInstance.Flags.Deleted, CitizenInstance.Flags.Created);\n\t\t}\n\n\t\tpublic bool IsCitizenValid(uint citizenId) {\n\t\t\treturn CheckCitizenFlags(citizenId, Citizen.Flags.Created);\n\t\t}\n\n\t\tpublic void ProcessCitizen(uint citizenId, CitizenHandler handler) {\n\t\t\tProcessCitizen(citizenId, ref Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenId], handler);\n\t\t}\n\n\t\tpublic void ProcessCitizen(uint citizenId, ref Citizen citizen, CitizenHandler handler) {\n\t\t\thandler(citizenId, ref citizen);\n\t\t}\n\n\t\tpublic void ProcessCitizenInstance(ushort citizenInstanceId, CitizenInstanceHandler handler) {\n\t\t\tProcessCitizenInstance(citizenInstanceId, ref Singleton<CitizenManager>.instance.m_instances.m_buffer[citizenInstanceId], handler);\n\t\t}\n\n\t\tpublic void ProcessCitizenInstance(ushort citizenInstanceId, ref CitizenInstance citizenInstance, CitizenInstanceHandler handler) {\n\t\t\thandler(citizenInstanceId, ref citizenInstance);\n\t\t}\n\n\t\tpublic void ReleaseCitizenInstance(ushort citizenInstanceId) {\n\t\t\tSingleton<CitizenManager>.instance.ReleaseCitizenInstance(citizenInstanceId);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.CitiesGameBridge/Service/NetService.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing GenericGameBridge.Service;\nusing System;\nusing System.Collections.Generic;\n\nnamespace CitiesGameBridge.Service {\n\tpublic class NetService : INetService {\n\t\tpublic static readonly INetService Instance = new NetService();\n\t\t\n\t\tprivate NetService() {\n\t\t\t\n\t\t}\n\n\t\tpublic bool IsSegmentValid(ushort segmentId) {\n\t\t\treturn CheckSegmentFlags(segmentId, NetSegment.Flags.Created | NetSegment.Flags.Deleted, NetSegment.Flags.Created);\n\t\t}\n\n\t\tpublic void ProcessSegment(ushort segmentId, NetSegmentHandler handler) {\n\t\t\tProcessSegment(segmentId, ref Singleton<NetManager>.instance.m_segments.m_buffer[segmentId], handler);\n\t\t}\n\n\t\tpublic void ProcessSegment(ushort segmentId, ref NetSegment segment, NetSegmentHandler handler) {\n\t\t\thandler(segmentId, ref segment);\n\t\t}\n\n\t\tpublic bool IsNodeValid(ushort nodeId) {\n\t\t\treturn CheckNodeFlags(nodeId, NetNode.Flags.Created | NetNode.Flags.Deleted, NetNode.Flags.Created);\n\t\t}\n\n\t\tpublic void ProcessNode(ushort nodeId, NetNodeHandler handler) {\n\t\t\tProcessNode(nodeId, ref Singleton<NetManager>.instance.m_nodes.m_buffer[nodeId], handler);\n\t\t}\n\n\t\tpublic void ProcessNode(ushort nodeId, ref NetNode node, NetNodeHandler handler) {\n\t\t\thandler(nodeId, ref node);\n\t\t}\n\n\t\t[Obsolete]\n\t\tbool IsLaneValid(ref NetLane lane) {\n\t\t\tif ((lane.m_flags & (uint)(NetLane.Flags.Created | NetLane.Flags.Deleted)) != (uint)NetLane.Flags.Created) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn IsSegmentValid(lane.m_segment);\n\t\t}\n\t\t\n\t\tpublic bool IsLaneValid(uint laneId) {\n\t\t\tif (!CheckLaneFlags(laneId, NetLane.Flags.Created | NetLane.Flags.Deleted, NetLane.Flags.Created)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbool ret = false;\n\t\t\tProcessLane(laneId, delegate(uint lId, ref NetLane lane) {\n\t\t\t\tret = IsSegmentValid(lane.m_segment);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic void ProcessLane(uint laneId, NetLaneHandler handler) {\n\t\t\tProcessLane(laneId, ref Singleton<NetManager>.instance.m_lanes.m_buffer[laneId], handler);\n\t\t}\n\n\t\tpublic void ProcessLane(uint laneId, ref NetLane lane, NetLaneHandler handler) {\n\t\t\thandler(laneId, ref lane);\n\t\t}\n\n\t\tpublic ushort GetSegmentNodeId(ushort segmentId, bool startNode) {\n\t\t\tushort nodeId = 0;\n\t\t\tProcessSegment(segmentId, delegate(ushort segId, ref NetSegment segment) {\n\t\t\t\tnodeId = startNode ? segment.m_startNode : segment.m_endNode;\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn nodeId;\n\t\t}\n\n\t\tpublic void IterateNodeSegments(ushort nodeId, NetSegmentHandler handler) {\n\t\t\tIterateNodeSegments(nodeId, ClockDirection.None, handler);\n\t\t}\n\n\t\tpublic void IterateNodeSegments(ushort nodeId, ClockDirection dir, NetSegmentHandler handler) {\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\n\t\t\tProcessNode(nodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\tif (dir == ClockDirection.None) {\n\t\t\t\t\tfor (int i = 0; i < 8; ++i) {\n\t\t\t\t\t\tushort segmentId = node.GetSegment(i);\n\t\t\t\t\t\tif (segmentId != 0) {\n\t\t\t\t\t\t\tif (!handler(segmentId, ref netManager.m_segments.m_buffer[segmentId])) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tushort segmentId = node.GetSegment(0);\n\t\t\t\t\tushort initSegId = segmentId;\n\n\t\t\t\t\twhile (true) {\n\t\t\t\t\t\tif (segmentId != 0) {\n\t\t\t\t\t\t\tif (!handler(segmentId, ref netManager.m_segments.m_buffer[segmentId])) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tswitch (dir) {\n\t\t\t\t\t\t\tcase ClockDirection.Clockwise:\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tsegmentId = netManager.m_segments.m_buffer[segmentId].GetLeftSegment(nodeId);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ClockDirection.CounterClockwise:\n\t\t\t\t\t\t\t\tsegmentId = netManager.m_segments.m_buffer[segmentId].GetRightSegment(nodeId);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (segmentId == initSegId || segmentId == 0) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\tpublic void IterateSegmentLanes(ushort segmentId, NetSegmentLaneHandler handler) {\n\t\t\tIterateSegmentLanes(segmentId, ref Singleton<NetManager>.instance.m_segments.m_buffer[segmentId], handler);\n\t\t}\n\n\t\tpublic void IterateSegmentLanes(ushort segmentId, ref NetSegment segment, NetSegmentLaneHandler handler) {\n\t\t\tNetInfo segmentInfo = segment.Info;\n\t\t\tif (segmentInfo == null)\n\t\t\t\treturn;\n\n\t\t\tbyte laneIndex = 0;\n\t\t\tuint curLaneId = segment.m_lanes;\n\t\t\twhile (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) {\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\thandler(curLaneId, ref Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId], laneInfo, segmentId, ref segment, laneIndex);\n\n\t\t\t\tcurLaneId = Singleton<NetManager>.instance.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t++laneIndex;\n\t\t\t}\n\t\t}\n\n\t\tpublic NetInfo.Direction GetFinalSegmentEndDirection(ushort segmentId, bool startNode) {\n\t\t\treturn GetFinalSegmentEndDirection(segmentId, ref Singleton<NetManager>.instance.m_segments.m_buffer[segmentId], startNode);\n\t\t}\n\n\t\tpublic NetInfo.Direction GetFinalSegmentEndDirection(ushort segmentId, ref NetSegment segment, bool startNode) {\n\t\t\tNetInfo segmentInfo = segment.Info;\n\n\t\t\tvar dir = startNode ? NetInfo.Direction.Backward : NetInfo.Direction.Forward;\n\t\t\tif ((segment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None /*^ SimulationService.Instance.LeftHandDrive*/)\n\t\t\t\tdir = NetInfo.InvertDirection(dir);\n\n\t\t\treturn dir;\n\t\t}\n\n\t\tpublic bool CheckNodeFlags(ushort nodeId, NetNode.Flags flagMask, NetNode.Flags? expectedResult=null) {\n\t\t\tbool ret = false;\n\t\t\tProcessNode(nodeId, delegate (ushort nId, ref NetNode node) {\n\t\t\t\tret = LogicUtil.CheckFlags((uint)node.m_flags, (uint)flagMask, (uint?)expectedResult);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool CheckSegmentFlags(ushort segmentId, NetSegment.Flags flagMask, NetSegment.Flags? expectedResult=null) {\n\t\t\tbool ret = false;\n\t\t\tProcessSegment(segmentId, delegate (ushort sId, ref NetSegment segment) {\n\t\t\t\tret = LogicUtil.CheckFlags((uint)segment.m_flags, (uint)flagMask, (uint?)expectedResult);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool CheckLaneFlags(uint laneId, NetLane.Flags flagMask, NetLane.Flags? expectedResult=null) {\n\t\t\tbool ret = false;\n\t\t\tProcessLane(laneId, delegate (uint lId, ref NetLane lane) {\n\t\t\t\tret = LogicUtil.CheckFlags((uint)lane.m_flags, (uint)flagMask, (uint?)expectedResult);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\t/// <summary>\n\t\t/// Assembles a geometrically sorted list of lanes for the given segment.\n\t\t/// If the <paramref name=\"startNode\"/> parameter is set only lanes supporting traffic to flow towards the given node are added to the list, otherwise all matched lanes are added.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"segment\">segment data</param>\n\t\t/// <param name=\"startNode\">reference node (optional)</param>\n\t\t/// <param name=\"laneTypeFilter\">lane type filter, lanes must match this filter mask</param>\n\t\t/// <param name=\"vehicleTypeFilter\">vehicle type filter, lanes must match this filter mask</param>\n\t\t/// <param name=\"reverse\">if true, lanes are ordered from right to left (relative to the segment's start node / the given node), otherwise from left to right</param>\n\t\t/// <returns>sorted list of lanes for the given segment</returns>\n\t\tpublic IList<LanePos> GetSortedLanes(ushort segmentId, ref NetSegment segment, bool? startNode, NetInfo.LaneType? laneTypeFilter = null, VehicleInfo.VehicleType? vehicleTypeFilter = null, bool reverse = false) { // TODO refactor together with getSegmentNumVehicleLanes, especially the vehicle type and lane type checks\n\t\t\tNetManager netManager = Singleton<NetManager>.instance;\n\t\t\tvar laneList = new List<LanePos>();\n\n\t\t\tbool inverted = ((segment.m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None);\n\n\t\t\tNetInfo.Direction? filterDir = null;\n\t\t\tNetInfo.Direction sortDir = NetInfo.Direction.Forward;\n\t\t\tif (startNode != null) {\n\t\t\t\tfilterDir = (bool)startNode ? NetInfo.Direction.Backward : NetInfo.Direction.Forward;\n\t\t\t\tfilterDir = inverted ? NetInfo.InvertDirection((NetInfo.Direction)filterDir) : filterDir;\n\t\t\t\tsortDir = NetInfo.InvertDirection((NetInfo.Direction)filterDir);\n\t\t\t} else if (inverted) {\n\t\t\t\tsortDir = NetInfo.Direction.Backward;\n\t\t\t}\n\n\t\t\tif (reverse) {\n\t\t\t\tsortDir = NetInfo.InvertDirection(sortDir);\n\t\t\t}\n\n\t\t\tNetInfo segmentInfo = segment.Info;\n\t\t\tuint curLaneId = segment.m_lanes;\n\t\t\tbyte laneIndex = 0;\n\t\t\twhile (laneIndex < segmentInfo.m_lanes.Length && curLaneId != 0u) {\n\t\t\t\tNetInfo.Lane laneInfo = segmentInfo.m_lanes[laneIndex];\n\t\t\t\tif ((laneTypeFilter == null || (laneInfo.m_laneType & laneTypeFilter) != NetInfo.LaneType.None) &&\n\t\t\t\t\t(vehicleTypeFilter == null || (laneInfo.m_vehicleType & vehicleTypeFilter) != VehicleInfo.VehicleType.None) &&\n\t\t\t\t\t(filterDir == null || segmentInfo.m_lanes[laneIndex].m_finalDirection == filterDir)) {\n\t\t\t\t\tlaneList.Add(new LanePos(curLaneId, laneIndex, segmentInfo.m_lanes[laneIndex].m_position, laneInfo.m_vehicleType, laneInfo.m_laneType));\n\t\t\t\t}\n\n\t\t\t\tcurLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;\n\t\t\t\t++laneIndex;\n\t\t\t}\n\n\t\t\tlaneList.Sort(delegate (LanePos x, LanePos y) {\n\t\t\t\tbool fwd = sortDir == NetInfo.Direction.Forward;\n\t\t\t\tif (x.position == y.position) {\n\t\t\t\t\tif (x.position > 0) {\n\t\t\t\t\t\t// mirror type-bound lanes (e.g. for coherent disply of lane-wise speed limits)\n\t\t\t\t\t\tfwd = !fwd;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (x.laneType == y.laneType) {\n\t\t\t\t\t\tif (x.vehicleType == y.vehicleType) {\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t} else if ((x.vehicleType < y.vehicleType) == fwd) {\n\t\t\t\t\t\t\treturn -1;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if ((x.laneType < y.laneType) == fwd) {\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ((x.position < y.position) == fwd) {\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\treturn 1;\n\t\t\t});\n\t\t\treturn laneList;\n\t\t}\n\n\t\tpublic void PublishSegmentChanges(ushort segmentId) {\n#if DEBUG\n\t\t\tLog.Warning($\"NetService.PublishSegmentChanges({segmentId}) called.\");\n#endif\n\t\t\tISimulationService simService = SimulationService.Instance;\n\n\t\t\tProcessSegment(segmentId, delegate (ushort sId, ref NetSegment segment) {\n\t\t\t\tuint currentBuildIndex = simService.CurrentBuildIndex;\n\t\t\t\tsimService.CurrentBuildIndex = currentBuildIndex + 1;\n\t\t\t\tsegment.m_modifiedIndex = currentBuildIndex;\n\n\t\t\t\t++segment.m_buildIndex;\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.CitiesGameBridge/Service/PathService.cs",
    "content": "﻿using ColossalFramework;\nusing CSUtil.Commons;\nusing GenericGameBridge.Service;\nusing System;\nusing System.Collections.Generic;\n\nnamespace CitiesGameBridge.Service {\n\tpublic class PathService : IPathService {\n\t\tpublic static readonly IPathService Instance = new PathService();\n\n\t\tprivate PathService() {\n\t\t\t\n\t\t}\n\t\t\n\t\tpublic bool CheckUnitFlags(uint unitId, byte flagMask, byte? expectedResult=null) {\n\t\t\tbool ret = false;\n\t\t\tProcessUnit(unitId, delegate (uint uId, ref PathUnit unit) {\n\t\t\t\tret = LogicUtil.CheckFlags((uint)unit.m_pathFindFlags, (uint)flagMask, (uint?)expectedResult);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic void ProcessUnit(uint unitId, PathUnitHandler handler) {\n\t\t\tProcessUnit(unitId, ref Singleton<PathManager>.instance.m_pathUnits.m_buffer[unitId], handler);\n\t\t}\n\n\t\tpublic void ProcessUnit(uint unitId, ref PathUnit unit, PathUnitHandler handler) {\n\t\t\thandler(unitId, ref unit);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.CitiesGameBridge/Service/SimulationService.cs",
    "content": "﻿using System;\nusing ColossalFramework;\nusing ColossalFramework.Math;\nusing GenericGameBridge.Service;\nusing UnityEngine;\n\nnamespace CitiesGameBridge.Service {\n\tpublic class SimulationService : ISimulationService {\n\t\tpublic static readonly ISimulationService Instance = new SimulationService();\n\t\t\n\t\tprivate SimulationService() {\n\n\t\t}\n\n\t\tpublic bool LeftHandDrive {\n\t\t\tget {\n\t\t\t\treturn Singleton<SimulationManager>.instance.m_metaData.m_invertTraffic == SimulationMetaData.MetaBool.True;\n\t\t\t}\n\t\t}\n\n\t\tpublic uint CurrentBuildIndex {\n\t\t\tget {\n\t\t\t\treturn Singleton<SimulationManager>.instance.m_currentBuildIndex;\n\t\t\t}\n\n\t\t\tset {\n\t\t\t\tSingleton<SimulationManager>.instance.m_currentBuildIndex = value;\n\t\t\t}\n\t\t}\n\n\t\tpublic uint CurrentFrameIndex {\n\t\t\tget {\n\t\t\t\treturn Singleton<SimulationManager>.instance.m_currentFrameIndex;\n\t\t\t}\n\t\t}\n\n\t\tpublic Vector3 CameraPosition {\n\t\t\tget {\n\t\t\t\treturn Singleton<SimulationManager>.instance.m_simulationView.m_position;\n\t\t\t}\n\t\t}\n\n\t\tpublic Randomizer Randomizer {\n\t\t\tget {\n\t\t\t\treturn Singleton<SimulationManager>.instance.m_randomizer;\n\t\t\t}\n\t\t}\n\n\t\tpublic bool SimulationPaused {\n\t\t\tget {\n\t\t\t\treturn Singleton<SimulationManager>.instance.SimulationPaused;\n\t\t\t}\t\t\t\n\t\t}\n\n\t\tpublic bool ForcedSimulationPaused {\n\t\t\tget {\n\t\t\t\treturn Singleton<SimulationManager>.instance.ForcedSimulationPaused;\n\t\t\t}\n\t\t}\n\n\t\tpublic AsyncAction AddAction(Action action) {\n\t\t\treturn Singleton<SimulationManager>.instance.AddAction(action);\n\t\t}\n\n\t\tpublic void PauseSimulation(bool forced) {\n\t\t\tif (forced) {\n\t\t\t\tSingleton<SimulationManager>.instance.ForcedSimulationPaused = true;\n\t\t\t} else {\n\t\t\t\tSingleton<SimulationManager>.instance.SimulationPaused = true;\n\t\t\t}\n\t\t}\n\n\t\tpublic void ResumeSimulation(bool forced) {\n\t\t\tif (forced) {\n\t\t\t\tSingleton<SimulationManager>.instance.ForcedSimulationPaused = false;\n\t\t\t} else {\n\t\t\t\tSingleton<SimulationManager>.instance.SimulationPaused = false;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.CitiesGameBridge/Service/VehicleService.cs",
    "content": "﻿using System;\nusing ColossalFramework;\nusing CSUtil.Commons;\nusing GenericGameBridge.Service;\n\nnamespace CitiesGameBridge.Service {\n\tpublic class VehicleService : IVehicleService {\n\t\tpublic static readonly IVehicleService Instance = new VehicleService();\n\n\t\tprivate VehicleService() {\n\n\t\t}\n\n\t\tpublic bool CheckVehicleFlags(ushort vehicleId, Vehicle.Flags flagMask, Vehicle.Flags? expectedResult = default(Vehicle.Flags?)) {\n\t\t\tbool ret = false;\n\t\t\tProcessVehicle(vehicleId, delegate (ushort vId, ref Vehicle vehicle) {\n\t\t\t\tret = LogicUtil.CheckFlags((uint)vehicle.m_flags, (uint)flagMask, (uint?)expectedResult);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool CheckVehicleFlags2(ushort vehicleId, Vehicle.Flags2 flagMask, Vehicle.Flags2? expectedResult = default(Vehicle.Flags2?)) {\n\t\t\tbool ret = false;\n\t\t\tProcessVehicle(vehicleId, delegate (ushort vId, ref Vehicle vehicle) {\n\t\t\t\tret = LogicUtil.CheckFlags((uint)vehicle.m_flags2, (uint)flagMask, (uint?)expectedResult);\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\treturn ret;\n\t\t}\n\n\t\tpublic bool IsVehicleValid(ushort vehicleId) {\n\t\t\treturn CheckVehicleFlags(vehicleId, Vehicle.Flags.Created | Vehicle.Flags.Deleted, Vehicle.Flags.Created);\n\t\t}\n\n\t\tpublic void ProcessParkedVehicle(ushort parkedVehicleId, ParkedVehicleHandler handler) {\n\t\t\tProcessParkedVehicle(parkedVehicleId, ref Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId], handler);\n\t\t}\n\n\t\tpublic void ProcessParkedVehicle(ushort parkedVehicleId, ref VehicleParked parkedVehicle, ParkedVehicleHandler handler) {\n\t\t\thandler(parkedVehicleId, ref parkedVehicle);\n\t\t}\n\n\t\tpublic void ProcessVehicle(ushort vehicleId, VehicleHandler handler) {\n\t\t\tProcessVehicle(vehicleId, ref Singleton<VehicleManager>.instance.m_vehicles.m_buffer[vehicleId], handler);\n\t\t}\n\n\t\tpublic void ProcessVehicle(ushort vehicleId, ref Vehicle vehicle, VehicleHandler handler) {\n\t\t\thandler(vehicleId, ref vehicle);\n\t\t}\n\n\t\tpublic void ReleaseParkedVehicle(ushort parkedVehicleId) {\n\t\t\tSingleton<VehicleManager>.instance.ReleaseParkedVehicle(parkedVehicleId);\n\t\t}\n\n\t\tpublic void ReleaseVehicle(ushort vehicleId) {\n\t\t\tSingleton<VehicleManager>.instance.ReleaseVehicle(vehicleId);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.CitiesGameBridge/TMPE.CitiesGameBridge.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"14.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{3F2F7926-5D51-4880-A2B7-4594A10D7E54}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>CitiesGameBridge</RootNamespace>\n    <AssemblyName>TMPE.CitiesGameBridge</AssemblyName>\n    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Benchmark|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\Benchmark\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'PF2_Debug|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\PF2_Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Assembly-CSharp\">\n      <HintPath>..\\dependencies\\Assembly-CSharp.dll</HintPath>\n    </Reference>\n    <Reference Include=\"ColossalManaged\">\n      <HintPath>..\\dependencies\\ColossalManaged.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"UnityEngine\">\n      <HintPath>..\\dependencies\\UnityEngine.dll</HintPath>\n    </Reference>\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Factory\\ServiceFactory.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"Service\\CitizenService.cs\" />\n    <Compile Include=\"Service\\PathService.cs\" />\n    <Compile Include=\"Service\\NetService.cs\" />\n    <Compile Include=\"Service\\SimulationService.cs\" />\n    <Compile Include=\"Service\\BuildingService.cs\" />\n    <Compile Include=\"Service\\VehicleService.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\CSUtil.Commons\\CSUtil.Commons.csproj\">\n      <Project>{D3ADE06E-F493-4819-865A-3BB44FEEDF01}</Project>\n      <Name>CSUtil.Commons</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\TMPE.GenericGameBridge\\TMPE.GenericGameBridge.csproj\">\n      <Project>{663B991F-32A1-46E1-A4D3-540F8EA7F386}</Project>\n      <Name>TMPE.GenericGameBridge</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.CodeFixes.dll\" />\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.dll\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "TLM/TMPE.CitiesGameBridge/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"StyleCop.Analyzers\" version=\"1.0.2\" targetFramework=\"net35\" developmentDependency=\"true\" />\n</packages>"
  },
  {
    "path": "TLM/TMPE.GenericGameBridge/Factory/IServiceFactory.cs",
    "content": "﻿using GenericGameBridge.Service;\n\nnamespace GenericGameBridge.Factory {\n\tpublic interface IServiceFactory {\n\t\tIBuildingService BuildingService { get; }\n\t\tICitizenService CitizenService { get; }\n\t\tINetService NetService { get; }\n\t\tIPathService PathService { get; }\n\t\tISimulationService SimulationService { get; }\n\t\tIVehicleService VehicleService { get; }\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.GenericGameBridge/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following \n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"GenericGameBridge\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"GenericGameBridge\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2017\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible \n// to COM components.  If you need to access a type in this assembly from \n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"663b991f-32a1-46e1-a4d3-540f8ea7f386\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version \n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers \n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.*\")]\n"
  },
  {
    "path": "TLM/TMPE.GenericGameBridge/Service/IBuildingService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace GenericGameBridge.Service {\n\tpublic delegate bool BuildingHandler(ushort buildingId, ref Building building);\n\n\tpublic interface IBuildingService {\n\t\tbool CheckBuildingFlags(ushort buildingId, Building.Flags flagMask, Building.Flags? expectedResult = default(Building.Flags?));\n\t\tbool IsBuildingValid(ushort buildingId);\n\t\tvoid ProcessBuilding(ushort buildingId, BuildingHandler handler);\n\t\tvoid ProcessBuilding(ushort buildingId, ref Building building, BuildingHandler handler);\t\t\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.GenericGameBridge/Service/ICitizenService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace GenericGameBridge.Service {\n\tpublic delegate bool CitizenHandler(uint citizenId, ref Citizen citizen);\n\tpublic delegate bool CitizenInstanceHandler(ushort citizenInstanceId, ref CitizenInstance citizenInstance);\n\n\tpublic interface ICitizenService {\n\t\tbool CheckCitizenFlags(uint citizenId, Citizen.Flags flagMask, Citizen.Flags? expectedResult = default(Citizen.Flags?));\n\t\tbool IsCitizenValid(uint citizenId);\n\t\tvoid ProcessCitizen(uint citizenId, CitizenHandler handler);\n\t\tvoid ProcessCitizen(uint citizenId, ref Citizen citizen, CitizenHandler handler);\n\n\t\tbool CheckCitizenInstanceFlags(ushort citizenInstanceId, CitizenInstance.Flags flagMask, CitizenInstance.Flags? expectedResult = default(CitizenInstance.Flags?));\n\t\tbool IsCitizenInstanceValid(ushort citizenInstanceId);\n\t\tvoid ProcessCitizenInstance(ushort citizenInstanceId, CitizenInstanceHandler handler);\n\t\tvoid ProcessCitizenInstance(ushort citizenInstanceId, ref CitizenInstance citizenInstance, CitizenInstanceHandler handler);\n\n\t\t/// <summary>\n\t\t/// Despawns and releases the given citizen instance.\n\t\t/// </summary>\n\t\t/// <param name=\"citizenInstanceId\">Citizen instance id to release</param>\n\t\tvoid ReleaseCitizenInstance(ushort citizenInstanceId);\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.GenericGameBridge/Service/INetService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace GenericGameBridge.Service {\n\tpublic delegate bool NetSegmentHandler(ushort segmentId, ref NetSegment segment);\n\tpublic delegate bool NetNodeHandler(ushort nodeId, ref NetNode node);\n\tpublic delegate bool NetLaneHandler(uint laneId, ref NetLane lane);\n\tpublic delegate bool NetSegmentLaneHandler(uint laneId, ref NetLane lane, NetInfo.Lane laneInfo, ushort segmentId, ref NetSegment segment, byte laneIndex);\n\n\tpublic struct LanePos {\n\t\tpublic uint laneId;\n\t\tpublic byte laneIndex;\n\t\tpublic float position;\n\t\tpublic VehicleInfo.VehicleType vehicleType;\n\t\tpublic NetInfo.LaneType laneType;\n\n\t\tpublic LanePos(uint laneId, byte laneIndex, float position, VehicleInfo.VehicleType vehicleType, NetInfo.LaneType laneType) {\n\t\t\tthis.laneId = laneId;\n\t\t\tthis.laneIndex = laneIndex;\n\t\t\tthis.position = position;\n\t\t\tthis.vehicleType = vehicleType;\n\t\t\tthis.laneType = laneType;\n\t\t}\n\t}\n\n\tpublic enum ClockDirection {\n\t\tNone,\n\t\tClockwise,\n\t\tCounterClockwise\n\t}\n\n\tpublic interface INetService {\n\t\tbool CheckLaneFlags(uint laneId, NetLane.Flags flagMask, NetLane.Flags? expectedResult = default(NetLane.Flags?));\n\t\tbool CheckNodeFlags(ushort nodeId, NetNode.Flags flagMask, NetNode.Flags? expectedResult = default(NetNode.Flags?));\n\t\tbool CheckSegmentFlags(ushort segmentId, NetSegment.Flags flagMask, NetSegment.Flags? expectedResult = default(NetSegment.Flags?));\n\t\tNetInfo.Direction GetFinalSegmentEndDirection(ushort segmentId, bool startNode);\n\t\tNetInfo.Direction GetFinalSegmentEndDirection(ushort segmentId, ref NetSegment segment, bool startNode);\n\t\tushort GetSegmentNodeId(ushort segmentId, bool startNode);\n\t\t/// <summary>\n\t\t/// Assembles a geometrically sorted list of lanes for the given segment.\n\t\t/// If the <paramref name=\"startNode\"/> parameter is set only lanes supporting traffic to flow towards the given node are added to the list, otherwise all matched lanes are added.\n\t\t/// </summary>\n\t\t/// <param name=\"segmentId\">segment id</param>\n\t\t/// <param name=\"segment\">segment data</param>\n\t\t/// <param name=\"startNode\">reference node (optional)</param>\n\t\t/// <param name=\"laneTypeFilter\">lane type filter, lanes must match this filter mask</param>\n\t\t/// <param name=\"vehicleTypeFilter\">vehicle type filter, lanes must match this filter mask</param>\n\t\t/// <param name=\"reverse\">if true, lanes are ordered from right to left (relative to the segment's start node / the given node), otherwise from left to right</param>\n\t\t/// <returns>sorted list of lanes for the given segment</returns>\n\t\tIList<LanePos> GetSortedLanes(ushort segmentId, ref NetSegment segment, bool? startNode, NetInfo.LaneType? laneTypeFilter = default(NetInfo.LaneType?), VehicleInfo.VehicleType? vehicleTypeFilter = default(VehicleInfo.VehicleType?), bool reverse = false);\n\t\tbool IsLaneValid(uint laneId);\n\t\tbool IsNodeValid(ushort nodeId);\n\t\tbool IsSegmentValid(ushort segmentId);\n\t\tvoid IterateNodeSegments(ushort nodeId, NetSegmentHandler handler);\n\t\tvoid IterateNodeSegments(ushort nodeId, ClockDirection dir, NetSegmentHandler handler);\n\t\tvoid IterateSegmentLanes(ushort segmentId, NetSegmentLaneHandler handler);\n\t\tvoid IterateSegmentLanes(ushort segmentId, ref NetSegment segment, NetSegmentLaneHandler handler);\n\t\tvoid ProcessLane(uint laneId, NetLaneHandler handler);\n\t\tvoid ProcessLane(uint laneId, ref NetLane lane, NetLaneHandler handler);\n\t\tvoid ProcessNode(ushort nodeId, NetNodeHandler handler);\n\t\tvoid ProcessNode(ushort nodeId, ref NetNode node, NetNodeHandler handler);\n\t\tvoid ProcessSegment(ushort segmentId, NetSegmentHandler handler);\n\t\tvoid ProcessSegment(ushort segmentId, ref NetSegment segment, NetSegmentHandler handler);\n\t\tvoid PublishSegmentChanges(ushort segmentId);\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.GenericGameBridge/Service/IPathService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace GenericGameBridge.Service {\n\tpublic delegate bool PathUnitHandler(uint unitId, ref PathUnit unit);\n\n\tpublic interface IPathService {\n\t\tbool CheckUnitFlags(uint unitId, byte flagMask, byte? expectedResult = null);\n\t\tvoid ProcessUnit(uint unitId, PathUnitHandler handler);\n\t\tvoid ProcessUnit(uint unitId, ref PathUnit unit, PathUnitHandler handler);\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.GenericGameBridge/Service/ISimulationService.cs",
    "content": "﻿using ColossalFramework.Math;\nusing UnityEngine;\n\nnamespace GenericGameBridge.Service {\n\tpublic interface ISimulationService {\n\t\tbool LeftHandDrive { get; }\n\t\tuint CurrentBuildIndex { get; set; }\n\t\tuint CurrentFrameIndex { get; }\n\t\tVector3 CameraPosition { get; }\n\t\tRandomizer Randomizer { get; }\n\t\tbool SimulationPaused { get; }\n\t\tbool ForcedSimulationPaused { get; }\n\t\tAsyncAction AddAction(System.Action action);\n\t\tvoid PauseSimulation(bool forced);\n\t\tvoid ResumeSimulation(bool forced);\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.GenericGameBridge/Service/IVehicleService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace GenericGameBridge.Service {\n\tpublic delegate bool VehicleHandler(ushort vehicleId, ref Vehicle vehicle);\n\tpublic delegate bool ParkedVehicleHandler(ushort parkedVehicleId, ref VehicleParked parkedVehicle);\n\n\tpublic interface IVehicleService {\n\t\tbool CheckVehicleFlags(ushort vehicleId, Vehicle.Flags flagMask, Vehicle.Flags? expectedResult = default(Vehicle.Flags?));\n\t\tbool CheckVehicleFlags2(ushort vehicleId, Vehicle.Flags2 flagMask, Vehicle.Flags2? expectedResult = default(Vehicle.Flags2?));\n\t\tbool IsVehicleValid(ushort vehicleId);\n\t\tvoid ProcessVehicle(ushort vehicleId, VehicleHandler handler);\n\t\tvoid ProcessVehicle(ushort vehicleId, ref Vehicle vehicle, VehicleHandler handler);\n\t\tvoid ProcessParkedVehicle(ushort parkedVehicleId, ParkedVehicleHandler handler);\n\t\tvoid ProcessParkedVehicle(ushort parkedVehicleId, ref VehicleParked parkedVehicle, ParkedVehicleHandler handler);\n\t\tvoid ReleaseVehicle(ushort vehicleId);\n\t\tvoid ReleaseParkedVehicle(ushort parkedVehicleId);\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.GenericGameBridge/TMPE.GenericGameBridge.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"14.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{663B991F-32A1-46E1-A4D3-540F8EA7F386}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>GenericGameBridge</RootNamespace>\n    <AssemblyName>TMPE.GenericGameBridge</AssemblyName>\n    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Benchmark|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\Benchmark\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'PF2_Debug|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\PF2_Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Assembly-CSharp\">\n      <HintPath>..\\dependencies\\Assembly-CSharp.dll</HintPath>\n    </Reference>\n    <Reference Include=\"ColossalManaged, Version=0.3.0.0, Culture=neutral, processorArchitecture=MSIL\">\n      <SpecificVersion>False</SpecificVersion>\n      <HintPath>..\\dependencies\\ColossalManaged.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"UnityEngine, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL\">\n      <SpecificVersion>False</SpecificVersion>\n      <HintPath>..\\dependencies\\UnityEngine.dll</HintPath>\n    </Reference>\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Factory\\IServiceFactory.cs\" />\n    <Compile Include=\"Service\\ICitizenService.cs\" />\n    <Compile Include=\"Service\\IBuildingService.cs\" />\n    <Compile Include=\"Service\\IPathService.cs\" />\n    <Compile Include=\"Service\\IVehicleService.cs\" />\n    <Compile Include=\"Service\\INetService.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"Service\\ISimulationService.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.CodeFixes.dll\" />\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.dll\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "TLM/TMPE.GenericGameBridge/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"StyleCop.Analyzers\" version=\"1.0.2\" targetFramework=\"net35\" developmentDependency=\"true\" />\n</packages>"
  },
  {
    "path": "TLM/TMPE.GlobalConfigGenerator/App.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n    <startup> \n        \n    <supportedRuntime version=\"v2.0.50727\"/></startup>\n</configuration>\n"
  },
  {
    "path": "TLM/TMPE.GlobalConfigGenerator/Generator.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Xml;\nusing System.Xml.Serialization;\nusing TrafficManager.State;\n\nnamespace GlobalConfigGenerator {\n\tclass Generator {\n\t\tpublic const string FILENAME = \"TMPE_GlobalConfig.xml\";\n\t\tpublic static int? RushHourParkingSearchRadius { get; private set; } = null;\n\t\t//private static DateTime? rushHourConfigModifiedTime = null;\n\t\tprivate const string RUSHHOUR_CONFIG_FILENAME = \"RushHourOptions.xml\";\n\t\t//private static uint lastRushHourConfigCheck = 0;\n\n\t\tpublic static void Main(string[] args) {\n\t\t\t/*WriteDefaultConfig();\n\t\t\tGlobalConfig conf = LoadConfig();*/\n\t\t\tTestRushHourConf();\n\t\t\tConsole.ReadLine();\n\t\t}\n\n\t\t/*public static void WriteDefaultConfig() {\n\t\t\tGlobalConfig conf = new GlobalConfig();\n\t\t\tXmlSerializer serializer = new XmlSerializer(typeof(GlobalConfig));\n\t\t\tTextWriter writer = new StreamWriter(FILENAME);\n\t\t\tserializer.Serialize(writer, conf);\n\t\t\twriter.Close();\n\t\t}\n\n\t\tpublic static GlobalConfig LoadConfig() {\n\t\t\tFileStream fs = new FileStream(FILENAME, FileMode.Open);\n\t\t\tXmlSerializer serializer = new XmlSerializer(typeof(GlobalConfig));\n\t\t\tGlobalConfig conf = (GlobalConfig)serializer.Deserialize(fs);\n\t\t\tConsole.WriteLine(\"OK\");\n\t\t\treturn conf;\n\t\t}*/\n\n\t\tprivate static void TestRushHourConf() { // TODO refactor\n\t\t\tXmlDocument doc = new XmlDocument();\n\t\t\tdoc.Load(RUSHHOUR_CONFIG_FILENAME);\n\t\t\tXmlNode root = doc.DocumentElement;\n\n\t\t\tXmlNode betterParkingNode = root.SelectSingleNode(\"OptionPanel/data/BetterParking\");\n\t\t\tXmlNode parkingSpaceRadiusNode = root.SelectSingleNode(\"OptionPanel/data/ParkingSearchRadius\");\n\n\t\t\tstring  s = betterParkingNode.InnerText;\n\n\t\t\tif (\"True\".Equals(s)) {\n\t\t\t\tRushHourParkingSearchRadius = int.Parse(parkingSpaceRadiusNode.InnerText);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.GlobalConfigGenerator/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// Allgemeine Informationen über eine Assembly werden über die folgenden \n// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,\n// die einer Assembly zugeordnet sind.\n[assembly: AssemblyTitle(\"GlobalConfigGenerator\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"GlobalConfigGenerator\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2016\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Durch Festlegen von ComVisible auf \"false\" werden die Typen in dieser Assembly unsichtbar \n// für COM-Komponenten.  Wenn Sie auf einen Typ in dieser Assembly von \n// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf \"True\" festlegen.\n[assembly: ComVisible(false)]\n\n// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird\n[assembly: Guid(\"48d1868b-ee81-4339-95c9-4c5eadeb9eca\")]\n\n// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:\n//\n//      Hauptversion\n//      Nebenversion \n//      Buildnummer\n//      Revision\n//\n// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern \n// übernehmen, indem Sie \"*\" eingeben:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.*\")]"
  },
  {
    "path": "TLM/TMPE.GlobalConfigGenerator/TMPE.GlobalConfigGenerator.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"14.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>GlobalConfigGenerator</RootNamespace>\n    <AssemblyName>TMPE.GlobalConfigGenerator</AssemblyName>\n    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'QueuedStats|AnyCPU'\">\n    <OutputPath>bin\\QueuedStats\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <Prefer32Bit>true</Prefer32Bit>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Benchmark|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\Benchmark\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'PF2_Debug|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\PF2_Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Generator.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\" />\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\TLM\\TLM.csproj\">\n      <Project>{7422AE58-8B0A-401C-9404-F4A438EFFE10}</Project>\n      <Name>TLM</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.CodeFixes.dll\" />\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.dll\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "TLM/TMPE.GlobalConfigGenerator/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"StyleCop.Analyzers\" version=\"1.0.2\" targetFramework=\"net35\" developmentDependency=\"true\" />\n</packages>"
  },
  {
    "path": "TLM/TMPE.SpiralLoopTest/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.5.2\" />\n    </startup>\n</configuration>"
  },
  {
    "path": "TLM/TMPE.SpiralLoopTest/Program.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing TrafficManager.Util;\n\nnamespace SpiralLoopTest {\n\tclass Program {\n\t\tstatic void Main(string[] args) {\n\t\t\tLoopUtil.SpiralLoop(0, 0, 3, 3, delegate(int x, int y) {\n\t\t\t\tConsole.WriteLine($\"x={x} y={y}\");\n\t\t\t\treturn true;\n\t\t\t});\n\t\t\tConsole.ReadLine();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.SpiralLoopTest/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// Allgemeine Informationen über eine Assembly werden über die folgenden \n// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,\n// die einer Assembly zugeordnet sind.\n[assembly: AssemblyTitle(\"SpiralLoopTest\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"SpiralLoopTest\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2016\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Durch Festlegen von ComVisible auf \"false\" werden die Typen in dieser Assembly unsichtbar \n// für COM-Komponenten.  Wenn Sie auf einen Typ in dieser Assembly von \n// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf \"True\" festlegen.\n[assembly: ComVisible(false)]\n\n// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird\n[assembly: Guid(\"615ef202-3e13-4a15-a82f-594d90cd0ecd\")]\n\n// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:\n//\n//      Hauptversion\n//      Nebenversion \n//      Buildnummer\n//      Revision\n//\n// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern \n// übernehmen, indem Sie \"*\" eingeben:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "TLM/TMPE.SpiralLoopTest/SpiralLoopTest.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"14.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{615EF202-3E13-4A15-A82F-594D90CD0ECD}</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>SpiralLoopTest</RootNamespace>\n    <AssemblyName>SpiralLoopTest</AssemblyName>\n    <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'QueuedStats|AnyCPU'\">\n    <OutputPath>bin\\QueuedStats\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <Optimize>true</Optimize>\n    <DebugType>pdbonly</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>\n    <Prefer32Bit>true</Prefer32Bit>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Program.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\TLM\\TLM.csproj\">\n      <Project>{7422AE58-8B0A-401C-9404-F4A438EFFE10}</Project>\n      <Name>TLM</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "TLM/TMPE.TestGameBridge/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following \n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"TestGameBridge\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"TestGameBridge\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2017\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible \n// to COM components.  If you need to access a type in this assembly from \n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"97dbfe4d-0db1-43b3-aa35-067c06cf125b\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version \n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers \n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.*\")]"
  },
  {
    "path": "TLM/TMPE.TestGameBridge/TMPE.TestGameBridge.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"14.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{97DBFE4D-0DB1-43B3-AA35-067C06CF125B}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>TestGameBridge</RootNamespace>\n    <AssemblyName>TMPE.TestGameBridge</AssemblyName>\n    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Benchmark|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\Benchmark\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'PF2_Debug|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\PF2_Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Assembly-CSharp\">\n      <HintPath>..\\dependencies\\Assembly-CSharp.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.CodeFixes.dll\" />\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.dll\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "TLM/TMPE.TestGameBridge/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"StyleCop.Analyzers\" version=\"1.0.2\" targetFramework=\"net35\" developmentDependency=\"true\" />\n</packages>"
  },
  {
    "path": "TLM/TMPE.UnitTest/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// Allgemeine Informationen über eine Assembly werden über folgende \n// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,\n// die einer Assembly zugeordnet sind.\n[assembly: AssemblyTitle(\"TMUnitTest\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(\"\")]\n[assembly: AssemblyProduct(\"TMUnitTest\")]\n[assembly: AssemblyCopyright(\"Copyright ©  2017\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Wenn ComVisible auf \"false\" festgelegt wird, sind die Typen innerhalb dieser Assembly \n// für COM-Komponenten unsichtbar.  Wenn Sie auf einen Typ in dieser Assembly von \n// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf \"True\" festlegen.\n[assembly: ComVisible(false)]\n\n// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird\n[assembly: Guid(\"d0d1848a-9bae-4121-89a0-66757d16bc73\")]\n\n// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:\n//\n//      Hauptversion\n//      Nebenversion \n//      Buildnummer\n//      Revision\n//\n// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern \n// übernehmen, indem Sie \"*\" eingeben:\n// [Assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "TLM/TMPE.UnitTest/TMPE.UnitTest.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"14.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{D0D1848A-9BAE-4121-89A0-66757D16BC73}</ProjectGuid>\n    <OutputType>Library</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>TMUnitTest</RootNamespace>\n    <AssemblyName>TMPE.UnitTest</AssemblyName>\n    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\n    <VisualStudioVersion Condition=\"'$(VisualStudioVersion)' == ''\">10.0</VisualStudioVersion>\n    <VSToolsPath Condition=\"'$(VSToolsPath)' == ''\">$(MSBuildExtensionsPath32)\\Microsoft\\VisualStudio\\v$(VisualStudioVersion)</VSToolsPath>\n    <ReferencePath>$(ProgramFiles)\\Common Files\\microsoft shared\\VSTT\\$(VisualStudioVersion)\\UITestExtensionPackages</ReferencePath>\n    <IsCodedUITest>False</IsCodedUITest>\n    <TestProjectType>UnitTest</TestProjectType>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Benchmark|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\Benchmark\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'PF2_Debug|AnyCPU'\">\n    <DebugSymbols>true</DebugSymbols>\n    <OutputPath>bin\\PF2_Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <DebugType>full</DebugType>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <ErrorReport>prompt</ErrorReport>\n    <CodeAnalysisRuleSet>..\\TMPE.ruleset</CodeAnalysisRuleSet>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"Assembly-CSharp\">\n      <HintPath>..\\dependencies\\Assembly-CSharp.dll</HintPath>\n    </Reference>\n    <Reference Include=\"ColossalManaged\">\n      <HintPath>..\\dependencies\\ColossalManaged.dll</HintPath>\n    </Reference>\n    <Reference Include=\"ICities\">\n      <HintPath>..\\dependencies\\ICities.dll</HintPath>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"UnityEngine\">\n      <HintPath>..\\dependencies\\UnityEngine.dll</HintPath>\n    </Reference>\n    <Reference Include=\"UnityEngine.UI\">\n      <HintPath>..\\dependencies\\UnityEngine.UI.dll</HintPath>\n    </Reference>\n  </ItemGroup>\n  <Choose>\n    <When Condition=\"('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'\">\n      <ItemGroup>\n        <Reference Include=\"Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\" />\n      </ItemGroup>\n    </When>\n    <Otherwise>\n      <ItemGroup>\n        <Reference Include=\"Microsoft.VisualStudio.QualityTools.UnitTestFramework\">\n          <Private>False</Private>\n        </Reference>\n      </ItemGroup>\n    </Otherwise>\n  </Choose>\n  <ItemGroup>\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"Util\\TinyDictionaryUnitTest.cs\" />\n    <Compile Include=\"Util\\LogicUtilUnitTest.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\CSUtil.Commons\\CSUtil.Commons.csproj\">\n      <Project>{D3ADE06E-F493-4819-865A-3BB44FEEDF01}</Project>\n      <Name>CSUtil.Commons</Name>\n    </ProjectReference>\n    <ProjectReference Include=\"..\\TLM\\TLM.csproj\">\n      <Project>{7422AE58-8B0A-401C-9404-F4A438EFFE10}</Project>\n      <Name>TLM</Name>\n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"packages.config\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.CodeFixes.dll\" />\n    <Analyzer Include=\"..\\packages\\StyleCop.Analyzers.1.0.2\\analyzers\\dotnet\\cs\\StyleCop.Analyzers.dll\" />\n  </ItemGroup>\n  <Choose>\n    <When Condition=\"'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'\">\n      <ItemGroup>\n        <Reference Include=\"Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n          <Private>False</Private>\n        </Reference>\n        <Reference Include=\"Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n          <Private>False</Private>\n        </Reference>\n        <Reference Include=\"Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n          <Private>False</Private>\n        </Reference>\n        <Reference Include=\"Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\n          <Private>False</Private>\n        </Reference>\n      </ItemGroup>\n    </When>\n  </Choose>\n  <Import Project=\"$(VSToolsPath)\\TeamTest\\Microsoft.TestTools.targets\" Condition=\"Exists('$(VSToolsPath)\\TeamTest\\Microsoft.TestTools.targets')\" />\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "TLM/TMPE.UnitTest/Util/LogicUtilUnitTest.cs",
    "content": "﻿using System;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\nusing CSUtil.Commons;\n\nnamespace TMUnitTest.Util {\n\t[TestClass]\n\tpublic class LogicUtilUnitTest {\n\t\t[TestMethod]\n\t\tpublic void TestCheckFlags1() {\n\t\t\tAssert.IsTrue(LogicUtil.CheckFlags((uint)(NetSegment.Flags.Created | NetSegment.Flags.Deleted), (uint)NetSegment.Flags.Created));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestCheckFlags2() {\n\t\t\tAssert.IsFalse(LogicUtil.CheckFlags((uint)(NetSegment.Flags.Created | NetSegment.Flags.Deleted), (uint)NetSegment.Flags.Collapsed));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestCheckFlags3() {\n\t\t\tAssert.IsTrue(LogicUtil.CheckFlags((uint)(NetSegment.Flags.Created | NetSegment.Flags.Collapsed), (uint)NetSegment.Flags.Created, (uint)NetSegment.Flags.Created));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestCheckFlags4() {\n\t\t\tAssert.IsTrue(LogicUtil.CheckFlags((uint)(NetSegment.Flags.Created | NetSegment.Flags.Collapsed), (uint)(NetSegment.Flags.Created | NetSegment.Flags.Deleted), (uint)NetSegment.Flags.Created));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestCheckFlags5() {\n\t\t\tAssert.IsFalse(LogicUtil.CheckFlags((uint)(NetSegment.Flags.Created | NetSegment.Flags.Deleted | NetSegment.Flags.Collapsed), (uint)(NetSegment.Flags.Created | NetSegment.Flags.Deleted), (uint)NetSegment.Flags.Created));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.UnitTest/Util/TinyDictionaryUnitTest.cs",
    "content": "﻿using System;\nusing System.Text;\nusing System.Collections.Generic;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\nusing TrafficManager.Util;\nusing TrafficManager.Traffic;\nusing CSUtil.Commons;\n\nnamespace TMUnitTest.Util {\n\t[TestClass]\n\tpublic class TinyDictionaryUnitTest {\n\t\tprivate TinyDictionary<byte, byte> dict0;\n\t\tprivate TinyDictionary<string, int> dict1;\n\t\tprivate TinyDictionary<string, IList<string>> dict2;\n\t\tprivate TinyDictionary<ICollection<string>, bool> dict3;\n\t\tprivate TinyDictionary<ushort, IDictionary<ushort, ArrowDirection>> dict4;\n\n\t\tprivate static string alice, bob, cedric, dora;\n\t\tprivate static IList<string> alicesNicknames, bobsNicknames, cedricsNicknames, dorasNicknames;\n\t\tprivate static ICollection<string> girls, boys;\n\n\t\t#region Zusätzliche Testattribute\n\t\t//\n\t\t// Sie können beim Schreiben der Tests folgende zusätzliche Attribute verwenden:\n\t\t//\n\t\t// Verwenden Sie ClassInitialize, um vor Ausführung des ersten Tests in der Klasse Code auszuführen.\n\t\t// [ClassInitialize()]\n\t\t// public static void MyClassInitialize(TestContext testContext) { }\n\t\t//\n\t\t// Verwenden Sie ClassCleanup, um nach Ausführung aller Tests in einer Klasse Code auszuführen.\n\t\t// [ClassCleanup()]\n\t\t// public static void MyClassCleanup() { }\n\t\t//\n\t\t// Mit TestInitialize können Sie vor jedem einzelnen Test Code ausführen. \n\t\t// [TestInitialize()]\n\t\t// public void MyTestInitialize() { }\n\t\t//\n\t\t// Mit TestCleanup können Sie nach jedem Test Code ausführen.\n\t\t// [TestCleanup()]\n\t\t// public void MyTestCleanup() { }\n\t\t//\n\t\t#endregion\n\n\t\t[ClassInitialize]\n\t\tpublic static void InitializeClass(TestContext testContext) {\n\t\t\talice = \"Alice\";\n\t\t\tbob = \"Bob\";\n\t\t\tcedric = \"Cedric\";\n\t\t\tdora = \"Dora\";\n\n\t\t\talicesNicknames = null;\n\t\t\tbobsNicknames = new List<string> { \"Bobby\", \"Bobbi\", \"Bobb\" };\n\t\t\tcedricsNicknames = new List<string> { \"Ced\" };\n\t\t\tdorasNicknames = new List<string>();\n\n\t\t\tgirls = new HashSet<string> { alice, dora };\n\t\t\tboys = new HashSet<string> { bob, cedric };\n\t\t}\n\n\t\t[TestInitialize()]\n\t\tpublic void InitializeTest() {\n\t\t\tdict0 = new TinyDictionary<byte, byte>();\n\n\t\t\tdict1 = new TinyDictionary<string, int>();\n\t\t\tdict1.Add(alice, 1);\n\t\t\tdict1.Add(bob, 2);\n\t\t\tdict1.Add(cedric, 3);\n\n\t\t\tdict2 = new TinyDictionary<string, IList<string>>();\n\t\t\tdict2.Add(bob, bobsNicknames);\n\t\t\tdict2.Add(cedric, cedricsNicknames);\n\t\t\tdict2.Add(dora, dorasNicknames);\n\t\t\tdict2.Add(alice, alicesNicknames);\n\n\t\t\tdict3 = new TinyDictionary<ICollection<string>, bool>();\n\t\t\tdict3.Add(girls, true);\n\t\t\tdict3.Add(boys, false);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestKeys0() {\n\t\t\tICollection<byte> keys = dict0.Keys;\n\t\t\tAssert.IsNotNull(keys);\n\t\t\tAssert.AreEqual(0, keys.Count);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestKeys1() {\n\t\t\tICollection<string> keys = dict1.Keys;\n\t\t\tAssert.IsNotNull(keys);\n\t\t\tAssert.AreEqual(3, keys.Count);\n\t\t\tAssert.IsTrue(keys.Contains(alice));\n\t\t\tAssert.IsTrue(keys.Contains(bob));\n\t\t\tAssert.IsTrue(keys.Contains(cedric));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestKeys2() {\n\t\t\tICollection<string> keys = dict2.Keys;\n\t\t\tAssert.IsNotNull(keys);\n\t\t\tAssert.AreEqual(4, keys.Count);\n\t\t\tAssert.IsTrue(keys.Contains(alice));\n\t\t\tAssert.IsTrue(keys.Contains(bob));\n\t\t\tAssert.IsTrue(keys.Contains(cedric));\n\t\t\tAssert.IsTrue(keys.Contains(dora));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestKeys3() {\n\t\t\tICollection<ICollection<string>> keys = dict3.Keys;\n\t\t\tAssert.IsNotNull(keys);\n\t\t\tAssert.AreEqual(2, keys.Count);\n\t\t\tAssert.IsTrue(keys.Contains(girls));\n\t\t\tAssert.IsTrue(keys.Contains(boys));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestValues0() {\n\t\t\tICollection<byte> values = dict0.Values;\n\t\t\tAssert.IsNotNull(values);\n\t\t\tAssert.AreEqual(0, values.Count);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestValues1() {\n\t\t\tICollection<int> values = dict1.Values;\n\t\t\tAssert.IsNotNull(values);\n\t\t\tAssert.AreEqual(3, values.Count);\n\t\t\tAssert.IsTrue(values.Contains(1));\n\t\t\tAssert.IsTrue(values.Contains(2));\n\t\t\tAssert.IsTrue(values.Contains(3));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestValues2() {\n\t\t\tICollection<IList<string>> values = dict2.Values;\n\t\t\tAssert.IsNotNull(values);\n\t\t\tAssert.AreEqual(4, values.Count);\n\t\t\tAssert.IsTrue(values.Contains(alicesNicknames));\n\t\t\tAssert.IsTrue(values.Contains(bobsNicknames));\n\t\t\tAssert.IsTrue(values.Contains(cedricsNicknames));\n\t\t\tAssert.IsTrue(values.Contains(dorasNicknames));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestValues3() {\n\t\t\tICollection<bool> values = dict3.Values;\n\t\t\tAssert.IsNotNull(values);\n\t\t\tAssert.AreEqual(2, values.Count);\n\t\t\tAssert.IsTrue(values.Contains(true));\n\t\t\tAssert.IsTrue(values.Contains(false));\n\t\t}\n\n\t\t[TestMethod]\n\t\t[ExpectedException(typeof(ArgumentNullException))]\n\t\tpublic void TestGetNull() {\n\t\t\tint val = dict1[null];\n\t\t}\n\n\t\t[TestMethod]\n\t\t[ExpectedException(typeof(KeyNotFoundException))]\n\t\tpublic void TestGetNotFound() {\n\t\t\tint val = dict1[\"Santa Claus\"];\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestGet1() {\n\t\t\tint val = dict1[cedric];\n\t\t\tAssert.AreEqual(3, val);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestGet2() {\n\t\t\tIList<string> val = dict2[alice];\n\t\t\tAssert.AreEqual(alicesNicknames, val);\n\n\t\t\tval = dict2[bob];\n\t\t\tAssert.AreEqual(bobsNicknames, val);\n\n\t\t\tval = dict2[cedric];\n\t\t\tAssert.AreEqual(cedricsNicknames, val);\n\n\t\t\tval = dict2[dora];\n\t\t\tAssert.AreEqual(dorasNicknames, val);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestGet3() {\n\t\t\tbool val = dict3[girls];\n\t\t\tAssert.IsTrue(val);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestSet0() {\n\t\t\tdict0[1] = 2;\n\t\t\tAssert.AreEqual(dict0[1], 2);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestSet1() {\n\t\t\tdict1[\"Eugen\"] = 42;\n\t\t\tAssert.AreEqual(dict1[\"Eugen\"], 42);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestSet2() {\n\t\t\tIList<string> eugensNicknames = new List<string> { \"Eugenius\" };\n\t\t\tdict2[\"Eugen\"] = eugensNicknames;\n\t\t\tAssert.AreEqual(dict2[\"Eugen\"], eugensNicknames);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestSet3() {\n\t\t\tdict3[girls] = false;\n\t\t\tAssert.AreEqual(dict3[girls], false);\n\t\t\tAssert.AreEqual(dict3[boys], false);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestContainsKey0() {\n\t\t\tAssert.IsFalse(dict0.ContainsKey(0));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestContainsKey1() {\n\t\t\tAssert.IsTrue(dict1.ContainsKey(alice));\n\t\t\tAssert.IsTrue(dict1.ContainsKey(bob));\n\t\t\tAssert.IsTrue(dict1.ContainsKey(cedric));\n\t\t\tAssert.IsFalse(dict1.ContainsKey(dora));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestContainsKey2() {\n\t\t\tAssert.IsTrue(dict2.ContainsKey(alice));\n\t\t\tAssert.IsTrue(dict2.ContainsKey(bob));\n\t\t\tAssert.IsTrue(dict2.ContainsKey(cedric));\n\t\t\tAssert.IsTrue(dict2.ContainsKey(dora));\n\t\t\tAssert.IsFalse(dict2.ContainsKey(\"Eugen\"));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestContainsKey3() {\n\t\t\tAssert.IsTrue(dict3.ContainsKey(girls));\n\t\t\tAssert.IsTrue(dict3.ContainsKey(boys));\n\t\t\tAssert.IsFalse(dict3.ContainsKey(new HashSet<string> { \"Chair\" }));\n\t\t\tAssert.IsFalse(dict3.ContainsKey(null));\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestAddRemove() {\n\t\t\tdict0.Add(1, 5);\n\t\t\tdict0.Add(5, 1);\n\t\t\tdict0.Add(2, 2);\n\t\t\tAssert.AreEqual(3, dict0.Count);\n\t\t\tAssert.AreEqual(5, dict0[1]);\n\t\t\tAssert.AreEqual(1, dict0[5]);\n\t\t\tAssert.AreEqual(2, dict0[2]);\n\t\t\tdict0.Remove(1);\n\t\t\tAssert.AreEqual(2, dict0.Count);\n\t\t\tAssert.IsFalse(dict0.ContainsKey(1));\n\t\t\tdict0.Add(2, 3);\n\t\t\tAssert.AreEqual(2, dict0.Count);\n\t\t\tAssert.AreEqual(3, dict0[2]);\n\t\t\tdict0.Add(3, 0);\n\t\t\tAssert.AreEqual(3, dict0.Count);\n\t\t\tAssert.AreEqual(0, dict0[3]);\n\t\t\tdict0.Add(1, 4);\n\t\t\tAssert.AreEqual(4, dict0.Count);\n\t\t\tAssert.AreEqual(4, dict0[1]);\n\t\t\tdict0.Add(1, 8);\n\t\t\tAssert.AreEqual(4, dict0.Count);\n\t\t\tAssert.AreEqual(8, dict0[1]);\n\t\t\tdict0.Remove(1);\n\t\t\tAssert.AreEqual(3, dict0.Count);\n\t\t\tdict0.Add(1, 2);\n\t\t\tAssert.AreEqual(4, dict0.Count);\n\t\t\tAssert.AreEqual(2, dict0[1]);\n\t\t\tAssert.AreEqual(3, dict0[2]);\n\t\t\tAssert.AreEqual(0, dict0[3]);\n\t\t\tAssert.AreEqual(1, dict0[5]);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestTryGetValue1() {\n\t\t\tint val;\n\t\t\tAssert.IsTrue(dict1.TryGetValue(bob, out val));\n\t\t\tAssert.AreEqual(2, val);\n\n\t\t\tAssert.IsFalse(dict1.TryGetValue(dora, out val));\n\t\t\tAssert.AreEqual(default(int), val);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestClear() {\n\t\t\tdict1.Clear();\n\t\t\tAssert.AreEqual(0, dict1.Count);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestCopyTo() {\n\t\t\tKeyValuePair<string, IList<string>>[] a = new KeyValuePair<string, IList<string>>[4];\n\t\t\tdict2.CopyTo(a, 0);\n\n\t\t\tbool[] r = new bool[4];\n\t\t\tforeach (KeyValuePair<string, IList<string>> e in a) {\n\t\t\t\tif (alice.Equals(e.Key) && e.Value == null) {\n\t\t\t\t\tr[0] = true;\n\t\t\t\t} else if (bob.Equals(e.Key) && bobsNicknames.Equals(e.Value)) {\n\t\t\t\t\tr[1] = true;\n\t\t\t\t} else if (cedric.Equals(e.Key) && cedricsNicknames.Equals(e.Value)) {\n\t\t\t\t\tr[2] = true;\n\t\t\t\t} else if (dora.Equals(e.Key) && dorasNicknames.Equals(e.Value)) {\n\t\t\t\t\tr[3] = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tforeach (bool rr in r) {\n\t\t\t\tAssert.IsTrue(rr);\n\t\t\t}\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestEnumeration() {\n\t\t\tint i = 0;\n\t\t\tbool[] r = new bool[3];\n\t\t\tforeach (KeyValuePair<string, int> e in dict1) {\n\t\t\t\tif (alice.Equals(e.Key)) {\n\t\t\t\t\tAssert.AreEqual(1, e.Value);\n\t\t\t\t} else if (bob.Equals(e.Key)) {\n\t\t\t\t\tAssert.AreEqual(2, e.Value);\n\t\t\t\t} else if (cedric.Equals(e.Key)) {\n\t\t\t\t\tAssert.AreEqual(3, e.Value);\n\t\t\t\t}\n\t\t\t\tr[e.Value - 1] = true;\n\t\t\t\t++i;\n\t\t\t}\n\t\t\tAssert.AreEqual(3, i);\n\n\t\t\tforeach (bool rr in r) {\n\t\t\t\tAssert.IsTrue(rr);\n\t\t\t}\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestEnumerationAfterModification() {\n\t\t\tdict1[alice] = 5;\n\n\t\t\tint i = 0;\n\t\t\tbool[] r = new bool[3];\n\t\t\tforeach (KeyValuePair<string, int> e in dict1) {\n\t\t\t\tif (alice.Equals(e.Key)) {\n\t\t\t\t\tAssert.AreEqual(5, e.Value);\n\t\t\t\t\tr[0] = true;\n\t\t\t\t} else if (bob.Equals(e.Key)) {\n\t\t\t\t\tAssert.AreEqual(2, e.Value);\n\t\t\t\t\tr[1] = true;\n\t\t\t\t} else if (cedric.Equals(e.Key)) {\n\t\t\t\t\tAssert.AreEqual(3, e.Value);\n\t\t\t\t\tr[2] = true;\n\t\t\t\t}\n\t\t\t\t++i;\n\t\t\t}\n\t\t\tAssert.AreEqual(3, i);\n\n\t\t\tforeach (bool rr in r) {\n\t\t\t\tAssert.IsTrue(rr);\n\t\t\t}\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestEnumerationAfterRemoval() {\n\t\t\tdict1.Remove(alice);\n\n\t\t\tint i = 0;\n\t\t\tbool[] r = new bool[2];\n\t\t\tforeach (KeyValuePair<string, int> e in dict1) {\n\t\t\t\tif (bob.Equals(e.Key)) {\n\t\t\t\t\tAssert.AreEqual(2, e.Value);\n\t\t\t\t\tr[0] = true;\n\t\t\t\t} else if (cedric.Equals(e.Key)) {\n\t\t\t\t\tAssert.AreEqual(3, e.Value);\n\t\t\t\t\tr[1] = true;\n\t\t\t\t}\n\t\t\t\t++i;\n\t\t\t}\n\t\t\tAssert.AreEqual(2, i);\n\n\t\t\tforeach (bool rr in r) {\n\t\t\t\tAssert.IsTrue(rr);\n\t\t\t}\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestIncrement() {\n\t\t\t++dict1[alice];\n\t\t\tAssert.AreEqual(2, dict1[alice]);\n\t\t\tAssert.AreEqual(2, dict1[bob]);\n\t\t\tAssert.AreEqual(3, dict1[cedric]);\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestArithmeticAssignment() {\n\t\t\tdict1[bob] *= 4;\n\t\t\tAssert.AreEqual(1, dict1[alice]);\n\t\t\tAssert.AreEqual(8, dict1[bob]);\n\t\t\tAssert.AreEqual(3, dict1[cedric]);\n\t\t}\n\n\t\t[TestMethod]\n\t\t[ExpectedException(typeof(KeyNotFoundException))]\n\t\tpublic void TestArithmeticAssignmentOnNonExistingKey() {\n\t\t\tdict1[dora]++;\n\t\t}\n\n\t\t[TestMethod]\n\t\tpublic void TestNested() {\n\t\t\tdict4 = new TinyDictionary<ushort, IDictionary<ushort, ArrowDirection>>();\n\n\t\t\tIDictionary<ushort, ArrowDirection> innerDict1 = new TinyDictionary<ushort, ArrowDirection>();\n\t\t\tdict4.Add(1270, innerDict1);\n\t\t\tinnerDict1.Add(1270, ArrowDirection.Turn);\n\t\t\tinnerDict1.Add(14929, ArrowDirection.Forward);\n\t\t\tinnerDict1.Add(26395, ArrowDirection.Left);\n\n\t\t\tIDictionary<ushort, ArrowDirection> innerDict2 = new TinyDictionary<ushort, ArrowDirection>();\n\t\t\tdict4.Add(14929, innerDict2);\n\t\t\tinnerDict2.Add(1270, ArrowDirection.Forward);\n\t\t\tinnerDict2.Add(14929, ArrowDirection.Turn);\n\t\t\tinnerDict2.Add(26395, ArrowDirection.Right);\n\n\t\t\tIDictionary<ushort, ArrowDirection> innerDict3 = new TinyDictionary<ushort, ArrowDirection>();\n\t\t\tdict4.Add(26395, innerDict3);\n\t\t\tinnerDict3.Add(1270, ArrowDirection.Right);\n\t\t\tinnerDict3.Add(14929, ArrowDirection.Left);\n\t\t\tinnerDict3.Add(26395, ArrowDirection.Turn);\n\n\t\t\tAssert.AreEqual(3, dict4.Count);\n\n\t\t\tAssert.IsTrue(dict4.ContainsKey(1270));\n\t\t\tAssert.IsTrue(dict4.ContainsKey(14929));\n\t\t\tAssert.IsTrue(dict4.ContainsKey(26395));\n\n\t\t\tAssert.AreEqual(3, dict4[1270].Count);\n\t\t\tAssert.AreEqual(3, dict4[14929].Count);\n\t\t\tAssert.AreEqual(3, dict4[26395].Count);\n\n\t\t\tAssert.IsTrue(dict4[1270].ContainsKey(1270));\n\t\t\tAssert.IsTrue(dict4[1270].ContainsKey(14929));\n\t\t\tAssert.IsTrue(dict4[1270].ContainsKey(26395));\n\n\t\t\tAssert.IsTrue(dict4[14929].ContainsKey(1270));\n\t\t\tAssert.IsTrue(dict4[14929].ContainsKey(14929));\n\t\t\tAssert.IsTrue(dict4[14929].ContainsKey(26395));\n\n\t\t\tAssert.IsTrue(dict4[26395].ContainsKey(1270));\n\t\t\tAssert.IsTrue(dict4[26395].ContainsKey(14929));\n\t\t\tAssert.IsTrue(dict4[26395].ContainsKey(26395));\n\n\t\t\tAssert.AreEqual(ArrowDirection.Turn, dict4[1270][1270]);\n\t\t\tAssert.AreEqual(ArrowDirection.Forward, dict4[1270][14929]);\n\t\t\tAssert.AreEqual(ArrowDirection.Left, dict4[1270][26395]);\n\n\t\t\tAssert.AreEqual(ArrowDirection.Forward, dict4[14929][1270]);\n\t\t\tAssert.AreEqual(ArrowDirection.Turn, dict4[14929][14929]);\n\t\t\tAssert.AreEqual(ArrowDirection.Right, dict4[14929][26395]);\n\n\t\t\tAssert.AreEqual(ArrowDirection.Right, dict4[26395][1270]);\n\t\t\tAssert.AreEqual(ArrowDirection.Left, dict4[26395][14929]);\n\t\t\tAssert.AreEqual(ArrowDirection.Turn, dict4[26395][26395]);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "TLM/TMPE.UnitTest/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"StyleCop.Analyzers\" version=\"1.0.2\" targetFramework=\"net35\" developmentDependency=\"true\" />\n</packages>"
  },
  {
    "path": "TLM/TMPE.ruleset",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RuleSet Name=\"Rules for TLM\" Description=\"Code analysis rules for TLM\" ToolsVersion=\"15.0\">\n  <Rules AnalyzerId=\"StyleCop.Analyzers\" RuleNamespace=\"StyleCop.Analyzers\">\n    <Rule Id=\"SA1000\" Action=\"None\" />\n    <Rule Id=\"SA1005\" Action=\"None\" />\n    <Rule Id=\"SA1027\" Action=\"None\" />\n    <Rule Id=\"SA1028\" Action=\"None\" />\n    <Rule Id=\"SA1100\" Action=\"None\" />\n    <Rule Id=\"SA1101\" Action=\"None\" />\n    <Rule Id=\"SA1107\" Action=\"None\" />\n    <Rule Id=\"SA1115\" Action=\"None\" />\n    <Rule Id=\"SA1116\" Action=\"None\" />\n    <Rule Id=\"SA1118\" Action=\"None\" />\n    <Rule Id=\"SA1200\" Action=\"None\" />\n    <Rule Id=\"SA1208\" Action=\"None\" />\n    <Rule Id=\"SA1210\" Action=\"None\" />\n    <Rule Id=\"SA1211\" Action=\"None\" />\n    <Rule Id=\"SA1216\" Action=\"None\" />\n    <Rule Id=\"SA1217\" Action=\"None\" />\n    <Rule Id=\"SA1308\" Action=\"Info\" />\n    <Rule Id=\"SA1309\" Action=\"Hidden\" />\n    <Rule Id=\"SA1310\" Action=\"None\" />\n    <Rule Id=\"SA1500\" Action=\"None\" />\n    <Rule Id=\"SA1503\" Action=\"Info\" />\n    <Rule Id=\"SA1505\" Action=\"Hidden\" />\n    <Rule Id=\"SA1512\" Action=\"None\" />\n    <Rule Id=\"SA1513\" Action=\"None\" />\n    <Rule Id=\"SA1515\" Action=\"None\" />\n    <Rule Id=\"SA1516\" Action=\"Hidden\" />\n    <Rule Id=\"SA1633\" Action=\"None\" />\n    <Rule Id=\"SA1634\" Action=\"None\" />\n    <Rule Id=\"SA1635\" Action=\"None\" />\n    <Rule Id=\"SA1636\" Action=\"None\" />\n    <Rule Id=\"SA1637\" Action=\"None\" />\n    <Rule Id=\"SA1638\" Action=\"None\" />\n    <Rule Id=\"SA1640\" Action=\"None\" />\n    <Rule Id=\"SA1652\" Action=\"None\" />\n  </Rules>\n</RuleSet>"
  },
  {
    "path": "TLM/TMPE.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 14\nVisualStudioVersion = 14.0.25420.1\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"TLM\", \"TLM\\TLM.csproj\", \"{7422AE58-8B0A-401C-9404-F4A438EFFE10}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{663B991F-32A1-46E1-A4D3-540F8EA7F386} = {663B991F-32A1-46E1-A4D3-540F8EA7F386}\n\t\t{3F2F7926-5D51-4880-A2B7-4594A10D7E54} = {3F2F7926-5D51-4880-A2B7-4594A10D7E54}\n\t\t{D3ADE06E-F493-4819-865A-3BB44FEEDF01} = {D3ADE06E-F493-4819-865A-3BB44FEEDF01}\n\t\t{F8759084-DF5B-4A54-B73C-824640A8FA3F} = {F8759084-DF5B-4A54-B73C-824640A8FA3F}\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{1D3D9CF0-947E-4A1A-BCFE-25F48E7908DF}\"\n\tProjectSection(SolutionItems) = preProject\n\t\t..\\README.md = ..\\README.md\n\tEndProjectSection\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"TMPE.GlobalConfigGenerator\", \"TMPE.GlobalConfigGenerator\\TMPE.GlobalConfigGenerator.csproj\", \"{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{7422AE58-8B0A-401C-9404-F4A438EFFE10} = {7422AE58-8B0A-401C-9404-F4A438EFFE10}\n\tEndProjectSection\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"TMPE.UnitTest\", \"TMPE.UnitTest\\TMPE.UnitTest.csproj\", \"{D0D1848A-9BAE-4121-89A0-66757D16BC73}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"TMPE.GenericGameBridge\", \"TMPE.GenericGameBridge\\TMPE.GenericGameBridge.csproj\", \"{663B991F-32A1-46E1-A4D3-540F8EA7F386}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"TMPE.CitiesGameBridge\", \"TMPE.CitiesGameBridge\\TMPE.CitiesGameBridge.csproj\", \"{3F2F7926-5D51-4880-A2B7-4594A10D7E54}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{D3ADE06E-F493-4819-865A-3BB44FEEDF01} = {D3ADE06E-F493-4819-865A-3BB44FEEDF01}\n\tEndProjectSection\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"TMPE.TestGameBridge\", \"TMPE.TestGameBridge\\TMPE.TestGameBridge.csproj\", \"{97DBFE4D-0DB1-43B3-AA35-067C06CF125B}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"CSUtil.Commons\", \"CSUtil.Commons\\CSUtil.Commons.csproj\", \"{D3ADE06E-F493-4819-865A-3BB44FEEDF01}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"OptionsFramework\", \"OptionsFramework\\OptionsFramework.csproj\", \"{F4BEABA8-E56B-4201-99C8-5E0115E87D1C}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"CSUtil.CameraControl\", \"CSUtil.CameraControl\\CSUtil.CameraControl\\CSUtil.CameraControl\\CSUtil.CameraControl.csproj\", \"{F8759084-DF5B-4A54-B73C-824640A8FA3F}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tBenchmark|Any CPU = Benchmark|Any CPU\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tFullDebug|Any CPU = FullDebug|Any CPU\n\t\tPF2_Debug|Any CPU = PF2_Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{7422AE58-8B0A-401C-9404-F4A438EFFE10}.Benchmark|Any CPU.ActiveCfg = Benchmark|Any CPU\n\t\t{7422AE58-8B0A-401C-9404-F4A438EFFE10}.Benchmark|Any CPU.Build.0 = Benchmark|Any CPU\n\t\t{7422AE58-8B0A-401C-9404-F4A438EFFE10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{7422AE58-8B0A-401C-9404-F4A438EFFE10}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{7422AE58-8B0A-401C-9404-F4A438EFFE10}.FullDebug|Any CPU.ActiveCfg = FullDebug|Any CPU\n\t\t{7422AE58-8B0A-401C-9404-F4A438EFFE10}.FullDebug|Any CPU.Build.0 = FullDebug|Any CPU\n\t\t{7422AE58-8B0A-401C-9404-F4A438EFFE10}.PF2_Debug|Any CPU.ActiveCfg = PF2_Debug|Any CPU\n\t\t{7422AE58-8B0A-401C-9404-F4A438EFFE10}.PF2_Debug|Any CPU.Build.0 = PF2_Debug|Any CPU\n\t\t{7422AE58-8B0A-401C-9404-F4A438EFFE10}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{7422AE58-8B0A-401C-9404-F4A438EFFE10}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Benchmark|Any CPU.ActiveCfg = Benchmark|Any CPU\n\t\t{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Benchmark|Any CPU.Build.0 = Benchmark|Any CPU\n\t\t{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.FullDebug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.PF2_Debug|Any CPU.ActiveCfg = PF2_Debug|Any CPU\n\t\t{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.PF2_Debug|Any CPU.Build.0 = PF2_Debug|Any CPU\n\t\t{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{D0D1848A-9BAE-4121-89A0-66757D16BC73}.Benchmark|Any CPU.ActiveCfg = Benchmark|Any CPU\n\t\t{D0D1848A-9BAE-4121-89A0-66757D16BC73}.Benchmark|Any CPU.Build.0 = Benchmark|Any CPU\n\t\t{D0D1848A-9BAE-4121-89A0-66757D16BC73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D0D1848A-9BAE-4121-89A0-66757D16BC73}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D0D1848A-9BAE-4121-89A0-66757D16BC73}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D0D1848A-9BAE-4121-89A0-66757D16BC73}.FullDebug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D0D1848A-9BAE-4121-89A0-66757D16BC73}.PF2_Debug|Any CPU.ActiveCfg = PF2_Debug|Any CPU\n\t\t{D0D1848A-9BAE-4121-89A0-66757D16BC73}.PF2_Debug|Any CPU.Build.0 = PF2_Debug|Any CPU\n\t\t{D0D1848A-9BAE-4121-89A0-66757D16BC73}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{D0D1848A-9BAE-4121-89A0-66757D16BC73}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{663B991F-32A1-46E1-A4D3-540F8EA7F386}.Benchmark|Any CPU.ActiveCfg = Benchmark|Any CPU\n\t\t{663B991F-32A1-46E1-A4D3-540F8EA7F386}.Benchmark|Any CPU.Build.0 = Benchmark|Any CPU\n\t\t{663B991F-32A1-46E1-A4D3-540F8EA7F386}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{663B991F-32A1-46E1-A4D3-540F8EA7F386}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{663B991F-32A1-46E1-A4D3-540F8EA7F386}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{663B991F-32A1-46E1-A4D3-540F8EA7F386}.FullDebug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{663B991F-32A1-46E1-A4D3-540F8EA7F386}.PF2_Debug|Any CPU.ActiveCfg = PF2_Debug|Any CPU\n\t\t{663B991F-32A1-46E1-A4D3-540F8EA7F386}.PF2_Debug|Any CPU.Build.0 = PF2_Debug|Any CPU\n\t\t{663B991F-32A1-46E1-A4D3-540F8EA7F386}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{663B991F-32A1-46E1-A4D3-540F8EA7F386}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{3F2F7926-5D51-4880-A2B7-4594A10D7E54}.Benchmark|Any CPU.ActiveCfg = Benchmark|Any CPU\n\t\t{3F2F7926-5D51-4880-A2B7-4594A10D7E54}.Benchmark|Any CPU.Build.0 = Benchmark|Any CPU\n\t\t{3F2F7926-5D51-4880-A2B7-4594A10D7E54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{3F2F7926-5D51-4880-A2B7-4594A10D7E54}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{3F2F7926-5D51-4880-A2B7-4594A10D7E54}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{3F2F7926-5D51-4880-A2B7-4594A10D7E54}.FullDebug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{3F2F7926-5D51-4880-A2B7-4594A10D7E54}.PF2_Debug|Any CPU.ActiveCfg = PF2_Debug|Any CPU\n\t\t{3F2F7926-5D51-4880-A2B7-4594A10D7E54}.PF2_Debug|Any CPU.Build.0 = PF2_Debug|Any CPU\n\t\t{3F2F7926-5D51-4880-A2B7-4594A10D7E54}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{3F2F7926-5D51-4880-A2B7-4594A10D7E54}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{97DBFE4D-0DB1-43B3-AA35-067C06CF125B}.Benchmark|Any CPU.ActiveCfg = Benchmark|Any CPU\n\t\t{97DBFE4D-0DB1-43B3-AA35-067C06CF125B}.Benchmark|Any CPU.Build.0 = Benchmark|Any CPU\n\t\t{97DBFE4D-0DB1-43B3-AA35-067C06CF125B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{97DBFE4D-0DB1-43B3-AA35-067C06CF125B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{97DBFE4D-0DB1-43B3-AA35-067C06CF125B}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{97DBFE4D-0DB1-43B3-AA35-067C06CF125B}.FullDebug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{97DBFE4D-0DB1-43B3-AA35-067C06CF125B}.PF2_Debug|Any CPU.ActiveCfg = PF2_Debug|Any CPU\n\t\t{97DBFE4D-0DB1-43B3-AA35-067C06CF125B}.PF2_Debug|Any CPU.Build.0 = PF2_Debug|Any CPU\n\t\t{97DBFE4D-0DB1-43B3-AA35-067C06CF125B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{97DBFE4D-0DB1-43B3-AA35-067C06CF125B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{D3ADE06E-F493-4819-865A-3BB44FEEDF01}.Benchmark|Any CPU.ActiveCfg = Benchmark|Any CPU\n\t\t{D3ADE06E-F493-4819-865A-3BB44FEEDF01}.Benchmark|Any CPU.Build.0 = Benchmark|Any CPU\n\t\t{D3ADE06E-F493-4819-865A-3BB44FEEDF01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D3ADE06E-F493-4819-865A-3BB44FEEDF01}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D3ADE06E-F493-4819-865A-3BB44FEEDF01}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D3ADE06E-F493-4819-865A-3BB44FEEDF01}.FullDebug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D3ADE06E-F493-4819-865A-3BB44FEEDF01}.PF2_Debug|Any CPU.ActiveCfg = PF2_Debug|Any CPU\n\t\t{D3ADE06E-F493-4819-865A-3BB44FEEDF01}.PF2_Debug|Any CPU.Build.0 = PF2_Debug|Any CPU\n\t\t{D3ADE06E-F493-4819-865A-3BB44FEEDF01}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{D3ADE06E-F493-4819-865A-3BB44FEEDF01}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{F4BEABA8-E56B-4201-99C8-5E0115E87D1C}.Benchmark|Any CPU.ActiveCfg = Benchmark|Any CPU\n\t\t{F4BEABA8-E56B-4201-99C8-5E0115E87D1C}.Benchmark|Any CPU.Build.0 = Benchmark|Any CPU\n\t\t{F4BEABA8-E56B-4201-99C8-5E0115E87D1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F4BEABA8-E56B-4201-99C8-5E0115E87D1C}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F4BEABA8-E56B-4201-99C8-5E0115E87D1C}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F4BEABA8-E56B-4201-99C8-5E0115E87D1C}.FullDebug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F4BEABA8-E56B-4201-99C8-5E0115E87D1C}.PF2_Debug|Any CPU.ActiveCfg = PF2_Debug|Any CPU\n\t\t{F4BEABA8-E56B-4201-99C8-5E0115E87D1C}.PF2_Debug|Any CPU.Build.0 = PF2_Debug|Any CPU\n\t\t{F4BEABA8-E56B-4201-99C8-5E0115E87D1C}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{F4BEABA8-E56B-4201-99C8-5E0115E87D1C}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{F8759084-DF5B-4A54-B73C-824640A8FA3F}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{F8759084-DF5B-4A54-B73C-824640A8FA3F}.Benchmark|Any CPU.Build.0 = Release|Any CPU\n\t\t{F8759084-DF5B-4A54-B73C-824640A8FA3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F8759084-DF5B-4A54-B73C-824640A8FA3F}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F8759084-DF5B-4A54-B73C-824640A8FA3F}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F8759084-DF5B-4A54-B73C-824640A8FA3F}.FullDebug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F8759084-DF5B-4A54-B73C-824640A8FA3F}.PF2_Debug|Any CPU.ActiveCfg = PF2_Debug|Any CPU\n\t\t{F8759084-DF5B-4A54-B73C-824640A8FA3F}.PF2_Debug|Any CPU.Build.0 = PF2_Debug|Any CPU\n\t\t{F8759084-DF5B-4A54-B73C-824640A8FA3F}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{F8759084-DF5B-4A54-B73C-824640A8FA3F}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\nEndGlobal\n"
  }
]