mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 15:33:44 +03:00
Add a second meter to the experiment.
This commit is contained in:
parent
e589f89922
commit
62dc979677
@ -24,8 +24,6 @@ impl Controller for InstantController {
|
|||||||
let mut dy = 0.0;
|
let mut dy = 0.0;
|
||||||
|
|
||||||
if let Some(dt) = ctx.input.nonblocking_is_update_event() {
|
if let Some(dt) = ctx.input.nonblocking_is_update_event() {
|
||||||
ctx.input.use_update_event();
|
|
||||||
|
|
||||||
let dist = (dt * self.speed).inner_meters();
|
let dist = (dt * self.speed).inner_meters();
|
||||||
if ctx.is_key_down(Key::LeftArrow) {
|
if ctx.is_key_down(Key::LeftArrow) {
|
||||||
dx -= dist;
|
dx -= dist;
|
||||||
@ -68,8 +66,6 @@ impl Controller for RotateController {
|
|||||||
let mut dy = 0.0;
|
let mut dy = 0.0;
|
||||||
|
|
||||||
if let Some(dt) = ctx.input.nonblocking_is_update_event() {
|
if let Some(dt) = ctx.input.nonblocking_is_update_event() {
|
||||||
ctx.input.use_update_event();
|
|
||||||
|
|
||||||
if ctx.is_key_down(Key::LeftArrow) {
|
if ctx.is_key_down(Key::LeftArrow) {
|
||||||
self.angle = self
|
self.angle = self
|
||||||
.angle
|
.angle
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use geom::{Circle, Distance, Pt2D, Speed};
|
use abstutil::Timer;
|
||||||
|
use geom::{Circle, Distance, Duration, Pt2D, Speed};
|
||||||
use map_gui::tools::{nice_map_name, CityPicker};
|
use map_gui::tools::{nice_map_name, CityPicker};
|
||||||
use map_gui::{Cached, SimpleApp, ID};
|
use map_gui::{Cached, SimpleApp, ID};
|
||||||
use map_model::{BuildingID, BuildingType};
|
use map_model::{BuildingID, BuildingType, PathConstraints};
|
||||||
use widgetry::{
|
use widgetry::{
|
||||||
lctrl, Btn, Checkbox, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
lctrl, Btn, Checkbox, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
||||||
Line, Outcome, Panel, State, Text, TextExt, Transition, UpdateType, VerticalAlignment, Widget,
|
Line, Outcome, Panel, State, Text, TextExt, Transition, UpdateType, VerticalAlignment, Widget,
|
||||||
@ -16,14 +17,18 @@ pub struct Game {
|
|||||||
controls: Box<dyn Controller>,
|
controls: Box<dyn Controller>,
|
||||||
|
|
||||||
sleigh: Pt2D,
|
sleigh: Pt2D,
|
||||||
score: Score,
|
state: SleighState,
|
||||||
over_bldg: Cached<BuildingID, OverBldg>,
|
over_bldg: Cached<BuildingID, OverBldg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
pub fn new(ctx: &mut EventCtx, app: &SimpleApp) -> Box<dyn State<SimpleApp>> {
|
pub fn new(
|
||||||
|
ctx: &mut EventCtx,
|
||||||
|
app: &SimpleApp,
|
||||||
|
timer: &mut Timer,
|
||||||
|
) -> Box<dyn State<SimpleApp>> {
|
||||||
// Start on a commerical building
|
// Start on a commerical building
|
||||||
let sleigh = app
|
let depot = app
|
||||||
.map
|
.map
|
||||||
.all_buildings()
|
.all_buildings()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -31,9 +36,10 @@ impl Game {
|
|||||||
BuildingType::Commercial(_) => true,
|
BuildingType::Commercial(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
})
|
||||||
.map(|b| b.label_center)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let sleigh = depot.label_center;
|
||||||
ctx.canvas.center_on_map_pt(sleigh);
|
ctx.canvas.center_on_map_pt(sleigh);
|
||||||
|
let state = SleighState::new(ctx, app, depot.id, timer);
|
||||||
|
|
||||||
Box::new(Game {
|
Box::new(Game {
|
||||||
panel: Panel::new(Widget::col(vec![
|
panel: Panel::new(Widget::col(vec![
|
||||||
@ -47,14 +53,19 @@ impl Game {
|
|||||||
Some(nice_map_name(app.map.get_name())),
|
Some(nice_map_name(app.map.get_name())),
|
||||||
)
|
)
|
||||||
.build(ctx, "change map", lctrl(Key::L))]),
|
.build(ctx, "change map", lctrl(Key::L))]),
|
||||||
format!("Score: 0").draw_text(ctx).named("score"),
|
format!("Score: {}", state.score)
|
||||||
|
.draw_text(ctx)
|
||||||
|
.named("score"),
|
||||||
|
format!("Energy: {}", state.energy)
|
||||||
|
.draw_text(ctx)
|
||||||
|
.named("energy"),
|
||||||
]))
|
]))
|
||||||
.aligned(HorizontalAlignment::Right, VerticalAlignment::Top)
|
.aligned(HorizontalAlignment::Right, VerticalAlignment::Top)
|
||||||
.build(ctx),
|
.build(ctx),
|
||||||
controls: Box::new(InstantController::new(Speed::miles_per_hour(30.0))),
|
controls: Box::new(InstantController::new(Speed::miles_per_hour(30.0))),
|
||||||
|
|
||||||
sleigh,
|
sleigh,
|
||||||
score: Score::new(ctx, app),
|
state,
|
||||||
over_bldg: Cached::new(),
|
over_bldg: Cached::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -63,7 +74,12 @@ impl Game {
|
|||||||
self.panel.replace(
|
self.panel.replace(
|
||||||
ctx,
|
ctx,
|
||||||
"score",
|
"score",
|
||||||
format!("Score: {}", abstutil::prettyprint_usize(self.score.score)).draw_text(ctx),
|
format!("Score: {}", abstutil::prettyprint_usize(self.state.score)).draw_text(ctx),
|
||||||
|
);
|
||||||
|
self.panel.replace(
|
||||||
|
ctx,
|
||||||
|
"energy",
|
||||||
|
format!("Energy: {}", self.state.energy).draw_text(ctx),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,20 +92,25 @@ impl State<SimpleApp> for Game {
|
|||||||
ctx.canvas.center_on_map_pt(self.sleigh);
|
ctx.canvas.center_on_map_pt(self.sleigh);
|
||||||
|
|
||||||
self.over_bldg
|
self.over_bldg
|
||||||
.update(OverBldg::key(app, self.sleigh, &self.score), |key| {
|
.update(OverBldg::key(app, self.sleigh, &self.state), |key| {
|
||||||
OverBldg::value(ctx, app, key)
|
OverBldg::value(ctx, app, key)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(b) = self.over_bldg.key() {
|
if let Some(b) = self.over_bldg.key() {
|
||||||
if ctx.input.pressed(Key::Space) {
|
if ctx.input.pressed(Key::Space) {
|
||||||
if self.score.present_dropped(ctx, app, b) {
|
if self.state.present_dropped(ctx, app, b) {
|
||||||
self.over_bldg.clear();
|
self.over_bldg.clear();
|
||||||
self.update_panel(ctx);
|
self.update_panel(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(dt) = ctx.input.nonblocking_is_update_event() {
|
||||||
|
self.state.energy -= dt;
|
||||||
|
self.update_panel(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
match self.panel.event(ctx) {
|
match self.panel.event(ctx) {
|
||||||
Outcome::Clicked(x) => match x.as_ref() {
|
Outcome::Clicked(x) => match x.as_ref() {
|
||||||
"close" => {
|
"close" => {
|
||||||
@ -100,10 +121,12 @@ impl State<SimpleApp> for Game {
|
|||||||
ctx,
|
ctx,
|
||||||
app,
|
app,
|
||||||
Box::new(|ctx, app| {
|
Box::new(|ctx, app| {
|
||||||
Transition::Multi(vec![
|
ctx.loading_screen("setup again", |ctx, mut timer| {
|
||||||
Transition::Pop,
|
Transition::Multi(vec![
|
||||||
Transition::Replace(Game::new(ctx, app)),
|
Transition::Pop,
|
||||||
])
|
Transition::Replace(Game::new(ctx, app, &mut timer)),
|
||||||
|
])
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -126,8 +149,8 @@ impl State<SimpleApp> for Game {
|
|||||||
fn draw(&self, g: &mut GfxCtx, _: &SimpleApp) {
|
fn draw(&self, g: &mut GfxCtx, _: &SimpleApp) {
|
||||||
self.panel.draw(g);
|
self.panel.draw(g);
|
||||||
|
|
||||||
g.redraw(&self.score.draw_scores);
|
g.redraw(&self.state.draw_scores);
|
||||||
g.redraw(&self.score.draw_done);
|
g.redraw(&self.state.draw_done);
|
||||||
if let Some(draw) = self.over_bldg.value() {
|
if let Some(draw) = self.over_bldg.value() {
|
||||||
g.redraw(&draw.0);
|
g.redraw(&draw.0);
|
||||||
}
|
}
|
||||||
@ -138,33 +161,67 @@ impl State<SimpleApp> for Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Score {
|
struct SleighState {
|
||||||
|
depot: BuildingID,
|
||||||
score: usize,
|
score: usize,
|
||||||
|
energy: Duration,
|
||||||
houses: HashMap<BuildingID, BldgState>,
|
houses: HashMap<BuildingID, BldgState>,
|
||||||
|
house_costs: HashMap<BuildingID, Duration>,
|
||||||
draw_scores: Drawable,
|
draw_scores: Drawable,
|
||||||
draw_done: Drawable,
|
draw_done: Drawable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Score {
|
impl SleighState {
|
||||||
fn new(ctx: &mut EventCtx, app: &SimpleApp) -> Score {
|
fn new(
|
||||||
|
ctx: &mut EventCtx,
|
||||||
|
app: &SimpleApp,
|
||||||
|
depot: BuildingID,
|
||||||
|
timer: &mut Timer,
|
||||||
|
) -> SleighState {
|
||||||
|
timer.start("calculate costs from depot");
|
||||||
|
let house_costs = map_model::connectivity::all_costs_from(
|
||||||
|
&app.map,
|
||||||
|
depot,
|
||||||
|
Duration::hours(3),
|
||||||
|
PathConstraints::Pedestrian,
|
||||||
|
);
|
||||||
|
timer.stop("calculate costs from depot");
|
||||||
|
|
||||||
let mut houses = HashMap::new();
|
let mut houses = HashMap::new();
|
||||||
let mut batch = GeomBatch::new();
|
let mut batch = GeomBatch::new();
|
||||||
|
timer.start_iter("assign score to houses", app.map.all_buildings().len());
|
||||||
for b in app.map.all_buildings() {
|
for b in app.map.all_buildings() {
|
||||||
|
timer.next();
|
||||||
if let BuildingType::Residential(_) = b.bldg_type {
|
if let BuildingType::Residential(_) = b.bldg_type {
|
||||||
let score = b.id.0;
|
let score = b.id.0;
|
||||||
|
let cost = house_costs.get(&b.id).cloned().unwrap_or(Duration::ZERO);
|
||||||
|
let color = if cost < Duration::minutes(5) {
|
||||||
|
Color::GREEN
|
||||||
|
} else if cost < Duration::minutes(15) {
|
||||||
|
Color::YELLOW
|
||||||
|
} else {
|
||||||
|
Color::RED
|
||||||
|
};
|
||||||
|
|
||||||
houses.insert(b.id, BldgState::Undelivered(score));
|
houses.insert(b.id, BldgState::Undelivered(score));
|
||||||
batch.append(
|
batch.append(
|
||||||
Text::from(Line(format!("{}", score)))
|
Text::from_multiline(vec![
|
||||||
.render_to_batch(ctx.prerender)
|
Line(format!("{}", score)),
|
||||||
.scale(0.1)
|
Line(format!("{}", cost)).fg(color),
|
||||||
.centered_on(b.label_center),
|
])
|
||||||
|
.render_to_batch(ctx.prerender)
|
||||||
|
.scale(0.1)
|
||||||
|
.centered_on(b.label_center),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Score {
|
SleighState {
|
||||||
|
depot,
|
||||||
score: 0,
|
score: 0,
|
||||||
|
energy: Duration::minutes(90),
|
||||||
houses,
|
houses,
|
||||||
|
house_costs,
|
||||||
draw_scores: ctx.upload(batch),
|
draw_scores: ctx.upload(batch),
|
||||||
draw_done: Drawable::empty(ctx),
|
draw_done: Drawable::empty(ctx),
|
||||||
}
|
}
|
||||||
@ -177,6 +234,7 @@ impl Score {
|
|||||||
batch.push(Color::BLACK, app.map.get_b(*b).polygon.clone());
|
batch.push(Color::BLACK, app.map.get_b(*b).polygon.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
batch.push(Color::GREEN, app.map.get_b(self.depot).polygon.clone());
|
||||||
self.draw_done = ctx.upload(batch);
|
self.draw_done = ctx.upload(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,6 +243,7 @@ impl Score {
|
|||||||
if let Some(BldgState::Undelivered(score)) = self.houses.get(&id) {
|
if let Some(BldgState::Undelivered(score)) = self.houses.get(&id) {
|
||||||
self.score += score;
|
self.score += score;
|
||||||
self.houses.insert(id, BldgState::Done);
|
self.houses.insert(id, BldgState::Done);
|
||||||
|
self.energy -= self.house_costs.get(&id).cloned().unwrap_or(Duration::ZERO);
|
||||||
self.redraw(ctx, app);
|
self.redraw(ctx, app);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -201,14 +260,14 @@ enum BldgState {
|
|||||||
struct OverBldg(Drawable);
|
struct OverBldg(Drawable);
|
||||||
|
|
||||||
impl OverBldg {
|
impl OverBldg {
|
||||||
fn key(app: &SimpleApp, sleigh: Pt2D, score: &Score) -> Option<BuildingID> {
|
fn key(app: &SimpleApp, sleigh: Pt2D, state: &SleighState) -> Option<BuildingID> {
|
||||||
for id in app
|
for id in app
|
||||||
.draw_map
|
.draw_map
|
||||||
.get_matching_objects(Circle::new(sleigh, Distance::meters(3.0)).get_bounds())
|
.get_matching_objects(Circle::new(sleigh, Distance::meters(3.0)).get_bounds())
|
||||||
{
|
{
|
||||||
if let ID::Building(b) = id {
|
if let ID::Building(b) = id {
|
||||||
if app.map.get_b(b).polygon.contains_pt(sleigh) {
|
if app.map.get_b(b).polygon.contains_pt(sleigh) {
|
||||||
if let Some(BldgState::Undelivered(_)) = score.houses.get(&b) {
|
if let Some(BldgState::Undelivered(_)) = state.houses.get(&b) {
|
||||||
return Some(b);
|
return Some(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,9 @@ mod game;
|
|||||||
fn main() {
|
fn main() {
|
||||||
widgetry::run(widgetry::Settings::new("experiment"), |ctx| {
|
widgetry::run(widgetry::Settings::new("experiment"), |ctx| {
|
||||||
let app = map_gui::SimpleApp::new(ctx, abstutil::CmdArgs::new());
|
let app = map_gui::SimpleApp::new(ctx, abstutil::CmdArgs::new());
|
||||||
let states = vec![game::Game::new(ctx, &app)];
|
let states = ctx.loading_screen("setup", |ctx, mut timer| {
|
||||||
|
vec![game::Game::new(ctx, &app, &mut timer)]
|
||||||
|
});
|
||||||
(app, states)
|
(app, states)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user