mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
Add a basic boost mechanic
This commit is contained in:
parent
12d0294692
commit
97c5c45ca4
@ -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<SimpleApp> 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<SimpleApp> 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<SimpleApp> 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<EnergylessArrow>,
|
||||
@ -292,6 +336,7 @@ impl GameState {
|
||||
|
||||
score: 0,
|
||||
energy,
|
||||
boost: Duration::ZERO,
|
||||
|
||||
draw_done_houses: Drawable::empty(ctx),
|
||||
energyless_arrow: None,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user