#!/usr/local/bin/perl
#################################################################################
#
#  IIII  NN    NN  FFFFFFF OOOO   RRRRRRR   MM    MM    AAAA    PPPPPP
#   II   MM    NN  FF     OO  OO  RR    RR  MMM  MMM   AA  AA   PP   PP
#   II   NNN   NN  FF    OO    OO RR    RR  MMM  MMM  AA    AA  PP   PP
#   II   NN N  MM  FFFFF OO    OO RRRRRRR   MM MM MM  AAAAAAAA  PPPPPP
#   II   NN  NNNN  FF    OO    OO RR  RR    MM    MM  AA    AA  PP
#   II   NN    NN  FF     OO  OO  RR   RR   MM    MM  AA    AA  PP
#  IIII  NN    NN  FF      OOOO   RR    RR  MM    MM  AA    AA  PP
#
#
#               F L O A T   T H E   C A V E  . . .
#
#
# (c) 1998/1999 by Toni Arnold, Zurich
# ------------------------------------------------------------------------------
# Perl-Script that draws a map in ASCII-"graphics"
# out of an Inform source file
#
# Version 1.1b0
# Serial Number 990507
#
# Some information about perl can be found at:
# http://www.gnu.org/software/perl/perl.html
# ------------------------------------------------------------------------------
# Comments and bug reports to:
#  [email protected]
#
# Copying is free as long as it is
#  - not commercial
#  - copied as a whole
# ------------------------------------------------------------------------------
#
# Call
# ----
#
# perl informap.pl infile [outfile] [flags]
#
# This creates a map with filename: filename.map.txt
# Have a look at that map with your favorite ASCII-Text-editor.
#     (be sure to switch word wrapping off!)
#
# outfile
# -------
# Some machines (eg. DOS) don't allow long filenames. For that reason
# the second argument is recognized as the output filename if it is
# not a flag. If there are multiple columns selected, the (necessary)
# numbers in the filename are added as extension to the outfile which
# makes it a file type. This not really good, but I found no other
# general solution.
#
# Flags
# -----
#
# flags are recognized by a letter followed by =
# ignores the case of the flag letter and they can appear in any order
#
# n=[number]  e.g. N=15
# ----------
# number is the width of a single room NAME printed and defaults to 11
# only odd numbers are accepded, even numers are increased by 1
#
# c=[number]  e.g. C=80
# ----------
# number is the width of a single COLUMN to be printed out. Maps get often
# larger than the maximum width of a text on the printer. For printing them
# they are split if they are larger than the column width. The filenames
# become then filename.map.001.txt filename.map.002.txt ....
#
# f=[on/off]  e.g. f=off
# ----------
# This flag indicates whether "Include"-FILEPATHS should be FOLLOWED
# or not. There are files defined which are ignored always. Edit
# @include_ignore = (...); below to add files to the ignore-list.
# Default is on.
#
# p=[hor/ver] e.g. p=ver
# -----------
# It is common style to program both a HORIZONTAL direction, e.g. w_to,
# and a VERTICAL direction, e.g. d_to, to the same room. In that case
# informap has to choose whether the horizontal direction has to be drawn
# (on the same floor) or the vertical one on a new floor (below in the
# example). Default is to PREFER horizontal - the flag can change this.
#
#
# Comment about the call in Windows
# ---------------------------------
# On Unix it is possible to call perl without "perl" by adjoining the
# perl binary path to the first line of the script. On Windows NT *.pl
# can be associated with perl, which works at least with the ActiveState
# distribution.
# ------------------------------------------------------------------------------
#
# Revision history
# ----------------
#
# V1.0   first release
# V1.1   before[go; n_obj: PlayerTo(Room,2)]; is now recognized
#
# ------------------------------------------------------------------------------
#
# Known Bugs and lacks
# --------------------
#
# - Doors (door_dir) are not handled
# - out_to is not handled. This is really a problem e.g. in adventure.inf
#
# ------------------------------------------------------------------------------
#
# Example
# =======
#
# The following is a shortened inform file:
# -----------------------------------------
#
# Object Middle
#  n_to North
#  s_to south
#  w_to West
#  sw_to southwest
#  nw_to northwest
#
# Object Southwest
#  ne_to Middle
#  n_to West
#
# Object North
#  s_to middle
#  d_to cellar
#
# Object West
#  e_to Middle
#  n_to Northwest
#  s_to South
#
# Object Cellar
#  u_to North
#
# Object NorthWest
#  se_to South
#  s_to West
#
# Object South
#  nw_to Northwest
#  n_to Middle
#
#
# This is the map drawn by INFORMAP:
# ----------------------------------
#
#                     ______________          ______________
#                    /             /         /             /
#                   /  northwest  /         /    north    /
#                  /___________|south|     /_____________/
#                                                  |
#                       /         ^             /  |
#                      /           ^           /   |
#                     /             ^         /    :
#             ______________          ______________
#            /             /         /             /
#           /    west     / ------- /   middle    /.
#          /___>south>___/         /_____________/
#                               ___
#               ^           ___/        /          .
#              ^        ___/           /
#             ^     ___/              /
#     ______________          ______________       .
#    /             /     |northwe|         /
#   /  southwest  /         /    south    /
#  /_____________/         /_____________/         .
#
#
#                                                  .
#                                                  :
#                                                  |
#                                                  |
#                                             _____|________
#                                            /             /
#                                           /   cellar    /
#                                          /_____________/
#
#
# Legend
# ------
# - Names in squares are rooms
# - Connections in between ("lines") are compass directions
# - If a connection is drawn as >>> od ^^^ it is only in one direction
# - If a room cannot be connected with a line it is printed on the border
#   of the room it is attached to. It is written as |room| if it is bidirectional
#   and as >room> if it is only in one direction
#
# ------------------------------------------------------------------------------
# This file contains the whole commented source code. I suppose that it still
# contains inconsistencies and the like; I just made it work by hacking.
#
# Table of sections
# -----------------
#
# SECTION 1: GET COMMAND LINE
# SECTION 2: GET FILE(S) AND DECODE INPUT
# SECTION 3: BUILD LOGICAL MAP
# SECTION 4: BUILD LOGICAL DIRECTIONS
# SECTION 5: BUILD GRAPHICAL MAP
# SECTION 6: SAVE GRAPHICAL MAP
#
# For non-perl-users: the script has to be read as a script which is executed
# line per line from top to bottom. Subroutines (sub name { ) of course are
# skipped while passing them executing the script.
# Some parts (esp. sections 3 and 4) are extremely redundant. With Object
# oriented programming the space could possibly be reduced, but I was not
# able to use objects, so the code is old fashioned c-klone-style.
#################################################################################


# This Array contains "include"-files that shoud be ignored.
# It can be parts of the game that should not be "mapped"
# or - more important - library files containig rooms
# Case is ignored!

@include_ignore = ("Parser","VerbLib","Grammar",
                  "GermanG");                  # language library



$outfile_suffix = ".map";               # change it if you like it different
$outfile_type = ".txt";



#######################
# Print title message #
#######################

print "\nINFORMAP  V1.1b0\n";
print "(c) 1998/1999 by Toni Arnold, Zurich\n\n";



####################### SECTION 1: GET COMMAND LINE ##########################


#####################################
# get the arguments
#####################################

# first argument = inform file
# $inform to draw a map from
# -> open it and read content to
# @inform


$inform = $ARGV[0];
$outfile_name = $inform;        # can be overridden by explicite filename

if ((not defined($inform)) or ($inform =~ /\w=.*/)) {
 die "No input file specified!\nCall: perl informap.pl infile [outfile] [flags]\n";
}

# argument defaults
$room_width = 13;
$column_width = 99999;
$include_flag = "on";
$prefer_dir = "hor";


# facualtative more arguments
# n=13  width of the room Names (default = 11)
#        allows only odd numbers
# c=80  width of a column for printing
#       the columns get numbered from 000 to 999
#        (default = 99999 [+- infinite for screen])


for ($i=1;$i<=@ARGV;$i++) {                     # number of arguments
 if (defined($ARGV[$i])) {
   $arg = $ARGV[$i];

   if (($i==1) &&      ($arg ne "") &&         # second argument as possible filename
        ($arg !~ /\w=.*/)) {                   # matches everything except flags
     $outfile_name = $arg;                     # take it as outfilename
     $outfile_suffix = "";                     # no additional suffix and type
     $outfile_type = "";
     $arg = "";                        # mark as done
   };


   if ($arg =~ /n=(\d+)/i) {           # --- N=23 ---
     $room_width = $1;

     if ($room_width > 33) {           # names in inform up to 32 chars
       $room_width = 33;
     };

     if ($room_width < 0) {            # if < 0 make it > 0
       $room_width = $room_width * -1;  # (not necessary no more)
     };

     if (($room_width % 2) == 0) {     # if even
       $room_width++;          # make it odd
     };
     $arg = "";                        # mark as done
   };


   if ($arg =~ /c=(\d+)/i) {           # --- C=23 ---
     $column_width = $1;
     $arg = "";
   };


   if (lc($arg) eq "f=on") {           # --- F=ON ---
     $include_flag = "on";
     $arg = "";
   };
   if (lc($arg) eq "f=off") {          # --- F=OFF ---
     $include_flag = "off";
     $arg = "";
   };


   if (lc($arg) eq "p=hor") {          # --- P=HOR ---
     $prefer_dir = "hor";
     $arg = "";
   };
   if (lc($arg) eq "p=ver") {          # --- P=VER ---
     $prefer_dir = "ver";
     $arg = "";
   };


   if ($arg ne "") {           # !! die on invalid arguments !!
     die ("Invalid argument: $arg\n");
   };
 };
};







#################### SECTION 2: GET FILE(S) AND DECODE INPUT ####################


#########################################
# Read Map-Relevant Data into Array
# of arrays of room
# --------------------------------------
# The array @room[n] contains afterwards
# for each room n an entry like below.
# Entry for room $x:
# @room($room_elements*$x)
#########################################

# @rooms[n] =
$name = 0;
$n_to = 1;
$ne_to = 2;
$e_to = 3;
$se_to = 4;
$s_to = 5;
$sw_to = 6;
$w_to = 7;
$nw_to = 8;
$u_to = 9;
$d_to = 10;
$out_to = 11;
$in_to = 12;
#$door_dir = 13;

$room_elements = 14; # the number of elements to manually create
                 # multidimensional array
                 # (everything else didn't work)




# ------------------------------------------
# accommodate_new_room
# is called inside the input line loop
# every time when a direction is detected.
# if it's the first direction of the object,
# then the object is recognized as a room
# ------------------------------------------

sub accommodate_new_room {
 my($i);

 if ($room_detected eq "false") { # if it's not already recogn. as a room

   $room_number++; # increase room number to new room
   @room[$room_number*$room_elements] = $possible_room; # name of the room

   for ($i=1,$i<$room_elements,$i++) { # init direction strings
     @room[($room_number*$room_elements)+$i] = "";      # initial value
   };

   $room_detected = "true"; # mark current room as initialized
 };
}



# --------------------------------------------------
# is_valid_file(Filename)
# checks the Filename case insensitive against
# @include_ignore (at the beginning of the script)
# returns true if it is valid, else false
# --------------------------------------------------

sub is_valid_filename {
  my($file) = @_;
  my($a,$b,$i,$end,$out);

  $out = "true";               # init default value
  $a = lc($file);              # lower case filename
  $end = @include_ignore;

  for ($i=0;$i<=$end;$i++) {   # loop trough every skip filename
    $b = lc($include_ignore[$i]);
    if ($a eq $b) {
      $out = "false";
    };
  };

  $out;
}




# ---------------- input line loop --------------------
# @inform holds the current inform file
# graps the information from this file
# to handle include-statements it is a subroutine

sub input_line_loop {

 $line_number=0;
 $room_detected = "false";    # whether current object is a room
 $before_detected = "false";  # for before go: n_obj: PlayerTo...
 $go_detected = "false";      # the 'go:' itself
 $go_obj_detected = "";       # no go-obj yet detected



 while (<INFORMFILE>) {
  $line = $_;           # $line contains the actual line


  if ($line !~ /^\s*!/) {      # skip commented lines

   $line =~ s/!.*//;   # remove comment at the end


 # pattern matching: recognize Include-Statements
   if ($include_flag eq "on") {
     if (($line =~ /include\s*"(.+)"/i) and
         (is_valid_filename($1) eq "true"))            {
        push (@file_stack,$1);
     };
   };

 # pattern matching: recognize object header (= new room)
   if ($line =~ /object\s+(\w+)/i ) {
                 # ^word "object" (/i = ignore case)
                       # ^word boundary
                            # ^name of the (possible) room object


     $possible_room = "\L$1\E";              # room name could be any object
     $possible_room =~ s/\Afalse\Z/false /;  # avoid name conflicts because
 #     FOR PERL false is equal to the string "false" (SHIT) -> add a space

     $room_detected = "false";    # so it is not detected as room up to now
     $before_detected = "false";  # and no before
     $go_detected = "false";      # and no go up to now
     $go_obj_detected = "";       # no go-obj yet detected
   };


 # === pattern matching: assign the explicite directions ===

   if ($line =~ /\bn_to\s+(\w+)/i ) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$n_to] = $r;
   };

   if ($line =~ /\bne_to\s+(\w+)/i ) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$ne_to] = $r;
   };

   if ($line =~ /\be_to\s+(\w+)/i ) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$e_to] = $r;
   };

   if ($line =~ /\bse_to\s+(\w+)/i ) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$se_to] = $r;
   };

   if ($line =~ /\bs_to\s+(\w+)/i ) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$s_to] = $r;
   };

   if ($line =~ /\bsw_to\s+(\w+)/i ) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$sw_to] = $r;
   };

   if ($line =~ /\bw_to\s+(\w+)/i ) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$w_to] = $r;
   };

   if ($line =~ /\bnw_to\s+(\w+)/i ) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$nw_to] = $r;
   };

   if ($line =~ /\bu_to\s+(\w+)/i ) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$u_to] = $r;
   };

   if ($line =~ /\bd_to\s+(\w+)/i ) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$d_to] = $r;
   };

   if ($line =~ /\bin_to\s+(\w+)/i ) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$in_to] = $r;
   };

   if ($line =~ /\bout_to\s+(\w+)/i ) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$out_to] = $r;
   };


 # === pattern matching: assign the before go: - directions ===

   if ($line =~ /\bbefore\b/i ) {          # detect before
          $before_detected = "true";
   };

   if ($line =~ /\bgo\s*:/i ) {            # detect go
       if ($before_detected eq "true") {   # only if before is already detected
          $go_detected = "true";
       };
   };

 # --- recognize go_objects ---

   if ($go_detected eq "true") {

     if ($line =~ /\b(n_obj)\s*:/i ) {
         $go_obj_detected = lc($1);
     };
     if ($line =~ /\b(ne_obj)\s*:/i ) {
         $go_obj_detected = lc($1);
     };
     if ($line =~ /\b(e_obj)\s*:/i ) {
         $go_obj_detected = lc($1);
     };
     if ($line =~ /\b(se_obj)\s*:/i ) {
         $go_obj_detected = lc($1);
     };
     if ($line =~ /\b(s_obj)\s*:/i ) {
         $go_obj_detected = lc($1);
     };
     if ($line =~ /\b(sw_obj)\s*:/i ) {
         $go_obj_detected = lc($1);
     };
     if ($line =~ /\b(w_obj)\s*:/i ) {
         $go_obj_detected = lc($1);
     };
     if ($line =~ /\b(nw_obj)\s*:/i ) {
         $go_obj_detected = lc($1);
     };
     if ($line =~ /\b(u_obj)\s*:/i ) {
         $go_obj_detected = lc($1);
     };
     if ($line =~ /\b(d_obj)\s*:/i ) {
         $go_obj_detected = lc($1);
     };

  };


 # --- beginning of before go - playerto-routines ---

   if (($go_obj_detected eq "n_obj") && ($line =~ /\bplayerto\((\w+),/i )) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$n_to] = $r;
   };

   if (($go_obj_detected eq "ne_obj") && ($line =~ /\bplayerto\((\w+),/i )) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$ne_to] = $r;
   };

   if (($go_obj_detected eq "e_obj") && ($line =~ /\bplayerto\((\w+),/i )) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$e_to] = $r;
   };

   if (($go_obj_detected eq "se_obj") && ($line =~ /\bplayerto\((\w+),/i )) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$se_to] = $r;
   };

   if (($go_obj_detected eq "s_obj") && ($line =~ /\bplayerto\((\w+),/i )) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$s_to] = $r;
   };

   if (($go_obj_detected eq "sw_obj") && ($line =~ /\bplayerto\((\w+),/i )) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$sw_to] = $r;
   };

   if (($go_obj_detected eq "w_obj") && ($line =~ /\bplayerto\((\w+),/i )) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$w_to] = $r;
   };

   if (($go_obj_detected eq "nw_obj") && ($line =~ /\bplayerto\((\w+),/i )) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$nw_to] = $r;
   };

   if (($go_obj_detected eq "u_obj") && ($line =~ /\bplayerto\((\w+),/i )) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$u_to] = $r;
   };

   if (($go_obj_detected eq "d_obj") && ($line =~ /\bplayerto\((\w+),/i )) {
     &accommodate_new_room;
     $r = "\L$1\E";
     $r =~ s/\Afalse\Z/false /; # avoid name conflicts
     @room[($room_number*$room_elements)+$d_to] = $r;
   };

 # --- end of before go - directions ---



 #  if ($line =~ /\bdoor_dir\s+(\w+)/i ) {
 #    &accommodate_new_room;
 #    $r = "\L$1\E";
 #    $r =~ s/\Afalse\Z/false /; # avoid name conflicts
 #    @room[($room_number*$room_elements)+$door_dir] = $r;
 #  };


  }; # endif skip commented lines
  $line_number++; # get next line

 };

}

# ------------ end of input line loop -----------------




############################################
# Load the Infocom Game File(s) into @inform
# $inform holds the main file name
############################################


# STATUS
print "Drawing a map out of the file '$inform'\n";

$room_number=-1; # initialize room counter (-1 for no room)


$input_file = $inform;          # main file = first input file


while (defined($input_file)) {  # loop for all include files

 $skip_nonfile = "false";              # for skipping nonexistent files

 unless (open(INFORMFILE, $input_file)) {
   if ($input_file eq $inform) {               # unable to open main file
     if (-e $input_file) {
        die ("Inform main file '$input_file' exists, but cannot be opened\n");
     } else {
        die ("Can't open Inform main file '$input_file'\n");
     };
   } else {
     if (-e $input_file) {
        print "Included Inform file '$input_file' exists, but cannot be opened\n";
     } else {
        print "Can't open included Inform file '$input_file'\n";
     };
     $skip_nonfile = "true";
   };
 };

 if ($skip_nonfile eq "false") {       # if it could be opened
   if ($input_file ne $inform)  {      # message only for include files
     print "Including '$input_file'\n";
   };
   &input_line_loop;           # get the whole information from the file
   close(INFORMFILE);          # close it
 };

 $input_file = pop(@file_stack);       # Include-Statements can push files
};



# -----------------------------------
# for debugging:
# print the information got from
# the inform file
# to switch on comment out:
# - - - - - - - - - - - - - - - - - -

# &print_room_array;

# -----------------------------------

sub print_room_array {

$t = $room_number + 1;
print "\ntotal number of rooms: $t\n\n";

for ($i=0;$i<=$room_number;$i++) {
  print "Room: @room[$i*$room_elements]\n";
  for ($j=1;$j<$room_elements;$j++) {
    $print = @room[($i*$room_elements)+$j];
    if ($print ne "") {
      print " $j: $print\n";
    };
  };
};
print "\n\n";
}



# STATUS

print "Got map data\n";







####################### SECTION 3: BUILD LOGICAL MAP ########################


#################################################
# Assigns to every room a position in a
# 3-dimensions-space @map
# ----------------------------------------------
# The Array has the length $room_number
# with three integers with the following indices:
# 0  Name of the room
# 1  x-Position  west -> east
# 2  y-Position  North -> South
# 3  z-Poistion  Down -> Up
#################################################


# ****************** Subroutines ******************

# index = room_in_map (+Room_Name)
# returns -1 if the room is not in the map
# else returns the index of the name of the room

sub room_in_map {
 my($room) = @_;
 my($i,$maplen,$out);
 $out=-1;                     # default: -1
 $maplen = int(@map/4);          # current length of the map
 for ($i=0;$i<$maplen;$i++) {    # go through every room
   if (@map[$i*4] eq $room) {    # * 4 = 1 array-entry
     $out = $i;
   };
 };
 $out;         # returns the index for the room
}



# boolean/string = room_at_place(+x,+y,+z)
# false if there is no room at that place,
# "name" of the room if there is one
# at this place already

sub room_at_place {
 my($x,$y,$z) = @_;
 my($i,$maplen,$return);
 $return = "false";            # init return value
 if (($x>=0) and ($y>=0) and ($z>=0)) {; # can be < 0 in the computation
   $maplen = int(@map/4);          # current length of the map
   for ($i=0;$i<$maplen;$i++) {    # go through every room
     if ((@map[($i*4)+1] == $x) and
         (@map[($i*4)+2] == $y) and
         (@map[($i*4)+3] == $z) ) {
       $return = @map[$i*4];   # name of the room
     };
   };
 };
 $return;
}







# ----------------------------
# getting room coordinates
# by room name
# ----------------------------

# x_coord = get_room_x (+RoomName)

sub get_room_x {
 my($name) = @_;
 my($i,$maplen,$return);
 $maplen = int(@map/4);          # current length of the map
 for ($i=0;$i<$maplen;$i++) {    # go through every room
   if (@map[($i*4)] eq $name) {  # if current room is the room searched for
      $return = @map[($i*4)+1];  # store its x-coordinate
   };
 };
 $return;
}


# y_coord = get_room_y (+RoomName)

sub get_room_y {
 my($name) = @_;
 my($i,$maplen,$return);
 $maplen = int(@map/4);          # current length of the map
 for ($i=0;$i<$maplen;$i++) {    # go through every room
   if (@map[($i*4)] eq $name) {  # if current room is the room searched for
      $return = @map[($i*4)+2];  # store its y-coordinate
   };
 };
 $return;
}


# z_coord = get_room_z (+RoomName)

sub get_room_z {
 my($name) = @_;
 my($i,$maplen,$return);
 $maplen = int(@map/4);          # current length of the map
 for ($i=0;$i<$maplen;$i++) {    # go through every room
   if (@map[($i*4)] eq $name) {  # if current room is the room searched for
      $return = @map[($i*4)+3];  # store its z-coordinate
   };
 };
 $return;
}




# ----------------------------
#  shifting rooms around
#  while drawing the map
# ----------------------------


# sift_x(+Position)
# Shifts all x-coordinates of the rooms in the map
# from position x until end

sub shift_x {
 my($pos) = @_;
 my($i,$maplen);
 $maplen = int(@map/4);          # current length of the map
 for ($i=0;$i<$maplen;$i++) {    # go through every room
   if (@map[($i*4)+1] >= $pos) { # if current room is right or eq of x_pos
      @map[($i*4)+1]++;          # shift it right
   };
 };
}


# sift_y(+Position)
# Shifts all y-coordinates of the rooms in the map
# from position y until end

sub shift_y {
 my($pos) = @_;
 my($i,$maplen);
 $maplen = int(@map/4);          # current length of the map
 for ($i=0;$i<$maplen;$i++) {    # go through every room
   if (@map[($i*4)+2] >= $pos) { # if current room is right or eq of y_pos
      @map[($i*4)+2]++;          # shift it right
   };
 };
}



# sift_z(+Position)
# Shifts all z-coordinates of the rooms in the map
# from position z until end

sub shift_z {
 my($pos) = @_;
 my($i,$maplen);
 $maplen = int(@map/4);          # current length of the map
 for ($i=0;$i<$maplen;$i++) {    # go through every room
   if (@map[($i*4)+3] >= $pos) { # if current room is right or eq of z_pos
      @map[($i*4)+3]++;          # shift it right
   };
 };
}





# ---- adding of a new room ----

# add_new_room (+Room_Name)
# adds a new room to the map
# (=not connected with the map)
# under every other room

sub add_new_room {
 my($room_name) = @_;
 my($i,$maplen,$z,$max_z);
 $maplen = int(@map/4);    # current length of the map
 if ($map[0] eq "") {  # if it's the first room at all
   $map[0] = $room_name;       # add it at 0,0,0
   $map[1] = 0;
   $map[2] = 0;
   $map[3] = 0;
 } else {              # if it's a new map in the map
   $max_z = 0;
   for ($i=0;$i<$maplen;$i++) {   # find max. z-pos
     if ($map[($i*4)+3] > $max_z) {   # i+3 = z-pos
       $max_z = $map[($i*4)+3];
     };
   };
   $z = @map;
   $map[$z] = $room_name;  # enter the new room at 0,0,(max_z+1)
   $map[$z+1] = 0;
   $map[$z+2] = 0;
   $map[$z+3] = $max_z + 1;
 };
}



# index = get_room_index (+Room)
# finding a room index to
# @room[i] by its name

sub get_room_index {
 my($room_name) = @_;
 my($i,$out);
 for($i=0;$i<=$room_number;$i++) {
   if (@room[$i*$room_elements] eq $room_name) {
     $out = $i*$room_elements;
   };
 };
 $out;         # returns the index for the room
}



# $current_room = next_room_left;
# compares the list of rooms detected
# against the list of rooms on the logical map
# returns "" if there is no room left,
# else the name of the next new room

sub next_room_left {
 my ($i,$room,$out);
 $out = "";            # init return

 if ($out eq "") {             # if no neighbour room found
   for ($i=0;$i<=$room_number;$i++) {  # loop trough every room detected again
     $room = @room[$i*$room_elements];
     if ((room_in_map ($room) == -1) and      # if the room is not in the map
        ($out eq "")) {                # if it's the first new room
         $out = $room;
     };
   };
 };

 $out;
}






# ****************** End of Subroutines ******************






# -----------------------------------
# Building of the map
# - - - - - - - - - - - - - - - - - -
# The first room in the text is taken
# as startpoint. It is added to the
# map and then every direction is
# scanned. If there is a room,
# it becomes added to @map
# @dirs holds the directions defined
# -----------------------------------


$current_room = next_room_left; # the first room at all
                       # -> $current_room holds a room name as a string



# the p=ver/hor - flag made it necessary to
# extract the vertical directions into a subroutine,
# but logically it belongs to the loop just like the others.

sub float_up_down {

  # u_to
  $next_room = $room[$room_index+$u_to];
  if ($next_room ne "")        {               # if there IS a room upwards
    if (room_in_map($next_room) == -1) {       # if room is not on the map

      $x = get_room_x ($current_room);         # Position of the
      $y = get_room_y ($current_room); # new room
      $z = get_room_z ($current_room)-1;

      if (room_at_place($x,$y,$z) ne "false") {        # if new pos is taken
        shift_z($z);                   # create place
      };

     if ($z == -1) {                   # new room at top -> push down
        shift_z(0);                            # all other rooms
        $z = 0;                                # z-pos of the new room
      };

      $index = @map;
      $map[$index] = $next_room;               # add the new room
      $map[$index+1] = $x;
      $map[$index+2] = $y;
      $map[$index+3] = $z;

      push (@room_stack, $next_room);  # push the new room on stack

    };
  };


  # d_to
  $next_room = $room[$room_index+$d_to];
  if ($next_room ne "")        {               # if there IS a room downwards
    if (room_in_map($next_room) == -1) {       # if room is not on the map

      $x = get_room_x ($current_room);         # Position of the
      $y = get_room_y ($current_room); # new room
      $z = get_room_z ($current_room)+1;

      if (room_at_place($x,$y,$z) ne "false") {  # if new pos is taken
        shift_z($z);                             # create place
      };

      $index = @map;
      $map[$index] = $next_room;               # add the new room
      $map[$index+1] = $x;
      $map[$index+2] = $y;
      $map[$index+3] = $z;

      push (@room_stack, $next_room);  # push the new room on stack

    };
  };
}



# --- floats every cave ---

while ($current_room ne "") {


# -------- floats one cave starting with $current_room -----------

while (defined($current_room)) {       # main loop, stop if stack is empty
  if (room_in_map($current_room) == -1) {      # if room is not on the map
    add_new_room($current_room);               # add it to the map bottom
  };

  $room_index = get_room_index($current_room);


  # if u_to and d_to have priority over the same floor e.g. e_to or n_to:
  if ($prefer_dir eq "ver") {float_up_down;};


  # n_to
  $next_room = $room[$room_index+$n_to];
  if ($next_room ne "")        {               # if there IS a room nordwards
    if (room_in_map($next_room) == -1) {       # if room is not on the map

      $x = get_room_x ($current_room);         # Position of the
      $y = get_room_y ($current_room)-1;       # new room
      $z = get_room_z ($current_room);

      if (room_at_place($x,$y,$z) ne "false") { # if new pos is taken
        shift_y($y);                            # create place
      };

      if ($y == -1) {                  # new room at back -> push forward
        shift_y(0);                            # all other rooms
        $y = 0;                                # y-pos of the new room
      };

      $index = @map;
      $map[$index] = $next_room;               # add the new room
      $map[$index+1] = $x;
      $map[$index+2] = $y;
      $map[$index+3] = $z;

      push (@room_stack, $next_room);  # push the new room on stack

    };
  };


  # s_to
  $next_room = $room[$room_index+$s_to];
  if ($next_room ne "")        {               # if there IS a room southwards
    if (room_in_map($next_room) == -1) {       # if room is not on the map

      $x = get_room_x ($current_room);         # Position of the
      $y = get_room_y ($current_room)+1;       # new room
      $z = get_room_z ($current_room);

      if (room_at_place($x,$y,$z) ne "false") {        # if new pos is taken
        shift_y($y);                   # create place
      };

      $index = @map;
      $map[$index] = $next_room;               # add the new room
      $map[$index+1] = $x;
      $map[$index+2] = $y;
      $map[$index+3] = $z;

      push (@room_stack, $next_room);  # push the new room on stack

    };
  };


  # w_to
  $next_room = $room[$room_index+$w_to];
  if ($next_room ne "")        {               # if there IS a room westwards
    if (room_in_map($next_room) == -1) {       # if room is not on the map

      $x = get_room_x ($current_room)-1;       # Position of the
      $y = get_room_y ($current_room); # new room
      $z = get_room_z ($current_room);

      if (room_at_place($x,$y,$z) ne "false") { # if new pos is taken
        shift_x($x);                            # create place
      };

      if ($x == -1) {                  # new room on left -> push
        shift_x(0);                            # right all other rooms
        $x = 0;                                # x-pos of the new room
      };

      $index = @map;
      $map[$index] = $next_room;               # add the new room
      $map[$index+1] = $x;
      $map[$index+2] = $y;
      $map[$index+3] = $z;

      push (@room_stack, $next_room);  # push the new room on stack

    };
  };


  # e_to
  $next_room = $room[$room_index+$e_to];
  if ($next_room ne "")        {               # if there IS a room eastwards
    if (room_in_map($next_room) == -1) {       # if room is not on the map

      $x = get_room_x ($current_room)+1;       # Position of the
      $y = get_room_y ($current_room); # new room
      $z = get_room_z ($current_room);

      if (room_at_place($x,$y,$z) ne "false") {  # if new pos is taken
        shift_x($x);                             # create place
      };

      $index = @map;
      $map[$index] = $next_room;               # add the new room
      $map[$index+1] = $x;
      $map[$index+2] = $y;
      $map[$index+3] = $z;

      push (@room_stack, $next_room);  # push the new room on stack

    };
  };


  # nw_to
  $next_room = $room[$room_index+$nw_to];
  if ($next_room ne "")        {               # if there IS a room northwestwards

    if (room_in_map($next_room) == -1) {       # if room is not on the map

      $x = get_room_x ($current_room)-1;       # Position of the
      $y = get_room_y ($current_room)-1;       # new room
      $z = get_room_z ($current_room);

      if (room_at_place($x,$y,$z) ne "false") {  # if new pos is taken
        shift_x($x);                             # create place on the right
      };

      if ($x == -1) {                  # new room on left -> push
        shift_x(0);                            # right all other rooms
        $x = 0;                                # x-pos of the new room
      };

      if ($y == -1) {                  # new room at back -> push
        shift_y(0);                            # forward all other rooms
        $y = 0;                                # y-pos of the new room
      };

      $index = @map;
      $map[$index] = $next_room;               # add the new room
      $map[$index+1] = $x;
      $map[$index+2] = $y;
      $map[$index+3] = $z;

      push (@room_stack, $next_room);  # push the new room on stack

    };
  };


  # se_to
  $next_room = $room[$room_index+$se_to];
  if ($next_room ne "")        {               # if there IS a room southeastwards

    if (room_in_map($next_room) == -1) {       # if room is not on the map

      $x = get_room_x ($current_room)+1;       # Position of the
      $y = get_room_y ($current_room)+1;       # new room
      $z = get_room_z ($current_room);

      if (room_at_place($x,$y,$z) ne "false") {  # if new pos is taken
        shift_x($x);                             # create place
      };

      $index = @map;
      $map[$index] = $next_room;               # add the new room
      $map[$index+1] = $x;
      $map[$index+2] = $y;
      $map[$index+3] = $z;

      push (@room_stack, $next_room);  # push the new room on stack

    };
  };


 # sw_to
  $next_room = $room[$room_index+$sw_to];
  if ($next_room ne "")        {               # if there IS a room northwestwards
    if (room_in_map($next_room) == -1) {       # if room is not on the map

      $x = get_room_x ($current_room)-1;       # Position of the
      $y = get_room_y ($current_room)+1;       # new room
      $z = get_room_z ($current_room);

      if (room_at_place($x,$y,$z) ne "false") {  # if new pos is taken
        shift_x($x);                             # create place on the right
      };

      if ($x == -1) {                  # new room on left -> push
        shift_x(0);                            # right all other rooms
        $x = 0;                                # x-pos of the new room
      };

      $index = @map;
      $map[$index] = $next_room;               # add the new room
      $map[$index+1] = $x;
      $map[$index+2] = $y;
      $map[$index+3] = $z;

      push (@room_stack, $next_room);  # push the new room on stack

    };
  };


 # ne_to
  $next_room = $room[$room_index+$ne_to];
  if ($next_room ne "")        {               # if there IS a room northwestwards
    if (room_in_map($next_room) == -1) {       # if room is not on the map

      $x = get_room_x ($current_room)+1;       # Position of the
      $y = get_room_y ($current_room)-1;       # new room
      $z = get_room_z ($current_room);

      if (room_at_place($x,$y,$z) ne "false") {  # if new pos is taken
        shift_x($x);                             # create place on the right
      };

      if ($y == -1) {                  # new room at back -> push
        shift_y(0);                            # forward all other rooms
        $y = 0;                                # x-pos of the new room
      };

      $index = @map;
      $map[$index] = $next_room;               # add the new room
      $map[$index+1] = $x;
      $map[$index+2] = $y;
      $map[$index+3] = $z;

      push (@room_stack, $next_room);  # push the new room on stack

    };
  };

  # if u_to and d_to don't have priority over the same room e.g. e_to or n_to:
  if ($prefer_dir eq "hor") {float_up_down;};


  $current_room = pop (@room_stack);           # loop with the next room
};

# -------- end of floating one cave -------------

$current_room = next_room_left;        # the next map
};

# ----- end of floating every cave





# --- compute the size of the derived map ---
# now definitive and constant

$maplen = int(@map/4);


# compute greatest x-pos
$max_x = 0;
for ($i=0;$i<$maplen;$i++) {
 $x = @map[($i*4)+1];          # actual x pos
 if ($x > $max_x) {            # max x pos
   $max_x=$x;
 };
};

# compute greatest y-pos
$max_y = 0;
for ($i=0;$i<$maplen;$i++) {
 $y = @map[($i*4)+2];          # actual x pos
 if ($y > $max_y) {            # max x pos
   $max_y=$y;
 };
};

# compute greatest z-pos
$max_z = 0;
for ($i=0;$i<$maplen;$i++) {
 $z = @map[($i*4)+3];          # actual x pos
 if ($z > $max_z) {            # max x pos
   $max_z=$z;
 };
};




# -----------------------------------
# for debugging:
# print the logical map on screen
# (every room with its coordinates)
# to switch on comment out:
# - - - - - - - - - - - - - - - - - -

# &print_room_map;

# -----------------------------------

sub print_room_map {

 my($i,$maplen);
 $maplen = int(@map/4);       # current length of the map
 for ($i=0;$i<$maplen;$i++) {
   print "@map[($i*4)+1], ";
   print "@map[($i*4)+2], ";
   print "@map[($i*4)+3]:  ";
   print "@map[($i*4)]\n";
 };
 print "\n\n";
}



# ---------------- End of Building of the Map itself






#################### SECTION 4: BUILD LOGICAL DIRECTIONS ########################


# -----------------------------------
# Building of the connections
# (graphical connections)
# - - - - - - - - - - - - - - - - - -
# This loops through the logical
# map and scans the attached
# directions for every room.
# It creates a new array
# for every direction, e.g.:
# @map_w_e  dirtype, (see below)
#           x1,y1,z1, (the room itself)
#           x2,x2,z2  (the endpoint of the line)
#                   (7 elements)
#
#
# w_e --- XXXXXX
#      _/  / | \
#    _/   /  |  \
# sw_ne  s_n |se_nw
#           d_u
# -----------------------------------
# if the directions are unary
# (n->s but no way back)
# it's mentioned as follows:
# 0: binary direction
# 1: direction away from room FROM
# 2: direction to room TO
#
# The arrays:
# @map_s_n
# @map_w_e
# @map_sw_ne
# @map_se_nw
# @map_d_u
# -----------------------------------
# if the connections cannot
# be regurarly drawn, they
# come into the 4-element-array
# @map_name_n  name,
#              x,y,z   (4 elements)
# There is an array for every
# direction, e.g
# @map_name_n
# @map_name_nw
# @map_name_w
# ...
# -----------------------------------

# number of elements in a directions map (constant)
$n_map_dir = 7; # N elements for MAP DIRections

# the direction codes (constants)
$bin = 0;
$from = 1;
$to = 2;


# next_DIRECTION (x,y,z)
# these routines (which the direction as suffix)
#  grab the next room to each compass direction
#  straightly forward
# 45DEG-directions (e.g. ne) are only
#  taken if they match exact 45DEG
# updates the global variables
#  $next_x
#  $next_y
#  $next_z
#  with the coordinates of the room found

# u_to
sub room_next_u {
 my($x,$y,$z) = @_;
 my($i,$room,$out);
 $out = "";                            # initial value
 for ($i=$z-1;$i>=0;$i--) {
   $room = room_at_place($x,$y,$i);
   if (($out eq "") and ($room ne "false")) {  # if room not found yet
     $out = $room;                     # store next room
     $next_x = $x;
     $next_y = $y;
     $next_z = $i;
   };
 };
 $out;
}

# d_to
sub room_next_d {
 my($x,$y,$z) = @_;
 my($i,$room,$out);
 $out = "";
 for ($i=$z+1;$i<=$max_z;$i++) {
   $room = room_at_place($x,$y,$i);
   if (($out eq "") and ($room ne "false")) {
     $out = $room;
     $next_x = $x;
     $next_y = $y;
     $next_z = $i;
   };
 };
 $out;
}

# n_to
sub room_next_n {
 my($x,$y,$z) = @_;
 my($i,$room,$out);
 $out = "";
 for ($i=$y-1;$i>=0;$i--) {
   $room = room_at_place($x,$i,$z);
   if (($out eq "") and ($room ne "false")) {
     $out = $room;
     $next_x = $x;
     $next_y = $i;
     $next_z = $z;
   };
 };
 $out;
}

# s_to
sub room_next_s {
 my($x,$y,$z) = @_;
 my($i,$room,$out);
 $out = "";
 for ($i=$y+1;$i<=$max_y;$i++) {
   $room = room_at_place($x,$i,$z);
   if (($out eq "") and ($room ne "false")) {
     $out = $room;
     $next_x = $x;
     $next_y = $i;
     $next_z = $z;
   };
 };
 $out;
}

# w_to
sub room_next_w {
 my($x,$y,$z) = @_;
 my($i,$room,$out);
 $out = "";
 for ($i=$x-1;$i>=0;$i--) {
   $room = room_at_place($i,$y,$z);
   if (($out eq "") and ($room ne "false")) {
     $out = $room;
     $next_x = $i;
     $next_y = $y;
     $next_z = $z;
   };
 };
 $out;
}

# e_to
sub room_next_e {
 my($x,$y,$z) = @_;
 my($i,$room,$out);
 $out = "";
 for ($i=$x+1;$i<=$max_x;$i++) {
   $room = room_at_place($i,$y,$z);
   if (($out eq "") and ($room ne "false")) {
     $out = $room;
     $next_x = $i;
     $next_y = $y;
     $next_z = $z;
   };
 };
 $out;
}

# nw_to
# main counter for n
sub room_next_nw {
 my($x,$y,$z) = @_;
 my($i,$j,$d,$room,$out);
 $out = "";
 $j=0;         # counter for w
 for ($i=$y-1;$i>=0;$i--) {
   $j--;
   $room = room_at_place($x+$j,$i,$z);
   if (($out eq "") and ($room ne "false") and (($x+$j)>=0)) {
     $out = $room;
     $next_x = $x+$j;
     $next_y = $i;
     $next_z = $z;
   };
 };
 $out;
}

# se_to
# main counter for s
sub room_next_se {
 my($x,$y,$z) = @_;
 my($i,$j,$d,$room,$out);
 $out = "";
 $j=0;         # counter for e
 for ($i=$y+1;$i<=$max_y;$i++) {
   $j++;
   $room = room_at_place($x+$j,$i,$z);
   if (($out eq "") and ($room ne "false")) {
     $out = $room;
     $next_x = $x+$j;
     $next_y = $i;
     $next_z = $z;
   };
 };
 $out;
}

# ne_to
# main counter for n
sub room_next_ne {
 my($x,$y,$z) = @_;
 my($i,$j,$d,$room,$out);
 $out = "";
 $j=0;         # counter for e
 for ($i=$y-1;$i>=0;$i--) {
   $j++;
   $room = room_at_place($x+$j,$i,$z);
   if (($out eq "") and ($room ne "false")) {
     $out = $room;
     $next_x = $x+$j;
     $next_y = $i;
     $next_z = $z;
   };
 };
 $out;
}

# sw_to
# main counter for s
sub room_next_sw {
 my($x,$y,$z) = @_;
 my($i,$j,$d,$room,$out);
 $out = "";
 $j=0;         # counter for w
 for ($i=$y+1;$i<=$max_y;$i++) {
   $j--;
   $room = room_at_place($x+$j,$i,$z);
   if (($out eq "") and ($room ne "false") and (($x+$j)>=0)) {
     $out = $room;
     $next_x = $x+$j;
     $next_y = $i;
     $next_z = $z;
   };
 };
 $out;
}

# --- end of next_DIRECTION ---




# --- get_map_DIR(x,y,z) ---
# e.g.: number = get_map_s_n(x,y,z)
# returns -1 if there is no connection at that pos,
# else the index of the found connection


# for down_up-direction
sub get_map_d_u {
 my($x,$y,$z) = @_;
 my($i,$stop,$out);
 $out = -1;            # init out
 $stop = int(@map_d_u/$n_map_dir);     # length of the map
 for ($i=0;$i<$stop;$i++) {    # loop through all dirs in the map
   if ((@map_d_u[($i*$n_map_dir)+1] == $x) and  # succeeds if there is a direction
       (@map_d_u[($i*$n_map_dir)+2] == $y) and  # at that place
       (@map_d_u[($i*$n_map_dir)+3] == $z)) {
     $out = $i*$n_map_dir;                     # the index (only one possible)
   };
 };
 $out;
}

# for south_north-direction
sub get_map_s_n {
 my($x,$y,$z) = @_;
 my($i,$stop,$out);
 $out = -1;            # init out
 $stop = int(@map_s_n/$n_map_dir);     # length of the map
 for ($i=0;$i<$stop;$i++) {    # loop through all dirs in the map
   if ((@map_s_n[($i*$n_map_dir)+1] == $x) and  # succeeds if there is a direction
       (@map_s_n[($i*$n_map_dir)+2] == $y) and  # at that place
       (@map_s_n[($i*$n_map_dir)+3] == $z)) {
     $out = $i*$n_map_dir;                     # the index (only one possible)
   };
 };
 $out;
}

# for west_east-direction
sub get_map_w_e {
 my($x,$y,$z) = @_;
 my($i,$stop,$out);
 $out = -1;            # init out
 $stop = int(@map_w_e/$n_map_dir);     # length of the map
 for ($i=0;$i<$stop;$i++) {    # loop through all dirs in the map
   if ((@map_w_e[($i*$n_map_dir)+1] == $x) and  # succeeds if there is a direction
       (@map_w_e[($i*$n_map_dir)+2] == $y) and  # at that place
       (@map_w_e[($i*$n_map_dir)+3] == $z)) {
     $out = $i*$n_map_dir;                     # the index (only one possible)
   };
 };
 $out;
}

# for southwest_northeast-direction
sub get_map_sw_ne {
 my($x,$y,$z) = @_;
 my($i,$stop,$out);
 $out = -1;            # init out
 $stop = int(@map_sw_ne/$n_map_dir);   # length of the map
 for ($i=0;$i<$stop;$i++) {    # loop through all dirs in the map
   if ((@map_sw_ne[($i*$n_map_dir)+1] == $x) and  # succeeds if there is a direction
       (@map_sw_ne[($i*$n_map_dir)+2] == $y) and  # at that place
       (@map_sw_ne[($i*$n_map_dir)+3] == $z)) {
     $out = $i*$n_map_dir;                     # the index (only one possible)
   };
 };
 $out;
}

# for southeast_northwest-direction
sub get_map_se_nw {
 my($x,$y,$z) = @_;
 my($i,$stop,$out);
 $out = -1;            # init out
 $stop = int(@map_se_nw/$n_map_dir);   # length of the map
 for ($i=0;$i<$stop;$i++) {    # loop through all dirs in the map
   if ((@map_se_nw[($i*$n_map_dir)+1] == $x) and  # succeeds if there is a direction
       (@map_se_nw[($i*$n_map_dir)+2] == $y) and  # at that place
       (@map_se_nw[($i*$n_map_dir)+3] == $z)) {
     $out = $i*$n_map_dir;                     # the index (only one possible)
   };
 };
 $out;
}




# --- build up connections main routine ---


$maplen = int(@map/4);
for ($i=0;$i<$maplen;$i++) {            # loop trough the rooms in the map

 $name = @map[($i*4)];         # get name & coordinates of this room
 $x = @map[($i*4)+1];
 $y = @map[($i*4)+2];
 $z = @map[($i*4)+3];

 $index = get_room_index($name);       # $index is not defined if room not declared,
 if (defined($index)) {                #   but mentioned in a direction
                               #    -> lines only possible TO

# ---- d_u (down-up)-pair ----

   # u_to
   # line is attached to the room upwards of room!
   $next_room = $room[$index+$u_to];           # --- room upwards of $name ---
   if ($next_room ne "")       {               # if there is a room upwards

     if (room_next_u($x,$y,$z) eq $next_room) { # and on drawable place up

       $ix = @map_d_u;                 # index to new entry for direction
       $inext = get_map_d_u($next_x,$next_y,$next_z); # globals by room_next_...
                                       # index for the next room in dirs
       if ($inext == -1) {                     # if no connection already declared
         $map_d_u[$ix] = $to;          # direction TO (seen from upwards)
         $map_d_u[$ix+1] = $next_x;            # create new entry
         $map_d_u[$ix+2] = $next_y;            # dir attached to the UP room !!
         $map_d_u[$ix+3] = $next_z;
         $map_d_u[$ix+4] = $x;         # endpoint of line
         $map_d_u[$ix+5] = $y;
         $map_d_u[$ix+6] = $z;
       } else {                                # else it is already declared
         $map_d_u[$inext] = $bin;              # change it to BINARY direction
       };

     } else {                          # road to next room is not drawable
       $jx = get_room_index($next_room);
       if (defined($jx) and            # if next_room declared
           ($room[$jx+$d_to] eq $name)) {      # and points to this room
             $next_room_out = $next_room;      # it is bidirectional
       } else {
             $next_room_out = ">$next_room";   # mark single directions
       };
       $ix = @map_name_u;                      # index to new entry for
       $map_name_u[$ix] = $next_room_out;      # room to be printed
       $map_name_u[$ix+1] = $x;                # on THIS room !!
       $map_name_u[$ix+2] = $y;
       $map_name_u[$ix+3] = $z;
     };
   };


   # d_to
   # line is attached to the room itself!
   $next_room = $room[$index+$d_to];           # --- room downwards of $name ---
   if ($next_room ne "")       {               # if there is a room downwards

     if (room_next_d($x,$y,$z) eq $next_room) { # and on drawable place down

       $ix = @map_d_u;                 # index to new entry for direction
       $iatt = get_map_d_u($x,$y,$z);          # $i for the attach-room (= this room)

       if ($iatt == -1) {                      # if no connection already declared
         $map_d_u[$ix] = $from;                # direction FROM (seen from here)
         $map_d_u[$ix+1] = $x;         # create new entry
         $map_d_u[$ix+2] = $y;         # dir attached to here !!
         $map_d_u[$ix+3] = $z;
         $map_d_u[$ix+4] = $next_x;            # endpoint of line to down
         $map_d_u[$ix+5] = $next_y;
         $map_d_u[$ix+6] = $next_z;
       } else {                                # else it is already declared
         $map_d_u[$iatt] = 0;          # change it to BINARY direction
       };

     } else {                          # road to next room is not drawable
       $jx = get_room_index($next_room);
       if (defined($jx) and            # if next_room declared
           ($room[$jx+$u_to] eq $name)) {      # and points to this room
             $next_room_out = $next_room;      # it is bidirectional
       } else {
             $next_room_out = ">$next_room";   # mark single directions
       };
       $ix = @map_name_d;                      # index to new south(!)-entry for
       $map_name_d[$ix] = $next_room_out;      # room to be printed
       $map_name_d[$ix+1] = $x;                # on THIS room !!
       $map_name_d[$ix+2] = $y;
       $map_name_d[$ix+3] = $z;
     };
   };



# ---- s_n (south-north)-pair ----

   # n_to
   # line is attached to the room nordwards of room!
   $next_room = $room[$index+$n_to];           # --- room northwards of $name ---
   if ($next_room ne "")       {               # if there is a room northwards

     if (room_next_n($x,$y,$z) eq $next_room) { # and on drawable place north

       $ix = @map_s_n;                 # index to new entry for direction
       $inext = get_map_s_n($next_x,$next_y,$next_z); # globals by room_next_...
                                       # index for the next room in dirs
       if ($inext == -1) {                     # if no connection already declared
         $map_s_n[$ix] = $to;          # direction TO (seen from nordwards)
         $map_s_n[$ix+1] = $next_x;            # create new entry
         $map_s_n[$ix+2] = $next_y;            # dir attached to the NORTH room !!
         $map_s_n[$ix+3] = $next_z;
         $map_s_n[$ix+4] = $x;         # endpoint of line
         $map_s_n[$ix+5] = $y;
         $map_s_n[$ix+6] = $z;
       } else {                                # else it is already declared
         $map_s_n[$inext] = $bin;              # change it to BINARY direction
       };

     } else {                          # road to next room is not drawable
       $jx = get_room_index($next_room);
       if (defined($jx) and            # if next_room declared
           ($room[$jx+$s_to] eq $name)) {      # and points to this room
             $next_room_out = $next_room;      # it is bidirectional
       } else {
             $next_room_out = ">$next_room";   # mark single directions
       };
       $ix = @map_name_n;                      # index to new entry for
       $map_name_n[$ix] = $next_room_out;      # room to be printed
       $map_name_n[$ix+1] = $x;                # on THIS room !!
       $map_name_n[$ix+2] = $y;
       $map_name_n[$ix+3] = $z;
     };
   };


   # s_to
   # line is attached to the room itself!
   $next_room = $room[$index+$s_to];           # --- room southwards of $name ---
   if ($next_room ne "")       {               # if there is a room southwards

     if (room_next_s($x,$y,$z) eq $next_room) { # and on drawable place south

       $ix = @map_s_n;                 # index to new entry for direction
       $iatt = get_map_s_n($x,$y,$z);          # $i for the attach-room (= this room)

       if ($iatt == -1) {                      # if no connection already declared
         $map_s_n[$ix] = $from;                # direction FROM (seen from here)
         $map_s_n[$ix+1] = $x;         # create new entry
         $map_s_n[$ix+2] = $y;         # dir attached to here !!
         $map_s_n[$ix+3] = $z;
         $map_s_n[$ix+4] = $next_x;            # endpoint of line to the south
         $map_s_n[$ix+5] = $next_y;
         $map_s_n[$ix+6] = $next_z;
       } else {                                # else it is already declared
         $map_s_n[$iatt] = 0;          # change it to BINARY direction
       };

     } else {                          # road to next room is not drawable
       $jx = get_room_index($next_room);
       if (defined($jx) and            # if next_room declared
           ($room[$jx+$n_to] eq $name)) {      # and points to this room
             $next_room_out = $next_room;      # it is bidirectional
       } else {
             $next_room_out = ">$next_room";   # mark single directions
       };
       $ix = @map_name_s;                      # index to new south(!)-entry for
       $map_name_s[$ix] = $next_room_out;      # room to be printed
       $map_name_s[$ix+1] = $x;                # on THIS room !!
       $map_name_s[$ix+2] = $y;
       $map_name_s[$ix+3] = $z;
     };
   };



# ---- w_e (west-east)-pair ----

   # e_to
   # line is attached to the room eastwards of room!
   $next_room = $room[$index+$e_to];           # --- room eastwards of $name ---
   if ($next_room ne "")       {               # if there is a room eastwards

     if (room_next_e($x,$y,$z) eq $next_room) { # and on drawable place east

       $ix = @map_w_e;                 # index to new entry for direction
       $inext = get_map_w_e($next_x,$next_y,$next_z); # globals by room_next_...
                                       # index for the next room in dirs
       if ($inext == -1) {                     # if no connection already declared
         $map_w_e[$ix] = $to;          # direction TO (seen from eastwards)
         $map_w_e[$ix+1] = $next_x;            # create new entry
         $map_w_e[$ix+2] = $next_y;            # dir attached to the EAST room !!
         $map_w_e[$ix+3] = $next_z;
         $map_w_e[$ix+4] = $x;         # endpoint of line
         $map_w_e[$ix+5] = $y;
         $map_w_e[$ix+6] = $z;
       } else {                                # else it is already declared
         $map_w_e[$inext] = $bin;              # change it to BINARY direction
       };

     } else {                          # road to next room is not drawable
       $jx = get_room_index($next_room);
       if (defined($jx) and            # if next_room declared
           ($room[$jx+$w_to] eq $name)) {      # and points to this room
             $next_room_out = $next_room;      # it is bidirectional
       } else {
             $next_room_out = ">$next_room";   # mark single directions
       };
       $ix = @map_name_e;                      # index to new entry for
       $map_name_e[$ix] = $next_room_out;      # room to be printed
       $map_name_e[$ix+1] = $x;                # on THIS room !!
       $map_name_e[$ix+2] = $y;
       $map_name_e[$ix+3] = $z;
     };
   };


   # w_to
   # line is attached to the room itself!
   $next_room = $room[$index+$w_to];           # --- room westwards of $name ---
   if ($next_room ne "")       {               # if there is a room westwards

     if (room_next_w($x,$y,$z) eq $next_room) { # and on drawable place west

       $ix = @map_w_e;                 # index to new entry for direction
       $iatt = get_map_w_e($x,$y,$z);          # $i for the attach-room (= this room)

       if ($iatt == -1) {                      # if no connection already declared
         $map_w_e[$ix] = $from;                # direction FROM (seen from here)
         $map_w_e[$ix+1] = $x;         # create new entry
         $map_w_e[$ix+2] = $y;         # dir attached to here !!
         $map_w_e[$ix+3] = $z;
         $map_w_e[$ix+4] = $next_x;            # endpoint of line to the west
         $map_w_e[$ix+5] = $next_y;
         $map_w_e[$ix+6] = $next_z;
       } else {                                # else it is already declared
         $map_w_e[$iatt] = 0;          # change it to BINARY direction
       };

     } else {                          # road to next room is not drawable
       $jx = get_room_index($next_room);
       if (defined($jx) and            # if next_room declared
           ($room[$jx+$e_to] eq $name)) {      # and points to this room
             $next_room_out = $next_room;      # it is bidirectional
       } else {
             $next_room_out = ">$next_room";   # mark single directions
       };
       $ix = @map_name_w;                      # index to new south(!)-entry for
       $map_name_w[$ix] = $next_room_out;      # room to be printed
       $map_name_w[$ix+1] = $x;                # on THIS room !!
       $map_name_w[$ix+2] = $y;
       $map_name_w[$ix+3] = $z;
     };
   };



# ---- sw_ne (southwest-northeast)-pair ----

   # ne_to
   # line is attached to the room northeastwards of room!
   $next_room = $room[$index+$ne_to];  # --- room northeastwards of $name ---
   if ($next_room ne "")       {               # if there is a room northeastwards

     if (room_next_ne($x,$y,$z) eq $next_room) { # and on drawable place northeast

       $ix = @map_sw_ne;                       # index to new entry for direction
       $inext = get_map_sw_ne($next_x,$next_y,$next_z); # globals by room_next_...
                                       # index for the next room in dirs
       if ($inext == -1) {                     # if no connection already declared
         $map_sw_ne[$ix] = $to;                # direction TO (seen from ne-wards)
         $map_sw_ne[$ix+1] = $next_x;  # create new entry
         $map_sw_ne[$ix+2] = $next_y;  # dir attached to the NORTHEAST room !!
         $map_sw_ne[$ix+3] = $next_z;
         $map_sw_ne[$ix+4] = $x;               # endpoint of line
         $map_sw_ne[$ix+5] = $y;
         $map_sw_ne[$ix+6] = $z;
       } else {                                # else it is already declared
         $map_sw_ne[$inext] = $bin;            # change it to BINARY direction
       };

     } else {                          # road to next room is not drawable
       $jx = get_room_index($next_room);
       if (defined($jx) and            # if next_room declared
           ($room[$jx+$sw_to] eq $name)) {     # and points to this room
             $next_room_out = $next_room;      # it is bidirectional
       } else {
             $next_room_out = ">$next_room";   # mark single directions
       };
       $ix = @map_name_ne;                     # index to new entry for
       $map_name_ne[$ix] = $next_room_out;     # room to be printed
       $map_name_ne[$ix+1] = $x;               # on THIS room !!
       $map_name_ne[$ix+2] = $y;
       $map_name_ne[$ix+3] = $z;
     };
   };


   # sw_to
   # line is attached to the room itself!
   $next_room = $room[$index+$sw_to];  # --- room southwestwards of $name ---
   if ($next_room ne "")       {               # if there is a room southwestwards

     if (room_next_sw($x,$y,$z) eq $next_room) { # and on drawable place southwest

       $ix = @map_sw_ne;                       # index to new entry for direction
       $iatt = get_map_sw_ne($x,$y,$z);        # $i for the attach-room (= this room)

       if ($iatt == -1) {                      # if no connection already declared
         $map_sw_ne[$ix] = $from;              # direction FROM (seen from here)
         $map_sw_ne[$ix+1] = $x;               # create new entry
         $map_sw_ne[$ix+2] = $y;               # dir attached to here !!
         $map_sw_ne[$ix+3] = $z;
         $map_sw_ne[$ix+4] = $next_x;  # endpoint of line to the southwest
         $map_sw_ne[$ix+5] = $next_y;
         $map_sw_ne[$ix+6] = $next_z;
       } else {                                # else it is already declared
         $map_sw_ne[$iatt] = 0;                # change it to BINARY direction
       };

     } else {                          # road to next room is not drawable
       $jx = get_room_index($next_room);
       if (defined($jx) and            # if next_room declared
           ($room[$jx+$ne_to] eq $name)) {     # and points to this room
             $next_room_out = $next_room;      # it is bidirectional
       } else {
             $next_room_out = ">$next_room";   # mark single directions
       };
       $ix = @map_name_sw;                     # index to new south(!)-entry for
       $map_name_sw[$ix] = $next_room_out;     # room to be printed
       $map_name_sw[$ix+1] = $x;               # on THIS room !!
       $map_name_sw[$ix+2] = $y;
       $map_name_sw[$ix+3] = $z;
     };
   };



# ---- se_nw (southwest-northeast)-pair ----

   # nw_to
   # line is attached to the room northwestwards of room!
   $next_room = $room[$index+$nw_to];  # --- room northwestwards of $name ---
   if ($next_room ne "")       {               # if there is a room northwestwards

     if (room_next_nw($x,$y,$z) eq $next_room) { # and on drawable place northwest

       $ix = @map_se_nw;                       # index to new entry for direction
       $inext = get_map_se_nw($next_x,$next_y,$next_z); # globals by room_next_...
                                       # index for the next room in dirs
       if ($inext == -1) {                     # if no connection already declared
         $map_se_nw[$ix] = $to;                # direction TO (seen from ne-wards)
         $map_se_nw[$ix+1] = $next_x;  # create new entry
         $map_se_nw[$ix+2] = $next_y;  # dir attached to the NORTHWEST room !!
         $map_se_nw[$ix+3] = $next_z;
         $map_se_nw[$ix+4] = $x;               # endpoint of line
         $map_se_nw[$ix+5] = $y;
         $map_se_nw[$ix+6] = $z;
       } else {                                # else it is already declared
         $map_se_nw[$inext] = $bin;            # change it to BINARY direction
       };

     } else {                          # road to next room is not drawable
       $jx = get_room_index($next_room);
       if (defined($jx) and            # if next_room declared
           ($room[$jx+$se_to] eq $name)) {     # and points to this room
             $next_room_out = $next_room;      # it is bidirectional
       } else {
             $next_room_out = ">$next_room";   # mark single directions
       };
       $ix = @map_name_nw;                     # index to new entry for
       $map_name_nw[$ix] = $next_room_out;     # room to be printed
       $map_name_nw[$ix+1] = $x;               # on THIS room !!
       $map_name_nw[$ix+2] = $y;
       $map_name_nw[$ix+3] = $z;
     };
   };


   # se_to
   # line is attached to the room itself!
   $next_room = $room[$index+$se_to];  # --- room southeastwards of $name ---
   if ($next_room ne "")       {               # if there is a room southeastwards

     if (room_next_se($x,$y,$z) eq $next_room) { # and on drawable place southeast

       $ix = @map_se_nw;                       # index to new entry for direction
       $iatt = get_map_se_nw($x,$y,$z);        # $i for the attach-room (= this room)

       if ($iatt == -1) {                      # if no connection already declared
         $map_se_nw[$ix] = $from;              # direction FROM (seen from here)
         $map_se_nw[$ix+1] = $x;               # create new entry
         $map_se_nw[$ix+2] = $y;               # dir attached to here !!
         $map_se_nw[$ix+3] = $z;
         $map_se_nw[$ix+4] = $next_x;  # endpoint of line to the southeast
         $map_se_nw[$ix+5] = $next_y;
         $map_se_nw[$ix+6] = $next_z;
       } else {                                # else it is already declared
         $map_se_nw[$iatt] = 0;                # change it to BINARY direction
       };

     } else {                          # road to next room is not drawable
       $jx = get_room_index($next_room);
       if (defined($jx) and            # if next_room declared
           ($room[$jx+$nw_to] eq $name)) {     # and points to this room
             $next_room_out = $next_room;      # it is bidirectional
       } else {
             $next_room_out = ">$next_room";   # mark single directions
       };
       $ix = @map_name_se;                     # index to new south(!)-entry for
       $map_name_se[$ix] = $next_room_out;     # room to be printed
       $map_name_se[$ix+1] = $x;               # on THIS room !!
       $map_name_se[$ix+2] = $y;
       $map_name_se[$ix+3] = $z;
     };
   };



 };
};





# -----------------------------------
# for debugging:
# print the logical map directions
# to switch on comment out:
# - - - - - - - - - - - - - - - - - -

# &print_room_dirs;

# -----------------------------------

sub print_room_dirs {

 my($i,$dirlen,$name);


# d_u-pair
 $dirlen = int(@map_d_u/$n_map_dir);       # down-up-directions
 print "#down-up (down of):\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_d_u[($i*$n_map_dir)+1],
                         @map_d_u[($i*$n_map_dir)+2],
                         @map_d_u[($i*$n_map_dir)+3]);
   $nname = room_at_place(@map_d_u[($i*$n_map_dir)+4],
                         @map_d_u[($i*$n_map_dir)+5],
                         @map_d_u[($i*$n_map_dir)+6]);
   print "$name to $nname: @map_d_u[($i*$n_map_dir)]\n";
 };

 $dirlen = int(@map_name_d/4); # names in the west
 print "#names down from-to:\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_name_d[($i*4)+1],
                         @map_name_d[($i*4)+2],
                         @map_name_d[($i*4)+3]);
   print "$name: @map_name_d[($i*4)]\n";
 };

 $dirlen = int(@map_name_u/4); # names in the east
 print "#names up from-to:\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_name_u[($i*4)+1],
                         @map_name_u[($i*4)+2],
                         @map_name_u[($i*4)+3]);
   print "$name: @map_name_u[($i*4)]\n";
 };
 print "\n";



# s_n-pair
 $dirlen = int(@map_s_n/$n_map_dir);       # south-north-directions
 print "#south-north (south of):\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_s_n[($i*$n_map_dir)+1],
                         @map_s_n[($i*$n_map_dir)+2],
                         @map_s_n[($i*$n_map_dir)+3]);
   $nname = room_at_place(@map_s_n[($i*$n_map_dir)+4],
                         @map_s_n[($i*$n_map_dir)+5],
                         @map_s_n[($i*$n_map_dir)+6]);
   print "$name to $nname: @map_s_n[($i*$n_map_dir)]\n";
 };

 $dirlen = int(@map_name_s/4); # names in the south
 print "#names south from-to:\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_name_s[($i*4)+1],
                         @map_name_s[($i*4)+2],
                         @map_name_s[($i*4)+3]);
   print "$name: @map_name_s[($i*4)]\n";
 };

 $dirlen = int(@map_name_n/4); # names in the north
 print "#names north from-to:\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_name_n[($i*4)+1],
                         @map_name_n[($i*4)+2],
                         @map_name_n[($i*4)+3]);
   print "$name: @map_name_n[($i*4)]\n";
 };
 print "\n";


# w_e-pair
 $dirlen = int(@map_w_e/$n_map_dir);       # west-east-directions
 print "#west-east (west of):\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_w_e[($i*$n_map_dir)+1],
                         @map_w_e[($i*$n_map_dir)+2],
                         @map_w_e[($i*$n_map_dir)+3]);
   $nname = room_at_place(@map_w_e[($i*$n_map_dir)+4],
                         @map_w_e[($i*$n_map_dir)+5],
                         @map_w_e[($i*$n_map_dir)+6]);
   print "$name to $nname: @map_w_e[($i*$n_map_dir)]\n";
 };

 $dirlen = int(@map_name_w/4); # names in the west
 print "#names west from-to:\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_name_w[($i*4)+1],
                         @map_name_w[($i*4)+2],
                         @map_name_w[($i*4)+3]);
   print "$name: @map_name_w[($i*4)]\n";
 };

 $dirlen = int(@map_name_e/4); # names in the east
 print "#names east from-to:\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_name_e[($i*4)+1],
                         @map_name_e[($i*4)+2],
                         @map_name_e[($i*4)+3]);
   print "$name: @map_name_e[($i*4)]\n";
 };
 print "\n";


# sw_ne-pair
 $dirlen = int(@map_sw_ne/$n_map_dir);       # southwest-northeast-directions
 print "#southwest-northeast (southwest of):\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_sw_ne[($i*$n_map_dir)+1],
                         @map_sw_ne[($i*$n_map_dir)+2],
                         @map_sw_ne[($i*$n_map_dir)+3]);
   $nname = room_at_place(@map_sw_ne[($i*$n_map_dir)+4],
                         @map_sw_ne[($i*$n_map_dir)+5],
                         @map_sw_ne[($i*$n_map_dir)+6]);
   print "$name to $nname: @map_sw_ne[($i*$n_map_dir)]\n";
 };

 $dirlen = int(@map_name_sw/4);        # names in the southwest
 print "#names southwest from-to:\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_name_sw[($i*4)+1],
                         @map_name_sw[($i*4)+2],
                         @map_name_sw[($i*4)+3]);
   print "$name: @map_name_sw[($i*4)]\n";
 };

 $dirlen = int(@map_name_ne/4);        # names in the northeast
 print "#names northeast from-to:\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_name_ne[($i*4)+1],
                         @map_name_ne[($i*4)+2],
                         @map_name_ne[($i*4)+3]);
   print "$name: @map_name_ne[($i*4)]\n";
 };
 print "\n";


# se_nw-pair
 $dirlen = int(@map_se_nw/$n_map_dir);       # southeast-northwest-directions
 print "#southeast-northwest (southwest of):\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_se_nw[($i*$n_map_dir)+1],
                         @map_se_nw[($i*$n_map_dir)+2],
                         @map_se_nw[($i*$n_map_dir)+3]);
   $nname = room_at_place(@map_se_nw[($i*$n_map_dir)+4],
                         @map_se_nw[($i*$n_map_dir)+5],
                         @map_se_nw[($i*$n_map_dir)+6]);
   print "$name to $nname: @map_se_nw[($i*$n_map_dir)]\n";
 };

 $dirlen = int(@map_name_se/4);        # names in the southeast
 print "#names southeast from-to:\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_name_se[($i*4)+1],
                         @map_name_se[($i*4)+2],
                         @map_name_se[($i*4)+3]);
   print "$name: @map_name_se[($i*4)]\n";
 };

 $dirlen = int(@map_name_nw/4);        # names in the northeast
 print "#names nortwest from-to:\n";
 for ($i=0;$i<$dirlen;$i++) {
   $name = room_at_place(@map_name_nw[($i*4)+1],
                         @map_name_nw[($i*4)+2],
                         @map_name_nw[($i*4)+3]);
   print "$name: @map_name_nw[($i*4)]\n";
 };
 print "\n";

}


# ----------------- End of building the connections




# STATUS

print "Created logical map\n";









####################### SECTION 5: BUILD GRAPHICAL MAP ########################


######################################
# first compute the x-size of the map
# (y-size not easily computable)
# $drawing_size_x

# $room_width comes from the command line

# this variables serve as constants
# they are not strictly independent of each other
# (e.g. room_space_x and room_space_y)
# -> leave them as they are to avoid (really) ugly maps
# (count starts from 1)

$room_size_x = $room_width + 4 + 2;     # width of a room  (with 2 lines)
$room_size_y = 2 + 2;           # height of a room (with 2 lines)

$room_space_x = 7+2;            # horizontal space between rooms
$room_space_y = 1 + 3;          # vertical space >=4 between rooms

$room_grid_x = $room_size_x + $room_space_x;
$room_grid_y = $room_size_y + $room_space_y;

$draw_offset_init = 3;          # init for $draw_offset up->down (on map drawing)
$draw_offset_z = 6;             # space between two "floors"

# $maplen = (@map/4);           # from here on static !! (moved upwards)



# computable after max_, max_y:

$drawing_offset_xy =  (($max_y+1)*$room_size_y) +       # x-offset on y-change
                     ($max_y*$room_space_y);           # for all rooms

#                                       this is the maximal 2D-width
$drawing_size_x = (($max_x+1)*$room_size_x) +   # number on one x * real width
                 ($max_x*$room_space_x) +      # spaces = number - 1
                 $drawing_offset_xy;




##############################################
# draw map into array @drawing
# -------------------------------------------
# Begin in the upper right corner and
# draw from right->left  up->down
##############################################


# put_char(char,x,y)
# put a char on a certain position
# in the 2D-array @drawing
# Attention: x and y have their
# normal 2d-meaning

$lowest_point = 0;              # reset "lowest point" of the graphic (max y)
$leftmost_point = $drawing_size_x;              # for avoiding space on the left
$rightmost_point = 0;                   # for avoiding space on the right

sub put_char {
 my($char,$x_2d,$y_2d) = @_;
 if ( ($x_2d>=0) and ($x_2d<$drawing_size_x) and ($y_2d>=0) ) {
   @drawing [ $y_2d*$drawing_size_x + $x_2d ] = $char;
   if ($y_2d > $lowest_point) {
     $lowest_point = $y_2d;
   };
   if ($x_2d < $leftmost_point) {
     $leftmost_point = $x_2d;
   };
   if ($x_2d > $rightmost_point) {
     $rightmost_point = $x_2d;
   };
 } else {
    print"### failed to put char '$char' on $x_2d,$y_2d\n";
 };
}



# draw_char(char,x,y)
# 3D-put-char-projection
# simulates 3d-printing
# on one floor
# (with down-offset
#  $draw_offset)
# x,y are char coordinates,
# not logical coordinates!

#            ----> x-coord
#           /|
#          / |
#         L  v
#  y-coord   z-coord wiht $draw_offset

sub draw_char {
 my($char,$x,$y) = @_;
 my($xm,$ym);
 $xm = $x - $y + $drawing_offset_xy;
 $ym = $y + $draw_offset;
 put_char($char,$xm,$ym);
}





# draw_room(name,x,y)
# draws a room at the
# logical position x,y
# (on one floor)

sub draw_room {
 my($name,$x_in,$y_in) = @_;
 my($stop,$xp,$x,$yp,$y,$i,$len);      # $xp and $yp are 2D-coordinates!

 # draw the two horizontal lines
 $xp = $room_grid_x * $x_in;
 $stop = $xp + $room_size_x-1; # here: start from 0
 $yp = $room_grid_y * $y_in;
 for ($x=$xp;$x<$stop;$x++) {
    draw_char("_",$x,$yp);
    draw_char("_",$x,$yp+$room_size_y-1);      # -1: starting from 0
 };

 # draw the two vertical lines
 $x = $xp;
 $y = $yp;                             # under the upper left corner
 for ($i=1;$i<$room_size_y;$i++) {             # -> $i=1
   draw_char("/",$x,$y+$i);
   draw_char("/",($x+$room_size_x-1),$y+$i);
 };

 # draw the name of the room
 $len = length($name);         # for centering the name
 if ($len > $room_width) {
   $len = $room_width;
 };
 $i = int( ($room_width-$len) / 2) ;           # leading spaces before name
 $x = $xp + 3 + $i;                    # beginning of the string
 $y = $yp + 2;
 for ($i=0;$i<$len;$i++) {
   draw_char (substr($name,$i,1),$x+$i,$y);    # draw char by char
 };

}



# number = empty_rows_y (floor)
# to compress output graphics compute number of rows
# that are blank (from y=0 to ...)
# -> gives a negative drawing offset for each floor

sub empty_rows_y {
 my($floor) = @_;
 my($return,$pfloor,$y,$z,$local_y);

 $return = 0;                  # init return

 if ($floor > 0) {             # if it's not the first floor (z=0)
    $pfloor = $floor - 1;              # compute previous floor:
    $local_y = 0;                      # find "most at front" - y
    for ($i=0;$i<$maplen;$i++) {
      $y = @map[($i*4)+2];
      $z = @map[($i*4)+3];
      if (($z == $pfloor) and ($y > $local_y)) {
         $local_y = $y;
      };
    };
    $return = $max_y - $local_y;       # -> number of empty rows in front
 };


 $local_y = $max_y;            # compute the "most at back" - y
 for ($i=0;$i<$maplen;$i++) {  # on the floor
    $y = @map[($i*4)+2];
    $z = @map[($i*4)+3];
    if (($z == $floor) and ($y < $local_y)) {
       $local_y = $y;
    };
 };

 $return = ($return + $local_y) * $room_grid_y;

 $return;
}






# ------- The subroutines for the connections ----------

# ----------------------------------------------
# draw the lines (''=bn ---, _to > >, _from < <


sub draw_line_w_e {     # the coordinates are (east point,_,west point)
 my ($x1,$y1,$x2,$style) = @_; #               -> $x1 > $x2
 my ($ystop,$ystart,$xp);
 $xstart = ($room_grid_x * $x2) + $room_size_x + 1;
 $xstop = ($room_grid_x * $x1) - 2;
 $yp = ($room_grid_y * $y1) + 2;
 if ($style == $bin) {                 # line like -----
   for ($xp=$xstart;$xp<=$xstop;$xp++) {
     draw_char("-",$xp,$yp);
   };
 } else {                      # line like > > or < <
   for ($xp=$xstart;$xp<=$xstop;$xp=$xp+2) {
     if ($style == $to) {
       draw_char(">",$xp,$yp);
     } else {
       draw_char("<",$xp,$yp);
     };
   };
 };
}


sub draw_line_s_n {     # the coordinates are (north point,_,south point)
 my ($x1,$y1,$y2,$style) = @_; #               -> $y1 < $2
 my( $ystop,$ystart,$xp);
 $ystart = ($room_grid_y * $y1) + $room_size_y + 1;
 $ystop = ($room_grid_y * $y2) - 1;
 $xp = ($room_grid_x * $x1) + int ($room_size_x/2);
 if ($style == $bin) {                 # line like |
   for ($yp=$ystart;$yp<=$ystop;$yp++) {       #           |
     draw_char("/",$xp,$yp);
   };
 } else {                      # line like ^ or v
   for ($yp=$ystart;$yp<=$ystop;$yp++) {
     if ($style == $to) {
       draw_char("^",$xp,$yp);
     } else {
       draw_char("v",$xp,$yp);
     };
   };
 };
}


sub draw_line_sw_ne {   # the coordinates are (northeast - southwest)
 my ($x1,$y1,$x2,$y2,$style) = @_;
 my ($ystart,$ystop,$xp);
 $xp = ($room_grid_x * $x1);
 $ystart = ($room_grid_y * $y1) + $room_size_y;
 $ystop = ($room_grid_y * $y2) - 1;
 if ($style == $bin) {                 # line like  ___
   for ($yp=$ystart;$yp<=$ystop;$yp++) {       #        ___/
     draw_char("_",$xp,$yp);
     draw_char("_",$xp-1,$yp);
     draw_char("_",$xp-2,$yp);
     if ($yp != $ystop) {      # suppress last / downwards
       draw_char("/",$xp-2,$yp+1);
     };
    $xp = $xp - 3;             # for the perspective
   };
 } else {                      # line like > or <
   $xp--;      # looks better
   for ($yp=$ystart;$yp<=$ystop;$yp++) {
     if ($style == $to) {
       draw_char(">",$xp,$yp);
     } else {
       draw_char("<",$xp,$yp);
     };
     $xp = $xp - 3;
   };
 };
}


sub draw_line_se_nw {   # the coordinates are (northwest - southeast)
 my ($x1,$y1,$x2,$y2,$style) = @_;
 my ($ystart,$ystop,$xp);
 $xp = ($room_grid_x * $x1) + $room_size_x + 2;        # looks best
 $ystart = ($room_grid_y * $y1) + $room_size_y + 1;
 $ystop = ($room_grid_y * $y2) - 1;
 if ($style == $bin) {                 # line like \
   for ($yp=$ystart;$yp<=$ystop;$yp++) {       #            \
     draw_char("\\",$xp,$yp);  # ! Perl ! is the single char \
     $xp = $xp + 2;    # watch perspective
   };
 } else {                      # line like > or <
   for ($yp=$ystart;$yp<=$ystop;$yp++) {
     if ($style == $to) {
       draw_char("^",$xp,$yp);
     } else {
       draw_char("v",$xp,$yp);
     };
     $xp = $xp + 2;
   };
 };
}




# -------------------------------
# draw_room_name(Room,x,y)
# draw (=prints) a room name
# centered on a 2D-position
# on a defined floor
# uses $room_width!
# ">room"-rooms are recognized
# as single direction rooms

sub draw_room_name {
 my($room,$xp,$yp) = @_;
 my ($deliminiter,$len,$i,$x,$y);

 $len = length($room);
 if (substr($room,0,1) eq ">") {       # if single direction
   $room = substr($room,1,$len-1);     # delete the ">"
   $deliminiter = ">";
 } else {
   $deliminiter = "-";
 };

 $len = length($room);         # again, for centering the name
 if ($len > ($room_width-2)) { # -2 for the /deliminiters/
   $len = $room_width-2;
 };
 $x = $xp -  int($len/2) - 1;          # alignment on the left
 $y = $yp;
 draw_char ($deliminiter,$x,$y);               # left room deliminiter
 draw_char ($deliminiter,$x+$len+1,$y);        # right deliminiter
 for ($i=0;$i<$len;$i++) {
   draw_char (substr($room,$i,1),$x+$i+1,$y);  # draw char by char
 };
}



# -----------------------------------
# draw_map_name_[dir] (name,x,y)
# draw names for not drawable lines
# for each (really each) direction
# x and y are logical coordinates!


sub draw_map_name_w {
 my($name,$x_in,$y_in) = @_;
 my($x,$y);
 $x = ($room_grid_x * $x_in);
 $y = ($room_grid_y * $y_in) + 2;
 draw_room_name($name,$x,$y);
}

sub draw_map_name_e {
 my($name,$x_in,$y_in) = @_;
 my($x,$y);
 $x = ($room_grid_x * $x_in) + $room_size_x;
 $y = ($room_grid_y * $y_in) + 2;
 draw_room_name($name,$x,$y);
}

sub draw_map_name_nw {
 my($name,$x_in,$y_in) = @_;
 my($x,$y);
 $x = ($room_grid_x * $x_in);
 $y = ($room_grid_y * $y_in) + 1;
 draw_room_name($name,$x,$y);
}

sub draw_map_name_ne {
 my($name,$x_in,$y_in) = @_;
 my($x,$y);
 $x = ($room_grid_x * $x_in) + $room_size_x;
 $y = ($room_grid_y * $y_in) + 1;
 draw_room_name($name,$x,$y);
}

sub draw_map_name_sw {
 my($name,$x_in,$y_in) = @_;
 my($x,$y);
 $x = ($room_grid_x * $x_in);
 $y = ($room_grid_y * $y_in) + 3;
 draw_room_name($name,$x,$y);
}

sub draw_map_name_se {
 my($name,$x_in,$y_in) = @_;
 my($x,$y);
 $x = ($room_grid_x * $x_in) + $room_size_x;
 $y = ($room_grid_y * $y_in) + 3;
 draw_room_name($name,$x,$y);
}

sub draw_map_name_n {
 my($name,$x_in,$y_in) = @_;
 my($x,$y);
 $x = ($room_grid_x * $x_in) + int($room_size_x/2);
 $y = ($room_grid_y * $y_in) + 1;
 draw_room_name($name,$x,$y);
}

sub draw_map_name_s {
 my($name,$x_in,$y_in) = @_;
 my($x,$y);
 $x = ($room_grid_x * $x_in) + int($room_size_x/2);
 $y = ($room_grid_y * $y_in) + 3;
 draw_room_name($name,$x,$y);
}

sub draw_map_name_u {           # up is different
 my($name,$x_in,$y_in) = @_;
 my($x,$y);
 $x = ($room_grid_x * $x_in) + int($room_size_x/2) - 3;
 $y = ($room_grid_y * $y_in) - 1;
 draw_char("|",$x,$y);
 draw_room_name($name,$x-1,$y-1);
}

sub draw_map_name_d {           # down is different
 my($name,$x_in,$y_in) = @_;
 my($x,$y);
 $x = ($room_grid_x * $x_in) + int($room_size_x/2) + 2;
 $y = ($room_grid_y * $y_in) + 4;
 draw_char("|",$x,$y);
 draw_room_name($name,$x+1,$y+1);
}



# ------- end of subroutines for the connections -------






# ======= draw the map, each floor separately =======
# sets up the array
# @draw_offset_on_floor[$z]
# which holds the draw offset for each floor
# for further additions to the drawing


$draw_offset = $draw_offset_init;

for ($z=0;$z<=$max_z;$z++) {            # for each floor

 # draw floor $z

 $draw_offset = $draw_offset - empty_rows_y($z);       # adjust draw offset
 $draw_offset_on_floor[$z] = $draw_offset;             # store it in array

 for ($i=0;$i<$maplen;$i++) {  # loop trough every room on the map
   if (@map[($i*4)+3] == $z) {         # select if room is on the right floor
     $name = @map[$i*4];
     $x = @map[($i*4)+1];
     $y = @map[($i*4)+2];
     draw_room ($name,$x,$y);
   };
 };

 $draw_offset = $draw_offset + # for next floor
                $drawing_offset_xy +
                $draw_offset_z;

};

# ---------- end of drawing the room map --------------






# ======= draw the connections, each floor separately =======


for ($z=0;$z<=$max_z;$z++) {            # for each floor

 # --- draw connections on floor $z ---

$draw_offset = $draw_offset_on_floor[$z];      # adjust draw offset


 # w_e-pairs
 $dirlen = int(@map_w_e/$n_map_dir);       # west-east-directions
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_w_e[($i*$n_map_dir)+3] == $z) {    # only if on the right floor
     $x1 = @map_w_e[($i*$n_map_dir)+1];        # starting of the line
     $y1 = @map_w_e[($i*$n_map_dir)+2];
     $x2 = @map_w_e[($i*$n_map_dir)+4];        # ($y2 == $y1)
     $style = @map_w_e[($i*$n_map_dir)];
     draw_line_w_e($x1,$y1,$x2,$style);
   };
 };

 $dirlen = int(@map_name_w/4); # names in the west
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_name_w[($i*4)+3] == $z) {  # only if on the right floor
     $x = @map_name_w[($i*4)+1];
     $y = @map_name_w[($i*4)+2];
     $name = @map_name_w[($i*4)];
     draw_map_name_w($name,$x,$y);
   };
 };

 $dirlen = int(@map_name_e/4); # names in the east
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_name_e[($i*4)+3] == $z) {
     $x = @map_name_e[($i*4)+1];
     $y = @map_name_e[($i*4)+2];
     $name = @map_name_e[($i*4)];
     draw_map_name_e($name,$x,$y);
   };
 };



 # s_n-pairs
 $dirlen = int(@map_s_n/$n_map_dir);           # south-north-directions
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_s_n[($i*$n_map_dir)+3] == $z) {    # only if on the right floor
     $x1 = @map_s_n[($i*$n_map_dir)+1];        # starting of the line
     $y1 = @map_s_n[($i*$n_map_dir)+2];
     $y2 = @map_s_n[($i*$n_map_dir)+5];        # ($x2 == $x1)
     $style = @map_s_n[($i*$n_map_dir)];
     draw_line_s_n($x1,$y1,$y2,$style);
   };
 };

 $dirlen = int(@map_name_s/4); # names in the south
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_name_s[($i*4)+3] == $z) {  # only if on the right floor
     $x = @map_name_s[($i*4)+1];
     $y = @map_name_s[($i*4)+2];
     $name = @map_name_s[($i*4)];
     draw_map_name_s($name,$x,$y);
   };
 };

 $dirlen = int(@map_name_n/4); # names in the north
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_name_n[($i*4)+3] == $z) {
     $x = @map_name_n[($i*4)+1];
     $y = @map_name_n[($i*4)+2];
     $name = @map_name_n[($i*4)];
     draw_map_name_n($name,$x,$y);
   };
 };



 # sw_ne-pairs
 $dirlen = int(@map_sw_ne/$n_map_dir); # southwest-northeast-directions
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_sw_ne[($i*$n_map_dir)+3] == $z) {  # only if on the right floor
     $x1 = @map_sw_ne[($i*$n_map_dir)+1];      # starting of the line
     $y1 = @map_sw_ne[($i*$n_map_dir)+2];
     $x2 = @map_sw_ne[($i*$n_map_dir)+4];
     $y2 = @map_sw_ne[($i*$n_map_dir)+5];
     $style = @map_sw_ne[($i*$n_map_dir)];
     draw_line_sw_ne($x1,$y1,$x2,$y2,$style);
   };
 };

 $dirlen = int(@map_name_sw/4);        # names in the southwest
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_name_sw[($i*4)+3] == $z) { # only if on the right floor
     $x = @map_name_sw[($i*4)+1];
     $y = @map_name_sw[($i*4)+2];
     $name = @map_name_sw[($i*4)];
     draw_map_name_sw($name,$x,$y);
   };
 };

 $dirlen = int(@map_name_ne/4);        # names in the northeast
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_name_ne[($i*4)+3] == $z) {
     $x = @map_name_ne[($i*4)+1];
     $y = @map_name_ne[($i*4)+2];
     $name = @map_name_ne[($i*4)];
     draw_map_name_ne($name,$x,$y);
   };
 };



 # se_nw-pairs
 $dirlen = int(@map_se_nw/$n_map_dir); # southeast-northwest-directions
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_se_nw[($i*$n_map_dir)+3] == $z) {  # only if on the right floor
     $x1 = @map_se_nw[($i*$n_map_dir)+1];      # starting of the line
     $y1 = @map_se_nw[($i*$n_map_dir)+2];
     $x2 = @map_se_nw[($i*$n_map_dir)+4];
     $y2 = @map_se_nw[($i*$n_map_dir)+5];
     $style = @map_se_nw[($i*$n_map_dir)];
     draw_line_se_nw($x1,$y1,$x2,$y2,$style);
   };
 };

 $dirlen = int(@map_name_se/4);        # names in the southeast
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_name_se[($i*4)+3] == $z) { # only if on the right floor
     $x = @map_name_se[($i*4)+1];
     $y = @map_name_se[($i*4)+2];
     $name = @map_name_se[($i*4)];
     draw_map_name_se($name,$x,$y);
   };
 };

 $dirlen = int(@map_name_nw/4);        # names in the northwest
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_name_nw[($i*4)+3] == $z) {
     $x = @map_name_nw[($i*4)+1];
     $y = @map_name_nw[($i*4)+2];
     $name = @map_name_nw[($i*4)];
     draw_map_name_nw($name,$x,$y);
   };
 };



 # d_u-pairs
 $dirlen = int(@map_d_u/$n_map_dir);   # down-up-directions
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_d_u[($i*$n_map_dir)+3] == $z) {    # only if on the right floor
     $x1 = @map_d_u[($i*$n_map_dir)+1];        # starting of the line
     $y1 = @map_d_u[($i*$n_map_dir)+2];
     $z1 = @map_d_u[($i*$n_map_dir)+3];
     $x2 = @map_d_u[($i*$n_map_dir)+4];
     $y2 = @map_d_u[($i*$n_map_dir)+5];
     $z2 = @map_d_u[($i*$n_map_dir)+6];
     $style = @map_d_u[($i*$n_map_dir)];
     push (@stack_draw_d_u, ($x1,$y1,$z1,$x2,$y2,$z2,$style));  # on stack!
   };
 };

 $dirlen = int(@map_name_d/4); # names downwards
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_name_d[($i*4)+3] == $z) {  # only if on the right floor
     $x = @map_name_d[($i*4)+1];
     $y = @map_name_d[($i*4)+2];
     $name = @map_name_d[($i*4)];
     draw_map_name_d($name,$x,$y);
   };
 };

 $dirlen = int(@map_name_u/4); # names upwards
 for ($i=0;$i<$dirlen;$i++) {
   if (@map_name_u[($i*4)+3] == $z) {
     $x = @map_name_u[($i*4)+1];
     $y = @map_name_u[($i*4)+2];
     $name = @map_name_u[($i*4)];
     draw_map_name_u($name,$x,$y);
   };
 };


};




# ========= draw the up-down-connections at last =========
# They are held in the Array
# @stack_draw_d_u[n] = ($x1,$y1,$z1,$x2,$y2,$z2,$style)


# pop the first line
$style = pop(@stack_draw_d_u);
$z2 = pop(@stack_draw_d_u);
$y2 = pop(@stack_draw_d_u);
$x2 = pop(@stack_draw_d_u);
$z1 = pop(@stack_draw_d_u);
$y1 = pop(@stack_draw_d_u);
$x1 = pop(@stack_draw_d_u);



while (defined($x1)) {

 # first part from up to down

 $offset1 = $draw_offset_on_floor[$z1];                # z-offset 1
 $x3d1 = ($room_grid_x * $x1) + int ($room_size_x / 2) + 2;
 $y3d1 = ($room_grid_y * $y1) + int ($room_size_y / 2) + 2;

 $x2d1 = $x3d1 - $y3d1 + $drawing_offset_xy;
 $y2d1 = $y3d1 + $offset1;

 $index2d = ($y2d1 * $drawing_size_x) + $x2d1; # index for start of the line

 for ($i=0;$i<=$room_space_y-2;$i++) {
   $drawindex = $index2d + ($i*$drawing_size_x);
   if ($style == $bin) {
     $drawing [$drawindex] = "|";
   };
   if ($style == $to) {
     $drawing [$drawindex] = "^";
   };
   if ($style == $from) {
     $drawing [$drawindex] = "v";
   };
 };
 $dp_index1 = $index2d+(($room_space_y-1)*$drawing_size_x);
 $drawing [$dp_index1] = ":"; # last char of line


 # second part from down to up

 $offset2 = $draw_offset_on_floor[$z2];                # z-offset 2
#  $x3d2 = ($room_grid_x * $x2) + int ($room_size_x / 2) + 2;  #not needed
 $y3d2 = ($room_grid_y * $y1) + int ($room_size_y / 2) - 2;

#  $x2d2 = $x3d2 - $y3d2 + $drawing_offset_xy;  #not needed
 $y2d2 = $y3d2 + $offset2;

 $index2d = ($y2d2 * $drawing_size_x) + $x2d1; # index for start of the line
                                               # it's the same as from up-down

 for ($i=0;$i<=$room_space_y-2;$i++) {
   $drawindex = $index2d - ($i*$drawing_size_x);       # upwards
   if ($style == $bin) {
     $drawing [$drawindex] = "|";
   };
   if ($style == $to) {
     $drawing [$drawindex] = "^";
   };
   if ($style == $from) {
     $drawing [$drawindex] = "v";
   };
 };
 $dp_index2 = $index2d-(($room_space_y-1)*$drawing_size_x);
 $char = $drawing[$dp_index2];
 if ($char ne ":") {
   $drawing [$dp_index2] = ":"; # last char of line
 } else {
   if ($style == $bin) {
     $drawing [$dp_index2] = "|";
   };
   if ($style == $to) {
     $drawing [$dp_index2] = "^";
   };
   if ($style == $from) {
     $drawing [$dp_index2] = "v";
   };
 };


 # print lose dot connections between two rooms (afterwards!)

 $dp_index1 = $dp_index1 + ($drawing_size_x * 3);      # init value
 while ($dp_index2 > $dp_index1) {
   $drawing [$dp_index1]  = ".";
   $dp_index1 = $dp_index1 + ($drawing_size_x * 3);    # continue
 };


 # pop next room
 $style = pop(@stack_draw_d_u);
 $z2 = pop(@stack_draw_d_u);
 $y2 = pop(@stack_draw_d_u);
 $x2 = pop(@stack_draw_d_u);
 $z1 = pop(@stack_draw_d_u);
 $y1 = pop(@stack_draw_d_u);
 $x1 = pop(@stack_draw_d_u);
};



# ------------ end of drawing the connections ----------------









####################### SECTION 6: SAVE GRAPHICAL MAP ########################



#######################
# save map into file(s)
# If the map is larger
# than $column_width
# draw multiple maps
#######################

# draws only the really printed map width ("if point in space")


if ($column_width >= $drawing_size_x) { # --- single file for 1 column ---

 $outfile = "$outfile_name$outfile_suffix$outfile_type";

# STATUS
 print "Saving '$outfile'\n";

 unless (open(OUTFILE, ">$outfile")) {
   die ("Can't write to output file '$outfile'\n");
 };

   $drawing_size_y = int(@drawing/$drawing_size_x)+1;

   for ($y=0;$y<$drawing_size_y;$y++) {        # every line
     $out_buffer = "";                 # reset output line buffer

     $y_offset = $drawing_size_x * $y; # offset into @drawing-array

     for ($x=$leftmost_point;$x<$drawing_size_x;$x++) {        # draw each line
       if ($x <= $rightmost_point) {           # put only if in space
         $char = $drawing[$y_offset+$x];
         if ($char eq "") {
           $out_buffer = "$out_buffer ";       # add space to buffer
         } else {
           $out_buffer = "$out_buffer$char";   # else add char to buffer
         };
       };
     };

     print OUTFILE "$out_buffer\n";            # write one line to output
   };

 close(OUTFILE);


} else {                                        # --- multiple files for n columns ---


 $column_number = int($drawing_size_x/$column_width)+1;
 if ($column_number>999) {
   print "Abort due to $column_number columns...\n";
   die ("-> increase the 'c=$column_width' flag\n");
 };


 $outfile_body = "$outfile_name$outfile_suffix";

# STATUS
 print "Saving the columns '$outfile_body.xxx$outfile_type'\n";

 for ($j=0;$j<$column_number;$j++) {           # loop through the columns

   if ($j<=999) {$digit = "$j";};
   if ($j<=99) {$digit = "0$j";};
   if ($j<=9) {$digit = "00$j";};
   if ($j==0) {$digit = "000";};


   if ( (($j*$column_width)+$leftmost_point)   # leftmost point of this column
              <= $rightmost_point) {           # open file if point in space

     unless (open(OUTFILE, ">$outfile_body.$digit$outfile_type")) {
       die ("Can't write to output file '$outfile'\n");
     };

       $drawing_size_y = int(@drawing/$drawing_size_x)+1;

       for ($y=0;$y<$drawing_size_y;$y++) {    # every line
         $out_buffer = "";                     # reset output line buffer

         $y_offset = $drawing_size_x * $y;     # offset into @drawing-array

         $x_start = ($j*$column_width)+$leftmost_point;
         if ($j == ($column_number-1)) {       # last column
             $x_end = $drawing_size_x;
         } else {
             $x_end = $x_start + $column_width;        # intermediate column
         };

         for ($x=$x_start;$x<$x_end;$x++) {            # draw each line
           $char = $drawing[$y_offset+$x];
           if ($x <= $rightmost_point) {               # put only if in space
             if ($char eq "") {
               $out_buffer = "$out_buffer ";           # add space to buffer
             } else {
               $out_buffer = "$out_buffer$char";       # else add char to buffer
             };
           };
         };

         print OUTFILE "$out_buffer\n";                # write one line to output
       };

     close(OUTFILE);
   };

 };
};





# STATUS

print "Done\n";

####################### end of INFORMAP "float the cave" ########################