Skip to content

Commit

Permalink
TMPE 1.8.4:
Browse files Browse the repository at this point in the history
- New feature: "Stay on lane": By pressing Shift + S in the Lane Connector tool you can now link connected lanes such that vehicles are not allowed to change lanes at this point. Press Shift + S again to restrict "stay on lane" to either road direction.
- U-turns are now only allowed to be performed from the innermost lane
- TMPE now detects if the number of spawned vehicles is reaching its limit (16384). If so, spawning of service/emergency vehicles is prioritized over spawning other vehicles.
- Bugfix: Bicycles cannot change from bicycle lanes to pedestrian lanes
- Bugfix: Travel probabilities set in the "Citizen Lifecycle Rebalance v2.1" mod are not obeyed (thanks to @informmanuel, @shaundoddmusic for reporting this issue)
- Bugfix: Number of tourists seems to drop when activating the mod (statistics were not updated, thanks to @hpp7117, @wjrohn for reporting this issue)
- Bugfix: When loading a second savegame a second main menu button is displayed (thanks to @cpt. Whitepaw for reporting this issue)
- Bugfix: While path-finding is in progress vehicles do "bungee-jumping" on the current segment (thanks to @mxolsenx, @Howzitworld for reporting this issue)
- Bugfix: Cims leaving the city search for parking spaces near the outside connection which is obviously not required
VictorPhilipp committed Dec 11, 2016
1 parent a09d91c commit 99f75e2
Showing 23 changed files with 631 additions and 413 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -4,6 +4,17 @@ A work-in-progress modification for **Cities: Skylines** to add additional traff
User manual: http://www.viathinksoft.de/tmpe

# Changelog
1.8.4, 12/11/2016
- New feature: "Stay on lane": By pressing Shift + S in the Lane Connector tool you can now link connected lanes such that vehicles are not allowed to change lanes at this point. Press Shift + S again to restrict "stay on lane" to either road direction.
- U-turns are now only allowed to be performed from the innermost lane
- TMPE now detects if the number of spawned vehicles is reaching its limit (16384). If so, spawning of service/emergency vehicles is prioritized over spawning other vehicles.
- Bugfix: Bicycles cannot change from bicycle lanes to pedestrian lanes
- Bugfix: Travel probabilities set in the "Citizen Lifecycle Rebalance v2.1" mod are not obeyed (thanks to @informmanuel, @shaundoddmusic for reporting this issue)
- Bugfix: Number of tourists seems to drop when activating the mod (statistics were not updated, thanks to @hpp7117, @wjrohn for reporting this issue)
- Bugfix: When loading a second savegame a second main menu button is displayed (thanks to @Cpt. Whitepaw for reporting this issue)
- Bugfix: While path-finding is in progress vehicles do "bungee-jumping" on the current segment (thanks to @mxolsenx, @Howzitworld for reporting this issue)
- Bugfix: Cims leaving the city search for parking spaces near the outside connection which is obviously not required

1.8.3, 12/4/2016
- Bugfix: Despite having the Parking AI activated, cims sometimes still spawn pocket cars.
- Bugfix: When the Parking AI is active, bicycle lanes are not used (thanks to @informmanuel for reporting this issue)
10 changes: 10 additions & 0 deletions TLM/GlobalConfigGenerator/GlobalConfigGenerator.csproj
Original file line number Diff line number Diff line change
@@ -32,6 +32,16 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'QueuedStats|AnyCPU'">
<OutputPath>bin\QueuedStats\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
10 changes: 10 additions & 0 deletions TLM/SpiralLoopTest/SpiralLoopTest.csproj
Original file line number Diff line number Diff line change
@@ -32,6 +32,16 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'QueuedStats|AnyCPU'">
<OutputPath>bin\QueuedStats\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
7 changes: 7 additions & 0 deletions TLM/TLM.sln
Original file line number Diff line number Diff line change
@@ -23,19 +23,26 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
QueuedStats|Any CPU = QueuedStats|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7422AE58-8B0A-401C-9404-F4A438EFFE10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7422AE58-8B0A-401C-9404-F4A438EFFE10}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7422AE58-8B0A-401C-9404-F4A438EFFE10}.QueuedStats|Any CPU.ActiveCfg = QueuedStats|Any CPU
{7422AE58-8B0A-401C-9404-F4A438EFFE10}.QueuedStats|Any CPU.Build.0 = QueuedStats|Any CPU
{7422AE58-8B0A-401C-9404-F4A438EFFE10}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7422AE58-8B0A-401C-9404-F4A438EFFE10}.Release|Any CPU.Build.0 = Release|Any CPU
{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.QueuedStats|Any CPU.ActiveCfg = QueuedStats|Any CPU
{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.QueuedStats|Any CPU.Build.0 = QueuedStats|Any CPU
{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{48D1868B-EE81-4339-95C9-4C5EADEB9ECA}.Release|Any CPU.Build.0 = Release|Any CPU
{615EF202-3E13-4A15-A82F-594D90CD0ECD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{615EF202-3E13-4A15-A82F-594D90CD0ECD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{615EF202-3E13-4A15-A82F-594D90CD0ECD}.QueuedStats|Any CPU.ActiveCfg = QueuedStats|Any CPU
{615EF202-3E13-4A15-A82F-594D90CD0ECD}.QueuedStats|Any CPU.Build.0 = QueuedStats|Any CPU
{615EF202-3E13-4A15-A82F-594D90CD0ECD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{615EF202-3E13-4A15-A82F-594D90CD0ECD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
57 changes: 50 additions & 7 deletions TLM/TLM/Custom/AI/CustomCarAI.cs
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
using TrafficManager.State;
using TrafficManager.Manager;
using TrafficManager.Traffic;
using static TrafficManager.Traffic.ExtCitizenInstance;

namespace TrafficManager.Custom.AI {
public class CustomCarAI : CarAI { // TODO inherit from VehicleAI (in order to keep the correct references to `base`)
@@ -40,24 +41,66 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect
}*/
#endif

if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0) {
// NON-STOCK CODE START
VehicleState state = null;
ExtCitizenInstance driverExtInstance = null;
bool prohibitPocketCars = Options.prohibitPocketCars;
if (prohibitPocketCars) {
state = VehicleStateManager.Instance._GetVehicleState(vehicleData.GetFirstVehicle(vehicleId));
if (state.VehicleType == ExtVehicleType.PassengerCar) {
driverExtInstance = state.GetDriverExtInstance();
if (driverExtInstance == null) {
prohibitPocketCars = false;
} else {
driverExtInstance.UpdateReturnPathState();
}
} else {
prohibitPocketCars = false;
}
}
// NON-STOCK CODE END

if ((vehicleData.m_flags & Vehicle.Flags.WaitingPath) != 0 &&
(! prohibitPocketCars || driverExtInstance.ReturnPathState != ExtCitizenInstance.ExtPathState.Calculating)) {
PathManager pathManager = Singleton<PathManager>.instance;
byte pathFindFlags = pathManager.m_pathUnits.m_buffer[vehicleData.m_path].m_pathFindFlags;

bool pathFindFailed = (pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0; // path == 0: non-stock code!
bool pathFindSucceeded = (pathFindFlags & PathUnit.FLAG_READY) != 0;

#if USEPATHWAITCOUNTER
if ((pathFindFlags & (PathUnit.FLAG_READY | PathUnit.FLAG_FAILED)) != 0) {
VehicleState state = VehicleStateManager.Instance._GetVehicleState(vehicleId);
state.PathWaitCounter = 0; // NON-STOCK CODE
}
#endif

if ((pathFindFlags & PathUnit.FLAG_READY) != 0) {
if (prohibitPocketCars) {
if (driverExtInstance.ReturnPathState == ExtPathState.Failed) {
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomCarAI.CustomSimulationStep: Return path {driverExtInstance.ReturnPathId} FAILED. Forcing path-finding to fail.");
#endif
pathFindSucceeded = false;
pathFindFailed = true;
}

driverExtInstance.ReleaseReturnPath();

if (pathFindSucceeded) {
CustomPassengerCarAI.OnPathFindSuccess(vehicleId, ref vehicleData, driverExtInstance);
} else if (pathFindFailed) {
CustomPassengerCarAI.OnPathFindFailure(driverExtInstance, vehicleId);
}
}

if (pathFindSucceeded) {
vehicleData.m_pathPositionIndex = 255;
vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
vehicleData.m_flags &= ~Vehicle.Flags.Arriving;
this.PathfindSuccess(vehicleId, ref vehicleData);
this.TrySpawn(vehicleId, ref vehicleData);
} else if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || vehicleData.m_path == 0) { // path == 0: non-stock code!
} else if (pathFindFailed) {
vehicleData.m_flags &= ~Vehicle.Flags.WaitingPath;
Singleton<PathManager>.instance.ReleasePath(vehicleData.m_path);
vehicleData.m_path = 0u;
@@ -106,13 +149,13 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect
}
this.SimulationStep(vehicleId, ref vehicleData, vehicleId, ref vehicleData, lodPhysics);
if (vehicleData.m_leadingVehicle == 0 && vehicleData.m_trailingVehicle != 0) {
VehicleManager instance2 = Singleton<VehicleManager>.instance;
VehicleManager vehManager = Singleton<VehicleManager>.instance;
ushort num = vehicleData.m_trailingVehicle;
int num2 = 0;
while (num != 0) {
ushort trailingVehicle = instance2.m_vehicles.m_buffer[(int)num].m_trailingVehicle;
VehicleInfo info = instance2.m_vehicles.m_buffer[(int)num].Info;
info.m_vehicleAI.SimulationStep(num, ref instance2.m_vehicles.m_buffer[(int)num], vehicleId, ref vehicleData, lodPhysics);
ushort trailingVehicle = vehManager.m_vehicles.m_buffer[(int)num].m_trailingVehicle;
VehicleInfo info = vehManager.m_vehicles.m_buffer[(int)num].Info;
info.m_vehicleAI.SimulationStep(num, ref vehManager.m_vehicles.m_buffer[(int)num], vehicleId, ref vehicleData, lodPhysics);
num = trailingVehicle;
if (++num2 > 16384) {
CODebugBase<LogChannel>.Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace);
4 changes: 2 additions & 2 deletions TLM/TLM/Custom/AI/CustomCitizenAI.cs
Original file line number Diff line number Diff line change
@@ -224,8 +224,8 @@ public bool CustomStartPathFind(ushort instanceID, ref CitizenInstance citizenDa
ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID);
ushort homeId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenData.m_citizen].m_homeBuilding;

// if the citizen is a resident and is starting its journey: find a suitable parking space near the target
if (extInstance.PathMode == ExtCitizenInstance.ExtPathMode.ParkedCarReached) {
// if the citizen is a resident starting its journey and the target is not an outside connection: find a suitable parking space near the target
if (extInstance.PathMode == ExtCitizenInstance.ExtPathMode.ParkedCarReached && (citizenData.m_targetBuilding == 0 || (Singleton<BuildingManager>.instance.m_buildings.m_buffer[citizenData.m_targetBuilding].m_flags & Building.Flags.IncomingOutgoing) == Building.Flags.None)) {
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomCitizenAI.CustomStartPathFind: Finding parking space at target for citizen instance {instanceID}. CurrentDepartureMode={extInstance.PathMode} parkedVehicleId={parkedVehicleId}");
132 changes: 45 additions & 87 deletions TLM/TLM/Custom/AI/CustomHumanAI.cs
Original file line number Diff line number Diff line change
@@ -25,28 +25,26 @@ public void CustomSimulationStep(ushort instanceID, ref CitizenInstance instance
}

// NON-STOCK CODE START
ExtCitizenInstance extInstance = null;
if (Options.prohibitPocketCars) {
ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID);
// query the state of the return path
extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID);
extInstance.UpdateReturnPathState();
}
// NON-STOCK CODE END

if ((instanceData.m_flags & CitizenInstance.Flags.WaitingPath) != CitizenInstance.Flags.None) {
if ((instanceData.m_flags & CitizenInstance.Flags.WaitingPath) != CitizenInstance.Flags.None
&& (!Options.prohibitPocketCars || extInstance.ReturnPathState != ExtPathState.Calculating)) {
PathManager pathManager = Singleton<PathManager>.instance;
byte pathFindFlags = pathManager.m_pathUnits.m_buffer[instanceData.m_path].m_pathFindFlags;

// NON-STOCK CODE START
bool pathFindFailed = (pathFindFlags & PathUnit.FLAG_FAILED) != 0;
bool pathFindFailed = (pathFindFlags & PathUnit.FLAG_FAILED) != 0 || instanceData.m_path == 0;
bool pathFindSucceeded = (pathFindFlags & PathUnit.FLAG_READY) != 0;
bool handleSuccess = true;

if (Options.prohibitPocketCars) {
ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID);

if (extInstance.ReturnPathState == ExtPathState.Calculating) {
// wait for the return path being calculated
return;
} else if (extInstance.ReturnPathState == ExtPathState.Failed) {
if (extInstance.ReturnPathState == ExtPathState.Failed) {
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomPassengerCarAI.CustomSimulationStep: Return path {extInstance.ReturnPathId} FAILED. Forcing path-finding to fail.");
@@ -55,8 +53,8 @@ public void CustomSimulationStep(ushort instanceID, ref CitizenInstance instance
pathFindFailed = true;
}

if (extInstance.ReturnPathState == ExtPathState.Ready || extInstance.ReturnPathState == ExtPathState.Failed)
extInstance.ReleaseReturnPath();
//if (extInstance.ReturnPathState == ExtPathState.Ready || extInstance.ReturnPathState == ExtPathState.Failed)
extInstance.ReleaseReturnPath();

#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[4] && (pathFindFailed || pathFindSucceeded)) {
@@ -165,7 +163,7 @@ public void CustomSimulationStep(ushort instanceID, ref CitizenInstance instance
}
}

public bool CustomArriveAtTarget(ushort instanceID, ref CitizenInstance citizenData) { // TODO stock code
/*public bool CustomArriveAtTarget(ushort instanceID, ref CitizenInstance citizenData) { // TODO stock code
if ((citizenData.m_flags & CitizenInstance.Flags.HangAround) != CitizenInstance.Flags.None) {
uint citizenId = citizenData.m_citizen;
if (citizenId != 0u) {
@@ -184,7 +182,7 @@ public bool CustomArriveAtTarget(ushort instanceID, ref CitizenInstance citizenD
this.ArriveAtDestination(instanceID, ref citizenData, true);
}
return true;
}
}*/

internal static bool OnPathFindFailure(ushort instanceID, ref CitizenInstance instanceData, ExtCitizenInstance extInstance) {
#if DEBUG
@@ -315,7 +313,6 @@ internal static bool OnPathFindSuccess(ushort instanceID, ref CitizenInstance in
isAtOutsideConnection = (Singleton<BuildingManager>.instance.m_buildings.m_buffer[sourceBuildingId].m_flags & Building.Flags.IncomingOutgoing) != Building.Flags.None;// Info.m_buildingAI is OutsideConnectionAI;
if (isAtOutsideConnection && (instanceData.GetLastFramePosition() - Singleton<BuildingManager>.instance.m_buildings.m_buffer[sourceBuildingId].m_position).magnitude > GlobalConfig.Instance.MaxBuildingToPedestrianLaneDistance)
isAtOutsideConnection = false;
//isAtOutsideConnection = Singleton<BuildingManager>.instance.m_buildings.m_buffer[sourceBuildingId].Info.m_buildingAI is OutsideConnectionAI;
}

#if DEBUG
@@ -335,96 +332,57 @@ internal static bool OnPathFindSuccess(ushort instanceID, ref CitizenInstance in

// try to spawn parked vehicle in the vicinity of the starting point.
VehicleInfo vehicleInfo = null;
Citizen.AgeGroup ageGroup = CustomCitizenAI.GetAgeGroup(instanceData.Info.m_agePhase);
if (ageGroup > Citizen.AgeGroup.Child) {
PrefabAI ai = Singleton<CitizenManager>.instance.m_citizens.m_buffer[instanceData.m_citizen].GetCitizenInfo(instanceData.m_citizen).GetAI();
if (ai is ResidentAI) {
vehicleInfo = CustomResidentAI.GetVehicleInfo(instanceID, ref instanceData, true);
} else if (ai is TouristAI) {
vehicleInfo = CustomTouristAI.GetVehicleInfo(instanceID, ref instanceData, true);
}
if (instanceData.Info.m_agePhase > Citizen.AgePhase.Child) {
// 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)
vehicleInfo = Singleton<VehicleManager>.instance.GetRandomVehicleInfo(ref Singleton<SimulationManager>.instance.m_randomizer, ItemClass.Service.Residential, ItemClass.SubService.ResidentialLow, ItemClass.Level.Level1);
}

if (vehicleInfo != null) {
/*switch (vehicleInfo.GetService()) {
case ItemClass.Service.PublicTransport:
if (usesPublicTransport) {
extInstance.PathMode = ExtCitizenInstance.ExtPathMode.PublicTransportToTarget;
if (instanceData.m_sourceBuilding != 0)
ExtBuildingManager.Instance.GetExtBuilding(instanceData.m_sourceBuilding).RemovePublicTransportDemand((uint)GlobalConfig.Instance.PublicTransportDemandUsageDecrement, true);
if (instanceData.m_targetBuilding != 0)
ExtBuildingManager.Instance.GetExtBuilding(instanceData.m_targetBuilding).RemovePublicTransportDemand((uint)GlobalConfig.Instance.PublicTransportDemandUsageDecrement, false);
} else {
extInstance.PathMode = ExtCitizenInstance.ExtPathMode.CalculatingWalkingPathToTarget;
handleSoftPathFindFailure = true;
return false;
}
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomHumanAI.OnPathFindSuccess: Citizen {instanceData.m_citizen} (citizen instance {instanceID}), source building {sourceBuildingId} is using public transport/is walking. CurrentPathMode={extInstance.PathMode}");
#endif
return true;
case ItemClass.Service.Residential:*/
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomHumanAI.OnPathFindSuccess: Citizen {instanceData.m_citizen} (citizen instance {instanceID}), source building {sourceBuildingId} is using their own passenger car. CurrentPathMode={extInstance.PathMode}");
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomHumanAI.OnPathFindSuccess: Citizen {instanceData.m_citizen} (citizen instance {instanceID}), source building {sourceBuildingId} is using their own passenger car. CurrentPathMode={extInstance.PathMode}");
#endif

Vector3 currentPos;
if (sourceBuildingId != 0) {
currentPos = Singleton<BuildingManager>.instance.m_buildings.m_buffer[sourceBuildingId].m_position;
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomHumanAI.OnPathFindSuccess: Taking current position from source building {sourceBuildingId} for citizen {instanceData.m_citizen} (citizen instance {instanceID}): {currentPos} CurrentPathMode={extInstance.PathMode}");
#endif
} else {
ushort currentBuildingId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[instanceData.m_citizen].GetBuildingByLocation();
if (currentBuildingId != 0) {
currentPos = Singleton<BuildingManager>.instance.m_buildings.m_buffer[currentBuildingId].m_position;
// determine current position vector
Vector3 currentPos;
ushort currentBuildingId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[instanceData.m_citizen].GetBuildingByLocation();
if (currentBuildingId != 0) {
currentPos = Singleton<BuildingManager>.instance.m_buildings.m_buffer[sourceBuildingId].m_position;
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomHumanAI.OnPathFindSuccess: Taking current position from current building {currentBuildingId} for citizen {instanceData.m_citizen} (citizen instance {instanceID}): {currentPos}. CurrentPathMode={extInstance.PathMode}");
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomHumanAI.OnPathFindSuccess: Taking current position from source building {sourceBuildingId} for citizen {instanceData.m_citizen} (citizen instance {instanceID}): {currentPos} CurrentPathMode={extInstance.PathMode}");
#endif
} else {
currentPos = instanceData.GetLastFramePosition();
} else {
currentPos = instanceData.GetLastFramePosition();
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomHumanAI.OnPathFindSuccess: 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}");
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomHumanAI.OnPathFindSuccess: 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}");
#endif
}
}
}

Vector3 parkPos;
if (CustomCitizenAI.TrySpawnParkedPassengerCar(instanceData.m_citizen, homeId, currentPos, vehicleInfo, out parkPos)) {
parkedVehicleId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[instanceData.m_citizen].m_parkedVehicle;
// spawn a passenger car near the current position
Vector3 parkPos;
if (CustomCitizenAI.TrySpawnParkedPassengerCar(instanceData.m_citizen, homeId, currentPos, vehicleInfo, out parkPos)) {
parkedVehicleId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[instanceData.m_citizen].m_parkedVehicle;
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2] && sourceBuildingId != 0)
Log._Debug($"Parked vehicle for citizen {instanceData.m_citizen} (instance {instanceID}) is {parkedVehicleId} now.");
if (GlobalConfig.Instance.DebugSwitches[2] && sourceBuildingId != 0)
Log._Debug($"Parked vehicle for citizen {instanceData.m_citizen} (instance {instanceID}) is {parkedVehicleId} now.");
#endif

if (sourceBuildingId != 0) {
ExtBuildingManager.Instance.GetExtBuilding(sourceBuildingId).ModifyParkingSpaceDemand(parkPos, GlobalConfig.Instance.MinSpawnedCarParkingSpaceDemandDelta, GlobalConfig.Instance.MaxSpawnedCarParkingSpaceDemandDelta);
}
} else {
if (sourceBuildingId != 0) {
ExtBuildingManager.Instance.GetExtBuilding(sourceBuildingId).ModifyParkingSpaceDemand(parkPos, GlobalConfig.Instance.MinSpawnedCarParkingSpaceDemandDelta, GlobalConfig.Instance.MaxSpawnedCarParkingSpaceDemandDelta);
}
} else {
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2]) {
Log._Debug($">> Failed to spawn parked vehicle for citizen {instanceData.m_citizen} (citizen instance {instanceID}). homePos: {Singleton<BuildingManager>.instance.m_buildings.m_buffer[homeId].m_position}");
}
if (GlobalConfig.Instance.DebugSwitches[2]) {
Log._Debug($">> Failed to spawn parked vehicle for citizen {instanceData.m_citizen} (citizen instance {instanceID}). homePos: {Singleton<BuildingManager>.instance.m_buildings.m_buffer[homeId].m_position}");
}
#endif

if (sourceBuildingId != 0) {
ExtBuildingManager.Instance.GetExtBuilding(sourceBuildingId).AddParkingSpaceDemand(GlobalConfig.Instance.FailedSpawnParkingSpaceDemandIncrement);
}
}
/*break;
default:
extInstance.PathMode = ExtCitizenInstance.ExtPathMode.WalkingToTarget;
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomHumanAI.OnPathFindSuccess: Citizen {instanceData.m_citizen} (citizen instance {instanceID}), source building {sourceBuildingId} is using an UNHANDLED {vehicleInfo.GetService()} vehicle. CurrentPathMode={extInstance.PathMode}");
#endif
return true;
}*/
if (sourceBuildingId != 0) {
ExtBuildingManager.Instance.GetExtBuilding(sourceBuildingId).AddParkingSpaceDemand(GlobalConfig.Instance.FailedSpawnParkingSpaceDemandIncrement);
}
}
} else {
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2]) {
210 changes: 82 additions & 128 deletions TLM/TLM/Custom/AI/CustomPassengerCarAI.cs

Large diffs are not rendered by default.

50 changes: 26 additions & 24 deletions TLM/TLM/Custom/AI/CustomResidentAI.cs
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
using static TrafficManager.Traffic.ExtCitizenInstance;

namespace TrafficManager.Custom.AI {
public class CustomResidentAI : HumanAI {
public class CustomResidentAI : ResidentAI {
public string CustomGetLocalizedStatus(ushort instanceID, ref CitizenInstance data, out InstanceID target) {
if ((data.m_flags & (CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating)) != CitizenInstance.Flags.None) {
target = InstanceID.Empty;
@@ -123,10 +123,6 @@ public string CustomGetLocalizedStatus(ushort instanceID, ref CitizenInstance da
}

public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance citizenData, bool forceProbability) {
return CustomResidentAI.GetVehicleInfo(instanceID, ref citizenData, forceProbability);
}

public static new VehicleInfo GetVehicleInfo(ushort instanceID, ref CitizenInstance citizenData, bool forceProbability) {
if (citizenData.m_citizen == 0u) {
return null;
}
@@ -137,15 +133,15 @@ public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance c
ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID);
if (extInstance.PathMode == ExtPathMode.TaxiToTarget) {
forceTaxi = true;
}

ushort parkedVehicleId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenData.m_citizen].m_parkedVehicle;
if (parkedVehicleId != 0) {
} else {
ushort parkedVehicleId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenData.m_citizen].m_parkedVehicle;
if (parkedVehicleId != 0) {
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomResidentAI.GetVehicleInfo: Citizen instance {instanceID} owns a parked vehicle {parkedVehicleId}. Reusing vehicle info.");
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomResidentAI.GetVehicleInfo: Citizen instance {instanceID} owns a parked vehicle {parkedVehicleId}. Reusing vehicle info.");
#endif
return Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info;
return Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info;
}
}
}
// NON-STOCK CODE END
@@ -155,18 +151,21 @@ public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance c
int carProb;
int bikeProb;
int taxiProb;
// NON-STOCK CODE START
if (forceTaxi) {
carProb = 0;
bikeProb = 0;
taxiProb = 100;
} else if (forceProbability || (citizenData.m_flags & CitizenInstance.Flags.BorrowCar) != CitizenInstance.Flags.None) {
} else
// NON-STOCK CODE END
if (forceProbability || (citizenData.m_flags & CitizenInstance.Flags.BorrowCar) != CitizenInstance.Flags.None) {
carProb = 100;
bikeProb = 0;
taxiProb = 0;
} else {
carProb = CustomResidentAI.GetCarProbability(instanceID, ref citizenData, ageGroup);
bikeProb = CustomResidentAI.GetBikeProbability(instanceID, ref citizenData, ageGroup);
taxiProb = CustomResidentAI.GetTaxiProbability(instanceID, ref citizenData, ageGroup);
carProb = GetCarProbability(instanceID, ref citizenData, ageGroup);
bikeProb = GetBikeProbability(instanceID, ref citizenData, ageGroup);
taxiProb = GetTaxiProbability(instanceID, ref citizenData, ageGroup);
}
Randomizer randomizer = new Randomizer(citizenData.m_citizen);
bool useCar = randomizer.Int32(100u) < carProb;
@@ -189,7 +188,8 @@ public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance c
return null;
}

private static int GetTaxiProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) {
private int GetTaxiProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) {
Log.Error("CustomResidentAI.GetTaxiProbability called!");
switch (ageGroup) {
case Citizen.AgeGroup.Child:
return 0;
@@ -206,7 +206,8 @@ private static int GetTaxiProbability(ushort instanceID, ref CitizenInstance cit
}
}

private static int GetBikeProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) {
private int GetBikeProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) {
Log.Error("CustomResidentAI.GetBikeProbability called!");
CitizenManager citizenManager = Singleton<CitizenManager>.instance;
uint citizenId = citizenData.m_citizen;
ushort homeId = citizenManager.m_citizens.m_buffer[(int)((UIntPtr)citizenId)].m_homeBuilding;
@@ -236,18 +237,19 @@ private static int GetBikeProbability(ushort instanceID, ref CitizenInstance cit
}
}

private static int GetCarProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) {
private int GetCarProbability(ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) {
Log.Error("CustomResidentAI.GetCarProbability called!");
switch (ageGroup) {
case Citizen.AgeGroup.Child:
return ResidentAI.CAR_PROBABILITY_CHILD;
return 0;
case Citizen.AgeGroup.Teen:
return ResidentAI.CAR_PROBABILITY_TEEN;
return 5;
case Citizen.AgeGroup.Young:
return ResidentAI.CAR_PROBABILITY_YOUNG;
return 15;
case Citizen.AgeGroup.Adult:
return ResidentAI.CAR_PROBABILITY_ADULT;
return 20;
case Citizen.AgeGroup.Senior:
return ResidentAI.CAR_PROBABILITY_SENIOR;
return 10;
default:
return 0;
}
43 changes: 28 additions & 15 deletions TLM/TLM/Custom/AI/CustomTouristAI.cs
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
using TrafficManager.Manager;
using TrafficManager.State;
using TrafficManager.Traffic;
using static TrafficManager.Traffic.ExtCitizenInstance;

namespace TrafficManager.Custom.AI {
public class CustomTouristAI : TouristAI {
@@ -77,38 +78,47 @@ public string CustomGetLocalizedStatus(ushort instanceID, ref CitizenInstance da
}

public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance citizenData, bool forceProbability) {
return CustomTouristAI.GetVehicleInfo(instanceID, ref citizenData, forceProbability);
}

public static new VehicleInfo GetVehicleInfo(ushort instanceID, ref CitizenInstance citizenData, bool forceProbability) {
if (citizenData.m_citizen == 0u) {
return null;
}

// NON-STOCK CODE START
bool forceTaxi = false;
if (Options.prohibitPocketCars) {
ushort parkedVehicleId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenData.m_citizen].m_parkedVehicle;
if (parkedVehicleId != 0) {
ExtCitizenInstance extInstance = ExtCitizenInstanceManager.Instance.GetExtInstance(instanceID);
if (extInstance.PathMode == ExtPathMode.TaxiToTarget) {
forceTaxi = true;
} else {
ushort parkedVehicleId = Singleton<CitizenManager>.instance.m_citizens.m_buffer[citizenData.m_citizen].m_parkedVehicle;
if (parkedVehicleId != 0) {
#if DEBUG
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomTouristAI.GetVehicleInfo: Citizen instance {instanceID} owns a parked vehicle {parkedVehicleId}. Reusing vehicle info.");
if (GlobalConfig.Instance.DebugSwitches[2])
Log._Debug($"CustomTouristAI.GetVehicleInfo: Citizen instance {instanceID} owns a parked vehicle {parkedVehicleId}. Reusing vehicle info.");
#endif
return Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info;
return Singleton<VehicleManager>.instance.m_parkedVehicles.m_buffer[parkedVehicleId].Info;
}
}
}
// NON-STOCK CODE END

int carProb;
int bikeProb;
int taxiProb;
// NON-STOCK CODE START
if (forceTaxi) {
carProb = 0;
bikeProb = 0;
taxiProb = 100;
} else
// NON-STOCK CODE END
if (forceProbability || (citizenData.m_flags & CitizenInstance.Flags.BorrowCar) != CitizenInstance.Flags.None) {
carProb = 100;
bikeProb = 0;
taxiProb = 0;
} else {
carProb = CustomTouristAI.GetCarProbability();
bikeProb = CustomTouristAI.GetBikeProbability();
taxiProb = CustomTouristAI.GetTaxiProbability();
carProb = GetCarProbability();
bikeProb = GetBikeProbability();
taxiProb = GetTaxiProbability();
}
Randomizer randomizer = new Randomizer(citizenData.m_citizen);
bool useCar = randomizer.Int32(100u) < carProb;
@@ -131,15 +141,18 @@ public VehicleInfo CustomGetVehicleInfo(ushort instanceID, ref CitizenInstance c
return null;
}

private static int GetTaxiProbability() {
private int GetTaxiProbability() {
Log.Error("CustomTouristAI.GetTaxiProbability called!");
return 20;
}

private static int GetBikeProbability() {
private int GetBikeProbability() {
Log.Error("CustomTouristAI.GetBikeProbability called!");
return 20;
}

private static int GetCarProbability() {
private int GetCarProbability() {
Log.Error("CustomTouristAI.GetCarProbability called!");
return 20;
}
}
11 changes: 11 additions & 0 deletions TLM/TLM/Custom/Manager/CustomVehicleManager.cs
Original file line number Diff line number Diff line change
@@ -21,6 +21,17 @@ private void ReleaseVehicleImplementation(ushort vehicleId, ref Vehicle vehicleD
}

public bool CustomCreateVehicle(out ushort vehicleId, ref Randomizer r, VehicleInfo info, Vector3 position, TransferManager.TransferReason type, bool transferToSource, bool transferToTarget) {
// NON-STOCK CODE START
if (this.m_vehicleCount > VehicleManager.MAX_VEHICLE_COUNT - 5) {
// prioritize service vehicles and public transport when hitting the vehicle limit
ItemClass.Service service = info.GetService();
if (service == ItemClass.Service.Residential || service == ItemClass.Service.Industrial || service == ItemClass.Service.Commercial || service == ItemClass.Service.Office) {
vehicleId = 0;
return false;
}
}
// NON-STOCK CODE END

ushort vehId;
if (this.m_vehicles.CreateItem(out vehId, ref r)) {
vehicleId = vehId;
20 changes: 14 additions & 6 deletions TLM/TLM/Custom/PathFinding/CustomPathFind.cs
Original file line number Diff line number Diff line change
@@ -906,6 +906,7 @@ private void ProcessItemMain(uint unitId, BufferItem item, ref NetSegment prevSe
} else {
// pocket car spawning
if (Options.prohibitPocketCars &&
_extVehicleType == ExtVehicleType.PassengerCar &&
(_extPathType == ExtCitizenInstance.ExtPathType.WalkingOnly || (_extPathType == ExtCitizenInstance.ExtPathType.DrivingOnly && item.m_position.m_segment != _startSegmentA && item.m_position.m_segment != _startSegmentB))) {
allowPedSwitch = false;
} else {
@@ -1453,7 +1454,10 @@ private void ProcessItemMain(uint unitId, BufferItem item, ref NetSegment prevSe
// min/max compatible outer similar lane indices
short minNextOuterSimilarIndex = -1;
short maxNextOuterSimilarIndex = -1;
if (nextIsRealJunction) {
if (uturn) {
// force u-turns to happen on the most inner lane
minNextOuterSimilarIndex = maxNextOuterSimilarIndex = (short)((short)nextCompatibleLaneCount - 1);
} else if (nextIsRealJunction) {
// at junctions: try to match distinct lanes

/*if (prevOuterSimilarLaneIndex >= nextCompatibleLaneCount-1) {
@@ -1463,7 +1467,10 @@ private void ProcessItemMain(uint unitId, BufferItem item, ref NetSegment prevSe
if (debug)
logBuf.Add($"City rules: Splitting inner lane. minNextOuterSimilarLaneIndex={minNextOuterSimilarIndex} maxNextOuterSimilarLaneIndex={maxNextOuterSimilarIndex}");
#endif
} else */if (nextCompatibleLaneCount > prevSimilarLaneCount && prevOuterSimilarLaneIndex == prevSimilarLaneCount-1) {
} else */


if (nextCompatibleLaneCount > prevSimilarLaneCount && prevOuterSimilarLaneIndex == prevSimilarLaneCount-1) {
// merge inner lanes
minNextOuterSimilarIndex = prevOuterSimilarLaneIndex;
maxNextOuterSimilarIndex = (short)((short)nextCompatibleLaneCount - 1);
@@ -1793,11 +1800,12 @@ private static short FindValue(ref byte[] values, int value, short length) {
}

/// <summary>
/// xxx Finds the value in `values` having the (n+1)th lowest index, or, if (n+1) > number of valid elements in `values` finds the value in `values` with the highest index.
/// Finds the element in <paramref name="values"/> with the highest associated index <code>i</code> satisfying <code>i</code> &lt;= <paramref name="n" />.
/// If no such element is found, the element with the highest index is returned.
/// </summary>
/// <param name="values">array to be queried</param>
/// <param name="validMask">a bitmask holding all valid indices of `values`</param>
/// <param name="n">query</param>
/// <param name="validMask">a bitmask holding all valid indices of <paramref name="values" /></param>
/// <param name="n">query index</param>
/// <returns></returns>
private static short FindCompatibleLane(ref byte[] values, ushort validMask, short n) {
short nextLaneI = -1;
@@ -2335,7 +2343,7 @@ private bool ProcessItemCosts(bool allowAdvancedAI, bool ignoreLaneArrows, bool
if (debug)
logBuf.Add($"ProcessItemCosts: applying u-turn cost factor on deactivated advaned AI");
#endif
prevCost *= _conf.UturnLaneDistance;
prevCost *= (float)_conf.UturnLaneDistance;
}
}

254 changes: 183 additions & 71 deletions TLM/TLM/LoadingExtension.cs

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions TLM/TLM/State/GlobalConfig.cs
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ namespace TrafficManager.State {
public class GlobalConfig {
public const string FILENAME = "TMPE_GlobalConfig.xml";
public const string BACKUP_FILENAME = FILENAME + ".bak";
private static int LATEST_VERSION = 2;
private static int LATEST_VERSION = 3;
#if DEBUG
private static uint lastModificationCheckFrame = 0;
#endif
@@ -42,16 +42,16 @@ internal static void OnLevelUnloading() {
}

//public static GlobalConfig Instance() {
//#if DEBUG
// uint curDebugFrame = Singleton<SimulationManager>.instance.m_currentFrameIndex >> 10;
// if (lastModificationCheckFrame == 0) {
// lastModificationCheckFrame = curDebugFrame;
// } else if (lastModificationCheckFrame < curDebugFrame) {
// lastModificationCheckFrame = curDebugFrame;
// ReloadIfNewer();
// }
//#endif
//return Instance;
//#if DEBUG
// uint curDebugFrame = Singleton<SimulationManager>.instance.m_currentFrameIndex >> 10;
// if (lastModificationCheckFrame == 0) {
// lastModificationCheckFrame = curDebugFrame;
// } else if (lastModificationCheckFrame < curDebugFrame) {
// lastModificationCheckFrame = curDebugFrame;
// ReloadIfNewer();
// }
//#endif
//return Instance;
//}

private static DateTime ModifiedTime = DateTime.MinValue;
@@ -82,7 +82,7 @@ internal static void OnLevelUnloading() {
/// <summary>
/// base lane changing cost factor on city streets
/// </summary>
public float CityRoadLaneChangingBaseCost = 0.1f;
public float CityRoadLaneChangingBaseCost = 0.15f;

/// <summary>
/// lane changing cost base before junctions
4 changes: 2 additions & 2 deletions TLM/TLM/State/Options.cs
Original file line number Diff line number Diff line change
@@ -122,8 +122,8 @@ public static bool MenuRebuildRequired {
get { return menuRebuildRequired; }
private set {
menuRebuildRequired = value;
if (LoadingExtension.Instance != null && LoadingExtension.Instance.BaseUI != null)
LoadingExtension.Instance.BaseUI.Close();
if (LoadingExtension.BaseUI != null)
LoadingExtension.BaseUI.Close();
}
}

12 changes: 11 additions & 1 deletion TLM/TLM/TLM.csproj
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<DefineConstants>DEBUG;QUEUEDSTATS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>0</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -33,6 +33,16 @@
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'QueuedStats|AnyCPU'">
<OutputPath>bin\QueuedStats\</OutputPath>
<DefineConstants>QUEUEDSTATS</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Assembly-CSharp">
<HintPath>D:\Games\Steam\steamapps\common\Cities_Skylines\Cities_Data\Managed\Assembly-CSharp.dll</HintPath>
8 changes: 4 additions & 4 deletions TLM/TLM/ThreadingExtension.cs
Original file line number Diff line number Diff line change
@@ -42,16 +42,16 @@ public override void OnAfterSimulationFrame() {
public override void OnUpdate(float realTimeDelta, float simulationTimeDelta) {
base.OnUpdate(realTimeDelta, simulationTimeDelta);
#if !TAM
if (LoadingExtension.Instance == null || ToolsModifierControl.toolController == null || ToolsModifierControl.toolController == null || LoadingExtension.Instance.BaseUI == null) {
if (ToolsModifierControl.toolController == null || ToolsModifierControl.toolController == null || LoadingExtension.BaseUI == null) {
return;
}

if (ToolsModifierControl.toolController.CurrentTool != LoadingExtension.Instance.TrafficManagerTool && LoadingExtension.Instance.BaseUI.IsVisible()) {
LoadingExtension.Instance.BaseUI.Close();
if (ToolsModifierControl.toolController.CurrentTool != LoadingExtension.TrafficManagerTool && LoadingExtension.BaseUI.IsVisible()) {
LoadingExtension.BaseUI.Close();
}

if (Input.GetKeyDown(KeyCode.Escape)) {
LoadingExtension.Instance.BaseUI.Close();
LoadingExtension.BaseUI.Close();
}
#endif
}
2 changes: 1 addition & 1 deletion TLM/TLM/TrafficManagerMod.cs
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
namespace TrafficManager {
public class TrafficManagerMod : IUserMod {

public static readonly string Version = "1.8.3";
public static readonly string Version = "1.8.4";

public static readonly uint GameVersion = 159507472u;
public static readonly uint GameVersionA = 1u;
85 changes: 78 additions & 7 deletions TLM/TLM/UI/SubTools/LaneConnectorTool.cs
Original file line number Diff line number Diff line change
@@ -22,10 +22,18 @@ enum MarkerSelectionMode {
SelectTarget
}

enum StayInLaneMode {
None,
Both,
Forward,
Backward
}

private Dictionary<ushort, IDisposable> nodeGeometryUnsubscribers;
private NodeLaneMarker selectedMarker = null;
private NodeLaneMarker hoveredMarker = null;
private Dictionary<ushort, List<NodeLaneMarker>> currentNodeMarkers;
private StayInLaneMode stayInLaneMode = StayInLaneMode.None;
//private bool initDone = false;

class NodeLaneMarker {
@@ -35,6 +43,8 @@ class NodeLaneMarker {
internal Vector3 position;
internal bool isSource;
internal uint laneId;
internal int innerSimilarLaneIndex;
internal NetInfo.Direction finalDirection;
internal float radius = 1f;
internal Color color;
internal List<NodeLaneMarker> connectedMarkers = new List<NodeLaneMarker>();
@@ -138,7 +148,12 @@ public override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {
}
}

if (Input.GetKey(KeyCode.Delete)) {
bool deleteAll = Input.GetKeyDown(KeyCode.Delete);
bool stayInLane = Input.GetKeyDown(KeyCode.S) && (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) && Singleton<NetManager>.instance.m_nodes.m_buffer[SelectedNodeId].CountSegments() == 2;
if (stayInLane)
deleteAll = true;

if (deleteAll) {
// remove all connections at selected node

List<NodeLaneMarker> nodeMarkers = GetNodeMarkers(SelectedNodeId);
@@ -152,6 +167,52 @@ public override void RenderOverlay(RenderManager.CameraInfo cameraInfo) {
}
RefreshCurrentNodeMarkers();
}

if (stayInLane) {
// "stay in lane"
switch (stayInLaneMode) {
case StayInLaneMode.None:
stayInLaneMode = StayInLaneMode.Both;
break;
case StayInLaneMode.Both:
stayInLaneMode = StayInLaneMode.Forward;
break;
case StayInLaneMode.Forward:
stayInLaneMode = StayInLaneMode.Backward;
break;
case StayInLaneMode.Backward:
stayInLaneMode = StayInLaneMode.None;
break;
}

if (stayInLaneMode != StayInLaneMode.None) {
List<NodeLaneMarker> nodeMarkers = GetNodeMarkers(SelectedNodeId);
if (nodeMarkers != null) {
selectedMarker = null;
foreach (NodeLaneMarker sourceLaneMarker in nodeMarkers) {
if (!sourceLaneMarker.isSource)
continue;

if (stayInLaneMode == StayInLaneMode.Forward || stayInLaneMode == StayInLaneMode.Backward) {
if (sourceLaneMarker.finalDirection == NetInfo.Direction.Backward ^ stayInLaneMode == StayInLaneMode.Backward) {
continue;
}
}

foreach (NodeLaneMarker targetLaneMarker in nodeMarkers) {
if (targetLaneMarker.isSource || targetLaneMarker.segmentId == sourceLaneMarker.segmentId)
continue;

if (targetLaneMarker.innerSimilarLaneIndex == sourceLaneMarker.innerSimilarLaneIndex) {
Log._Debug($"Adding lane connection {sourceLaneMarker.laneId} -> {targetLaneMarker.laneId}");
LaneConnectionManager.Instance.AddLaneConnection(sourceLaneMarker.laneId, targetLaneMarker.laneId, sourceLaneMarker.startNode);
}
}
}
}
RefreshCurrentNodeMarkers();
}
}
}

if (GetMarkerSelectionMode() == MarkerSelectionMode.None && HoveredNodeId != 0) {
@@ -178,6 +239,7 @@ public override void OnPrimaryClickOverlay() {
#endif
SelectedNodeId = 0;
selectedMarker = null;
stayInLaneMode = StayInLaneMode.None;
return;
}

@@ -191,6 +253,7 @@ public override void OnPrimaryClickOverlay() {
if (markers != null) {
SelectedNodeId = HoveredNodeId;
selectedMarker = null;
stayInLaneMode = StayInLaneMode.None;

currentNodeMarkers[SelectedNodeId] = markers;
}
@@ -204,11 +267,14 @@ public override void OnPrimaryClickOverlay() {
// click on free spot. deselect node
SelectedNodeId = 0;
selectedMarker = null;
stayInLaneMode = StayInLaneMode.None;
return;
}
}

if (hoveredMarker != null) {
stayInLaneMode = StayInLaneMode.None;

#if DEBUGCONN
Log._Debug($"TppLaneConnectorTool: hoveredMarker != null. selMode={GetMarkerSelectionMode()}");
#endif
@@ -254,6 +320,7 @@ public override void OnSecondaryClickOverlay() {
#if DEBUGCONN
Log._Debug($"TppLaneConnectorTool: OnSecondaryClickOverlay: nothing to do");
#endif
stayInLaneMode = StayInLaneMode.None;
break;
case MarkerSelectionMode.SelectSource:
// deselect node
@@ -279,6 +346,7 @@ public override void OnActivate() {
SelectedNodeId = 0;
selectedMarker = null;
hoveredMarker = null;
stayInLaneMode = StayInLaneMode.None;
RefreshCurrentNodeMarkers();
}

@@ -338,12 +406,13 @@ private List<NodeLaneMarker> GetNodeMarkers(ushort nodeId) {
NetInfo.Lane[] lanes = NetManager.instance.m_segments.m_buffer[segmentId].Info.m_lanes;
uint laneId = NetManager.instance.m_segments.m_buffer[segmentId].m_lanes;
for (byte laneIndex = 0; laneIndex < lanes.Length && laneId != 0; laneIndex++) {
if ((lanes[laneIndex].m_laneType & (NetInfo.LaneType.TransportVehicle | NetInfo.LaneType.Vehicle)) != NetInfo.LaneType.None &&
(lanes[laneIndex].m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) != VehicleInfo.VehicleType.None) {
NetInfo.Lane laneInfo = lanes[laneIndex];
if ((laneInfo.m_laneType & (NetInfo.LaneType.TransportVehicle | NetInfo.LaneType.Vehicle)) != NetInfo.LaneType.None &&
(laneInfo.m_vehicleType & (VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) != VehicleInfo.VehicleType.None) {

Vector3? pos = null;
bool isSource = false;
if (connManager.GetLaneEndPoint(segmentId, !isEndNode, laneIndex, laneId, lanes[laneIndex], out isSource, out pos)) {
if (connManager.GetLaneEndPoint(segmentId, !isEndNode, laneIndex, laneId, laneInfo, out isSource, out pos)) {
nodeMarkers.Add(new NodeLaneMarker() {
segmentId = segmentId,
laneId = laneId,
@@ -352,8 +421,10 @@ private List<NodeLaneMarker> GetNodeMarkers(ushort nodeId) {
position = (Vector3)pos + offset,
color = colors[nodeMarkers.Count],
isSource = isSource,
laneType = lanes[laneIndex].m_laneType,
vehicleType = lanes[laneIndex].m_vehicleType
laneType = laneInfo.m_laneType,
vehicleType = laneInfo.m_vehicleType,
innerSimilarLaneIndex = ((byte)(laneInfo.m_finalDirection & NetInfo.Direction.Forward) != 0) ? laneInfo.m_similarLaneIndex : laneInfo.m_similarLaneCount - laneInfo.m_similarLaneIndex - 1,
finalDirection = laneInfo.m_finalDirection
});
}
}
@@ -400,7 +471,7 @@ private bool CheckSegmentsTurningAngle(ushort sourceSegmentId, ref NetSegment so

NetInfo sourceSegmentInfo = netManager.m_segments.m_buffer[sourceSegmentId].Info;
NetInfo targetSegmentInfo = netManager.m_segments.m_buffer[targetSegmentId].Info;

float turningAngle = 0.01f - Mathf.Min(sourceSegmentInfo.m_maxTurnAngleCos, targetSegmentInfo.m_maxTurnAngleCos);
if (turningAngle < 1f) {
Vector3 sourceDirection;
56 changes: 26 additions & 30 deletions TLM/TLM/UI/UIBase.cs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
namespace TrafficManager.UI {
#if !TAM
public class UIBase : UICustomControl {

private UIMainMenuButton button;
private bool _uiShown = false;

@@ -23,6 +23,10 @@ public UIBase() {
button = (UIMainMenuButton)uiView.AddUIComponent(typeof(UIMainMenuButton));
}

~UIBase() {
UnityEngine.Object.Destroy(button);
}

public bool IsVisible() {
return _uiShown;
}
@@ -35,41 +39,33 @@ public void ToggleMainMenu() {
}

public void Show() {
if (LoadingExtension.Instance != null) {
try {
ToolsModifierControl.mainToolbar.CloseEverything();
} catch (Exception e) {
Log.Error("Error on Show(): " + e.ToString());
}
GetMenu().Show();
LoadingExtension.Instance.SetToolMode(TrafficManagerMode.Activated);
_uiShown = true;
button.UpdateSprites();
} else {
Log._Debug("TM UI Show: LoadingExtension.Instance is null!");
try {
ToolsModifierControl.mainToolbar.CloseEverything();
} catch (Exception e) {
Log.Error("Error on Show(): " + e.ToString());
}
GetMenu().Show();
LoadingExtension.SetToolMode(TrafficManagerMode.Activated);
_uiShown = true;
button.UpdateSprites();
}

public void Close() {
if (LoadingExtension.Instance != null) {
var uiView = UIView.GetAView();
var trafficManager = uiView.FindUIComponent("UITrafficManager");
if (trafficManager != null) {
Log._Debug("Hiding TM UI");
Destroy(trafficManager);
//trafficManager.Hide();
} else {
Log._Debug("Hiding TM UI: null!");
}

UITrafficManager.deactivateButtons();
TrafficManagerTool.SetToolMode(ToolMode.None);
LoadingExtension.Instance.SetToolMode(TrafficManagerMode.None);
_uiShown = false;
button.UpdateSprites();
var uiView = UIView.GetAView();
var trafficManager = uiView.FindUIComponent("UITrafficManager");
if (trafficManager != null) {
Log._Debug("Hiding TM UI");
Destroy(trafficManager);
//trafficManager.Hide();
} else {
Log._Debug("TM UI Close: LoadingExtension.Instance is null!");
Log._Debug("Hiding TM UI: null!");
}

UITrafficManager.deactivateButtons();
TrafficManagerTool.SetToolMode(ToolMode.None);
LoadingExtension.SetToolMode(TrafficManagerMode.None);
_uiShown = false;
button.UpdateSprites();
}

internal static UITrafficManager GetMenu() {
4 changes: 2 additions & 2 deletions TLM/TLM/UI/UIMainMenuButton.cs
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ public override void Start() {
}

protected override void OnClick(UIMouseEventParameter p) {
LoadingExtension.Instance.BaseUI.ToggleMainMenu();
LoadingExtension.BaseUI.ToggleMainMenu();
UpdateSprites();
}

@@ -67,7 +67,7 @@ protected override void OnPositionChanged() {
}

internal void UpdateSprites() {
if (! LoadingExtension.Instance.BaseUI.IsVisible()) {
if (! LoadingExtension.BaseUI.IsVisible()) {
normalBgSprite = disabledBgSprite = focusedBgSprite = MAIN_MENU_BUTTON_BG_BASE;
hoveredBgSprite = MAIN_MENU_BUTTON_BG_HOVERED;
pressedBgSprite = MAIN_MENU_BUTTON_BG_ACTIVE;
24 changes: 16 additions & 8 deletions TLM/TLM/UI/UITrafficManager.cs
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ namespace TrafficManager.UI {
public class UITrafficManager : UIPanel {
//private static UIState _uiState = UIState.None;

#if DEBUG
#if QUEUEDSTATS
private static bool showPathFindStats = false;
#endif

@@ -43,19 +43,18 @@ public class UITrafficManager : UIPanel {
private static UIButton _printDebugInfoButton = null;
private static UIButton _noneToVehicleButton = null;
private static UIButton _vehicleToNoneButton = null;
private static UIButton _togglePathFindStatsButton = null;
private static UIButton _removeStuckEntitiesButton = null;
#endif

#if QUEUEDSTATS
private static UIButton _togglePathFindStatsButton = null;
#endif

public static TrafficManagerTool TrafficLightTool;
public static UILabel title;

public override void Start() {
if (LoadingExtension.Instance == null) {
Log.Error("UITrafficManager.Start(): LoadingExtension is null.");
return;
}
TrafficLightTool = LoadingExtension.Instance.TrafficManagerTool;
TrafficLightTool = LoadingExtension.TrafficManagerTool;

backgroundSprite = "GenericPanel";
color = new Color32(75, 75, 135, 255);
@@ -157,9 +156,13 @@ public override void Start() {
_vehicleToNoneButton = _createButton("Vehicle -> None", y, clickVehicleToNone);
y += 40;
height += 40;
#endif
#if QUEUEDSTATS
_togglePathFindStatsButton = _createButton("Toggle PathFind stats", y, clickTogglePathFindStats);
y += 40;
height += 40;
#endif
#if DEBUG
_removeStuckEntitiesButton = _createButton("Remove stuck entities", y, clickRemoveStuckEntities);
y += 40;
height += 40;
@@ -263,10 +266,15 @@ private void clickNoneToVehicle(UIComponent component, UIMouseEventParameter eve
}
}
}
#endif

#if QUEUEDSTATS
private void clickTogglePathFindStats(UIComponent component, UIMouseEventParameter eventParam) {
showPathFindStats = !showPathFindStats;
}
#endif

#if DEBUG

private void clickRemoveStuckEntities(UIComponent component, UIMouseEventParameter eventParam) {
TrafficManagerTool.SetToolMode(ToolMode.None);
@@ -471,7 +479,7 @@ private void clickLaneConnector(UIComponent component, UIMouseEventParameter eve
}

public override void Update() {
#if DEBUG && QUEUEDSTATS
#if QUEUEDSTATS
if (showPathFindStats && title != null) {
title.text = CustomPathManager.TotalQueuedPathFinds.ToString();
#if EXTRAPF
6 changes: 0 additions & 6 deletions TLM/TLM/UI/UITransportDemand.cs
Original file line number Diff line number Diff line change
@@ -18,11 +18,6 @@ public class UITransportDemand : UIPanel {
private static UILabel viewModeLabel;

public override void Start() {
if (LoadingExtension.Instance == null) {
Log.Error("UITrafficManager.Start(): LoadingExtension is null.");
return;
}

var transportInfoViewPanel = GameObject.Find("(Library) PublicTransportInfoViewPanel").GetComponent<PublicTransportInfoViewPanel>();
if (transportInfoViewPanel != null) {
Log._Debug($"Public transport info view panel found.");
@@ -49,7 +44,6 @@ public override void Start() {
switchViewModeButton = _createButton(Translation.GetString("Switch_view"), 3, 3, clickSwitchViewMode);
}


private UIButton _createButton(string text, int x, int y, MouseEventHandler eventClick) {
var button = AddUIComponent<UIButton>();
button.textScale = 0.8f;

0 comments on commit 99f75e2

Please sign in to comment.