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::{
|
||||
IResult, Parser,
|
||||
bytes::complete::{tag, take},
|
||||
@@ -58,10 +60,10 @@ pub struct Board {
|
||||
pub tiles: Vec<Tile>,
|
||||
pub max_shots: u8,
|
||||
pub is_dark: bool,
|
||||
pub exit_north: u8,
|
||||
pub exit_south: u8,
|
||||
pub exit_west: u8,
|
||||
pub exit_east: u8,
|
||||
pub exit_north: Option<NonZero<u8>>,
|
||||
pub exit_south: Option<NonZero<u8>>,
|
||||
pub exit_west: Option<NonZero<u8>>,
|
||||
pub exit_east: Option<NonZero<u8>>,
|
||||
pub restart_on_zap: bool,
|
||||
pub message: String,
|
||||
pub enter_x: u8,
|
||||
@@ -251,8 +253,12 @@ impl Board {
|
||||
|
||||
// Read board info
|
||||
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)?;
|
||||
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, (enter_x, enter_y, time_limit)) = (le_u8, le_u8, le_i16).parse(input)?;
|
||||
let (input, _) = take(16usize)(input)?;
|
||||
@@ -313,10 +319,10 @@ impl Board {
|
||||
// Board info
|
||||
result.push(self.max_shots);
|
||||
result.push_bool(self.is_dark);
|
||||
result.push(self.exit_north);
|
||||
result.push(self.exit_south);
|
||||
result.push(self.exit_west);
|
||||
result.push(self.exit_east);
|
||||
result.push(self.exit_north.map_or(0, |n| n.get()));
|
||||
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_string(58, &self.message)?;
|
||||
result.push(self.enter_x);
|
||||
|
||||
+38
-17
@@ -1,5 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
use std::num::NonZero;
|
||||
|
||||
use nom::{
|
||||
IResult, Parser,
|
||||
@@ -103,7 +104,12 @@ fn write_world_header(output: &mut String, world: &World) {
|
||||
|
||||
if world.starting_board != 0 {
|
||||
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);
|
||||
|
||||
@@ -177,12 +183,17 @@ fn write_board_properties(output: &mut String, board: &Board, boards: Option<&[B
|
||||
kv_bool!(output, "dark", board.is_dark);
|
||||
|
||||
// Helper to write exit with optional board title comment
|
||||
fn write_exit(output: &mut String, name: &str, index: u8, boards: Option<&[Board]>) {
|
||||
if index != 0 {
|
||||
fn write_exit(
|
||||
output: &mut String,
|
||||
name: &str,
|
||||
index: Option<NonZero<u8>>,
|
||||
boards: Option<&[Board]>,
|
||||
) {
|
||||
if let Some(n) = index {
|
||||
let comment = boards
|
||||
.map(|b| board_title_comment(b, index))
|
||||
.map(|b| board_title_comment(b, n.get()))
|
||||
.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,
|
||||
max_shots: 255,
|
||||
is_dark: false,
|
||||
exit_north: 0,
|
||||
exit_south: 0,
|
||||
exit_west: 0,
|
||||
exit_east: 0,
|
||||
exit_north: None,
|
||||
exit_south: None,
|
||||
exit_west: None,
|
||||
exit_east: None,
|
||||
restart_on_zap: false,
|
||||
message: String::new(),
|
||||
enter_x: 1,
|
||||
@@ -981,22 +992,22 @@ fn parse_board_section(input: &str) -> Result<(&str, Board, Option<usize>), Pars
|
||||
}
|
||||
"exit_n" => {
|
||||
if let Value::Int(n) = value {
|
||||
board.exit_north = n as u8;
|
||||
board.exit_north = NonZero::new(n as u8);
|
||||
}
|
||||
}
|
||||
"exit_s" => {
|
||||
if let Value::Int(n) = value {
|
||||
board.exit_south = n as u8;
|
||||
board.exit_south = NonZero::new(n as u8);
|
||||
}
|
||||
}
|
||||
"exit_e" => {
|
||||
if let Value::Int(n) = value {
|
||||
board.exit_east = n as u8;
|
||||
board.exit_east = NonZero::new(n as u8);
|
||||
}
|
||||
}
|
||||
"exit_w" => {
|
||||
if let Value::Int(n) = value {
|
||||
board.exit_west = n as u8;
|
||||
board.exit_west = NonZero::new(n as u8);
|
||||
}
|
||||
}
|
||||
"reenter" => {
|
||||
@@ -1063,12 +1074,22 @@ fn remap_board_ref(value: u8, map: &HashMap<usize, usize>) -> u8 {
|
||||
.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.
|
||||
fn remap_board_refs(board: &mut Board, map: &HashMap<usize, usize>) {
|
||||
board.exit_north = remap_board_ref(board.exit_north, map);
|
||||
board.exit_south = remap_board_ref(board.exit_south, map);
|
||||
board.exit_east = remap_board_ref(board.exit_east, map);
|
||||
board.exit_west = remap_board_ref(board.exit_west, map);
|
||||
board.exit_north = remap_exit_ref(board.exit_north, map);
|
||||
board.exit_south = remap_exit_ref(board.exit_south, map);
|
||||
board.exit_east = remap_exit_ref(board.exit_east, map);
|
||||
board.exit_west = remap_exit_ref(board.exit_west, map);
|
||||
|
||||
// Remap passage destinations (p3 for Passage elements)
|
||||
for stat in &mut board.stats {
|
||||
|
||||
Reference in New Issue
Block a user