mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-24 23:15:24 +03:00
Add a warning when low on time or blood sugar
This commit is contained in:
parent
57ea1b898d
commit
631b164cf8
@ -2,17 +2,19 @@ use rand::{Rng, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use geom::{Distance, Duration, PolyLine, Pt2D, Time};
|
||||
use widgetry::{Color, Drawable, EventCtx, GeomBatch, GfxCtx};
|
||||
use widgetry::{Color, Drawable, EventCtx, GeomBatch, GfxCtx, RewriteColor};
|
||||
|
||||
pub struct Animator {
|
||||
active: Vec<Animation>,
|
||||
draw_current: Drawable,
|
||||
draw_mapspace: Drawable,
|
||||
draw_screenspace: Option<Drawable>,
|
||||
}
|
||||
|
||||
struct Animation {
|
||||
start: Time,
|
||||
end: Time,
|
||||
effect: Effect,
|
||||
screenspace: bool,
|
||||
}
|
||||
|
||||
pub enum Effect {
|
||||
@ -26,13 +28,19 @@ pub enum Effect {
|
||||
width: Distance,
|
||||
pl: PolyLine,
|
||||
},
|
||||
Flash {
|
||||
orig: GeomBatch,
|
||||
alpha_scale: (f32, f32),
|
||||
cycles: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl Animator {
|
||||
pub fn new(ctx: &EventCtx) -> Animator {
|
||||
Animator {
|
||||
active: Vec::new(),
|
||||
draw_current: Drawable::empty(ctx),
|
||||
draw_mapspace: Drawable::empty(ctx),
|
||||
draw_screenspace: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,6 +50,16 @@ impl Animator {
|
||||
start: now,
|
||||
end: now + duration,
|
||||
effect,
|
||||
screenspace: false,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn add_screenspace(&mut self, now: Time, duration: Duration, effect: Effect) {
|
||||
self.active.push(Animation {
|
||||
start: now,
|
||||
end: now + duration,
|
||||
effect,
|
||||
screenspace: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -49,7 +67,8 @@ impl Animator {
|
||||
if self.active.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut batch = GeomBatch::new();
|
||||
let mut mapspace = GeomBatch::new();
|
||||
let mut screenspace = GeomBatch::new();
|
||||
self.active.retain(|anim| {
|
||||
let pct = (now - anim.start) / (anim.end - anim.start);
|
||||
if pct < 0.0 {
|
||||
@ -58,15 +77,29 @@ impl Animator {
|
||||
} else if pct > 1.0 {
|
||||
false
|
||||
} else {
|
||||
anim.effect.render(pct, &mut batch);
|
||||
if anim.screenspace {
|
||||
anim.effect.render(pct, &mut screenspace);
|
||||
} else {
|
||||
anim.effect.render(pct, &mut mapspace);
|
||||
}
|
||||
true
|
||||
}
|
||||
});
|
||||
self.draw_current = ctx.upload(batch);
|
||||
self.draw_mapspace = ctx.upload(mapspace);
|
||||
if screenspace.is_empty() {
|
||||
self.draw_screenspace = None;
|
||||
} else {
|
||||
self.draw_screenspace = Some(ctx.upload(screenspace));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
g.redraw(&self.draw_current);
|
||||
g.redraw(&self.draw_mapspace);
|
||||
if let Some(ref d) = self.draw_screenspace {
|
||||
g.fork_screenspace();
|
||||
g.redraw(d);
|
||||
g.unfork();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_done(&self) -> bool {
|
||||
@ -94,6 +127,19 @@ impl Effect {
|
||||
batch.push(*color, pl.make_polygons(*width));
|
||||
}
|
||||
}
|
||||
Effect::Flash {
|
||||
ref orig,
|
||||
alpha_scale,
|
||||
cycles,
|
||||
} => {
|
||||
// -1 to 1
|
||||
let shift = (pct * (*cycles as f64) * (2.0 * std::f64::consts::PI)).sin() as f32;
|
||||
let midpt = (alpha_scale.0 + alpha_scale.1) / 2.0;
|
||||
let half_range = (alpha_scale.1 - alpha_scale.0) / 2.0;
|
||||
let alpha = midpt + shift * half_range;
|
||||
|
||||
batch.append(orig.clone().color(RewriteColor::ChangeAlpha(alpha)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -255,6 +255,7 @@ impl Game {
|
||||
let refill = self.state.vehicle.max_energy - self.state.energy;
|
||||
if refill > 0 {
|
||||
self.state.energy += refill;
|
||||
self.state.warned_low_energy = false;
|
||||
let path_speed = Duration::seconds(0.2);
|
||||
self.animator.add(
|
||||
app.time,
|
||||
@ -387,6 +388,69 @@ impl State<App> for Game {
|
||||
);
|
||||
}
|
||||
|
||||
if !self.state.warned_low_time
|
||||
&& self.state.level.time_limit - (app.time - Time::START_OF_DAY)
|
||||
<= Duration::seconds(20.0)
|
||||
{
|
||||
self.state.warned_low_time = true;
|
||||
self.animator.add(
|
||||
app.time,
|
||||
Duration::seconds(2.0),
|
||||
Effect::Flash {
|
||||
alpha_scale: (0.1, 0.5),
|
||||
cycles: 2,
|
||||
orig: GeomBatch::from(vec![(
|
||||
Color::RED,
|
||||
app.map.get_boundary_polygon().clone(),
|
||||
)]),
|
||||
},
|
||||
);
|
||||
self.animator.add_screenspace(
|
||||
app.time,
|
||||
Duration::seconds(2.0),
|
||||
Effect::Scale {
|
||||
lerp_scale: (1.0, 4.0),
|
||||
center: {
|
||||
let pt = ctx.canvas.center_to_screen_pt();
|
||||
Pt2D::new(pt.x, pt.y / 2.0)
|
||||
},
|
||||
orig: Text::from(Line("Almost out of time!"))
|
||||
.bg(Color::RED)
|
||||
.render_autocropped(ctx),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if !self.state.warned_low_energy && self.state.energy < 30 {
|
||||
self.state.warned_low_energy = true;
|
||||
self.animator.add(
|
||||
app.time,
|
||||
Duration::seconds(2.0),
|
||||
Effect::Flash {
|
||||
alpha_scale: (0.1, 0.5),
|
||||
cycles: 2,
|
||||
orig: GeomBatch::from(vec![(
|
||||
Color::RED,
|
||||
app.map.get_boundary_polygon().clone(),
|
||||
)]),
|
||||
},
|
||||
);
|
||||
self.animator.add_screenspace(
|
||||
app.time,
|
||||
Duration::seconds(2.0),
|
||||
Effect::Scale {
|
||||
lerp_scale: (1.0, 4.0),
|
||||
center: {
|
||||
let pt = ctx.canvas.center_to_screen_pt();
|
||||
Pt2D::new(pt.x, pt.y / 2.0)
|
||||
},
|
||||
orig: Text::from(Line("Low on blood sugar, refill soon!"))
|
||||
.bg(Color::RED)
|
||||
.render_autocropped(ctx),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
ctx.request_update(UpdateType::Game);
|
||||
return Transition::Keep;
|
||||
}
|
||||
@ -504,6 +568,8 @@ struct GameState {
|
||||
idle_time: Duration,
|
||||
|
||||
game_over: bool,
|
||||
warned_low_time: bool,
|
||||
warned_low_energy: bool,
|
||||
}
|
||||
|
||||
impl GameState {
|
||||
@ -524,6 +590,8 @@ impl GameState {
|
||||
idle_time: Duration::ZERO,
|
||||
|
||||
game_over: false,
|
||||
warned_low_time: false,
|
||||
warned_low_energy: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ impl Vehicle {
|
||||
name: "bike".to_string(),
|
||||
|
||||
speed: Speed::miles_per_hour(30.0),
|
||||
max_energy: 100,
|
||||
max_energy: 50,
|
||||
|
||||
draw_frames: vec!["bike1.svg", "bike2.svg", "bike1.svg", "bike3.svg"],
|
||||
scale: 0.05,
|
||||
|
@ -123,7 +123,7 @@ impl GeomBatch {
|
||||
}
|
||||
|
||||
/// True when the batch is empty.
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.list.is_empty()
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user