diff --git a/game/assets/speed/small_step.svg b/game/assets/speed/small_step.svg
deleted file mode 100644
index f68640e3af..0000000000
--- a/game/assets/speed/small_step.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/game/src/sandbox/mod.rs b/game/src/sandbox/mod.rs
index 656fe85315..369bdfef70 100644
--- a/game/src/sandbox/mod.rs
+++ b/game/src/sandbox/mod.rs
@@ -74,12 +74,8 @@ impl SandboxMode {
None
},
gameplay: gameplay::GameplayRunner::initialize(mode, ui, ctx),
- menu: ModalMenu::new(
- "Sandbox Mode",
- vec![(lctrl(Key::E), "edit mode"), (hotkey(Key::X), "reset sim")],
- ctx,
- )
- .disable_standalone_layout(),
+ menu: ModalMenu::new("Sandbox Mode", vec![(lctrl(Key::E), "edit mode")], ctx)
+ .disable_standalone_layout(),
}
}
}
@@ -257,7 +253,7 @@ impl State for SandboxMode {
}
}
- if let Some(t) = self.speed.event(ctx, ui) {
+ if let Some(t) = self.speed.event(ctx, ui, &self.gameplay.mode) {
return t;
}
@@ -266,15 +262,6 @@ impl State for SandboxMode {
return Transition::Replace(Box::new(EditMode::new(ctx, self.gameplay.mode.clone())));
}
if self.speed.is_paused() {
- if !ui.primary.sim.is_empty() && self.menu.action("reset sim") {
- ui.primary.clear_sim();
- return Transition::Replace(Box::new(SandboxMode::new(
- ctx,
- ui,
- self.gameplay.mode.clone(),
- )));
- }
-
Transition::Keep
} else {
Transition::KeepWithMode(EventLoopMode::Animation)
diff --git a/game/src/sandbox/speed.rs b/game/src/sandbox/speed.rs
index 10148d6ad5..0640ba7eb6 100644
--- a/game/src/sandbox/speed.rs
+++ b/game/src/sandbox/speed.rs
@@ -1,8 +1,9 @@
use crate::game::{State, Transition, WizardState};
+use crate::sandbox::{GameplayMode, SandboxMode};
use crate::ui::UI;
use ezgui::layout::Widget;
use ezgui::{
- hotkey, Button, Color, DrawBoth, EventCtx, EventLoopMode, GeomBatch, GfxCtx,
+ hotkey, layout, Button, Color, DrawBoth, EventCtx, EventLoopMode, GeomBatch, GfxCtx,
HorizontalAlignment, Key, Line, RewriteColor, ScreenPt, ScreenRectangle, Slider, Text,
VerticalAlignment, Wizard,
};
@@ -11,18 +12,25 @@ use std::time::Instant;
// Layouting is very hardcoded right now.
-const PANEL_WIDTH: f64 = 379.0;
-const PANEL_HEIGHT: f64 = 166.0;
+const TIME_PANEL_WIDTH: f64 = 340.0;
+const TIME_PANEL_HEIGHT: f64 = 130.0;
+
+const SPEED_PANEL_WIDTH: f64 = 650.0;
+const SPEED_PANEL_HEIGHT: f64 = 50.0;
const ADJUST_SPEED_PERCENT: f64 = 0.01;
pub struct SpeedControls {
+ time_panel: TimePanel,
+ top_left: ScreenPt,
+
draw_fixed: DrawBoth,
resume_btn: Button,
pause_btn: Button,
jump_to_time_btn: Button,
small_step_btn: Button,
large_step_btn: Button,
+ reset_btn: Button,
speed_slider: Slider,
slow_down_btn: Button,
speed_up_btn: Button,
@@ -47,59 +55,47 @@ impl SpeedControls {
let mut batch = GeomBatch::new();
let mut txt = Vec::new();
- // Panel background
+ // Speed panel
batch.push(
Color::hex("#4C4C4C"),
Polygon::rounded_rectangle(
- Distance::meters(PANEL_WIDTH),
- Distance::meters(PANEL_HEIGHT),
+ Distance::meters(SPEED_PANEL_WIDTH),
+ Distance::meters(SPEED_PANEL_HEIGHT),
Distance::meters(5.0),
),
);
- // Row 3 of labels for the time slider
- txt.push((
- Text::from(Line("00:00").size(12).roboto()).no_bg(),
- ScreenPt::new(25.0, 97.0),
- ));
- batch.add_svg("assets/speed/sunrise.svg", 94.0, 94.0);
- txt.push((
- Text::from(Line("12:00").size(12).roboto()).no_bg(),
- ScreenPt::new(153.0, 97.0),
- ));
- batch.add_svg("assets/speed/sunset.svg", 220.0, 94.0);
- txt.push((
- Text::from(Line("24:00").size(12).roboto()).no_bg(),
- ScreenPt::new(280.0, 97.0),
- ));
-
- // Speed panel
+ // Slider background
+ // TODO Figure these out automatically...
batch.push(
Color::grey(0.5),
Polygon::rounded_rectangle(
- Distance::meters(331.0),
+ Distance::meters(310.0),
Distance::meters(22.0),
Distance::meters(5.0),
)
- .translate(24.0, 128.0),
+ .translate(330.0, 10.0),
);
txt.push((
Text::from(Line("speed").size(14).roboto()).no_bg(),
- ScreenPt::new(32.0, 131.0),
+ ScreenPt::new(330.0, 15.0),
));
DrawBoth::new(ctx, batch, txt)
};
- // Row 1
- let resume_btn = Button::rectangle_svg(
+ let top_left = ScreenPt::new(
+ (ctx.canvas.window_width - SPEED_PANEL_WIDTH) / 2.0,
+ ctx.canvas.window_height - SPEED_PANEL_HEIGHT - 50.0,
+ );
+
+ let mut resume_btn = Button::rectangle_svg(
"assets/speed/resume.svg",
"resume",
hotkey(Key::Space),
RewriteColor::ChangeAll(Color::ORANGE),
ctx,
- )
- .at(ScreenPt::new(23.0, 14.0));
+ );
let pause_btn = Button::rectangle_svg(
"assets/speed/pause.svg",
"pause",
@@ -107,45 +103,53 @@ impl SpeedControls {
RewriteColor::ChangeAll(Color::ORANGE),
ctx,
)
- .at(ScreenPt::new(23.0, 14.0));
-
- let jump_to_time_btn = Button::rectangle_svg(
+ .at(top_left);
+ let mut jump_to_time_btn = Button::rectangle_svg(
"assets/speed/jump_to_time.svg",
"jump to specific time",
hotkey(Key::B),
RewriteColor::ChangeAll(Color::ORANGE),
ctx,
- )
- .at(ScreenPt::new(300.0, 20.0));
-
- // Row 2
-
- let small_step_btn = Button::rectangle_svg(
- "assets/speed/small_step.svg",
- "step forwards 0.1s",
+ );
+ let mut small_step_btn = Button::text(
+ Text::from(Line("+0.1s").fg(Color::WHITE).size(12)),
+ Color::WHITE.alpha(0.0),
+ Color::ORANGE,
hotkey(Key::M),
- RewriteColor::ChangeAll(Color::ORANGE),
ctx,
- )
- .at(ScreenPt::new(315.0, 60.0));
-
- let large_step_btn = Button::rectangle_svg(
+ );
+ let mut large_step_btn = Button::rectangle_svg(
"assets/speed/large_step.svg",
"step forwards 10 mins",
hotkey(Key::N),
RewriteColor::ChangeAll(Color::ORANGE),
ctx,
- )
- .at(ScreenPt::new(315.0, 75.0));
-
- // Row 4
+ );
+ let mut reset_btn = Button::text(
+ Text::from(Line("reset").fg(Color::WHITE).size(12)),
+ Color::WHITE.alpha(0.0),
+ Color::ORANGE,
+ hotkey(Key::X),
+ ctx,
+ );
+ layout::stack_horizontally(
+ ScreenPt::new(top_left.x, top_left.y + 5.0),
+ 10.0,
+ vec![
+ &mut resume_btn,
+ &mut jump_to_time_btn,
+ &mut small_step_btn,
+ &mut large_step_btn,
+ &mut reset_btn,
+ ],
+ );
// 10 sim minutes / real second normally, or 1 sim hour / real second for dev mode
let speed_cap: f64 = if dev_mode { 3600.0 } else { 600.0 };
let mut speed_slider = Slider::new(157.0, 10.0);
// Start with speed=1.0
speed_slider.set_percent(ctx, (speed_cap / 1.0).powf(-1.0 / std::f64::consts::E));
- speed_slider.set_pos(ScreenPt::new(92.0, 134.0));
+ speed_slider.set_pos(ScreenPt::new(top_left.x + 350.0, top_left.y + 15.0));
let slow_down_btn = Button::rectangle_svg(
"assets/speed/slow_down.svg",
@@ -154,7 +158,7 @@ impl SpeedControls {
RewriteColor::ChangeAll(Color::ORANGE),
ctx,
)
- .at(ScreenPt::new(245.0, 129.0));
+ .at(ScreenPt::new(top_left.x + 500.0, top_left.y + 10.0));
let speed_up_btn = Button::rectangle_svg(
"assets/speed/speed_up.svg",
"speed up",
@@ -162,15 +166,19 @@ impl SpeedControls {
RewriteColor::ChangeAll(Color::ORANGE),
ctx,
)
- .at(ScreenPt::new(330.0, 129.0));
+ .at(ScreenPt::new(top_left.x + 600.0, top_left.y + 10.0));
SpeedControls {
+ time_panel: TimePanel::new(ctx),
+ top_left,
+
draw_fixed,
resume_btn,
pause_btn,
jump_to_time_btn,
small_step_btn,
large_step_btn,
+ reset_btn,
speed_slider,
slow_down_btn,
speed_up_btn,
@@ -180,7 +188,12 @@ impl SpeedControls {
}
}
- pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Option {
+ pub fn event(
+ &mut self,
+ ctx: &mut EventCtx,
+ ui: &mut UI,
+ gameplay: &GameplayMode,
+ ) -> Option {
self.slow_down_btn.event(ctx);
self.speed_up_btn.event(ctx);
@@ -268,19 +281,29 @@ impl SpeedControls {
return Some(Transition::Push(WizardState::new(Box::new(jump_to_time))));
}
+ self.reset_btn.event(ctx);
+ if self.reset_btn.clicked() {
+ ui.primary.clear_sim();
+ return Some(Transition::Replace(Box::new(SandboxMode::new(
+ ctx,
+ ui,
+ gameplay.clone(),
+ ))));
+ }
+
None
}
pub fn draw(&self, g: &mut GfxCtx, ui: &UI) {
- self.draw_fixed.redraw(ScreenPt::new(0.0, 0.0), g);
- g.canvas.mark_covered_area(ScreenRectangle {
- x1: 0.0,
- y1: 0.0,
- x2: PANEL_WIDTH,
- y2: PANEL_HEIGHT,
- });
+ self.time_panel.draw(g, ui);
- // Row 1
+ self.draw_fixed.redraw(self.top_left, g);
+ g.canvas.mark_covered_area(ScreenRectangle {
+ x1: self.top_left.x,
+ y1: self.top_left.y,
+ x2: self.top_left.x + SPEED_PANEL_WIDTH,
+ y2: self.top_left.y + SPEED_PANEL_HEIGHT,
+ });
if self.is_paused() {
self.resume_btn.draw(g);
@@ -288,40 +311,11 @@ impl SpeedControls {
self.pause_btn.draw(g);
}
- g.draw_text_at_screenspace_topleft(
- &Text::from(Line(ui.primary.sim.time().ampm_tostring()).size(30)).no_bg(),
- ScreenPt::new(86.0, 19.0),
- );
-
self.jump_to_time_btn.draw(g);
- // Row 2
-
- // TODO Actual slider
- {
- let x1 = 24.0;
- let y1 = 75.0;
- let percent = ui.primary.sim.time().to_percent(Time::END_OF_DAY);
- let width = 287.0;
- let height = Distance::meters(15.0);
-
- g.fork_screenspace();
- // TODO rounded
- g.draw_polygon(
- Color::WHITE,
- &Line::new(Pt2D::new(x1, y1), Pt2D::new(x1 + width, y1)).make_polygons(height),
- );
- if let Some(l) = Line::maybe_new(Pt2D::new(x1, y1), Pt2D::new(x1 + percent * width, y1))
- {
- g.draw_polygon(Color::grey(0.5), &l.make_polygons(height));
- }
- g.unfork();
- }
-
self.small_step_btn.draw(g);
self.large_step_btn.draw(g);
-
- // Row 4
+ self.reset_btn.draw(g);
{
self.speed_slider.draw(g);
@@ -336,7 +330,7 @@ impl SpeedControls {
.roboto(),
)
.no_bg(),
- ScreenPt::new(275.0, 131.0),
+ ScreenPt::new(self.top_left.x + 530.0, self.top_left.y + 10.0),
);
self.speed_up_btn.draw(g);
@@ -423,3 +417,78 @@ impl State for TimeWarpScreen {
);
}
}
+
+struct TimePanel {
+ draw_fixed: DrawBoth,
+}
+
+impl TimePanel {
+ fn new(ctx: &mut EventCtx) -> TimePanel {
+ let mut batch = GeomBatch::new();
+ let mut txt = Vec::new();
+
+ // Time panel background
+ batch.push(
+ Color::hex("#4C4C4C"),
+ Polygon::rounded_rectangle(
+ Distance::meters(TIME_PANEL_WIDTH),
+ Distance::meters(TIME_PANEL_HEIGHT),
+ Distance::meters(5.0),
+ ),
+ );
+
+ txt.push((
+ Text::from(Line("00:00").size(12).roboto()).no_bg(),
+ ScreenPt::new(25.0, 80.0),
+ ));
+ batch.add_svg("assets/speed/sunrise.svg", 94.0, 80.0);
+ txt.push((
+ Text::from(Line("12:00").size(12).roboto()).no_bg(),
+ ScreenPt::new(153.0, 80.0),
+ ));
+ batch.add_svg("assets/speed/sunset.svg", 220.0, 80.0);
+ txt.push((
+ Text::from(Line("24:00").size(12).roboto()).no_bg(),
+ ScreenPt::new(280.0, 80.0),
+ ));
+
+ TimePanel {
+ draw_fixed: DrawBoth::new(ctx, batch, txt),
+ }
+ }
+
+ fn draw(&self, g: &mut GfxCtx, ui: &UI) {
+ self.draw_fixed.redraw(ScreenPt::new(0.0, 0.0), g);
+ g.canvas.mark_covered_area(ScreenRectangle {
+ x1: 0.0,
+ y1: 0.0,
+ x2: TIME_PANEL_WIDTH,
+ y2: TIME_PANEL_HEIGHT,
+ });
+
+ g.draw_text_at_screenspace_topleft(
+ &Text::from(Line(ui.primary.sim.time().ampm_tostring()).size(30)).no_bg(),
+ ScreenPt::new(24.0, 10.0),
+ );
+
+ {
+ let x1 = 24.0;
+ let y1 = 65.0;
+ let percent = ui.primary.sim.time().to_percent(Time::END_OF_DAY);
+ let width = 287.0;
+ let height = Distance::meters(15.0);
+
+ g.fork_screenspace();
+ // TODO rounded
+ g.draw_polygon(
+ Color::WHITE,
+ &Line::new(Pt2D::new(x1, y1), Pt2D::new(x1 + width, y1)).make_polygons(height),
+ );
+ if let Some(l) = Line::maybe_new(Pt2D::new(x1, y1), Pt2D::new(x1 + percent * width, y1))
+ {
+ g.draw_polygon(Color::grey(0.5), &l.make_polygons(height));
+ }
+ g.unfork();
+ }
+ }
+}