Ejemplo n.º 1
0
        /// <summary>
        /// Assigns a vehicle to a target building.
        /// </summary>
        /// <param name="targetBuilding">The target building.</param>
        /// <param name="ignoreRange">If set to <c>true</c> ignore the building range.</param>
        /// <param name="allowCreateSpares">If set to <c>true</c> allow creation of spare vehicles.</param>
        /// <returns>
        /// True if vehicle was assigned.
        /// </returns>
        private bool AssignVehicle(TargetBuildingInfo targetBuilding, bool ignoreRange, bool allowCreateSpares)
        {
            // Get target district.
            byte targetDistrict = 0;
            if (this.serviceSettings.DispatchByDistrict)
            {
                targetDistrict = Singleton<DistrictManager>.instance.GetDistrict(targetBuilding.Position);
            }

            // Set target info on service buildings.
            foreach (ServiceBuildingInfo serviceBuilding in this.serviceBuildings.Values.Where(sb => sb.CanReceive && sb.VehiclesFree > 0))
            {
                serviceBuilding.SetTargetInfo(targetBuilding, ignoreRange);
            }

            // Vehicles that failed to find a path.
            HashSet<ushort> lostVehicles = new HashSet<ushort>();

            // Found vehicle that has enough free capacity.
            ushort foundVehicleId;
            float foundVehicleDistance;
            ServiceBuildingInfo foundVehicleBuilding;

            // Found vehicle with decent free capacity.
            ushort foundVehicleDecentId;
            float foundVehicleDecentDistance;

            // Found vehicle with ok free capacity.
            ushort foundVehicleOkId;
            float foundVehicleOkDistance;
            ServiceBuildingInfo foundVehicleOkBuilding;
            bool foundVehicleOkCheck;

            // Found vehicle with any free capacity.
            ushort foundVehicleLastResortId;
            float foundVehicleLastResortDistance;
            ServiceBuildingInfo foundVehicleLastResortBuilding;
            bool foundVehicleLastResortCheck;

            // Created new vehicle.
            bool createdVehicle;

            // try n times (in case vehicles have problem finding a path to the buidling)
            for (int tries = 0; tries < 16; tries++)
            {
                ////if (Log.LogALot)
                ////{
                ////    Log.DevDebug(this, "AssignVehicle", "Try", tries, allowCreateSpares, this.createSpareVehicles);
                ////}

                // Found vehicle that has enough free capacity.
                foundVehicleId = 0;
                foundVehicleDistance = float.PositiveInfinity;
                foundVehicleBuilding = null;

                // Found vehicle with decent free capacity.
                foundVehicleDecentId = 0;
                foundVehicleDecentDistance = float.PositiveInfinity;

                // Found vehicle with ok free capacity.
                foundVehicleOkId = 0;
                foundVehicleOkDistance = float.PositiveInfinity;
                foundVehicleOkBuilding = null;

                // Found vehicle with any free capacity.
                foundVehicleLastResortId = 0;
                foundVehicleLastResortDistance = float.PositiveInfinity;
                foundVehicleLastResortBuilding = null;

                // Created new vehicle.
                createdVehicle = false;

                // Loop through service buildings in priority order and assign a vehicle to the target.
                ////if (Log.LogALot)
                ////{
                ////    foreach (ServiceBuildingInfo serviceBuilding in this.serviceBuildings.Values)
                ////    {
                ////        Log.DevDebug(this, "AssignVehicle", "ServiceBuilding?", serviceBuilding.BuildingId, serviceBuilding.CanReceive, serviceBuilding.VehiclesFree, serviceBuilding.VehiclesSpare, serviceBuilding.InRange, serviceBuilding.BuildingName, serviceBuilding.DistrictName);
                ////    }
                ////}

                foreach (ServiceBuildingInfo serviceBuilding in this.serviceBuildings.Values.Where(sb => sb.CanReceive && (sb.VehiclesFree > 0 || (allowCreateSpares && this.serviceSettings.CreateSpares != ServiceDispatcherSettings.SpareVehiclesCreation.Never && sb.VehiclesSpare > 0)) && sb.InRange).OrderBy(sb => sb, Global.ServiceBuildingInfoPriorityComparer))
                {
                    // Found vehicle that has enough free capacity.
                    foundVehicleId = 0;
                    foundVehicleDistance = float.PositiveInfinity;

                    // Found vehicle with decent free capacity.
                    foundVehicleDecentId = 0;
                    foundVehicleDecentDistance = float.PositiveInfinity;

                    // Whether to check for vehicles with not so decent free capacity.
                    foundVehicleOkCheck = foundVehicleOkId == 0;
                    foundVehicleLastResortCheck = foundVehicleLastResortId == 0;

                    // If prefer to send new vehicle when building is closer.
                    if (allowCreateSpares && this.serviceSettings.CreateSpares == ServiceDispatcherSettings.SpareVehiclesCreation.WhenBuildingIsCloser && serviceBuilding.VehiclesSpare > 0)
                    {
                        foundVehicleDistance = serviceBuilding.Distance;
                    }

                    if (Log.LogALot)
                    {
                        Log.DevDebug(this, "AssignVehicle", "ServiceBuilding", serviceBuilding.BuildingId, serviceBuilding.BuildingName, serviceBuilding.DistrictName, serviceBuilding.Distance, foundVehicleDistance);
                    }

                    // Loop through vehicles and save the closest free vehicle.
                    foreach (ServiceVehicleInfo vehicleInfo in serviceBuilding.Vehicles.Values.Where(vi => vi.FreeToCollect))
                    {
                        // Don't re-check vehicles that just failed to find path.
                        if (lostVehicles.Contains(vehicleInfo.VehicleId))
                        {
                            continue;
                        }

                        if (vehicleInfo.IsConfused)
                        {
                            lostVehicles.Add(vehicleInfo.VehicleId);
                            continue;
                        }

                        float distance = (targetBuilding.Position - vehicleInfo.Position).sqrMagnitude;

                        ////if (Log.LogALot)
                        ////{
                        ////    Log.DevDebug(this, "AssignVehicle", "ServiceVehicle", vehicleInfo.VehicleId, vehicleInfo.VehicleName, vehicleInfo.DistrictName, distance);
                        ////}

                        // Check for vehicle with enough free capacity.
                        if ((distance < foundVehicleDistance || (foundVehicleId == 0 && distance == foundVehicleDistance)) && vehicleInfo.CapacityFree >= targetBuilding.ProblemSize)
                        {
                            if (Log.LogALot)
                            {
                                Log.DevDebug(this, "AssignVehicle", "Found", vehicleInfo.VehicleId, distance);
                            }

                            foundVehicleId = vehicleInfo.VehicleId;
                            foundVehicleDistance = distance;
                        }

                        // Check for vehicle with decent free capacity.
                        if (distance < foundVehicleDecentDistance && (vehicleInfo.CapacityUsed < CapacityUsedDecent || (float)vehicleInfo.CapacityFree >= (float)targetBuilding.ProblemSize * CapacityProportionDecent))
                        {
                            if (Log.LogALot)
                            {
                                Log.DevDebug(this, "AssignVehicle", "FoundDecent", vehicleInfo.VehicleId, distance);
                            }

                            foundVehicleDecentId = vehicleInfo.VehicleId;
                            foundVehicleDecentDistance = distance;
                        }

                        // Check for vehicle with ok free capacity.
                        if (foundVehicleOkCheck && distance < foundVehicleOkDistance && (vehicleInfo.CapacityUsed < CapacityUsedOk || (float)vehicleInfo.CapacityFree >= (float)targetBuilding.ProblemSize * CapacityProportionOk))
                        {
                            if (Log.LogALot)
                            {
                                Log.DevDebug(this, "AssignVehicle", "FoundOk", vehicleInfo.VehicleId, distance);
                            }

                            foundVehicleOkId = vehicleInfo.VehicleId;
                            foundVehicleOkDistance = distance;
                            foundVehicleOkBuilding = serviceBuilding;
                        }

                        // Check for vehicle with any free capacity.
                        if (foundVehicleLastResortCheck && distance < foundVehicleLastResortDistance && vehicleInfo.CapacityFree > 0)
                        {
                            if (Log.LogALot)
                            {
                                Log.DevDebug(this, "AssignVehicle", "FoundLastResort", vehicleInfo.VehicleId, distance);
                            }

                            foundVehicleLastResortId = vehicleInfo.VehicleId;
                            foundVehicleLastResortDistance = distance;
                            foundVehicleLastResortBuilding = serviceBuilding;
                        }
                    }

                    // If no vehicle with enough free capacity (including the spare vehicles in the building), use the closest with decent free capacity instead.
                    if (foundVehicleDistance == float.PositiveInfinity && foundVehicleDecentDistance < float.PositiveInfinity)
                    {
                        foundVehicleId = foundVehicleDecentId;
                        foundVehicleDistance = foundVehicleDecentDistance;

                        Log.Debug(this, "AssignVehicle", "UsingDecent", targetBuilding.BuildingId, serviceBuilding.BuildingId, serviceBuilding.VehiclesSpare, foundVehicleId, foundVehicleDistance);
                    }

                    // No free vehicle found, but building has spare vehicles so we send one of those.
                    if (foundVehicleId == 0 && allowCreateSpares && !lostVehicles.Contains(0) && this.serviceSettings.CreateSpares != ServiceDispatcherSettings.SpareVehiclesCreation.Never && serviceBuilding.VehiclesSpare > 0)
                    {
                        Log.Debug(this, "AssignVehicle", "CreateSpare", targetBuilding.BuildingId, serviceBuilding.BuildingId, serviceBuilding.VehiclesSpare);

                        lostVehicles.Add(0);

                        foundVehicleId = serviceBuilding.CreateVehicle(this.TransferType, targetBuilding.BuildingId);
                        if (foundVehicleId == 0)
                        {
                            Log.Debug(this, "AssignVehicle", "SpareNotCreated", targetBuilding.BuildingId, serviceBuilding.BuildingId);
                        }
                        else
                        {
                            createdVehicle = true;
                            foundVehicleDistance = serviceBuilding.Distance;

                            if (Log.LogALot)
                            {
                                Vehicle[] vehicles = Singleton<VehicleManager>.instance.m_vehicles.m_buffer;

                                Log.DevDebug(
                                    this,
                                    "AssignVehicle",
                                    "Assign",
                                    "C",
                                    targetBuilding.BuildingId,
                                    serviceBuilding.BuildingId,
                                    foundVehicleId,
                                    serviceBuilding.VehiclesSpare,
                                    serviceBuilding.VehiclesFree,
                                    serviceBuilding.VehiclesTotal,
                                    targetBuilding.BuildingName,
                                    targetBuilding.DistrictName,
                                    serviceBuilding.BuildingName,
                                    serviceBuilding.DistrictName,
                                    VehicleHelper.GetVehicleName(foundVehicleId),
                                    vehicles[foundVehicleId].m_targetBuilding,
                                    vehicles[foundVehicleId].m_flags);
                            }
                        }
                    }

                    if (foundVehicleId != 0)
                    {
                        foundVehicleBuilding = serviceBuilding;
                        break;
                    }
                }

                if (!createdVehicle)
                {
                    // If no vehicle with decent free capacity (including the spare vehicles in the buildings), use the closest with ok free capacity instead.
                    if (foundVehicleDistance == float.PositiveInfinity && foundVehicleOkDistance < float.PositiveInfinity)
                    {
                        foundVehicleId = foundVehicleDecentId;
                        foundVehicleDistance = foundVehicleDecentDistance;
                        foundVehicleBuilding = foundVehicleOkBuilding;

                        Log.Debug(this, "AssignVehicle", "UsingOk", targetBuilding.BuildingId, foundVehicleBuilding.BuildingId, foundVehicleBuilding.VehiclesSpare, foundVehicleId, foundVehicleDistance);
                    }

                    // If no vehicle with ok free capacity (including the spare vehicles in the buildings), use the closest with any free capacity instead.
                    if (foundVehicleDistance == float.PositiveInfinity && foundVehicleLastResortDistance < float.PositiveInfinity)
                    {
                        foundVehicleId = foundVehicleLastResortId;
                        foundVehicleDistance = foundVehicleLastResortDistance;
                        foundVehicleBuilding = foundVehicleLastResortBuilding;

                        Log.Debug(this, "AssignVehicle", "UsingLastResort", targetBuilding.BuildingId, foundVehicleBuilding.BuildingId, foundVehicleBuilding.VehiclesSpare, foundVehicleId, foundVehicleDistance);
                    }

                    // No free vehicle was found, return.
                    if (foundVehicleId == 0)
                    {
                        if (Log.LogALot)
                        {
                            Log.DevDebug(this, "AssignVehicle", "NotFound");
                        }

                        return false;
                    }

                    // A free vehicle was found, assign it to the target.
                    {
                        Vehicle[] vehicles = Singleton<VehicleManager>.instance.m_vehicles.m_buffer;

                        if (!foundVehicleBuilding.Vehicles[foundVehicleId].SetTarget(targetBuilding.BuildingId, ref vehicles[foundVehicleId], (TransferManager.TransferReason)this.TransferType))
                        {
                            // The vehicle failed to find a path to the target.
                            Log.Debug("AssignVehicle", "SetTarget", "Failed", targetBuilding.BuildingId, foundVehicleBuilding.BuildingId, foundVehicleBuilding.VehiclesSpare, foundVehicleId, foundVehicleDistance, vehicles[foundVehicleId].m_flags);

                            lostVehicles.Add(foundVehicleId);
                            continue;
                        }
                    }

                    this.freeVehicles--;
                    foundVehicleBuilding.VehiclesFree--;
                }

                targetBuilding.Handled = true;

                this.assignedTargets[targetBuilding.BuildingId] = Global.CurrentFrame;

                if (Log.LogToFile)
                {
                    if (Log.LogALot)
                    {
                        Vehicle[] vehicles = Singleton<VehicleManager>.instance.m_vehicles.m_buffer;

                        Log.DevDebug(
                            this,
                            "AssignVehicle",
                            "Assign",
                            "T",
                            targetBuilding.BuildingId,
                            foundVehicleBuilding.BuildingId,
                            foundVehicleId,
                            targetBuilding.District,
                            targetBuilding.HasProblem,
                            targetBuilding.ProblemValue,
                            targetBuilding.ProblemSize,
                            targetBuilding.BuildingName,
                            targetBuilding.DistrictName,
                            targetBuilding.HasProblem ? "HasProblem" : (string)null,
                            targetBuilding.ProblemValue >= ProblemLimit ? "ProblemLimit" : (string)null,
                            targetBuilding.ProblemValue >= ProblemLimitMajor ? "ProblemLimitMajor" : (string)null,
                            targetBuilding.ProblemValue >= ProblemLimitForgotten ? "ProblemLimitForgotten" : (string)null);
                        Log.DevDebug(
                            this,
                            "AssignVehicle",
                            "Assign",
                            "S",
                            targetBuilding.BuildingId,
                            foundVehicleBuilding.BuildingId,
                            foundVehicleId,
                            foundVehicleBuilding.District,
                            foundVehicleBuilding.InDistrict,
                            foundVehicleBuilding.InRange,
                            foundVehicleBuilding.Range,
                            foundVehicleBuilding.Distance,
                            foundVehicleBuilding.VehiclesTotal,
                            foundVehicleBuilding.VehiclesMade,
                            foundVehicleBuilding.VehiclesFree,
                            foundVehicleBuilding.VehiclesSpare,
                            foundVehicleBuilding.BuildingName,
                            foundVehicleBuilding.DistrictName,
                            foundVehicleBuilding.InDistrict ? "InDistrict" : (string)null,
                            foundVehicleBuilding.InRange ? "InRange" : "OutOfRange");
                        Log.DevDebug(
                            this,
                            "AssignVehicle",
                            "Assign",
                            "V",
                            targetBuilding.BuildingId,
                            foundVehicleBuilding.BuildingId,
                            foundVehicleId,
                            foundVehicleDistance,
                            foundVehicleBuilding.Vehicles[foundVehicleId].CapacityUsed,
                            foundVehicleBuilding.Vehicles[foundVehicleId].CapacityFree,
                            VehicleHelper.GetVehicleName(foundVehicleId),
                            VehicleHelper.GetDistrictName(foundVehicleId),
                            vehicles[foundVehicleId].m_targetBuilding,
                            vehicles[foundVehicleId].m_flags);
                    }
                    else if (Log.LogNames)
                    {
                        Log.DevDebug(
                            this,
                            "AssignVehicle",
                            "Assign",
                            targetBuilding.BuildingId,
                            foundVehicleBuilding.BuildingId,
                            foundVehicleId,
                            targetBuilding.HasProblem,
                            targetBuilding.ProblemValue,
                            foundVehicleBuilding.InDistrict,
                            foundVehicleBuilding.InRange,
                            foundVehicleBuilding.Range,
                            foundVehicleBuilding.Distance,
                            foundVehicleBuilding.Distance,
                            targetBuilding.BuildingName,
                            targetBuilding.DistrictName,
                            foundVehicleBuilding.BuildingName,
                            foundVehicleBuilding.DistrictName,
                            VehicleHelper.GetVehicleName(foundVehicleId));
                    }
                    else
                    {
                        Log.Debug(
                            this,
                            "AssignVehicle",
                            "Assign",
                            targetBuilding.BuildingId,
                            foundVehicleBuilding.BuildingId,
                            foundVehicleId,
                            targetBuilding.HasProblem,
                            targetBuilding.ProblemValue,
                            foundVehicleBuilding.InDistrict,
                            foundVehicleBuilding.InRange,
                            foundVehicleBuilding.Range,
                            foundVehicleBuilding.Distance,
                            foundVehicleDistance);
                    }
                }

                return true;
            }

            return false;
        }
 /// <summary>
 /// Logs building info for debug use.
 /// </summary>
 /// <param name="buildings">The buildings.</param>
 /// <param name="vehicles">The vehicles.</param>
 /// <param name="districtManager">The district manager.</param>
 /// <param name="citizenManager">The citizen manager.</param>
 /// <param name="buildingId">The building identifier.</param>
 /// <param name="serviceBuilding">The service building.</param>
 /// <param name="targetBuilding">The target building.</param>
 /// <param name="buildingStamp">The building stamp.</param>
 private static void DebugListLog(
     Building[] buildings,
     Vehicle[] vehicles,
     DistrictManager districtManager,
     CitizenManager citizenManager,
     ushort buildingId,
     ServiceBuildingInfo serviceBuilding,
     TargetBuildingInfo targetBuilding,
     BuildingStamp buildingStamp)
 {
     if (buildings[buildingId].Info != null && (buildings[buildingId].m_flags & Building.Flags.Created) == Building.Flags.Created)
     {
         Log.InfoList info = DebugInfoMsg(buildings, vehicles, districtManager, citizenManager, buildingId, serviceBuilding, targetBuilding, buildingStamp);
         Log.DevDebug(typeof(BuildingHelper), "DebugListLog", info.ToString());
     }
 }
Ejemplo n.º 3
0
 /// <summary>
 /// Check whether this building should be checked.
 /// </summary>
 /// <param name="building">The building.</param>
 /// <returns>
 /// True if the building should be checked.
 /// </returns>
 public bool CheckThis(TargetBuildingInfo building)
 {
     return building.CheckThis && !building.HandledNow &&
            (building.NeedsService || (this.IncludeUneedy && building.WantsService)) &&
            building.ProblemValue >= this.MinProblemValue && (building.HasProblem || !this.OnlyProblematic);
 }
        /// <summary>
        /// Collects building info for debug use.
        /// </summary>
        /// <param name="buildings">The buildings.</param>
        /// <param name="vehicles">The vehicles.</param>
        /// <param name="districtManager">The district manager.</param>
        /// <param name="citizenManager">The citizen manager.</param>
        /// <param name="buildingId">The building identifier.</param>
        /// <param name="serviceBuilding">The service building.</param>
        /// <param name="targetBuilding">The target building.</param>
        /// <param name="buildingStamp">The building stamp.</param>
        /// <param name="verbose">If set to <c>true</c> include more information.</param>
        /// <returns>The debug information.</returns>
        private static Log.InfoList DebugInfoMsg(
            Building[] buildings,
            Vehicle[] vehicles,
            DistrictManager districtManager,
            CitizenManager citizenManager,
            ushort buildingId,
            ServiceBuildingInfo serviceBuilding,
            TargetBuildingInfo targetBuilding,
            BuildingStamp buildingStamp,
            bool verbose = false)
        {
            Log.InfoList info = new Log.InfoList();

            if (buildingStamp != null)
            {
                info.Add("O", "BuildingStamp");
            }

            if (serviceBuilding != null)
            {
                info.Add("O", "ServiceBuilding");
            }

            if (targetBuilding != null)
            {
                info.Add("O", "TargetBuilding");
            }

            List<TargetBuildingInfo> targetBuildings = null;
            List<ServiceBuildingInfo> serviceBuildings = null;

            if (verbose && Global.Buildings != null)
            {
                targetBuildings = new List<TargetBuildingInfo>();
                serviceBuildings = new List<ServiceBuildingInfo>();

                if (serviceBuilding == null)
                {
                    if (Global.Buildings.GarbageBuildings == null || !Global.Buildings.GarbageBuildings.TryGetValue(buildingId, out serviceBuilding))
                    {
                        serviceBuildings.Add(serviceBuilding);
                    }

                    if (Global.Buildings.DeathCareBuildings == null || !Global.Buildings.DeathCareBuildings.TryGetValue(buildingId, out serviceBuilding))
                    {
                        serviceBuildings.Add(serviceBuilding);
                    }

                    if (Global.Buildings.HealthCareBuildings == null || !Global.Buildings.HealthCareBuildings.TryGetValue(buildingId, out serviceBuilding))
                    {
                        serviceBuildings.Add(serviceBuilding);
                    }
                }

                if (targetBuilding == null)
                {
                    if (Global.Buildings.DeadPeopleBuildings == null || !Global.Buildings.DeadPeopleBuildings.TryGetValue(buildingId, out targetBuilding))
                    {
                        targetBuildings.Add(targetBuilding);
                    }

                    if (Global.Buildings.DirtyBuildings == null || !Global.Buildings.DirtyBuildings.TryGetValue(buildingId, out targetBuilding))
                    {
                        targetBuildings.Add(targetBuilding);
                    }

                    if (Global.Buildings.SickPeopleBuildings == null || !Global.Buildings.SickPeopleBuildings.TryGetValue(buildingId, out targetBuilding))
                    {
                        targetBuildings.Add(targetBuilding);
                    }
                }
            }

            info.Add("BuildingId", buildingId);
            info.Add("AI", buildings[buildingId].Info.m_buildingAI.GetType());
            info.Add("InfoName", buildings[buildingId].Info.name);

            string name = GetBuildingName(buildingId);
            if (!String.IsNullOrEmpty(name) && name != buildings[buildingId].Info.name)
            {
                info.Add("BuildingName", name);
            }

            byte district = districtManager.GetDistrict(buildings[buildingId].m_position);
            info.Add("District", district);
            info.Add("DistrictName", districtManager.GetDistrictName(district));

            if (buildingStamp != null)
            {
                info.Add("Source", buildingStamp.Source);
                info.Add("SimulationTimeStamp", buildingStamp.SimulationTimeStamp);
                info.Add("SimulationTimeDelta", buildingStamp.SimulationTimeDelta);
            }

            if (serviceBuilding != null)
            {
                info.Add("CanReceive", serviceBuilding.CanReceive);
                info.Add("CapacityLevel", serviceBuilding.CapacityLevel);
                info.Add("CapactyFree", serviceBuilding.CapacityFree);
                info.Add("CapactyMax", serviceBuilding.CapacityMax);
                info.Add("CapactyOverflow", serviceBuilding.CapacityOverflow);
                info.Add("Range", serviceBuilding.Range);
                info.Add("VehiclesFree", serviceBuilding.VehiclesFree);
                info.Add("VehiclesSpare", serviceBuilding.VehiclesSpare);
                info.Add("VehiclesMade", serviceBuilding.VehiclesMade);
                info.Add("VehiclesTotal", serviceBuilding.VehiclesTotal);
            }

            if (serviceBuildings != null)
            {
                for (int i = 0; i < serviceBuildings.Count; i++)
                {
                    serviceBuilding = serviceBuildings[i];
                    string n = (i + 1).ToString();

                    info.Add("CanReceive" + n, serviceBuilding.CanReceive);
                    info.Add("CapacityLevel" + n, serviceBuilding.CapacityLevel);
                    info.Add("CapactyFree" + n, serviceBuilding.CapacityFree);
                    info.Add("CapactyMax" + n, serviceBuilding.CapacityMax);
                    info.Add("CapactyOverflow" + n, serviceBuilding.CapacityOverflow);
                    info.Add("Range" + n, serviceBuilding.Range);
                    info.Add("VehiclesFree" + n, serviceBuilding.VehiclesFree);
                    info.Add("VehiclesSpare" + n, serviceBuilding.VehiclesSpare);
                    info.Add("VehiclesMade" + n, serviceBuilding.VehiclesMade);
                    info.Add("VehiclesTotal" + n, serviceBuilding.VehiclesTotal);
                }
            }

            if (targetBuilding != null)
            {
                info.Add("Demand", targetBuilding.Demand);
                info.Add("HasProblem", targetBuilding.HasProblem);
                info.Add("ProblemSize", targetBuilding.ProblemSize);
                info.Add("ProblemValue", targetBuilding.ProblemValue);
            }

            if (targetBuildings != null)
            {
                for (int i = 0; i < targetBuildings.Count; i++)
                {
                    targetBuilding = targetBuildings[i];
                    string n = (i + 1).ToString();

                    info.Add("Demand" + n, targetBuilding.Demand);
                    info.Add("HasProblem" + n, targetBuilding.HasProblem);
                    info.Add("ProblemSize" + n, targetBuilding.ProblemSize);
                    info.Add("ProblemValue" + n, targetBuilding.ProblemValue);
                }
            }

            if (verbose && Global.Buildings != null)
            {
                Double desolate;

                if (Global.Buildings.DesolateBuildings != null && Global.Buildings.DesolateBuildings.TryGetValue(buildingId, out desolate))
                {
                    info.Add("Desolate", desolate);
                }
            }

            int materialMax = 0;
            int materialAmount = 0;
            int serviceVehicleCount = 0;
            if (buildings[buildingId].Info.m_buildingAI is CemeteryAI)
            {
                serviceVehicleCount = ((CemeteryAI)buildings[buildingId].Info.m_buildingAI).m_hearseCount;
                info.Add("CorpseCapacity", ((CemeteryAI)buildings[buildingId].Info.m_buildingAI).m_corpseCapacity);
                info.Add("GraveCount", ((CemeteryAI)buildings[buildingId].Info.m_buildingAI).m_graveCount);
                info.Add("CustomBuffer1", buildings[buildingId].m_customBuffer1); // GraveCapacity?
                info.Add("CustomBuffer2", buildings[buildingId].m_customBuffer2);
                info.Add("PR_HC_Calc", ((buildings[buildingId].m_productionRate * ((CemeteryAI)buildings[buildingId].Info.m_buildingAI).m_hearseCount) + 99) / 100); // Hearse capacity?
                buildings[buildingId].Info.m_buildingAI.GetMaterialAmount(buildingId, ref buildings[buildingId], TransferManager.TransferReason.Dead, out materialAmount, out materialMax);
            }
            else if (buildings[buildingId].Info.m_buildingAI is LandfillSiteAI)
            {
                serviceVehicleCount = ((LandfillSiteAI)buildings[buildingId].Info.m_buildingAI).m_garbageTruckCount;
                buildings[buildingId].Info.m_buildingAI.GetMaterialAmount(buildingId, ref buildings[buildingId], TransferManager.TransferReason.Garbage, out materialAmount, out materialMax);
            }
            else if (buildings[buildingId].Info.m_buildingAI is HospitalAI)
            {
                serviceVehicleCount = ((HospitalAI)buildings[buildingId].Info.m_buildingAI).m_ambulanceCount;
                info.Add("", ((HospitalAI)buildings[buildingId].Info.m_buildingAI).m_patientCapacity);
                buildings[buildingId].Info.m_buildingAI.GetMaterialAmount(buildingId, ref buildings[buildingId], TransferManager.TransferReason.Sick, out materialAmount, out materialMax);
            }
            info.Add("materialMax", materialMax);
            info.Add("materialAmount", materialAmount);
            info.Add("materialFree", materialMax - materialAmount);

            int productionRate = buildings[buildingId].m_productionRate;

            ushort ownVehicleCount = 0;
            ushort madeVehicleCount = 0;
            ushort vehicleId = buildings[buildingId].m_ownVehicles;
            while (vehicleId != 0 && ownVehicleCount < ushort.MaxValue)
            {
                ownVehicleCount++;
                if ((vehicles[vehicleId].m_transferType == (byte)TransferManager.TransferReason.Garbage || vehicles[vehicleId].m_transferType == (byte)TransferManager.TransferReason.Dead) &&
                    vehicles[vehicleId].Info != null &&
                    (vehicles[vehicleId].m_flags & Vehicle.Flags.Created) == Vehicle.Flags.Created &&
                    (vehicles[vehicleId].m_flags & VehicleHelper.VehicleExists) != ~Vehicle.Flags.All)
                {
                    madeVehicleCount++;
                }

                vehicleId = vehicles[vehicleId].m_nextOwnVehicle;
            }
            info.Add("OwnVehicles", ownVehicleCount);
            info.Add("MadeVehicles", madeVehicleCount);

            info.Add("VehicleCount", serviceVehicleCount);
            info.Add("ProductionRate", productionRate);
            info.Add("VehicleCountNominal", ((productionRate * serviceVehicleCount) + 99) / 100);

            int budget = Singleton<EconomyManager>.instance.GetBudget(buildings[buildingId].Info.m_buildingAI.m_info.m_class);
            productionRate = PlayerBuildingAI.GetProductionRate(100, budget);
            int actualVehicleCount = ((productionRate * serviceVehicleCount) + 99) / 100;
            info.Add("Budget", budget);
            info.Add("ProductionRateActual", productionRate);
            info.Add("VehicleCountActual", actualVehicleCount);
            info.Add("SpareVehicles", actualVehicleCount - ownVehicleCount);

            float range = buildings[buildingId].Info.m_buildingAI.GetCurrentRange(buildingId, ref buildings[buildingId]);
            range = range * range * Global.Settings.RangeModifier;
            if (range < Global.Settings.RangeMinimum)
            {
                info.Add("Range", range, Global.Settings.RangeMinimum);
            }
            else if (range > Global.Settings.RangeMaximum)
            {
                info.Add("Range", range, Global.Settings.RangeMaximum);
            }
            else
            {
                info.Add("Range", range);
            }

            List<string> needs = new List<string>();
            if (buildings[buildingId].m_garbageBuffer >= Global.Settings.Garbage.MinimumAmountForDispatch)
            {
                needs.Add("Filthy");
            }
            if (buildings[buildingId].m_garbageBuffer >= Global.Settings.Garbage.MinimumAmountForPatrol)
            {
                needs.Add("Dirty");
            }
            else if (buildings[buildingId].m_garbageBuffer > 0)
            {
                needs.Add("Dusty");
            }
            if (buildings[buildingId].m_deathProblemTimer > 0)
            {
                needs.Add("Dead");
            }
            if (buildings[buildingId].m_garbageBuffer * Dispatcher.ProblemBufferModifier >= Dispatcher.ProblemLimitForgotten ||
                buildings[buildingId].m_deathProblemTimer * Dispatcher.ProblemTimerModifier >= Dispatcher.ProblemLimitForgotten)
            {
                needs.Add("Forgotten");
            }
            info.Add("Needs", needs);

            info.Add("DeathProblemTimer", buildings[buildingId].m_deathProblemTimer);
            info.Add("HealthProblemTimer", buildings[buildingId].m_healthProblemTimer);
            info.Add("MajorProblemTimer", buildings[buildingId].m_majorProblemTimer);

            int citizens = 0;
            int count = 0;
            uint unitId = buildings[buildingId].m_citizenUnits;
            while (unitId != 0)
            {
                CitizenUnit unit = citizenManager.m_units.m_buffer[unitId];
                for (int i = 0; i < 5; i++)
                {
                    uint citizenId = unit.GetCitizen(i);
                    if (citizenId != 0)
                    {
                        Citizen citizen = citizenManager.m_citizens.m_buffer[citizenId];
                        if (citizen.Dead && citizen.GetBuildingByLocation() == buildingId)
                        {
                            citizens++;
                        }
                    }
                }

                count++;
                if (count > (int)ushort.MaxValue * 10)
                {
                    break;
                }

                unitId = unit.m_nextUnit;
            }
            info.Add("DeadCitizens", citizens);

            info.Add("GarbageAmount", buildings[buildingId].Info.m_buildingAI.GetGarbageAmount(buildingId, ref buildings[buildingId]));
            info.Add("GarbageBuffer", buildings[buildingId].m_garbageBuffer);

            string problems = buildings[buildingId].m_problems.ToString();
            if (problems.IndexOfAny(new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }) >= 0)
            {
                foreach (Notification.Problem problem in Enum.GetValues(typeof(Notification.Problem)))
                {
                    if (problem != Notification.Problem.None && (buildings[buildingId].m_problems & problem) == problem)
                    {
                        problems += ", " + problem.ToString();
                    }
                }
            }
            info.Add("Problems", problems);

            string flags = buildings[buildingId].m_flags.ToString();
            if (flags.IndexOfAny(new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }) >= 0)
            {
                foreach (Building.Flags flag in Enum.GetValues(typeof(Building.Flags)))
                {
                    if (flag != Building.Flags.None && (buildings[buildingId].m_flags & flag) == flag)
                    {
                        flags += ", " + flag.ToString();
                    }
                }
            }
            info.Add("Flags", flags);

            string status = buildings[buildingId].Info.m_buildingAI.GetLocalizedStatus(buildingId, ref buildings[buildingId]);
            if (!String.IsNullOrEmpty(status))
            {
                info.Add("Status", status);
            }

            info.Add("AI", buildings[buildingId].Info.m_buildingAI.GetType().AssemblyQualifiedName);

            return info;
        }