Compare commits
10 Commits
76149f0a69
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d41eaaf56 | |||
| 5fffaf2582 | |||
| 1f3173a563 | |||
| e1a5c6528d | |||
| cfb4518a53 | |||
| 24d20ae01f | |||
| 4ed4eb46b4 | |||
| ba1889d40d | |||
| d82e05505c | |||
| 3e8bedb0c9 |
Generated
+50
-118
@@ -10,9 +10,9 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.0"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -31,14 +31,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.11"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
|
||||
checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -81,14 +80,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
@@ -120,9 +119,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@@ -138,21 +137,21 @@ checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.13.0"
|
||||
version = "2.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.1",
|
||||
"hashbrown 0.17.0",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.46.3"
|
||||
version = "1.47.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e82db8c87c7f1ccecb34ce0c24399b8a73081427f3c7c50a5d597925356115e4"
|
||||
checksum = "7b4a6248eb93a4401ed2f37dfe8ea592d3cf05b7cf4f8efa867b6895af7e094e"
|
||||
dependencies = [
|
||||
"console",
|
||||
"once_cell",
|
||||
@@ -162,9 +161,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
@@ -174,9 +173,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.183"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
@@ -207,9 +206,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.5"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c"
|
||||
checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
"rustversion",
|
||||
@@ -217,9 +216,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.5"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7"
|
||||
checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -229,9 +228,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
@@ -286,7 +285,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -303,9 +302,9 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
@@ -368,15 +367,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.26.0"
|
||||
version = "3.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -401,18 +400,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.0.0+spec-1.1.0"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e"
|
||||
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.25.4+spec-1.1.0"
|
||||
version = "0.25.11+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2"
|
||||
checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
@@ -422,9 +421,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.9+spec-1.1.0"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
|
||||
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
@@ -443,11 +442,11 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.2+wasi-0.2.9"
|
||||
version = "1.0.3+wasi-0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
||||
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
"wit-bindgen 0.57.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -456,7 +455,7 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
"wit-bindgen 0.51.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -499,15 +498,6 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
@@ -517,75 +507,11 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.15"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
||||
checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -599,6 +525,12 @@ dependencies = [
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.57.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.51.0"
|
||||
@@ -686,7 +618,7 @@ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
|
||||
[[package]]
|
||||
name = "zztff"
|
||||
version = "0.1.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"codepage-437",
|
||||
"insta",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "zztff"
|
||||
version = "0.1.0"
|
||||
version = "0.3.0"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
description = "Library for reading/writing ZZT worlds and boards"
|
||||
|
||||
+1
-1
@@ -69,6 +69,6 @@ pub enum Element {
|
||||
CyanText = 49,
|
||||
RedText = 50,
|
||||
PurpleText = 51,
|
||||
BrownText = 52,
|
||||
YellowText = 52,
|
||||
WhiteText = 53,
|
||||
}
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@
|
||||
//! ```rust,no_run
|
||||
//! let bytes = std::fs::read("TOWN.ZZT")?;
|
||||
//! let world = zztff::World::from_bytes(&bytes)?;
|
||||
//! let first3: Vec<&str> = world.boards.iter().take(3).map(|b| b.name.as_str()).collect();
|
||||
//! let first3: Vec<&str> = world.boards.iter().take(3).map(|b| b.title.as_str()).collect();
|
||||
//! println!("Loaded world {:?}, {} boards.", world.name, world.boards.len());
|
||||
//! println!("First 3 boards: {:?}", first3);
|
||||
//! # Ok::<(), Box<dyn std::error::Error>>(())
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: src/world.rs
|
||||
assertion_line: 589
|
||||
expression: world
|
||||
---
|
||||
World {
|
||||
@@ -39,7 +38,7 @@ World {
|
||||
saved_game: true,
|
||||
boards: [
|
||||
Board {
|
||||
name: "Title screen",
|
||||
title: "Title screen",
|
||||
tiles: [Tile; 1500],
|
||||
max_shots: 255,
|
||||
is_dark: false,
|
||||
@@ -47,7 +46,7 @@ World {
|
||||
exit_south: None,
|
||||
exit_west: None,
|
||||
exit_east: None,
|
||||
restart_on_zap: false,
|
||||
reenter_when_zapped: false,
|
||||
message: "",
|
||||
enter_x: 1,
|
||||
enter_y: 1,
|
||||
@@ -56,8 +55,8 @@ World {
|
||||
Stat {
|
||||
x: 1,
|
||||
y: 1,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 1,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
@@ -76,7 +75,7 @@ World {
|
||||
],
|
||||
},
|
||||
Board {
|
||||
name: "First board (NW, defaults)",
|
||||
title: "First board (NW, defaults)",
|
||||
tiles: [Tile; 1500],
|
||||
max_shots: 255,
|
||||
is_dark: false,
|
||||
@@ -88,7 +87,7 @@ World {
|
||||
exit_east: Some(
|
||||
2,
|
||||
),
|
||||
restart_on_zap: false,
|
||||
reenter_when_zapped: false,
|
||||
message: "",
|
||||
enter_x: 1,
|
||||
enter_y: 1,
|
||||
@@ -97,8 +96,8 @@ World {
|
||||
Stat {
|
||||
x: 1,
|
||||
y: 1,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 1,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
@@ -117,7 +116,7 @@ World {
|
||||
],
|
||||
},
|
||||
Board {
|
||||
name: "Second board (NE, non-default settings)",
|
||||
title: "Second board (NE, non-default settings)",
|
||||
tiles: [Tile; 1500],
|
||||
max_shots: 0,
|
||||
is_dark: true,
|
||||
@@ -129,7 +128,7 @@ World {
|
||||
1,
|
||||
),
|
||||
exit_east: None,
|
||||
restart_on_zap: true,
|
||||
reenter_when_zapped: true,
|
||||
message: "Hello, board message!",
|
||||
enter_x: 12,
|
||||
enter_y: 34,
|
||||
@@ -138,8 +137,8 @@ World {
|
||||
Stat {
|
||||
x: 1,
|
||||
y: 1,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 1,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
@@ -158,7 +157,7 @@ World {
|
||||
],
|
||||
},
|
||||
Board {
|
||||
name: "Third board (SE, mostly object stats)",
|
||||
title: "Third board (SE, mostly object stats)",
|
||||
tiles: [Tile; 1500],
|
||||
max_shots: 255,
|
||||
is_dark: false,
|
||||
@@ -170,7 +169,7 @@ World {
|
||||
4,
|
||||
),
|
||||
exit_east: None,
|
||||
restart_on_zap: false,
|
||||
reenter_when_zapped: false,
|
||||
message: "",
|
||||
enter_x: 1,
|
||||
enter_y: 1,
|
||||
@@ -179,8 +178,8 @@ World {
|
||||
Stat {
|
||||
x: 1,
|
||||
y: 1,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 1,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
@@ -199,8 +198,8 @@ World {
|
||||
Stat {
|
||||
x: 3,
|
||||
y: 2,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 3,
|
||||
p1: 51,
|
||||
p2: 0,
|
||||
@@ -219,8 +218,8 @@ World {
|
||||
Stat {
|
||||
x: 20,
|
||||
y: 4,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 0,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
@@ -239,8 +238,8 @@ World {
|
||||
Stat {
|
||||
x: 5,
|
||||
y: 2,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 1,
|
||||
p1: 49,
|
||||
p2: 1,
|
||||
@@ -259,8 +258,8 @@ World {
|
||||
Stat {
|
||||
x: 7,
|
||||
y: 2,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 3,
|
||||
p1: 64,
|
||||
p2: 0,
|
||||
@@ -279,8 +278,8 @@ World {
|
||||
Stat {
|
||||
x: 10,
|
||||
y: 2,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 3,
|
||||
p1: 9,
|
||||
p2: 0,
|
||||
@@ -299,7 +298,7 @@ World {
|
||||
],
|
||||
},
|
||||
Board {
|
||||
name: "Fourth board (SW, other stats)",
|
||||
title: "Fourth board (SW, other stats)",
|
||||
tiles: [Tile; 1500],
|
||||
max_shots: 255,
|
||||
is_dark: false,
|
||||
@@ -311,7 +310,7 @@ World {
|
||||
exit_east: Some(
|
||||
3,
|
||||
),
|
||||
restart_on_zap: false,
|
||||
reenter_when_zapped: false,
|
||||
message: "",
|
||||
enter_x: 1,
|
||||
enter_y: 1,
|
||||
@@ -320,8 +319,8 @@ World {
|
||||
Stat {
|
||||
x: 1,
|
||||
y: 1,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 1,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
@@ -340,8 +339,8 @@ World {
|
||||
Stat {
|
||||
x: 3,
|
||||
y: 2,
|
||||
x_step: 0,
|
||||
y_step: -1,
|
||||
step_x: 0,
|
||||
step_y: -1,
|
||||
cycle: 4,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
@@ -360,8 +359,8 @@ World {
|
||||
Stat {
|
||||
x: 5,
|
||||
y: 2,
|
||||
x_step: 0,
|
||||
y_step: 1,
|
||||
step_x: 0,
|
||||
step_y: 1,
|
||||
cycle: 4,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
@@ -380,8 +379,8 @@ World {
|
||||
Stat {
|
||||
x: 7,
|
||||
y: 2,
|
||||
x_step: 1,
|
||||
y_step: 0,
|
||||
step_x: 1,
|
||||
step_y: 0,
|
||||
cycle: 4,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
@@ -400,8 +399,8 @@ World {
|
||||
Stat {
|
||||
x: 9,
|
||||
y: 2,
|
||||
x_step: -1,
|
||||
y_step: 0,
|
||||
step_x: -1,
|
||||
step_y: 0,
|
||||
cycle: 4,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
@@ -420,8 +419,8 @@ World {
|
||||
Stat {
|
||||
x: 3,
|
||||
y: 4,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 2,
|
||||
p1: 4,
|
||||
p2: 4,
|
||||
@@ -440,8 +439,8 @@ World {
|
||||
Stat {
|
||||
x: 4,
|
||||
y: 4,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 2,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
@@ -460,8 +459,8 @@ World {
|
||||
Stat {
|
||||
x: 5,
|
||||
y: 4,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 2,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
|
||||
+10
-6
@@ -3,7 +3,8 @@ use codepage_437::CP437_WINGDINGS;
|
||||
use super::errors::EncodeError;
|
||||
|
||||
/// Serialize a multi-line code string for an object or a scroll.
|
||||
/// Uses ZZT's convention of CR-terminated lines.
|
||||
///
|
||||
/// Characters that do not have a CP437 equivalent will result in an [`EncodeError::EncodingError`].
|
||||
pub fn encode_multiline(input: &str) -> Result<Vec<u8>, EncodeError> {
|
||||
input
|
||||
.chars()
|
||||
@@ -33,8 +34,11 @@ pub fn decode_multiline(input: &[u8]) -> String {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Serialize a single-line string for a board title.
|
||||
/// Newlines cannot be encoded because ZZT interprets them as dingbats (♪).
|
||||
/// Serialize a single-line string, such as a board title.
|
||||
///
|
||||
/// Newlines cannot be encoded because in single-line contexts, ZZT interprets that byte as a
|
||||
/// dingbat, specifically the '♪' character. Passing a newline, or any non-CP437 character, will
|
||||
/// result in an [`EncodeError::EncodingError`].
|
||||
pub fn encode_oneline(input: &str) -> Result<Vec<u8>, EncodeError> {
|
||||
input
|
||||
.chars()
|
||||
@@ -46,7 +50,7 @@ pub fn encode_oneline(input: &str) -> Result<Vec<u8>, EncodeError> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Deserialize a board title.
|
||||
/// Deserialize a single-line string, such as a board title.
|
||||
pub fn decode_oneline(input: &[u8]) -> String {
|
||||
input.iter().map(|&x| CP437_WINGDINGS.decode(x)).collect()
|
||||
}
|
||||
@@ -60,7 +64,7 @@ mod tests {
|
||||
encode_multiline(input).expect("Error in test")
|
||||
}
|
||||
|
||||
fn serialize_title(input: &str) -> Vec<u8> {
|
||||
fn serialize_line(input: &str) -> Vec<u8> {
|
||||
encode_oneline(input).expect("Error in test")
|
||||
}
|
||||
|
||||
@@ -73,7 +77,7 @@ mod tests {
|
||||
#[test]
|
||||
fn roundtrip_oneline() {
|
||||
let bytes: Vec<u8> = (0..=255).collect();
|
||||
assert_eq!(bytes, serialize_title(&decode_oneline(&bytes)))
|
||||
assert_eq!(bytes, serialize_line(&decode_oneline(&bytes)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+81
-45
@@ -125,7 +125,7 @@ pub struct Keys {
|
||||
///
|
||||
/// Boards mainly exist within a containing [`World`]. But as part of a editing workflow, they can
|
||||
/// also be packaged as standalone .BRD files, which use the same format; use
|
||||
/// [`Board::from_brd_bytes`] to parse one.
|
||||
/// [`Board::from_bytes`] to parse one.
|
||||
#[derive(Clone)]
|
||||
pub struct Board {
|
||||
/// The board's title.
|
||||
@@ -137,7 +137,7 @@ pub struct Board {
|
||||
/// shorter limits: ZZT's internal editor artificially limits titles to 34 characters, and
|
||||
/// titles longer than 42 characters may cause visual glitches when displayed in a
|
||||
/// standard-width message box.
|
||||
pub name: String,
|
||||
pub title: String,
|
||||
/// The 60x25 terrain grid, stored in order from left-to-right, top-to-bottom.
|
||||
///
|
||||
/// For convenience, [`Board::tile`] and [`Board::set_tile`] are available for coordinate-based
|
||||
@@ -159,15 +159,16 @@ pub struct Board {
|
||||
pub exit_west: Option<NonZero<u8>>,
|
||||
/// Board index of the eastern neighbor, or `None` for no exit.
|
||||
pub exit_east: Option<NonZero<u8>>,
|
||||
/// Whether the player restarts at the board entrance after taking damage.
|
||||
pub restart_on_zap: bool,
|
||||
/// Whether the player, upon taking damage, is transported back to where they entered the board.
|
||||
pub reenter_when_zapped: bool,
|
||||
/// The currently-flashing status bar message (max 58 characters).
|
||||
pub message: String,
|
||||
/// X coordinate where the player entered the board (1-based).
|
||||
///
|
||||
/// The entrance coordinates are part of the "re-enter when zapped" feature. The values only
|
||||
/// really matter if the user starts out on this board; they will be overwritten with the
|
||||
/// player's actual entry coordinates during play.
|
||||
/// The entry coordinates are part of the "re-enter when zapped" feature. These don't matter
|
||||
/// most of the time; ZZT ignores the coordinates from the file, preferring to update these with
|
||||
/// the player's actual entry coordinates. The only time they are ever loaded is for the current
|
||||
/// board when the user restores a save file.
|
||||
pub enter_x: u8,
|
||||
/// Y coordinate where the player entered the board (1-based).
|
||||
pub enter_y: u8,
|
||||
@@ -184,7 +185,7 @@ pub struct Board {
|
||||
impl Default for Board {
|
||||
fn default() -> Self {
|
||||
Board {
|
||||
name: String::new(),
|
||||
title: String::new(),
|
||||
tiles: [Tile::default(); 1500],
|
||||
max_shots: 255,
|
||||
is_dark: false,
|
||||
@@ -192,8 +193,11 @@ impl Default for Board {
|
||||
exit_south: None,
|
||||
exit_west: None,
|
||||
exit_east: None,
|
||||
restart_on_zap: false,
|
||||
reenter_when_zapped: false,
|
||||
message: String::new(),
|
||||
// (1, 1) is an objectively better default for enter x/y than (0, 0). It is possible to
|
||||
// get ZZT to consume this value by restoring a game, and in that case (0, 0) will take
|
||||
// the player out of bounds, leading to a crash.
|
||||
enter_x: 1,
|
||||
enter_y: 1,
|
||||
time_limit: 0,
|
||||
@@ -213,7 +217,7 @@ impl Default for Board {
|
||||
impl Debug for Board {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Board")
|
||||
.field("name", &self.name)
|
||||
.field("title", &self.title)
|
||||
.field("tiles", &format_args!("[Tile; 1500]"))
|
||||
.field("max_shots", &self.max_shots)
|
||||
.field("is_dark", &self.is_dark)
|
||||
@@ -221,7 +225,7 @@ impl Debug for Board {
|
||||
.field("exit_south", &self.exit_south)
|
||||
.field("exit_west", &self.exit_west)
|
||||
.field("exit_east", &self.exit_east)
|
||||
.field("restart_on_zap", &self.restart_on_zap)
|
||||
.field("reenter_when_zapped", &self.reenter_when_zapped)
|
||||
.field("message", &self.message)
|
||||
.field("enter_x", &self.enter_x)
|
||||
.field("enter_y", &self.enter_y)
|
||||
@@ -263,9 +267,9 @@ pub struct Stat {
|
||||
/// are possible and are useful for advanced techniques, but care must be taken to ensure that
|
||||
/// they are safe; ZZT's bounds checking code assumes step distances will never exceed 1, so
|
||||
/// steps larger than that may go off-board and cause memory corruption.
|
||||
pub x_step: i16,
|
||||
pub step_x: i16,
|
||||
/// Vertical step delta.
|
||||
pub y_step: i16,
|
||||
pub step_y: i16,
|
||||
/// How often this stat is updated, in game ticks.
|
||||
///
|
||||
/// Roughly speaking, stats update every 1/cycle game ticks. A cycle 1 object will run its code
|
||||
@@ -326,8 +330,8 @@ impl Default for Stat {
|
||||
Stat {
|
||||
x: 0,
|
||||
y: 0,
|
||||
x_step: 0,
|
||||
y_step: 0,
|
||||
step_x: 0,
|
||||
step_y: 0,
|
||||
cycle: 0,
|
||||
p1: 0,
|
||||
p2: 0,
|
||||
@@ -351,15 +355,36 @@ pub struct Tile {
|
||||
pub color: u8,
|
||||
}
|
||||
|
||||
/// A stat's ZZT-OOP program.
|
||||
/// A stat's ZZT-OOP program text.
|
||||
///
|
||||
/// In ZZT, stats can either have their own code or bind to another stat's code.
|
||||
/// This enum prevents invalid states where both are set.
|
||||
/// Stats must either have their own program or bind to another stat's program. This is either-or:
|
||||
/// in ZZT, it is impossible to represent a stat that has both (or neither). This enum models that
|
||||
/// constraint.
|
||||
///
|
||||
/// Internally, this is because ZZT uses a single `i16` like an enum discriminant. Zero or more
|
||||
/// indicates the length of the program text, i.e., how many bytes follow the stat on disk. Negative
|
||||
/// values are interpreted as negated bind indexes: -123 means no program text follows, and that the
|
||||
/// stat instead shares code with the 123rd stat on the board.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Program {
|
||||
/// The stat owns its own code.
|
||||
/// The stat has its own program code.
|
||||
///
|
||||
/// This is the typical default for most stats. For stats that don't have or need a program, an
|
||||
/// empty `String` is used. The text has a maximum of 32767 characters, though in ZZT 3.2 you
|
||||
/// will encounter other limits before that.
|
||||
///
|
||||
/// Despite the name, this has more to do with ZZT's behavior than with Rust ownership.
|
||||
Own(String),
|
||||
/// The stat is bound to another stat (index into the stats list).
|
||||
/// The stat is bound to the code at the given stat index.
|
||||
///
|
||||
/// Bind indexes are 0-based, with the asterisk that zero is not representable due to how ZZT
|
||||
/// encodes bind indexes. The smallest possible value is 1, which represents being bound to the
|
||||
/// stat that comes immediately after the player.
|
||||
///
|
||||
/// The largest representable bind index is 32768. zztff will successfully serialize this value,
|
||||
/// though it's only useful as a curiosity and will likely crash ZZT: the index will definitely
|
||||
/// be out of bounds because it is impossible to serialize a stat list that long without hitting
|
||||
/// other limits in the file format. Indexes larger than that will result in an [`EncodeError`].
|
||||
Bound(NonZero<u16>),
|
||||
}
|
||||
|
||||
@@ -484,13 +509,13 @@ impl Board {
|
||||
self.tiles[(y - 1) * 60 + (x - 1)] = tile;
|
||||
}
|
||||
|
||||
/// Parse a board from bytes (including the 2-byte size header).
|
||||
/// Parse a ZZT board from bytes.
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Board, DecodeError> {
|
||||
// Ignore length bytes
|
||||
let (input, _) = le_u16.parse(bytes)?;
|
||||
|
||||
// Read board name
|
||||
let (input, name) = pstring(50)(input)?;
|
||||
// Read board title
|
||||
let (input, title) = pstring(50)(input)?;
|
||||
|
||||
// Read terrain
|
||||
const NUM_TILES: usize = 60 * 25;
|
||||
@@ -518,7 +543,7 @@ impl Board {
|
||||
let exit_south = NonZero::new(exit_s);
|
||||
let exit_west = NonZero::new(exit_w);
|
||||
let exit_east = NonZero::new(exit_e);
|
||||
let (input, (restart_on_zap, message)) = (bool_u8, pstring(58)).parse(input)?;
|
||||
let (input, (reenter_when_zapped, message)) = (bool_u8, pstring(58)).parse(input)?;
|
||||
let (input, (enter_x, enter_y, time_limit)) = (le_u8, le_u8, le_i16).parse(input)?;
|
||||
let (input, _) = take(16usize)(input)?;
|
||||
|
||||
@@ -531,7 +556,7 @@ impl Board {
|
||||
let (_input, stats) = count(Stat::parse, num_stats as usize).parse(input)?;
|
||||
|
||||
Ok(Board {
|
||||
name,
|
||||
title,
|
||||
tiles,
|
||||
max_shots,
|
||||
is_dark,
|
||||
@@ -539,7 +564,7 @@ impl Board {
|
||||
exit_south,
|
||||
exit_east,
|
||||
exit_west,
|
||||
restart_on_zap,
|
||||
reenter_when_zapped,
|
||||
message,
|
||||
enter_x,
|
||||
enter_y,
|
||||
@@ -548,16 +573,16 @@ impl Board {
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a standalone .brd file (same format, no world header).
|
||||
pub fn from_brd_bytes(bytes: &[u8]) -> Result<Board, DecodeError> {
|
||||
Board::from_bytes(bytes)
|
||||
}
|
||||
|
||||
/// Serialize this board to bytes.
|
||||
///
|
||||
/// Returns [`EncodeError::BoardTooLarge`] if the board is over 64 KiB when serialized. The file
|
||||
/// format allows up to this size, but be aware that boards larger than 20 kB will cause memory
|
||||
/// corruption in ZZT 3.2. Tools may want to warn the user if this function returns more than
|
||||
/// 20,002 bytes (2 byte size header + 20 kB content).
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, EncodeError> {
|
||||
let mut result = vec![];
|
||||
result.push_padding(2); // reserve space for board size
|
||||
result.push_string(50, &self.name)?;
|
||||
result.push_string(50, &self.title)?;
|
||||
|
||||
// Encode terrain
|
||||
let mut iter = self.tiles.iter().peekable();
|
||||
@@ -579,7 +604,7 @@ impl Board {
|
||||
result.push(self.exit_south.map_or(0, |n| n.get()));
|
||||
result.push(self.exit_west.map_or(0, |n| n.get()));
|
||||
result.push(self.exit_east.map_or(0, |n| n.get()));
|
||||
result.push_bool(self.restart_on_zap);
|
||||
result.push_bool(self.reenter_when_zapped);
|
||||
result.push_string(58, &self.message)?;
|
||||
result.push(self.enter_x);
|
||||
result.push(self.enter_y);
|
||||
@@ -605,7 +630,7 @@ impl Board {
|
||||
|
||||
impl Stat {
|
||||
fn parse(input: &[u8]) -> IResult<&[u8], Self, DecodeError> {
|
||||
let (input, (x, y, x_step, y_step)) = (le_u8, le_u8, le_i16, le_i16).parse(input)?;
|
||||
let (input, (x, y, step_x, step_y)) = (le_u8, le_u8, le_i16, le_i16).parse(input)?;
|
||||
let (input, (cycle, p1, p2, p3)) = (le_i16, le_u8, le_u8, le_u8).parse(input)?;
|
||||
let (input, (follower, leader)) = (le_i16, le_i16).parse(input)?;
|
||||
let (input, (under_element, under_color)) = (le_u8, le_u8).parse(input)?;
|
||||
@@ -630,8 +655,8 @@ impl Stat {
|
||||
Stat {
|
||||
x,
|
||||
y,
|
||||
x_step,
|
||||
y_step,
|
||||
step_x,
|
||||
step_y,
|
||||
follower,
|
||||
leader,
|
||||
cycle,
|
||||
@@ -652,8 +677,8 @@ impl Stat {
|
||||
let mut result = vec![];
|
||||
result.push(self.x);
|
||||
result.push(self.y);
|
||||
result.push_i16(self.x_step);
|
||||
result.push_i16(self.y_step);
|
||||
result.push_i16(self.step_x);
|
||||
result.push_i16(self.step_y);
|
||||
result.push_i16(self.cycle);
|
||||
result.push(self.p1);
|
||||
result.push(self.p2);
|
||||
@@ -743,22 +768,33 @@ mod tests {
|
||||
use super::*;
|
||||
use insta::assert_debug_snapshot;
|
||||
|
||||
const BYTES: &[u8] = include_bytes!("../tests/fixtures/all.zzt");
|
||||
const WORLD: &[u8] = include_bytes!("../tests/fixtures/all.zzt");
|
||||
const BOARD: &[u8] = include_bytes!("../tests/fixtures/title.brd");
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
let world = World::from_bytes(BYTES).unwrap();
|
||||
let world = World::from_bytes(WORLD).unwrap();
|
||||
let encoded = world.to_bytes().unwrap();
|
||||
assert_eq!(BYTES, &encoded[..]);
|
||||
assert_eq!(WORLD, &encoded[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_snapshot() {
|
||||
let world = World::from_bytes(BYTES).unwrap();
|
||||
let world = World::from_bytes(WORLD).unwrap();
|
||||
assert_debug_snapshot!(world);
|
||||
}
|
||||
|
||||
/// Format board terrain as two hex grids (elements, then colors).
|
||||
#[test]
|
||||
fn brd_roundtrip() {
|
||||
// We don't bother with a snapshot test for the individual board case, because the world
|
||||
// snapshot already exercises the contents of boards. This test is just to make sure it's
|
||||
// *possible* to read/write standalone board files.
|
||||
let board = Board::from_bytes(BOARD).unwrap();
|
||||
let encoded = board.to_bytes().unwrap();
|
||||
assert_eq!(BOARD, &encoded[..]);
|
||||
}
|
||||
|
||||
/// Test helper: format board terrain as two hex grids (elements, then colors).
|
||||
fn format_tiles(board: &Board) -> String {
|
||||
let mut out = String::new();
|
||||
for y in 1..=25 {
|
||||
@@ -780,10 +816,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn tiles_snapshot() {
|
||||
let world = World::from_bytes(BYTES).unwrap();
|
||||
let world = World::from_bytes(WORLD).unwrap();
|
||||
let mut all_tiles = String::new();
|
||||
for (i, board) in world.boards.iter().enumerate() {
|
||||
all_tiles.push_str(&format!("// board {}: {}\n", i, board.name));
|
||||
all_tiles.push_str(&format!("// board {}: {}\n", i, board.title));
|
||||
all_tiles.push_str(&format_tiles(board));
|
||||
}
|
||||
insta::assert_snapshot!(all_tiles);
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Reference in New Issue
Block a user