예제 #1
0
    public Map(int width, int height)
    {
        Width  = width;
        Height = height;
        rng    = new System.Random();
        tiles  = new List <List <Tile> >();

        //Generate
        Dictionary <int, Tile> seed = new Dictionary <int, Tile>();

        seed.Add(1000, TilePrototypes.Get("grass"));
        seed.Add(1010, TilePrototypes.Get("forest"));
        seed.Add(1060, TilePrototypes.Get("sparse_forest"));
        seed.Add(1080, TilePrototypes.Get("fertile_ground"));
        int max = 1080;

        for (int x = 0; x < width; x++)
        {
            tiles.Add(new List <Tile>());
            for (int y = 0; y < height; y++)
            {
                int  random = rng.Next(max);
                Tile tile   = null;
                foreach (KeyValuePair <int, Tile> seed_pair in seed)
                {
                    if (seed_pair.Key >= random)
                    {
                        tile = seed_pair.Value;
                        break;
                    }
                }
                tiles[x].Add(new Tile(this, x, y, tile));
            }
        }

        //Update appeal
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                tiles[x][y].Update_Appeal(0.0f, 0.0f);
            }
        }

        //Spawn hills
        int hill_change = 10; //Promiles

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                if (rng.Next(1000) < hill_change)
                {
                    //Get squares under new hill
                    List <Tile> hill_tiles   = new List <Tile>();
                    bool        invalid_tile = false;
                    for (int delta_x = 0; delta_x < 3 && !invalid_tile; delta_x++)
                    {
                        for (int delta_y = 0; delta_y < 3 && !invalid_tile; delta_y++)
                        {
                            Tile t = Get_Tile_At(x + delta_x, y + delta_y);
                            if (t == null || t.Terrain == "Hill")
                            {
                                invalid_tile = true;
                            }
                            hill_tiles.Add(t);
                        }
                    }
                    if (invalid_tile)
                    {
                        continue;
                    }
                    for (int i = 0; i < hill_tiles.Count; i++)
                    {
                        hill_tiles[i].Change_To(TilePrototypes.Get("hill"));
                        if (i != 2)
                        {
                            hill_tiles[i].No_Rendering = true;
                        }
                    }
                }
            }
        }

        //Spread terrain
        Dictionary <string, Dictionary <Tile, float[]> > spread = new Dictionary <string, Dictionary <Tile, float[]> >();

        //Forest
        spread.Add("Forest", new Dictionary <Tile, float[]>());
        spread["Forest"].Add(TilePrototypes.Get("sparse_forest"), new float[2] {
            50.0f, 2.0f
        });                                                                                     //Change, range
        spread["Forest"].Add(TilePrototypes.Get("forest"), new float[2] {
            25.0f, 2.0f
        });
        spread.Add("Sparse Forest", new Dictionary <Tile, float[]>());
        spread["Sparse Forest"].Add(TilePrototypes.Get("sparse_forest"), new float[2] {
            1.0f, 5.0f
        });
        spread.Add("Hill", new Dictionary <Tile, float[]>());
        spread["Hill"].Add(TilePrototypes.Get("sparse_forest"), new float[2] {
            5.0f, 4.0f
        });
        spread["Hill"].Add(TilePrototypes.Get("forest"), new float[2] {
            3.0f, 2.0f
        });
        spread.Add("Fertile Ground", new Dictionary <Tile, float[]>());
        spread["Fertile Ground"].Add(TilePrototypes.Get("sparse_forest"), new float[2] {
            20.0f, 1.0f
        });
        spread["Fertile Ground"].Add(TilePrototypes.Get("fertile_ground"), new float[2] {
            1.0f, 2.0f
        });


        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                if (spread.ContainsKey(tiles[x][y].Terrain))
                {
                    foreach (KeyValuePair <Tile, float[]> spread_data in spread[tiles[x][y].Terrain])
                    {
                        List <Tile> affected_tiles = Get_Tiles_In_Circle(x, y, spread_data.Value[1]);
                        foreach (Tile tile in affected_tiles)
                        {
                            if (tile.Terrain != "Hill" && rng.Next(100) < spread_data.Value[0] / (tile.Distance(tiles[x][y])))
                            {
                                tile.Change_To(spread_data.Key);
                            }
                        }
                    }
                }
            }
        }

        //Spawn ore
        Dictionary <int, string> ore_seed = new Dictionary <int, string>();

        ore_seed.Add(1000, "iron");
        ore_seed.Add(2200, "coal");
        ore_seed.Add(3250, "salt");
        max = 3250;
        int base_ore_spawn_change = 3; //Promiles

        Dictionary <string, int[]> ore_data = new Dictionary <string, int[]>();

        ore_data.Add("iron", new int[2] {
            25, 200
        });                                          // / 100
        ore_data.Add("coal", new int[2] {
            25, 200
        });
        ore_data.Add("salt", new int[2] {
            25, 200
        });

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                int random           = rng.Next(1000);
                int ore_spawn_change = base_ore_spawn_change;
                if (tiles[x][y].Terrain == "Hill")
                {
                    ore_spawn_change *= 10;
                }
                if (random < ore_spawn_change)
                {
                    //Spawn vein
                    random = rng.Next(max);
                    string ore_name   = "???";
                    float  ore_amount = -1.0f;
                    foreach (KeyValuePair <int, string> seed_pair in ore_seed)
                    {
                        if (seed_pair.Key >= random)
                        {
                            ore_name   = seed_pair.Value;
                            ore_amount = (rng.Next(ore_data[ore_name][1] - ore_data[ore_name][0]) + ore_data[ore_name][0]) / 100.0f;
                            break;
                        }
                    }
                    tiles[x][y].Ore.Add(ore_name, ore_amount);
                }
            }
        }

        //Spread ore
        Dictionary <string, float[]> ore_spread = new Dictionary <string, float[]>();

        ore_spread.Add("iron", new float[2] {
            15.0f, 2.0f
        });
        ore_spread.Add("coal", new float[2] {
            8.0f, 2.5f
        });
        ore_spread.Add("salt", new float[2] {
            19.0f, 1.5f
        });

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                foreach (KeyValuePair <string, float> ore in tiles[x][y].Ore)
                {
                    if (ore_spread.ContainsKey(ore.Key))
                    {
                        List <Tile> affected_tiles = Get_Tiles_In_Circle(x, y, ore_spread[ore.Key][1]);
                        foreach (Tile tile in affected_tiles)
                        {
                            if (!tile.Ore.ContainsKey(ore.Key))
                            {
                                int random            = rng.Next(100);
                                int ore_spread_change = Mathf.RoundToInt(ore_spread[ore.Key][0] / (tile.Distance(tiles[x][y])));
                                if (tile.Terrain == "Hill")
                                {
                                    ore_spread_change *= 3;
                                }
                                if (random < ore_spread_change)
                                {
                                    tile.Ore.Add(ore.Key, (rng.Next(ore_data[ore.Key][1] - ore_data[ore.Key][0]) + ore_data[ore.Key][0]) / 100.0f);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    /// <summary>
    /// Creates building prototypes
    /// </summary>
    private static void Initialize()
    {
        prototypes = new Dictionary <string, Building>();

        //Town hall
        Dictionary <string, float[]> storage = new Dictionary <string, float[]>();

        storage.Add("wood", new float[2] {
            750.0f, 2500.0f
        });
        storage.Add("lumber", new float[2] {
            0.0f, 2500.0f
        });
        storage.Add("stone", new float[2] {
            750.0f, 2500.0f
        });
        storage.Add("tools", new float[2] {
            750.0f, 2500.0f
        });
        Building town_hall = new Building("town_hall", 2, 2, 0.0f, 2500, 50, 5, storage, Building.UI_Category.ADMIN);

        town_hall.Attribute("build_speed", 5.0f);
        town_hall.Attribute("build_range", 15.0f);
        town_hall.Attribute("storehouse", 1.0f);
        town_hall.Attribute("highlight_tiles_during_build", 15.0f);

        /*town_hall.Toggle_Select =
         * (Building building) => {
         *  //Highlight tiles
         *  foreach (Tile tile in building.Get_Tiles_In_Circle(15.0f)) {
         *      tile.Highlight = new Color(0.15f, 0.15f, 0.15f, 0.5f);
         *  }
         * };*/
        prototypes.Add("town_hall", town_hall);

        //Road
        Building road = new Building("road", 1, 1, 10.0f, 0, 0, 0, null, Building.UI_Category.INFRASTRUCTURE);

        road.Is_Road          = true;
        road.Texture_Connects = new List <string>();
        road.Texture_Connects.Add("road");
        road.Texture_Connects.Add("marketplace");
        road.Cost.Add("cash", 10.0f);
        road.Cost.Add("stone", 10.0f);
        road.Cost.Add("tools", 1.0f);
        prototypes.Add("road", road);

        //Chop forest
        storage = new Dictionary <string, float[]>();
        storage.Add("wood", new float[2] {
            0.0f, 1000.0f
        });
        Building chop = new Building("axe", 1, 1, 15.0f, 1000, 0, 0, storage, Building.UI_Category.FORESTRY);

        chop.Name = "Chop Forest";
        chop.Cost.Add("cash", 5.0f);
        chop.Cost.Add("tools", 1.0f);
        chop.Valid_Terrain.Add("Forest");
        chop.Valid_Terrain.Add("Sparse Forest");
        chop.Upkeep        = 0.0f;
        chop.Can_Be_Paused = false;
        chop.On_Build      =
            (Building building) => {
            if (building.Tile.Terrain == "Forest")
            {
                building.Add("wood", 10.0f);
            }
            else if (building.Tile.Terrain == "Sparse Forest")
            {
                building.Add("wood", 3.0f);
            }
            building.Tile.Change_To(TilePrototypes.Get("grass"));
            building.Demolish();
        };
        prototypes.Add("axe", chop);

        //Cabin
        Building hut = new Building("hut", 2, 2, 115.0f, 0, 0, 0, null, Building.UI_Category.HOUSING);

        hut.Name = "Cabin";
        hut.Cost.Add("cash", 100.0f);
        hut.Cost.Add("wood", 100.0f);
        hut.Cost.Add("stone", 15.0f);
        hut.Cost.Add("tools", 10.0f);
        hut.Population_Type = Building.Resident.PEASANT;
        hut.Population_Max  = 10;
        hut.Can_Be_Paused   = false;
        prototypes.Add("hut", hut);

        //Wood cutter
        storage = new Dictionary <string, float[]>();
        storage.Add("wood", new float[2] {
            0.0f, 100.0f
        });
        Building wood_cutter = new Building("wood_cutters_lodge", 2, 2, 85.0f, 100, 0, 0, storage, Building.UI_Category.FORESTRY);

        wood_cutter.Cost.Add("cash", 90);
        wood_cutter.Cost.Add("wood", 75);
        wood_cutter.Cost.Add("stone", 5);
        wood_cutter.Cost.Add("tools", 15);
        wood_cutter.Upkeep = 0.75f;
        wood_cutter.Workers.Add(Building.Resident.PEASANT, new int[] { 0, 5 });
        wood_cutter.Produces.Add("wood");
        wood_cutter.Attribute("highlight_tiles_during_build", 3.0f);
        wood_cutter.Action =
            (float delta_time, Building building) => {
            if (!building.Is_Built || building.Is_Paused)
            {
                return;
            }
            //Check tiles
            float wood = 0.0f;
            foreach (Tile tile in building.Harvest(3.0f))
            {
                if (tile.Terrain == "Forest")
                {
                    wood += 0.125f;
                }
                else if (tile.Terrain == "Sparse Forest")
                {
                    wood += 0.0375f;
                }
                else if (tile.Terrain == "Hill")
                {
                    wood += 0.00625f;
                }
            }
            wood *= delta_time;
            wood *= building.Get_Worker_Efficiency();

            building.Add("wood", wood);
            building.Set_Statistic("produced_wood", wood / delta_time);
        };
        wood_cutter.On_Demolish =
            (Building building) => {
            building.Release_Harvested_Tiles(3.0f);
        };
        wood_cutter.Toggle_Select =
            (Building building) => {
            //Highlight tiles
            foreach (Tile tile in building.Harvest(3.0f))
            {
                tile.Highlight = new Color(0.1f, 0.5f, 0.1f, 0.5f);
            }
        };
        prototypes.Add("wood_cutters_lodge", wood_cutter);

        //Lumber mill
        storage = new Dictionary <string, float[]>();
        storage.Add("wood", new float[2] {
            0.0f, 150.0f
        });
        storage.Add("lumber", new float[2] {
            0.0f, 150.0f
        });
        Building lumber_mill = new Building("lumber_mill", 3, 3, 250.0f, 300, 10, 5, storage, Building.UI_Category.INDUSTRY);

        lumber_mill.Cost.Add("cash", 200);
        lumber_mill.Cost.Add("wood", 225);
        lumber_mill.Cost.Add("stone", 20);
        lumber_mill.Cost.Add("tools", 40);
        lumber_mill.Upkeep = 2.0f;
        lumber_mill.Workers.Add(Building.Resident.PEASANT, new int[] { 0, 10 });
        lumber_mill.Changeable_Workers_PC.Add(0);
        lumber_mill.Changeable_Workers_PC.Add(10);
        lumber_mill.Changeable_Workers_PC.Add(0);
        lumber_mill.Consumes.Add("wood");
        lumber_mill.Produces.Add("lumber");
        lumber_mill.Appeal_Effect = -0.5f;
        lumber_mill.Appeal_Range  = 4.0f;
        lumber_mill.Action        =
            (float delta_time, Building building) => {
            if (!building.Is_Built || building.Is_Paused)
            {
                return;
            }
            building.Refine_Resources("wood", 2.0f, "lumber", 2.0f, delta_time);
        };
        prototypes.Add("lumber_mill", lumber_mill);

        //Quarry
        storage = new Dictionary <string, float[]>();
        storage.Add("stone", new float[2] {
            0.0f, 225.0f
        });
        Building quarry = new Building("quarry", 3, 3, 225.0f, 250, 0, 0, storage, Building.UI_Category.INDUSTRY);

        quarry.Cost.Add("cash", 175);
        quarry.Cost.Add("wood", 65);
        quarry.Cost.Add("lumber", 80);
        quarry.Cost.Add("tools", 45);
        quarry.Upkeep = 1.75f;
        quarry.Workers.Add(Building.Resident.PEASANT, new int[] { 0, 20 });
        quarry.Produces.Add("stone");
        quarry.Appeal_Effect = -0.75f;
        quarry.Appeal_Range  = 5.0f;
        quarry.Action        =
            (float delta_time, Building building) => {
            if (!building.Is_Built || building.Is_Paused)
            {
                return;
            }
            float stone = 2.5f * building.Get_Worker_Efficiency() * delta_time;
            building.Add("stone", stone);
            building.Set_Statistic("produced_stone", stone / delta_time);
        };
        prototypes.Add("quarry", quarry);

        //Hunter
        storage = new Dictionary <string, float[]>();
        storage.Add("game", new float[2] {
            0.0f, 100.0f
        });
        Building hunter = new Building("hunting_lodge", 2, 2, 95.0f, 100, 0, 0, storage, Building.UI_Category.FORESTRY);

        hunter.Cost.Add("cash", 110);
        hunter.Cost.Add("wood", 85);
        hunter.Cost.Add("stone", 10);
        hunter.Cost.Add("tools", 10);
        hunter.Upkeep = 0.75f;
        hunter.Workers.Add(Building.Resident.PEASANT, new int[] { 0, 5 });
        hunter.Produces.Add("game");
        hunter.Attribute("highlight_tiles_during_build", 5.0f);
        hunter.Action =
            (float delta_time, Building building) => {
            if (!building.Is_Built || building.Is_Paused)
            {
                return;
            }
            //Add game
            float game = 0.0f;
            foreach (Tile tile in building.Harvest(5.0f))
            {
                if (tile.Terrain == "Forest")
                {
                    game += 0.0675f;
                }
                else if (tile.Terrain == "Sparse Forest")
                {
                    game += 0.0550f;
                }
                else if (tile.Terrain == "Fertile Ground")
                {
                    game += 0.0250f;
                }
                else if (tile.Terrain == "Grass")
                {
                    game += 0.0110f;
                }
                else if (tile.Terrain == "Hill")
                {
                    game += 0.0335f;
                }
            }
            //Reduce game
            foreach (Tile tile in building.Get_Tiles_In_Circle(5.0f))
            {
                if (tile.Building != null && tile.Building.Id != building.Id)
                {
                    if (tile.Building.Is_Road)
                    {
                        game -= 0.005f;
                    }
                    else
                    {
                        game -= 0.05f;
                    }
                }
            }
            if (game < 0.0f)
            {
                game = 0.0f;
            }
            game *= delta_time;
            game *= building.Get_Worker_Efficiency();

            building.Add("game", game);
            building.Set_Statistic("produced_game", game / delta_time);
            building.Set_Statistic("food_produced", game / delta_time);
        };
        hunter.On_Demolish =
            (Building building) => {
            building.Release_Harvested_Tiles(5.0f);
        };
        hunter.Toggle_Select =
            (Building building) => {
            //Highlight tiles
            foreach (Tile tile in building.Harvest(5.0f))
            {
                tile.Highlight = new Color(0.1f, 0.5f, 0.1f, 0.5f);
            }
        };
        prototypes.Add("hunting_lodge", hunter);

        //Cellar
        storage = new Dictionary <string, float[]>();
        foreach (string food in City.Instance.Get_Food_Resource_Types())
        {
            storage.Add(food, new float[2] {
                0.0f, 1000.0f
            });
        }
        storage.Add("salt", new float[2] {
            0.0f, 1000.0f
        });
        storage.Add("alcohol", new float[2] {
            0.0f, 1000.0f
        });
        Building cellar = new Building("cellar", 1, 1, 110.0f, 1000, 15, 10, storage, Building.UI_Category.INFRASTRUCTURE);

        cellar.Cost.Add("cash", 100);
        cellar.Cost.Add("wood", 15);
        cellar.Cost.Add("lumber", 50);
        cellar.Cost.Add("stone", 50);
        cellar.Cost.Add("tools", 10);
        cellar.Upkeep = 0.5f;
        cellar.Attribute("storehouse", 1.0f);
        cellar.Workers.Add(Building.Resident.PEASANT, new int[] { 0, 5 });
        prototypes.Add("cellar", cellar);

        //Market
        storage = new Dictionary <string, float[]>();
        foreach (string food in City.Instance.Get_Food_Resource_Types())
        {
            storage.Add(food, new float[2] {
                0.0f, 10.0f
            });
        }
        storage.Add("salt", new float[2] {
            0.0f, 10.0f
        });
        Building market = new Building("marketplace", 3, 3, 110.0f, 100, 7, 0, storage, Building.UI_Category.SERVICES);

        market.Is_Road = true;
        market.Cost.Add("cash", 110);
        market.Cost.Add("lumber", 20);
        market.Cost.Add("stone", 90);
        market.Cost.Add("tools", 10);
        market.Attribute("limited_raw_material_warnings", 1.0f);
        market.Attribute("limited_storage_settings", 1.0f);
        market.Upkeep        = 1.0f;
        market.Appeal_Effect = 0.1f;
        market.Appeal_Range  = 5.0f;
        market.Changeable_Workers_PC.Add(0);
        market.Changeable_Workers_PC.Add(10);
        market.Changeable_Workers_PC.Add(0);
        foreach (string food in City.Instance.Get_Food_Resource_Types())
        {
            market.Consumes.Add(food);
        }
        market.Consumes.Add("salt");
        market.Action =
            (float delta_t, Building building) => {
            if (!building.Active)
            {
                return;
            }
            List <Building> residential_buildings = new List <Building>();
            //Search for residential buildings
            foreach (Building b in building.Get_Connected_Buildings(building.Range, -1))
            {
                if (b.Is_Residential && b.Is_Built)
                {
                    residential_buildings.Add(b);
                }
            }
            if (residential_buildings.Count == 0)
            {
                //No residences
                return;
            }
            //Stats
            float income = 0.0f;
            Dictionary <string, float> food_sold_stats = new Dictionary <string, float>();
            List <string> food_types = City.Instance.Get_Food_Resource_Types();
            foreach (string food_type in food_types)
            {
                food_sold_stats.Add(food_type, 0.0f);
                if (building.Statistics.ContainsKey(food_type + "_sold"))
                {
                    building.Statistics.Remove(food_type + "_sold");
                }
            }
            //Serve food
            foreach (string food_type in food_types)
            {
                if (building.Resource_Amount(food_type) != 0.0f)
                {
                    float food_per_residence = building.Resource_Amount(food_type) / residential_buildings.Count;
                    float food_served        = 0.0f;
                    foreach (Building residence in residential_buildings)
                    {
                        food_served += residence.Sell_Food(food_type, food_per_residence);
                    }
                    //Substract and stats
                    food_served = building.Take(food_type, food_served);
                    food_sold_stats[food_type] = food_served;
                    income += food_served * City.Instance.Get_Resource_Data(food_type)["price"];
                }
            }
            //Serve salt
            income += building.Sell_Resources_As_Service("salt", "salt", residential_buildings, delta_t, 1.0f, false);

            //Stats and income
            City.Instance.Cash += income;
            building.Set_Statistic("income", income / delta_t);
            foreach (KeyValuePair <string, float> stat in food_sold_stats)
            {
                if (stat.Value != 0.0f)
                {
                    building.Set_Statistic(stat.Key + "_sold", stat.Value / delta_t);
                }
            }
        };
        prototypes.Add("marketplace", market);

        //Storehouse
        storage = new Dictionary <string, float[]>();
        storage.Add("wood", new float[2] {
            0.0f, 2000.0f
        });
        storage.Add("lumber", new float[2] {
            0.0f, 2000.0f
        });
        storage.Add("stone", new float[2] {
            0.0f, 2000.0f
        });
        storage.Add("tools", new float[2] {
            0.0f, 2000.0f
        });
        storage.Add("coal", new float[2] {
            0.0f, 2000.0f
        });
        storage.Add("iron_ore", new float[2] {
            0.0f, 2000.0f
        });
        storage.Add("iron_bars", new float[2] {
            0.0f, 2000.0f
        });
        Building storehouse = new Building("storehouse", 2, 2, 250.0f, 2000, 10, 20, storage, Building.UI_Category.INFRASTRUCTURE);

        storehouse.Cost.Add("cash", 255.0f);
        storehouse.Cost.Add("lumber", 275.0f);
        storehouse.Cost.Add("stone", 30.0f);
        storehouse.Cost.Add("tools", 25.0f);
        storehouse.Upkeep = 1.0f;
        storehouse.Workers.Add(Building.Resident.PEASANT, new int[2] {
            0, 10
        });
        storehouse.Attribute("storehouse", 1.0f);
        prototypes.Add("storehouse", storehouse);

        //Gatherer's hut
        storage = new Dictionary <string, float[]>();
        storage.Add("berries", new float[2] {
            0.0f, 100.0f
        });
        storage.Add("mushrooms", new float[2] {
            0.0f, 100.0f
        });
        storage.Add("roots", new float[2] {
            0.0f, 100.0f
        });
        storage.Add("herbs", new float[2] {
            0.0f, 100.0f
        });
        Building gatherers_lodge = new Building("gatherers_lodge", 2, 2, 95.0f, 100, 0, 0, storage, Building.UI_Category.FORESTRY);

        gatherers_lodge.Cost.Add("cash", 100);
        gatherers_lodge.Cost.Add("wood", 85);
        gatherers_lodge.Cost.Add("stone", 10);
        gatherers_lodge.Cost.Add("tools", 10);
        gatherers_lodge.Upkeep = 0.75f;
        gatherers_lodge.Workers.Add(Building.Resident.PEASANT, new int[] { 0, 5 });
        gatherers_lodge.Produces.Add("berries");
        gatherers_lodge.Produces.Add("mushrooms");
        gatherers_lodge.Produces.Add("roots");
        gatherers_lodge.Produces.Add("herbs");
        gatherers_lodge.Attribute("highlight_tiles_during_build", 3.0f);
        gatherers_lodge.Action =
            (float delta_time, Building building) => {
            if (!building.Is_Built || building.Is_Paused)
            {
                return;
            }
            //Add food
            float berries   = 0.0f;
            float mushrooms = 0.0f;
            float roots     = 0.0f;
            float herbs     = 0.0f;
            foreach (Tile tile in building.Harvest(3.0f))
            {
                if (tile.Terrain == "Forest")
                {
                    berries   += 0.0085f;
                    mushrooms += 0.0075f;
                    roots     += 0.0045f;
                    herbs     += 0.0006f;
                }
                else if (tile.Terrain == "Sparse Forest")
                {
                    berries   += 0.0075f;
                    mushrooms += 0.0050f;
                    roots     += 0.0040f;
                    herbs     += 0.0005f;
                }
                else if (tile.Terrain == "Fertile Ground")
                {
                    berries   += 0.0025f;
                    mushrooms += 0.0005f;
                    roots     += 0.0050f;
                    herbs     += 0.0010f;
                }
                else if (tile.Terrain == "Grass")
                {
                    berries   += 0.0020f;
                    mushrooms += 0.0005f;
                    roots     += 0.0040f;
                    herbs     += 0.0004f;
                }
                else if (tile.Terrain == "Hill")
                {
                    berries   += 0.0020f;
                    mushrooms += 0.0010f;
                    roots     += 0.0045f;
                    herbs     += 0.0003f;
                }
            }

            float efficiency = building.Get_Worker_Efficiency();
            berries   = (berries * delta_time) * efficiency;
            mushrooms = (mushrooms * delta_time) * efficiency;
            roots     = (roots * delta_time) * efficiency;
            herbs     = (herbs * delta_time) * efficiency;

            building.Set_Statistic("food_produced", (berries + mushrooms + roots + herbs) / delta_time);
            building.Add("berries", berries);
            building.Set_Statistic("produced_berries", berries / delta_time);
            building.Add("mushrooms", mushrooms);
            building.Set_Statistic("produced_mushrooms", mushrooms / delta_time);
            building.Add("roots", roots);
            building.Set_Statistic("produced_roots", roots / delta_time);
            building.Add("herbs", herbs);
            building.Set_Statistic("produced_herbs", herbs / delta_time);
        };
        gatherers_lodge.On_Demolish =
            (Building building) => {
            building.Release_Harvested_Tiles(3.0f);
        };
        gatherers_lodge.Toggle_Select =
            (Building building) => {
            //Highlight tiles
            foreach (Tile tile in building.Harvest(3.0f))
            {
                tile.Highlight = new Color(0.1f, 0.5f, 0.1f, 0.5f);
            }
        };
        prototypes.Add("gatherers_lodge", gatherers_lodge);

        //Decorative tree
        Building decorative_tree = new Building("decorative_tree", 1, 1, 50.0f, 0, 0, 0, new Dictionary <string, float[]>(), Building.UI_Category.SERVICES);

        decorative_tree.Cost.Add("cash", 25.0f);
        decorative_tree.Cost.Add("stone", 5.0f);
        decorative_tree.Cost.Add("tools", 1.0f);
        decorative_tree.Appeal_Effect = 2.0f;
        decorative_tree.Appeal_Range  = 2.0f;
        decorative_tree.Can_Be_Paused = false;
        decorative_tree.Attribute("highlight_tiles_during_build", 2.0f);
        prototypes.Add("decorative_tree", decorative_tree);

        //Mine
        storage = new Dictionary <string, float[]>();
        storage.Add("iron_ore", new float[2] {
            0.0f, 100.0f
        });
        storage.Add("coal", new float[2] {
            0.0f, 100.0f
        });
        storage.Add("salt", new float[2] {
            0.0f, 100.0f
        });
        Building mine = new Building("mine", 2, 2, 250.0f, 100, 0, 0, storage, Building.UI_Category.INDUSTRY);

        mine.Cost.Add("cash", 250);
        mine.Cost.Add("wood", 90);
        mine.Cost.Add("lumber", 90);
        mine.Cost.Add("stone", 15);
        mine.Cost.Add("tools", 40);
        mine.Upkeep = 2.00f;
        mine.Workers.Add(Building.Resident.PEASANT, new int[] { 0, 15 });
        mine.Produces.Add("iron_ore");
        mine.Produces.Add("coal");
        mine.Produces.Add("salt");
        mine.Appeal_Effect = -1.25f;
        mine.Appeal_Range  = 5.0f;
        mine.Attribute("highlight_tiles_during_build", 5.0f);
        mine.Action =
            (float delta_time, Building building) => {
            if (!building.Active)
            {
                return;
            }
            float iron_ore = 0.0f;
            float coal     = 0.0f;
            float salt     = 0.0f;
            foreach (Tile tile in building.Harvest(3.0f, false, true))
            {
                if (tile.Ore.ContainsKey("iron"))
                {
                    iron_ore += tile.Ore["iron"];
                }
                if (tile.Ore.ContainsKey("coal"))
                {
                    coal += tile.Ore["coal"];
                }
                if (tile.Ore.ContainsKey("salt"))
                {
                    salt += tile.Ore["salt"];
                }
            }

            float efficiency = building.Get_Worker_Efficiency();
            iron_ore = (iron_ore * delta_time) * efficiency;
            coal     = (coal * delta_time) * efficiency;
            salt     = (salt * delta_time) * efficiency;

            building.Add("iron_ore", iron_ore);
            building.Set_Statistic("produced_iron_ore", iron_ore / delta_time);
            building.Add("coal", coal);
            building.Set_Statistic("produced_coal", coal / delta_time);
            building.Add("salt", salt);
            building.Set_Statistic("produced_salt", salt / delta_time);
        };
        mine.On_Demolish =
            (Building building) => {
            building.Release_Harvested_Tiles(5.0f, true);
        };
        mine.Toggle_Select =
            (Building building) => {
            //Highlight tiles
            foreach (Tile tile in building.Harvest(5.0f, true, true))
            {
                tile.Highlight = new Color(0.1f, 0.5f, 0.1f, 0.5f);
            }
        };
        prototypes.Add("mine", mine);

        //Charcoal burner
        storage = new Dictionary <string, float[]>();
        storage.Add("wood", new float[2] {
            0.0f, 50.0f
        });
        storage.Add("coal", new float[2] {
            0.0f, 50.0f
        });
        Building charcoal_burner = new Building("charcoal_burner", 2, 2, 50.0f, 100, 5, 2, storage, Building.UI_Category.FORESTRY);

        charcoal_burner.Cost.Add("cash", 45);
        charcoal_burner.Cost.Add("lumber", 50);
        charcoal_burner.Cost.Add("stone", 5);
        charcoal_burner.Cost.Add("tools", 5);
        charcoal_burner.Upkeep = 0.5f;
        charcoal_burner.Workers.Add(Building.Resident.PEASANT, new int[] { 0, 5 });
        charcoal_burner.Consumes.Add("wood");
        charcoal_burner.Produces.Add("coal");
        charcoal_burner.Appeal_Effect = -1.25f;
        charcoal_burner.Appeal_Range  = 6.0f;
        charcoal_burner.Action        =
            (float delta_time, Building building) => {
            if (!building.Active)
            {
                return;
            }
            building.Refine_Resources("wood", 0.5f, "coal", 0.5f, delta_time);
        };
        prototypes.Add("charcoal_burner", charcoal_burner);

        //Foundry
        storage = new Dictionary <string, float[]>();
        storage.Add("coal", new float[2] {
            0.0f, 50.0f
        });
        storage.Add("iron_ore", new float[2] {
            0.0f, 100.0f
        });
        storage.Add("iron_bars", new float[2] {
            0.0f, 50.0f
        });
        Building foundry = new Building("foundry", 2, 2, 205.0f, 200, 5, 5, storage, Building.UI_Category.INDUSTRY);

        foundry.Cost.Add("cash", 160);
        foundry.Cost.Add("lumber", 75);
        foundry.Cost.Add("stone", 130);
        foundry.Cost.Add("tools", 20);
        foundry.Upkeep = 1.0f;
        foundry.Workers.Add(Building.Resident.PEASANT, new int[] { 0, 10 });
        foundry.Consumes.Add("coal");
        foundry.Consumes.Add("iron_ore");
        foundry.Produces.Add("iron_bars");
        foundry.Appeal_Effect = -1.25f;
        foundry.Appeal_Range  = 6.0f;
        foundry.Action        =
            (float delta_time, Building building) => {
            if (!building.Active)
            {
                return;
            }
            Dictionary <string, float> raw_material = new Dictionary <string, float>();
            raw_material.Add("coal", 1.0f);
            raw_material.Add("iron_ore", 2.0f);
            Dictionary <string, float> produce = new Dictionary <string, float>();
            produce.Add("iron_bars", 1.0f);
            building.Refine_Resources(raw_material, produce, delta_time);
        };
        prototypes.Add("foundry", foundry);

        //Smithy
        storage = new Dictionary <string, float[]>();
        storage.Add("coal", new float[2] {
            0.0f, 50.0f
        });
        storage.Add("iron_bars", new float[2] {
            0.0f, 100.0f
        });
        storage.Add("tools", new float[2] {
            0.0f, 50.0f
        });
        Building smithy = new Building("smithy", 2, 2, 165.0f, 200, 5, 5, storage, Building.UI_Category.INDUSTRY);

        smithy.Cost.Add("cash", 150);
        smithy.Cost.Add("lumber", 65);
        smithy.Cost.Add("stone", 100);
        smithy.Cost.Add("tools", 30);
        smithy.Upkeep = 2.0f;
        smithy.Changeable_Workers_PC.Add(0);
        smithy.Changeable_Workers_PC.Add(5);
        smithy.Changeable_Workers_PC.Add(0);
        smithy.Consumes.Add("coal");
        smithy.Consumes.Add("iron_bars");
        smithy.Produces.Add("tools");
        smithy.Appeal_Effect = -0.75f;
        smithy.Appeal_Range  = 4.0f;
        smithy.Action        =
            (float delta_time, Building building) => {
            if (!building.Active)
            {
                return;
            }
            Dictionary <string, float> raw_material = new Dictionary <string, float>();
            raw_material.Add("coal", 0.5f);
            raw_material.Add("iron_bars", 1.0f);
            Dictionary <string, float> produce = new Dictionary <string, float>();
            produce.Add("tools", 0.5f);
            building.Refine_Resources(raw_material, produce, delta_time);
        };
        prototypes.Add("smithy", smithy);

        //Small farm
        storage = new Dictionary <string, float[]>();
        storage.Add("potatoes", new float[2] {
            0.0f, 200.0f
        });
        Building small_farm = new Building("small_farm", 2, 2, 135.0f, 200, 0, 0, storage, Building.UI_Category.AGRICULTURE);

        small_farm.Cost.Add("cash", 100);
        small_farm.Cost.Add("lumber", 110);
        small_farm.Cost.Add("stone", 15);
        small_farm.Cost.Add("tools", 10);
        small_farm.Upkeep = 1.00f;
        small_farm.Workers.Add(Building.Resident.PEASANT, new int[] { 0, 10 });
        small_farm.Produces.Add("potatoes");
        small_farm.Attribute("highlight_tiles_during_build", 4.0f);
        small_farm.Action =
            (float delta_time, Building building) => {
            if (!building.Active)
            {
                return;
            }

            float potatoes = 0.0f;
            foreach (Tile tile in building.Harvest(4.0f, true))
            {
                if (tile.Building != null && tile.Building.Internal_Name == "potato_field")
                {
                    if (tile.Terrain == "Fertile Ground")
                    {
                        potatoes += 0.090f;
                    }
                    else
                    {
                        potatoes += 0.045f;
                    }
                }
            }

            potatoes *= delta_time;
            potatoes *= building.Get_Worker_Efficiency();

            building.Add("potatoes", potatoes);
            building.Set_Statistic("produced_potatoes", potatoes / delta_time);
            building.Set_Statistic("food_produced", potatoes / delta_time);
        };
        small_farm.On_Demolish =
            (Building building) => {
            building.Release_Harvested_Tiles(4.0f);
        };
        small_farm.Toggle_Select =
            (Building building) => {
            //Highlight tiles
            foreach (Tile tile in building.Harvest(4.0f, true))
            {
                tile.Highlight = new Color(0.1f, 0.5f, 0.1f, 0.5f);
            }
        };
        prototypes.Add("small_farm", small_farm);

        //Potato field
        Building potato_field = new Building("potato_field", 1, 1, 20.0f, 0, 0, 0, new Dictionary <string, float[]>(), Building.UI_Category.AGRICULTURE);

        potato_field.Cost.Add("cash", 10.0f);
        potato_field.Cost.Add("tools", 1.0f);
        potato_field.Appeal_Effect = -0.05f;
        potato_field.Appeal_Range  = 2.0f;
        potato_field.Can_Be_Paused = false;
        prototypes.Add("potato_field", potato_field);

        //Chapel
        Building chapel = new Building("chapel", 2, 2, 235.0f, 0, 10, 0, new Dictionary <string, float[]>(), Building.UI_Category.SERVICES);

        chapel.Cost.Add("cash", 210);
        chapel.Cost.Add("lumber", 60);
        chapel.Cost.Add("stone", 175);
        chapel.Cost.Add("tools", 20);
        chapel.Upkeep        = 2.0f;
        chapel.Appeal_Effect = 0.2f;
        chapel.Appeal_Range  = 5.0f;
        chapel.Changeable_Workers_PC.Add(0);
        chapel.Changeable_Workers_PC.Add(5);
        chapel.Changeable_Workers_PC.Add(0);
        chapel.Action =
            (float delta_t, Building building) => {
            if (!building.Active)
            {
                return;
            }
            List <Building> residential_buildings = new List <Building>();
            //Search for residential buildings in need of services
            foreach (Building b in building.Get_Connected_Buildings(building.Range, -1))
            {
                if (b.Is_Residential && b.Is_Built && b.Missing_Service("chapel") >= 0.05f)
                {
                    residential_buildings.Add(b);
                }
            }
            //Serve buildings
            float quality = ((building.Get_Worker_Efficiency() + 1.0f) / 2.0f);
            foreach (Building residence in residential_buildings)
            {
                //Serve
                residence.Serve("chapel", 1.0f, quality);
            }
        };
        prototypes.Add("chapel", chapel);

        //Brewery
        storage = new Dictionary <string, float[]>();
        storage.Add("potatoes", new float[2] {
            0.0f, 50.0f
        });
        storage.Add("alcohol", new float[2] {
            0.0f, 50.0f
        });
        Building brewery = new Building("brewery", 2, 2, 135.0f, 100, 5, 5, storage, Building.UI_Category.AGRICULTURE);

        brewery.Cost.Add("cash", 110);
        brewery.Cost.Add("lumber", 120);
        brewery.Cost.Add("stone", 15);
        brewery.Cost.Add("tools", 15);
        brewery.Upkeep = 1.0f;
        brewery.Changeable_Workers_PC.Add(0);
        brewery.Changeable_Workers_PC.Add(5);
        brewery.Changeable_Workers_PC.Add(0);
        brewery.Consumes.Add("potatoes");
        brewery.Produces.Add("alcohol");
        brewery.Appeal_Effect = -0.25f;
        brewery.Appeal_Range  = 2.0f;
        brewery.Action        =
            (float delta_time, Building building) => {
            if (!building.Active)
            {
                return;
            }
            building.Refine_Resources("potatoes", 1.0f, "alcohol", 0.5f, delta_time);
        };
        prototypes.Add("brewery", brewery);

        //Tavern
        storage = new Dictionary <string, float[]>();
        storage.Add("alcohol", new float[2] {
            0.0f, 100.0f
        });
        Building tavern = new Building("tavern", 2, 2, 350.0f, 100, 5, 5, storage, Building.UI_Category.SERVICES);

        tavern.Cost.Add("cash", 205);
        tavern.Cost.Add("lumber", 175);
        tavern.Cost.Add("stone", 175);
        tavern.Cost.Add("tools", 15);
        tavern.Upkeep = 1.5f;
        tavern.Changeable_Workers_PC.Add(0);
        tavern.Changeable_Workers_PC.Add(10);
        tavern.Changeable_Workers_PC.Add(0);
        tavern.Consumes.Add("alcohol");
        tavern.Action =
            (float delta_time, Building building) => {
            if (!building.Active)
            {
                return;
            }
            List <Building> residential_buildings = new List <Building>();
            //Search for residential buildings in need of services
            foreach (Building b in building.Get_Connected_Buildings(building.Range, -1))
            {
                if (b.Is_Residential && b.Is_Built && b.Missing_Service("tavern") >= 0.05f)
                {
                    residential_buildings.Add(b);
                }
            }
            //Serve
            building.Sell_Resources_As_Service("alcohol", "tavern", residential_buildings, delta_time, (building.Get_Worker_Efficiency() + 0.5f) / 1.5f);
        };
        prototypes.Add("tavern", tavern);

        //Abode
        Building abode = new Building("abode", 2, 2, 125.0f, 0, 0, 0, null, Building.UI_Category.HOUSING);

        abode.Cost.Add("cash", 150.0f);
        abode.Cost.Add("lumber", 50.0f);
        abode.Cost.Add("stone", 75.0f);
        abode.Cost.Add("tools", 10.0f);
        abode.Population_Type = Building.Resident.CITIZEN;
        abode.Population_Max  = 5;
        abode.Can_Be_Paused   = false;
        prototypes.Add("abode", abode);

        //Tax office
        Building tax_office = new Building("tax_office", 2, 2, 180.0f, 0, 10, 0, new Dictionary <string, float[]>(), Building.UI_Category.ADMIN);

        tax_office.Cost.Add("cash", 225);
        tax_office.Cost.Add("lumber", 90);
        tax_office.Cost.Add("stone", 90);
        tax_office.Cost.Add("tools", 10);
        tax_office.Upkeep = 1.5f;
        tax_office.Workers.Add(Building.Resident.CITIZEN, new int[] { 0, 5 });
        tax_office.Action =
            (float delta_time, Building building) => {
            if (!building.Active)
            {
                return;
            }
            List <Building> residential_buildings = new List <Building>();
            //Search for residential buildings
            foreach (Building b in building.Get_Connected_Buildings(building.Range, -1))
            {
                if (b.Is_Residential && b.Is_Built)
                {
                    residential_buildings.Add(b);
                }
            }
            //Collect tax
            float income = 0.0f;
            foreach (Building residence in residential_buildings)
            {
                if (!residence.Data.ContainsKey("tax_collected") || (int)(((float[])residence.Data["tax_collected"])[0]) == building.Id)
                {
                    if (!residence.Data.ContainsKey("tax_collected"))
                    {
                        residence.Data.Add("tax_collected", new float[2] {
                            building.Id, 1.0f
                        });
                    }
                    else
                    {
                        ((float[])residence.Data["tax_collected"])[1] = 1.0f;
                    }
                    switch (residence.Population_Type)
                    {
                    case Building.Resident.PEASANT:
                        income += 0.05f * residence.Population;
                        break;

                    case Building.Resident.CITIZEN:
                        income += 0.25f * residence.Population;
                        break;

                    case Building.Resident.NOBLE:
                        income += 5.0f * residence.Population;
                        break;
                    }
                }
            }
            income *= building.Get_Worker_Efficiency();
            building.Set_Statistic("income", income);
            income             *= delta_time;
            City.Instance.Cash += income;
        };
        prototypes.Add("tax_office", tax_office);

        //Builder's hall
        Building builders_hall = new Building("builders_hall", 2, 2, 275.0f, 0, 0, 0, new Dictionary <string, float[]>(), Building.UI_Category.INFRASTRUCTURE);

        builders_hall.Name = "Builder's Hall";
        builders_hall.Cost.Add("cash", 260);
        builders_hall.Cost.Add("lumber", 85);
        builders_hall.Cost.Add("stone", 190);
        builders_hall.Cost.Add("tools", 35);
        builders_hall.Upkeep = 1.25f;
        builders_hall.Changeable_Workers_PC.Add(0);
        builders_hall.Changeable_Workers_PC.Add(10);
        builders_hall.Changeable_Workers_PC.Add(0);
        builders_hall.Attribute("build_speed", 10.0f);
        builders_hall.Attribute("build_range", 10.0f);
        builders_hall.Attribute("highlight_tiles_during_build", 10.0f);
        prototypes.Add("builders_hall", builders_hall);
    }