#!/usr/bin/perl -w use Tk; use Tk::ROText; use Tk::BrowseEntry; my $TOME_LIB = '/usr/local/lib/tome2/edit'; my %W = ( dist1 => 'not very far', dir1 => 'north', town1 => 'of Bree', dist2 => 'not very far', dir2 => 'north', town2 => 'of Minas Anor', x_max => 0, y_max => 0, melkor => 0, ); my @dist = ('not very far', 'quite some way', 'a long way', 'a very long way'); my @dir = qw(north south east west northeast northwest southeast southwest); my %dist = ( 'not very far' => [0, 7], 'quite some way' => [8, 24], 'a long way' => [25, 40], 'a very long way' => [41, 99999], ); my %dir = ( # x, y north => [ 0, -1], south => [ 0, 1], east => [ 1, 0], west => [-1, 0], northeast => [ 1, -1], northwest => [-1, -1], southeast => [ 1, 1], southwest => [-1, 1], ); my %town = ( 'of Bree' => [34, 21], 'of Minas Anor' => [60, 56], 'of Angband' => [34, 7], 'of Mordor' => [65, 58], ); my @map = (); sub load_map () { local $_; local $live = 1; open IN, "<$TOME_LIB/w_info.txt" or die "Cannot open $TOME_LIB/w_info.txt: $!\n"; while () { chomp; if (/^W:D:(.*)$/) { # A map line. Skip it if we're in a dead conditional. next if !$live; push @map, $1; next; } if (/^\?:\[/) { # A conditional line; assume it's false. $live = 0; next; } if (/^\?:1$/) { # End of conditional. $live = 1; next; } } close IN; # Max x and y shouldn't count the impenetrable border around the map. $W{y_max} = $#map; $W{x_max} = length($map[0]) - 1; } sub in_range ($$$$$) { my ($dist, $dir, $town, $x, $y) = @_; my ($xt, $yt) = @{$town{$town}}; my ($dx, $dy) = ($x - $xt, $y - $yt); my ($ddx, $ddy) = @{$dir{$dir}}; my $d = (abs($dy) > abs($dx) ? abs($dy) : abs($dx)); # Check dir. return 0 if $dx * $ddx < 0 || ($ddx == 0 && abs($dx) >= 3); return 0 if $dy * $ddy < 0 || ($ddy == 0 && abs($dy) >= 3); # Check dist. return ($d >= $dist{$dist}[0] && $d <= $dist{$dist}[1]); } sub update_map () { my @live = (); $W{map}->tagRemove('live', '1.0' => 'end'); $W{map}->tagRemove('town', '1.0' => 'end'); for (my $y = 1; $y < $W{y_max}; $y++) { my $x1 = -1; for (my $x = 1; $x < $W{x_max}; $x++) { my $live = (in_range($W{dist1}, $W{dir1}, $W{town1}, $x, $y) && in_range($W{dist2}, $W{dir2}, $W{town2}, $x, $y)); if ($live && $x1 < 0) { $x1 = $x; next; } if (!$live && $x1 >= 0) { push @live, ("${\( $y + 1 )}.$x1", "${\( $y + 1 )}.$x"); $x1 = -1; next; } } push @live, ("${\( $y + 1 )}.$x1", "${\( $y + 1 )}.end") if $x1 >= 0; } $W{map}->tagAdd('live', @live) if scalar @live > 0; my ($xt1, $yt1) = @{$town{$W{town1}}}; my ($xt2, $yt2) = @{$town{$W{town2}}}; $W{map}->tagAdd('town', "${\( $yt1 + 1 )}.$xt1"); $W{map}->tagAdd('town', "${\( $yt2 + 1 )}.$xt2"); } sub update_towns () { if ($W{melkor}) { $W{town1} = 'of Angband'; $W{town2} = 'of Mordor'; } else { $W{town1} = 'of Bree'; $W{town2} = 'of Minas Anor'; } update_map; } sub setup_window () { $W{main} = new MainWindow; $W{line1} = $W{main}->Frame->pack(-side => 'top', -fill => 'x'); $W{line2} = $W{main}->Frame->pack(-side => 'top', -fill => 'x'); $W{buttons} = $W{main}->Frame->pack(-side => 'top', -fill => 'x'); $W{map} = $W{main}->Scrolled('ROText', -setgrid => 1, -relief => 'sunken', -scrollbars => 'se', -wrap => 'none', -font => 'Courier', -fg => 'white', -bg => 'black'); $W{map}->pack(-side => 'bottom', -fill => 'both'); $W{map}->tagConfigure('live', -foreground => 'green'); $W{map}->tagConfigure('town', -foreground => 'blue'); $W{map}->insert('1.0', join("\n", @map)); $W{line1}->BrowseEntry(-variable => \$W{dist1}, -choices => \@dist, -browsecmd => sub { update_map }) ->pack(-side => 'left', -fill => 'x'); $W{line1}->BrowseEntry(-variable => \$W{dir1}, -choices => \@dir, -label => 'to the', -browsecmd => sub { update_map }) ->pack(-side => 'left', -fill => 'x'); $W{line1}->Label(-textvariable => \$W{town1}) ->pack(-side => 'left', -fill => 'x'); $W{line2}->BrowseEntry(-variable => \$W{dist2}, -choices => \@dist, -label => 'and', -browsecmd => sub { update_map }) ->pack(-side => 'left', -fill => 'x'); $W{line2}->BrowseEntry(-variable => \$W{dir2}, -choices => \@dir, -label => 'to the', -browsecmd => sub { update_map }) ->pack(-side => 'left', -fill => 'x'); $W{line2}->Label(-textvariable => \$W{town2}) ->pack(-side => 'left', -fill => 'x'); $W{buttons}->Button(-text => 'Exit', -command => sub { $W{main}->destroy }) ->pack(-side => 'right', -fill => 'x'); $W{buttons}->Checkbutton(-text => 'Melkor-based', -variable => \$W{melkor}, -command => sub { update_towns }) ->pack(-side => 'right', -fill => 'x'); } load_map; setup_window; update_map; MainLoop;