mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-30 18:24:04 +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
|
||||
let mut choices = Vec::new();
|
||||
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 {
|
||||
continue;
|
||||
@ -415,7 +415,7 @@ impl StoryMap {
|
||||
.collect(),
|
||||
};
|
||||
abstio::write_json(
|
||||
abstio::path(format!("player/stories/{}.json", story.name)),
|
||||
abstio::path_player(format!("stories/{}.json", story.name)),
|
||||
&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;
|
||||
|
||||
mod elevation;
|
||||
pub mod favorites;
|
||||
pub mod map;
|
||||
mod pandemic;
|
||||
mod parking;
|
||||
@ -116,6 +117,7 @@ impl PickLayer {
|
||||
btn("transit network", Key::U),
|
||||
btn("population map", Key::X),
|
||||
btn("no sidewalks", Key::S),
|
||||
btn("favorite buildings", Key::F),
|
||||
]),
|
||||
])
|
||||
.evenly_spaced(),
|
||||
@ -183,6 +185,9 @@ impl State<App> for PickLayer {
|
||||
"no sidewalks" => {
|
||||
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" => {
|
||||
app.primary.layer = Some(Box::new(pandemic::Pandemic::new(
|
||||
ctx,
|
||||
|
@ -26,6 +26,7 @@ use crate::edit::{
|
||||
can_edit_lane, EditMode, LaneEditor, SaveEdits, StopSignEditor, TrafficSignalEditor,
|
||||
};
|
||||
use crate::info::ContextualActions;
|
||||
use crate::layer::favorites::{Favorites, ShowFavorites};
|
||||
use crate::layer::PickLayer;
|
||||
use crate::pregame::MainMenu;
|
||||
|
||||
@ -537,6 +538,13 @@ impl ContextualActions for Actions {
|
||||
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(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)") => {
|
||||
*close_panel = false;
|
||||
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);
|
||||
}
|
||||
}
|
||||
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));
|
||||
return Transition::Multi(vec![
|
||||
|
@ -61,7 +61,7 @@ impl SimFlags {
|
||||
|
||||
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));
|
||||
|
||||
let sim: Sim = abstio::must_read_object(self.load.clone(), timer);
|
||||
|
Loading…
Reference in New Issue
Block a user