diff --git a/data/system/assets/characters/santa.svg b/data/system/assets/characters/santa.svg
new file mode 100644
index 0000000000..81df290b6d
--- /dev/null
+++ b/data/system/assets/characters/santa.svg
@@ -0,0 +1,961 @@
+
+
+
+
\ No newline at end of file
diff --git a/experiment/santa.svg b/experiment/santa.svg
new file mode 100644
index 0000000000..b7053cb514
--- /dev/null
+++ b/experiment/santa.svg
@@ -0,0 +1,621 @@
+
+
+
diff --git a/experiment/santa2.svg b/experiment/santa2.svg
new file mode 100644
index 0000000000..81df290b6d
--- /dev/null
+++ b/experiment/santa2.svg
@@ -0,0 +1,961 @@
+
+
+
+
\ No newline at end of file
diff --git a/experiment/src/controls.rs b/experiment/src/controls.rs
index 0bd1937806..c4e3ad2d9d 100644
--- a/experiment/src/controls.rs
+++ b/experiment/src/controls.rs
@@ -1,82 +1,61 @@
-use geom::{Angle, Pt2D, Speed};
+use geom::{Angle, Speed};
use widgetry::{EventCtx, Key};
// TODO The timestep accumulation seems fine. What's wrong? Clamping errors repeated?
const HACK: f64 = 5.0;
-pub trait Controller {
- fn displacement(&mut self, ctx: &mut EventCtx, speed: Speed) -> (f64, f64);
+pub struct InstantController {
+ /// Which of the 8 directions are we facing, based on the last set of keys pressed down?
+ pub facing: Angle,
}
-pub struct InstantController;
-
impl InstantController {
pub fn new() -> InstantController {
- InstantController
+ InstantController {
+ facing: Angle::ZERO,
+ }
}
-}
-impl Controller for InstantController {
- fn displacement(&mut self, ctx: &mut EventCtx, speed: Speed) -> (f64, f64) {
+ pub fn displacement(&mut self, ctx: &mut EventCtx, speed: Speed) -> (f64, f64) {
let mut dx = 0.0;
let mut dy = 0.0;
if let Some(dt) = ctx.input.nonblocking_is_update_event() {
let dist = (dt * HACK * speed).inner_meters();
+ let mut x = 0;
+ let mut y = 0;
+ // The x direction is inverted; somehow the usual Y inversion did this? Oh well,
+ // it's isolated here nicely.
if ctx.is_key_down(Key::LeftArrow) {
dx -= dist;
+ x = 1;
}
if ctx.is_key_down(Key::RightArrow) {
dx += dist;
+ x = -1;
}
if ctx.is_key_down(Key::UpArrow) {
dy -= dist;
+ y = -1;
}
if ctx.is_key_down(Key::DownArrow) {
dy += dist;
+ y = 1;
}
- }
-
- (dx, dy)
- }
-}
-
-pub struct RotateController {
- angle: Angle,
-}
-
-impl RotateController {
- #[allow(unused)]
- pub fn new() -> RotateController {
- RotateController { angle: Angle::ZERO }
- }
-}
-
-impl Controller for RotateController {
- fn displacement(&mut self, ctx: &mut EventCtx, fwd_speed: Speed) -> (f64, f64) {
- let rot_speed_degrees = 100.0;
-
- let mut dx = 0.0;
- let mut dy = 0.0;
-
- if let Some(dt) = ctx.input.nonblocking_is_update_event() {
- if ctx.is_key_down(Key::LeftArrow) {
- self.angle = self
- .angle
- .rotate_degs(-rot_speed_degrees * dt.inner_seconds());
- }
- if ctx.is_key_down(Key::RightArrow) {
- self.angle = self
- .angle
- .rotate_degs(rot_speed_degrees * dt.inner_seconds());
- }
-
- if ctx.is_key_down(Key::UpArrow) {
- let dist = dt * HACK * fwd_speed;
- let pt = Pt2D::new(0.0, 0.0).project_away(dist, self.angle);
- dx = pt.x();
- dy = pt.y();
- }
+
+ // TODO Better way to do this; acos and asin?
+ self.facing = match (x, y) {
+ (-1, -1) => Angle::degrees(135.0),
+ (-1, 0) => Angle::degrees(180.0),
+ (-1, 1) => Angle::degrees(225.0),
+ (0, -1) => Angle::degrees(90.0),
+ (0, 1) => Angle::degrees(270.0),
+ (1, -1) => Angle::degrees(45.0),
+ (1, 0) => Angle::degrees(0.0),
+ (1, 1) => Angle::degrees(315.0),
+ (0, 0) => self.facing,
+ _ => unreachable!(),
+ };
}
(dx, dy)
diff --git a/experiment/src/game.rs b/experiment/src/game.rs
index 56e031f3b4..21bb5615bc 100644
--- a/experiment/src/game.rs
+++ b/experiment/src/game.rs
@@ -13,14 +13,14 @@ use widgetry::{
};
use crate::animation::{Animator, SnowEffect};
-use crate::controls::{Controller, InstantController};
+use crate::controls::InstantController;
use crate::levels::Config;
const ZOOM: f64 = 10.0;
pub struct Game {
panel: Panel,
- controls: Box,
+ controls: InstantController,
minimap: SimpleMinimap,
animator: Animator,
snow: SnowEffect,
@@ -69,7 +69,7 @@ impl Game {
let with_zorder = false;
let mut game = Game {
panel,
- controls: Box::new(InstantController::new()),
+ controls: InstantController::new(),
minimap: SimpleMinimap::new(ctx, app, with_zorder),
animator: Animator::new(ctx),
snow: SnowEffect::new(ctx),
@@ -266,10 +266,12 @@ impl State for Game {
if !self.state.has_energy() {
g.redraw(&self.state.draw_all_depots);
}
- g.draw_polygon(
- Color::RED,
- Circle::new(self.sleigh, Distance::meters(5.0)).to_polygon(),
- );
+
+ GeomBatch::load_svg(g.prerender, "system/assets/characters/santa.svg")
+ .scale(0.1)
+ .centered_on(self.sleigh)
+ .rotate_around_batch_center(self.controls.facing)
+ .draw(g);
self.snow.draw(g);
self.animator.draw(g);
if let Some(ref arrow) = self.state.energyless_arrow {
diff --git a/experiment/src/levels.rs b/experiment/src/levels.rs
index a551bb2137..9dcc21ac25 100644
--- a/experiment/src/levels.rs
+++ b/experiment/src/levels.rs
@@ -1,6 +1,6 @@
use abstutil::MapName;
use geom::{Duration, Speed};
-use map_gui::tools::PopupMsg;
+use map_gui::tools::{open_browser, PopupMsg};
use map_gui::SimpleApp;
use map_model::osm;
use widgetry::{
@@ -70,6 +70,11 @@ impl TitleScreen {
txt.add(Line("An experiment"));
txt.draw(ctx).centered_horiz()
},
+ Btn::text_fg("Santa character created by @parallaxcreativedesign").build(
+ ctx,
+ "open https://www.instagram.com/parallaxcreativedesign/",
+ None,
+ ),
Btn::text_bg2("Instructions").build_def(ctx, None),
Widget::row(
levels
@@ -127,6 +132,11 @@ impl State for TitleScreen {
));
}
x => {
+ if let Some(url) = x.strip_prefix("open ") {
+ open_browser(url.to_string());
+ return Transition::Keep;
+ }
+
for lvl in all_levels() {
if x == lvl.title {
return Transition::Push(crate::game::Game::new(ctx, app, lvl));