Use NonZero type to represent board edge exits
These are the only board indexes in the ZZT file format that cannot reference the title screen; 0 is reserved to mean "no connection". This change encodes that in the type system.
This commit is contained in:
+15
-9
@@ -1,3 +1,5 @@
|
|||||||
|
use std::num::NonZero;
|
||||||
|
|
||||||
use nom::{
|
use nom::{
|
||||||
IResult, Parser,
|
IResult, Parser,
|
||||||
bytes::complete::{tag, take},
|
bytes::complete::{tag, take},
|
||||||
@@ -58,10 +60,10 @@ pub struct Board {
|
|||||||
pub tiles: Vec<Tile>,
|
pub tiles: Vec<Tile>,
|
||||||
pub max_shots: u8,
|
pub max_shots: u8,
|
||||||
pub is_dark: bool,
|
pub is_dark: bool,
|
||||||
pub exit_north: u8,
|
pub exit_north: Option<NonZero<u8>>,
|
||||||
pub exit_south: u8,
|
pub exit_south: Option<NonZero<u8>>,
|
||||||
pub exit_west: u8,
|
pub exit_west: Option<NonZero<u8>>,
|
||||||
pub exit_east: u8,
|
pub exit_east: Option<NonZero<u8>>,
|
||||||
pub restart_on_zap: bool,
|
pub restart_on_zap: bool,
|
||||||
pub message: String,
|
pub message: String,
|
||||||
pub enter_x: u8,
|
pub enter_x: u8,
|
||||||
@@ -251,8 +253,12 @@ impl Board {
|
|||||||
|
|
||||||
// Read board info
|
// Read board info
|
||||||
let (input, (max_shots, is_dark)) = (le_u8, bool_u8).parse(input)?;
|
let (input, (max_shots, is_dark)) = (le_u8, bool_u8).parse(input)?;
|
||||||
let (input, (exit_north, exit_south, exit_west, exit_east)) =
|
let (input, (exit_n, exit_s, exit_w, exit_e)) =
|
||||||
(le_u8, le_u8, le_u8, le_u8).parse(input)?;
|
(le_u8, le_u8, le_u8, le_u8).parse(input)?;
|
||||||
|
let exit_north = NonZero::new(exit_n);
|
||||||
|
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, (restart_on_zap, 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, (enter_x, enter_y, time_limit)) = (le_u8, le_u8, le_i16).parse(input)?;
|
||||||
let (input, _) = take(16usize)(input)?;
|
let (input, _) = take(16usize)(input)?;
|
||||||
@@ -313,10 +319,10 @@ impl Board {
|
|||||||
// Board info
|
// Board info
|
||||||
result.push(self.max_shots);
|
result.push(self.max_shots);
|
||||||
result.push_bool(self.is_dark);
|
result.push_bool(self.is_dark);
|
||||||
result.push(self.exit_north);
|
result.push(self.exit_north.map_or(0, |n| n.get()));
|
||||||
result.push(self.exit_south);
|
result.push(self.exit_south.map_or(0, |n| n.get()));
|
||||||
result.push(self.exit_west);
|
result.push(self.exit_west.map_or(0, |n| n.get()));
|
||||||
result.push(self.exit_east);
|
result.push(self.exit_east.map_or(0, |n| n.get()));
|
||||||
result.push_bool(self.restart_on_zap);
|
result.push_bool(self.restart_on_zap);
|
||||||
result.push_string(58, &self.message)?;
|
result.push_string(58, &self.message)?;
|
||||||
result.push(self.enter_x);
|
result.push(self.enter_x);
|
||||||
|
|||||||
+38
-17
@@ -1,5 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
use std::num::NonZero;
|
||||||
|
|
||||||
use nom::{
|
use nom::{
|
||||||
IResult, Parser,
|
IResult, Parser,
|
||||||
@@ -103,7 +104,12 @@ fn write_world_header(output: &mut String, world: &World) {
|
|||||||
|
|
||||||
if world.starting_board != 0 {
|
if world.starting_board != 0 {
|
||||||
let comment = board_title_comment(&world.boards, world.starting_board as u8);
|
let comment = board_title_comment(&world.boards, world.starting_board as u8);
|
||||||
writeln!(output, "starting_board = {}{}", world.starting_board, comment).unwrap();
|
writeln!(
|
||||||
|
output,
|
||||||
|
"starting_board = {}{}",
|
||||||
|
world.starting_board, comment
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
kv_bool!(output, "saved_game", world.locked);
|
kv_bool!(output, "saved_game", world.locked);
|
||||||
|
|
||||||
@@ -177,12 +183,17 @@ fn write_board_properties(output: &mut String, board: &Board, boards: Option<&[B
|
|||||||
kv_bool!(output, "dark", board.is_dark);
|
kv_bool!(output, "dark", board.is_dark);
|
||||||
|
|
||||||
// Helper to write exit with optional board title comment
|
// Helper to write exit with optional board title comment
|
||||||
fn write_exit(output: &mut String, name: &str, index: u8, boards: Option<&[Board]>) {
|
fn write_exit(
|
||||||
if index != 0 {
|
output: &mut String,
|
||||||
|
name: &str,
|
||||||
|
index: Option<NonZero<u8>>,
|
||||||
|
boards: Option<&[Board]>,
|
||||||
|
) {
|
||||||
|
if let Some(n) = index {
|
||||||
let comment = boards
|
let comment = boards
|
||||||
.map(|b| board_title_comment(b, index))
|
.map(|b| board_title_comment(b, n.get()))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
writeln!(output, "{} = {}{}", name, index, comment).unwrap();
|
writeln!(output, "{} = {}{}", name, n, comment).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -946,10 +957,10 @@ fn parse_board_section(input: &str) -> Result<(&str, Board, Option<usize>), Pars
|
|||||||
tiles,
|
tiles,
|
||||||
max_shots: 255,
|
max_shots: 255,
|
||||||
is_dark: false,
|
is_dark: false,
|
||||||
exit_north: 0,
|
exit_north: None,
|
||||||
exit_south: 0,
|
exit_south: None,
|
||||||
exit_west: 0,
|
exit_west: None,
|
||||||
exit_east: 0,
|
exit_east: None,
|
||||||
restart_on_zap: false,
|
restart_on_zap: false,
|
||||||
message: String::new(),
|
message: String::new(),
|
||||||
enter_x: 1,
|
enter_x: 1,
|
||||||
@@ -981,22 +992,22 @@ fn parse_board_section(input: &str) -> Result<(&str, Board, Option<usize>), Pars
|
|||||||
}
|
}
|
||||||
"exit_n" => {
|
"exit_n" => {
|
||||||
if let Value::Int(n) = value {
|
if let Value::Int(n) = value {
|
||||||
board.exit_north = n as u8;
|
board.exit_north = NonZero::new(n as u8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"exit_s" => {
|
"exit_s" => {
|
||||||
if let Value::Int(n) = value {
|
if let Value::Int(n) = value {
|
||||||
board.exit_south = n as u8;
|
board.exit_south = NonZero::new(n as u8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"exit_e" => {
|
"exit_e" => {
|
||||||
if let Value::Int(n) = value {
|
if let Value::Int(n) = value {
|
||||||
board.exit_east = n as u8;
|
board.exit_east = NonZero::new(n as u8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"exit_w" => {
|
"exit_w" => {
|
||||||
if let Value::Int(n) = value {
|
if let Value::Int(n) = value {
|
||||||
board.exit_west = n as u8;
|
board.exit_west = NonZero::new(n as u8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"reenter" => {
|
"reenter" => {
|
||||||
@@ -1063,12 +1074,22 @@ fn remap_board_ref(value: u8, map: &HashMap<usize, usize>) -> u8 {
|
|||||||
.unwrap_or(value)
|
.unwrap_or(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remap an exit reference using the ephemeral ID -> index mapping.
|
||||||
|
/// Returns the value unchanged if no mapping exists.
|
||||||
|
fn remap_exit_ref(value: Option<NonZero<u8>>, map: &HashMap<usize, usize>) -> Option<NonZero<u8>> {
|
||||||
|
value.and_then(|n| {
|
||||||
|
map.get(&(n.get() as usize))
|
||||||
|
.and_then(|&idx| NonZero::new(idx as u8))
|
||||||
|
.or(Some(n))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Remap board references in a board's exits and passage stats.
|
/// Remap board references in a board's exits and passage stats.
|
||||||
fn remap_board_refs(board: &mut Board, map: &HashMap<usize, usize>) {
|
fn remap_board_refs(board: &mut Board, map: &HashMap<usize, usize>) {
|
||||||
board.exit_north = remap_board_ref(board.exit_north, map);
|
board.exit_north = remap_exit_ref(board.exit_north, map);
|
||||||
board.exit_south = remap_board_ref(board.exit_south, map);
|
board.exit_south = remap_exit_ref(board.exit_south, map);
|
||||||
board.exit_east = remap_board_ref(board.exit_east, map);
|
board.exit_east = remap_exit_ref(board.exit_east, map);
|
||||||
board.exit_west = remap_board_ref(board.exit_west, map);
|
board.exit_west = remap_exit_ref(board.exit_west, map);
|
||||||
|
|
||||||
// Remap passage destinations (p3 for Passage elements)
|
// Remap passage destinations (p3 for Passage elements)
|
||||||
for stat in &mut board.stats {
|
for stat in &mut board.stats {
|
||||||
|
|||||||
Reference in New Issue
Block a user