Improve the out-of-energy arrow:

- point to the driveway, not the building center
- scale arrow length so it doesn't overshoot where we're supposed to go
- explicitly say to go refill from a store
This commit is contained in:
Dustin Carlino 2020-12-14 15:03:22 -08:00
parent 2abf87c1a8
commit 4d057cb74f
7 changed files with 64 additions and 29 deletions

View File

@ -97,6 +97,8 @@ impl Strategize {
ColorLegend::row(ctx, app.session.colors.house, "house"),
ColorLegend::row(ctx, app.session.colors.apartment, "apartment"),
ColorLegend::row(ctx, app.session.colors.store, "store"),
]),
Widget::row(vec![
ColorLegend::row(ctx, Color::PINK, "upzoned store"),
ColorLegend::row(ctx, Color::RED, "delivered!"),
])

View File

@ -14,7 +14,7 @@ use crate::animation::{Animator, Effect, SnowEffect};
use crate::buildings::{BldgState, Buildings};
use crate::levels::Level;
use crate::meters::{custom_bar, make_bar};
use crate::movement::Player;
use crate::player::Player;
use crate::vehicles::Vehicle;
use crate::{App, Transition};
@ -63,7 +63,7 @@ impl Game {
let status_panel = Panel::new(Widget::col(vec![
"Complete Deliveries".draw_text(ctx).named("score label"),
Widget::draw_batch(ctx, GeomBatch::new()).named("score"),
"Remaining Gifts".draw_text(ctx),
"Remaining Gifts".draw_text(ctx).named("energy label"),
Widget::draw_batch(ctx, GeomBatch::new()).named("energy"),
]))
.aligned(HorizontalAlignment::RightInset, VerticalAlignment::TopInset)
@ -296,18 +296,27 @@ impl Game {
self.animator.event(ctx, app.time);
self.snow.event(ctx, app.time);
if self.state.has_energy() {
self.state.energyless_arrow = None;
if self.state.energyless_arrow.is_some() {
self.state.energyless_arrow = None;
let label = "Remaining Gifts".draw_text(ctx);
self.status_panel.replace(ctx, "energy label", label);
}
} else {
if self.state.energyless_arrow.is_none() {
self.state.energyless_arrow = Some(EnergylessArrow::new(ctx, app.time));
self.state.energyless_arrow = Some(EnergylessArrow::new(
ctx,
app.time,
self.state.bldgs.all_stores(),
));
let label = Text::from(Line("Out of gifts - refill from a store!").fg(Color::RED))
.draw(ctx);
self.status_panel.replace(ctx, "energy label", label);
}
let stores = self.state.bldgs.all_stores();
self.state.energyless_arrow.as_mut().unwrap().update(
ctx,
app,
self.player.get_pos(),
stores,
);
self.state
.energyless_arrow
.as_mut()
.unwrap()
.update(ctx, app, self.player.get_pos());
}
if self.state.boost != orig_boost {
@ -534,28 +543,33 @@ struct EnergylessArrow {
draw: Drawable,
started: Time,
last_update: Time,
all_stores: Vec<BuildingID>,
}
impl EnergylessArrow {
fn new(ctx: &EventCtx, started: Time) -> EnergylessArrow {
fn new(ctx: &EventCtx, started: Time, all_stores: Vec<BuildingID>) -> EnergylessArrow {
EnergylessArrow {
draw: Drawable::empty(ctx),
started,
last_update: Time::START_OF_DAY,
all_stores,
}
}
fn update(&mut self, ctx: &mut EventCtx, app: &App, sleigh: Pt2D, all_stores: Vec<BuildingID>) {
fn update(&mut self, ctx: &mut EventCtx, app: &App, sleigh: Pt2D) {
if self.last_update == app.time {
return;
}
self.last_update = app.time;
// Find the closest store as the crow -- or Santa -- flies
// Find the closest store as the crow -- or Santa -- flies. Point to the end of the
// driveway, since sometimes it's hard to quickly spot which road a building is connected
// to.
// TODO Or pathfind and show them that?
let store = app.map.get_b(
all_stores
.into_iter()
.min_by_key(|b| app.map.get_b(*b).label_center.fast_dist(sleigh))
*self
.all_stores
.iter()
.min_by_key(|b| app.map.get_b(**b).driveway_geom.last_pt().fast_dist(sleigh))
.unwrap(),
);
@ -566,13 +580,22 @@ impl EnergylessArrow {
let shift = (pct * std::f64::consts::PI).sin();
let thickness = Distance::meters(5.0 + shift);
let angle = sleigh.angle_to(store.label_center);
let arrow = PolyLine::must_new(vec![
let goto = store.driveway_geom.last_pt();
let angle = sleigh.angle_to(goto);
// TODO When we're too close, we get an awkward arrowcap; the intention was for it to
// disappear...
if let Some(arrow) = PolyLine::new(vec![
sleigh.project_away(Distance::meters(20.0), angle),
sleigh.project_away(Distance::meters(40.0), angle),
goto,
])
.make_arrow(thickness, ArrowCap::Triangle);
self.draw = ctx.upload(GeomBatch::from(vec![(Color::RED.alpha(0.8), arrow)]));
.and_then(|pl| {
pl.maybe_exact_slice(Distance::ZERO, Distance::meters(20.0).min(pl.length()))
})
.ok()
.and_then(|slice| slice.maybe_make_arrow(thickness, ArrowCap::Triangle))
{
self.draw = ctx.upload(GeomBatch::from(vec![(Color::RED.alpha(0.8), arrow)]));
}
}
}

View File

@ -29,7 +29,7 @@ impl Level {
map: MapName::seattle("montlake"),
start: osm::NodeID(53084814),
minimap_zoom: 1,
time_limit: Duration::seconds(5.0),
time_limit: Duration::seconds(50.0),
goal: 20,
unlock_upzones: 2,

View File

@ -9,8 +9,8 @@ mod controls;
mod game;
mod levels;
mod meters;
mod movement;
mod music;
mod player;
mod session;
mod title;
mod vehicles;

View File

@ -22,7 +22,7 @@ impl Vehicle {
normal_speed: Speed::miles_per_hour(30.0),
tired_speed: Speed::miles_per_hour(10.0),
max_energy: 80,
max_energy: 20,
max_boost: Duration::seconds(5.0),
draw_frames: vec!["sleigh.svg"],

View File

@ -519,13 +519,13 @@ impl PolyLine {
.exact_dashed_polygons(width, dash_len, dash_separation)
}
pub fn make_arrow(&self, thickness: Distance, cap: ArrowCap) -> Polygon {
/// Fail if the length is too short.
pub fn maybe_make_arrow(&self, thickness: Distance, cap: ArrowCap) -> Option<Polygon> {
let head_size = thickness * 2.0;
let triangle_height = head_size / 2.0_f64.sqrt();
if self.length() < triangle_height + EPSILON_DIST {
// Just give up and make the thick line.
return self.make_polygons(thickness);
return None;
}
let slice = self.exact_slice(Distance::ZERO, self.length() - triangle_height);
@ -550,7 +550,17 @@ impl PolyLine {
pts.extend(side2);
pts.push(pts[0]);
pts.dedup();
Ring::must_new(pts).to_polygon()
Some(Ring::must_new(pts).to_polygon())
}
/// If the length is too short, just give up and make the thick line
pub fn make_arrow(&self, thickness: Distance, cap: ArrowCap) -> Polygon {
if let Some(p) = self.maybe_make_arrow(thickness, cap) {
p
} else {
// Just give up and make the thick line.
self.make_polygons(thickness)
}
}
pub fn make_double_arrow(&self, thickness: Distance, cap: ArrowCap) -> Polygon {