diff --git a/src/zzt/text.rs b/src/zzt/text.rs index c50097b..f8173ee 100644 --- a/src/zzt/text.rs +++ b/src/zzt/text.rs @@ -45,6 +45,27 @@ macro_rules! kv_str { }; } +/// Get board title comment string for a board index. +fn board_title_comment(boards: &[Board], index: u8) -> String { + boards + .get(index as usize) + .map(|b| format!(" # {}", b.name)) + .unwrap_or_default() +} + +/// Get bind target comment string (first line if it starts with @). +fn bind_target_comment(stats: &[Stat], index: u16) -> String { + stats + .get(index as usize) + .and_then(|s| match &s.program { + Program::Own(code) if code.starts_with('@') => { + code.lines().next().map(|line| format!(" # {}", line)) + } + _ => None, + }) + .unwrap_or_default() +} + /// Convert a World to its text representation. pub fn world_to_text(world: &World) -> String { let mut output = String::new(); @@ -52,7 +73,7 @@ pub fn world_to_text(world: &World) -> String { for (i, board) in world.boards.iter().enumerate() { output.push_str("\n\n"); - write_board(&mut output, Some(i), board); + write_board(&mut output, Some(i), board, Some(&world.boards)); } output @@ -61,7 +82,7 @@ pub fn world_to_text(world: &World) -> String { /// Convert a standalone Board to its text representation. pub fn board_to_text(board: &Board) -> String { let mut output = String::new(); - write_board(&mut output, None, board); + write_board(&mut output, None, board, None); output } @@ -80,7 +101,10 @@ fn write_world_header(output: &mut String, world: &World) { writeln!(output, "keys = [{}]", key_arr.join(", ")).unwrap(); } - kv!(output, "starting_board", world.starting_board, 0); + 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(); + } kv_bool!(output, "saved_game", world.locked); // Print flags until we've printed all the non-empty ones. @@ -113,7 +137,7 @@ fn keys_to_array(keys: &[bool; 7]) -> Vec<&'static str> { .collect() } -fn write_board(output: &mut String, index: Option, board: &Board) { +fn write_board(output: &mut String, index: Option, board: &Board, boards: Option<&[Board]>) { // Header cluster (always present) match index { Some(i) => writeln!(output, "[board {}]", i).unwrap(), @@ -127,20 +151,20 @@ fn write_board(output: &mut String, index: Option, board: &Board) { // Board properties cluster (may be empty) let mut props = String::new(); - write_board_properties(&mut props, board); + write_board_properties(&mut props, board, boards); // Stats cluster (may be empty) - let mut stats = String::new(); + let mut stats_output = String::new(); for (i, stat) in board.stats.iter().enumerate() { if i > 0 { - stats.push('\n'); // blank between stats + stats_output.push('\n'); // blank between stats } let element = get_element_at(board, stat.x, stat.y); - write_stat(&mut stats, i, stat, element); + write_stat(&mut stats_output, i, stat, element, &board.stats, boards); } // Join non-empty clusters with single blank lines - for cluster in [props, stats] { + for cluster in [props, stats_output] { if !cluster.is_empty() { output.push('\n'); output.push_str(&cluster); @@ -148,13 +172,25 @@ fn write_board(output: &mut String, index: Option, board: &Board) { } } -fn write_board_properties(output: &mut String, board: &Board) { +fn write_board_properties(output: &mut String, board: &Board, boards: Option<&[Board]>) { kv!(output, "shots", board.max_shots, 255); kv_bool!(output, "dark", board.is_dark); - kv!(output, "exit_n", board.exit_north, 0); - kv!(output, "exit_s", board.exit_south, 0); - kv!(output, "exit_e", board.exit_east, 0); - kv!(output, "exit_w", board.exit_west, 0); + + // 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 { + let comment = boards + .map(|b| board_title_comment(b, index)) + .unwrap_or_default(); + writeln!(output, "{} = {}{}", name, index, comment).unwrap(); + } + } + + write_exit(output, "exit_n", board.exit_north, boards); + write_exit(output, "exit_s", board.exit_south, boards); + write_exit(output, "exit_e", board.exit_east, boards); + write_exit(output, "exit_w", board.exit_west, boards); + kv_bool!(output, "reenter", board.restart_on_zap); kv!(output, "time_limit", board.time_limit, 0); if board.enter_x != 1 || board.enter_y != 1 { @@ -193,7 +229,14 @@ fn get_element_at(board: &Board, x: u8, y: u8) -> Option { board.tiles.get(index).map(|t| t.element) } -fn write_stat(output: &mut String, index: usize, stat: &Stat, element: Option) { +fn write_stat( + output: &mut String, + index: usize, + stat: &Stat, + element: Option, + stats: &[Stat], + boards: Option<&[Board]>, +) { // Stat header with element type comment let element_comment = match element { Some(id) => element_name(id), @@ -229,7 +272,17 @@ fn write_stat(output: &mut String, index: usize, stat: &Stat, element: Option { - writeln!(output, "bind = {}", idx).unwrap(); + let comment = bind_target_comment(stats, *idx); + writeln!(output, "bind = {}{}", idx, comment).unwrap(); } _ => {} }