From 97c5c45ca49ea2c8f1bdd3b6008a2c7008741d90 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Thu, 3 Dec 2020 20:36:35 -0800 Subject: [PATCH] Add a basic boost mechanic --- experiment/src/game.rs | 49 ++++++++++++++++++++++++++++++++++++-- experiment/src/meters.rs | 20 +++++++++------- experiment/src/movement.rs | 14 ++++++++++- experiment/src/vehicles.rs | 6 ++++- 4 files changed, 77 insertions(+), 12 deletions(-) diff --git a/experiment/src/game.rs b/experiment/src/game.rs index 5ae02123bd..6ce696362e 100644 --- a/experiment/src/game.rs +++ b/experiment/src/game.rs @@ -13,14 +13,18 @@ use widgetry::{ use crate::animation::{Animator, SnowEffect}; use crate::buildings::{BldgState, Buildings}; use crate::levels::Level; -use crate::meters::make_bar; +use crate::meters::{custom_bar, make_bar}; use crate::movement::Player; use crate::vehicles::Vehicle; +const ACQUIRE_BOOST_RATE: f64 = 0.5; +const BOOST_SPEED_MULTIPLIER: f64 = 1.5; + pub struct Game { title_panel: Panel, status_panel: Panel, time_panel: Panel, + boost_panel: Panel, minimap: SimpleMinimap, animator: Animator, @@ -71,6 +75,15 @@ impl Game { .aligned(HorizontalAlignment::LeftInset, VerticalAlignment::TopInset) .build(ctx); + let boost_panel = Panel::new(Widget::row(vec![ + "Boost".draw_text(ctx), + Widget::draw_batch(ctx, GeomBatch::new()) + .named("boost") + .align_right(), + ])) + .aligned(HorizontalAlignment::Center, VerticalAlignment::BottomInset) + .build(ctx); + let start = app .map .find_i_by_osm_id(level.start) @@ -85,6 +98,7 @@ impl Game { title_panel, status_panel, time_panel, + boost_panel, minimap: SimpleMinimap::new(ctx, app, with_zorder), animator: Animator::new(ctx), @@ -119,6 +133,18 @@ impl Game { self.state.vehicle.max_energy, ); self.status_panel.replace(ctx, "energy", energy_bar); + + let boost_bar = custom_bar( + ctx, + Color::hex("#A32015"), + self.state.boost / self.state.vehicle.max_boost, + if self.state.boost == Duration::ZERO { + Text::from(Line("Find a bike or bus lane to get a boost")) + } else { + Text::from(Line("Press space to boost")) + }, + ); + self.boost_panel.replace(ctx, "boost", boost_bar); } } @@ -128,11 +154,21 @@ impl State for Game { self.time += dt; } - let speed = if self.state.has_energy() { + let base_speed = if self.state.has_energy() { self.state.vehicle.normal_speed } else { self.state.vehicle.tired_speed }; + let speed = if ctx.is_key_down(Key::Space) && self.state.boost > Duration::ZERO { + if let Some(dt) = ctx.input.nonblocking_is_update_event() { + self.state.boost -= dt; + self.state.boost = self.state.boost.max(Duration::ZERO); + } + base_speed * BOOST_SPEED_MULTIPLIER + } else { + base_speed + }; + for b in self.player.update_with_speed(ctx, app, speed) { match self.state.bldgs.buildings[&b] { BldgState::Undelivered(_) => { @@ -168,6 +204,12 @@ impl State for Game { BldgState::Done => {} } } + if let Some(dt) = ctx.input.nonblocking_is_update_event() { + if self.player.on_good_road(app) { + self.state.boost += dt * ACQUIRE_BOOST_RATE; + self.state.boost = self.state.boost.min(self.state.vehicle.max_boost); + } + } if let Some(t) = self.minimap.event(ctx, app) { return t; @@ -222,6 +264,7 @@ impl State for Game { self.title_panel.draw(g); self.status_panel.draw(g); self.time_panel.draw(g); + self.boost_panel.draw(g); let santa_tracker = g.upload(GeomBatch::from(vec![( Color::RED, @@ -271,6 +314,7 @@ struct GameState { score: usize, // Number of gifts currently being carried energy: usize, + boost: Duration, draw_done_houses: Drawable, energyless_arrow: Option, @@ -292,6 +336,7 @@ impl GameState { score: 0, energy, + boost: Duration::ZERO, draw_done_houses: Drawable::empty(ctx), energyless_arrow: None, diff --git a/experiment/src/meters.rs b/experiment/src/meters.rs index ceff7617e9..5effd3d3e2 100644 --- a/experiment/src/meters.rs +++ b/experiment/src/meters.rs @@ -2,11 +2,10 @@ use abstutil::prettyprint_usize; use geom::Polygon; use widgetry::{Color, EventCtx, GeomBatch, Line, Text, Widget}; -pub fn make_bar(ctx: &mut EventCtx, filled_color: Color, value: usize, max: usize) -> Widget { +pub fn custom_bar(ctx: &mut EventCtx, filled_color: Color, pct_full: f64, txt: Text) -> Widget { let total_width = 300.0; let height = 32.0; let radius = Some(4.0); - let pct_full = (value as f64) / (max as f64); let mut batch = GeomBatch::new(); // Background @@ -19,13 +18,18 @@ pub fn make_bar(ctx: &mut EventCtx, filled_color: Color, value: usize, max: usiz batch.push(filled_color, poly); } // Text - let label = Text::from(Line(format!( - "{} / {}", - prettyprint_usize(value), - prettyprint_usize(max) - ))) - .render_to_batch(ctx.prerender); + let label = txt.render_to_batch(ctx.prerender); let dims = label.get_dims(); batch.append(label.translate(10.0, height / 2.0 - dims.height / 2.0)); Widget::draw_batch(ctx, batch) } + +pub fn make_bar(ctx: &mut EventCtx, filled_color: Color, value: usize, max: usize) -> Widget { + let pct_full = (value as f64) / (max as f64); + let txt = Text::from(Line(format!( + "{} / {}", + prettyprint_usize(value), + prettyprint_usize(max) + ))); + custom_bar(ctx, filled_color, pct_full, txt) +} diff --git a/experiment/src/movement.rs b/experiment/src/movement.rs index 49fc6fea22..ba31e3a551 100644 --- a/experiment/src/movement.rs +++ b/experiment/src/movement.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use abstutil::MultiMap; use geom::{Angle, Circle, Distance, Pt2D, Speed}; use map_gui::{SimpleApp, ID}; -use map_model::{BuildingID, Direction, IntersectionID, RoadID}; +use map_model::{BuildingID, Direction, IntersectionID, LaneType, RoadID}; use widgetry::EventCtx; use crate::controls::InstantController; @@ -134,6 +134,18 @@ impl Player { pub fn get_angle(&self) -> Angle { self.controls.facing } + + /// Is the player currently on a road with a bus or bike lane? + pub fn on_good_road(&self, app: &SimpleApp) -> bool { + if let On::Road(r, _) = self.on { + for (_, _, lt) in app.map.get_r(r).lanes_ltr() { + if lt == LaneType::Biking || lt == LaneType::Bus { + return true; + } + } + } + false + } } #[derive(Clone)] diff --git a/experiment/src/vehicles.rs b/experiment/src/vehicles.rs index 923231892e..84b7d7a04e 100644 --- a/experiment/src/vehicles.rs +++ b/experiment/src/vehicles.rs @@ -1,4 +1,4 @@ -use geom::{Speed, Time}; +use geom::{Duration, Speed, Time}; pub struct Vehicle { pub name: &'static str, @@ -6,6 +6,7 @@ pub struct Vehicle { pub normal_speed: Speed, pub tired_speed: Speed, pub max_energy: usize, + pub max_boost: Duration, // Paths to SVGs to draw in sequence pub draw_frames: Vec<&'static str>, @@ -20,6 +21,7 @@ impl Vehicle { normal_speed: Speed::miles_per_hour(30.0), tired_speed: Speed::miles_per_hour(10.0), max_energy: 80, + max_boost: Duration::seconds(5.0), draw_frames: vec!["sleigh.svg"], }, @@ -29,6 +31,7 @@ impl Vehicle { normal_speed: Speed::miles_per_hour(40.0), tired_speed: Speed::miles_per_hour(15.0), max_energy: 50, + max_boost: Duration::seconds(8.0), draw_frames: vec!["bike1.svg", "bike2.svg", "bike1.svg", "bike3.svg"], }, @@ -38,6 +41,7 @@ impl Vehicle { normal_speed: Speed::miles_per_hour(40.0), tired_speed: Speed::miles_per_hour(5.0), max_energy: 150, + max_boost: Duration::seconds(10.0), draw_frames: vec![ "cargo_bike1.svg",