#!/usr/local/bin/perl
# atopojo.pl - Generate MUD inter-area topology as JSON
#
# Copyright 2015 David Meyer <
[email protected]> +JMJ
# (License and documentation at bottom of file.)
use strict;
use warnings;
use Getopt::Std;
use JSON;
our (@AREA, %RMAREA, @RMLINK, %ALINK, @ALINK);
## @AREA: {ID, FILE, NAME}
## %RMAREA: {rmnum}, areaid
## @RMLINK: {ORIGIN, DEST}
## %ALINK: {aorigin}{adest}, type (0: 1-way; 1: 2-way (indirect);
## 2: 2-way (same rooms))
## @ALINK: {AREAO, AREAD, TYPE}
our %OPTIONS = ();
getopts('df:', \%OPTIONS);
%FSM::STATE = (SKIP => 0,
AREA => 1,
ROOMS => 2,
PENDRNAME => 3,
PENDRDESC => 4,
WAITDOOR => 5,
PENDDDESC => 6,
PENDDKW => 7,
PENDDLKD => 8);
$FSM::state = $FSM::STATE{SKIP};
sub parse_are_fsm {
my($afile, $aline) = @_;
if ($FSM::state == $FSM::STATE{SKIP}) {
if ($aline =~ /^#AREA\s+({.*}\s+\w+\s+)?(.*)~/) {
$FSM::areaname = $2;
$FSM::areaid = $#AREA + 1;
push @AREA, {ID => $FSM::areaid, FILE => $afile, NAME => $FSM::areaname};
$FSM::state = $FSM::STATE{AREA};
}
}
elsif ($FSM::state == $FSM::STATE{AREA}) {
if ($aline =~ /^#ROOMS$/) {
$FSM::state = $FSM::STATE{ROOMS};
}
elsif ($aline =~ /^#\$$/) {
$FSM::state = $FSM::STATE{SKIP};
}
}
elsif ($FSM::state == $FSM::STATE{ROOMS}) {
if ($aline =~ /^#0$/) {
$FSM::state = $FSM::STATE{AREA};
}
elsif ($aline =~ /^#(\d+)$/) {
$FSM::room = $1;
$RMAREA{$FSM::room} = $FSM::areaid;
$FSM::state = $FSM::STATE{PENDRNAME};
# if (defined $OPTIONS{d}) {print STDERR "$afile\t#$1:\t";}
}
}
elsif ($FSM::state == $FSM::STATE{PENDRNAME}) {
if ($aline =~ /~$/) {
$FSM::state = $FSM::STATE{PENDRDESC};
}
}
elsif ($FSM::state == $FSM::STATE{PENDRDESC}) {
if ($aline =~ /~$/) {
$FSM::state = $FSM::STATE{WAITDOOR};
}
}
elsif ($FSM::state == $FSM::STATE{WAITDOOR}) {
if ($aline =~ /^D(\d+)$/) {
# $FSM::exdir = $1;
$FSM::state = $FSM::STATE{PENDDDESC};
}
elsif ($aline =~ /^S$/) {
$FSM::state = $FSM::STATE{ROOMS};
# if (defined($OPTIONS{d})) {print STDERR "\n";}
}
}
elsif ($FSM::state == $FSM::STATE{PENDDDESC}) {
if ($aline =~ /~$/) {
$FSM::state = $FSM::STATE{PENDDKW};
}
}
elsif ($FSM::state == $FSM::STATE{PENDDKW}) {
if ($aline =~ /~$/) {
$FSM::state = $FSM::STATE{PENDDLKD};
}
}
elsif ($FSM::state == $FSM::STATE{PENDDLKD}) {
if ($aline =~ /^\S+\s+\S+\s+(\d+)$/) {
$FSM::exdest = $1;
push @RMLINK, {ORIGIN => $FSM::room, DEST => $FSM::exdest};
$FSM::state = $FSM::STATE{WAITDOOR};
# if (defined $OPTIONS{d}) {print STDERR "$FSM::exdest ";}
}
}
else {
die "Invalid state processing file $afile line $.";
}
}
## Read and parse area files
if (defined($OPTIONS{f})) {
open(my $FLIST, "<", $OPTIONS{f}) or die "Can't open file $OPTIONS{f}: $!";
my @afiles;
while (<$FLIST>) {
chop $_;
if ($_ eq '$') {last;}
push @afiles, $_;
}
foreach my $afile (@afiles) {
open(my $AF, "<", $afile) or die "Can't open file $afile: $!";
while (<$AF>) {
parse_are_fsm($afile, $_);
}
close $AF;
}
}
else {
while (<>) {
parse_are_fsm($ARGV, $_);
}
}
## Select links between different areas
our %arlink;
foreach my $link (@RMLINK) {
my $rorigin = $link->{ORIGIN};
my $rdest = $link->{DEST};
if (defined($RMAREA{$rdest})) {
my $aorigin = $RMAREA{$rorigin};
my $adest = $RMAREA{$rdest};
if ($aorigin != $adest) {
$arlink{$rorigin}{$rdest} = 1;
if (defined $OPTIONS{d}) {print STDERR "$rorigin\t-> $rdest";}
if (defined $OPTIONS{d}) {if (defined($arlink{$rdest}{$rorigin})) {print STDERR "\nD>O\n";} else {print STDERR "\t--\n";}}
if (defined($arlink{$rdest}{$rorigin})) {
$ALINK{$adest}{$aorigin} = 2;
}
elsif (defined($ALINK{$adest}{$aorigin})) {
if ($ALINK{$adest}{$aorigin} == 0) {
$ALINK{$adest}{$aorigin} = 1;
}
}
else {
$ALINK{$aorigin}{$adest} = 0;
}
# if (defined $OPTIONS{d}) {print STDERR "$rorigin\t-> $rdest";}
# if (defined $OPTIONS{d}) {if (defined $ALINK{$aorigin}{$adest}) {print STDERR " >$ALINK{$aorigin}{$adest}\n";} if (defined $ALINK{$adest}{$aorigin}) {print STDERR " <$ALINK{$adest}{$aorigin}\n";}}
}
}
}
foreach my $area (0 .. $#AREA) {
if (defined $ALINK{$area}) {
foreach my $adest (keys %{$ALINK{$area}}) {
push @ALINK, {AREAO => $AREA[$area]->{NAME},
AREAD => $AREA[$adest]->{NAME},
TYPE => $ALINK{$area}{$adest}};
# print "test: $ALINK[-1]->{TYPE}\t$ALINK[-1]->{AREAO}\t-> $ALINK[-1]->{AREAD}\n";
}
}
}
print encode_json {links => [@ALINK]};
exit 0
__END__
# Documentation #####################################################
=head1 NAME
template.pl - Perl script template (command line)
=head1 SYNOPSIS/USAGE
=head1 DESCRIPTION
Mark up code elements with C<>, file names with F<> (or C<> for
readability), command names with B<>. Also I<> for italics, U<> for
underline. Entities: E<lt> ('<'), E<gt> ('>').
=head1 OPTIONS
=item B<-o> I<value>, B<--option>=I<value>
=head1 RETURN VALUE
=head1 ERRORS
=head1 DIAGNOSTICS
=head1 EXAMPLES
=head1 ENVIRONMENT
=over 6
=item VARIABLE
Description of usage of environment variable C<VARIABLE>.
=back
=head1 FILES
=head1 BUGS, LIMITATIONS, AND CAVEATS
=head1 NOTES
=head1 AUTHOR
David Meyer <
[email protected]>
=head1 HISTORY
=head1 COPYRIGHT AND LICENSE
Copyright 201x David Meyer
=head1 SEE ALSO
# Emacs control #####################################################
#Local variables:
#mode: perl
#End: