diff --git a/Cargo.lock b/Cargo.lock index 447496e..0c003d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,10 +3,76 @@ version = 3 [[package]] -name = "anyhow" -version = "1.0.55" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "159bb86af3a200e19a068f4224eae4c8bb2d0fa054c7e5d1cacd5cef95e684cd" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "associative-cache" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46016233fc1bb55c23b856fe556b7db6ccd05119a0a392e04f0b3b7c79058f16" + +[[package]] +name = "atk" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a83b21d2aa75e464db56225e1bda2dd5993311ba1095acaa8fa03d1ae67026ba" +dependencies = [ + "atk-sys", + "bitflags", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "badcf670157c84bb8b1cf6b5f70b650fed78da2033c9eed84c4e49b11cbe83ea" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] [[package]] name = "autocfg" @@ -14,24 +80,175 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bit_field" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "bytemuck" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cairo-rs" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b5725979db0c586d98abad2193cdb612dd40ef95cd26bd99851bf93b3cb482" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "libc", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b448b876970834fda82ba3aeaccadbd760206b75388fc5c1b02f1e343b697570" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "cc" version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +[[package]] +name = "cfg-expr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b412e83326147c2bb881f8b40edfbf9905b9b8abaebd0e47ca190ba62fda8f0e" +dependencies = [ + "smallvec", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clipboard" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a904646c0340239dcf7c51677b33928bf24fdf424b79a57909c0109075b2e7" +dependencies = [ + "clipboard-win", + "objc", + "objc-foundation", + "objc_id", + "x11-clipboard", +] + +[[package]] +name = "clipboard-win" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a093d6fed558e5fe24c3dfc85a68bb68f1c824f440d3ba5aca189e2998786b" +dependencies = [ + "winapi", +] + +[[package]] +name = "cocoa" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +dependencies = [ + "bitflags", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -39,10 +256,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] -name = "crossbeam-channel" -version = "0.5.2" +name = "core-graphics" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +dependencies = [ + "bitflags", + "core-foundation", + "foreign-types", + "libc", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdbfe11fe19ff083c48923cf179540e8cd0535903dc35e178a1fdeeb59aef51f" dependencies = [ "cfg-if", "crossbeam-utils", @@ -61,10 +324,11 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" dependencies = [ + "autocfg", "cfg-if", "crossbeam-utils", "lazy_static", @@ -74,41 +338,553 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ "cfg-if", "lazy_static", ] +[[package]] +name = "deflate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" +dependencies = [ + "adler32", +] + +[[package]] +name = "druid" +version = "0.7.0" +source = "git+https://github.com/linebender/druid.git?rev=fc05e965c85fced8720c655685e02478e0530e94#fc05e965c85fced8720c655685e02478e0530e94" +dependencies = [ + "console_error_panic_hook", + "druid-derive", + "druid-shell", + "fluent-bundle", + "fluent-langneg", + "fluent-syntax", + "fnv", + "im", + "instant", + "tracing", + "tracing-subscriber", + "tracing-wasm", + "unic-langid", + "unicode-segmentation", + "xi-unicode", +] + +[[package]] +name = "druid-derive" +version = "0.4.0" +source = "git+https://github.com/linebender/druid.git?rev=fc05e965c85fced8720c655685e02478e0530e94#fc05e965c85fced8720c655685e02478e0530e94" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "druid-shell" +version = "0.7.0" +source = "git+https://github.com/linebender/druid.git?rev=fc05e965c85fced8720c655685e02478e0530e94#fc05e965c85fced8720c655685e02478e0530e94" +dependencies = [ + "anyhow", + "bitflags", + "block", + "cairo-rs", + "cfg-if", + "cocoa", + "core-graphics", + "foreign-types", + "gdk-sys", + "glib-sys", + "gtk", + "gtk-sys", + "instant", + "js-sys", + "keyboard-types", + "kurbo", + "lazy_static", + "objc", + "piet-common", + "scopeguard", + "time", + "tracing", + "wasm-bindgen", + "web-sys", + "winapi", + "wio", +] + +[[package]] +name = "druid-widget-nursery" +version = "0.1.0" +source = "git+https://github.com/linebender/druid-widget-nursery#f6710e79f191acafb549bc2294eff2021f650f3d" +dependencies = [ + "druid", + "log", + "tracing", +] + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "exr" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4badb9489a465cb2c555af1f00f0bfd8cecd6fc12ac11da9d5b40c5dd5f0200" +dependencies = [ + "bit_field", + "deflate", + "flume", + "half", + "inflate", + "lebe", + "smallvec", + "threadpool", +] + [[package]] name = "ffxii-tza-auto-notes" version = "0.1.0" dependencies = [ "anyhow", + "clipboard", + "druid", + "druid-widget-nursery", + "image", "itertools", "lazy_static", "maplit", + "num-format", + "parking_lot", "process_list", + "pulldown-cmark", "serde", "serde_yaml", "sysinfo", "vmemory", ] +[[package]] +name = "field-offset" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78" +dependencies = [ + "thiserror", +] + +[[package]] +name = "flume" +version = "0.10.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843c03199d0c0ca54bc1ea90ac0d507274c28abcc4f691ae8b4eaa375087c76a" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-executor" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gdk" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d749dcfc00d8de0d7c3a289e04a04293eb5ba3d8a4e64d64911d481fa9933b" +dependencies = [ + "bitflags", + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534192cb8f01daeb8fab2c8d4baa8f9aae5b7a39130525779f5c2608e235b10f" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f097c0704201fbc8f69c1762dc58c6947c8bb188b8ed0bc7e65259f1894fe590" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e091b3d3d6696949ac3b3fb3c62090e5bfd7bd6850bef5c3c5ea701de1b1f1e" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a7187e78088aead22ceedeee99779455b23fc231fe13ec443f99bb71694e5b" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gio" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711c3632b3ebd095578a9c091418d10fed492da9443f58ebc8f45efbeb215cb0" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-io", + "gio-sys", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0a41df66e57fcc287c4bcf74fc26b884f31901ea9792ec75607289b456f48fa" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c515f1e62bf151ef6635f528d05b02c11506de986e43b34a5c920ef0b3796a4" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aad66361f66796bfc73f530c51ef123970eb895ffba991a234fcf7bea89e518" +dependencies = [ + "anyhow", + "heck", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c1d60554a212445e2a858e42a0e48cece1bd57b311a19a9468f70376cf554ae" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa92cae29759dae34ab5921d73fff5ad54b3d794ab842c117e36cafc7994c3f5" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb51122dd3317e9327ec1e4faa151d1fa0d95664cd8fb8dcfacf4d4d29ac70c" +dependencies = [ + "atk", + "bitflags", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "once_cell", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c14c8d3da0545785a7c5a120345b3abb534010fb8ae0f2ef3f47c027fba303e" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21de1da96dc117443fb03c2e270b2d34b7de98d0a79a19bbb689476173745b79" +dependencies = [ + "anyhow", + "heck", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -118,6 +894,41 @@ dependencies = [ "libc", ] +[[package]] +name = "im" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "111c1983f3c5bb72732df25cddacee9b546d08325fb584b5ebd38148be7b0246" +dependencies = [ + "bitmaps", + "rand_core", + "rand_xoshiro", + "serde", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "image" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder 0.2.2", + "num-iter", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + [[package]] name = "indexmap" version = "1.8.0" @@ -128,6 +939,47 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inflate" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" +dependencies = [ + "adler32", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "intl-memoizer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b18f988384267d7066cc2be425e6faf352900652c046b6971d2e228d3b1c5ecf" +dependencies = [ + "tinystr", + "unic-langid", +] + [[package]] name = "itertools" version = "0.10.3" @@ -137,6 +989,55 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "jpeg-decoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" + +[[package]] +name = "jpeg-decoder" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "105fb082d64e2100074587f59a74231f771750c664af903f1f9f76c9dedfc6f1" +dependencies = [ + "rayon", +] + +[[package]] +name = "js-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keyboard-types" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7668b7cff6a51fe61cdde64cd27c8a220786f399501b57ebe36f7d8112fd68" +dependencies = [ + "bitflags", +] + +[[package]] +name = "kurbo" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" +dependencies = [ + "arrayvec 0.7.2", + "serde", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -144,10 +1045,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] -name = "libc" -version = "0.2.119" +name = "lebe" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "7efd1d698db0759e6ef11a7cd44407407399a910c774dd804c64c032da7826ff" + +[[package]] +name = "libc" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad5c14e80759d0939d013e6ca49930e59fc53dd8e5009132f76240c179380c09" [[package]] name = "linked-hash-map" @@ -155,6 +1062,24 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + [[package]] name = "mach" version = "0.3.2" @@ -164,12 +1089,33 @@ dependencies = [ "libc", ] +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "maplit" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + [[package]] name = "memoffset" version = "0.6.5" @@ -179,6 +1125,34 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +dependencies = [ + "adler", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + [[package]] name = "nix" version = "0.23.1" @@ -192,6 +1166,12 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + [[package]] name = "ntapi" version = "0.2.0" @@ -210,6 +1190,57 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-format" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" +dependencies = [ + "arrayvec 0.4.12", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.1" @@ -220,12 +1251,305 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0" +dependencies = [ + "libc", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + [[package]] name = "once_cell" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +[[package]] +name = "pango" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "546fd59801e5ca735af82839007edd226fe7d3bb06433ec48072be4439c28581" +dependencies = [ + "bitflags", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2367099ca5e761546ba1d501955079f097caa186bb53ce0f718dca99ac1942fe" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "pangocairo" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03ac1e8d456f8f436168aeac41201f0bf49d1dc6c8d01bfb04de2cca25df631" +dependencies = [ + "bitflags", + "cairo-rs", + "glib", + "libc", + "pango", + "pangocairo-sys", +] + +[[package]] +name = "pangocairo-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b9b679ad5c8503e3e533ce06e1619d033274b246e977a6fa1655a6c6ef2b51" +dependencies = [ + "cairo-sys-rs", + "glib-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "piet" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c14a2944b6da638045428a9a7901b77bea62bf430d2b9d4d7146acce96e14a15" +dependencies = [ + "kurbo", + "unic-bidi", +] + +[[package]] +name = "piet-cairo" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecdd587016b022cbe2ee290d7641a362337b244adf1bfe156a35d85855a5221" +dependencies = [ + "cairo-rs", + "glib", + "pango", + "pango-sys", + "pangocairo", + "piet", + "unicode-segmentation", + "xi-unicode", +] + +[[package]] +name = "piet-common" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b2fc8550e18e3ea1dd552f48291acc31996712dccfeb15404977f6350abf63" +dependencies = [ + "cairo-rs", + "cairo-sys-rs", + "cfg-if", + "core-graphics", + "piet", + "piet-cairo", + "piet-coregraphics", + "piet-direct2d", + "piet-web", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "piet-coregraphics" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "803509526b882838db55ae8f6cae276a08aca6b64819c8f371fe9505d37e9d72" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "core-graphics", + "core-text", + "foreign-types", + "piet", +] + +[[package]] +name = "piet-direct2d" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46e2d4f9108976e6166d3de76afa238ab837ba5d5f52fd2a3f2e2c959be2b9a4" +dependencies = [ + "associative-cache", + "dwrote", + "piet", + "utf16_lit", + "winapi", + "wio", +] + +[[package]] +name = "piet-web" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bee71a4d0c2b00e51807d381a6ced49c792f0b0a2e42135f9f4547111bf2bb" +dependencies = [ + "js-sys", + "piet", + "unicode-segmentation", + "wasm-bindgen", + "web-sys", + "xi-unicode", +] + +[[package]] +name = "pin-project" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" + +[[package]] +name = "png" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.5.1", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.36" @@ -244,6 +1568,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "pulldown-cmark" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6" +dependencies = [ + "bitflags", + "getopts", + "memchr", + "unicase", +] + [[package]] name = "quote" version = "1.0.15" @@ -253,6 +1589,21 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_xoshiro" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" +dependencies = [ + "rand_core", +] + [[package]] name = "rayon" version = "1.5.1" @@ -278,18 +1629,72 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redox_syscall" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "self_cell" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.136" @@ -323,10 +1728,68 @@ dependencies = [ ] [[package]] -name = "syn" -version = "1.0.86" +name = "sharded-slab" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "spin" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +dependencies = [ + "lock_api", +] + +[[package]] +name = "strum" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" + +[[package]] +name = "strum_macros" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54" dependencies = [ "proc-macro2", "quote", @@ -348,12 +1811,295 @@ dependencies = [ "winapi", ] +[[package]] +name = "system-deps" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "480c269f870722b3b08d2f13053ce0c2ab722839f472863c3e2d61ff3a1c2fa6" +dependencies = [ + "anyhow", + "cfg-expr", + "heck", + "itertools", + "pkg-config", + "strum", + "strum_macros", + "thiserror", + "toml", + "version-compare", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tiff" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0247608e998cb6ce39dfc8f4a16c50361ce71e5b52e6d24ea1227ea8ea8ee0b2" +dependencies = [ + "flate2", + "jpeg-decoder 0.1.22", + "weezl", +] + +[[package]] +name = "time" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" +dependencies = [ + "libc", + "num_threads", +] + +[[package]] +name = "tinystr" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1" + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" +dependencies = [ + "lazy_static", + "valuable", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce" +dependencies = [ + "ansi_term", + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + +[[package]] +name = "type-map" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46" +dependencies = [ + "rustc-hash", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unic-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1356b759fb6a82050666f11dce4b6fe3571781f1449f3ef78074e408d468ec09" +dependencies = [ + "matches", + "unic-ucd-bidi", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-langid" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d" +dependencies = [ + "tinystr", +] + +[[package]] +name = "unic-ucd-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-segmentation" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + [[package]] name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "utf16_lit" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14706d2a800ee8ff38c1d3edb873cd616971ea59eb7c0d046bb44ef59b06a1ae" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version-compare" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "vmemory" version = "0.1.8" @@ -366,6 +2112,82 @@ dependencies = [ "winapi", ] +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" + +[[package]] +name = "web-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e" + [[package]] name = "winapi" version = "0.3.9" @@ -388,6 +2210,83 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" + +[[package]] +name = "windows_i686_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" + +[[package]] +name = "windows_i686_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] +name = "x11-clipboard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89bd49c06c9eb5d98e6ba6536cf64ac9f7ee3a009b2f53996d405b3944f6bcea" +dependencies = [ + "xcb", +] + +[[package]] +name = "xcb" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e917a3f24142e9ff8be2414e36c649d47d6cc2ba81f16201cdef96e533e02de" +dependencies = [ + "libc", + "log", +] + +[[package]] +name = "xi-unicode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index a0c0fe2..7d98b7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,11 +7,19 @@ edition = "2021" [dependencies] anyhow = "1" -vmemory = "0.1" -sysinfo = "0.23" -process_list = "0.2" +clipboard = "0.5" +druid = { git = "https://github.com/linebender/druid.git", rev = "fc05e965c85fced8720c655685e02478e0530e94", features = ["im", "serde"] } +druid-widget-nursery = { git = "https://github.com/linebender/druid-widget-nursery" } itertools = "0.10" +image = "0.24" +lazy_static = "1" +maplit = "1" +num-format = "0.4" +parking_lot = "0.12" +process_list = "0.2" +pulldown-cmark = "0.9" +#rfd = "0.6" serde = { version = "1", features = ["derive"] } serde_yaml = "0.8" -maplit = "1" -lazy_static = "1" +sysinfo = "0.23" +vmemory = "0.1" diff --git a/img/dustia_mistake_map.png b/img/dustia_mistake_map.png new file mode 100755 index 0000000..f488546 Binary files /dev/null and b/img/dustia_mistake_map.png differ diff --git a/notes.yaml b/notes.yaml index 8ae4466..4a0aaa2 100644 --- a/notes.yaml +++ b/notes.yaml @@ -21,9 +21,9 @@ steps: - area: 275 steps: |- - Menu - 1. Active - 2. Battle speed: fast - 3. Cursor position: last selection + - Active + - Battle speed: fast + - Cursor position: last selection - **Skip**: 1 - Kill Remora with Thunder spam - **Skip**: 1 @@ -111,7 +111,6 @@ steps: - **Skip**: 1 - Straight ahead to Rogue Tomato - Attack, follow, attack - - **Skip**: 1 # 78: killed rogue tomato - stage: 78 areas: @@ -119,6 +118,7 @@ steps: # 305: eastgate - area: 227 steps: |- + - **Skip**: 1 - Return to Rabanastre - area: 305 steps: |- @@ -196,11 +196,11 @@ steps: - stage: 140 areas: # 1155: stage 1 - # 1166: stage 2 + # 1156: stage 2 - area: 1155 steps: |- - Grab Diamond Armlet, kill rats - - area: 1166 + - area: 1156 steps: |- - Quit to title - stage: 0 @@ -225,6 +225,7 @@ steps: # 248: crystal glade (dry) # 247: starfall field (dry) # 249: gizas south bank (dry) + # 238: gizas north bank (dry) # 228: yardang labyrinth # 630: west barbican - area: 248 @@ -253,7 +254,7 @@ steps: steps: |- - Use sunstone - Exit North - - area: 0 + - area: 238 steps: |- - Use Northwest sunstone # 160: after getting sunstone @@ -265,6 +266,7 @@ steps: # 292: southern plaza # 307: westgate # 345: galtea downs + # 346: corridor of sand # 306: southgate # 702: south sprawl - area: 243 @@ -288,10 +290,30 @@ steps: steps: |- - **Skip**: 1 - Northeast, stop before zoning - - Get Vaan to critical HP - ![vaan dustia hp chart](./img/vaan_dustia_hp_chart.png) + - Get Vaan to critical HP + + | HP | Action + | -- | ------ + | 0-10 | Nothing + | 16-23 | Penelo punch armour + | 22-29 | Penelo punch no armour + | 30-35 | Vaan punch no armour + | 36-38 | PPA + VPA + | 39-42 | Penelo dagger armour + | 43-46 | Vaan dagger armour + | 47-51 | Penelo punch no armour + ? + | 52-56 | Penelo dagger no armour + | 57-60 | Vaan dagger no armour + | 61-72 | Vaan dagger armour + ? + | 73-83 | Vaan dagger no armor + ? + + ![mistake map](./img/dustia_mistake_map.png) + - The map above shows routes you can take if you despawn Dustia - Kill Penelo, set battle speed to slow - - Exit, spawn Dustia, use Phoenix Down to kill + - Exit + - area: 346 + steps: |- + - Spawn Dustia, use Phoenix Down to kill - Kill 12 times total, until level 13 Vaan - Return to Rabanastre - area: 307 @@ -391,13 +413,16 @@ steps: steps: |- - Use switch on back left wall - Open door - - **Skip**: 1 # 215: gone through hidden door # 220: you obtain the goddess's magicite - stage: 220 areas: + # 571: treasure room no. 8 # 572: the garden stairs - - area: 0 + - area: 571 + steps: |- + - **Skip**: 1 + - area: 572 steps: |- - **Skip**: 1 - Exit upstairs @@ -420,16 +445,24 @@ steps: # 319: east sluice control - area: 314 steps: |- - - Menu: - 1. Licence boards - - All: Red battlemage - 2. Licences - - All: Dark - 3. Gambits - - Vaan, Fran: turn on gambits, highest hp: Dark - - Balthier: HP < 50%: Potion, highest hp: Dark - 4. Config - - Battle speed: fast + - **Licences** + + | Character | Board | Licences + | --------- | ----- | -------- + | Vaan | RDM | Dark + | Balthier | RDM | Dark + | Fran | RDM | Dark + + - **Gambits** + + | Character | Slot | Target | Action + | --------- | ---- | ------ | ------ + | Vaan (on) | 1 | Foe: highest HP | Dark + | Fran | 1 | Foe: highest HP | Dark + | Balthier | 1 | Ally: HP < 50% | Potion + | Balthier | 2 | Foe: highest HP | Dark + + - **Battle speed**: fast - Exit ahead - area: 316 steps: |- @@ -836,6 +869,2638 @@ steps: - stage: 710 areas: # 806: lhusu square + # 805: miners' end + # 804: cloudborne row + # 820: the cloudborne + # 914: cutscene - area: 806 steps: |- - - bruh idk lmao + - Exit + - area: 805 + steps: |- + - Exit Northwest + - area: 804 + steps: |- + - West, North, shout to two people + - East to The Cloudborne + - area: 820 + steps: |- + - Shout at the corner of the overlapping rugs ahead + - **Skip**: 3 + - stage: 730 + areas: + # 820: the cloudborne + # 804: cloudborne row + # 803: travica way + - area: 820 + steps: |- + - Exit + - area: 804 + steps: |- + - Exit Northwest + - area: 803 + steps: |- + - North + - Talk to guards + - **Skip**: 4 + - stage: 920 + areas: + # 552: port launch + # 555: port section + - area: 552 + steps: |- + - Exit ahead + - area: 555 + steps: |- + - Go forward + - **Skip**: 1 + - stage: 930 + areas: + # 555: port section + # 556: large freight stores + # 561: airship berth access + # 564: central brig access + - area: 555 + steps: |- + - Switch to Vossler + - Go South and take the middle path + - Go East, then South to the square + - Go East, exit South + - area: 556 + steps: |- + - Follow the path South + - Downstairs, exit North + - area: 561 + steps: |- + - Follow the path East + - Exit South + - area: 564 + steps: |- + - **Skip**: 1 + - **Judges** + - Flee to the mid-East square + - Add Balthier and Fran to the party + - Change Balthier to use a Hi-Potion + - Have Vossler use Traveler + - stage: 966 + areas: + # 564: central brig access + # 876: brig no. 1 + - area: 564 + steps: |- + - Exit ahead + - area: 876 + steps: |- + - Open the back-right cell + - **Skip**: 1 + - stage: 970 + areas: + # 876: brig. no 1 + # 561: airship berth access + # 556: large freight stores + - area: 876 + steps: |- + - Exit + - area: 564 + steps: |- + - **Skip**: 1 + - stage: 990 + areas: + # 564: central brig access + # 561: airship berth access + # 555: port section + - area: 564 + steps: |- + - Switch to Vossler + - Exit ahead + - area: 561 + steps: |- + - Follow path East + - Exit South + - area: 556 + steps: |- + - Get the nearest left chest + - Go up the Southwest stairs + - Exit North + - area: 555 + steps: |- + - Go West + - **Skip**: 1 + - stage: 1000 + areas: + # 555: port section + # 552: port launch + - area: 555 + steps: |- + - Go all the way North + - Exit West + - area: 552 + steps: |- + - **Skip**: 1 + - **Ghis** + - Gambits to win + - **Skip**: 1 + - stage: 1090 + areas: + # 791: aerodrome (bhujerba) + - area: 791 + steps: |- + - **Skip**: 1 + - stage: 1100 + areas: + # 791: aerodrome (bhujerba) + # 803: travica way + - area: 791 + steps: |- + - Exit + - area: 803 + steps: |- + - Go East, then North + - Talk to guard + - **Skip**: 4 + - stage: 1240 + areas: + # 350: the western divide + # 379: platform 1 - east tanks + - area: 350 + steps: |- + - **Shop** + - **Buy** + - Cura + - Aero + - Raise + - Protect + - 5x Gold Needle + - 5x Echo Herbs + - Exit West + - area: 379 + steps: |- + - Go forward + - **Skip**: 1 + - stage: 1270 + areas: + # 379: platform 1 - east tanks + # 380: platform 1 - refinery + - area: 379 + steps: |- + - Go South up the ramp + - Go Southwest + - Exit West + - area: 380 + steps: |- + - **Skip**: 1 + - stage: 1330 + areas: + # 380: platform 1 - refinery + # 384: platform 1 - south tanks + # 387: south tank approach + # 386: yensa border tunnel + # 392: augur hill + - area: 380 + steps: |- + - Switch to Vossler + - Go down the West Ramp + - Take the North ramps all the way to a chest + - Go West + - Go Southwest + - Exit Southwest + - area: 384 + steps: |- + - Go West + - Exit Southwest + - area: 387 + steps: |- + - Go down the West ramp + - Exit West + - area: 386 + steps: |- + - **Licences** + + | Character | Board | Licences + | --------- | ----- | -------- + | Penelo | WHM | White Magick 2-4, Accessories 1-6 + | Ashe | BLM | Mystic Armor 1, Magick Lore x2, Gambit Slot, Mystic Armor 2, Staves 1, Mystic Armor 3-4, Black Magick 4 + + - **Equip** + - **Penelo** + - Unequip all + - Golden Amulet + - **Gambits** + + | Character | Slot | Target | Action + | --------- | ---- | ------ | ------ + | Balthier | 1 | Turn off | - + | Penelo | 1 | Ally: any | Raise + | Penelo | 2 | Ally: HP < 50% | Cura + + - Exit West + - area: 392 + steps: |- + - **Skip**: 1 + - stage: 1338 + areas: + # 392: augur hill + # 391: withering shores + # 398: demesne of the sandqueen + # 399: trail of fading warmth + - area: 392 + steps: |- + - Go West to the exit + - area: 391 + steps: |- + - Go West and up the the ramp + - Exit West + - area: 398 + steps: |- + - Go West down the ramp + - Use Traveler on first enemy, note current step count + - Go Southeast + - Exit West + - area: 399 + steps: |- + - Follow path West and North + - Exit Northwest + - stage: 1342 + areas: + # 403: valley of the dead + - area: 403 + steps: |- + - Don't save + - **Skip**: 1 + - stage: 1345 + areas: + # 403: valley of the dead + - area: 403 + steps: |- + - Walk forward + - stage: 1352 + areas: + # 403: valley of the dead + - area: 403 + steps: |- + - **Skip**: 1 + - **Garuda** + - Gambits + - **Skip**: 2 + - stage: 1360 + areas: + # 403: valley of the dead + - area: 403 + steps: |- + - Go upstairs + - Use the device + - **Skip**: 1 + - stage: 1364 + areas: + # 406: hall of the destroyer + # 407: hall of the sentinel + - area: 406 + steps: |- + - Go West + - **Skip**: 1 + - Flee West and exit + - area: 407 + steps: |- + - **Skip**: 1 + - Switch to Vossler + - **Party** + - **Remove** Balthier and Fran + - **Add** Basch and Penelo + - **Config** + - **Battle Mode**: Wait + - **Battle Speed**: Slow (if not already) + - **Demon Wall 2** + - Get steps within 10 of last use + - **Vossler**: Traveler + - **Skip**: 1 + - stage: 1372 + areas: + # 407: hall of the sentinel + - area: 407 + steps: |- + - Exit West + - stage: 1374 + areas: + # 410: royal passage + # 411: southfall passage + # 412: northfall passage + # 415: cloister of flame + - area: 410 + steps: |- + - **Skip**: 1 + - Switch to Vossler + - Go South + - Take the first stairs East + - Exit + - area: 411 + steps: |- + - Get chest to the West + - Hug wall to avoid trap + - Go all the way East, then South + - Use Traveler at the bottom of the stairs + - Go West to pedestal and use + - Use the teleporter + - area: 410 + steps: |- + - Go North, skipping stairs + - Get chest at the end of the path + - Exit + - area: 412 + steps: |- + - Follow path all the way East + - Go North, then West + - Use pedestal + - Take new path West to exit + - area: 415 + steps: |- + - **Skip**: 1 + - stage: 1376 + areas: + # 415: cloister of flame + - area: 415 + steps: |- + - **Battle Speed**: Fast + - Go west + - Take the first stairs + - **Skip**: 1 + - **Belias** + - Switch to Penelo + - Grab South chest + - Get to Traveler range, use at ~50% + - **Skip**: 2 + - stage: 1380 + areas: + # 415: cloister of flame + # 418: chamber of first light + - area: 415 + steps: |- + - Exit ahead + - area: 418 + steps: |- + - **Party**: Remove Penelo, add Ashe + - **Licences** + + | Character | Board | Licences | + | --------- | ----- | -------- | + | Basch | KNT | +230 HP, Second Board + | Basch | SHK | Swiftness, +70 HP, +150 HP + | Ashe | BLM | Second Board + | Ashe | BSH | Remedy Lore 1, Accessories 6 + + - **Equip** + - **Penelo**: Orrachea Armlet + - **Ashe**: Golden Amulet + - **Gambits** + + | Character | Slot | Target | Action | + | --------- | ---- | ------ | ------ | + | Vaan (off) | 1 | Ally: Vaan | Dark + | Balthier | 2 | Foe: highest HP | Steal + | Basch | 1 | Ally: HP < 70% | Hi-Potion + | Basch | 2 | Foe: Lowest HP | Attack + | Ashe (off) | 1 | Ally: Ashe | Aero + | Ashe | 2 | Foe: highest level | Aero + + - Go forward + - stage: 1390 + areas: + # 418: chamber of first light + # 406: hall of the destroyer + # 403: valley of the dead + - area: 418 + steps: |- + - **Skip**: 1 + - Go downstairs and use teleporter + - area: 406 + steps: |- + - Use teleporter + - area: 403 + steps: |- + - Go downstairs + - **Skip**: 3 + - stage: 1460 + areas: + # 872: battle launch + - area: 872 + steps: |- + - **Vossler** + - Use Reflectga Mote + - Use Aeroga Mote on all enemies + - Turn on gambits + - Use Basch to keep Vossler near group + - **Skip**: 6 + - stage: 1580 + areas: + # 290: muthru bazaar + # 253: throne road (rains) + # 254: warrior's wash + # 256: toam hills (rains) + # 264: starfall field (rains) + # 525: field of fallen wings + # 526: the switchback + # 527: haulo green + - area: 290 + steps: |- + - Go North + - Use Moogling to Southgate + - **Party**: Balthier, Fran, Ashe + - **Gambits**: Clear Ashe's first + - Get a chocobo + - area: 253 + steps: |- + - Exit Southwest + - area: 254 + steps: |- + - Go South + - Exit Southeast + - area: 256 + steps: |- + - Follow outside path to exit + - area: 264 + steps: |- + - Go West + - Go South across Bridge + - Take the East branch, then the West branch + - Exit South + - area: 525 + steps: |- + - **Skip**: 1 + - Exit South + - area: 526 + steps: |- + - Go Southeast to exit + - area: 527 + steps: |- + - Quit to title + - stage: 0 + areas: + # 12: main menu + - area: 12 + steps: |- + - Enter Trial Mode + - stage: 1580 + areas: + # 1155: stage 1 + # 1159: stage 5 + # 1163: stage 6 + - area: 1155 + steps: |- + - Get the Diamond Armlet + - area: 1159 + steps: |- + - Walk forward with Balthier + - Steal once, then walk back + - area: 1163 + steps: |- + - Quit to title + - stage: 0 + areas: + # 12: main menu + - area: 12 + steps: |- + - Load autosave + - stage: 1580 + areas: + # 527: haulo green + # 751: banks of the sogoht + - area: 527 + steps: |- + - Go South, then loop West + - Exit West, dismounting + - area: 751 + steps: |- + - **Skip**: 1 + - **Equip**: Remove all on all characters + - Talk to the guard ahead + - stage: 1626 + areas: + # 752: lull of the land + - area: 752 + steps: |- + - Go West and North to talk to the high chief + - stage: 1627 + areas: + # 752: lull of the land + - area: 752 + steps: |- + - Go West to the shop + - **Shop** (12,800 gil) + - **Sell**: all weapons, loot (not jaya stick), Diamond Armlet + - **Buy** + - Magoroku + - Flame Staff (if necessary) + - Feathered Cap + - Traveler's Vestment + - Fira + - Go North + - Talk to War Chief + - stage: 1628 + areas: + # 752: lull of the land + # 753: the elderknoll + - area: 752 + steps: |- + - Exit ahead + - area: 753 + steps: |- + - Go forward + - **Skip**: 3 + - stage: 1650 + areas: + # 751: banks of the sogoht + # 527: haulo green + - area: 751 + steps: |- + - **Licences** + + | Character | Board | Licences + | --------- | ----- | -------- + | Ashe | BSH | Mystic Armor 6 + | Ashe | BLM | Staves 3, Black Magick 5, Warmage, Magick Lore + | Ashe | BSH | Headsman + + - **Equip** + - **Ashe**: Optimise, Golden Amulet + - **Gambits** + + | Character | Slot | Target | Action + | --------- | ---- | ------ | ------ + | Ashe | 2 | Foe: highest level | Fira + | Larsa | 1 | Ally: HP < 70% | Cura + | Larsa | 2 | Ally: Ashe | Potion + | Larsa | 3 | Clear | - + | Larsa | 4 | Clear | - + | Larsa | 5 | Foe: any | Move to bottom + | Larsa | 6 | Ally: any | Phoenix Down + + - Touch Save Crystal + - Rent free Chocobo + - area: 527 + steps: |- + - Quit to title + - stage: 0 + areas: + # 12: main menu + - area: 12 + steps: |- + - Trial mode + - stage: 1650 + areas: + # 1155: stage 1 + # 1157: stage 3 + # 1159: stage 5 + # 1163: stage 6 + # 1164: stage 7 + # 1165: stage 8 + - area: 1155 + steps: |- + - Switch to Ashe, try to conserve mana + - Diamond Armlet + - area: 1157 + steps: |- + - Run behind rock for Flowering Cactoid + - area: 1159 + steps: |- + - One Fira only + - Get Bubble Mote from back left chest + - area: 1163 + steps: |- + - **Menu** + - **Licences** + + | Character | Board | Licences + | --------- | ----- | -------- + | Ashe | BSH | Magick Lore above Headsman x2, Serenity + - area: 1164 + steps: |- + - Use two Firas + - area: 1165 + steps: |- + - Return to title + - stage: 0 + areas: + # 12: main menu + - area: 12 + steps: |- + - Load autosave + - stage: 1650 + areas: + # 527: haulo green + # 536: the shred + # 531: field of light winds + - area: 527 + steps: |- + - Exit East to chocobo-only path + - area: 536 + steps: |- + - Exit North to chocobo-only path + - area: 531 + steps: |- + - Return to title + - stage: 0 + areas: + # 12: main menu + - area: 12 + steps: |- + - Trial mode + - stage: 1650 + areas: + # 1155: stage 1 + # 1157: stage 3 + # 1159: stage 5 + # 1164: stage 7 + # 1165: stage 8 + - area: 1155 + steps: |- + - Diamond Armlet + - area: 1157 + steps: |- + - Run behind rock for Flowering Cactoid + - area: 1159 + steps: |- + - One Fira only + - Get Bubble Mote from back left chest + - area: 1164 + steps: |- + - Use two Firas + - area: 1165 + steps: |- + - Return to title + - stage: 0 + areas: + # 12: main menu + - area: 12 + steps: |- + - Load autosave + - stage: 1650 + areas: + # 531: field of light winds + - area: 531 + steps: |- + - Exit East + - stage: 1658 + areas: + # 533: sunlit path + - area: 533 + steps: |- + - **Skip**: 2 + - stage: 1660 + areas: + # 533: sunlit path + # 447: paths of chained light + # 448: the needlebrake + - area: 533 + steps: |- + - Exit East + - area: 447 + steps: |- + - **Skip**: 1 + - Go all the way East + - Exit Southeast + - area: 448 + steps: |- + - **Repeat Trial Mode** + - Go East + - Dismount before barrier + - **Skip**: 1 + - Exit ahead + - stage: 1700 + areas: + # 768: road of verdant praise + - area: 768 + steps: |- + - **Skip**: 1 + - stage: 1740 + areas: + # 768: road of verdant praise + # 767: the spiritwood + # 766: fane of the path + - area: 768 + steps: |- + - Touch crystal + - Exit ahead + - area: 767 + steps: |- + - **Skip**: 1 + - Follow path North, then Southwest at circle to exit + - area: 766 + steps: |- + - **Repeat Trial Mode** + - Go East + - **Skip**: 1 + - stage: 1756 + areas: + # 766: fane of the path + # 767: the spiritwood + # 768: road of verdant praise + - area: 766 + steps: |- + - Exit Southwest + - area: 767 + steps: |- + - **Repeat Trial Mode** + - Go Northeast, then South at the circle + - Go Southwest, exit South + - area: 768 + steps: |- + - **Skip**: 1 + - stage: 1790 + areas: + # 768: road of verdant praise + - area: 768 + steps: |- + - **Shop** + + | Action | Items + | ------ | ----- + | Sell | Diamond Armlet x5 + | Buy | Mage's Hat, Mage's Habit, Shielded Armor, Demonsbane, Esuna, Nu Khai Sand x11 + + - **Licences** + + | Character | Board | Licences + | --------- | ----- | -------- + | Basch | SHK | Battle Lore x4, Swiftness, +270 HP + | Basch | KNT | Swords 5, Heavy Armor 4, Shield Block, Gambit Slot under armor 1 + | Basch | SHK | Focus, +190 HP, Spellbound, Adrenaline, Fulminating Darkness + | Basch | KNT | Accessories 6 + | Ashe | BSH | +310 HP, +230 HP, Magick Lore, +270 HP, Swiftness, Katana 2, +190 HP, Swiftness + | Ashe | BLM | Magick Lore above Headsman, Accessories 14, Spellbound, Accessories 13, Black Magick 9 + | Ashe | BSH | Mystic Armor 8, 7, 11 + | Penelo | WHM | Mystic Armor 5, Ether Lore 1, Second Board + | Penelo | BSH | +190 HP, Inquisitor, +230 HP, Magick Lore, +270 HP, Swiftness + | Penelo | WHM | Gambit Slot next to Attack, bottom left Gambit Slot + | Penelo | BSH | Gambit slot to far right (top) + | Penelo | WHM | Bottom right Spellbound, White Magick 5, 9, 7, Channeling, Magick Lore (left), Mystic Armor 10 + + - **Menu** + - **Party**: Basch, Ashe, Penelo + - **Equip** + - **Basch**, **Ashe**, **Penelo**: Optimise + - **Penelo**: Golden Amulet + - **Ashe**: Flame Staff, Bangle + - **Basch**: Remove weapon, Dawn Shard + - **Gambits** + + | Character | Slot | Target | Action + | --------- | ---- | ------ | ------ + | Basch | 1 | Ally: Any | Nu Khai Sand + | Basch | 2 | Turn off | Move to bottom + | Basch | 2 | Ally: Penelo | Phoenix Down + | Ashe | 1 | Foe: lowest HP | Attack (off) + | Penelo | 1 | Turn off | - + | Penelo | 3 | Ally: any | Esuna + | Penelo | 4 | Foe: highest level | Dispel + | Penelo | 5 | Ally: any | Protect + + - Teleport to Jahara + # 751: Banks of the Sogoht + - area: 751 + steps: |- + - Get chocobo + # 536: The Shred + - area: 536 + steps: |- + - Exit East chocobo-only + - Go East, dismount near Southeast exit + - Walk towards exit + - **Skip**: 1 + - stage: 1810 + areas: + # 536: The Shred + - area: 536 + steps: |- + - Exit ahead + # 712: North Entrance + - area: 712 + steps: |- + - **Skip**: 1 + - Switch to Basch + - Turn off fast-forward for next step + - **Ashe**: Kill Larsa with Fira + - Cancel cast after first hit + - Go South, use switchboard + - Exit South to opened door + # 713: Pithead Junction A + - area: 713 + steps: |- + - Exit West + # 714: Phase 1 Shaft + - area: 714 + steps: |- + - Follow path South + - Group and kill bats + - Exit West + # 715: Phase 1 Dig + - area: 715 + steps: |- + - Follow path + - South at intersection + - East at intersection + - Exit East + # 716: Crossover A + - area: 716 + steps: |- + - Follow path + - Left branch + - Exit East + # 719: Crossover B + - area: 719 + steps: |- + - Slight North, East as far as possible + - Exit South + # 717: Pithead Junction B + - area: 717 + steps: |- + - Use button + - Have Ashe kill everything + - Don't pick up drops + - Exit West + # 716: Crossover A + - area: 716 + steps: |- + - Go West + - North at branch + - Exit East + # 719: Crossover B + - area: 719 + steps: |- + - Exit way you just entered + # 716: Crossover A + - area: 716 + steps: |- + - Go South + - East branch to exit + # 717: Pithead Junction B + - area: 717 + steps: |- + - Use button + - Pick up gold loot + - Exit North + # 719: Crossover B + - area: 719 + steps: |- + - Go North, West to exit + # 716: Crossover A + - area: 716 + steps: |- + - Go back and return to jellies room + # 717: Pithead Junction B + - area: 717 + steps: |- + - Continue farming until ~75 chain + - Exit West and go to second screen + # 719: Crossover B + - area: 719 + steps: |- + - Exit North + # 718: Staging Shaft + - area: 718 + steps: |- + - **Skip**: 1 + - stage: 1820 + areas: + # 718: Staging Shaft + - area: 718 + steps: |- + - Touch crystal + - **Licences** + + | Character | Board | Licences + | --------- | ----- | -------- + | Penelo | WHM | Serenity, Gambit under Mystic Armor 1 + | Basch | SHK | Ninja Swords 1 above Quickening + + - **Menu** + - **Equip** + - **Basch**: Optimise + - **Ashe**: Optimise + - **Gambits** + + | Character | Slot | Target | Action + | --------- | ---- | ------ | ------ + | Basch | 1 | Ally: any | Echo Herbs + | Basch | 3 | Turn on | - + | Ashe | 1 | Turn on | - + | Penelo | 1 | Turn on | - + | Penelo | 6 | Ally: Penelo | Echo Herbs (move to top) + | Penelo | 3 | Ally: HP < 70% | Cura + + - Go North + - Bacchus Wine on Ashe before exit if possible + - Exit + # 722: Ore Separation + - area: 722 + steps: |- + - **Skip**: 1 + - **Tiamat** + - Control Penelo + - Stay spread out + - Gambit to win + - **Skip**: 3 + - stage: 1950 + areas: + # 768: Road of Verdant Praise + - area: 768 + steps: |- + - **Menu** + - **Battle Speed**: slow + - Exit ahead + # 448: The Needlebrake + - area: 448 + steps: |- + - Switch to Basch + - Go West past exit + - Go South at intersection + - Go through barrier + - Follow path East to exit + # 453: The Rustling Chapel + - area: 453 + steps: |- + - Take the Southernmost West path + - Go all the way South to exit + # 539: Walk of Flitting Rifts + - area: 539 + steps: |- + - **Skip**: 1 + - Go East to Exit + # 469: Icebound Flow + - area: 469 + steps: |- + - **Skip**: 1 + - Go North + - Go East to exit + # 468: Frozen Brook + - area: 468 + steps: |- + - Exit North + # 465: Freezing Gorge + - area: 465 + steps: |- + - **Skip**: 2 + - stage: 1976 + areas: + # 465: Freezing Gorge + - area: 465 + steps: |- + - Go all the way North to exit + # 742: Sand-strewn Pass + - area: 742 + steps: |- + - **Skip**: 1 + - stage: 1982 + areas: + # 742: Sand-strewn Pass + - area: 742 + steps: |- + - **Shop** + - **Sell**: All loot but Teleport Stones + - \> 50,000 gil = no Thief's Cuffs + - \> 52,000 gil = no Thief's Cuffs or Ruby Ring + - Touch crystal + - Exit North + - stage: 1984 + areas: + # 741: Temple Approach + - area: 741 + steps: |- + - Follow path to exit + # 738: Temple Grounds + - area: 738 + steps: |- + - **Skip**: 1 + - stage: 1986 + areas: + # 738: Temple Grounds + - area: 738 + steps: |- + - Follow path to temple + # 736: Hall of the Light + - area: 736 + steps: |- + - Go forward + - **Skip**: 3 + - stage: 2050 + areas: + # 736: Hall of the Light + - area: 736 + steps: |- + - Exit ahead + # 738: Temple Grounds + - area: 738 + steps: |- + - Retrace your steps + # 741: Temple Approach + - area: 741 + steps: |- + - Retrace your steps + # 742: Sand-strewn Pass + - area: 742 + steps: |- + - Rent chocobo + # 465: Freezing Gorge + - area: 465 + steps: |- + - Go all the way South to exit + # 468: Frozen Brook + - area: 468 + steps: |- + - Go all the way South to exit again + # 473: Path of the Firstfall + - area: 473 + steps: |- + - Follow path to exit + # 474: Spine of the Icewyrm + - area: 474 + steps: |- + - Go all the way South to exit + # 475: Silverflow's End + - area: 475 + steps: |- + - Go South-by-Southwest all the way to exit + # 593: Walk of Sky + - area: 593 + steps: |- + - Go forward to exit + # 594: Walk of Mind + - area: 594 + steps: |- + - Dismount in front of door + - Exit + # 597: Ward of Measure + - area: 597 + steps: |- + - **Skip**: 1 + - stage: 2057 + areas: + # 597: Ward of Measure + - area: 597 + steps: |- + - Go around to pedestal + - Touch pedestal + # 606: Ward of Velitation + - area: 606 + steps: |- + - Exit West + # 607: Walk of Torn Illusion + - area: 607 + steps: |- + - Go North and East to pedestal + - Touch pedestal + - Return where you entered + # 606: Ward of Velitation + - area: 606 + steps: |- + - Go South and East to exit + # 608: Walk of Revelation + - area: 608 + steps: |- + - Follow path around to middle exit + # 609: Ward of the Sword-King + - area: 609 + steps: |- + - Go West and South to touch the sword + - Go all the way North and touch device + # 597: Ward of Measure + - area: 597 + steps: |- + - Go West and North + - Exit West + # 598: Cold Distance + - area: 598 + steps: |- + - Exit West + # 599: Walk of Prescience + - area: 599 + steps: |- + - **Ruby Ring**: West to treasure chest, then back East + - Follow North path + - Turn statue to face left + - Exit ahead + # 600: Walk of Reason + - area: 600 + steps: |- + - Follow path to door + - Go through and to next door + - Follow path South to left-most chest with Ashura + - Go back and take stairs North + - Go East and up stairs South + - Continue South to statue + - Turn statue so facing left + - All the way North to exit + # 598: Cold Distance + - area: 598 + steps: |- + - **Menu** + - **Equip** + - **Basch**: Optimise, remove Headgear + - **Battle speed**: Fast + - Follow path + - Exit East + # 603: Ward of Steel + - area: 603 + steps: |- + - Follow path to open door + - **Skip**: 1 + - **Vinuskar** + - Switch to Penelo + - Gambits to win + - Southeast trap: Manafont + - Southwest trap: Rejuvenation + - **Skip**: 1 + - stage: 2100 + areas: + # 603: Ward of Steel + - area: 603 + steps: |- + - Open East door, grabbing chest + - Face statue towards you + - Exit West + # 598: Cold Distance + - area: 598 + steps: |- + - Exit North + # 597: Ward of Measure + - area: 597 + steps: |- + - Go upstairs + - All the way South to device + - Touch device + # 609: Ward of the Sword-King + - area: 609 + steps: |- + - Exit South + # 612: Hall of Worth + - area: 612 + steps: |- + - **Skip**: 1 + - **Mateus** + - Switch to Penelo + - Get West chest for Elixir + - Bubble Mote in East chest if needed + - South trap is mana + - **Skip**: 1 + - stage: 2150 + areas: + # 612: Hall of Worth + - area: 612 + steps: |- + - Switch to Basch + - Exit South + # 615: Vault of the Champion + - area: 615 + steps: |- + - Walk forward + - **Skip**: 1 + - stage: 2200 + areas: + # 615: Vault of the Champion + - area: 615 + steps: |- + - Exit ahead + # 612: Hall of Worth + - area: 612 + steps: |- + - Exit North + # 609: Ward of the Sword-King + - area: 609 + steps: |- + - All the way North + - Use device + # 597: Ward of Measure + - area: 597 + steps: |- + - Exit North + # 594: Walk of Mind + - area: 594 + steps: |- + - **Licences** + + | Character | Board | Licences + | --------- | ----- | -------- + | Basch | SHK | Accessories 18 + | Ashe | BLM | Top-left Quickening, +390 HP + | Ashe | BSH | Katana 4, Battle Lore next to Quickening + | Penelo | BSH | Mystic Armor 7, 8 + | Penelo | WHM | Accessories 12, 14 + + - **Menu** + - **Equip** + - **Penelo**: Orrachea Armlet + - **Ashe**: Sage's Ring, Flame Staff + - **Basch**: Golden Amulet + - **Gambits** + + | Character | Slot | Target | Action + | --------- | ---- | ------ | ------ + | Ashe | 1 | Turn off | - + | Penelo | 1 | Ally: Ashe | Cura (move to #4) + + - Walk forward + - **Skip**: 1 + - stage: 2250 + areas: + # 594: Walk of Mind + - area: 594 + steps: |- + - Teleport to Mt. Bur-Omisace + # 742: Sand-strewn Pass + - area: 742 + steps: |- + - Exit North + - stage: 2290 + areas: + # 741: Temple Approach + - area: 741 + steps: |- + - Follow path + # 738: Temple Grounds + - area: 738 + steps: |- + - Follow path to temple + # 737: Hall of the Light + - area: 737 + steps: |- + - **Skip**: 1 + - **Judge Bergan** + - Gambits to win + - **Skip**: 2 + - stage: 3050 + areas: + # 738: Temple Grounds + - area: 738 + steps: |- + - Exit West + # 741: Temple Approach + - area: 741 + steps: |- + - Exit South + # 742: Sand-strewn Pass + - area: 742 + steps: |- + - Teleport to Nalbina + # 630: West Barbican + - area: 630 + steps: |- + - Exit North + # 209: Southern Skirts + - area: 209 + steps: |- + - **Skip**: 1 + - All the way West to exit + # 210: Summit Path + - area: 210 + steps: |- + - Go West + - Exit South + # 133: Broken Sands + - area: 133 + steps: |- + - Go Southwest for Berserker Bracers + - Exit North + # 210: Summit Path + - area: 210 + steps: |- + - Exit Northwest + # 1151: Babbling Vale + - area: 1151 + steps: |- + - Go North-by-Northwest + - **Thief's Cuffs**: Grab chest next to exit + - Exit North + # 218: Trail of Sky-flung Stone + - area: 218 + steps: |- + - Take East branch + - Exit North + # 219: Northern Skirts + - area: 219 + steps: |- + - Go West, then North + - Exit Northwest + # 220: Halny Crossing + - area: 220 + steps: |- + - Exit Northwest + # 151: The Omen-Spur + - area: 151 + steps: |- + - **Skip**: 1 + - Follow path + - Exit Northeast + # 156: Path of Hours + - area: 156 + steps: |- + - Exit North + # 152: Trunkwall Road + - area: 152 + steps: |- + - Go all the way East + - Take every North and East + - Exit East + # 153: Diverging Way + - area: 153 + steps: |- + - Take Southernmost East path + - Exit East + # 908: Living Chasm + - area: 908 + steps: |- + - Talk to moogle + - Exit + # 153: Diverging Way + - area: 153 + steps: |- + - Exit Northwest + # 152: Trunkwall Road + - area: 152 + steps: |- + - North, South, West, South + - Talk to moogle in the nub + - South, then West to exit + # 156: Path of Hours + - area: 156 + steps: |- + - Talk to Eastern moogle + - Exit North + # 152: Trunkwall Road + - area: 152 + steps: |- + - Go North + - Exit West + # 154: Sun-dappled Path + - area: 154 + steps: |- + - Follow path South, then North + - Exit North + # 155: Garden of Decay + - area: 155 + steps: |- + - Go all the way North + - Talk to both moogles + - Go with them to the gate + - stage: 3070 + areas: + # 908: Living Chasm + - area: 908 + steps: |- + - Talk to the moogle boss + - Exit South + # 483: Kaukula Pass + - area: 483 + steps: |- + - **Skip**: 1 + - Exit South + # 478: The Reseta Strand + - area: 478 + steps: |- + - Exit Southeast + # 479: Pora-Pora Sands + - area: 479 + steps: |- + - Exit Southeast + # 481: Cape Uahuk + - area: 481 + steps: |- + - Exit East + # 488: Hunters' Camp + - area: 488 + steps: |- + - Go West + - **Skip**: 1 + - stage: 3100 + areas: + # 488: Hunters' Camp + - area: 488 + steps: |- + - **Shop** + + | Action | Items + | ------ | ----- + | Buy | Astrakhan Hat (Ashe), Carmagnole (Ashe), Firaga, Remedy x4, Bacchus's Wine (x11), Gold Needle x2, Echo Herbs x3 + + - **Gambits** + + | Character | Slot | Target | Action + | --------- | ---- | ------ | ------ + | Ashe | 2 | Foe: highest level | Firaga + + - Rent a chocobo (Tchita) + # 491: Caima Hills + - area: 491 + steps: |- + - Exit Northeast + # 493: Limatra Hills + - area: 493 + steps: |- + - Exit Northeast + # 494: Rava's Pass + - area: 494 + steps: |- + - Exit North + # 145: Realm of the Elder Dream + - area: 145 + steps: |- + - **Skip**: 1 + - Go Southeast + - Exit East + # 144: The Skytrail + - area: 144 + steps: |- + - Go East + - Take South-facing exit after bridge + # 142: The Chosen Path + - area: 142 + steps: |- + - Dismount + - Touch crystal + - Talk to child + - Switch to Ashe + - Exit East + # 139: The Highlands + - area: 139 + steps: |- + - Go East, killing snakes + - Exit North + # 140: Fields of Eternity + - area: 140 + steps: |- + - Exit North + - stage: 3120 + areas: + # 187: Doubt Abandoned + - area: 187 + steps: |- + - Don't save + - **Skip**: 1 + - stage: 3130 + areas: + # 187: Doubt Abandoned + - area: 187 + steps: |- + - Switch to Basch + - Take the East path North + - Take last West + - Go North to exit + # 200: Hall of Lambent Darkness + - area: 200 + steps: |- + - **Skip**: 1 + - **Mandragoras** + - Gambits to win + - Pilot Ashe to finish if necessary + - stage: 3150 + areas: + # 200: Hall of Lambent Darkness + - area: 200 + steps: |- + - **Skip**: 1 + - Exit East + # 186: The Acolyte's Burden + - area: 186 + steps: |- + - Get South chest + - Follow path North to dip + - Get chest + - Go North + - Exit East + # 185: Mirror of the Soul + - area: 185 + steps: |- + - Go East, then North + - Follow path all the way West + - Exit North + # 184: Falls of Time + - area: 184 + steps: |- + - Go Northeast + - Exit North + # 192: Destiny's March + - area: 192 + steps: |- + - Go Northeast to door + - Go North to door (x3) + - Exit North + # 198: Hall of Shadowlight + - area: 198 + steps: |- + - **Skip**: 1 + - **Ahriman** + - Switch to Penelo + - Get West chest + - Gambits to win + - **Skip**: 1 + - stage: 3200 + areas: + # 198: Hall of Shadowlight + - area: 198 + steps: |- + - Use Float Mote + - Exit East + # 193: Temptation Eluded + - area: 193 + steps: |- + - Follow path + - Exit North + # 194: Chamber of the Chosen + - area: 194 + steps: |- + - Go up the slope + - Use pedestal + - **Skip**: 1 + # 188: Skybent Chamber + - area: 188 + steps: |- + - Exit North + - **Skip**: 1 + - stage: 3210 + areas: + # 763: Alley of Low Whispers + - area: 763 + steps: |- + - Go Northwest + - Go West + - Follow path North to West exit + # 762: Alley of Muted Sighs + - area: 762 + steps: |- + - Talk to Lucky Man + - Go West, then South toward save crystal + - Touch crystal + - Talk to Imperial + - Go North and East to man in corner + - Talk, then go West + - Talk to Jules + - **Skip**: 1 + - stage: 3250 + areas: + # 641: Trant + - area: 641 + steps: |- + - Run forward + - Exit Northwest + - stage: 3253 + areas: + # 640: Molberry + - area: 640 + steps: |- + - Exit South + # 745: Nilbasse + - area: 745 + steps: |- + - East to Weapon Shop + # 747: Vint's Armaments + - area: 747 + steps: |- + - **Shops** + - **Armor** (31,800 gil) + + | Action | Items + | ------ | ----- + | Sell | Weapons except Flame Staff, Ashura, and Sword of Kings + | Buy | Gaia Hat, Maduin Gear, Platinum Helm, Platinum Armor + + - **Weapons** (9,300 gil, need 2,500 gil left over) + + | Action | Items + | ------ | ----- + | Buy | Yakei + + - **Licences** + + | Character | Board | Licences + | --------- | ----- | -------- + | Basch | SHK | Battle Lore (bottom) + | Basch | KNT | Heavy Armor 8, Greatswords 2 + | Ashe | BSH | Battle Lore (down) + | Penelo | BSH | +110 HP, Swiftness (up), Belias + + - **Equip** + - **Basch**, **Ashe**, **Penelo**: Optimise + - **Ashe**: Flame Staff + - Exit + # 745: Nilbasse + - area: 745 + steps: |- + - Ahead to cabbie + - stage: 3255 + areas: + # 745: Nilbasse + - area: 745 + steps: |- + - Woman in purple (Senior Researcher) + - East to man in purple (Researcher) + - Grey man with blonde hair (Archadian Gentry, Wages Halved) + - West to woman with green top (Archadian Gentry) + - Grey, blonde man in front of cab (Archadian Gentry, Working up a Sweat) + - Running man in shorts (Eager Crier) + - Back to cab guide + - stage: 3260 + areas: + # 756: Tsenoble + - area: 756 + steps: |- + - Go North + - **Skip**: 1 + - stage: 3300 + areas: + # 756: Tsenoble + - area: 756 + steps: |- + - Go South + - Talk to cabbie, option 2 + - **Skip**: 1 + - stage: 3350 + areas: + # 1008: 66th Floor + - area: 1008 + steps: |- + - Go West + - stage: 3355 + areas: + # 1008: 66th Floor + - area: 1008 + steps: |- + - Go all the way North to exit + - Go to Floor 67 + # 1019: 67th Floor + - area: 1019 + steps: |- + - Go East + - Exit North + - **Skip**: 1 + - stage: 3400 + areas: + # 1054: 67th Floor + - area: 1054 + steps: |- + - Enter second room to the right + # 1060: Rm 6703 East + - area: 1060 + steps: |- + - Use Bulkhead Controls + - Exit + # 1054: 67th Floor + - area: 1054 + steps: |- + - Go back to elevator + - Go to Floor 68 + # 1067: 68th Floor + - area: 1067 + steps: |- + - Go left, then left again + - Enter room + # 1079: Rm 6803 East + - area: 1079 + steps: |- + - Use Bulkhead Controls + - Exit + # 1067: 68th Floor + - area: 1067 + steps: |- + - Go left three times + - Enter room + # 1076: Rm 6804 West + - area: 1076 + steps: |- + - Use Bulkhead Controls + - Exit + # 1067: 68th Floor + - area: 1067 + steps: |- + - Left, right, right, left, left + - Enter room + # 1074: Rm 6811 West + - area: 1074 + steps: |- + - Use Bulkhead Controls + - Exit + # 1067: 68th Floor + - area: 1067 + steps: |- + - Left, left, right to elevator + - Go to Floor 70 + - **Skip**: 1 + - stage: 3440 + areas: + # 1047: 70th Floor + - area: 1047 + steps: |- + - Touch crystal + - Make sure party has Protect + - Exit upstairs + # 286: Energy Transitarium + - area: 286 + steps: |- + - **Skip**: 1 + - **Cid 1** + - Switch to Penelo + - Let Ashe Fira twice to kill bits + - **Equip**: + - **Ashe**: Yakei, Berserker Bracelets + - **Penelo**: Sage's Ring + - South chests for Holy Mote and Hastega Mote + - **Skip**: 5 + - stage: 4150 + areas: + # 826: Saccio Lane + - area: 826 + steps: |- + - Exit Northeast + # 825: Quayside Court + - area: 825 + steps: |- + - **Equip**: **Basch**: Sash + - Exit East + # 824: Gallerina Marketplace + - area: 824 + steps: |- + - Go North to Magic Shop + - **Shop** (7,700 gil) + + | Action | Items + | ------ | ----- + | Sell | Shielded Armor, Mage's Habit, Mage's Hat, Ruby Ring + | Buy | Curaja + + - Exit Northeast + # 823: Sea Breeze Lane + - area: 823 + steps: |- + - Get chest south of shop + - Teleport to Mt. Bur-Omisace + # 742: Sand-strewn Pass + - area: 742 + steps: |- + - Rent chocobo + # 465: Freezing Gorge + - area: 465 + steps: |- + - Exit South + # 468: Frozen Brook + - area: 468 + steps: |- + - Exit West + # 469: Icebound Flow + - area: 469 + steps: |- + - Exit Southwest + # 539: Walk of Flitting Rifts + - area: 539 + steps: |- + - Dismount before Southeast exit + - Exit Southeast + # 540: Walk of Stolen Truths + - area: 540 + steps: |- + - All the way South (stay Westish) to exit + # 542: Antiquity's End + - area: 542 + steps: |- + - Touch crystal + - Exit South + - **Skip**: 1 + - stage: 4160 + areas: + # 542: Antiquity's End + - area: 542 + steps: |- + - **Basch**: Bacchus's Wine + - **Penelo**: Hastega Mote + - Exit ahead + # 545: Redolent Glade + - area: 545 + steps: |- + - **Skip**: 1 + - **Rafflesia** + - Childish Gambito + - **Skip**: 1 + - stage: 4200 + areas: + # 545: Redolent Glade + - area: 545 + steps: |- + - Go North + - Get chest to the West before exit + # 542: Antiquity's End + - area: 542 + steps: |- + - Touch crystal + - Exit South + # 545: Redolent Glade + - area: 545 + steps: |- + - Switch to Basch + - **Equip** + - **Ashe**: Flame Staff, remove accessory + - Exit South + # 548: White Magick's Embrace + - area: 548 + steps: |- + - Go Southwest to exit + # 549: Ice Field of Clearsight + - area: 549 + steps: |- + - Go into Southwest shrine + - Kill mantis + - Open menu and find exit with greenery + - Divert West slightly for chest with Bubble Belt + - Go to next shrine + - Kill behemoths + - Exit Southwest + # 550: The Edge of Reason + - area: 550 + steps: |- + - Go Southwest to middle shrine + - Follow shrines, killing stuff + - At Southwest shrine, go East for Defender chest + - **Equip**: **Basch**: Optimise + - **Gambits** + + | Character | Slot | Target | Action + | --------- | ---- | ------ | ------ + | Penelo | 4 | Ally: HP < 50% | Curaja (move to #2) + + - Go back to Southwest shrine, go to West door + - Summon Belias + - Exit + - **Skip**: 2 + - stage: 4250 + areas: + # 686: Gate of Earth + - area: 686 + steps: |- + - Go forward + - Touch crystal + - Use device + - **Skip**: 1 + - stage: 4295 + areas: + # 687: Gate of Water + - area: 687 + steps: |- + - **Before gate** + - Switch to Penelo + - **Basch**: Bacchus's Wine + - **Ashe**: Domaine Calvados on Basch + - Go forward + - **Skip**: 1 + - **Daedalus** + - Nice gambits bro + - **Skip**: 1 + - stage: 4300 + areas: + # 687: Gate of Water + - area: 687 + steps: |- + - **Battle Speed**: slow + - Touch device + # 690: The Trimahla Water-Steps + - area: 690 + steps: |- + - Despawns? TODO + - Go South, use device + - Continue South, use device + - Go North, up one ramp and down one + - Then go South + - Follow path to exit + # 691: The Aadha Water-Steps + - area: 691 + steps: |- + - Despawn enemies + - Follow path, skipping devices + - At barrier, continue South and follow path + - Use device + - Return to Northwest-most exit + # 694: The Haalmikah Water-Steps + - area: 694 + steps: |- + - **Skip**: 1 + - stage: 4320 + areas: + # 694: The Haalmikah Water-Steps + - area: 694 + steps: |- + - **Licences** + + | Character | Board | Licences + | --------- | ----- | -------- + | Basch | SHK | Accessories 17 + + - **Equip** + - **Basch**: Bubble Belt + - **Ashe**: Berserker Bracers, Optimise + - **Battle Speed**: fast + - Switch to Basch + - Turn gambits back on + - Go forward + - Touch crystal + - Exit + # 695: Gate of Fire + - area: 695 + steps: |- + - Go Southwest, then Northwest + - Go Northeast for hidden path + - Before boss, have Penelo use Bubble Mote on Ashe + - **Skip**: 1 + - **Tyrant** + - Switch to Penelo + - Around 70% HP, use Bacchus's Wine on Basch + - **Skip**: 1 + - stage: 4350 + areas: + # 695: Gate of Fire + - area: 695 + steps: |- + - **Battle Speed**: slow + - **Equip**: **Ashe**: remove Berserker Bracers + - Switch to Ashe + - Use device + # 621: A Prama Vikaari + - area: 621 + steps: |- + - Exit to Ashe's right + # 622: Kabonii Jilaam Pratii'vaa + - area: 622 + steps: |- + - Go forward + - Exit left + # 623: Kabonii Jilaam Avaa + - area: 623 + steps: |- + - Go forward + - Exit right + # 624: Dha Vikaari Bhrum + - area: 624 + steps: |- + - Use device + # 926: A Vikaari Kabonii + - area: 926 + steps: |- + - Exit to Ashe's far left + # 927: Sthaana Cancer + - area: 927 + steps: |- + - Use device + - Return the way you came + # 926: A Vikaari Kabonii + - area: 926 + steps: |- + - Switch to Basch + - Exit far left + # 929: Bhrum Pis Pratii + - area: 929 + steps: |- + - Get chest + - Continue straight + # 930: Dha Vikaari Trahk + - area: 930 + steps: |- + - Use device + # 332: A Vikaari Bhrum + - area: 332 + steps: |- + - Exit to Basch's slight left + # 333: Trahk Pis Praa + - area: 333 + steps: |- + - Exit right + # 334: Sthaana Pisces + - area: 334 + steps: |- + - Use device + - Return the way you came + # 333: Trahk Pis Praa + - area: 333 + steps: |- + - Grab chest + - Exit left + # 332: A Vikaari Bhrum + - area: 332 + steps: |- + - Exit left + # 335: Dha Vikaari Jula + - area: 335 + steps: |- + - Use device + # 341: Crystal Core + - area: 341 + steps: |- + - **Skip**: 1 + - stage: 4370 + areas: + # 341: Crystal Core + - area: 341 + steps: |- + - Go forward + - Touch crystal + - Use device + # 698: Gate of Wind + - area: 698 + steps: |- + - **Gambits** + + | Character | Slot | Target | Action + | --------- | ---- | ------ | ------ + | Basch | 1 | Ally: Penelo | Echo Herbs + | Basch | 3 | Foe: highest level | Attack + + - **Equip** + - **Ashe**, **Penelo**: Optimise + - **Ashe**: Berserker Bracers + - **Battle Speed**: fast + - Use Bubble Mote on Ashe + - Use door + - **Skip**: 1 + - **Shemhazai** + - Switch to Penelo + - Run around arena with Penelo for MP + - Get West chest for Hastega Mote + - **Skip**: 1 + - stage: 4400 + areas: + # 698: Gate of Wind + - area: 698 + steps: |- + - Switch to Basch + - Exit ahead + - Use device downstairs + - **Skip**: 3 + - stage: 5100 + areas: + # 698: Gate of Wind + - area: 698 + steps: |- + - Use device + # 686: Gate of Earth + - area: 686 + steps: |- + - Go East + - Teleport to Balfonheim + # 823: Sea Breeze Lane + - area: 823 + steps: |- + - Exit South + # 824: Gallerina Marketplace + - area: 824 + steps: |- + - Exit West + # 825: Quayside Court + - area: 825 + steps: |- + - Go West to Weapon Shop + # 833: Beruny's Armaments + - area: 833 + steps: |- + - **Weapon Shop** (11,600 gil) + + | Action | Items + | ------ | ----- + | Sell | Ashura, Carmagnole + | Buy | Ame-no-Murakumo + # 825: Quayside Court + - area: 825 + steps: |- + - Exit West + # 826: Saccio Lane + - area: 826 + steps: |- + - Go South + - Talk to guard + - **Skip**: 1 + - stage: 5160 + areas: + # 826: Saccio Lane + - area: 826 + steps: |- + - **Gambits** + + | Character | Slot | Target | Action + | --------- | ---- | ------ | ------ + | Reddas | 1 | Off | - + | Reddas | 3 | Off | - + | Reddas | 4 | Off | - + | Reddas | 5 | Off | - + | Reddas | 2 | Foe: highest level | Move to #10 + | Reddas | 9 | Ally: any | Eye Drops + | Reddas | 8 | Ally: Penelo | Echo Herbs + + - Exit North + # 825: Quayside Court + - area: 825 + steps: |- + - Exit East + # 824: Gallerina Marketplace + - area: 824 + steps: |- + - Exit first South + # 827: Chivany Breakwater + - area: 827 + steps: |- + - Exit East + # 797: Aerodrome (Balfonheim Port) + - area: 797 + steps: |- + - Back left airship NPC + - Travel to The Ridorana Cataract + - **Skip**: 2 + - stage: 5200 + areas: + # 509: Footfalls of the Past + - area: 509 + steps: |- + - Exit East + # 511: Echoes from Time's Garden + - area: 511 + steps: |- + - Take middle North path to exit + # 517: City of Other Days + - area: 517 + steps: |- + - Follow main path East to exit + # 518: Path of Hidden Blessing + - area: 518 + steps: |- + - Follow path (hug right) + - Avoid Save Crystal traps + - Touch crystal + - Exit North + - **Skip**: 1 + # 522: They Who Thirst Not + - area: 522 + steps: |- + - **Basch**: Domaine Calvados + - **Reddas**: Domaine Calvados on Ashe + - **Basch**: Bacchus's Wine + - **Reddas**: Bubble Mote on Ashe + - Switch to Penelo + - Go East + - **Skip**: 1 + - **Hydro** + - Run circles for MP + - Use Elixir on Penelo if Fearga'd + - **Skip**: 1 + - stage: 5300 + areas: + # 522: They Who Thirst Not + - area: 522 + steps: |- + - Go East + - **Skip**: 2 + - stage: 5303 + areas: + # 1101: The Wellspring + - area: 1101 + steps: |- + - Switch to Basch + - Touch crystal Northwest + - Exit South + # 1111: Wellspring Labyrinth + - area: 1111 + steps: |- + - **Equip**: **Ashe**: Optimise + - Follow path all the way East to exit + - Kill stuff for Black Orbs on the way + - 2/3 orbs here, 0/1 on North side + # 1101: The Wellspring + - area: 1101 + steps: |- + - Follow path North to device and use + - Return the way you came + # 1111: Wellspring Labyrinth + - area: 1111 + steps: |- + - Follow path all the way West to exit + # 1101: The Wellspring + - area: 1101 + steps: |- + - Go Southeast + - Use device + - Exit North + # 1111: Wellspring Labyrinth + - area: 1111 + steps: |- + - Follow path all the way East to exit + # 1101: The Wellspring + - area: 1101 + steps: |- + - Follow path + - Use device + - Return the way you came + # 1111: Wellspring Labyrinth + - area: 1111 + steps: |- + - Go up the stairs + - Go North and take exits until there's an intersection + - Go East at intersection + - Follow path to door + - Use door + # 1113: Dunes of Profaning Wind + - area: 1113 + steps: |- + - Go ahead and right + - **Basch**: Domaine Calvados + - **Skip**: 1 + - **Pandaemonium** + - Switch to Penelo + - Run circles for MP + - Have Reddas use Arise on boss after Greater Barrier + - **Skip**: 1 + # 1114: Blackrock Vault + - area: 1114 + steps: |- + - Switch to Basch + - **Licences** + + | Character | Board | Licences + | --------- | ----- | -------- + | Basch | KNT | Greatswords 3, Heavy Armor 10 + | Ashe | BSH | Swiftness (down), +435 HP, Katana 5 + | Penelo | WHM | Rods 3, Channeling, Battle Lore (right), Channeling, Quickening (top mid) + + - **Gambits** + + | Character | Slot | Target | Action + | --------- | ---- | ------ | ------ + | Penelo | 2 | Turn off | - + | Penelo | 5 | Turn off | - + | Penelo | 6 | Ally: Penelo | Protect + + - Exit to dark door + # 1111: Wellspring Labyrinth + - area: 1111 + steps: |- + - Grab chest for Hermes Sandals + - Go all the way North + - Go all the way West + - Go South, down the stairs, and exit + # 1101: The Wellspring + - area: 1101 + steps: |- + - Touch crystal + - Use teleporter device + - stage: 5320 + areas: + # 1115: Wellspring Ravel - 1st Flight + - area: 1115 + steps: |- + - Use door ahead + - Go North, kill enemies + - Go up the stairs, kill Brainpans + - Follow new path, kill enemies, then Brainpan + - Follow path, kill enemies and Brainpan + - Continue up the stairs and around corner for another Brainpan + - Go through doorway and North, kill Brainpan + - Get chest for Dragon Helm + - Go back downstairs and East across new path + - Flee to exit + # 1116: Wellspring Ravel - 2nd Flight + - area: 1116 + steps: |- + - Follow path to Brainpan in corner + - Go in doorway and West to Brainpan + - Exit doorway and West to Brainpan + - Follow path West to Brainpan x3 + - Go across new path to exit + # 1118: Wellspring Ravel - 3rd Flight + - area: 1118 + steps: |- + - Follow path to Brainpan x2 + - Go up the stairs and kill enemies + - Use Southwest red door + - Ahead to Brainpan + - South to Brainpan + - Set up Penelo for despawns here + - Run to the corner in front of East stairs, despawn + - Go up the East stairs, bring Penelo + - Follow path to Brainpan x1 (if despawn worked) + - Follow new path, kill Brainpan (avoid spooky Brainpan) + - Follow path until intersection + - Kill Brainpan x9 by respawning in the loop + - Go across new path to exit + # 1119: Wellspring Ravel - 4th Flight + - area: 1119 + steps: |- + - Follow path to Brainpan x2 + - Continue following path + - Kill Brainpan on 42F + - Continue on the main path across green bridge + - Follow path to exit + # 1102: Horizon's Break + - area: 1102 + steps: |- + - Switch to Basch + - Touch crystal + - Go South + - **Equip**: **Ashe**: Flame Staff, remove Berserker Bracers + - Exit in the L-shaped corridor + # 1122: Horizon's Cusp + - area: 1122 + steps: |- + - Follow path + - Grab both chests before door + - Open door + # 1121: Marsh of Profaning Wind + - area: 1121 + steps: |- + - Go right + - **Skip**: 1 + - **Slyt** + - Switch to Penelo + - **Skip**: 1 + # 1122: Horizon's Cusp + - area: 1122 + steps: |- + - Open door ahead + - Follow path + - Use device + - **Skip**: 1 + # 1103: The Reach + - area: 1103 + steps: |- + - Go South + - Use device + - Turn off Basch's gambits + - Exit through purple door + - Go left + - Go upstairs to exit + # 1134: Station of Banishment + - area: 1134 + steps: |- + - Go left, right, right + - Go upstairs + # 1136: Station of Suffering + - area: 1136 + steps: |- + - Go left, ahead, right, left + - Follow path all the way back + - Go north upstairs to exit + # 1138: Station of Ascension + - area: 1138 + steps: |- + - **Battle speed**: slow + - Go ahead, right, ahead, right + - Go out to the circle + - Go West to the stairs + - Go West and South to chest with Dragon Mail + - Go back to circle + - Go North to stairs + - Use West illusory wall + - Exit upstairs + # 1104: Reach of the Damned + - area: 1104 + steps: |- + - Go to circle + - Touch crystal + - Go South + - Take the L-shaped corridor to exit + # 1133: The Bounds of Truth + - area: 1133 + steps: |- + - Follow path + - Grab both chests before door + - **Equip** + - **Penelo**, **Ashe**, **Basch**: optimise + - **Ashe**: Berserker Bracers + - **Gambits** + + | Character | Slot | Target | Action + | --------- | ---- | ------ | ------ + | Penelo | 2 | Ally: HP < 70% | Curaja (on) + | Penelo | 5 | Turn on | - + + - **Battle speed**: fast + - **Basch**: Domaine Calvados on Ashe + - **Penelo**: Bubble Mote on Ashe + - **Basch**: Turn on Gambits + - Switch to Reddas + - Exit + # 1132: Cleft of Profaning Wind + - area: 1132 + steps: |- + - **Skip**: 1 + - **Fenrir** + - Switch to Penelo + - Run in circles for MP + - **Skip**: 1 + # 1133: The Bounds of Truth + - area: 1133 + steps: |- + - Switch to Basch + - Exit ahead + - Follow path to exit + - Go South + - Use device + - **Skip**: 1 + - Go South to elevator + - Go to floor 67 + - **Skip**: 1 + # 1105: Reach of the Occult + - area: 1105 + steps: |- + - Go towards camera + - Use device + - **Skip**: 1 + # 1140: Spire Ravel - 1st Flight + - area: 1140 + steps: |- + - Go forward + - Go Northwest + - Touch Black Sigil + - Go forward + - Turn off Basch's gambits + - Go South to illusory wall + - Touch Green Sigil + # 1141: Spire Ravel - 2nd Flight + - area: 1141 + steps: |- + - Get chest ahead + - Take right path to illusory wall + - Go forward to illusory wall + - Go right + - Touch Red Sigil + - Go forward to Ultima Blade chest + - Touch Purple Sigil (Sigil of Sacrifice) + - **Licences** + + | Character | Board | Licences + | --------- | ----- | -------- + | Penelo | BSH | +350 HP (below Quickening, bot right) + + - **Equip** + - **Basch**, **Ashe**, **Penelo**: Optimise + - **Ashe**: Ame-no-Murakumo + - Use Float Mote if you have one, otherwise let Vaan eat the trap + - Go North, use illusory wall + - Use device + - Run up to elevator + - **Basch**: Domaine Calvados on Ashe + - **Reddas**: Bubble Mote on Ashe + - Go to floor 90 + # 670: Heaven's Challenge + - area: 670 + steps: |- + - **Skip**: 1 + - **Hashmal** + - Switch to Penelo + - Turn on Basch's gambits + - Run in circles for MP + - **Skip**: 1 + # 1143: Empyrean Ravel + - area: 1143 + steps: |- + - Follow path + - **Skip**: 1 + - stage: 5370 + areas: + # 1143: Empyrean Ravel + - area: 1143 + steps: |- + - Follow path + - Touch crystal + - **Equip**: **Ashe**: Optimise + - Follow path + - **Remove Basch** + - Use device + # 666: Womb of the Sun-cryst + - area: 666 + steps: |- + - **Skip**: 2 + - stage: 5470 + areas: + # 668: Womb of the Sun-cryst + - area: 668 + steps: |- + - **Judge Gabranth** + - Switch to Penelo + - **Add Basch** after cutscene + - **Reddas**: Hastega Mote after cutscene attack + - **Skip**: 1 + - stage: 5550 + areas: + # 668: Womb of the Sun-cryst + - area: 668 + steps: |- + - **Cid 2 (Famrit)** + - Move Penelo behind Cid + - **Skip**: 1 + - **After cutscene** + - **Equip**: **Ashe**: Flame Staff, remove Berserker Bracers + - **Reddas**: Dispel Mote Famfrit when buffed + - **Skip**: 1 + - **Cid 2** + - **Equip**: **Ashe**: Optimise, Berserker Bracers + - **Basch**: Bacchus's Wine + - **Skip**: 2 + - stage: 6000 + areas: + # 826: Saccio Lane + - area: 826 + steps: |- + - Don't save + - **Skip**: 1 + - stage: 6110 + areas: + # 826: Saccio Lane + - area: 826 + steps: |- + - Exit North + # 825: Quayside Court + - area: 825 + steps: |- + - Exit East + # 824: Gallerina Marketplace + - area: 824 + steps: |- + - Exit first South + # 827: Chivany Breakwater + - area: 827 + steps: |- + - Follow path to exit + # 797: Aerodrome (Balfonheim Port) + - area: 797 + steps: |- + - Back left airship NPC + - Go to Bahamut + - **Skip**: 4 + - stage: 6450 + areas: + # 777: Antechamber + - area: 777 + steps: |- + - Switch to Basch + - Exit North + # 774: Periphery + - area: 774 + steps: |- + - Follow path + - Exit Southwest + # 775: Catwalk + - area: 775 + steps: |- + - **Skip**: 1 + - stage: 6600 + areas: + # 775: Catwalk + - area: 775 + steps: |- + - Go forward + - Exit West + # 779: Central Lift + - area: 779 + steps: |- + - **Basch**: Domaine Calvados on self and Ashe + - **Penelo**: Bacchus's Wine on Basch + - Switch to Penelo + - Use console + - **Skip**: 1 + - **Judge Gabranth** + - **After cutscene**: Reflectga Mote on Gabranth + - **After special move**: Heal, then Reverse Mote on Penelo + - **Skip**: 1 + - stage: 6750 + areas: + # 779: Central Lift + - area: 779 + steps: |- + - Use lift controls ahead + - **Skip**: 1 + # 782: Central Shaft + - area: 782 + steps: |- + - **Vayne** + - Run in circles for MP + - **After cutscene and special move** + - **Basch**: Domaine Calvados + - **Penelo**: Domaine Calvados on Ashe + - **Basch**: Bubble Mote on Ashe + - **Penelo**: Bubble Mote + - **Basch**: Bacchus's Wine + - **Skip**: 1 + - stage: 6850 + areas: + # 782: Central Shaft + - area: 782 + steps: |- + - **Vayne Novus** + - If more than 1 Hastega Mote, use + - Use Elixir on Penelo for MP if necessary + - Use Holy Mote to skip Tree of Sephira after Inviolable Will + - **Skip**: 2 + - stage: 7100 + areas: + # 785: Cannon Superstructure + - area: 785 + steps: |- + - **Licences** + + | Character | Board | Licences + | --------- | ----- | -------- + | Ashe | BLM | Accessories 20 (bot right) + + - **The Undying** + - **Equip**: **Ashe**: Hermes Bracers + - **Basch**, **Ashe**: Domaine Calvados and Bacchus's Wine + - ATB cancel both + - **Penelo**: Hastega Mote after buffs + - ATB cancel diff --git a/src/app.rs b/src/app.rs new file mode 100755 index 0000000..2f9b329 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,309 @@ +use std::fs::File; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread::JoinHandle; + +use anyhow::{Context, Result}; +use clipboard::ClipboardProvider; +use druid::{AppDelegate, AppLauncher, Command, Data, DelegateCtx, Env, FileDialogOptions, FileSpec, Handled, Lens, Menu, MenuItem, RawMods, Selector, Target, Widget, WindowDesc, WindowId}; +use druid::widget::{Align, Padding}; +use num_format::Locale; + +use crate::{GameState, Notes, NotesState}; +use crate::widget::notes_viewer_scroll::NotesViewerScroll; + +#[derive(Clone, Data, Lens)] +pub struct App { + pub notes: Option<(Notes, String)>, + pub game_state: Option, + pub notes_state: Option, + #[data(same_fn = "PartialEq::eq")] + locale: Locale, + change_counter: u64, + update_thread: Option>>, + update_thread_continue: Arc, +} + +impl App { + fn changed(&mut self) { + self.change_counter = self.change_counter.wrapping_add(1); + } +} + +impl Default for App { + fn default() -> Self { + Self { + notes: None, + game_state: None, + notes_state: None, + locale: Locale::en_GB, + change_counter: 0, + update_thread: None, + update_thread_continue: Arc::new(AtomicBool::new(true)), + } + } +} + +impl App { + const CHANGED: Selector<()> = Selector::new("changed"); + const CONNECT: Selector<()> = Selector::new("connect"); + const DISCONNECT: Selector<()> = Selector::new("disconnect"); + const REFRESH: Selector<()> = Selector::new("refresh"); + + pub fn launch(mut self) -> Result<()> { + let main_window = App::build_window(); + let launcher = AppLauncher::with_window(main_window); + let event_sink = launcher.get_external_handle(); + + let should_continue = Arc::clone(&self.update_thread_continue); + self.update_thread = Some(Arc::new(std::thread::spawn(move || { + while should_continue.load(Ordering::SeqCst) { + event_sink.submit_command( + Self::REFRESH, + (), + Target::Auto, + ).ok(); + + std::thread::sleep(std::time::Duration::from_millis(16)); + } + }))); + + launcher + .delegate(Delegate) + .launch(self) + .context("could not launch app") + } + + fn build_window() -> WindowDesc { + WindowDesc::new(Self::build_root_widget()) + .title("FFXII: TZA Auto Notes") + .menu(Self::build_menu) + .window_size((400.0, 800.0)) + } + + fn build_root_widget() -> impl Widget { + // let location = Label::new(|data: &Option, _: &Env| { + // match &data { + // Some(game_state) => format!( + // "{} ({})", + // game_state.location_name(), + // game_state.location, + // ), + // None => "Not connected".into(), + // } + // }) + // .with_line_break_mode(LineBreaking::WordWrap) + // .lens(App::game_state); + // + // let stage = Label::new(|data: &Option, _: &Env| { + // match &data { + // Some(game_state) => game_state.stage.to_string(), + // None => "Not connected".into(), + // } + // }) + // .with_line_break_mode(LineBreaking::WordWrap) + // .lens(App::game_state); + // + // let notes_state = Label::new(|data: &Option, _: &Env| { + // match data { + // Some(notes_state) => format!("Step {:?}, area {}", notes_state.step_idx, notes_state.area_idx), + // None => "Not connected".into(), + // } + // }) + // .with_line_break_mode(LineBreaking::WordWrap) + // .lens(App::notes_state); + // + // let buttons = Flex::row() + // .with_flex_child( + // Button::new("Stage") + // .on_click(|ctx, data: &mut Option, env: &Env| { + // let game_state = match data { + // Some(game_state) => game_state.clone(), + // None => return, + // }; + // + // if let Ok(mut clipboard) = clipboard::ClipboardContext::new() { + // clipboard.set_contents(format!( + // " - stage: {stage} + // areas: + // # {area_id}: {area_name} + // - area: {area_id} + // steps: |- + // - ", + // stage = game_state.stage, + // area_id = game_state.location, + // area_name = game_state.location_name(), + // )).ok(); + // } + // }) + // .lens(App::game_state), + // 1.0, + // ) + // .with_flex_child( + // Button::new("Area") + // .on_click(|ctx, data: &mut Option, env: &Env| { + // let game_state = match data { + // Some(game_state) => game_state.clone(), + // None => return, + // }; + // + // if let Ok(mut clipboard) = clipboard::ClipboardContext::new() { + // clipboard.set_contents(format!( + // " # {area_id}: {area_name} + // - area: {area_id} + // steps: |- + // - ", + // area_id = game_state.location, + // area_name = game_state.location_name(), + // )).ok(); + // } + // }) + // .lens(App::game_state), + // 1.0, + // ); + + // let info = Flex::column() + // .with_child(location) + // .with_child(Separator::new().with_orientation(Orientation::Horizontal)) + // .with_child(stage) + // .with_child(Separator::new().with_orientation(Orientation::Horizontal)) + // .with_child(notes_state) + // .with_child(Separator::new().with_orientation(Orientation::Horizontal)) + // .with_child(buttons); + + // let layout = Split::columns(NotesViewerScroll::new(), info) + // .draggable(true) + // .split_point(0.75); + + Align::centered(Padding::new((8.0, 8.0, 8.0, 0.0), NotesViewerScroll::new())) + } + + fn build_menu(_: Option, _data: &App, _env: &Env) -> Menu { + Menu::empty() + .entry(Self::build_file_menu()) + } + + fn build_file_menu() -> Menu { + let connect: MenuItem = MenuItem::new("Connect") + .command(Command::new(Self::CONNECT, (), Target::Auto)) + .enabled_if(|data: &App, _: &Env| data.game_state.is_none()); + + let disconnect: MenuItem = MenuItem::new("Disconnect") + .command(Command::new(Self::DISCONNECT, (), Target::Auto)) + .enabled_if(|data: &App, _: &Env| data.game_state.is_some()); + + let yaml = FileSpec::new("Auto notes", &["yml", "yaml"]); + let open_options = FileDialogOptions::new() + .allowed_types(vec![yaml]) + .default_type(yaml) + .name_label("Notes") + .title("Open auto notes") + .button_text("Open"); + let open_notes: MenuItem = MenuItem::new("Open notes") + .command(Command::new( + druid::commands::SHOW_OPEN_PANEL, + open_options, + Target::Auto)) + .hotkey(RawMods::Ctrl, "o"); + + let exit: MenuItem = MenuItem::new("Exit") + .command(druid::commands::CLOSE_ALL_WINDOWS); + + Menu::new("File") + .entry(connect) + .entry(disconnect) + .separator() + .entry(open_notes) + .separator() + .entry(exit) + } + + fn set_up_notes_state(&mut self) { + let notes = match self.notes.clone() { + Some((notes, _)) => notes, + None => return, + }; + + if let Some(game_state) = &mut self.game_state { + match NotesState::new(notes, game_state) { + Ok(notes_state) => self.notes_state = Some(notes_state), + Err(e) => eprintln!("could not set up notes state: {:#?}", e), + } + } + } +} + +struct Delegate; + +impl AppDelegate for Delegate { + fn command(&mut self, _ctx: &mut DelegateCtx, _target: Target, cmd: &Command, data: &mut App, _env: &Env) -> Handled { + if let Some(()) = cmd.get(App::CHANGED) { + data.changed(); + } + + if let Some(()) = cmd.get(App::CONNECT) { + match GameState::new() { + Ok(game_state) => { + data.game_state = Some(game_state); + data.set_up_notes_state(); + data.changed(); + } + Err(e) => eprintln!("could not connect: {:#?}", e), + } + + return Handled::Yes; + } + + if let Some(()) = cmd.get(App::DISCONNECT) { + data.game_state = None; + data.notes_state = None; + } + + if let Some(()) = cmd.get(App::REFRESH) { + if let Some(game_state) = &mut data.game_state { + match &mut data.notes_state { + Some(notes_state) => { + // this will refresh gamestate + if let Err(e) = notes_state.tick(game_state) { + eprintln!("could not refresh notes: {:#?}", e); + } + } + None => if let Err(e) = game_state.refresh() { + eprintln!("could not refresh game state: {:#?}", e); + } + } + } + } + + if let Some(info) = cmd.get(druid::commands::OPEN_FILE) { + match File::open(info.path()) + .context("could not open notes file") + .and_then(|file| serde_yaml::from_reader::<_, Notes>(file).context("could not parse notes file")) + { + Ok(notes) => { + data.notes = Some((notes.clone(), info.path().to_string_lossy().to_string())); + data.set_up_notes_state(); + data.changed(); + } + Err(err) => { + eprintln!("{:#?}", err); + } + } + return Handled::Yes; + } + + Handled::No + } + + fn window_removed(&mut self, _id: WindowId, data: &mut App, _env: &Env, _ctx: &mut DelegateCtx) { + let thread = match data.update_thread.take() { + Some(thread) => thread, + None => return, + }; + + data.update_thread_continue.store(false, Ordering::SeqCst); + if let Ok(thread) = Arc::try_unwrap(thread) { + thread.join().ok(); + } + } +} diff --git a/src/game_state.rs b/src/game_state.rs index e83ac05..993b18a 100644 --- a/src/game_state.rs +++ b/src/game_state.rs @@ -1,21 +1,51 @@ +use std::sync::Arc; + use anyhow::{Context, Result}; +use druid::Data; use sysinfo::{PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt}; use vmemory::ProcessMemory; +#[derive(Clone, Data)] pub struct GameState { - pub location: u16, + pub location: u32, pub gil: u32, pub steps: u32, pub stage: u16, + pub traveler_steps: u16, + pub chain: u16, pid: u32, + #[data(ignore)] base: usize, + #[data(ignore)] base_size: usize, - mem: Vec, - proc_mem: ProcessMemory, + #[data(ignore)] + mem: Arc>, + #[data(ignore)] + proc_mem: Arc, + #[data(ignore)] data_pattern: Vec, + #[data(ignore)] data_addr: usize, + + #[data(ignore)] + traveler_pattern: Vec, + #[data(ignore)] + traveler_addr: usize, + + #[data(ignore)] + chain_pattern: Vec, + #[data(ignore)] + chain_addr: usize, +} + +macro_rules! replace { + ($binding: expr, $value: expr) => {{ + let same = *$binding == $value; + *$binding = $value; + same + }}; } impl GameState { @@ -23,6 +53,7 @@ impl GameState { const GIL_OFFSET: isize = 0x8; const STEPS_OFFSET: isize = 0xC; const STAGE_OFFSET: isize = 0x200; + const TRAVELER_OFFSET: isize = 0x5B00; pub fn new() -> Result { let sys = System::new_with_specifics(RefreshKind::new().with_processes(ProcessRefreshKind::new())); @@ -50,21 +81,31 @@ impl GameState { let tza_mem = mem.read_memory(base, base_size, false); let data_pattern = crate::util::parse_pattern("48 8D 0D ?? ?? ?? ?? 41 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 33 C9 E8").unwrap(); + let traveler_pattern = crate::util::parse_pattern("48 8B 05 ?? ?? ?? ?? 48 85 C0 75 28 E8").unwrap(); + let chain_pattern = crate::util::parse_pattern("89 05 ?? ?? ?? ?? 89 1D ?? ?? ?? ?? 48 8B 87").unwrap(); let mut state = Self { location: 0, gil: 0, steps: 0, stage: 0, + traveler_steps: 0, + chain: 0, pid: pid.as_u32(), base, base_size, - mem: tza_mem, - proc_mem: mem, + mem: Arc::new(tza_mem), + proc_mem: Arc::new(mem), data_pattern, data_addr: 0, + + traveler_pattern, + traveler_addr: 0, + + chain_pattern, + chain_addr: 0, }; state.set_addresses()?; @@ -100,25 +141,36 @@ impl GameState { fn set_addresses(&mut self) -> Result<()> { self.data_addr = self.find_address(&self.data_pattern)?; + self.traveler_addr = self.find_address_indirect(&self.traveler_pattern)?; + self.chain_addr = self.find_address(&self.chain_pattern)?; Ok(()) } - fn read_data(&self, offset: isize) -> [u8; SIZE] { - let addr = self.data_addr as isize + offset; + fn read_data(&self, addr: usize, offset: isize) -> [u8; SIZE] { + let addr = addr as isize + offset; let vec = self.proc_mem.read_memory(addr as usize, SIZE, true); vec.try_into().unwrap() } - pub fn refresh(&mut self) -> Result<()> { - self.location = u16::from_le_bytes(self.read_data(Self::LOCATION_OFFSET)); - self.gil = u32::from_le_bytes(self.read_data(Self::GIL_OFFSET)); - self.steps = u32::from_le_bytes(self.read_data(Self::STEPS_OFFSET)); - self.stage = u16::from_le_bytes(self.read_data(Self::STAGE_OFFSET)); - - Ok(()) + fn replace(binding: &mut T, value: T) -> bool { + let same = *binding == value; + *binding = value; + same } - pub fn location_name(&self) -> Option<&'static str> { - crate::util::LOCATIONS.get(&self.location).map(std::ops::Deref::deref) + pub fn refresh(&mut self) -> Result { + // boolean or short-circuits + let any_changed = replace!(&mut self.location, u32::from_le_bytes(self.read_data(self.data_addr, Self::LOCATION_OFFSET))) + | replace!(&mut self.gil, u32::from_le_bytes(self.read_data(self.data_addr, Self::GIL_OFFSET))) + | replace!(&mut self.steps, u32::from_le_bytes(self.read_data(self.data_addr, Self::STEPS_OFFSET))) + | replace!(&mut self.stage, u16::from_le_bytes(self.read_data(self.data_addr, Self::STAGE_OFFSET))) + | replace!(&mut self.traveler_steps, u16::from_le_bytes(self.read_data(self.traveler_addr, Self::TRAVELER_OFFSET))) + | replace!(&mut self.chain, u16::from_le_bytes(self.read_data(self.chain_addr, 0))); + + Ok(any_changed) + } + + pub fn location_name(&self) -> &'static str { + crate::util::location_name(self.location) } } diff --git a/src/main.rs b/src/main.rs old mode 100644 new mode 100755 index 167ff88..a001a88 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,19 @@ +#![windows_subsystem = "windows"] + use anyhow::Result; -use std::fs::File; -use std::thread; -use crate::notes::Notes; + use crate::game_state::GameState; +use crate::notes::Notes; use crate::notes_state::NotesState; +mod app; mod notes; mod game_state; mod notes_state; mod util; +mod widget; fn main() -> Result<()> { - let notes: Notes = serde_yaml::from_reader(File::open("notes.yaml")?)?; - let mut game_state = GameState::new()?; - let mut notes_state = NotesState::new(¬es, &mut game_state)?; - - println!("Ready"); - - loop { - notes_state.tick(&mut game_state)?; - thread::sleep(std::time::Duration::from_millis(16)); - } + let app = crate::app::App::default(); + app.launch() } diff --git a/src/notes.rs b/src/notes.rs index 0d8b02e..2c31258 100644 --- a/src/notes.rs +++ b/src/notes.rs @@ -1,18 +1,19 @@ +use druid::{Data, im::Vector}; use serde::Deserialize; -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone, Data)] pub struct Notes { - pub steps: Vec, + pub steps: Vector, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone, Data)] pub struct Step { pub stage: u16, - pub areas: Vec, + pub areas: Vector, } -#[derive(Debug, Deserialize, Clone)] +#[derive(Debug, Deserialize, Clone, Data)] pub struct Area { - pub area: u16, + pub area: u32, pub steps: String, } diff --git a/src/notes_state.rs b/src/notes_state.rs index 92774c5..542c456 100644 --- a/src/notes_state.rs +++ b/src/notes_state.rs @@ -1,35 +1,37 @@ +use std::sync::Arc; + use anyhow::Result; +use druid::Data; + use crate::{GameState, Notes}; use crate::notes::{Area, Step}; -pub struct NotesState<'a> { - notes: &'a Notes, - last_printed_location: u16, +#[derive(Clone, Data)] +pub struct NotesState { + pub notes: Arc, last_stage: u16, - last_location: u16, + last_location: u32, - step_idx: usize, - area_idx: usize, + pub step_idx: Option, + pub area_idx: usize, - current_step: &'a Step, - first_step: bool, - force_print: bool, + current_step: Step, } -impl<'a> NotesState<'a> { - pub fn new(notes: &'a Notes, game: &mut GameState) -> Result { +impl NotesState { + pub fn new(notes: Notes, game: &mut GameState) -> Result { game.refresh()?; // find first step with a matching story stage let steps = notes.steps .iter() .take_while(|step| step.stage <= game.stage) .count(); - let (step, step_idx, force) = if steps <= 1 { - // nothing previous OR first step - (¬es.steps[0], 0, false) + let (step, step_idx) = if steps == 0 { + // nothing previous + (notes.steps[0].clone(), None) } else { // in progress - (¬es.steps[steps - 1], steps - 1, true) + (notes.steps[steps - 1].clone(), Some(steps - 1)) }; // find first area that matches let area_idx = step.areas @@ -38,19 +40,16 @@ impl<'a> NotesState<'a> { .unwrap_or(0); Ok(Self { - notes, - last_printed_location: 0, + notes: Arc::new(notes), last_stage: 0, last_location: 0, step_idx, area_idx, current_step: step, - first_step: true, - force_print: force, }) } - pub fn tick(&mut self, game: &mut GameState) -> Result<()> { + pub fn tick(&mut self, game: &mut GameState) -> Result { self.last_stage = game.stage; self.last_location = game.location; game.refresh()?; @@ -59,49 +58,38 @@ impl<'a> NotesState<'a> { let location_changed = self.last_location != game.location; let step_advanced = stage_changed && self.change_step(game); - let area = match self.area() { + let mut highlight_changed = step_advanced; + let area = match self.next_area() { Some(area) => area.clone(), - None => return Ok(()), + None => return Ok(highlight_changed), }; - if self.force_print || ((step_advanced || location_changed) && (game.location == area.area || area.area == 0)) { - if self.last_printed_location != game.location { - self.last_printed_location = game.location; - println!("{}", game.location_name().unwrap_or("Unknown Location")) - } - - println!("{}", area.steps); + if !step_advanced && location_changed && (game.location == area.area || area.area == 0) { self.area_idx += 1; - - if self.first_step { - self.first_step = false; - self.step_idx += 1; - if !self.force_print { - self.area_idx = 0; - } - } - - if self.force_print { - self.force_print = false; - } + highlight_changed = true; } - Ok(()) + Ok(highlight_changed) } fn area(&self) -> Option<&Area> { self.current_step.areas.get(self.area_idx) } + fn next_area(&self) -> Option<&Area> { + self.current_step.areas.get(self.area_idx + 1) + } + fn change_step(&mut self, game: &mut GameState) -> bool { - let next = match self.notes.steps.get(self.step_idx) { + let idx = self.step_idx.map(|x| x + 1).unwrap_or(0); + let next = match self.notes.steps.get(idx) { Some(step) => step, None => return false, }; if next.stage == game.stage { - self.current_step = next; - self.step_idx += 1; + self.current_step = next.clone(); + self.step_idx = Some(idx); self.area_idx = 0; return true; } diff --git a/src/util.rs b/src/util.rs old mode 100644 new mode 100755 index b58a655..98594c6 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,9 @@ use std::collections::HashMap; + use itertools::Itertools; +pub mod markdown_renderer; + pub fn parse_pattern(s: &str) -> Option> { let no_whitespace = s.replace(char::is_whitespace, ""); if no_whitespace.len() % 2 == 1 { @@ -65,8 +68,12 @@ pub fn get_static_address(mem: &[u8], mut addr: usize, base: usize) -> Option &'static str { + LOCATIONS.get(&id).map(std::ops::Deref::deref).unwrap_or("???") +} + lazy_static::lazy_static! { - pub static ref LOCATIONS: HashMap = maplit::hashmap! { + pub static ref LOCATIONS: HashMap = maplit::hashmap! { 12 => "Main Menu", 13 => "End Credits", 48 => "Stockade", diff --git a/src/util/markdown_renderer.rs b/src/util/markdown_renderer.rs new file mode 100755 index 0000000..72e055e --- /dev/null +++ b/src/util/markdown_renderer.rs @@ -0,0 +1,327 @@ +use std::ops::RangeBounds; +use std::path::Path; + +use druid::{Color, Env, FontFamily, FontStyle, FontWeight, ImageBuf, Widget, WidgetExt}; +use druid::piet::ImageFormat; +use druid::text::RichTextBuilder; +use druid::widget::{FillStrat, Flex}; +use druid_widget_nursery::table::{ComplexTableColumnWidth, FlexTable, TableCellVerticalAlignment, TableColumnWidth, TableRow}; +use image::GenericImageView; +use pulldown_cmark::{Event, HeadingLevel, Options, Parser, Tag}; + +use crate::app::App; +use crate::NotesState; +use crate::widget::rich_text_display::RichTextDisplay; + +pub struct MarkdownRenderer<'e, 'p> { + src: &'e str, + path: &'p str, + pos: (usize, usize), + add_newline: bool, + current_pos: usize, + builder: RichTextBuilder, + tag_stack: Vec<(usize, Tag<'e>)>, + list_depth: usize, + in_table: Option, + in_image: Option<(String, String)>, + rendered: Vec>>, +} + +impl<'e, 'p> MarkdownRenderer<'e, 'p> { + const BULLET: &'static str = "• "; + + pub fn new(src: &'e str, path: &'p str, pos: (usize, usize)) -> Self { + Self { + src, + path, + pos, + add_newline: false, + current_pos: 0, + builder: RichTextBuilder::new(), + tag_stack: Vec::new(), + list_depth: 0, + in_table: None, + in_image: None, + rendered: Vec::with_capacity(1), + } + } + + pub fn render(mut self) -> Vec>> { + let mut events = Parser::new_ext(self.src, Options::ENABLE_TABLES); + while let Some(event) = events.next() { + self.event(event); + self.image(&mut events); + self.table(&mut events); + } + + self.render_builder(); + self.rendered + } + + fn render_builder(&mut self) { + let pos = self.pos.clone(); + + let mut builder = RichTextBuilder::new(); + std::mem::swap(&mut builder, &mut self.builder); + self.current_pos = 0; + + let built = builder.build(); + if built.is_empty() { + return; + } + + self.rendered.push(RichTextDisplay::new(built) + .disabled_if(move |data: &Option, _env: &Env| { + let highlight_pos = data + .as_ref() + .and_then(|state| state.step_idx.map(|step_idx| (step_idx, state.area_idx))); + highlight_pos != Some(pos) + }) + .lens(App::notes_state) + .boxed()); + } + + fn builder_push(&mut self, s: &str) { + self.builder.push(s); + self.current_pos += s.len(); + } + + fn add_attribute_for_tag(&mut self, tag: &Tag, range: impl RangeBounds) { + let mut attrs = self.builder.add_attributes_for_range(range); + match tag { + Tag::Heading(lvl, _, _) => { + let font_size = match lvl { + HeadingLevel::H1 => 38.0, + HeadingLevel::H2 => 32.0, + HeadingLevel::H3 => 26.0, + HeadingLevel::H4 => 20.0, + HeadingLevel::H5 => 16.0, + HeadingLevel::H6 => 12.0, + _ => 12.0, + }; + attrs.size(font_size).weight(FontWeight::BOLD); + } + Tag::BlockQuote => { + attrs.style(FontStyle::Italic).text_color(Color::GRAY); + } + Tag::CodeBlock(_) => { + attrs.font_family(FontFamily::MONOSPACE); + } + Tag::Emphasis => { + attrs.style(FontStyle::Italic); + } + Tag::Strong => { + attrs.weight(FontWeight::BOLD); + } + Tag::Strikethrough => { + attrs.strikethrough(true); + } + Tag::Link(_link_ty, _target, _title) => { + attrs + .underline(true) + .text_color(Color::AQUA); + // .link(OPEN_LINK.with(target.to_string())); + } + // ignore other tags for now + _ => (), + } + } + + fn add_newline_after_tag(tag: &Tag) -> bool { + !matches!( + tag, + Tag::Emphasis | Tag::Strong | Tag::Strikethrough | Tag::Link(..), + ) + } + + fn event(&mut self, event: Event<'e>) { + match event { + Event::Start(tag) => { + if let Tag::Table(aligns) = &tag { + self.in_table = Some(aligns.len()); + self.add_newline = false; + } + + if let Tag::Image(_, url, title) = &tag { + self.in_image = Some((url.to_string(), title.to_string())); + } + + if matches!(&tag, Tag::List(..)) { + if self.list_depth > 0 { + self.add_newline = true; + } + + self.list_depth += 1; + } + + if self.add_newline { + self.builder_push("\n"); + self.add_newline = false; + } + + if matches!(&tag, Tag::Item) { + for _ in 0..self.list_depth { + self.builder_push(" "); + } + + self.builder_push(Self::BULLET); + } + + self.tag_stack.push((self.current_pos, tag)); + } + Event::Text(text) => { + self.builder_push(&text); + } + Event::End(end_tag) => { + if matches!(&end_tag, Tag::Table(..)) { + self.in_table = None; + } + + if matches!(&end_tag, Tag::Image(..)) { + self.in_image = None; + } + + let (start_off, tag) = self.tag_stack + .pop() + .expect("parser does not return unbalanced tags"); + assert_eq!(end_tag, tag, "mismatched tags"); + self.add_attribute_for_tag(&tag, start_off..self.current_pos); + if Self::add_newline_after_tag(&tag) { + self.add_newline = true; + } + if matches!(&tag, Tag::List(..)) { + self.list_depth -= 1; + } + } + Event::Code(text) => { + self.builder.push(&text).font_family(FontFamily::MONOSPACE); + self.current_pos += text.len(); + } + Event::Html(text) => { + self.builder + .push(&text) + .font_family(FontFamily::MONOSPACE) + .text_color(Color::RED); + self.current_pos += text.len(); + } + Event::HardBreak => { + self.add_newline = true; + } + _ => {} + } + } + + fn table(&mut self, mut events: impl Iterator>) { + let cols = match self.in_table { + Some(cols) => cols, + None => return, + }; + + // handle any text yet to render + self.render_builder(); + let mut start = self.rendered.len(); + let mut table = FlexTable::new() + .default_vertical_alignment(TableCellVerticalAlignment::Top); + let widths: Vec = (0..cols) + .map(|i| if i == cols - 1 { + TableColumnWidth::Flex(1.0).into() + } else { + TableColumnWidth::Intrinsic.into() + }) + .collect(); + table.set_column_widths(&widths); + let mut row = None; + + while self.in_table.is_some() { + if let Some(event) = events.next() { + match &event { + Event::Start(Tag::TableRow | Tag::TableHead) => { + row = Some(TableRow::new()); + } + Event::Start(Tag::TableCell) => { + start = self.rendered.len(); + } + Event::End(Tag::TableRow | Tag::TableHead) => { + if let Some(row) = row.take() { + table.add_row(row); + } + + row = None; + } + Event::End(Tag::TableCell) => { + self.render_builder(); + let mut widgets: Vec<_> = self.rendered.drain(start..).collect(); + if let Some(row) = &mut row { + if widgets.len() == 1 { + row.add_child(widgets.remove(0).padding(2.0)); + } else if widgets.len() > 1 { + let mut flex = Flex::column(); + for widget in widgets { + flex.add_child(widget.padding(2.0)); + } + + row.add_child(flex); + } + } + } + _ => {} + } + + self.event(event); + self.add_newline = false; // never add newlines in a table + } else { + break; + } + } + + self.rendered.push(table.boxed()); + } + + fn image(&mut self, mut events: impl Iterator>) { + let url = match &self.in_image { + Some((url, _)) => url.clone(), + None => return, + }; + + self.render_builder(); + let start = self.rendered.len(); + + while self.in_image.is_some() { + if let Some(event) = events.next() { + self.event(event); + self.add_newline = false; // never add newlines in an image + } else { + break; + } + } + + self.render_builder(); + let alt: Vec<_> = self.rendered.drain(start..).collect(); + + let image_path = Path::new(self.path) + .parent() + .map(|parent| parent.join(&url)) + .unwrap_or_else(|| Path::new(&url).to_owned()); + match image::open(image_path) { + Ok(image) => { + let image = image.into_rgba8(); + let width = image.width() as usize; + let height = image.height() as usize; + let image_buf = ImageBuf::from_raw( + image.into_flat_samples().samples, + ImageFormat::RgbaSeparate, + width, + height, + ); + let widget = druid::widget::Image::new(image_buf) + .fill_mode(FillStrat::ScaleDown) + .fix_width(width as f64); + self.rendered.push(widget.boxed()); + } + Err(err) => { + eprintln!("error loading image: {:?}", err); + self.rendered.extend(alt); + } + } + } +} diff --git a/src/widget/mod.rs b/src/widget/mod.rs new file mode 100755 index 0000000..77bf4fe --- /dev/null +++ b/src/widget/mod.rs @@ -0,0 +1,3 @@ +pub mod notes_viewer; +pub mod notes_viewer_scroll; +pub mod rich_text_display; diff --git a/src/widget/notes_viewer.rs b/src/widget/notes_viewer.rs new file mode 100755 index 0000000..15618f3 --- /dev/null +++ b/src/widget/notes_viewer.rs @@ -0,0 +1,125 @@ +use std::collections::HashMap; + +use druid::{BoxConstraints, Data, Env, Event, EventCtx, LayoutCtx, LensExt, LifeCycle, LifeCycleCtx, PaintCtx, Point, Rect, RenderContext, Size, UpdateCtx, Widget, WidgetExt, WidgetPod}; +use druid::widget::{CrossAxisAlignment, Flex, MainAxisAlignment}; + +use crate::app::App; +use crate::Notes; +use crate::util::markdown_renderer::MarkdownRenderer; + +#[derive(Default)] +pub struct NotesViewer { + children: Vec<((usize, usize), WidgetPod>>)>, + rects: HashMap<(usize, usize), Rect>, + highlighted: Option<(usize, usize)>, +} + +impl NotesViewer { + pub fn new() -> Self { + Self::default() + } + + pub fn rect_for_step(&self, step: usize, area: usize) -> Option { + self.rects.get(&(step, area)).cloned() + } + + fn rerender(&mut self, notes: &Option<(Notes, String)>) { + self.children.clear(); + + let (notes, path) = match notes { + Some(notes) => notes, + None => return, + }; + + for step_idx in 0..notes.steps.len() { + let step = ¬es.steps[step_idx]; + + for area_idx in 0..step.areas.len() { + let area = &step.areas[area_idx]; + + let mut flex: Flex = Flex::column() + .main_axis_alignment(MainAxisAlignment::Start) + .cross_axis_alignment(CrossAxisAlignment::Start); + let location_name = crate::util::location_name(area.area); + for child in Self::render_markdown(&format!("#### {}\n{}", location_name, area.steps), path, (step_idx, area_idx)) { + flex.add_child(child); + } + + self.children.push(( + (step_idx, area_idx), + WidgetPod::new(flex).boxed(), + )); + } + } + } + + fn render_markdown(src: &str, path: &str, pos: (usize, usize)) -> Vec>> { + MarkdownRenderer::new(src, path, pos).render() + } +} + +impl Widget for NotesViewer { + fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut App, env: &Env) { + for (_, child) in &mut self.children { + child.event(ctx, event, data, env); + } + } + + fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &App, env: &Env) { + for (_, child) in &mut self.children { + child.lifecycle(ctx, event, data, env); + } + + match event { + LifeCycle::WidgetAdded => { + self.rerender(&data.notes); + ctx.children_changed(); + } + _ => {} + } + } + + fn update(&mut self, ctx: &mut UpdateCtx, old_data: &App, data: &App, env: &Env) { + for (_, child) in &mut self.children { + child.update(ctx, data, env); + } + + // if the notes have changed, we need to rerender them + if !old_data.notes.same(&data.notes) { + self.rerender(&data.notes); + ctx.children_changed(); + } + + // if the note state has changed, a new section needs to be highlighted + if !old_data.notes_state.same(&data.notes_state) { + self.highlighted = data.notes_state + .as_ref() + .and_then(|state| state.step_idx.map(|step_idx| (step_idx, state.area_idx))); + ctx.request_paint(); + } + } + + fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &App, env: &Env) -> Size { + self.rects.clear(); + + let mut pos = Point::ZERO; + let mut size = bc.min(); + + for ((step, area), child) in &mut self.children { + let child_size = child.layout(ctx, bc, data, env); + child.set_origin(ctx, data, env, pos); + self.rects.insert((*step, *area), Rect::from_origin_size(pos, child_size)); + pos.y += child_size.height; + size.height += child_size.height; + size.width = child_size.width.max(size.width); + } + + size + } + + fn paint(&mut self, ctx: &mut PaintCtx, data: &App, env: &Env) { + for (_, child) in &mut self.children { + child.paint(ctx, data, env); + } + } +} diff --git a/src/widget/notes_viewer_scroll.rs b/src/widget/notes_viewer_scroll.rs new file mode 100755 index 0000000..b9c547f --- /dev/null +++ b/src/widget/notes_viewer_scroll.rs @@ -0,0 +1,67 @@ +use druid::{BoxConstraints, Data, Env, Event, EventCtx, LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx, Rect, Size, UpdateCtx, Widget, WidgetPod}; +use druid::widget::Scroll; + +use crate::app::App; +use crate::widget::notes_viewer::NotesViewer; + +pub struct NotesViewerScroll { + scroll: WidgetPod>, +} + +impl NotesViewerScroll { + pub fn new() -> Self { + Self { + scroll: WidgetPod::new(Scroll::new(NotesViewer::new()).vertical()), + } + } +} + +impl Widget for NotesViewerScroll { + fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut App, env: &Env) { + self.scroll.event(ctx, event, data, env); + } + + fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &App, env: &Env) { + self.scroll.lifecycle(ctx, event, data, env); + } + + fn update(&mut self, ctx: &mut UpdateCtx, old_data: &App, data: &App, env: &Env) { + self.scroll.update(ctx, data, env); + + let mut should_scroll = !old_data.notes_state.same(&data.notes_state) + || (old_data.game_state.is_none() && data.game_state.is_some()); + + if let (Some(old), Some(new)) = (&old_data.notes_state, &data.notes_state) { + if should_scroll && old.step_idx == new.step_idx && old.area_idx == new.area_idx { + should_scroll = false; + } + } + + // scroll to new section + if should_scroll { + if let Some((step_idx, area_idx)) = data.notes_state + .as_ref() + .and_then(|state| state.step_idx.map(|step_idx| (step_idx, state.area_idx))) + { + let rect = self.scroll.widget().child().rect_for_step(step_idx, area_idx); + println!("scroll to step {} area {}: {:?}", step_idx, area_idx, rect); + if let Some(rect) = rect { + let step_height = rect.height(); + let scroll_size = self.scroll.layout_rect(); + let mid_scroll = scroll_size.height() / 2.0; + let mid_step = mid_scroll - (step_height / 2.0); + let scroll_to = Rect::new(rect.x0, rect.y0, rect.x1, rect.y1 + mid_step); + self.scroll.widget_mut().scroll_to(scroll_to); + } + } + } + } + + fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &App, env: &Env) -> Size { + self.scroll.layout(ctx, bc, data, env) + } + + fn paint(&mut self, ctx: &mut PaintCtx, data: &App, env: &Env) { + self.scroll.paint(ctx, data, env); + } +} diff --git a/src/widget/rich_text_display.rs b/src/widget/rich_text_display.rs new file mode 100755 index 0000000..d91d9da --- /dev/null +++ b/src/widget/rich_text_display.rs @@ -0,0 +1,43 @@ +use std::marker::PhantomData; + +use druid::{BoxConstraints, Env, Event, EventCtx, LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx, Size, UpdateCtx, Widget, WidgetPod}; +use druid::text::RichText; +use druid::widget::{LineBreaking, RawLabel}; + +pub struct RichTextDisplay { + text: RichText, + label: WidgetPod>, + _phantom: PhantomData, +} + +impl RichTextDisplay { + pub fn new(text: RichText) -> Self { + Self { + text, + label: WidgetPod::new(RawLabel::new().with_line_break_mode(LineBreaking::WordWrap)), + _phantom: PhantomData, + } + } +} + +impl Widget for RichTextDisplay { + fn event(&mut self, ctx: &mut EventCtx, event: &Event, _data: &mut T, env: &Env) { + self.label.event(ctx, event, &mut self.text, env); + } + + fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, _data: &T, env: &Env) { + self.label.lifecycle(ctx, event, &self.text, env); + } + + fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &T, _data: &T, env: &Env) { + self.label.update(ctx, &self.text, env); + } + + fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, _data: &T, env: &Env) -> Size { + self.label.layout(ctx, bc, &self.text, env) + } + + fn paint(&mut self, ctx: &mut PaintCtx, _data: &T, env: &Env) { + self.label.paint(ctx, &self.text, env); + } +}