Split config for different levels, add some sort of title screen

This commit is contained in:
Dustin Carlino 2020-11-29 15:44:45 -08:00
parent 2716c44bf7
commit 057587076c
4 changed files with 182 additions and 91 deletions

View File

@ -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
View 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);
}
}

View File

@ -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)
});
}

View File

@ -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());