mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 03:35:51 +03:00
Add a new layer to star buildings. Autosave it. While trying out changes
for #446 in the UI, I kept losing track of the two buildings I'm focusing on. A player-defined list of shortcuts seems generally helpful.
This commit is contained in:
parent
549a625d57
commit
ff598f21a3
@ -209,7 +209,7 @@ impl State<App> for StoryMapEditor {
|
|||||||
// TODO autosave
|
// TODO autosave
|
||||||
let mut choices = Vec::new();
|
let mut choices = Vec::new();
|
||||||
for (name, story) in
|
for (name, story) in
|
||||||
abstio::load_all_objects::<RecordedStoryMap>(abstio::path("player/stories"))
|
abstio::load_all_objects::<RecordedStoryMap>(abstio::path_player("stories"))
|
||||||
{
|
{
|
||||||
if story.name == self.story.name {
|
if story.name == self.story.name {
|
||||||
continue;
|
continue;
|
||||||
@ -415,7 +415,7 @@ impl StoryMap {
|
|||||||
.collect(),
|
.collect(),
|
||||||
};
|
};
|
||||||
abstio::write_json(
|
abstio::write_json(
|
||||||
abstio::path(format!("player/stories/{}.json", story.name)),
|
abstio::path_player(format!("stories/{}.json", story.name)),
|
||||||
&story,
|
&story,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
101
game/src/layer/favorites.rs
Normal file
101
game/src/layer/favorites.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use abstutil::Timer;
|
||||||
|
use map_model::osm::OsmID;
|
||||||
|
use map_model::BuildingID;
|
||||||
|
use widgetry::{
|
||||||
|
Btn, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Panel, RewriteColor,
|
||||||
|
TextExt, VerticalAlignment, Widget,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::layer::{Layer, LayerOutcome};
|
||||||
|
|
||||||
|
/// A set of buildings that the player has starred, persisted as player data.
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Favorites {
|
||||||
|
pub buildings: BTreeSet<OsmID>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Favorites {
|
||||||
|
fn load(app: &App) -> Favorites {
|
||||||
|
abstio::maybe_read_json::<Favorites>(Favorites::path(app), &mut Timer::throwaway())
|
||||||
|
.unwrap_or_else(|_| Favorites {
|
||||||
|
buildings: BTreeSet::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(app: &App) -> String {
|
||||||
|
let name = app.primary.map.get_name();
|
||||||
|
abstio::path_player(format!("favorites/{}/{}.json", name.city, name.map))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(app: &App, b: BuildingID) -> bool {
|
||||||
|
Favorites::load(app)
|
||||||
|
.buildings
|
||||||
|
.contains(&app.primary.map.get_b(b).orig_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(app: &App, b: BuildingID) {
|
||||||
|
let mut faves = Favorites::load(app);
|
||||||
|
faves.buildings.insert(app.primary.map.get_b(b).orig_id);
|
||||||
|
abstio::write_json(Favorites::path(app), &faves);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(app: &App, b: BuildingID) {
|
||||||
|
let mut faves = Favorites::load(app);
|
||||||
|
faves.buildings.remove(&app.primary.map.get_b(b).orig_id);
|
||||||
|
abstio::write_json(Favorites::path(app), &faves);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ShowFavorites {
|
||||||
|
panel: Panel,
|
||||||
|
draw: Drawable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layer for ShowFavorites {
|
||||||
|
fn name(&self) -> Option<&'static str> {
|
||||||
|
Some("favorites")
|
||||||
|
}
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, _: &mut App, minimap: &Panel) -> Option<LayerOutcome> {
|
||||||
|
Layer::simple_event(ctx, minimap, &mut self.panel)
|
||||||
|
}
|
||||||
|
fn draw(&self, g: &mut GfxCtx, _: &App) {
|
||||||
|
self.panel.draw(g);
|
||||||
|
g.redraw(&self.draw);
|
||||||
|
}
|
||||||
|
fn draw_minimap(&self, g: &mut GfxCtx) {
|
||||||
|
g.redraw(&self.draw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShowFavorites {
|
||||||
|
pub fn new(ctx: &mut EventCtx, app: &App) -> ShowFavorites {
|
||||||
|
let mut batch = GeomBatch::new();
|
||||||
|
for orig_id in Favorites::load(app).buildings.into_iter() {
|
||||||
|
if let Some(b) = app.primary.map.find_b_by_osm_id(orig_id) {
|
||||||
|
batch.append(
|
||||||
|
GeomBatch::load_svg(ctx, "system/assets/tools/star.svg")
|
||||||
|
.centered_on(app.primary.map.get_b(b).polygon.center())
|
||||||
|
.color(RewriteColor::ChangeAll(Color::YELLOW)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let panel = Panel::new(Widget::row(vec![
|
||||||
|
Widget::draw_svg(ctx, "system/assets/tools/layers.svg"),
|
||||||
|
"Your favorite buildings".draw_text(ctx),
|
||||||
|
Btn::close(ctx),
|
||||||
|
]))
|
||||||
|
.aligned(HorizontalAlignment::Right, VerticalAlignment::Center)
|
||||||
|
.build(ctx);
|
||||||
|
|
||||||
|
ShowFavorites {
|
||||||
|
panel,
|
||||||
|
draw: ctx.upload(batch),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ use crate::common::hotkey_btn;
|
|||||||
use crate::sandbox::dashboards;
|
use crate::sandbox::dashboards;
|
||||||
|
|
||||||
mod elevation;
|
mod elevation;
|
||||||
|
pub mod favorites;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
mod pandemic;
|
mod pandemic;
|
||||||
mod parking;
|
mod parking;
|
||||||
@ -116,6 +117,7 @@ impl PickLayer {
|
|||||||
btn("transit network", Key::U),
|
btn("transit network", Key::U),
|
||||||
btn("population map", Key::X),
|
btn("population map", Key::X),
|
||||||
btn("no sidewalks", Key::S),
|
btn("no sidewalks", Key::S),
|
||||||
|
btn("favorite buildings", Key::F),
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
.evenly_spaced(),
|
.evenly_spaced(),
|
||||||
@ -183,6 +185,9 @@ impl State<App> for PickLayer {
|
|||||||
"no sidewalks" => {
|
"no sidewalks" => {
|
||||||
app.primary.layer = Some(Box::new(map::Static::no_sidewalks(ctx, app)));
|
app.primary.layer = Some(Box::new(map::Static::no_sidewalks(ctx, app)));
|
||||||
}
|
}
|
||||||
|
"favorite buildings" => {
|
||||||
|
app.primary.layer = Some(Box::new(favorites::ShowFavorites::new(ctx, app)));
|
||||||
|
}
|
||||||
"pandemic model" => {
|
"pandemic model" => {
|
||||||
app.primary.layer = Some(Box::new(pandemic::Pandemic::new(
|
app.primary.layer = Some(Box::new(pandemic::Pandemic::new(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -26,6 +26,7 @@ use crate::edit::{
|
|||||||
can_edit_lane, EditMode, LaneEditor, SaveEdits, StopSignEditor, TrafficSignalEditor,
|
can_edit_lane, EditMode, LaneEditor, SaveEdits, StopSignEditor, TrafficSignalEditor,
|
||||||
};
|
};
|
||||||
use crate::info::ContextualActions;
|
use crate::info::ContextualActions;
|
||||||
|
use crate::layer::favorites::{Favorites, ShowFavorites};
|
||||||
use crate::layer::PickLayer;
|
use crate::layer::PickLayer;
|
||||||
use crate::pregame::MainMenu;
|
use crate::pregame::MainMenu;
|
||||||
|
|
||||||
@ -537,6 +538,13 @@ impl ContextualActions for Actions {
|
|||||||
actions.push((Key::E, "edit lane".to_string()));
|
actions.push((Key::E, "edit lane".to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ID::Building(b) => {
|
||||||
|
if Favorites::contains(app, b) {
|
||||||
|
actions.push((Key::F, "remove this building from favorites".to_string()));
|
||||||
|
} else {
|
||||||
|
actions.push((Key::F, "add this building to favorites".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -582,6 +590,16 @@ impl ContextualActions for Actions {
|
|||||||
Transition::Push(EditMode::new(ctx, app, self.gameplay.clone())),
|
Transition::Push(EditMode::new(ctx, app, self.gameplay.clone())),
|
||||||
Transition::Push(LaneEditor::new(ctx, app, l, self.gameplay.clone())),
|
Transition::Push(LaneEditor::new(ctx, app, l, self.gameplay.clone())),
|
||||||
]),
|
]),
|
||||||
|
(ID::Building(b), "add this building to favorites") => {
|
||||||
|
Favorites::add(app, b);
|
||||||
|
app.primary.layer = Some(Box::new(ShowFavorites::new(ctx, app)));
|
||||||
|
Transition::Keep
|
||||||
|
}
|
||||||
|
(ID::Building(b), "remove this building from favorites") => {
|
||||||
|
Favorites::remove(app, b);
|
||||||
|
app.primary.layer = Some(Box::new(ShowFavorites::new(ctx, app)));
|
||||||
|
Transition::Keep
|
||||||
|
}
|
||||||
(_, "follow (run the simulation)") => {
|
(_, "follow (run the simulation)") => {
|
||||||
*close_panel = false;
|
*close_panel = false;
|
||||||
Transition::ModifyState(Box::new(|state, ctx, app| {
|
Transition::ModifyState(Box::new(|state, ctx, app| {
|
||||||
|
@ -73,7 +73,7 @@ impl<A: AppLike + 'static> State<A> for Picker<A> {
|
|||||||
data_packs.runtime.insert(city);
|
data_packs.runtime.insert(city);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
abstio::write_json(abstio::path("player/data.json"), &data_packs);
|
abstio::write_json(abstio::path_player("data.json"), &data_packs);
|
||||||
|
|
||||||
let messages = ctx.loading_screen("sync files", |_, timer| sync(timer));
|
let messages = ctx.loading_screen("sync files", |_, timer| sync(timer));
|
||||||
return Transition::Multi(vec![
|
return Transition::Multi(vec![
|
||||||
|
@ -61,7 +61,7 @@ impl SimFlags {
|
|||||||
|
|
||||||
let mut opts = self.opts.clone();
|
let mut opts = self.opts.clone();
|
||||||
|
|
||||||
if self.load.starts_with(&abstio::path("player/saves/")) {
|
if self.load.starts_with(&abstio::path_player("saves/")) {
|
||||||
timer.note(format!("Resuming from {}", self.load));
|
timer.note(format!("Resuming from {}", self.load));
|
||||||
|
|
||||||
let sim: Sim = abstio::must_read_object(self.load.clone(), timer);
|
let sim: Sim = abstio::must_read_object(self.load.clone(), timer);
|
||||||
|
Loading…
Reference in New Issue
Block a user