mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-24 23:15:24 +03:00
Split config for different levels, add some sort of title screen
This commit is contained in:
parent
2716c44bf7
commit
057587076c
@ -1,18 +1,20 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use abstutil::prettyprint_usize;
|
||||
use geom::{ArrowCap, Circle, Distance, Duration, Line, PolyLine, Polygon, Pt2D, Speed, Time};
|
||||
use map_gui::tools::{nice_map_name, CityPicker, ColorScale, DivergingScale, SimpleMinimap};
|
||||
use geom::{ArrowCap, Circle, Distance, Duration, Line, PolyLine, Polygon, Pt2D, Time};
|
||||
use map_gui::load::MapLoader;
|
||||
use map_gui::tools::{ColorScale, DivergingScale, SimpleMinimap};
|
||||
use map_gui::{Cached, SimpleApp, ID};
|
||||
use map_model::{BuildingID, BuildingType, PathConstraints};
|
||||
use widgetry::{
|
||||
lctrl, Btn, Checkbox, Color, Drawable, EventCtx, Fill, GeomBatch, GfxCtx, HorizontalAlignment,
|
||||
Key, Line, LinearGradient, Outcome, Panel, RewriteColor, State, Text, TextExt, Transition,
|
||||
Btn, Checkbox, Color, Drawable, EventCtx, Fill, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
||||
Line, LinearGradient, Outcome, Panel, RewriteColor, State, Text, TextExt, Transition,
|
||||
UpdateType, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use crate::animation::{Animator, SnowEffect};
|
||||
use crate::controls::{Controller, InstantController, RotateController};
|
||||
use crate::levels::Config;
|
||||
|
||||
const ZOOM: f64 = 10.0;
|
||||
|
||||
@ -29,65 +31,59 @@ pub struct Game {
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new(ctx: &mut EventCtx, app: &SimpleApp) -> Box<dyn State<SimpleApp>> {
|
||||
ctx.canvas.cam_zoom = ZOOM;
|
||||
pub fn new(ctx: &mut EventCtx, app: &SimpleApp, config: Config) -> Box<dyn State<SimpleApp>> {
|
||||
MapLoader::new(
|
||||
ctx,
|
||||
app,
|
||||
config.map.clone(),
|
||||
Box::new(move |ctx, app| {
|
||||
ctx.canvas.cam_zoom = ZOOM;
|
||||
|
||||
// Start on a commerical building
|
||||
let depot = app
|
||||
.map
|
||||
.all_buildings()
|
||||
.into_iter()
|
||||
.find(|b| match b.bldg_type {
|
||||
BuildingType::Commercial(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
.unwrap();
|
||||
let sleigh = depot.label_center;
|
||||
ctx.canvas.center_on_map_pt(sleigh);
|
||||
let state = SleighState::new(ctx, app, depot.id);
|
||||
let state = SleighState::new(ctx, app, config);
|
||||
let sleigh = app.map.get_b(state.depot).label_center;
|
||||
ctx.canvas.center_on_map_pt(sleigh);
|
||||
|
||||
let panel = Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
Line("Experiment").small_heading().draw(ctx),
|
||||
Btn::close(ctx),
|
||||
]),
|
||||
Checkbox::toggle(ctx, "control type", "rotate", "instant", Key::Tab, false),
|
||||
Widget::row(vec![Btn::pop_up(
|
||||
ctx,
|
||||
Some(nice_map_name(app.map.get_name())),
|
||||
)
|
||||
.build(ctx, "change map", lctrl(Key::L))]),
|
||||
"Score".draw_text(ctx).named("score"),
|
||||
Widget::row(vec![
|
||||
"Energy:".draw_text(ctx),
|
||||
Widget::draw_batch(ctx, GeomBatch::new())
|
||||
.named("energy")
|
||||
.align_right(),
|
||||
]),
|
||||
Widget::row(vec![
|
||||
"Next upzone:".draw_text(ctx),
|
||||
Widget::draw_batch(ctx, GeomBatch::new())
|
||||
.named("next upzone")
|
||||
.align_right(),
|
||||
]),
|
||||
"use upzone".draw_text(ctx).named("use upzone"),
|
||||
]))
|
||||
.aligned(HorizontalAlignment::Right, VerticalAlignment::Top)
|
||||
.build(ctx);
|
||||
let with_zorder = false;
|
||||
let mut game = Game {
|
||||
panel,
|
||||
controls: Box::new(InstantController::new()),
|
||||
minimap: SimpleMinimap::new(ctx, app, with_zorder),
|
||||
animator: Animator::new(ctx),
|
||||
snow: SnowEffect::new(ctx),
|
||||
let panel = Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
Line("Experiment").small_heading().draw(ctx),
|
||||
Btn::close(ctx),
|
||||
]),
|
||||
Checkbox::toggle(ctx, "control type", "rotate", "instant", Key::Tab, false),
|
||||
"Score".draw_text(ctx).named("score"),
|
||||
Widget::row(vec![
|
||||
"Energy:".draw_text(ctx),
|
||||
Widget::draw_batch(ctx, GeomBatch::new())
|
||||
.named("energy")
|
||||
.align_right(),
|
||||
]),
|
||||
Widget::row(vec![
|
||||
"Next upzone:".draw_text(ctx),
|
||||
Widget::draw_batch(ctx, GeomBatch::new())
|
||||
.named("next upzone")
|
||||
.align_right(),
|
||||
]),
|
||||
"use upzone".draw_text(ctx).named("use upzone"),
|
||||
]))
|
||||
.aligned(HorizontalAlignment::Right, VerticalAlignment::Top)
|
||||
.build(ctx);
|
||||
let with_zorder = false;
|
||||
let mut game = Game {
|
||||
panel,
|
||||
controls: Box::new(InstantController::new()),
|
||||
minimap: SimpleMinimap::new(ctx, app, with_zorder),
|
||||
animator: Animator::new(ctx),
|
||||
snow: SnowEffect::new(ctx),
|
||||
|
||||
sleigh,
|
||||
state,
|
||||
over_bldg: Cached::new(),
|
||||
};
|
||||
game.update_panel(ctx);
|
||||
Box::new(game)
|
||||
sleigh,
|
||||
state,
|
||||
over_bldg: Cached::new(),
|
||||
};
|
||||
game.update_panel(ctx);
|
||||
game.minimap
|
||||
.set_zoom(ctx, app, game.state.config.minimap_zoom);
|
||||
Transition::Replace(Box::new(game))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn update_panel(&mut self, ctx: &mut EventCtx) {
|
||||
@ -228,18 +224,6 @@ impl State<SimpleApp> for Game {
|
||||
"close" => {
|
||||
return Transition::Pop;
|
||||
}
|
||||
"change map" => {
|
||||
return Transition::Push(CityPicker::new(
|
||||
ctx,
|
||||
app,
|
||||
Box::new(|ctx, app| {
|
||||
Transition::Multi(vec![
|
||||
Transition::Pop,
|
||||
Transition::Replace(Game::new(ctx, app)),
|
||||
])
|
||||
}),
|
||||
));
|
||||
}
|
||||
"use upzone" => {
|
||||
let choices = self
|
||||
.state
|
||||
@ -301,14 +285,6 @@ impl State<SimpleApp> for Game {
|
||||
}
|
||||
}
|
||||
|
||||
struct Config {
|
||||
normal_speed: Speed,
|
||||
tired_speed: Speed,
|
||||
recharge_rate: f64,
|
||||
max_energy: Duration,
|
||||
upzone_rate: usize,
|
||||
}
|
||||
|
||||
struct SleighState {
|
||||
config: Config,
|
||||
|
||||
@ -330,8 +306,9 @@ struct SleighState {
|
||||
}
|
||||
|
||||
impl SleighState {
|
||||
fn new(ctx: &mut EventCtx, app: &SimpleApp, depot: BuildingID) -> SleighState {
|
||||
fn new(ctx: &mut EventCtx, app: &SimpleApp, config: Config) -> SleighState {
|
||||
let mut houses = HashMap::new();
|
||||
let mut depot = None;
|
||||
for b in app.map.all_buildings() {
|
||||
if let BuildingType::Residential(_) = b.bldg_type {
|
||||
let score = b.id.0;
|
||||
@ -339,17 +316,13 @@ impl SleighState {
|
||||
} else if !b.amenities.is_empty() {
|
||||
// TODO Maybe just food?
|
||||
houses.insert(b.id, BldgState::Depot);
|
||||
if b.orig_id == config.start_depot {
|
||||
depot = Some(b.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let config = Config {
|
||||
normal_speed: Speed::miles_per_hour(30.0),
|
||||
tired_speed: Speed::miles_per_hour(10.0),
|
||||
recharge_rate: 1000.0,
|
||||
max_energy: Duration::minutes(90),
|
||||
upzone_rate: 30_000,
|
||||
};
|
||||
|
||||
let depot = depot.expect(&format!("can't find {}", config.start_depot));
|
||||
let energy = config.max_energy;
|
||||
let mut s = SleighState {
|
||||
config,
|
||||
|
117
experiment/src/levels.rs
Normal file
117
experiment/src/levels.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use abstutil::MapName;
|
||||
use geom::{Duration, Speed};
|
||||
use map_gui::SimpleApp;
|
||||
use map_model::osm;
|
||||
use widgetry::{
|
||||
Btn, DrawBaselayer, EventCtx, GfxCtx, Key, Line, Outcome, Panel, State, Text, Transition,
|
||||
Widget,
|
||||
};
|
||||
|
||||
pub struct Config {
|
||||
pub title: &'static str,
|
||||
pub map: MapName,
|
||||
pub start_depot: osm::OsmID,
|
||||
pub minimap_zoom: usize,
|
||||
|
||||
pub normal_speed: Speed,
|
||||
pub tired_speed: Speed,
|
||||
pub recharge_rate: f64,
|
||||
pub max_energy: Duration,
|
||||
pub upzone_rate: usize,
|
||||
}
|
||||
|
||||
// TODO Like Challenge::all; cache with lazy static?
|
||||
fn all_levels() -> Vec<Config> {
|
||||
vec![
|
||||
Config {
|
||||
title: "Level 1",
|
||||
map: MapName::seattle("montlake"),
|
||||
start_depot: osm::OsmID::Way(osm::WayID(217700589)),
|
||||
minimap_zoom: 0,
|
||||
|
||||
normal_speed: Speed::miles_per_hour(30.0),
|
||||
tired_speed: Speed::miles_per_hour(10.0),
|
||||
recharge_rate: 1000.0,
|
||||
max_energy: Duration::minutes(90),
|
||||
upzone_rate: 30_000,
|
||||
},
|
||||
Config {
|
||||
title: "Level 2 - Magnolia",
|
||||
map: MapName::seattle("ballard"),
|
||||
start_depot: osm::OsmID::Way(osm::WayID(38655876)),
|
||||
minimap_zoom: 2,
|
||||
|
||||
normal_speed: Speed::miles_per_hour(40.0),
|
||||
tired_speed: Speed::miles_per_hour(15.0),
|
||||
recharge_rate: 2000.0,
|
||||
max_energy: Duration::minutes(120),
|
||||
upzone_rate: 30_000,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
pub struct TitleScreen {
|
||||
panel: Panel,
|
||||
}
|
||||
|
||||
impl TitleScreen {
|
||||
pub fn new(ctx: &mut EventCtx) -> Box<dyn State<SimpleApp>> {
|
||||
let levels = all_levels();
|
||||
|
||||
Box::new(TitleScreen {
|
||||
panel: Panel::new(
|
||||
Widget::col(vec![
|
||||
Btn::svg_def("system/assets/pregame/quit.svg")
|
||||
.build(ctx, "quit", Key::Escape)
|
||||
.align_left(),
|
||||
{
|
||||
let mut txt = Text::from(Line("15 minute Santa").display_title());
|
||||
txt.add(Line("An experiment"));
|
||||
txt.draw(ctx).centered_horiz()
|
||||
},
|
||||
Widget::row(
|
||||
levels
|
||||
.into_iter()
|
||||
.map(|lvl| Btn::text_bg2(lvl.title).build_def(ctx, None))
|
||||
.collect(),
|
||||
),
|
||||
])
|
||||
.evenly_spaced(),
|
||||
)
|
||||
.exact_size_percent(90, 85)
|
||||
.build_custom(ctx),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl State<SimpleApp> for TitleScreen {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut SimpleApp) -> Transition<SimpleApp> {
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
"quit" => {
|
||||
std::process::exit(0);
|
||||
}
|
||||
x => {
|
||||
for lvl in all_levels() {
|
||||
if x == lvl.title {
|
||||
return Transition::Push(crate::game::Game::new(ctx, app, lvl));
|
||||
}
|
||||
}
|
||||
panic!("Unknown action {}", x);
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw_baselayer(&self) -> DrawBaselayer {
|
||||
DrawBaselayer::Custom
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, app: &SimpleApp) {
|
||||
g.clear(app.cs.dialog_bg);
|
||||
self.panel.draw(g);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
mod animation;
|
||||
mod controls;
|
||||
mod game;
|
||||
mod levels;
|
||||
mod upzone;
|
||||
|
||||
fn main() {
|
||||
@ -8,7 +9,7 @@ fn main() {
|
||||
let mut opts = map_gui::options::Options::default();
|
||||
opts.color_scheme = map_gui::colors::ColorSchemeChoice::NightMode;
|
||||
let app = map_gui::SimpleApp::new_with_opts(ctx, abstutil::CmdArgs::new(), opts);
|
||||
let states = vec![game::Game::new(ctx, &app)];
|
||||
let states = vec![levels::TitleScreen::new(ctx)];
|
||||
(app, states)
|
||||
});
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ impl SimpleMinimap {
|
||||
(pct_x, pct_y)
|
||||
}
|
||||
|
||||
fn set_zoom(&mut self, ctx: &mut EventCtx, app: &SimpleApp, zoom_lvl: usize) {
|
||||
pub fn set_zoom(&mut self, ctx: &mut EventCtx, app: &SimpleApp, zoom_lvl: usize) {
|
||||
// Make the frame wind up in the same relative position on the minimap
|
||||
let (pct_x, pct_y) = self.map_to_minimap_pct(ctx.canvas.center_to_map_pt());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user