mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
adding a cutscene to a challenge mode
This commit is contained in:
parent
aa765f70ed
commit
1060ed208f
@ -69,8 +69,10 @@ Watching the Mercer backup start at 4am:
|
||||
See the [roadmap](docs/roadmap.md) for current work, including ways to help. If
|
||||
you want to bring this to your city or if you're skilled in design, traffic
|
||||
simulation, data visualization, or civic/government outreach, please contact
|
||||
Dustin Carlino at <dabreegster@gmail.com> or post at
|
||||
[r/abstreet](https://www.reddit.com/r/abstreet/).
|
||||
Dustin Carlino at <dabreegster@gmail.com>. Follow
|
||||
[r/abstreet](https://www.reddit.com/r/abstreet/) for weekly updates or
|
||||
[@CarlinoDustin](https://twitter.com/CarlinoDustin) for occasional videos of
|
||||
recent progress.
|
||||
|
||||
## Project mission
|
||||
|
||||
|
@ -173,6 +173,8 @@ cddd0aa7a98d4d511138befe8f56a6b7 data/system/assets/tools/delete.svg
|
||||
54a0052191997bae5237b67318555307 data/system/assets/tools/home.svg
|
||||
db1bfe4e2c3331cd2a8cbea30127ddc4 data/system/assets/tools/edit_map.svg
|
||||
9186d38868a7b02d97d21c4521ad581f data/system/assets/tools/info.svg
|
||||
4cdb2133662f1995ba50d36587acf550 data/system/assets/characters/boss.svg
|
||||
ed52d1380a29378419852d43b04d0d90 data/system/assets/characters/player.svg
|
||||
c2d2cf72d0058b898fb508c6ea175cf1 data/system/assets/map/parking.svg
|
||||
129a465d2ee86e4d6d568d1e58d8bce7 data/system/assets/map/dont_walk.svg
|
||||
c3fa1600a398928d248cf5d96f63d294 data/system/assets/map/parked_car.svg
|
||||
|
16
data/system/assets/characters/boss.svg
Normal file
16
data/system/assets/characters/boss.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<svg width="99" height="142" viewBox="0 0 99 142" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M50.7827 30.994C59.3414 30.994 66.2797 24.0558 66.2797 15.497C66.2797 6.93825 59.3414 0 50.7827 0C42.2239 0 35.2856 6.93825 35.2856 15.497C35.2856 24.0558 42.2239 30.994 50.7827 30.994Z" fill="#999999"/>
|
||||
<path d="M50.9117 96.7638C61.8643 96.7638 70.7431 82.3566 70.7431 64.5845C70.7431 46.8124 61.8643 32.4053 50.9117 32.4053C39.9591 32.4053 31.0803 46.8124 31.0803 64.5845C31.0803 82.3566 39.9591 96.7638 50.9117 96.7638Z" fill="#999999"/>
|
||||
<path d="M10.2513 30.8866C15.9129 38.899 24.4253 46.9956 29.2643 48.971C34.1034 50.9463 33.4365 46.0523 27.7749 38.0399C22.1133 30.0275 13.6009 21.9309 8.76185 19.9556C3.92283 17.9803 4.58966 22.8742 10.2513 30.8866Z" fill="#999999"/>
|
||||
<path d="M85.6429 22.4936C78.4654 29.1821 71.5882 38.7068 70.2821 43.7677C68.9761 48.8285 73.7358 47.5091 80.9132 40.8206C88.0907 34.1321 94.9679 24.6073 96.274 19.5465C97.5801 14.4856 92.8203 15.8051 85.6429 22.4936Z" fill="#999999"/>
|
||||
<path d="M58.4679 107.8C59.0294 117.595 61.9649 128.97 65.0245 133.208C68.0841 137.445 70.1092 132.94 69.5477 123.146C68.9862 113.351 66.0507 101.975 62.9911 97.7379C59.9315 93.5003 57.9064 98.0053 58.4679 107.8Z" fill="#999999"/>
|
||||
<path d="M35.8741 106.726C32.8924 116.073 31.5616 127.746 32.9017 132.798C34.2417 137.85 37.7452 134.368 40.7269 125.021C43.7086 115.674 45.0394 104.002 43.6994 98.95C42.3593 93.8981 38.8558 97.3797 35.8741 106.726Z" fill="#999999"/>
|
||||
<path d="M47.1581 30.4208C42.0767 29.1887 37.6889 25.0606 36.0632 19.9827C35.3347 17.7074 35.2777 13.6416 35.9426 11.3837C37.5034 6.08375 41.9991 1.78112 47.2763 0.53688C55.1613 -1.32221 63.2983 3.48845 65.6233 11.3837C66.2882 13.6416 66.2312 17.7074 65.5028 19.9827C64.3424 23.6072 61.5501 27.0568 58.247 28.9467C55.2739 30.6477 50.6362 31.2642 47.1581 30.4208V30.4208Z" fill="#808000"/>
|
||||
<path d="M48.6662 96.4583C46.6902 96.0305 44.5832 95.0949 43.0505 93.9647C35.051 88.0659 30.4257 75.0848 31.354 61.1378C32.2764 47.2796 38.5173 36.1203 46.9891 33.1807C49.273 32.3882 52.516 32.3912 54.8412 33.188C61.3118 35.4051 66.6343 42.5897 69.1734 52.5344C70.9431 59.4656 70.9954 69.4051 69.2978 76.1432C68.0454 81.1138 66.0769 85.6562 63.7808 88.8739C62.3575 90.8685 59.4107 93.6889 57.7065 94.6876C55.0212 96.2613 51.207 97.0084 48.6662 96.4583V96.4583Z" fill="#FF6600"/>
|
||||
<path d="M46.4173 30.0665C43.7838 29.2141 42.1067 28.1674 40.0584 26.0977C37.7646 23.78 36.5123 21.611 35.9581 18.9957C34.8748 13.8834 36.0943 9.15365 39.4512 5.44824C45.5263 -1.25764 55.132 -1.50605 61.5259 4.87737C64.5403 7.88681 65.9965 11.2873 65.9965 15.3169C65.9965 19.7168 64.6302 22.9536 61.4023 26.2005C59.1046 28.5117 57.5318 29.4499 54.7622 30.161C52.5777 30.722 48.2925 30.6734 46.4173 30.0665V30.0665Z" fill="#008000"/>
|
||||
<path d="M47.1597 95.911C41.5222 93.9708 36.4517 87.9089 33.7762 79.9108C29.9123 68.3597 30.7391 54.3329 35.8765 44.2801C42.0096 32.2788 52.2867 29.229 60.5599 36.9552C67.5291 43.4637 71.3342 55.9281 70.2612 68.7342C69.8749 73.3444 69.4297 75.589 68.1172 79.5436C67.0057 82.8926 65.4293 86.2258 63.8714 88.5214C62.4546 90.609 58.6208 94.1516 56.8945 94.9682C53.6338 96.5108 49.9416 96.8684 47.1597 95.911V95.911Z" fill="#008000"/>
|
||||
<path d="M29.6782 48.9727C24.7898 47.2284 15.2128 38.0574 9.76588 29.9046C6.52048 25.0469 5.02126 21.2173 5.9343 20.1171C8.44362 17.0936 23.2239 30.2695 29.8004 41.3926C32.0993 45.2808 32.8912 48.0231 31.9786 48.9357C31.4789 49.4354 30.9967 49.4431 29.6782 48.9727V48.9727Z" fill="#008000"/>
|
||||
<path d="M70.4934 46.3075C69.4887 45.097 71.3128 40.5011 74.8602 35.3048C80.4476 27.1204 89.072 18.6727 93.6095 16.9398C97.8081 15.3363 97.1137 19.9186 91.9887 27.635C84.9518 38.2302 72.655 48.9121 70.4934 46.3075Z" fill="#008000"/>
|
||||
<path d="M33.7622 134.412C32.8621 133.268 32.5406 131.262 32.575 127.007C32.6594 116.542 36.596 101.666 40.3155 97.7556C41.6967 96.3036 42.3578 96.3941 43.23 98.1544C43.8083 99.3216 43.8911 99.8867 43.989 103.333C44.2974 114.19 40.0122 130.695 35.9618 134.251C34.8241 135.25 34.4433 135.278 33.7622 134.412V134.412Z" fill="#008000"/>
|
||||
<path d="M66.6038 134.535C64.5472 133.737 61.7901 127.082 60.0871 118.805C58.935 113.206 58.7209 111.091 58.7207 105.311C58.7205 99.3335 58.9153 98.0711 60.037 96.7823C60.6451 96.0837 60.7072 96.0687 61.407 96.4519C64.2085 97.9859 67.2635 106.617 68.9442 117.746C69.1638 119.2 69.3021 122.343 69.2994 125.816C69.2955 130.872 69.2366 131.648 68.7703 132.797C68.1593 134.301 67.4555 134.866 66.6038 134.535V134.535Z" fill="#008000"/>
|
||||
</svg>
|
After Width: | Height: | Size: 4.4 KiB |
21
data/system/assets/characters/player.svg
Normal file
21
data/system/assets/characters/player.svg
Normal file
@ -0,0 +1,21 @@
|
||||
<svg width="99" height="142" viewBox="0 0 99 142" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M50.7827 30.994C59.3414 30.994 66.2797 24.0558 66.2797 15.497C66.2797 6.93825 59.3414 0 50.7827 0C42.2239 0 35.2856 6.93825 35.2856 15.497C35.2856 24.0558 42.2239 30.994 50.7827 30.994Z" fill="#999999"/>
|
||||
<path d="M50.9117 96.7638C61.8643 96.7638 70.7431 82.3566 70.7431 64.5845C70.7431 46.8124 61.8643 32.4053 50.9117 32.4053C39.9591 32.4053 31.0803 46.8124 31.0803 64.5845C31.0803 82.3566 39.9591 96.7638 50.9117 96.7638Z" fill="#999999"/>
|
||||
<path d="M10.2513 30.8866C15.9129 38.899 24.4253 46.9956 29.2643 48.971C34.1034 50.9463 33.4365 46.0523 27.7749 38.0399C22.1133 30.0275 13.6009 21.9309 8.76185 19.9556C3.92283 17.9803 4.58966 22.8742 10.2513 30.8866Z" fill="#999999"/>
|
||||
<path d="M85.6429 22.4936C78.4654 29.1821 71.5882 38.7068 70.2821 43.7677C68.9761 48.8285 73.7358 47.5091 80.9132 40.8206C88.0907 34.1321 94.9679 24.6073 96.274 19.5465C97.5801 14.4856 92.8203 15.8051 85.6429 22.4936Z" fill="#999999"/>
|
||||
<path d="M58.4679 107.8C59.0294 117.595 61.9649 128.97 65.0245 133.208C68.0841 137.445 70.1092 132.94 69.5477 123.146C68.9862 113.351 66.0507 101.975 62.9911 97.7379C59.9315 93.5003 57.9064 98.0053 58.4679 107.8Z" fill="#999999"/>
|
||||
<path d="M35.8741 106.726C32.8924 116.073 31.5616 127.746 32.9017 132.798C34.2417 137.85 37.7452 134.368 40.7269 125.021C43.7086 115.674 45.0394 104.002 43.6994 98.95C42.3593 93.8981 38.8558 97.3797 35.8741 106.726Z" fill="#999999"/>
|
||||
<path d="M47.1581 30.4208C42.0767 29.1887 37.6889 25.0606 36.0632 19.9827C35.3347 17.7074 35.2777 13.6416 35.9426 11.3837C37.5034 6.08375 41.9991 1.78112 47.2763 0.53688C55.1613 -1.32221 63.2983 3.48845 65.6233 11.3837C66.2882 13.6416 66.2312 17.7074 65.5028 19.9827C64.3424 23.6072 61.5501 27.0568 58.247 28.9467C55.2739 30.6477 50.6362 31.2642 47.1581 30.4208Z" fill="#808000"/>
|
||||
<path d="M48.6662 96.4583C46.6902 96.0305 44.5832 95.0949 43.0505 93.9647C35.051 88.0659 30.4257 75.0848 31.354 61.1378C32.2764 47.2796 38.5173 36.1203 46.9891 33.1807C49.273 32.3882 52.516 32.3912 54.8412 33.188C61.3118 35.4051 66.6343 42.5897 69.1734 52.5344C70.9431 59.4656 70.9954 69.4051 69.2978 76.1432C68.0454 81.1138 66.0769 85.6562 63.7808 88.8739C62.3575 90.8685 59.4107 93.6889 57.7065 94.6876C55.0212 96.2613 51.207 97.0084 48.6662 96.4583V96.4583Z" fill="#FF6600"/>
|
||||
<path d="M46.4173 30.0665C43.7838 29.2141 42.1067 28.1674 40.0584 26.0977C37.7646 23.78 36.5123 21.611 35.9581 18.9957C34.8748 13.8834 36.0943 9.15365 39.4512 5.44824C45.5263 -1.25764 55.132 -1.50605 61.5259 4.87737C64.5403 7.88681 65.9965 11.2873 65.9965 15.3169C65.9965 19.7168 64.6302 22.9536 61.4023 26.2005C59.1046 28.5117 57.5318 29.4499 54.7622 30.161C52.5777 30.722 48.2925 30.6734 46.4173 30.0665Z" fill="#008000"/>
|
||||
<path d="M47.1597 95.911C41.5222 93.9708 36.4517 87.9089 33.7762 79.9108C29.9123 68.3597 30.7391 54.3329 35.8765 44.2801C42.0096 32.2788 52.2867 29.229 60.5599 36.9552C67.5291 43.4637 71.3342 55.9281 70.2612 68.7342C69.8749 73.3444 69.4297 75.589 68.1172 79.5436C67.0057 82.8926 65.4293 86.2258 63.8714 88.5214C62.4546 90.609 58.6208 94.1516 56.8945 94.9682C53.6338 96.5108 49.9416 96.8684 47.1597 95.911Z" fill="#008000"/>
|
||||
<path d="M29.6782 48.9727C24.7898 47.2284 15.2128 38.0574 9.76588 29.9046C6.52048 25.0469 5.02126 21.2173 5.9343 20.1171C8.44362 17.0936 23.2239 30.2695 29.8004 41.3926C32.0993 45.2808 32.8912 48.0231 31.9786 48.9357C31.4789 49.4354 30.9967 49.4431 29.6782 48.9727Z" fill="#008000"/>
|
||||
<path d="M70.4934 46.3075C69.4887 45.097 71.3128 40.5011 74.8602 35.3048C80.4476 27.1204 89.072 18.6727 93.6095 16.9398C97.8081 15.3363 97.1137 19.9186 91.9887 27.635C84.9518 38.2302 72.655 48.9121 70.4934 46.3075Z" fill="#008000"/>
|
||||
<path d="M33.7622 134.412C32.8621 133.268 32.5406 131.262 32.575 127.007C32.6594 116.542 36.596 101.666 40.3155 97.7556C41.6967 96.3036 42.3578 96.3941 43.23 98.1544C43.8083 99.3216 43.8911 99.8867 43.989 103.333C44.2974 114.19 40.0122 130.695 35.9618 134.251C34.8241 135.25 34.4433 135.278 33.7622 134.412Z" fill="#008000"/>
|
||||
<path d="M66.6038 134.535C64.5472 133.737 61.7901 127.082 60.0871 118.805C58.935 113.206 58.7209 111.091 58.7207 105.311C58.7205 99.3335 58.9153 98.0711 60.037 96.7823C60.6451 96.0837 60.7072 96.0687 61.407 96.4519C64.2085 97.9859 67.2635 106.617 68.9442 117.746C69.1638 119.2 69.3021 122.343 69.2994 125.816C69.2955 130.872 69.2366 131.648 68.7703 132.797C68.1593 134.301 67.4555 134.866 66.6038 134.535Z" fill="#008000"/>
|
||||
<path d="M46.0829 7.20224C45.6292 7.30841 44.8829 7.56277 44.5585 7.922C44.1524 8.37155 44.235 9.16538 44.2787 9.72298C44.3665 10.8414 45.8631 11.5894 46.8435 11.612C47.335 11.6233 47.8991 11.6816 48.3364 11.4108C48.6705 11.204 49.1536 10.7085 49.2951 10.3233C49.4617 9.86977 49.3642 9.24665 49.3642 8.76749C49.3642 8.17814 48.9555 7.76792 48.4842 7.45369C47.7979 6.99617 46.9919 7.14567 46.196 7.14567" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M49.647 9.46533C50.8916 9.46533 52.1363 9.46533 53.3809 9.46533" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M57.3412 7.25885C56.4205 7.46066 55.4114 7.68178 54.6036 8.17348C53.8155 8.6532 52.6455 9.54181 52.6455 10.5779C52.6455 11.1305 53.1772 11.5354 53.5727 11.8509C54.0472 12.2294 54.56 12.5286 55.1757 12.5675C56.1379 12.6282 57.3913 12.5601 58.1019 11.8131C58.2921 11.6132 58.5193 11.4124 58.6865 11.194C59.0368 10.7362 58.9253 10.0223 58.9253 9.48414C58.9253 8.6797 58.6999 8.18902 58.1459 7.61087C57.8349 7.28636 57.521 6.97355 57.1967 6.66167C57.0229 6.49461 56.8922 6.52337 56.6623 6.52337" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M58.9253 9.29556C59.5429 8.92501 60.2165 8.64717 60.8048 8.22692C61.1482 7.98166 61.4487 7.6957 61.8672 7.57945C62.2213 7.48109 62.5856 7.3977 62.9421 7.31543" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M44.2724 9.46541C42.8391 8.7945 41.2775 8.30033 39.7653 7.83102C39.277 7.67948 38.8376 7.50649 38.332 7.42871" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 5.9 KiB |
@ -219,8 +219,8 @@ impl Widget {
|
||||
pub fn draw_batch(ctx: &EventCtx, batch: GeomBatch) -> Widget {
|
||||
JustDraw::wrap(ctx, batch)
|
||||
}
|
||||
pub fn draw_svg(ctx: &EventCtx, filename: &str) -> Widget {
|
||||
JustDraw::svg(ctx, filename)
|
||||
pub fn draw_svg<I: Into<String>>(ctx: &EventCtx, filename: I) -> Widget {
|
||||
JustDraw::svg(ctx, filename.into())
|
||||
}
|
||||
pub fn draw_svg_transform(ctx: &EventCtx, filename: &str, rewrite: RewriteColor) -> Widget {
|
||||
JustDraw::svg_transform(ctx, filename, rewrite)
|
||||
|
@ -20,8 +20,8 @@ impl JustDraw {
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn svg(ctx: &EventCtx, filename: &str) -> Widget {
|
||||
let (batch, bounds) = svg::load_svg(ctx.prerender, filename);
|
||||
pub(crate) fn svg(ctx: &EventCtx, filename: String) -> Widget {
|
||||
let (batch, bounds) = svg::load_svg(ctx.prerender, &filename);
|
||||
// TODO The dims will be wrong; it'll only look at geometry, not the padding in the image.
|
||||
Widget::new(Box::new(JustDraw {
|
||||
dims: ScreenDims::new(bounds.width(), bounds.height()),
|
||||
|
@ -16,6 +16,7 @@ pub struct Challenge {
|
||||
pub description: Vec<String>,
|
||||
pub alias: String,
|
||||
pub gameplay: GameplayMode,
|
||||
cutscene: Option<fn(&mut EventCtx, &App) -> Box<dyn State>>,
|
||||
}
|
||||
impl abstutil::Cloneable for Challenge {}
|
||||
|
||||
@ -29,12 +30,14 @@ pub fn all_challenges(dev: bool) -> BTreeMap<String, Vec<Challenge>> {
|
||||
description: vec!["Add or remove a dedicated left phase".to_string()],
|
||||
alias: "trafficsig/tut1".to_string(),
|
||||
gameplay: GameplayMode::FixTrafficSignalsTutorial(0),
|
||||
cutscene: None,
|
||||
},
|
||||
Challenge {
|
||||
title: "Tutorial 2".to_string(),
|
||||
description: vec!["Deal with heavy foot traffic".to_string()],
|
||||
alias: "trafficsig/tut2".to_string(),
|
||||
gameplay: GameplayMode::FixTrafficSignalsTutorial(1),
|
||||
cutscene: None,
|
||||
},
|
||||
Challenge {
|
||||
title: "The real challenge!".to_string(),
|
||||
@ -50,16 +53,18 @@ pub fn all_challenges(dev: bool) -> BTreeMap<String, Vec<Challenge>> {
|
||||
],
|
||||
alias: "trafficsig/main".to_string(),
|
||||
gameplay: GameplayMode::FixTrafficSignals,
|
||||
cutscene: None,
|
||||
},
|
||||
],
|
||||
);
|
||||
tree.insert(
|
||||
"Optimize somebody's commute".to_string(),
|
||||
"Optimize one commute".to_string(),
|
||||
vec![Challenge {
|
||||
title: "Part 1".to_string(),
|
||||
description: vec!["Speed up one person's daily commute".to_string()],
|
||||
description: vec!["Speed up one VIP's daily commute at all costs".to_string()],
|
||||
alias: "commute/pt1".to_string(),
|
||||
gameplay: GameplayMode::OptimizeCommute(PersonID(3434)),
|
||||
cutscene: Some(crate::sandbox::gameplay::commute::OptimizeCommute::cutscene),
|
||||
}],
|
||||
);
|
||||
|
||||
@ -78,6 +83,7 @@ pub fn all_challenges(dev: bool) -> BTreeMap<String, Vec<Challenge>> {
|
||||
abstutil::path_map("montlake"),
|
||||
"43".to_string(),
|
||||
),
|
||||
cutscene: None,
|
||||
},
|
||||
Challenge {
|
||||
title: "Route 43 in a larger area".to_string(),
|
||||
@ -90,6 +96,7 @@ pub fn all_challenges(dev: bool) -> BTreeMap<String, Vec<Challenge>> {
|
||||
abstutil::path_map("23rd"),
|
||||
"43".to_string(),
|
||||
),
|
||||
cutscene: None,
|
||||
},
|
||||
],
|
||||
);
|
||||
@ -100,6 +107,7 @@ pub fn all_challenges(dev: bool) -> BTreeMap<String, Vec<Challenge>> {
|
||||
description: vec!["Make traffic as BAD as possible!".to_string()],
|
||||
alias: "gridlock".to_string(),
|
||||
gameplay: GameplayMode::CreateGridlock(abstutil::path_map("montlake")),
|
||||
cutscene: None,
|
||||
}],
|
||||
);
|
||||
}
|
||||
@ -225,11 +233,12 @@ impl Tab {
|
||||
cbs.push((
|
||||
"Start!".to_string(),
|
||||
Box::new(move |ctx, app| {
|
||||
Some(Transition::Replace(Box::new(SandboxMode::new(
|
||||
ctx,
|
||||
app,
|
||||
challenge.gameplay.clone(),
|
||||
))))
|
||||
let sandbox = Box::new(SandboxMode::new(ctx, app, challenge.gameplay.clone()));
|
||||
if let Some(cutscene) = challenge.cutscene {
|
||||
Some(Transition::ReplaceThenPush(sandbox, cutscene(ctx, app)))
|
||||
} else {
|
||||
Some(Transition::Replace(sandbox))
|
||||
}
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
141
game/src/cutscene.rs
Normal file
141
game/src/cutscene.rs
Normal file
@ -0,0 +1,141 @@
|
||||
use crate::app::App;
|
||||
use crate::game::{State, Transition};
|
||||
use ezgui::{
|
||||
hotkey, hotkeys, Btn, Color, Composite, EventCtx, GfxCtx, Key, Line, Outcome, RewriteColor,
|
||||
Text, Widget,
|
||||
};
|
||||
|
||||
pub struct CutsceneBuilder {
|
||||
scenes: Vec<Scene>,
|
||||
}
|
||||
|
||||
struct Scene {
|
||||
avatar: Option<String>,
|
||||
msg: Text,
|
||||
}
|
||||
|
||||
impl CutsceneBuilder {
|
||||
pub fn new() -> CutsceneBuilder {
|
||||
CutsceneBuilder { scenes: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn scene(mut self, avatar: &str, msg: &str) -> CutsceneBuilder {
|
||||
self.scenes.push(Scene {
|
||||
avatar: Some(avatar.to_string()),
|
||||
msg: Text::from(Line(msg)),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn narrator(mut self, msg: &str) -> CutsceneBuilder {
|
||||
self.scenes.push(Scene {
|
||||
avatar: None,
|
||||
msg: Text::from(Line(msg)),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self, ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
Box::new(CutscenePlayer {
|
||||
composite: make_panel(ctx, app, &self.scenes, 0),
|
||||
scenes: self.scenes,
|
||||
idx: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct CutscenePlayer {
|
||||
scenes: Vec<Scene>,
|
||||
idx: usize,
|
||||
composite: Composite,
|
||||
}
|
||||
|
||||
impl State for CutscenePlayer {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
match self.composite.event(ctx) {
|
||||
Some(Outcome::Clicked(x)) => match x.as_ref() {
|
||||
"back" => {
|
||||
self.idx -= 1;
|
||||
self.composite = make_panel(ctx, app, &self.scenes, self.idx);
|
||||
}
|
||||
"next" => {
|
||||
self.idx += 1;
|
||||
self.composite = make_panel(ctx, app, &self.scenes, self.idx);
|
||||
}
|
||||
"Skip" | "Start" => {
|
||||
return Transition::Pop;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
// Happens to be a nice background color too ;)
|
||||
g.clear(app.cs.grass);
|
||||
self.composite.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
fn make_panel(ctx: &mut EventCtx, app: &App, scenes: &Vec<Scene>, idx: usize) -> Composite {
|
||||
let prev = if idx > 0 {
|
||||
Btn::svg(
|
||||
"../data/system/assets/tools/prev.svg",
|
||||
RewriteColor::Change(Color::WHITE, app.cs.hovering),
|
||||
)
|
||||
.build(ctx, "back", hotkey(Key::LeftArrow))
|
||||
.margin(5)
|
||||
} else {
|
||||
Widget::draw_svg_transform(
|
||||
ctx,
|
||||
"../data/system/assets/tools/prev.svg",
|
||||
RewriteColor::ChangeAll(Color::WHITE.alpha(0.5)),
|
||||
)
|
||||
};
|
||||
let next = if idx == scenes.len() - 1 {
|
||||
Widget::draw_svg_transform(
|
||||
ctx,
|
||||
"../data/system/assets/tools/next.svg",
|
||||
RewriteColor::ChangeAll(Color::WHITE.alpha(0.5)),
|
||||
)
|
||||
} else {
|
||||
Btn::svg(
|
||||
"../data/system/assets/tools/next.svg",
|
||||
RewriteColor::Change(Color::WHITE, app.cs.hovering),
|
||||
)
|
||||
.build(
|
||||
ctx,
|
||||
"next",
|
||||
hotkeys(vec![Key::RightArrow, Key::Space, Key::Enter]),
|
||||
)
|
||||
};
|
||||
|
||||
let mut col = vec![];
|
||||
let msg = scenes[idx].msg.clone();
|
||||
if let Some(ref name) = scenes[idx].avatar {
|
||||
col.push(Widget::row(vec![
|
||||
Widget::draw_svg(
|
||||
ctx,
|
||||
format!("../data/system/assets/characters/{}.svg", name),
|
||||
),
|
||||
msg.draw(ctx),
|
||||
]));
|
||||
} else {
|
||||
col.push(msg.draw(ctx));
|
||||
}
|
||||
|
||||
col.push(Widget::row(vec![prev, next]));
|
||||
if idx == scenes.len() - 1 {
|
||||
col.push(
|
||||
Btn::text_bg2("Start")
|
||||
.build_def(ctx, hotkeys(vec![Key::RightArrow, Key::Space, Key::Enter])),
|
||||
);
|
||||
} else {
|
||||
col.push(Btn::text_bg2("Skip").build_def(ctx, None));
|
||||
}
|
||||
|
||||
Composite::new(Widget::col(col)).build(ctx)
|
||||
}
|
@ -3,6 +3,7 @@ mod app;
|
||||
mod challenges;
|
||||
mod colors;
|
||||
mod common;
|
||||
mod cutscene;
|
||||
mod debug;
|
||||
mod devtools;
|
||||
mod edit;
|
||||
|
@ -1,11 +1,15 @@
|
||||
use crate::app::App;
|
||||
use crate::common::Tab;
|
||||
use crate::game::Transition;
|
||||
use crate::cutscene::CutsceneBuilder;
|
||||
use crate::edit::EditMode;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::helpers::cmp_duration_shorter;
|
||||
use crate::managed::{WrappedComposite, WrappedOutcome};
|
||||
use crate::sandbox::gameplay::{challenge_controller, FinalScore, GameplayMode, GameplayState};
|
||||
use crate::sandbox::gameplay::{challenge_header, FinalScore, GameplayMode, GameplayState};
|
||||
use crate::sandbox::{SandboxControls, SandboxMode};
|
||||
use ezgui::{Btn, EventCtx, GfxCtx, Line, Text, TextExt, Widget};
|
||||
use ezgui::{
|
||||
Btn, Composite, EventCtx, GfxCtx, HorizontalAlignment, Line, Outcome, Text, TextExt,
|
||||
VerticalAlignment, Widget,
|
||||
};
|
||||
use geom::{Duration, Time};
|
||||
use sim::{PersonID, TripID};
|
||||
use std::collections::BTreeSet;
|
||||
@ -16,7 +20,7 @@ use std::fmt::Write;
|
||||
const GOAL: Duration = Duration::const_seconds(3.0 * 60.0);
|
||||
|
||||
pub struct OptimizeCommute {
|
||||
top_center: WrappedComposite,
|
||||
top_center: Composite,
|
||||
person: PersonID,
|
||||
time: Time,
|
||||
|
||||
@ -28,12 +32,56 @@ impl OptimizeCommute {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, person: PersonID) -> Box<dyn GameplayState> {
|
||||
let trips = app.primary.sim.get_person(person).trips.clone();
|
||||
Box::new(OptimizeCommute {
|
||||
top_center: make_top_center(ctx, app, person, &trips),
|
||||
top_center: make_top_center(ctx, app, &trips),
|
||||
person,
|
||||
time: Time::START_OF_DAY,
|
||||
trips,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cutscene(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
|
||||
CutsceneBuilder::new()
|
||||
.scene("boss", "Listen up, I've got a special job for you today.")
|
||||
.scene(
|
||||
"player",
|
||||
"What is it? The scooter coalition back with demands for more valet parking?",
|
||||
)
|
||||
.scene(
|
||||
"boss",
|
||||
"No, all the tax-funded valets are still busy the kayakers.",
|
||||
)
|
||||
.scene(
|
||||
"boss",
|
||||
"I've got a... friend who's tired of getting stuck in traffic on Broadway. You've \
|
||||
got to make their commute as fast as possible.",
|
||||
)
|
||||
.scene(
|
||||
"player",
|
||||
"Ah, it's about time we finally put in those new bike lanes along Broadway! I'll \
|
||||
get right on --",
|
||||
)
|
||||
.scene("boss", "No! Just smooth things out for this one person.")
|
||||
.scene("player", "Uh, what's so special about them?")
|
||||
.scene(
|
||||
"boss",
|
||||
"That's none of your concern! I've anonymized their name, so don't even bother \
|
||||
digging into what happened at dinn --",
|
||||
)
|
||||
.scene("boss", "JUST GET TO WORK, KID!")
|
||||
.narrator(
|
||||
"Somebody's blackmailing the boss. Guess it's time to help this VIP (very \
|
||||
impatient person).",
|
||||
)
|
||||
.narrator(
|
||||
"The drone has been programmed to find the anonymous VIP. Watch their daily \
|
||||
route, figure out what's wrong, and fix it.",
|
||||
)
|
||||
.narrator(
|
||||
"Ignore the damage done to everyone else. Just speed up the VIP's trips by a \
|
||||
total of 3 minutes.",
|
||||
)
|
||||
.build(ctx, app)
|
||||
}
|
||||
}
|
||||
|
||||
impl GameplayState for OptimizeCommute {
|
||||
@ -44,15 +92,28 @@ impl GameplayState for OptimizeCommute {
|
||||
_: &mut SandboxControls,
|
||||
) -> (Option<Transition>, bool) {
|
||||
if self.time != app.primary.sim.time() {
|
||||
self.top_center = make_top_center(ctx, app, self.person, &self.trips);
|
||||
self.top_center = make_top_center(ctx, app, &self.trips);
|
||||
self.time = app.primary.sim.time();
|
||||
}
|
||||
|
||||
match self.top_center.event(ctx, app) {
|
||||
Some(WrappedOutcome::Transition(t)) => {
|
||||
return (Some(t), false);
|
||||
}
|
||||
Some(WrappedOutcome::Clicked(x)) => match x.as_ref() {
|
||||
match self.top_center.event(ctx) {
|
||||
Some(Outcome::Clicked(x)) => match x.as_ref() {
|
||||
"edit map" => {
|
||||
return (
|
||||
Some(Transition::Push(Box::new(EditMode::new(
|
||||
ctx,
|
||||
app,
|
||||
GameplayMode::OptimizeCommute(self.person),
|
||||
)))),
|
||||
false,
|
||||
);
|
||||
}
|
||||
"instructions" => {
|
||||
return (
|
||||
Some(Transition::Push(OptimizeCommute::cutscene(ctx, app))),
|
||||
false,
|
||||
);
|
||||
}
|
||||
"locate person" => {
|
||||
let person = self.person;
|
||||
return (
|
||||
@ -101,12 +162,7 @@ impl GameplayState for OptimizeCommute {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_top_center(
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
person: PersonID,
|
||||
trips: &Vec<TripID>,
|
||||
) -> WrappedComposite {
|
||||
fn make_top_center(ctx: &mut EventCtx, app: &App, trips: &Vec<TripID>) -> Composite {
|
||||
let mut done = 0;
|
||||
let mut baseline_time = Duration::ZERO;
|
||||
let mut experiment_time = Duration::ZERO;
|
||||
@ -121,24 +177,25 @@ fn make_top_center(
|
||||
let mut txt = Text::from(Line(format!("Total trip time: {} (", experiment_time)));
|
||||
txt.append_all(cmp_duration_shorter(experiment_time, baseline_time));
|
||||
txt.append(Line(")"));
|
||||
let row = vec![
|
||||
Btn::svg_def("../data/system/assets/tools/location.svg")
|
||||
.build(ctx, "locate person", None)
|
||||
.margin_right(10),
|
||||
format!("{}/{} trips done", done, trips.len())
|
||||
.draw_text(ctx)
|
||||
.margin_right(20),
|
||||
txt.draw(ctx).margin_right(20),
|
||||
format!("Goal: {} faster", GOAL).draw_text(ctx),
|
||||
];
|
||||
|
||||
challenge_controller(
|
||||
ctx,
|
||||
app,
|
||||
GameplayMode::OptimizeCommute(person),
|
||||
&format!("Optimize {}'s commute", person),
|
||||
vec![Widget::row(row)],
|
||||
Composite::new(
|
||||
Widget::col(vec![
|
||||
challenge_header(ctx, "Optimize the VIP's commute"),
|
||||
Widget::row(vec![
|
||||
Btn::svg_def("../data/system/assets/tools/location.svg")
|
||||
.build(ctx, "locate person", None)
|
||||
.margin_right(10),
|
||||
format!("{}/{} trips done", done, trips.len())
|
||||
.draw_text(ctx)
|
||||
.margin_right(20),
|
||||
txt.draw(ctx).margin_right(20),
|
||||
format!("Goal: {} faster", GOAL).draw_text(ctx),
|
||||
]),
|
||||
])
|
||||
.bg(app.cs.panel_bg),
|
||||
)
|
||||
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
|
||||
.build(ctx)
|
||||
}
|
||||
|
||||
// True if the challenge is completed
|
||||
|
@ -1,4 +1,5 @@
|
||||
mod commute;
|
||||
// TODO pub so challenges can grab cutscenes. Weird?
|
||||
pub mod commute;
|
||||
mod create_gridlock;
|
||||
mod fix_traffic_signals;
|
||||
mod freeform;
|
||||
@ -293,6 +294,23 @@ impl ContextualActions for GameplayMode {
|
||||
}
|
||||
}
|
||||
|
||||
fn challenge_header(ctx: &mut EventCtx, title: &str) -> Widget {
|
||||
Widget::row(vec![
|
||||
Line(title).small_heading().draw(ctx).margin(5),
|
||||
Btn::svg_def("../data/system/assets/tools/info.svg")
|
||||
.build(ctx, "instructions", None)
|
||||
.margin(5),
|
||||
Widget::draw_batch(
|
||||
ctx,
|
||||
GeomBatch::from(vec![(Color::WHITE, Polygon::rectangle(2.0, 50.0))]),
|
||||
)
|
||||
.margin(5),
|
||||
Btn::svg_def("../data/system/assets/tools/edit_map.svg")
|
||||
.build(ctx, "edit map", lctrl(Key::E))
|
||||
.margin(5),
|
||||
])
|
||||
}
|
||||
|
||||
fn challenge_controller(
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
@ -311,21 +329,7 @@ fn challenge_controller(
|
||||
}
|
||||
}
|
||||
|
||||
let mut rows = vec![Widget::row(vec![
|
||||
Line(title).small_heading().draw(ctx).margin(5),
|
||||
Btn::svg_def("../data/system/assets/tools/info.svg")
|
||||
.build(ctx, "instructions", None)
|
||||
.margin(5),
|
||||
Widget::draw_batch(
|
||||
ctx,
|
||||
GeomBatch::from(vec![(Color::WHITE, Polygon::rectangle(2.0, 50.0))]),
|
||||
)
|
||||
.margin(5),
|
||||
Btn::svg_def("../data/system/assets/tools/edit_map.svg")
|
||||
.build(ctx, "edit map", lctrl(Key::E))
|
||||
.margin(5),
|
||||
])
|
||||
.centered()];
|
||||
let mut rows = vec![challenge_header(ctx, title)];
|
||||
rows.extend(extra_rows);
|
||||
|
||||
WrappedComposite::new(
|
||||
|
@ -1,5 +1,5 @@
|
||||
mod dashboards;
|
||||
mod gameplay;
|
||||
pub mod gameplay;
|
||||
mod histogram;
|
||||
mod misc_tools;
|
||||
mod speed;
|
||||
|
Loading…
Reference in New Issue
Block a user