private void CreateFireman(ushort vehicleID, ref Vehicle data, Citizen.AgePhase agePhase) { SimulationManager instance = Singleton <SimulationManager> .instance; CitizenManager instance2 = Singleton <CitizenManager> .instance; CitizenInfo groupCitizenInfo = instance2.GetGroupCitizenInfo(ref instance.m_randomizer, this.m_info.m_class.m_service, Citizen.Gender.Male, Citizen.SubCulture.Generic, agePhase); if (groupCitizenInfo != null) { int family = instance.m_randomizer.Int32(256u); uint num = 0u; if (instance2.CreateCitizen(out num, 90, family, ref instance.m_randomizer, groupCitizenInfo.m_gender)) { ushort num2; if (instance2.CreateCitizenInstance(out num2, ref instance.m_randomizer, groupCitizenInfo, num)) { Vector3 randomDoorPosition = data.GetRandomDoorPosition(ref instance.m_randomizer, VehicleInfo.DoorType.Exit); groupCitizenInfo.m_citizenAI.SetCurrentVehicle(num2, ref instance2.m_instances.m_buffer[(int)num2], 0, 0u, randomDoorPosition); groupCitizenInfo.m_citizenAI.SetTarget(num2, ref instance2.m_instances.m_buffer[(int)num2], data.m_targetBuilding); instance2.m_citizens.m_buffer[(int)((UIntPtr)num)].SetVehicle(num, vehicleID, 0u); } else { instance2.ReleaseCitizen(num); } } } }
/// <summary> /// Removes a citizen with invalid flags. /// </summary> /// <param name="citizenManager">CitizenManager instance reference</param> /// <param name="citizenID">Citizen ID to remove</param> /// <param name="citizenUnit">Owning CitizenUnit ID</param> /// <param name="buildingID">Home building ID</param> /// <param name="flags">Citizen flags</param> private static void RemoveCitizen(CitizenManager citizenManager, ref uint citizenID, uint citizenUnit, ushort buildingID, Citizen.Flags flags) { // Log messaged. Logging.Message("found citizen ", citizenID.ToString(), " in unit ", citizenUnit.ToString(), " of building ", buildingID.ToString(), " with invalid flags ", flags.ToString()); // Remove citizen and reset reference in CitizenUnit. citizenManager.ReleaseCitizen(citizenID); citizenID = 0; }
public static void UpdateLocation(ResidentAI resident, uint citizenID, ref Citizen data) { try { CitizenManager _citizenManager = Singleton <CitizenManager> .instance; if (data.m_homeBuilding == 0 && data.m_workBuilding == 0 && data.m_visitBuilding == 0 && data.m_instance == 0 && data.m_vehicle == 0) { _citizenManager.ReleaseCitizen(citizenID); } else { bool goodsSatisfied = false; switch (data.CurrentLocation) { case Citizen.Location.Home: goodsSatisfied = ResidentLocationHandler.ProcessHome(ref resident, citizenID, ref data); break; case Citizen.Location.Work: goodsSatisfied = ResidentLocationHandler.ProcessWork(ref resident, citizenID, ref data); break; case Citizen.Location.Visit: goodsSatisfied = ResidentLocationHandler.ProcessVisit(ref resident, citizenID, ref data); break; case Citizen.Location.Moving: goodsSatisfied = ResidentLocationHandler.ProcessMoving(ref resident, citizenID, ref data); break; } if (goodsSatisfied) { data.m_flags &= ~Citizen.Flags.NeedGoods; } } } catch (Exception ex) { Debug.LogWarning("Error on " + citizenID); Debug.LogException(ex); } }
public override void OnUpdate(float realTimeDelta, float simulationTimeDelta) { _delta += simulationTimeDelta; if (_delta > 300) // run remover every 5 in-game minutes { _delta = 0; DebugOutputPanel.AddMessage(PluginManager.MessageType.Message, "iteration run"); Citizen[] citizens = _citizenManager.m_citizens.m_buffer; int deadCount = 0; int citizenCount = 0; for (uint i = 0; i < citizens.Length; i++) { Citizen citizen = citizens[i]; if (citizen.m_flags != Citizen.Flags.None) // Check if citizen struct has any flags (i.e. is the citizen initialized) { citizenCount++; if (citizen.Dead) { _citizenManager.ReleaseCitizen(i); // remove the citizen deadCount++; } } } if (deadCount > 0) { DebugOutputPanel.AddMessage(PluginManager.MessageType.Message, "Removed " + deadCount + " dead bodies out of " + citizenCount + " citizens"); } } base.OnUpdate(realTimeDelta, simulationTimeDelta); }
// copied from original private function ugh... private void MoveFamily(uint homeID, ref CitizenUnit data, ushort targetBuilding) { BuildingManager instance1 = Singleton <BuildingManager> .instance; CitizenManager instance2 = Singleton <CitizenManager> .instance; uint unitID = 0; if (targetBuilding != (ushort)0) { unitID = instance1.m_buildings.m_buffer[(int)targetBuilding].GetEmptyCitizenUnit(CitizenUnit.Flags.Home); } for (int index = 0; index < 5; ++index) { uint citizen = data.GetCitizen(index); if (citizen != 0U && !instance2.m_citizens.m_buffer[citizen].Dead) { instance2.m_citizens.m_buffer[citizen].SetHome(citizen, (ushort)0, unitID); if (instance2.m_citizens.m_buffer[citizen].m_homeBuilding == (ushort)0) { instance2.ReleaseCitizen(citizen); } } } }
/// <summary> /// Generic stuff that can be taken care of as a group. /// </summary> /// <param name="thisAI"></param> /// <param name="citizenID"></param> /// <param name="person"></param> /// <returns>Whether to continue or whether this has taken care of the resident.</returns> private static bool ProcessGenerics(ref ResidentAI thisAI, uint citizenID, ref Citizen person) { bool everythingOk = false; ushort residingBuilding = 0; CitizenManager _citizenManager = Singleton <CitizenManager> .instance; Citizen.Location _currentLocation = person.CurrentLocation; switch (_currentLocation) { case Citizen.Location.Home: residingBuilding = person.m_homeBuilding; break; case Citizen.Location.Work: residingBuilding = person.m_workBuilding; break; case Citizen.Location.Visit: residingBuilding = person.m_visitBuilding; break; } Building visitBuilding = Singleton <BuildingManager> .instance.m_buildings.m_buffer[person.m_visitBuilding]; bool inHealthcare = _currentLocation == Citizen.Location.Visit && residingBuilding != 0 && (visitBuilding.Info.m_class.m_service == ItemClass.Service.HealthCare || visitBuilding.Info.m_class.m_service == ItemClass.Service.Disaster); if (person.Dead) { if (residingBuilding == 0) { _citizenManager.ReleaseCitizen(citizenID); } else { if ((_currentLocation == Citizen.Location.Work || _currentLocation == Citizen.Location.Visit) && person.m_homeBuilding != 0) { person.SetWorkplace(citizenID, 0, 0U); } if ((_currentLocation == Citizen.Location.Home || _currentLocation == Citizen.Location.Visit) && person.m_workBuilding != 0) { person.SetWorkplace(citizenID, 0, 0U); } if ((_currentLocation == Citizen.Location.Home || _currentLocation == Citizen.Location.Work) && person.m_visitBuilding != 0) { person.SetVisitplace(citizenID, 0, 0U); } if (ExperimentsToggle.ImprovedDeathcare) { if (person.m_vehicle == 0 && !inHealthcare) { NewResidentAI.FindHospital(thisAI, citizenID, residingBuilding, TransferManager.TransferReason.Sick); } } else { if (person.m_vehicle == 0 && !inHealthcare) { NewResidentAI.FindHospital(thisAI, citizenID, residingBuilding, TransferManager.TransferReason.Dead); } } } } else if (person.Arrested) { if (_currentLocation == Citizen.Location.Visit && residingBuilding == 0) { person.Arrested = false; } else { person.Arrested = false; } } else if (person.Sick) { if (residingBuilding != 0) { if (person.m_vehicle == 0 && !inHealthcare) { NewResidentAI.FindHospital(thisAI, citizenID, residingBuilding, TransferManager.TransferReason.Sick); } } else { person.CurrentLocation = Citizen.Location.Home; } } else if (residingBuilding == 0) { person.CurrentLocation = Citizen.Location.Home; } else if (ShouldEvacuate(residingBuilding)) { NewResidentAI.FindEvacuationPlace(thisAI, citizenID, residingBuilding, NewResidentAI.GetEvacuationReason(thisAI, residingBuilding)); } else if (!person.Collapsed) { everythingOk = true; } return(everythingOk); }
public static bool ProcessHome(ref ResidentAI thisAI, uint citizenID, ref Citizen person) { CitizenManager _citizenManager = Singleton <CitizenManager> .instance; SimulationManager _simulation = Singleton <SimulationManager> .instance; if ((person.m_flags & Citizen.Flags.MovingIn) != Citizen.Flags.None) { _citizenManager.ReleaseCitizen(citizenID); } else if (ProcessGenerics(ref thisAI, citizenID, ref person)) { if ((person.m_flags & Citizen.Flags.NeedGoods) != Citizen.Flags.None && (!Chances.WorkDay(ref person) || _simulation.m_currentDayTimeHour > Chances.m_startWorkHour)) //Wants to go shopping { if (person.m_homeBuilding != 0 && person.m_instance == 0 && person.m_vehicle == 0) //Person isn't already out and about { if (_simulation.m_isNightTime) { uint chance = _simulation.m_randomizer.UInt32(1000); if (chance < Chances.GoOutAtNight(person.Age) && NewResidentAI.DoRandomMove(thisAI)) { //Only go locally to find a shop at night FindCloseVisitPlace(ref thisAI, citizenID, ref person, person.m_homeBuilding, 1000f); return(true); } } else if (NewResidentAI.DoRandomMove(thisAI)) { uint chance = _simulation.m_randomizer.UInt32(100); if (chance < 10) { uint localChance = _simulation.m_randomizer.UInt32(100); ushort localVisitPlace = 0; if (ExperimentsToggle.AllowLocalBuildingSearch && localChance < ExperimentsToggle.LocalBuildingPercentage) { LoggingWrapper.Log("Citizen " + citizenID + " trying to find a local commercial building."); localVisitPlace = FindCloseVisitPlace(ref thisAI, citizenID, ref person, person.m_homeBuilding, 1000f); } if (localVisitPlace == 0) { LoggingWrapper.Log("Citizen " + citizenID + " going to a random commercial building."); NewResidentAI.FindVisitPlace(thisAI, citizenID, person.m_homeBuilding, NewResidentAI.GetShoppingReason(thisAI)); } return(true); } } } } else if (person.m_homeBuilding != 0 && person.m_instance != 0 && person.m_vehicle == 0 || NewResidentAI.DoRandomMove(thisAI)) //If the person is already out and about, or can move (based on entities already visible) { int eventId = CityEventManager.instance.EventStartsWithin(citizenID, ref person, StartMovingToEventTime); if (eventId != -1) { CityEvent _cityEvent = CityEventManager.instance.m_nextEvents[eventId]; if (_cityEvent.EventStartsWithin(StartMovingToEventTime) && !_cityEvent.EventStartsWithin(MaxMoveToEventTime)) { if ((person.m_instance != 0 || NewResidentAI.DoRandomMove(thisAI)) && _cityEvent.Register(citizenID, ref person)) { NewResidentAI.StartMoving(thisAI, citizenID, ref person, person.m_homeBuilding, _cityEvent.m_eventData.m_eventBuilding); person.SetVisitplace(citizenID, _cityEvent.m_eventData.m_eventBuilding, 0U); person.m_visitBuilding = _cityEvent.m_eventData.m_eventBuilding; return(true); } } } else { if (person.m_workBuilding != 0 && !_simulation.m_isNightTime && !Chances.ShouldReturnFromWork(ref person)) { if (Chances.ShouldGoToWork(ref person)) { NewResidentAI.StartMoving(thisAI, citizenID, ref person, person.m_homeBuilding, person.m_workBuilding); return(true); } } else { if (Chances.ShouldGoFindEntertainment(ref person)) { if (_simulation.m_isNightTime) { FindLeisure(ref thisAI, citizenID, ref person, person.m_homeBuilding); } else { NewResidentAI.FindVisitPlace(thisAI, citizenID, person.m_homeBuilding, NewResidentAI.GetEntertainmentReason(thisAI)); } return(true); } } } } } return(false); }
int failedTimes; // how many times creation of tourists has failed public void Update() // called every frame by UnityEngine { // create tourists every minute, perhaps choose at which outside connections to create them // PROBABLY NOT spawn vehicles at outside connections more frequently, with reference to in game time? // TODO: Space Elevator NOT CONSIDERED!!!!! Debug.Log("Update() called by unity engine"); if (DataStorage.instance.modifierType == TourismIncreaseType.PopulationSizeRelated) { if (Utils.ComponentActionRequired) { ushort[] carConnections = Utils.GetConnectedOutsideConnections(ItemClass.SubService.None); // ItemClass.Service.Road ushort[] trainConnections = Utils.GetConnectedOutsideConnections(ItemClass.SubService.PublicTransportTrain); ushort[] planeConnections = Utils.GetConnectedOutsideConnections(ItemClass.SubService.PublicTransportPlane); ushort[] shipConnections = Utils.GetConnectedOutsideConnections(ItemClass.SubService.PublicTransportShip); // use avg land value to calculate the probability (percentage) of different modes of transport int avgLandValue = Singleton <DistrictManager> .instance.m_districts.m_buffer[0].GetLandValue(); int planeProb = planeConnections.Length == 0 ? 0 : 100 * avgLandValue / (avgLandValue + 40); int trainProb = trainConnections.Length == 0 ? 0 : planeProb == 0 ? ((100 - planeProb) * avgLandValue / (avgLandValue + 10)) / 2 : ((100 - planeProb) * avgLandValue / (avgLandValue + 15)) / 2; // ship and train prob, maybe just divide by 2 for each of these int shipProb = 0; if (trainProb == 0) { if (!(shipConnections.Length == 0)) // no train connections, do have ship connections { shipProb = planeProb == 0 ? ((100 - planeProb) * avgLandValue / (avgLandValue + 10)) : ((100 - planeProb) * avgLandValue / (avgLandValue + 15)); } } else { if (!(shipConnections.Length == 0)) { shipProb = trainProb; } else // have train connections but no ship connections { trainProb *= 2; } } int carProb = 100 - (planeProb + trainProb + shipProb); // would be pretty low even when land value is quite low //int trainProb = trainConnections.Length == 0 ? 0 : 100 - 100 * (avgLandValue / (avgLandValue + 25)); // TODO: maybe customize this //int shipProb = shipConnections.Length == 0 ? 0 : (100 - trainProb) - (100 - trainProb) * (avgLandValue / (avgLandValue + 30)); //int planeProb = 100 - (trainProb + shipProb); float amountOfTouristsThisMinute = DataStorage.instance.amountOfTouristsThisMinute; int amount = (int)Math.Truncate(amountOfTouristsThisMinute); amount += (float)random.NextDouble() < amountOfTouristsThisMinute - amount ? 1 : 0; amount += createTouristFailed; Debug.Log(string.Format("amount of tourists to create decided: {0}", amount)); int i; // for debug purpose for (i = 0; i < amount; ++i) { // decide which type of outside connection they are going to spawn at, hence decide the wealth level accordingly ItemClass.SubService typeOfTransport = ItemClass.SubService.PublicTransportPlane; int prob = randomizer.Int32(1, 100); if (prob < carProb) { typeOfTransport = ItemClass.SubService.None; } else if (prob < trainProb + shipProb) { typeOfTransport = randomizer.Int32(2) < 1 ? ItemClass.SubService.PublicTransportTrain : ItemClass.SubService.PublicTransportShip; } // else it's plane // decide the wealth of the tourist based on which type of outside connection it will come from Citizen.Wealth wealth = Citizen.Wealth.Low; if (typeOfTransport == ItemClass.SubService.PublicTransportPlane) { int rand = randomizer.Int32(100); wealth = rand > 18 ? Citizen.Wealth.High : Citizen.Wealth.Medium; } else if (typeOfTransport == ItemClass.SubService.PublicTransportTrain) { int rand = randomizer.Int32(100); wealth = rand > 80 ? Citizen.Wealth.High : rand > 40 ? Citizen.Wealth.Medium : Citizen.Wealth.Low; } else if (typeOfTransport == ItemClass.SubService.PublicTransportShip) { int rand = randomizer.Int32(100); wealth = rand > 70 ? Citizen.Wealth.High : rand > 25 ? Citizen.Wealth.Medium : Citizen.Wealth.Low; } else // roads, ItemClass.SubService.None { int rand = randomizer.Int32(100); wealth = rand > 70 ? Citizen.Wealth.High : rand > 30 ? Citizen.Wealth.Medium : Citizen.Wealth.Low; } // randomly choose which sourceBuilding aka the outside connection building the tourist is coming from ushort sourceBuilding = 0; try { switch (typeOfTransport) { case ItemClass.SubService.PublicTransportPlane: sourceBuilding = planeConnections[randomizer.Int32(Convert.ToUInt32(planeConnections.Length))]; break; case ItemClass.SubService.PublicTransportTrain: sourceBuilding = trainConnections[randomizer.Int32(Convert.ToUInt32(trainConnections.Length))]; break; case ItemClass.SubService.PublicTransportShip: sourceBuilding = shipConnections[randomizer.Int32(Convert.ToUInt32(shipConnections.Length))]; break; default: // road connections sourceBuilding = carConnections[randomizer.Int32(Convert.ToUInt32(carConnections.Length))]; break; } Debug.Log("will be spawned at this outside connection: " + sourceBuilding); } catch { Debug.Log("choosing connection building has thrown an exception"); } TransferManager.TransferReason transferReason = NewTouristAI.GetRandomTransferReason(0); TransferManager.TransferOffer offer = Utils.FindOffer(transferReason, true); ushort targetBuilding = offer.Building; Debug.Log(string.Format("component found building {0} for new tourist", targetBuilding)); uint unitID = buildingManager.m_buildings.m_buffer[targetBuilding].GetEmptyCitizenUnit(CitizenUnit.Flags.Visit); int family = Singleton <SimulationManager> .instance.m_randomizer.Int32(256U); int age = Singleton <SimulationManager> .instance.m_randomizer.Int32(0, 240); if (citizenManager.CreateCitizen(out uint citizen, age, family, ref Singleton <SimulationManager> .instance.m_randomizer)) { citizenManager.m_citizens.m_buffer[citizen].m_flags |= Citizen.Flags.Tourist; citizenManager.m_citizens.m_buffer[citizen].m_flags |= Citizen.Flags.MovingIn; citizenManager.m_citizens.m_buffer[citizen].WealthLevel = wealth; citizenManager.m_citizens.m_buffer[citizen].SetVisitplace(citizen, (ushort)0, unitID); CitizenInfo citizenInfo = citizenManager.m_citizens.m_buffer[citizen].GetCitizenInfo(citizen); if (citizenInfo != null && citizenManager.CreateCitizenInstance(out ushort citizenInstance, ref Singleton <SimulationManager> .instance.m_randomizer, citizenInfo, citizen)) { citizenInfo.m_citizenAI.SetSource(citizenInstance, ref citizenManager.m_instances.m_buffer[(int)citizenInstance], sourceBuilding); citizenInfo.m_citizenAI.SetTarget(citizenInstance, ref citizenManager.m_instances.m_buffer[(int)citizenInstance], targetBuilding, false); citizenManager.m_citizens.m_buffer[citizen].CurrentLocation = Citizen.Location.Moving; Singleton <StatisticsManager> .instance.Acquire <StatisticArray>(StatisticType.IncomingTourists).Acquire <StatisticInt32>((int)wealth, 3).Add(1); } else { failedTimes += 1; createTouristFailed += amount - i; citizenManager.ReleaseCitizen(citizen); break; } } else { failedTimes += 1; createTouristFailed += amount - i; break; } } Debug.Log(i + " amount of tourists created in this loop"); } } }
private void ProcessHumansUpdated() { SkylinesOverwatch.Data data = SkylinesOverwatch.Data.Instance; uint[] humans = data.HumansUpdated; if (humans.Length == 0) { return; } CitizenManager instance = Singleton <CitizenManager> .instance; foreach (uint i in humans) { if (!data.IsResident(i)) { continue; } Citizen[] residents = instance.m_citizens.m_buffer; Citizen resident = residents[(int)i]; if (resident.Dead) { continue; } if ((resident.m_flags & Citizen.Flags.Created) == Citizen.Flags.None) { continue; } if ((resident.m_flags & Citizen.Flags.DummyTraffic) != Citizen.Flags.None) { continue; } if (!Kill(resident)) { continue; } residents[(int)i].Sick = false; residents[(int)i].Dead = true; residents[(int)i].SetParkedVehicle(i, 0); ushort home = resident.GetBuildingByLocation(); if (home == 0) { home = resident.m_homeBuilding; } if (home != 0) { DistrictManager dm = Singleton <DistrictManager> .instance; Vector3 position = Singleton <BuildingManager> .instance.m_buildings.m_buffer[(int)home].m_position; byte district = dm.GetDistrict(position); District[] buffer = dm.m_districts.m_buffer; buffer[(int)district].m_deathData.m_tempCount = buffer[(int)district].m_deathData.m_tempCount + 1; } if (_randomizer.Int32(2) == 0) { instance.ReleaseCitizen(i); } SkylinesOverwatch.Helper.Instance.RequestHumanRemoval(i); } }
protected override void ProduceGoods(ushort buildingID, ref Building buildingData, ref Building.Frame frameData, int productionRate, int finalProductionRate, ref Citizen.BehaviourData behaviour, int aliveWorkerCount, int totalWorkerCount, int workPlaceCount, int aliveVisitorCount, int totalVisitorCount, int visitPlaceCount) { base.ProduceGoods(buildingID, ref buildingData, ref frameData, productionRate, finalProductionRate, ref behaviour, aliveWorkerCount, totalWorkerCount, workPlaceCount, aliveVisitorCount, totalVisitorCount, visitPlaceCount); int num = productionRate * m_deathCareAccumulation / 100; if (num != 0) { Singleton <ImmaterialResourceManager> .instance.AddResource(ImmaterialResourceManager.Resource.DeathCare, num, buildingData.m_position, m_deathCareRadius); } if (finalProductionRate != 0) { int num2 = buildingData.m_customBuffer1; int num3 = (m_burialRate * finalProductionRate * 100 + m_corpseCapacity - 1) / m_corpseCapacity; CitizenManager instance = Singleton <CitizenManager> .instance; uint num4 = buildingData.m_citizenUnits; int num5 = 0; int num6 = 0; int num7 = 0; while (num4 != 0) { uint nextUnit = instance.m_units.m_buffer[num4].m_nextUnit; if ((instance.m_units.m_buffer[num4].m_flags & CitizenUnit.Flags.Visit) != 0) { for (int i = 0; i < 5; i++) { uint citizen = instance.m_units.m_buffer[num4].GetCitizen(i); if (citizen != 0) { if (instance.m_citizens.m_buffer[citizen].Dead) { if (instance.m_citizens.m_buffer[citizen].CurrentLocation == Citizen.Location.Visit) { if (Singleton <SimulationManager> .instance.m_randomizer.Int32(10000u) < num3) { instance.ReleaseCitizen(citizen); num7++; num2++; } else { num6++; } } else { num6++; } } else if (instance.m_citizens.m_buffer[citizen].Sick && instance.m_citizens.m_buffer[citizen].CurrentLocation == Citizen.Location.Visit) { instance.m_citizens.m_buffer[citizen].Sick = false; } } } } num4 = nextUnit; if (++num5 > 524288) { CODebugBase <LogChannel> .Error(LogChannel.Core, "Invalid list detected!\n" + Environment.StackTrace); break; } } behaviour.m_deadCount += num6; if (m_graveCount == 0) { for (int j = num7; j < num2; j++) { if (Singleton <SimulationManager> .instance.m_randomizer.Int32(10000u) < num3) { num7++; num2--; } else { num6++; } } } buildingData.m_tempExport = (byte)Mathf.Min(buildingData.m_tempExport + num7, 255); DistrictManager instance2 = Singleton <DistrictManager> .instance; byte district = instance2.GetDistrict(buildingData.m_position); bool flag = IsFull(buildingID, ref buildingData); if (m_graveCount != 0) { num2 = Mathf.Min(num2, m_graveCount); buildingData.m_customBuffer1 = (ushort)num2; instance2.m_districts.m_buffer[district].m_productionData.m_tempDeadAmount += (uint)num2; instance2.m_districts.m_buffer[district].m_productionData.m_tempDeadCapacity += (uint)m_graveCount; } else { buildingData.m_customBuffer1 = (ushort)num2; instance2.m_districts.m_buffer[district].m_productionData.m_tempCremateCapacity += (uint)m_corpseCapacity; } bool flag2 = IsFull(buildingID, ref buildingData); if (flag != flag2) { if (flag2) { if ((object)m_fullPassMilestone != null) { m_fullPassMilestone.Unlock(); } } else if ((object)m_fullPassMilestone != null) { m_fullPassMilestone.Relock(); } } int count = 0; int count2 = 0; int cargo = 0; int capacity = 0; int outside = 0; CalculateOwnVehicles(buildingID, ref buildingData, TransferManager.TransferReason.Dead, ref count, ref cargo, ref capacity, ref outside); CalculateGuestVehicles(buildingID, ref buildingData, TransferManager.TransferReason.DeadMove, ref count2, ref cargo, ref capacity, ref outside); int num8 = m_corpseCapacity - num6 - capacity; int num9 = (finalProductionRate * m_hearseCount + 99) / 100; if ((buildingData.m_flags & Building.Flags.Downgrading) != 0) { if (m_graveCount != 0) { int count3 = 0; int cargo2 = 0; int capacity2 = 0; int outside2 = 0; CalculateOwnVehicles(buildingID, ref buildingData, TransferManager.TransferReason.DeadMove, ref count3, ref cargo2, ref capacity2, ref outside2); if (num2 > 0 && count3 < num9) { TransferManager.TransferOffer offer = default(TransferManager.TransferOffer); offer.Priority = 7; offer.Building = buildingID; offer.Position = buildingData.m_position; offer.Amount = 1; offer.Active = true; Singleton <TransferManager> .instance.AddOutgoingOffer(TransferManager.TransferReason.DeadMove, offer); } } } else if (m_graveCount != 0) { num8 = Mathf.Min(num8, m_graveCount - num2 - num6 - capacity + 9); int num10 = num8 / 10; if (count != 0) { num10--; } bool flag4 = num10 >= 1 && count < num9 && (num10 > 1); if (flag4) { TransferManager.TransferOffer offer3 = default(TransferManager.TransferOffer); offer3.Priority = 2 - count; offer3.Building = buildingID; offer3.Position = buildingData.m_position; offer3.Amount = 1; offer3.Active = true; Singleton <TransferManager> .instance.AddIncomingOffer(TransferManager.TransferReason.Dead, offer3); } if (buildingData.m_customBuffer1 > (m_corpseCapacity * 2 / 3)) { TransferManager.TransferOffer offer = default(TransferManager.TransferOffer); offer.Priority = 0; offer.Building = buildingID; offer.Position = buildingData.m_position; offer.Amount = 1; offer.Active = true; Singleton <TransferManager> .instance.AddOutgoingOffer(TransferManager.TransferReason.DeadMove, offer); } } else { int num11 = num8 / 10; bool flag5 = num11 >= 1 && (num11 > 1 || count >= num9 || Singleton <SimulationManager> .instance.m_randomizer.Int32(2u) == 0); bool flag6 = num11 >= 1 && count < num9 && (num11 > 1 || !flag5); if (flag5) { TransferManager.TransferOffer offer4 = default(TransferManager.TransferOffer); offer4.Priority = Mathf.Max(1, num8 * 6 / m_corpseCapacity); offer4.Building = buildingID; offer4.Position = buildingData.m_position; offer4.Amount = Mathf.Max(1, num11 - 1); offer4.Active = false; Singleton <TransferManager> .instance.AddIncomingOffer(TransferManager.TransferReason.DeadMove, offer4); } if (flag6) { TransferManager.TransferOffer offer5 = default(TransferManager.TransferOffer); offer5.Priority = 2 - count; offer5.Building = buildingID; offer5.Position = buildingData.m_position; offer5.Amount = 1; offer5.Active = true; Singleton <TransferManager> .instance.AddIncomingOffer(TransferManager.TransferReason.Dead, offer5); } } } }
public void CustomSimulationStep(ushort instanceId, ref CitizenInstance instanceData, Vector3 physicsLodRefPos) { #if DEBUG bool citizenDebug = (DebugSettings.CitizenInstanceId == 0 || DebugSettings.CitizenInstanceId == instanceId) && (DebugSettings.CitizenId == 0 || DebugSettings.CitizenId == instanceData.m_citizen) && (DebugSettings.SourceBuildingId == 0 || DebugSettings.SourceBuildingId == instanceData.m_sourceBuilding) && (DebugSettings.TargetBuildingId == 0 || DebugSettings.TargetBuildingId == instanceData.m_targetBuilding); bool logParkingAi = DebugSwitch.BasicParkingAILog.Get() && citizenDebug; #else var logParkingAi = false; #endif CitizenManager citizenManager = Singleton <CitizenManager> .instance; uint citizenId = instanceData.m_citizen; if ((instanceData.m_flags & (CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating)) != CitizenInstance.Flags.None && (instanceData.m_flags & CitizenInstance.Flags.Character) == CitizenInstance.Flags.None) { citizenManager.ReleaseCitizenInstance(instanceId); if (citizenId != 0u) { citizenManager.ReleaseCitizen(citizenId); } return; } Citizen[] citizensBuffer = citizenManager.m_citizens.m_buffer; if ((instanceData.m_flags & CitizenInstance.Flags.WaitingPath) != CitizenInstance.Flags.None) { PathManager pathManager = Singleton <PathManager> .instance; byte pathFindFlags = pathManager.m_pathUnits.m_buffer[instanceData.m_path].m_pathFindFlags; // NON-STOCK CODE START ExtPathState mainPathState = ExtPathState.Calculating; if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || instanceData.m_path == 0) { mainPathState = ExtPathState.Failed; } else if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { mainPathState = ExtPathState.Ready; } if (logParkingAi) { Log._Debug( $"CustomHumanAI.CustomSimulationStep({instanceId}): " + $"Path: {instanceData.m_path}, mainPathState={mainPathState}"); } ExtSoftPathState finalPathState; using (var bm = Benchmark.MaybeCreateBenchmark( null, "ConvertPathStateToSoftPathState+UpdateCitizenPathState")) { finalPathState = ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState); if (Options.parkingAI) { finalPathState = AdvancedParkingManager.Instance.UpdateCitizenPathState( instanceId, ref instanceData, ref ExtCitizenInstanceManager .Instance.ExtInstances[ instanceId], ref ExtCitizenManager .Instance.ExtCitizens[ citizenId], ref citizensBuffer[ instanceData.m_citizen], mainPathState); if (logParkingAi) { Log._Debug( $"CustomHumanAI.CustomSimulationStep({instanceId}): " + $"Applied Parking AI logic. Path: {instanceData.m_path}, " + $"mainPathState={mainPathState}, finalPathState={finalPathState}, " + $"extCitizenInstance={ExtCitizenInstanceManager.Instance.ExtInstances[instanceId]}"); } } // if Options.parkingAi } switch (finalPathState) { case ExtSoftPathState.Ready: { if (logParkingAi) { Log._Debug( $"CustomHumanAI.CustomSimulationStep({instanceId}): Path-finding " + $"succeeded for citizen instance {instanceId} " + $"(finalPathState={finalPathState}). Path: {instanceData.m_path} " + "-- calling HumanAI.PathfindSuccess"); } if (citizenId == 0 || citizensBuffer[instanceData.m_citizen].m_vehicle == 0) { Spawn(instanceId, ref instanceData); } instanceData.m_pathPositionIndex = 255; instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath; instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering); // NON-STOCK CODE START (transferred from ResidentAI.PathfindSuccess) const Citizen.Flags CTZ_MASK = Citizen.Flags.Tourist | Citizen.Flags.MovingIn | Citizen.Flags.DummyTraffic; if (citizenId != 0 && (citizensBuffer[citizenId].m_flags & CTZ_MASK) == Citizen.Flags.MovingIn) { StatisticBase statisticBase = Singleton <StatisticsManager> .instance.Acquire <StatisticInt32>(StatisticType.MoveRate); statisticBase.Add(1); } // NON-STOCK CODE END PathfindSuccess(instanceId, ref instanceData); break; } case ExtSoftPathState.Ignore: { if (logParkingAi) { Log._Debug( $"CustomHumanAI.CustomSimulationStep({instanceId}): " + "Path-finding result shall be ignored for citizen instance " + $"{instanceId} (finalPathState={finalPathState}). " + $"Path: {instanceData.m_path} -- ignoring"); } return; } case ExtSoftPathState.Calculating: default: { if (logParkingAi) { Log._Debug( $"CustomHumanAI.CustomSimulationStep({instanceId}): " + $"Path-finding result undetermined for citizen instance {instanceId} " + $"(finalPathState={finalPathState}). " + $"Path: {instanceData.m_path} -- continue"); } break; } case ExtSoftPathState.FailedHard: { if (logParkingAi) { Log._Debug( $"CustomHumanAI.CustomSimulationStep({instanceId}): " + $"HARD path-finding failure for citizen instance {instanceId} " + $"(finalPathState={finalPathState}). Path: {instanceData.m_path} " + "-- calling HumanAI.PathfindFailure"); } instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath; instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering); Singleton <PathManager> .instance.ReleasePath(instanceData.m_path); instanceData.m_path = 0u; PathfindFailure(instanceId, ref instanceData); return; } case ExtSoftPathState.FailedSoft: { if (logParkingAi) { Log._Debug( $"CustomHumanAI.CustomSimulationStep({instanceId}): " + $"SOFT path-finding failure for citizen instance {instanceId} " + $"(finalPathState={finalPathState}). Path: {instanceData.m_path} " + "-- calling HumanAI.InvalidPath"); } // path mode has been updated, repeat path-finding instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath; instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering); InvalidPath(instanceId, ref instanceData); break; } } // NON-STOCK CODE END } // NON-STOCK CODE START using (var bm = Benchmark.MaybeCreateBenchmark(null, "ExtSimulationStep")) { if (Options.parkingAI) { if (ExtSimulationStep( instanceId, ref instanceData, ref ExtCitizenInstanceManager.Instance.ExtInstances[instanceId], physicsLodRefPos)) { return; } } } // NON-STOCK CODE END base.SimulationStep(instanceId, ref instanceData, physicsLodRefPos); VehicleManager vehicleManager = Singleton <VehicleManager> .instance; ushort vehicleId = 0; if (instanceData.m_citizen != 0u) { vehicleId = citizensBuffer[instanceData.m_citizen].m_vehicle; } if (vehicleId != 0) { Vehicle[] vehiclesBuffer = vehicleManager.m_vehicles.m_buffer; VehicleInfo vehicleInfo = vehiclesBuffer[vehicleId].Info; if (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Bicycle) { vehicleInfo.m_vehicleAI.SimulationStep( vehicleId, ref vehiclesBuffer[vehicleId], vehicleId, ref vehiclesBuffer[vehicleId], 0); vehicleId = 0; } } if (vehicleId != 0 || (instanceData.m_flags & (CitizenInstance.Flags.Character | CitizenInstance.Flags.WaitingPath | CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating)) != CitizenInstance.Flags.None) { return; } instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown); ArriveAtDestination(instanceId, ref instanceData, false); citizenManager.ReleaseCitizenInstance(instanceId); }
public static bool Prefix(ref bool __result, ref ResidentAI __instance, uint citizenID, ref Citizen data) { // Method result. bool removed = false; if ((citizenID % DataStore.lifeSpanMultiplier) == Threading.counter) { // Local reference. CitizenManager citizenManager = Singleton <CitizenManager> .instance; if (citizenID == 575960) { Logging.Message("foundTarget"); } int num = data.Age + 1; if (num <= 45) { if (num == 15 || num == 45) { FinishSchoolOrWorkRev(__instance, citizenID, ref data); } } else if (num == 90 || num >= ModSettings.retirementAge) { FinishSchoolOrWorkRev(__instance, citizenID, ref data); } else if ((data.m_flags & Citizen.Flags.Student) != Citizen.Flags.None && (num % 15 == 0)) // Workspeed multiplier? { FinishSchoolOrWorkRev(__instance, citizenID, ref data); } if ((data.m_flags & Citizen.Flags.Original) != Citizen.Flags.None) { if (citizenManager.m_tempOldestOriginalResident < num) { citizenManager.m_tempOldestOriginalResident = num; } if (num == 240) { Singleton <StatisticsManager> .instance.Acquire <StatisticInt32>(StatisticType.FullLifespans).Add(1); } } data.Age = num; // Checking for death and sickness chances. // Citizens who are currently moving or currently in a vehicle aren't affected. if (data.CurrentLocation != Citizen.Location.Moving && data.m_vehicle == 0) { // Local reference. SimulationManager simulationManager = Singleton <SimulationManager> .instance; bool died = false; if (ModSettings.VanillaCalcs) { // Using vanilla lifecycle calculations. int num2 = 240; int num3 = 255; int num4 = Mathf.Max(0, 145 - (100 - data.m_health) * 3); if (num4 != 0) { num2 += num4 / 3; num3 += num4; } if (num >= num2) { bool flag = simulationManager.m_randomizer.Int32(2000u) < 3; died = (simulationManager.m_randomizer.Int32(num2 * 100, num3 * 100) / 100 <= num || flag); } } else { // Using custom lifecycle calculations. // Game defines years as being age divided by 3.5. Hence, 35 age increments per decade. // Legacy mod behaviour worked on 25 increments per decade. // If older than the maximum index - lucky them, but keep going using that final index. int index = Math.Min((int)(num * ModSettings.decadeFactor), 10); // Calculate 90% - 110%; using 100,000 as 100% (for precision). int modifier = 100000 + ((150 * data.m_health) + (50 * data.m_wellbeing) - 10000); // Death chance is simply if a random number between 0 and the modifier calculated above is less than the survival probability calculation for that decade of life. // Also set maximum age of 400 (~114 years) to be consistent with the base game. died = (simulationManager.m_randomizer.Int32(0, modifier) < DataStore.survivalProbCalc[index]) || num > 400; // Check for sickness chance if they haven't died. if (!died && simulationManager.m_randomizer.Int32(0, modifier) < DataStore.sicknessProbCalc[index]) { // Make people sick, if they're unlucky. data.Sick = true; if (Logging.UseSicknessLog) { Logging.WriteToLog(Logging.SicknessLogName, "Citizen became sick with chance factor ", DataStore.sicknessProbCalc[index].ToString()); } } } // Handle citizen death. if (died) { // Check if citizen is only remaining parent and there are children. uint unitID = data.GetContainingUnit(citizenID, Singleton <BuildingManager> .instance.m_buildings.m_buffer[data.m_homeBuilding].m_citizenUnits, CitizenUnit.Flags.Home); CitizenUnit containingUnit = citizenManager.m_units.m_buffer[unitID]; // Log if we're doing that. if (Logging.UseDeathLog) { Logging.WriteToLog(Logging.DeathLogName, "Killed citzen ", citizenID.ToString(), " at age ", data.Age.ToString(), " (", ((int)(data.Age / 3.5)).ToString(), " years old) with family ", containingUnit.m_citizen0.ToString(), ", " + containingUnit.m_citizen1.ToString(), ", " + containingUnit.m_citizen2.ToString(), ", ", containingUnit.m_citizen3.ToString(), ", ", containingUnit.m_citizen4.ToString()); } // Reverse redirect to access private method Die(). DieRev(__instance, citizenID, ref data); // If there are no adults remaining in this CitizenUnit, remove the others, as orphan households end up in simulation purgatory. bool isParent = containingUnit.m_citizen0 == citizenID || containingUnit.m_citizen1 == citizenID; bool singleParent = isParent && (containingUnit.m_citizen0 == 0 || containingUnit.m_citizen1 == 0); bool hasChild = containingUnit.m_citizen2 != 0 || containingUnit.m_citizen3 != 0 || containingUnit.m_citizen4 != 0; if (singleParent && hasChild) { for (int i = 0; i < 2; ++i) { uint currentChild; switch (i) { case 0: currentChild = containingUnit.m_citizen2; break; case 1: currentChild = containingUnit.m_citizen3; break; default: currentChild = containingUnit.m_citizen4; break; } if (currentChild != 0) { if (Logging.UseDeathLog) { Logging.WriteToLog(Logging.DeathLogName, "Removed orphan ", currentChild.ToString()); citizenManager.ReleaseCitizen(currentChild); } } } } // Chance for 'vanishing corpse' (no need for deathcare). if (!AIUtils.KeepCorpse()) { citizenManager.ReleaseCitizen(citizenID); removed = true; } } } } // Original method return value. __result = removed; // Don't execute base method after this. return(false); }
public void CustomSimulationStep(ushort instanceID, ref CitizenInstance instanceData, Vector3 physicsLodRefPos) { #if DEBUG bool citDebug = GlobalConfig.Instance.Debug.CitizenId == 0 || GlobalConfig.Instance.Debug.CitizenId == instanceData.m_citizen; bool debug = GlobalConfig.Instance.Debug.Switches[2] && citDebug; bool fineDebug = GlobalConfig.Instance.Debug.Switches[4] && citDebug; #endif CitizenManager citizenManager = Singleton <CitizenManager> .instance; uint citizenId = instanceData.m_citizen; if ((instanceData.m_flags & (CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating)) != CitizenInstance.Flags.None && (instanceData.m_flags & CitizenInstance.Flags.Character) == CitizenInstance.Flags.None) { citizenManager.ReleaseCitizenInstance(instanceID); if (citizenId != 0u) { citizenManager.ReleaseCitizen(citizenId); } return; } if ((instanceData.m_flags & CitizenInstance.Flags.WaitingPath) != CitizenInstance.Flags.None) { PathManager pathManager = Singleton <PathManager> .instance; byte pathFindFlags = pathManager.m_pathUnits.m_buffer[instanceData.m_path].m_pathFindFlags; // NON-STOCK CODE START ExtPathState mainPathState = ExtPathState.Calculating; if ((pathFindFlags & PathUnit.FLAG_FAILED) != 0 || instanceData.m_path == 0) { mainPathState = ExtPathState.Failed; } else if ((pathFindFlags & PathUnit.FLAG_READY) != 0) { mainPathState = ExtPathState.Ready; } #if DEBUG if (debug) { Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): Path: {instanceData.m_path}, mainPathState={mainPathState}"); } #endif ExtSoftPathState finalPathState = ExtSoftPathState.None; #if BENCHMARK using (var bm = new Benchmark(null, "ConvertPathStateToSoftPathState+UpdateCitizenPathState")) { #endif finalPathState = ExtCitizenInstance.ConvertPathStateToSoftPathState(mainPathState); if (Options.prohibitPocketCars) { finalPathState = AdvancedParkingManager.Instance.UpdateCitizenPathState(instanceID, ref instanceData, ref ExtCitizenInstanceManager.Instance.ExtInstances[instanceID], ref ExtCitizenManager.Instance.ExtCitizens[citizenId], ref citizenManager.m_citizens.m_buffer[instanceData.m_citizen], mainPathState); #if DEBUG if (debug) { Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): Applied Parking AI logic. Path: {instanceData.m_path}, mainPathState={mainPathState}, finalPathState={finalPathState}, extCitizenInstance={ExtCitizenInstanceManager.Instance.ExtInstances[instanceID]}"); } #endif } #if BENCHMARK } #endif switch (finalPathState) { case ExtSoftPathState.Ready: #if DEBUG if (debug) { Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): Path-finding succeeded for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- calling HumanAI.PathfindSuccess"); } #endif if (citizenId == 0 || citizenManager.m_citizens.m_buffer[instanceData.m_citizen].m_vehicle == 0) { this.Spawn(instanceID, ref instanceData); } instanceData.m_pathPositionIndex = 255; instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath; instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering); // NON-STOCK CODE START (transferred from ResidentAI.PathfindSuccess) if (citizenId != 0 && (citizenManager.m_citizens.m_buffer[citizenId].m_flags & (Citizen.Flags.Tourist | Citizen.Flags.MovingIn | Citizen.Flags.DummyTraffic)) == Citizen.Flags.MovingIn) { StatisticBase statisticBase = Singleton <StatisticsManager> .instance.Acquire <StatisticInt32>(StatisticType.MoveRate); statisticBase.Add(1); } // NON-STOCK CODE END this.PathfindSuccess(instanceID, ref instanceData); break; case ExtSoftPathState.Ignore: #if DEBUG if (debug) { Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): Path-finding result shall be ignored for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- ignoring"); } #endif return; case ExtSoftPathState.Calculating: default: #if DEBUG if (debug) { Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): Path-finding result undetermined for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- continue"); } #endif break; case ExtSoftPathState.FailedHard: #if DEBUG if (debug) { Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): HARD path-finding failure for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- calling HumanAI.PathfindFailure"); } #endif instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath; instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering); Singleton <PathManager> .instance.ReleasePath(instanceData.m_path); instanceData.m_path = 0u; this.PathfindFailure(instanceID, ref instanceData); return; case ExtSoftPathState.FailedSoft: #if DEBUG if (debug) { Log._Debug($"CustomHumanAI.CustomSimulationStep({instanceID}): SOFT path-finding failure for citizen instance {instanceID} (finalPathState={finalPathState}). Path: {instanceData.m_path} -- calling HumanAI.InvalidPath"); } #endif // path mode has been updated, repeat path-finding instanceData.m_flags &= ~CitizenInstance.Flags.WaitingPath; instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown | CitizenInstance.Flags.Cheering); this.InvalidPath(instanceID, ref instanceData); return; } // NON-STOCK CODE END } // NON-STOCK CODE START #if BENCHMARK using (var bm = new Benchmark(null, "ExtSimulationStep")) { #endif if (Options.prohibitPocketCars) { if (ExtSimulationStep(instanceID, ref instanceData, ref ExtCitizenInstanceManager.Instance.ExtInstances[instanceID], physicsLodRefPos)) { return; } } #if BENCHMARK } #endif // NON-STOCK CODE END base.SimulationStep(instanceID, ref instanceData, physicsLodRefPos); VehicleManager vehicleManager = Singleton <VehicleManager> .instance; ushort vehicleId = 0; if (instanceData.m_citizen != 0u) { vehicleId = citizenManager.m_citizens.m_buffer[instanceData.m_citizen].m_vehicle; } if (vehicleId != 0) { VehicleInfo vehicleInfo = vehicleManager.m_vehicles.m_buffer[(int)vehicleId].Info; if (vehicleInfo.m_vehicleType == VehicleInfo.VehicleType.Bicycle) { vehicleInfo.m_vehicleAI.SimulationStep(vehicleId, ref vehicleManager.m_vehicles.m_buffer[(int)vehicleId], vehicleId, ref vehicleManager.m_vehicles.m_buffer[(int)vehicleId], 0); vehicleId = 0; } } if (vehicleId == 0 && (instanceData.m_flags & (CitizenInstance.Flags.Character | CitizenInstance.Flags.WaitingPath | CitizenInstance.Flags.Blown | CitizenInstance.Flags.Floating)) == CitizenInstance.Flags.None) { instanceData.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking | CitizenInstance.Flags.SittingDown); CustomArriveAtDestination(instanceID, ref instanceData, false); citizenManager.ReleaseCitizenInstance(instanceID); } }