1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
use std::collections::{HashMap, HashSet};

use map_model::{BuildingID, BuildingType};
use widgetry::{Color, Drawable, EventCtx, GeomBatch, Line, Text};

use crate::App;

pub struct Buildings {
    // Every building in the map is here, to simplify lookup logic.
    pub buildings: HashMap<BuildingID, BldgState>,
    // This an unchanging base layer that can get covered up by drawing on top of it. Maybe we
    // could even replace the one in DrawMap.
    pub draw_all: Drawable,
    pub total_housing_units: usize,
}

#[derive(Clone)]
pub enum BldgState {
    // Score
    Undelivered(usize),
    Store,
    // Or not a relevant building
    Done,
}

impl Buildings {
    pub fn new(ctx: &mut EventCtx, app: &App, upzones: HashSet<BuildingID>) -> Buildings {
        let colors = &app.session.colors;

        let mut buildings = HashMap::new();
        let mut total_housing_units = 0;
        let mut batch = GeomBatch::new();
        for b in app.map.all_buildings() {
            if upzones.contains(&b.id) {
                buildings.insert(b.id, BldgState::Store);
                batch.push(colors.store, b.polygon.clone());
                continue;
            }

            if let BuildingType::Residential {
                num_housing_units, ..
            } = b.bldg_type
            {
                // There are some unused commercial buildings around!
                if num_housing_units > 0 {
                    buildings.insert(b.id, BldgState::Undelivered(num_housing_units));
                    total_housing_units += num_housing_units;

                    let color = if num_housing_units > 5 {
                        colors.apartment
                    } else {
                        colors.house
                    };
                    batch.push(color, b.polygon.clone());
                    // Call out non-single family homes
                    if num_housing_units > 1 {
                        // TODO Text can be slow to render, and this should be louder anyway
                        batch.append(
                            Text::from(Line(num_housing_units.to_string()).fg(Color::RED))
                                .render_to_batch(ctx.prerender)
                                .scale(0.2)
                                .centered_on(b.label_center),
                        );
                    }
                    continue;
                }
            } else if !b.amenities.is_empty() {
                // TODO Maybe just food?
                buildings.insert(b.id, BldgState::Store);
                batch.push(colors.store, b.polygon.clone());
                continue;
            }

            // If it's not a residence or store, just blank it out.
            buildings.insert(b.id, BldgState::Done);
            batch.push(colors.visited, b.polygon.clone());
        }

        Buildings {
            buildings,
            draw_all: ctx.upload(batch),
            total_housing_units,
        }
    }

    pub fn all_stores(&self) -> Vec<BuildingID> {
        let mut stores = Vec::new();
        for (b, state) in &self.buildings {
            if let BldgState::Store = state {
                stores.push(*b);
            }
        }
        stores
    }

    pub fn draw_done_houses(&self, ctx: &mut EventCtx, app: &App) -> Drawable {
        let mut batch = GeomBatch::new();
        for (b, state) in &self.buildings {
            if let BldgState::Done = state {
                batch.push(
                    app.session.colors.visited,
                    app.map.get_b(*b).polygon.clone(),
                );
            }
        }
        ctx.upload(batch)
    }
}