コード例 #1
0
ファイル: City.cs プロジェクト: Ville1/CityBuilderV2
    public void Update(float delta_time)
    {
        DiagnosticsManager.Instance.Message(GetType(), "--- UPDATE STARTS ---");
        DiagnosticsManager.Instance.Start(GetType(), "total", "Total");
        DiagnosticsManager.Instance.Start(GetType(), "expeditions", "Expeditions");

        if (grace_time_remaining > 0.0f)
        {
            grace_time_remaining = Math.Max(grace_time_remaining - TimeManager.Instance.Seconds_To_Days(delta_time), 0.0f);
        }
        //TODO: Add stopwatch?
        //Expeditions
        foreach (Expedition expedition in Expeditions)
        {
            expedition.Update(delta_time);
        }
        DiagnosticsManager.Instance.End(GetType(), "expeditions");
        DiagnosticsManager.Instance.Start(GetType(), "buildings", "Buildings");

        //Buildings
        foreach (Building building in Buildings)
        {
            if (building is Residence)
            {
                (building as Residence).Update(delta_time);
            }
            else
            {
                building.Update(delta_time);
            }
        }
        foreach (Building building in removed_buildings)
        {
            Buildings.Remove(building);
        }
        removed_buildings.Clear();
        foreach (Building building in added_buildings)
        {
            Buildings.Add(building);
        }
        added_buildings.Clear();

        foreach (Expedition expedition in removed_expeditions)
        {
            Expeditions.Remove(expedition);
        }
        removed_expeditions.Clear();
        foreach (Expedition expedition in added_expeditions)
        {
            Expeditions.Add(expedition);
        }
        added_expeditions.Clear();

        DiagnosticsManager.Instance.End(GetType(), "buildings");
        DiagnosticsManager.Instance.Start(GetType(), "statistics", "Update statistics");

        //Update statistics
        foreach (Resource resource in Resource.All)
        {
            Resource_Totals[resource]        = 0.0f;
            Usable_Resource_Totals[resource] = 0.0f;
            Resource_Max_Storage[resource]   = 0.0f;
            Resource_Delta[resource]         = 0.0f;
        }
        Cash_Delta    = 0.0f;
        Food_Current  = 0.0f;
        Food_Max      = 0.0f;
        Food_Produced = 0.0f;
        Food_Consumed = 0.0f;
        Food_Delta    = 0.0f;

        Dictionary <Building.Resident, int>   current_population = new Dictionary <Building.Resident, int>();
        Dictionary <Building.Resident, int>   max_population     = new Dictionary <Building.Resident, int>();
        Dictionary <Building.Resident, float> happiness          = new Dictionary <Building.Resident, float>();
        Dictionary <Building.Resident, int>   workers_required   = new Dictionary <Building.Resident, int>();
        Dictionary <Building.Resident, int>   available_workers  = new Dictionary <Building.Resident, int>();
        Dictionary <Building.Resident, float> total_education    = new Dictionary <Building.Resident, float>();

        foreach (Building.Resident resident in Enum.GetValues(typeof(Building.Resident)))
        {
            current_population.Add(resident, 0);
            max_population.Add(resident, 0);
            happiness.Add(resident, 0.0f);
            workers_required.Add(resident, 0);
            available_workers.Add(resident, 0);
            total_education.Add(resident, 0.0f);
        }
        foreach (Building building in Buildings)
        {
            foreach (KeyValuePair <Resource, float> pair in building.Storage)
            {
                Resource_Totals[pair.Key]        += pair.Value;
                Usable_Resource_Totals[pair.Key] += pair.Value;
                if (pair.Key.Is_Food)
                {
                    Food_Current += pair.Value;
                }
            }
            foreach (KeyValuePair <Resource, float> pair in building.Input_Storage)
            {
                Resource_Totals[pair.Key] += pair.Value;
                if (pair.Key.Is_Food)
                {
                    Food_Current += pair.Value;
                }
            }
            foreach (KeyValuePair <Resource, float> pair in building.Output_Storage)
            {
                Resource_Totals[pair.Key] += pair.Value;
                if (pair.Key.Is_Food)
                {
                    Food_Current += pair.Value;
                }
            }
            foreach (KeyValuePair <Resource, float> pair in building.Per_Day_Resource_Delta)
            {
                Resource_Delta[pair.Key] += pair.Value;
            }
            foreach (KeyValuePair <Resource, float> pair in building.Total_Max_Storage)
            {
                Resource_Max_Storage[pair.Key] += pair.Value;
                if (pair.Key.Is_Food)
                {
                    Food_Max += pair.Value;
                }
            }
            Cash_Delta    += building.Per_Day_Cash_Delta;
            Food_Produced += building.Food_Production_Per_Day;
            Food_Delta    += building.Food_Production_Per_Day;
            if (building is Residence)
            {
                Food_Consumed += (building as Residence).Food_Consumed;
                Food_Delta    -= (building as Residence).Food_Consumed;
            }
            if (building.Is_Complete)
            {
                if (building is Residence && building.Is_Operational)
                {
                    Residence residence = building as Residence;
                    foreach (Building.Resident resident in Enum.GetValues(typeof(Building.Resident)))
                    {
                        current_population[resident] += residence.Current_Residents[resident];
                        available_workers[resident]  += residence.Available_Work_Force[resident];
                        max_population[resident]     += residence.Resident_Space[resident];
                        happiness[resident]          += residence.Happiness[resident] * residence.Current_Residents[resident];
                        total_education[resident]    += residence.Education(resident) * residence.Current_Residents[resident];
                    }
                }
                if (building.Requires_Workers && building.Is_Complete && (building.Is_Connected || !building.Requires_Connection) && (!building.Is_Paused || PAUSED_BUILDINGS_KEEP_WORKERS))
                {
                    foreach (Building.Resident resident in Enum.GetValues(typeof(Building.Resident)))
                    {
                        workers_required[resident] += building.Worker_Settings[resident];
                    }
                }
            }
        }
        DiagnosticsManager.Instance.End(GetType(), "statistics");
        DiagnosticsManager.Instance.Start(GetType(), "workers", "Allocate workers");

        //Allocate workers
        Dictionary <Building.Resident, float> worker_ratios     = new Dictionary <Building.Resident, float>();
        Dictionary <Building.Resident, int>   workers_allocated = new Dictionary <Building.Resident, int>();

        foreach (Building.Resident resident in Enum.GetValues(typeof(Building.Resident)))
        {
            worker_ratios.Add(resident, workers_required[resident] == 0 ? 0.0f : current_population[resident] / (float)workers_required[resident]);
            workers_allocated.Add(resident, 0);
        }
        foreach (Building building in Buildings)
        {
            if (!building.Requires_Workers)
            {
                continue;
            }
            foreach (Building.Resident resident in Enum.GetValues(typeof(Building.Resident)))
            {
                building.Current_Workers[resident] = 0;
            }
            if (!(building.Is_Complete && (building.Is_Connected || !building.Requires_Connection) && (!building.Is_Paused || PAUSED_BUILDINGS_KEEP_WORKERS)))
            {
                continue;
            }
            if (building.Is_Complete && (building.Is_Connected || !building.Requires_Connection) && (!building.Is_Paused || PAUSED_BUILDINGS_KEEP_WORKERS))
            {
                foreach (Building.Resident resident in Enum.GetValues(typeof(Building.Resident)))
                {
                    int workers = Mathf.RoundToInt(building.Worker_Settings[resident] * Mathf.Clamp(worker_ratios[resident], 0.0f, 1.0f));
                    if (workers > available_workers[resident])
                    {
                        workers = available_workers[resident];
                    }
                    building.Current_Workers[resident] = workers;
                    available_workers[resident]       -= workers;
                    workers_allocated[resident]       += workers;
                    if (available_workers[resident] < 0)
                    {
                        CustomLogger.Instance.Error("More workers allocated, than there is available workers");
                    }
                }
            }
        }

        foreach (Building building in Buildings)
        {
            if (!building.Requires_Workers || !(building.Is_Complete && (building.Is_Connected || !building.Requires_Connection) && (!building.Is_Paused || PAUSED_BUILDINGS_KEEP_WORKERS)))
            {
                continue;
            }
            foreach (Building.Resident resident in Enum.GetValues(typeof(Building.Resident)))
            {
                if (available_workers[resident] > 0 && worker_ratios[resident] < 1.0f && building.Current_Workers[resident] < building.Worker_Settings[resident])
                {
                    building.Current_Workers[resident]++;
                    available_workers[resident]--;
                    workers_allocated[resident]++;
                }
            }
        }

        foreach (Building.Resident resident in Enum.GetValues(typeof(Building.Resident)))
        {
            if (available_workers[resident] > 0 && workers_allocated[resident] < workers_required[resident])
            {
                CustomLogger.Instance.Error("Worker allocation mismatch, not enough allocated");
            }
            if (workers_allocated[resident] > workers_required[resident])
            {
                CustomLogger.Instance.Error("Worker allocation mismatch, too many allocated");
            }
            if (workers_allocated[resident] > current_population[resident])
            {
                CustomLogger.Instance.Error("Worker allocation mismatch, allocated > population");
            }
        }
        DiagnosticsManager.Instance.End(GetType(), "workers");
        DiagnosticsManager.Instance.Start(GetType(), "walkers", "Spawn walkers");

        //Spawn walkers
        int max = Spawn_Walkers ? Mathf.Min(Mathf.RoundToInt((current_population[Building.Resident.Peasant] + current_population[Building.Resident.Citizen] + current_population[Building.Resident.Noble]) / 30.0f) + 1, 10) : 0;

        if (Walkers.Count < max)
        {
            List <Building> possible_buildings = Buildings.Where(x => x.Is_Connected && x.Requires_Connection && x.Is_Operational && !x.Is_Road && x.Entities_Spawned.Count == 0).ToList();
            if (possible_buildings.Count > 25)  //TODO
            //TODO: Implement faster pathfinding A* ?
            {
                if (Buildings.Count <= 1)
                {
                    //Use pathfinding for paths
                    Building spawner = RNG.Instance.Item(possible_buildings);
                    possible_buildings.Remove(spawner);
                    if (possible_buildings.Count >= 1)
                    {
                        Building road = null;
                        foreach (Building b in Map.Instance.Get_Buildings_Around(spawner))
                        {
                            if (b.Is_Road && b.Is_Built && b.Is_Connected)
                            {
                                road = b;
                                break;
                            }
                        }
                        if (road != null)
                        {
                            Building target_building = RNG.Instance.Item(possible_buildings);//TODO: allow x.Entities_Spawned.Count != 0 in this list
                            Building target_road     = null;
                            foreach (Building b in Map.Instance.Get_Buildings_Around(target_building))
                            {
                                if (b.Is_Road && b.Is_Built && b.Is_Connected)
                                {
                                    target_road = b;
                                    break;
                                }
                            }
                            if (target_road != null)
                            {
                                List <PathfindingNode> path = Pathfinding.Path(Map.Instance.Road_Pathing, road.Tile.PathfindingNode, target_road.Tile.Road_PathfindingNode, false);
                                if (path.Count > 2)
                                {
                                    Entity walker = new Entity(EntityPrototypes.Instance.Get("walker"), road.Tile, spawner);
                                    walker.Set_Path(path, 0.5f);
                                    Walkers.Add(walker);
                                }
                            }
                        }
                    }
                }
                else
                {
                    //Use connected buildings for paths
                    Tile     spawn              = null;
                    Building spawner            = null;
                    List <PathfindingNode> path = Entity.Find_Walker_City_Path(out spawn, out spawner);
                    if (path != null)
                    {
                        Entity walker = new Entity(EntityPrototypes.Instance.Get("walker"), spawn, spawner);
                        walker.Set_Path(path, 0.5f);
                        Walkers.Add(walker);
                    }
                }
            }
        }
        DiagnosticsManager.Instance.End(GetType(), "walkers");
        DiagnosticsManager.Instance.Start(GetType(), "ships", "Spawn ships");

        //Spawn ships
        if (Map.Instance.Ship_Spawns.Count > 0)
        {
            ship_spawn_cooldown -= delta_time * TimeManager.Instance.Multiplier;
            if (ship_spawn_cooldown <= 0.0f)
            {
                ship_spawn_cooldown += 120.0f * (200.0f / (100.0f + ((0.5f * current_population[Building.Resident.Peasant]) + (1.5f * current_population[Building.Resident.Citizen]) + current_population[Building.Resident.Noble]))) *
                                       (RNG.Instance.Next(50, 150) * 0.01f);
                foreach (Building building in Buildings)
                {
                    if (!building.Data.ContainsKey(Building.DOCK_ID_KEY))
                    {
                        continue;
                    }
                    Building dock = Buildings.FirstOrDefault(x => x.Id == long.Parse(building.Data[Building.DOCK_ID_KEY]));
                    if (dock == null || ships.ContainsKey(dock) || building.Tags.Contains(Building.Tag.Creates_Expeditions) || !building.Is_Operational)
                    {
                        continue;
                    }
                    Tile spawn  = RNG.Instance.Item(Map.Instance.Ship_Spawns);
                    Tile target = null;
                    foreach (Tile t in Map.Instance.Get_Adjanced_Tiles(dock.Tile).Select(x => x.Value).ToArray())
                    {
                        if (t.Building == null && t.Has_Ship_Access)
                        {
                            target = t;
                            break;
                        }
                    }
                    if (target == null)
                    {
                        break;
                    }
                    List <PathfindingNode> path = Pathfinding.Path(Map.Instance.Ship_Pathing, spawn.Ship_PathfindingNode, target.Ship_PathfindingNode, false);
                    if (path.Count > 2)
                    {
                        List <PathfindingNode> return_path = Pathfinding.Path(Map.Instance.Ship_Pathing, target.Ship_PathfindingNode, spawn.Ship_PathfindingNode, false);
                        path.AddRange(return_path.Where(x => !x.Coordinates.Equals(target.Coordinates)).ToList());
                        Entity ship = new Entity(EntityPrototypes.Instance.Get("ship"), spawn, dock);
                        ship.Set_Path(path, 0.75f);
                        ship.Add_Order(new Entity.PathOrder(target.Ship_PathfindingNode, 20.0f));
                        ships.Add(dock, ship);
                    }
                }
            }
        }
        DiagnosticsManager.Instance.End(GetType(), "ships");
        DiagnosticsManager.Instance.Start(GetType(), "gui", "Update GUI");

        //GUI
        int   peasant_current   = current_population[Building.Resident.Peasant];
        int   peasant_max       = max_population[Building.Resident.Peasant];
        float peasant_happiness = peasant_current > 0 ? happiness[Building.Resident.Peasant] / peasant_current : 0.0f;

        Happiness[Building.Resident.Peasant] = peasant_happiness;
        int   peasant_employment          = workers_allocated[Building.Resident.Peasant] - workers_required[Building.Resident.Peasant] + available_workers[Building.Resident.Peasant];
        float peasant_employment_relative = peasant_current == 0 ? 0.0f : peasant_employment / (float)peasant_current;

        Unemployment[Building.Resident.Peasant] = peasant_employment_relative > 0.0f ? peasant_employment_relative : 0.0f;
        Education[Building.Resident.Peasant]    = peasant_current == 0 ? 0.0f : total_education[Building.Resident.Peasant] / peasant_current;

        int   citizen_current   = current_population[Building.Resident.Citizen];
        int   citizen_max       = max_population[Building.Resident.Citizen];
        float citizen_happiness = citizen_current > 0 ? happiness[Building.Resident.Citizen] / citizen_current : 0.0f;

        Happiness[Building.Resident.Citizen] = citizen_happiness;
        int   citizen_employment          = workers_allocated[Building.Resident.Citizen] - workers_required[Building.Resident.Citizen] + available_workers[Building.Resident.Citizen];
        float citizen_employment_relative = citizen_current == 0 ? 0.0f : citizen_employment / (float)citizen_current;

        Unemployment[Building.Resident.Citizen] = citizen_employment_relative > 0.0f ? citizen_employment_relative : 0.0f;
        Education[Building.Resident.Citizen]    = citizen_current == 0 ? 0.0f : total_education[Building.Resident.Citizen] / citizen_current;

        int   noble_current   = current_population[Building.Resident.Noble];
        int   noble_max       = max_population[Building.Resident.Noble];
        float noble_happiness = noble_current > 0 ? happiness[Building.Resident.Noble] / noble_current : 0.0f;

        Happiness[Building.Resident.Noble] = noble_happiness;
        int   noble_employment          = workers_allocated[Building.Resident.Noble] - workers_required[Building.Resident.Noble] + available_workers[Building.Resident.Noble];
        float noble_employment_relative = noble_current == 0 ? 0.0f : noble_employment / (float)noble_current;

        Unemployment[Building.Resident.Noble] = noble_employment_relative > 0.0f ? noble_employment_relative : 0.0f;
        Education[Building.Resident.Noble]    = noble_current == 0 ? 0.0f : total_education[Building.Resident.Noble] / noble_current;
        noble_count = noble_current;

        TopGUIManager.Instance.Update_City_Info(Name, Cash, Cash_Delta, Mathf.RoundToInt(Usable_Resource_Totals[Resource.Wood]), Mathf.RoundToInt(Usable_Resource_Totals[Resource.Lumber]), Mathf.RoundToInt(Usable_Resource_Totals[Resource.Stone]),
                                                Mathf.RoundToInt(Usable_Resource_Totals[Resource.Bricks]), Mathf.RoundToInt(Usable_Resource_Totals[Resource.Tools]), Mathf.RoundToInt(Usable_Resource_Totals[Resource.Marble]), Mathf.RoundToInt(Usable_Resource_Totals[Resource.Mechanisms]),
                                                Mathf.RoundToInt(Usable_Resource_Totals[Resource.Glass]), peasant_current, peasant_max, peasant_happiness, peasant_employment_relative, peasant_employment, citizen_current, citizen_max, citizen_happiness,
                                                citizen_employment_relative, citizen_employment, noble_current, noble_max, noble_happiness, noble_employment_relative, noble_employment);

        DiagnosticsManager.Instance.End(GetType(), "gui");
        DiagnosticsManager.Instance.End(GetType(), "total");
    }