Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic support for Airports DLC #1458

Draft
wants to merge 18 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e37bf91
Basic support for Airports DLC
krzychu124 Mar 5, 2022
e8e8bea
Speed limit calculations,
krzychu124 Mar 5, 2022
a602834
Merge branch 'master' of https://github.com/CitiesSkylinesMods/TMPE i…
krzychu124 Mar 6, 2022
06d0801
Merge branch 'master' into feature/airports-DLC-support
krzychu124 Mar 13, 2022
fcd2a73
Merge branch 'master' into feature/airports-DLC-support
krzychu124 Mar 18, 2022
5d97b2e
merge conflict fix
krzychu124 Mar 18, 2022
2ff3032
Slightly improved lane reservation, increased wait time for planes (t…
krzychu124 Mar 18, 2022
1281bd7
Cargo truck paths recalculation if cargo plane changed target aircraf…
krzychu124 Mar 18, 2022
db3035b
Merge branch 'master' of https://github.com/CitiesSkylinesMods/TMPE i…
krzychu124 Mar 19, 2022
3172f76
ExtVehicle extensions
krzychu124 Mar 19, 2022
89754af
Updated game connection delegates
krzychu124 Mar 19, 2022
005fae8
Airplane Stand Search feature
krzychu124 Mar 19, 2022
4920792
Aircraft Simulation Step patch, improve landing angle(w-i-p), trigger…
krzychu124 Mar 19, 2022
21a80cb
Merge branch 'master' into feature/airports-DLC-support
krzychu124 Apr 8, 2022
eaffebf
Merge branch 'master' of https://github.com/CitiesSkylinesMods/TMPE i…
krzychu124 Apr 28, 2022
0830c69
temporary plane restriction textures, fixed plane lane routing
krzychu124 Apr 28, 2022
0913aaa
Merge branch 'master' into feature/airports-DLC-support
krzychu124 Jun 5, 2022
91987f5
[WiP] Aircraft size restrictions, not saveable yet
krzychu124 Jun 6, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
143 changes: 143 additions & 0 deletions TLM/TLM/Custom/BuildingSearch/AirplaneStandSearch.cs
@@ -0,0 +1,143 @@
namespace TrafficManager.Custom.BuildingSearch {
using System.Collections.Generic;
using CSUtil.Commons;
using TrafficManager.API.Traffic.Data;
using TrafficManager.Manager.Impl;
using TrafficManager.Util.Extensions;

internal class AirplaneStandSearch {
private static Queue<ushort> _queue = new Queue<ushort>();

private static ThreadProfiler profiler = new ThreadProfiler();

/// <summary>
/// Tries to find better Airplane Stand for Cargo plane traversing segments forward
/// starting from vehicle current position
/// </summary>
/// <param name="extVehicle">ExtVehicle value</param>
/// <param name="vehicle">Vanilla vehicle value</param>
/// <returns>Building ID of better airplane stand otherwise 0(zero)</returns>
internal static ushort FindBetterCargoPlaneStand(ref ExtVehicle extVehicle, ref Vehicle vehicle) {
if (vehicle.Info.m_vehicleAI is not CargoPlaneAI) {
//passenger planes supported yet
return 0;
}
#if DEBUG
profiler.BeginStep();
#endif
PathManager.instance.m_pathUnits.m_buffer[vehicle.m_path].GetPosition(
vehicle.m_pathPositionIndex >> 1,
out PathUnit.Position currentPosition);
PathManager.instance.m_pathUnits.m_buffer[vehicle.m_path].GetNextPosition(
vehicle.m_pathPositionIndex >> 1,
out PathUnit.Position nextPosition);

ushort nextNodeId = currentPosition.m_segment.ToSegment()
.GetSharedNode(nextPosition.m_segment);
_queue.Clear();
_queue.Enqueue(nextNodeId);
ushort buildingID = 0;
int counter = 0;
while (_queue.Count > 0 && buildingID == 0) {
ushort currentNodeId = _queue.Dequeue();

ref NetNode node = ref currentNodeId.ToNode();
for (int i = 0; i < 8; i++) {
ushort segmentId = node.GetSegment(i);
if (segmentId == 0 || segmentId == currentPosition.m_segment) {
continue;
}

ref NetSegment nextSegment = ref segmentId.ToSegment();

if (VehicleRestrictionsManager.Instance.IsRunwayNetInfo(nextSegment.Info)) {
continue;
}

bool isNextStartNodeOfNextSegment = nextSegment.m_startNode == currentNodeId;
bool nextSegIsInverted = (nextSegment.m_flags & NetSegment.Flags.Invert) !=
NetSegment.Flags.None;
NetInfo.Direction nextDir = isNextStartNodeOfNextSegment
? NetInfo.Direction.Forward
: NetInfo.Direction.Backward;
NetInfo.Direction forwardDirection = !nextSegIsInverted
? nextDir
: NetInfo.InvertDirection(nextDir);

// check direction (assuming we work with one-way segments for now)
if ((forwardDirection & NetInfo.Direction.Forward) == 0) {
continue;
}

// check segment traffic data to skip congested segments,
// increases threshold when checking further segments - mean speed may increase over time, if not..vehicle will at least move closer :)
if (segmentId != currentPosition.m_segment) {
int dirIndex = TrafficMeasurementManager.Instance.GetDirIndex(
segmentId: segmentId,
dir: NetInfo.Direction.Forward);
SegmentDirTrafficData segmentDirTrafficData =
TrafficMeasurementManager.Instance.SegmentDirTrafficData[dirIndex];
int min = 25 - (counter / 4 * 2);
if (segmentDirTrafficData.meanSpeed / 100 < min) {
continue;
}
}

// check if current segment is "End" - airplane stand stop segment contains flag End and one of its nodes also
if ((nextSegment.m_flags & NetSegment.Flags.End) != 0) {
ushort ownerBuildingId = NetSegment.FindOwnerBuilding(segmentId, 32f);
if (ownerBuildingId != 0) {
ref Building building = ref ownerBuildingId.ToBuilding();
// is active and correct vehicle level
if ((building.m_flags & Building.Flags.Active) != 0 &&
building.Info.m_class.m_level == vehicle.Info.m_class.m_level) {

if (vehicle.Info.m_vehicleAI.CanSpawnAt(building.m_position)) {
buildingID = ownerBuildingId;
}
}
}
}

// check lane vehicle restrictions
bool allowedDirectionAndVehicleType = false;
uint nextLaneIndex = 0;
NetInfo nextSegmentInfo = nextSegment.Info;
while (nextLaneIndex < nextSegmentInfo.m_lanes.Length) {
NetInfo.Lane nextLaneInfo = nextSegmentInfo.m_lanes[nextLaneIndex];

nextLaneIndex++;
if ((nextLaneInfo.m_vehicleType & VehicleInfo.VehicleType.Plane) == 0) {
continue;
}

if ((nextLaneInfo.m_finalDirection & forwardDirection) != NetInfo.Direction.None &&
VehicleRestrictionsManager.Instance.MayUseLane(
extVehicle.vehicleType,
segmentId,
(byte)nextLaneIndex,
nextSegmentInfo)) {
allowedDirectionAndVehicleType = true;
break;
}
}

if (!allowedDirectionAndVehicleType) {
continue;
}

_queue.Enqueue(nextSegment.GetOtherNode(currentNodeId));
}

if (counter++ == 40 /* TODO Configurable node count? */) {
break;
}
}
#if DEBUG
profiler.EndStep();
Log.Info($"[Benchmark(FindBetterCargoPlaneStand)] Time: {(profiler.m_lastStepDuration / 1000f):F2}ms, counter: {counter} b: {buildingID}");
#endif
return buildingID;
}
}
}
2 changes: 1 addition & 1 deletion TLM/TLM/Custom/PathFinding/CustomPathFind.cs
Expand Up @@ -3997,7 +3997,7 @@ private struct BufferItem {
queueItem_.vehicleType == ExtVehicleType.Tram ||
queueItem_.vehicleType == ExtVehicleType.Trolleybus ||
(laneInfo.m_vehicleType &
(VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train)) ==
(VehicleInfo.VehicleType.Car | VehicleInfo.VehicleType.Train | VehicleInfo.VehicleType.Plane)) ==
VehicleInfo.VehicleType.None) {
return true;
}
Expand Down
157 changes: 153 additions & 4 deletions TLM/TLM/Manager/Impl/ExtPathManager.cs
@@ -1,7 +1,8 @@
namespace TrafficManager.Manager.Impl {
using ColossalFramework;
using System;
using System.Linq;
using API.Traffic.Data;
using ColossalFramework.Math;
using State;
using TrafficManager.API.Manager;
using TrafficManager.Util;
Expand Down Expand Up @@ -448,8 +449,156 @@ public class ExtPathManager
return pathPosA.m_segment != 0;
}

// protected override void InternalPrintDebugInfo() {
// base.InternalPrintDebugInfo();
// }
/// <summary>
/// Try recalculating paths of transported cargo trucks
/// </summary>
/// <param name="vehicleId">Cargo plane id</param>
/// <param name="vehicle">Cargo plane vehicle data</param>
/// <param name="extVehicle">Cargo plane ExtVehicle data</param>
internal static void RecalculateCargoPlaneCargoTruckPaths(ushort vehicleId, ref Vehicle vehicle, ref ExtVehicle extVehicle) {

extVehicle.requiresCargoPathRecalculation = false; // reset flag

VehicleManager instance = VehicleManager.instance;
ref Building airplaneStand = ref vehicle.m_targetBuilding.ToBuilding();
int firstCargoVehicleId = vehicle.m_firstCargo;
int counter = 0;
uint maxVehicles = instance.m_vehicles.m_size;
while (firstCargoVehicleId != 0)
{
ref Vehicle v = ref instance.m_vehicles.m_buffer[firstCargoVehicleId];
ushort nextCargo = v.m_nextCargo;
VehicleInfo info = v.Info;
if (v.m_path != 0) {
if (vehicle.m_targetBuilding != 0 && v.m_targetBuilding != 0 && vehicle.m_targetBuilding != v.m_targetBuilding) {
//source building
Randomizer randomizer = new Randomizer(firstCargoVehicleId);
airplaneStand.Info.m_buildingAI.CalculateSpawnPosition(
vehicle.m_targetBuilding,
ref airplaneStand,
ref randomizer,
info,
out Vector3 _,
out Vector3 dir);
//target building
ref Building targetBuilding = ref v.m_targetBuilding.ToBuilding();
BuildingInfo buildingInfo = targetBuilding.Info;
Randomizer randomizer2 = new Randomizer(firstCargoVehicleId);
buildingInfo.m_buildingAI.CalculateUnspawnPosition(
(ushort)firstCargoVehicleId,
ref targetBuilding,
ref randomizer2,
info,
out Vector3 _,
out Vector3 dir2);

if (FindPathTransportedCargoTruck(dir, dir2, (ushort)firstCargoVehicleId, ref v)) {
// Log.Info($"FindPathTransportedCargoTruck success, pos: {pos}, dir: {dir}, target: {dir2} first: {firstCargoVehicleId}");
} else {
// Log.Info($"FindPathTransportedCargoTruck failure, pos: {pos}, dir: {dir}, target: {dir2} first: {firstCargoVehicleId}");
}
} else {
// Log.Info($"Skipped recalculation {firstCargoVehicleId}: veh_target: {vehicle.m_targetBuilding}, v_target: {v.m_targetBuilding}");
}
} else {
// Log.Info($"Attached Cargo Vehicle: {firstCargoVehicleId} to :{vehicleId} had no path...");
}

firstCargoVehicleId = nextCargo;
if (++counter > maxVehicles) {
CODebugBase<LogChannel>.Error(
LogChannel.Core,
$"Invalid list detected! nextCargo: {nextCargo} vehicleId: {vehicleId} vehicleInfo: {vehicle.Info.name} \n" + Environment.StackTrace);
break;
}
}
}

/// <summary>
/// Slightly modified StartPathFind of CargoTruck
/// </summary>
/// <param name="startPos">start position</param>
/// <param name="endPos">end position</param>
/// <param name="vehicleID">vehicle id</param>
/// <param name="vehicleData">vehicle data</param>
/// <returns>success or fail of swap part of path transported cargo truck</returns>
private static bool FindPathTransportedCargoTruck(Vector3 startPos,
Vector3 endPos,
ushort vehicleID,
ref Vehicle vehicleData) {
VehicleInfo info = vehicleData.Info;
bool allowUnderground = (vehicleData.m_flags & (Vehicle.Flags.Underground | Vehicle.Flags.Transition)) != 0;
if (PathManager.FindPathPosition(
startPos,
ItemClass.Service.Road,
NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle,
info.m_vehicleType,
allowUnderground,
requireConnect: false,
32f,
out PathUnit.Position pathPosA,
out PathUnit.Position pathPosB,
out float distanceSqrA,
out float _) &&
PathManager.FindPathPosition(
endPos,
ItemClass.Service.Road,
NetInfo.LaneType.Vehicle | NetInfo.LaneType.TransportVehicle,
info.m_vehicleType,
false,
requireConnect: false,
32f,
out PathUnit.Position pathPosA2,
out PathUnit.Position pathPosB2,
out float distanceSqrA2,
out float _)) {

if (distanceSqrA < 10f) {
pathPosB = default(PathUnit.Position);
}

if (distanceSqrA2 < 10f) {
pathPosB2 = default(PathUnit.Position);
}

if (Singleton<PathManager>.instance.CreatePath(
out uint unit,
ref Singleton<SimulationManager>.instance.m_randomizer,
Singleton<SimulationManager>.instance.m_currentBuildIndex,
pathPosA,
pathPosB,
pathPosA2,
pathPosB2,
default(PathUnit.Position),
NetInfo.LaneType.Vehicle,
info.m_vehicleType,
20000f,
((CargoTruckAI)info.m_vehicleAI).m_isHeavyVehicle,
false,
stablePath: false,
skipQueue: false,
randomParking: false,
ignoreFlooded: false,
true)) {

// get original path unit and "patch it" to jump to new part of the path
ref PathUnit originalPathUnit = ref PathManager.instance.m_pathUnits.m_buffer[vehicleData.m_path];
int firstIndex = vehicleData.m_pathPositionIndex >> 1;
uint nextOriginalPathUnit = originalPathUnit.m_nextPathUnit;
if (nextOriginalPathUnit != 0) {
// release no longer used, original part of the path (releases all units to the end)
PathManager.instance.ReleasePath(nextOriginalPathUnit);
}

originalPathUnit.m_nextPathUnit = unit;
originalPathUnit.m_positionCount = (byte)(firstIndex + 1);
return true;
}
// Log.Info($"Vehicle: {vehicleID} - CreatePath failed -> pA: {pathPosA} pB: {pathPosB} pA2: {pathPosA2} pB2: {pathPosB2}");
}

// Log.Info($"Vehicle: {vehicleID} - FindPathPosition failed -> pA: {pathPosA} pB: {pathPosB} ");
return false;
}
}
}