 Rem File: Dndutil for Dndbbs Version v5.0a Release r2.0
 Rem $Include: 'dndutil.inc'
 Rem $Include: 'datim.bi'

 ' declare some functions
 DECLARE FUNCTION Random1! (x!, y!)
 DECLARE FUNCTION Valid.Time(V$)

 ' define command error routine
 On Error Goto Error.Trap0

 ' reseed random number generator
 Randomize Timer

 ' define top users array
 Redim Temp.ArrayS(1 To 20) As String

 ' reset maximum automatons
 Const MaxAutomatons=1024

 ' read command line from PSP
 Command.line$ = Nul
 Call StdinInt(62,0)
 PSPsegment = OutregsX.BX
 PSPoffset = 128
 DEF SEG = PSPsegment
 FOR Count = 1 TO 127
    Command.Char = PEEK(PSPoffset + Count)
    SELECT CASE Command.Char
    CASE 0,10,13
       EXIT FOR
    CASE ELSE
       Command.line$ = Command.line$ + CHR$(Command.Char)
    END SELECT
 NEXT
 DEF SEG
 Command.Line$=Ltrim$(Command.Line$)
 Command.Line$=Rtrim$(Command.Line$)

 ' initialize screen
 Call Read.Rows(Max.Row)
 Locate Max.Row-1,1,1

 ' check display command
 Select Case Ucase$(Command.Line$)
 Case "/?", "-?", "/HELP"
    Goto Display.Command
 End Select

 ' check debug mode
 VarQ=Instr(Ucase$(Command.Line$),"/D")
 If VarQ Then
    Debug.Mode=True
    Command.Line$=Left$(Command.Line$,VarQ-1)+Mid$(Command.Line$,VarQ+2)
 Endif

 ' check quiet mode
 VarQ=Instr(Ucase$(Command.Line$),"/Q")
 If VarQ Then
    Quiet.Mode=True
    Command.Line$=Left$(Command.Line$,VarQ-1)+Mid$(Command.Line$,VarQ+2)
 Endif

 ' check to over-ride specific node number
 TempA$=Nul
 Var1=Instr(Ucase$(Command.Line$),"/N:")
 If Var1 Then
    VarQ$=Mid$(Command.Line$,Var1+3,1)
    Select Case Ucase$(VarQ$)
    Case "0" To "9"
       TempA$=VarQ$
       VarQ2$=Mid$(Command.Line$,Var1+4,1)
       If VarQ2$>="0" And VarQ2$<="9" Then
          TempA$=TempA$+VarQ2$ ' 10-99
          Command.Line$=Left$(Command.Line$,Var1-1)+Mid$(Command.Line$,Var1+5)
       Else
          Command.Line$=Left$(Command.Line$,Var1-1)+Mid$(Command.Line$,Var1+4)
       Endif
    Case "A" To "Z" ' 64-90
       TempA$=VarQ$
       Command.Line$=Left$(Command.Line$,Var1-1)+Mid$(Command.Line$,Var1+4)
    End Select
 Endif
 NodeX=Nul
 If Len(TempA$) Then
    TempA$=Ucase$(TempA$)
    Select Case Len(TempA$)
    Case 1
       Select Case TempA$
       Case "0" To "9"
          NodeX=TempA$
          TempA$=Nul
       Case "A" To "Z"
          NodeX=TempA$
          TempA$=Nul
       End Select
    Case 2
       VarX=Int(Val(TempA$))
       If VarX>=10 And VarX<=99 Then
          NodeX=Mid$(Str$(VarX),2)
          TempA$=Nul
       Endif
    End Select
 Endif
 If TempA$<>Nul Then
    Goto Boot.Error
 Endif
 Command.Line$=Ltrim$(Command.Line$)
 Command.Line$=Rtrim$(Command.Line$)

 ' check command line
 If Ucase$(Left$(Command.Line$,3)) = "/X:" Then ' send system message
    Var$=Mid$(Command.Line$,4)
    If Len(Var$) Then
       Color 15,0
       Print "Dndutil v"+Version$+" utility program."
       Call Get.Config
       Close
       Call Open.Mess1.File
       Call Open.Mess2.File
       Call Init.Nodes
       Call Write.Node(5,Var$)
       Color 14,0
       Print "System message sent."
       Color 7,0
       Goto Terminate
    Endif
    Goto Boot.Error
 Endif

 ' remove spaces from command line
 Do
    V=Instr(Command.Line$," ")
    If V Then
       Command.Line$=Left$(Command.Line$,V-1)+Mid$(Command.Line$,V+1)
    Else
       Exit Do
    Endif
 Loop

 ' check command line
 Select Case Ucase$(Left$(Command.Line$,3))
 Case "/P:" ' list/print utility with port override
    VarQ$=Mid$(Command.Line$,4)
    Var1=Val(VarQ$)
    If Var1>=1 And Var1<=3 Then
       PrinterPort="LPT"+Mid$(Str$(Var1),2)+":"
    Else
       PrinterPort="LPT1:"
    Endif
    Print.Log=True ' store list type
    Goto Write.Reports
 Case "/C:" ' check to clear specific node number
    VarQ$=Mid$(Command.Line$,4)
    Select Case Len(VarQ$)
    Case 0
       Var1=0
    Case 1
       Select Case Ucase$(VarQ$)
       Case "0" To "9" ' 1-10
          Var1=Val(VarQ$)+1
       Case "A" To "Z" ' 11-36
          Var1=Asc(VarQ$)-54
       Case Else
          Goto Boot.Error
       End Select
    Case 2
       VarX=Int(Val(VarQ$))
       If VarX>=10 And VarX<=99 Then
          Var1=VarX+27 ' 37-126
       Else
          Goto Boot.Error
       Endif
    Case Else
       Goto Boot.Error
    End Select
    Color 15,0
    Print "Dndutil v"+Version$+" utility program."
    Call Get.Config
    Close
    Call Open.Mess1.File
    Call Init.Nodes
    Call Clear.Nodes
    Call Write.Message.Record(MessWorkFile1,Var1+1)
    Color 14,0
    Print "Message file node";Var1;"cleared." ' termination message
    Goto Terminate
 Case "/S:" ' check to shutdown specific node number
    VarQ$=Mid$(Command.Line$,4)
    Select Case Len(VarQ$)
    Case 0
       Var1=0
       Var1$="-"
    Case 1
       Select Case Ucase$(VarQ$)
       Case "0" To "9" ' 1-10
          Var1=Val(VarQ$)+1
          Var1$=VarQ$
       Case "A" To "Z" ' 11-36
          Var1=Asc(VarQ$)-54
          Var1$=Ucase$(VarQ$)
       Case Else
          Goto Boot.Error
       End Select
    Case 2
       VarX=Int(Val(VarQ$))
       If VarX>=10 And VarX<=99 Then
          Var1=VarX+27 ' 37-126
          Var1$=Mid$(Str$(VarX),2)
       Else
          Goto Boot.Error
       Endif
    Case Else
       Goto Boot.Error
    End Select
    Color 15,0
    Print "Dndutil v"+Version$+" utility program."
    Call Get.Config
    Close
    Call Open.Mess1.File
    Call Open.Mess2.File
    Call Shutdown.Node(Var1)
    Color 14,0
    Print "Node "+Var1$+" shutdown." ' termination message
    Goto Terminate
 Case "/T:" ' check to terminate specific node number
    VarQ$=Mid$(Command.Line$,4)
    Select Case Len(VarQ$)
    Case 0
       Var1=0
       Var1$="-"
    Case 1
       Select Case Ucase$(VarQ$)
       Case "0" To "9" ' 1-10
          Var1=Val(VarQ$)+1
          Var1$=VarQ$
       Case "A" To "Z" ' 11-36
          Var1=Asc(VarQ$)-54
          Var1$=Ucase$(VarQ$)
       Case Else
          Goto Boot.Error
       End Select
    Case 2
       VarX=Int(Val(VarQ$))
       If VarX>=10 And VarX<=99 Then
          Var1=VarX+27 ' 37-126
          Var1$=Mid$(Str$(VarX),2)
       Else
          Goto Boot.Error
       Endif
    Case Else
       Goto Boot.Error
    End Select
    Color 15,0
    Print "Dndutil v"+Version$+" utility program."
    Call Get.Config
    Close
    Call Open.Mess1.File
    Call Open.Mess2.File
    Call Terminate.Node(Var1)
    Color 14,0
    Print "Node "+Var1$+" terminated." ' termination message
    Goto Terminate
 End Select

 ' parse command line
 Select Case Ucase$(Command.Line$)
 Case "/A" ' automaton mode
    Call Save.Screen(False)
    Call Move.Automatons ' realtime mode
    Color 7,0
    Cls
    Call Save.Screen(True)
    Goto Terminate
 Case "/R" ' monitor mode
    Call Save.Screen(False)
    Call Read.Nodes ' scan node files
    Color 7,0
    Cls
    Call Save.Screen(True)
    Goto Terminate
 Case "/L" ' list utility
    Print.Log=False ' store list type
    Goto Write.Reports
 Case "/P" ' list/print utility
    Print.Log=True ' store list type
    Goto Write.Reports
 Case "/S"
    Color 15,0
    Print "Dndutil v"+Version$+" utility program."
    Call Get.Config
    Close
    Call Open.Mess1.File
    Call Open.Mess2.File
    Call Shutdown.Nodes
    Color 14,0
    Print "All nodes shutdown." ' termination message
    Goto Terminate
 Case "/T"
    Color 15,0
    Print "Dndutil v"+Version$+" utility program."
    Call Get.Config
    Close
    Call Open.Mess1.File
    Call Open.Mess2.File
    Call Terminate.Nodes
    Color 14,0
    Print "All nodes terminated." ' termination message
    Goto Terminate
 Case "/C" ' clear nodes
    Color 15,0
    Print "Dndutil v"+Version$+" utility program."
    Call Zero.Nodes
    Color 14,0
    Print "All node message files cleared." ' termination message
    Goto Terminate
 Case "/E" ' edit datafile
    Call Save.Screen(False)
    Call Edit.Automatons
    Color 7,0
    Cls
    Call Save.Screen(True)
    Goto Terminate
 Case "/W1"
    Goto Write.Profile
 Case "/W2"
    Goto Write.Modems
 Case "/W3"
    Goto Write.Template
 Case "/W4"
    Goto Write.Ports
 Case "/W5"
    Goto Write.Copyright
 Case "/W6"
    Call Get.Config
    Close
    Call Write.Util.Config
    Color 14, 0
    Print "Dndutil data file written."
    Goto Terminate
 Case "/W7"
    Call Get.Config
    Close
    Goto Write.Guest
 Case "/W8"
    Call Get.Config
    Close
    Goto Write.Start
 Case "/W9"
    Call Get.Config
    Close
    Call Write.Options
    Color 14, 0
    Print "Option"+NodeX+".cfg data file written."
    Goto Terminate
 Case "/W?"
    Goto Display.Command2
 Case "/Y"
    Call Remove.All.Vehicles
    Goto Terminate
 Case "/Z"
    Call Save.Screen(False)
    Call Edit.Password
    Color 7,0
    Cls
    Call Save.Screen(True)
    Goto Terminate
 Case "/!"
    Call Clear.Passwords
    Goto Terminate
 End Select ' parse command line end
 Goto Boot.Error

Write.Reports:
 If Quiet.Mode=False Then
    Color 15,0
    Print "Dndutil v"+Version$+" utility program."
 Endif
 Call Get.Config
 Close
 Call Read.Util.Config ' read filenames from configure file
 If Data.Error Then ' check read filenames error
    ' display error reading message
    If Quiet.Mode=False Then
       Print "Error reading "+Lcase$(FileName)+". Writing configure file."
    Endif
    Call Write.Util.Config ' create new configure file
    Call Read.Util.Config ' re-read filenames from configure file
    If Data.Error Then ' recheck read filenames error
       ' display second error reading filenames error
       If Quiet.Mode=False Then
          Print "Error writing "+Lcase$(FileName)+". Aborting program."
       Endif
       Color 7,0
       End ' terminate program
    Endif ' recheck read error end
 Endif ' check read error end
 Color 14,0
 Call User.List ' routine displays users
 Call Top.Ten ' routine displays top ten users
 Call Team.List ' routine displays team lists
 Call Top.Ten.Teams ' routine displays top ten teams
 Call Last.User ' routine displays last users
 If Quiet.Mode=False Then
    Color 15,0
    Print "Bulletin files created." ' termination message
 Endif
 Goto Terminate

Write.Profile:
 DND.Path=Environ$("DNDBBS")
 If DND.Path<>Nul Then
    If Right$(DND.Path,1)<>"\" Then
       DND.Path=DND.Path+"\"
    Endif
 Endif
 Open DND.Path+"profile.dat" For Output As #1
 Restore Profile.Data
 For Temp=1 to MaxProfiles
    Read Temp1$,Temp1
    Write #1,Temp1$,Temp1
 Next
 Color 14, 0
 Print "Profile data file written."
 Goto Terminate

Profile.Data:
Data "'",-1
Data "ENDIF",-1
Data "END IF",-1
Data "STOP",-1
Data "REM",-1
Data "MID$",-1
Data "LEFT$",-1
Data "RIGHT$",-1
Data "PRINT #",-1
Data "DPRINT",-1
Data "LPRINT",-1
Data "SPRINT",-1
Data "UPRINT",-1
Data "INPUT;",-1
Data "FORIF",-1
Data "FOR",-1
Data "NEXTIF",-1
Data "NEXT",-1
Data "CONTINUE FORIF",-1
Data "CONTINUE FOR",-1
Data "EXIT FORIF",-1
Data "EXIT FOR",-1
Data "DO UNTIL",-1
Data "LOOP WHILE",-1
Data "EXIT DO",-1
Data "CONTINUE DO",-1
Data "GOTO",-1
Data "GOSUB",-1
Data "RETURN",-1
Data "DO WHILE",-1
Data "DO",-1
Data "OFF",-1
Data "IF",-1
Data "ELSEIF",-1
Data "CASEIF ELSE",-1
Data "CASEIF",-1
Data "SELECT CASE",-1
Data "END SELECTIF",-1
Data "BEEP",0
Data "SOUND",0
Data "COLOR",-1
Data "LOCATE",-1
Data "CLS",-1
Data "SCREEN",0
Data "WIDTH",0
Data "WRITE #",-1
Data "LINE INPUT;",-1
Data "LINE INPUT #",-1
Data "INPUT #",-1
Data "WEND",-1
Data "WHILE",-1
Data "CONTINUE WHILE",-1
Data "EXIT WHILE",-1
Data "ELSE",-1
Data "LOOP UNTIL",-1
Data "LOOPIF",-1
Data "END LOOPIF",-1
Data "EXIT LOOPIF",-1
Data "LOOP",-1
Data "RANDOMIZE",-1
Data "POKE",0
Data "INT86",0
Data "DEFSEG",0
Data "ABSOLUTE",0
Data "OUT",0
Data "WAIT",0
Data "SLEEP",0
Data "PAUSE",0
Data "SELECTIF CASE",-1
Data "END SELECT",-1
Data "CASE ELSE",-1
Data "CASE",-1
Data "CONTINUE LOOPIF",-1
Data "END",-1
Data "CLEAR",0
Data "SYSTEM",0
Data "SWAP",-1
Data "ERROR",-1
Data "ON ERROR GOTO",-1
Data "ON ERROR RESUME PREVIOUS",-1
Data "ON ERROR RESUME SAME",-1
Data "ON ERROR RESUME NEXT",-1
Data "ON ERROR STOP",-1
Data "RESUME PREVIOUS",-1
Data "RESUME SAME",-1
Data "RESUME NEXT",-1
Data "RESUME",-1
Data "ON",-1
Data "DATE$",0
Data "TIME$",0
Data "CHDRIVE",0
Data "CD",0
Data "CHDIR",0
Data "MD",0
Data "MKDIR",0
Data "RD",0
Data "RMDIR",0
Data "KILL",0
Data "DELETE",0
Data "RENAME",0
Data "NAME",0
Data "SHELL",0
Data "CHAIN",0
Data "LET",-1
Data "CLOSE #",-1
Data "OPEN #",-1
Data "FIELD #",-1
Data "WRITE",-1
Data "PRINT",-1
Data "INPUT",-1
Data "LINE INPUT",-1
Data "LSET #",-1
Data "RSET #",-1
Data "PUT #",-1
Data "GET #",-1
Data "READ #",-1
Data "DATA",-1
Data "READ",-1
Data "RESTORE",-1
Data "CIRCLE STEP",0
Data "LINE STEP",0
Data "PSET",0
Data "PRESET",0
Data "PAINT",0
Data "DRAW",0
Data "PLAY",0
Data "GET",0
Data "PUT",0
Data "BSAVE",0
Data "BLOAD",0
Data "VIEW SCREEN",0
Data "VIEW",0
Data "WINDOW SCREEN",0
Data "WINDOW",0
Data "CIRCLE",0
Data "LINE",0
Data "CLOSE",0
Data "DEF FN",-1
Data "GRPHSEG",0
Data "GRPHPTR",0
Data "PLAY",0
Data "DIR$",0
Data "CURDIR$",0
Data "DRV",0
Data "TDRV",0
Data "FILESIZE",0
Data "FILEATTR",0
Data "FILEDATE",0
Data "FILETIME",0
Data "VLABEL",0
Data "VSERIAL",0

Write.Modems:
 DND.Path=Environ$("DNDBBS")
 If DND.Path<>Nul Then
    If Right$(DND.Path,1)<>"\" Then
       DND.Path=DND.Path+"\"
    Endif
 Endif
 Open DND.Path+"modems.dat" For Output As #1
 Restore Modem.Data
 For Temp=1 to 30
    Read T1$, T2$, T3$, T4$, T5$, T6$
    Write #1, T1$, T2$, T3$, T4$, T5$, T6$
 Next
 Color 14, 0
 Print "Modem data file written."
 Goto Terminate

Modem.Data:
DATA "Anchor 2400E","ATZ","ATE0M0Q0V1X1&D2S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "Compaq 2400 (Internal)","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "CompuCom 9600 fax/modem","ATZ","AT+F&F2*H2E0M0&D2S0=1","ATQ0X1V1A","ATQ1E1H1M0","ATD"
DATA "Computer Peripherals VIVA 9642e","ATZ","ATE0M0Q0V1X1&K3S0=1Q0S2=255S10=30S95=3","ATA","ATH1M0","ATD"
DATA "Digicom Systems 9624E","ATZ","ATS0=1","ATL0M0Q0X4V1A","ATQ1E1H1M0","ATD"
DATA "Everex 24E","ATZ","AT&D2E0M0Q0V1X1S0=1S2=255S10=30","ATA","AT&D2H1M0","ATD"
DATA "Everex Evercom 24 (EV-941)","ATZ","AT&C1&D2E0M0V1Q0X1S0=1S2=255210=50","ATS0=2","AT&D2&C1H1M0","ATD"
DATA "Fastcomm 9696","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "Fastcomm 9696","ATZ","ATE0M0V1X4S0=1S10=10S69=2","ATA","ATH1M0","ATD"
DATA "Franklin 2400","ATZ","ATE0M0V1Q1X1S0=1S2=255S10=45E0","ATA","ATH1M0","ATD"
DATA "Hayes Compatible Generic 2400","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "Hayes Smartmodem 2400","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "Hayes V-Series 9600","ATZ","ATM0Q1S10=30E0Q0X1W1S0=1Q0X1","ATQ0X1V1A","ATM0Q1E1H1","ATD"
DATA "Lightspeed 2400LE MNP5","ATZ","ATE0M0S0=1S2=255S10=20\J0\N3\Q2\V1","ATA","","ATD"
DATA "Microcom AX9624c","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "MultiTech 224EC","ATZ","ATE0M0S2=255S0=1","ATA","ATH1M0","ATD"
DATA "MultiTech MT932EA","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "Packard Bell 2400 plus","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "Practical Peripherals 2400SA","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "Practical Peripherals 9600SA","ATZ","ATE0M0Q0V1X1W2S0=1S2=255S10=30S95=3","ATA","ATH1M0","ATD"
DATA "Supra 2400","ATZ","ATE0L2M0Q0V1X1S0=1S2=225S10=30","ATA","ATH1M0","ATD"
DATA "USR Courier 2400","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "USR Courier HST 9600","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "USR Direct 2400PC","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "USR Dual Standard HST/V.32","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "Ven-Tel Half Card 24","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "Viva 9642e (V.32/V.42/V.42bis)","ATZ","ATE0M0Q0V1X1&K3S0=1S2=255S10=30S95=3","ATA","ATH1M0","ATD"
DATA "Zenith ZM 2401","ATZ","AT&D2E0Q0V1X1S0=1S2=255S10=30","ATA","AT&D2H1M0","ATD"
DATA "Zenith ZM 2401","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"
DATA "Zipper 2400","ATZ","ATE0M0Q0V1X1S0=1S2=255S10=30","ATA","ATH1M0","ATD"

Write.Template:
 DND.Path=Environ$("DNDBBS")
 If DND.Path<>Nul Then
    If Right$(DND.Path,1)<>"\" Then
       DND.Path=DND.Path+"\"
    Endif
 Endif
 Open DND.Path+"editedit.dat" For Random As #1 Len=80
 Restore Template.Data
 For Temp=1 To 895
    Read Temp1$
    Buffer1=Temp1$
    Put #1,Temp,Buffer1
 Next
 Color 14, 0
 Print "Template data file written."
 Goto Terminate

Template.Data:
DATA ";begin block 0 main edit menu:"
DATA "##0."
DATA "#1.:[A]ction           #10.:[O]bject"
DATA "#2.:[C]ontainer        #11.:[P]layer"
DATA "#3.:[E]mail            #12.[Q]uit"
DATA "#4.:[F]ile editor      #13.:[R]oom"
DATA "#5.:[H]elp             #14.:[S]pell"
DATA "#6.[I]nterpreter      #15.:[T]reasure"
DATA "#7.:[L]ink             #16.:[U]ser"
DATA "#8.:[M]onster          #17.:[1]Utilities"
DATA "#9.:[N]onplayer        #18.:[2]Console"
DATA ";begin block 1 action menu:"
DATA "##1."
DATA ":Action edit menu"
DATA "#19.[A]dd"
DATA "#20.:[C]hange"
DATA "#21.[L]ist"
DATA "#582.[P]rint"
DATA "##20."
DATA ":Triggers:"
DATA "#22.[E]ncounter\attack monster"
DATA "#23.[S]pells"
DATA "#24.[M]onster talk"
DATA "#25.:[L]evel entry"
DATA ":Results:"
DATA "#26.:[H]ealth"
DATA "#27.:[R]ate"
DATA "#28.:[I]nventory"
DATA "#29.[F]umble"
DATA "#30.[T]eleport"
DATA ":Other:"
DATA "#31.:[A]ttribute"
DATA "#32.[C]lear action"
DATA "#33.[W]eapon rusting"
DATA "#34.[O]bject stealing"
DATA "#35.[D]isplay action"
DATA "#36.:[P]urchase item"
DATA "#700.[Z]Wish percent"
DATA "##25."
DATA ":Action level entry"
DATA "#37.[H]ighest level (at least) for entry to room"
DATA "#38.[L]owest level (at most) for entry to room"
DATA "#39.:[D]irection restrictions"
DATA "#53.:[R]oom directions hidden"
DATA "##39."
DATA ":Action room restriction"
DATA "#40.[N]orth"
DATA "#41.[E]ast"
DATA "#42.[S]outh"
DATA "#43.[W]est"
DATA "#44.[NE]northeast"
DATA "#45.[SE]southeast"
DATA "#46.[SW]southwest"
DATA "#47.[NW]northwest"
DATA "#48.[U]p"
DATA "#49.[D]own"
DATA "#50.[I]n"
DATA "#51.[O]ut"
DATA "#52.[G]o portal"
DATA "##53."
DATA ":Action room hidden"
DATA "#54.[N]orth"
DATA "#55.[E]ast"
DATA "#56.[S]outh"
DATA "#57.[W]est"
DATA "#58.[NE]northeast"
DATA "#59.[SE]southeast"
DATA "#60.[SW]southwest"
DATA "#61.[NW]northwest"
DATA "#62.[U]p"
DATA "#63.[D]own"
DATA "#64.[I]n"
DATA "#65.[O]ut"
DATA "##26."
DATA ":Action health rate"
DATA "#66.[F]atigue"
DATA "#67.[V]itality"
DATA "##27."
DATA ":Action room rate"
DATA "#68.[E]ncounter"
DATA "#69.[H]ealth"
DATA "##28."
DATA ":Action inventory"
DATA "#70.[1]armor"
DATA "#71.[2]boots"
DATA "#72.[3]helmets"
DATA "#73.[4]rings"
DATA "#74.[5]shields"
DATA "#75.[6]bracers"
DATA "#76.[7]weapons"
DATA "#77.[8]cloaks"
DATA "#78.[9]rings"
DATA "#79.[10]amulets"
DATA "#80.[X]none"
DATA "##31."
DATA ":Action attribute"
DATA "#81.[A]ir terrain"
DATA "#82.[L]and terrain"
DATA "#83.[W]ater terrain"
DATA "#84.[U]nlit room"
DATA "#85.[X]Clear attributes"
DATA "##36."
DATA "#86.[O]bject"
DATA "#87.[T]reasure"
DATA "#88.[N]one"
DATA ";begin block 2 container menu:"
DATA "##2."
DATA ":Container edit menu"
DATA "#89.:[A]dd"
DATA "#90.:[C]hange"
DATA "#91.[D]elete"
DATA "#92.[L]ist"
DATA "#93.[P]ack"
DATA "#94.[R]epair"
DATA "#95.[S]earch"
DATA "#96.[U]ndelete"
DATA "#583.[V]Print"
DATA "##89."
DATA ":Add container menu"
DATA "#97.[R]oom"
DATA "#98.[U]user"
DATA "##90."
DATA ":Change container menu"
DATA "#99.[A]Container name"
DATA "#100.[B]Short name"
DATA "#101.[C]Container maximum"
DATA "#102.[D]Container locked"
DATA "#103.:[E]Container trapped"
DATA "#104.[F]Container keynumber"
DATA "#105.[G]Container user owner"
DATA "#106.[H]Container room owner"
DATA "#107.[I]Container is invisible"
DATA "#108.[J]Container weight"
DATA "#109.:[K]Container inventory"
DATA "#110.[L]Container prefix"
DATA "#111.[M]Container disappears"
DATA "#112.:[N]Container size"
DATA "#113.[O]Container is permanent"
DATA "#114.[P]Container treasure index"
DATA "#115.[R]Container self implodes"
DATA "#116.[S]Container restricted items"
DATA "#117.[T]ANSI prefix code"
DATA "##103."
DATA ":Container trap menu"
DATA "#118.[C]lear"
DATA "#119.:[H]its"
DATA "#120.:[E]xplodes"
DATA "#121.[P]oison"
DATA "#122.[W]eapon"
DATA "##119."
DATA ":Container health trap menu"
DATA "#123.[F]atigue"
DATA "#124.[V]itality"
DATA "#125.[N]one"
DATA "##120."
DATA ":Container explodes trap menu"
DATA "#126.[F]atigue"
DATA "#127.[V]itality"
DATA "#128.[N]one"
DATA "##112."
DATA ":Container size menu"
DATA "#129.[0]any"
DATA "#130.[1]small"
DATA "#131.[2]medium"
DATA "#132.[3]large"
DATA ":Container material menu"
DATA "#687.[4]paper"
DATA "#688.[5]glass"
DATA "#689.[6]wood"
DATA "#690.[7]metal"
DATA "##109."
DATA ":Container inventory edit"
DATA "#135.:[T]reasure"
DATA "#134.:[O]bjects"
DATA "#133.:[C]ontainers"
DATA "##133."
DATA ":Container container inventory edit"
DATA "#136.[A]dd"
DATA "#137.[D]elete"
DATA "#138.[L]ist"
DATA "##134."
DATA ":Container object inventory edit"
DATA "#139.[A]dd"
DATA "#140.[D]elete"
DATA "#141.[L]ist"
DATA "##135."
DATA ":Container treasure inventory edit"
DATA "#142.[A]dd"
DATA "#143.[D]elete"
DATA "#144.[L]ist"
DATA ";begin block 3 email menu:"
DATA "##3."
DATA ":Email edit menu"
DATA "#145.[A]dd"
DATA "#146.:[C]hange"
DATA "#147.[D]elete"
DATA "#148.:[E]dit"
DATA "#149.[L]ist"
DATA "#150.[P]ack"
DATA "#151.[S]earch"
DATA "#152.[U]ndelete"
DATA "#584.[V]Print"
DATA "##146."
DATA ":Email change base"
DATA "#153.[C]lasstype"
DATA "#154.[F]ilename"
DATA "#155.[T]opic"
DATA "##148."
DATA ":Email edit messages"
DATA "#156.[A]dd"
DATA "#157.:[C]hange"
DATA "#158.[D]elete"
DATA "#159.[L]ist"
DATA "#160.[M]ove"
DATA "#161.[P]ack"
DATA "#162.[S]earch"
DATA "#163.[U]ndelete"
DATA "##157."
DATA ":Email message change"
DATA "#649.:[H]eader"
DATA "#650.[T]ext"
DATA "##649."
DATA ":Email header change"
DATA "#164.[A]Town Mayor"
DATA "#165.[B]Governor"
DATA "#166.[C]Guild Master"
DATA "#167.[D]Sysop"
DATA "#168.[E]None"
DATA ";begin block 7 link menu:"
DATA "##7."
DATA ":Link edit menu"
DATA "#169.[A]dd link"
DATA "#170.[C]heck links"
DATA "#171.[D]elete link"
DATA "#172.[L]ist links"
DATA "#585.[P]rint"
DATA "#173.[S]earch links"
DATA "#174.[X]Square links"
DATA "#175.[Y]Diamond links"
DATA ";begin block 8 monster menu:"
DATA "##8."
DATA ":Monster edit menu"
DATA "#176.[A]dd"
DATA "#177.:[C]hange"
DATA "#178.[L]ist"
DATA "#179.[P]ack"
DATA "#180.[R]epair"
DATA "#181.[S]earch"
DATA "#182.:[M]onclass"
DATA "#183.:[T]alk responses"
DATA "#184.:[U]ser command responses"
DATA "#586.[V]Print"
DATA "##177."
DATA ":Monster change menu"
DATA "#185.[A]Monster name"
DATA "#186.[B]Plural of name"
DATA "#187.[C]Monter level"
DATA "#188.[D]Magical monster"
DATA "#189.[E]Hit points"
DATA "#190.[F]Experience points"
DATA "#191.[G]Number appearing"
DATA "#192.[H]Poisonous monster"
DATA "#193.[I]Level draining monster"
DATA "#194.[J]Monster blocks exits"
DATA "#195.[K]Monster prevents user from taking treasure"
DATA "#196.[L]Monster follows user"
DATA "#197.[M]Monster casts spells"
DATA "#198.[N]Monster jails attacker"
DATA "#199.[O]Monster encounter rate"
DATA "#200.[P]Monster is permanent"
DATA "#201.[R]Monster uses psionics"
DATA "#202.:[S]Monster inventory"
DATA "#203.[T]Monster attack/surprise/saving throw equations"
DATA "#204.[U]Gold points        #206.[W]Weapon/Shield/Armor"
DATA "#205.[V]ANSI prefix codes  #207.:[X]Talk rewards"
DATA "##207."
DATA ":Monster talk rewards menu"
DATA "#208.:[M]onster reward"
DATA "#209.:[E]ncounter reward"
DATA "#210.[N]one"
DATA "##208."
DATA ":Monster reward for talking menu"
DATA "#211.[1]Talk gold"
DATA "#212.[2]Talk gold count"
DATA "#213.[3]Talk gold percent"
DATA "#214.[4]Talk item"
DATA "#215.[5]Talk item count"
DATA "#216.[6]Talk item percent"
DATA "#217.[7]Talk move direction"
DATA "##209."
DATA ":Encounter reward for talking menu"
DATA "#218.[1]Talk gold"
DATA "#219.[2]Talk gold count"
DATA "#220.[3]Talk gold percent"
DATA "#221.[4]Talk item"
DATA "#222.[5]Talk item count"
DATA "#223.[6]Talk item percent"
DATA "#224.[7]Talk move direction"
DATA "##182."
DATA ":Monclass edit menu"
DATA "#225.[A]dd class"
DATA "#226.[C]hange class"
DATA "#227.[L]ist class"
DATA "##183."
DATA ":Monster talk edit"
DATA "#228.[A]dd response"
DATA "#229.[C]hange response"
DATA "#230.[L]ist response"
DATA "#231.[M]onster response"
DATA "##184."
DATA ":Command response edit menu"
DATA "#232.[A]dd response"
DATA "#233.[C]hange response"
DATA "#234.[D]elete response"
DATA "#235.[L]ist response"
DATA "##202."
DATA ":Monster inventory menu"
DATA "#236.:[O]bjects"
DATA "#237.:[T]reasure"
DATA "##236."
DATA ":Monster object inventory"
DATA "#238.[A]dd"
DATA "#239.[D]elete"
DATA "#240.[L]ist"
DATA "##237."
DATA ":Monster treasure inventory"
DATA "#241.[A]dd"
DATA "#242.[D]elete"
DATA "#243.[L]ist"
DATA ";begin block 9 nonplayer menu:"
DATA "##9."
DATA ":Nonplayer edit menu"
DATA "#244.[A]dd"
DATA "#245.:[C]hange"
DATA "#246.[L]ist"
DATA "#247.[P]ack"
DATA "#248.[R]epair"
DATA "#249.[S]earch"
DATA "#250.:[T]alk responses"
DATA "#587.[V]Print"
DATA "##245."
DATA ":Nonplayer change menu"
DATA "#251.[A]Nonplayer name"
DATA "#252.[B]Comma-separated room list"
DATA "#253.[C]Nonplayer level"
DATA "#254.[D]Hit points"
DATA "#255.[E]Experience points"
DATA "#256.[F]Poisonous nonplayer"
DATA "#257.[G]Leveldraining nonplayer"
DATA "#258.[H]Nonplayer blocks exits"
DATA "#259.[I]Nonplayer prevents taking treasure"
DATA "#260.[J]Nonplayer follows user"
DATA "#261.[K]Nonplayer casts spells"
DATA "#262.[L]Nonplayer jails attacker"
DATA "#264.[M]Nonplayer encounter rate"
DATA "#265.[N]Nonplayer uses psionics"
DATA "#266.:[O]Nonplayer inventory"
DATA "#267.[P]Nonplayer attack/surprise/saving throw equations"
DATA "#268.[R]ANSI prefix codes"
DATA "#269.[S]Weapon/armor/shield"
DATA "##266."
DATA ":Nonplayer inventory menu"
DATA "#270.:[O]bjects"
DATA "#271.:[T]reasure"
DATA "##270."
DATA ":Nonplayer object inventory"
DATA "#272.[A]dd"
DATA "#273.[D]elete"
DATA "#274.[L]ist"
DATA "##271."
DATA ":Nonplayer treasure inventory"
DATA "#275.[A]dd"
DATA "#276.[D]elete"
DATA "#277.[L]ist"
DATA "##250."
DATA ":Nonplayer talk edit"
DATA "#278.[A]dd response"
DATA "#279.[C]hange response"
DATA "#280.[L]ist response"
DATA "#281.[N]onplayer response"
DATA ";begin block 10 object menu:"
DATA "##10."
DATA ":Object edit menu"
DATA "#282.[A]dd"
DATA "#283.:[C]hange"
DATA "#284.[L]ist"
DATA "#588.[P]rint"
DATA "#577.[S]earch"
DATA "##283."
DATA ":Object change menu"
DATA "#285.[A]Object name"
DATA "#286.[B]Object identifier"
DATA "#287.[C]Room link numbers"
DATA "#288.:[D]Trapped portal"
DATA "#289.[E]Long description"
DATA "#290.[F]Entry description"
DATA "#291.[G]Hidden objects"
DATA "#292.[H]Invisible object"
DATA "#293.[I]Jail attacker trap"
DATA "#294.[J]Locked portal"
DATA "#295.[K]Relocking portal"
DATA "#296.[L]Key number"
DATA "#297.[M]Permanent object"
DATA "#298.[N]Object is a light"
DATA "#299.:[O]Object is a switch"
DATA "#300.[P]ANSI prefix codes"
DATA "#301.[R]Portal password"
DATA "#302.[S]Multiline description"
DATA "##288."
DATA ":Trapped portal"
DATA "#303.[B]lade barrier"
DATA "#304.[C]rushing ceiling"
DATA "#305.:[D]eadly spears"
DATA "#306.[F]alling door"
DATA "#307.[P]oison needles"
DATA "##305."
DATA ":Trapped portal hits"
DATA "#308.[F]atigue"
DATA "#309.[V]itality"
DATA "#310.[N]one"
DATA "##299."
DATA ":Object switch menu"
DATA "#311.[E]nters room number"
DATA "#312.[L]ights room"
DATA "#313.[U]nlocks object"
DATA ";begin block 11 player menu:"
DATA "##11."
DATA ":Player teams edit menu"
DATA "#314.[A]dd"
DATA "#315.:[C]hange"
DATA "#316.[D]elete"
DATA "#317.[L]ist"
DATA "#318.[P]ack"
DATA "#319.[R]epair"
DATA "#320.[S]earch"
DATA "#321.[T]op ten"
DATA "#322.[U]ndelete"
DATA "#589.[V]Print"
DATA "##315."
DATA ":Player teams change menu"
DATA "#323.[A]Team name"
DATA "#324.[B]Team password"
DATA "#325.[C]Team gold"
DATA "#326.:[D]Team inventory"
DATA "#327.[1]Team member1"
DATA "#328.[2]Team member2"
DATA "#329.[3]Team member3"
DATA "#330.[4]Team member4"
DATA "#331.[5]Team member5"
DATA "##326."
DATA ":Team player inventory edit"
DATA "#332.:[T]reasure"
DATA "#333.:[O]bjects"
DATA "#334.:[C]ontainers"
DATA "##332."
DATA ":Team player treasure inventory edit"
DATA "#335.[A]dd"
DATA "#336.[D]elete"
DATA "#337.[L]ist"
DATA "##333."
DATA ":Team player object inventory edit"
DATA "#338.[A]dd"
DATA "#339.[D]elete"
DATA "#340.[L]ist"
DATA "##334."
DATA ":Team player container inventory edit"
DATA "#341.[A]dd"
DATA "#342.[D]elete"
DATA "#343.[L]ist"
DATA ";begin block 13 room menu:"
DATA "##13."
DATA ":Room edit menu"
DATA "#344.[A]dd"
DATA "#345.:[C]hange"
DATA "#346.[L]ist"
DATA "#347.[P]ack"
DATA "#348.[R]epair"
DATA "#349.[S]earch"
DATA "#590.[V]Print"
DATA "##345."
DATA ":Room change menu"
DATA "#350.[A]ction"
DATA "#351.:[C]ontainers"
DATA "#352.:[D]escription"
DATA "#353.[M]onster class"
DATA "#354.:[O]bject edit"
DATA "#355.:[T]reasure edit"
DATA "##351."
DATA ":Room container edit"
DATA "#356.[A]dd"
DATA "#357.[D]elete"
DATA "#358.[L]ist"
DATA "##352."
DATA ":Room description edit"
DATA "#359.[D]isplay description"
DATA "#360.[L]ong description"
DATA "#361.[S]hort description"
DATA "##354."
DATA ":Room object edit"
DATA "#362.[A]dd"
DATA "#363.[D]elete"
DATA "#364.[L]ist"
DATA "##355."
DATA ":Room treasure edit"
DATA "#365.[A]dd"
DATA "#366.[D]elete"
DATA "#367.[L]ist"
DATA ";begin block 14 spell menu:"
DATA "##14."
DATA ":Spell edit menu"
DATA "#368.[A]dd"
DATA "#369.:[C]hange"
DATA "#370.[L]ist"
DATA "#591.[P]rint"
DATA "#578.[S]earch"
DATA "##369."
DATA ":Spell change edit"
DATA "#371.[A]Spell name"
DATA "#372.[B]Spell chant"
DATA "#373.[C]Spell cast description"
DATA "#374.[D]Spell level"
DATA "#375.:[E]Spell types"
DATA "#376.:[F]Spell is psionic"
DATA "#377.:[G]Spell classes"
DATA "#378.[H]Spell ingredients"
DATA "#379.:[I]Spell casting type"
DATA "#380.[J]Spell offense equation"
DATA "#381.[K]ANSI prefix codes"
DATA "##375."
DATA ":Spell type"
DATA "#382.[A]Enchant      #399.[S]Detect Trap"
DATA "#383.[B]Offense      #400.[T]Intoxicate"
DATA "#384.[C]Bless        #401.[U]Set Trap"
DATA "#385.[D]Wish         #402.[V]Hide"
DATA "#386.[E]Poison       #403.[W]Search"
DATA "#387.[F]Vigor        #404.[X]Invisibility"
DATA "#388.[G]Heal         #405.[Y]Identify"
DATA "#389.[H]Curepoison   #406.[Z]Enlighten"
DATA "#390.[I]Level Drain  #407.[1]Illuminate"
DATA "#391.:[J]Teleport     #408.[2]Psyche"
DATA "#392.[K]Befuddle     #409.[3]Telepathy"
DATA "#393.[L]Turn Undead  #684.[4]Curse"
DATA "#394.[M]Pass Door    #685.[5]Damnate"
DATA "#395.[N]Conjure      #695.[6]Resurrect"
DATA "#396.[O]Psionic      #696.[7]Grant Wish"
DATA "#397.[P]Detect Lock  #699.[8]Steal Wish"
DATA "#398.[R]Detect Evil"
DATA "##391."
DATA ":Spell teleport option"
DATA "#410.[R]oom prompt for teleport"
DATA "#411.[T]eleport to room number"
DATA "##376."
DATA ":Spell psionic option"
DATA "#412.[A]ttack mode"
DATA "#413.[D]efense mode"
DATA "##377."
DATA ":Character class options"
DATA "#414.[A]Fighter"
DATA "#415.[B]Magic User"
DATA "#416.[C]Thief"
DATA "#417.[D]Cleric"
DATA "#418.[E]Paladin"
DATA "#419.[F]Ranger"
DATA "#420.[G]Druid"
DATA "#421.[H]Lady"
DATA "#422.[N]one"
DATA "#423.[X]All"
DATA "##379."
DATA ":Spell casting type for ingredients"
DATA "#424.[A]use command"
DATA "#425.[B]read scroll"
DATA "#426.[C]cast spell"
DATA "#427.[N]one"
DATA "#428.[X]All"
DATA ";begin block 15 treasure menu:"
DATA "##15."
DATA ":Treasure edit menu"
DATA "#429.[A]dd"
DATA "#430.:[C]hange"
DATA "#431.[L]ist"
DATA "#592.[P]rint"
DATA "#432.[S]earch"
DATA "##430."
DATA ":Treasure edit menu"
DATA "#433.[A]Treasure name             #452.[U]Treasure is coins"
DATA "#434.[B]Treasure identifier       #453.[V]Treasure is potion"
DATA "#435.[C]Weight                    #454.[W]Treasure is scroll"
DATA "#436.[D]Gold coin value           #455.[X]Treasure is invisible"
DATA "#437.:[E]Treasure type             #456.[Y]Treasure key number"
DATA "#438.[F]Hit plus                  #457.[Z]Treasure is fuel"
DATA "#439.[G]Charges                   #458.[1]Weapon rusts"
DATA "#440.:[H]Weapon class              #459.[2]Weapon is stealable"
DATA "#441.[I]Treasure is permanent     #460.[3]Container disappears"
DATA "#442.[J]Treasure is edible        #461.[4]Container is locked"
DATA "#443.[K]Treasure is magical       #638.:[5]Container is trapped"
DATA "#444.:[L]Ring protection           #462.[6]Container maximum"
DATA "#445.[M]Treasure is a light       #463.[7]Container prefix"
DATA "#446.:[N]Treasure is a vehicle     #464.:[8]Container size"
DATA "#447.[O]Loads from device         #637.[9]Container restrictions"
DATA "#448.[P]Is loadable device        #465.[10]ANSI prefix code"
DATA "#449.[R]Launchs from device       #686.[11]Persistent"
DATA "#450.[S]Is launchable device"
DATA "#451.[T]Launchable device can be moved"
DATA "##437."
DATA ":Treasure type"
DATA "#466.[1]Container"
DATA "#467.[A]rmor"
DATA "#468.[B]oots"
DATA "#469.[C]loak"
DATA "#470.[H]elmet"
DATA "#471.[R]ing"
DATA "#472.[S]hield"
DATA "#473.[T]Bracers"
DATA "#474.[U]Amulet"
DATA "#475.[W]eapon"
DATA "#476.[X]None"
DATA "##440."
DATA ":Weapon class type"
DATA "#477.[B]lunt"
DATA "#478.[P]ole"
DATA "#479.[S]harp"
DATA "#480.[T]hrusting"
DATA "#481.[N]one"
DATA "##444."
DATA ":Ring type protection against"
DATA "#482.[A]ny spell"
DATA "#483.[F]umbling"
DATA "#484.[I]nventory damage"
DATA "#485.[L]evel drain"
DATA "#486.[O]ffense/invisible spell"
DATA "#487.[P]oison"
DATA "#488.[R]usting"
DATA "#489.[S]tealing"
DATA "#490.[T]eleporting"
DATA "#491.[X]All traps"
DATA "##446."
DATA ":Vehicle terrain type"
DATA "#492.[A]ir terrain"
DATA "#493.[L]and terrain"
DATA "#494.[W]ater terrain"
DATA "#495.[X]all terrain"
DATA "##464."
DATA ":Container size menu"
DATA "#597.[0]any"
DATA "#598.[1]small"
DATA "#599.[2]medium"
DATA "#600.[3]large"
DATA ":Container material menu"
DATA "#691.[4]paper"
DATA "#692.[5]glass"
DATA "#693.[6]wood"
DATA "#694.[7]metal"
DATA ";begin block 16 user menu:"
DATA "##16."
DATA ":User edit menu"
DATA "#496.[A]dd"
DATA "#497.:[C]hange"
DATA "#498.[D]elete"
DATA "#579.[F]ind"
DATA "#499.:[L]ist"
DATA "#500.[P]ack"
DATA "#501.[R]epair"
DATA "#502.[S]ort"
DATA "#503.[U]pdate"
DATA "#701.:[V]ehicles"
DATA "#593.[X]Sysnews"
DATA "#594.[Y]Print"
DATA "##497."
DATA ":User change menu"
DATA "#505.[A]Codename"
DATA "#506.[B]Password"
DATA "#507.:[C]Level"
DATA "#508.:[D]Classtype"
DATA "#509.:[E]Weapon proficiency"
DATA "#510.[F]Blunt weapon"
DATA "#511.[G]Pole weapon"
DATA "#512.[H]Sharp weapon"
DATA "#513.[I]Thrusting weapon"
DATA "#514.[J]Classname"
DATA "#515.:[K]Statistics"
DATA "#516.[L]Experience"
DATA "#517.[M]Gold"
DATA "#518.[N]Room number"
DATA "#519.[O]Time limits"
DATA "#520.:[P]Inventory"
DATA "#521.:[R]Special characters"
DATA "#522.[S]ANSI prefix codes"
DATA "#697.[T]Objects created"
DATA "#698.[W]ishs left"
DATA "#659.:[Z]Nodelist edit"
DATA "#663.:[!]User command list"
DATA "##507."
DATA ":Race options"
DATA "#523.[A]Human"
DATA "#524.[B]Elf"
DATA "#525.[C]Gnome"
DATA "#526.[D]Dwarf"
DATA "#527.[E]Halfling"
DATA "#528.[F]Half-elf"
DATA "#529.[G]Half-orc"
DATA "#530.[H]Ogre"
DATA "##508."
DATA ":Classtype"
DATA "#531.[A]Fighter"
DATA "#532.[B]Magic User"
DATA "#533.[C]Thief"
DATA "#534.[D]Cleric"
DATA "#535.[E]Paladin"
DATA "#536.[F]Ranger"
DATA "#537.[G]Druid"
DATA "#538.[H]Lady"
DATA "#595.[I]Assistant DM"
DATA "#596.[J]Dungeon Master"
DATA "##509."
DATA ":Weapon proficiency"
DATA "#539.[A]blunt"
DATA "#540.[B]pole"
DATA "#541.[C]sharp"
DATA "#542.[D]thrusting"
DATA "##520."
DATA ":Inventory"
DATA "#544.:[T]reasure"
DATA "#543.:[O]bjects"
DATA "#545.:[C]ontainers"
DATA "##543."
DATA ":Object inventory"
DATA "#546.[A]dd"
DATA "#547.[D]elete"
DATA "#548.[L]ist"
DATA "##544."
DATA ":Treasure inventory"
DATA "#549.[A]dd"
DATA "#550.[D]elete"
DATA "#551.[L]ist"
DATA "##545."
DATA ":Container inventory"
DATA "#552.[A]dd"
DATA "#553.[D]elete"
DATA "#580.[L]ist"
DATA "#581.[Z]ap"
DATA "##521."
DATA ":Special characters edit"
DATA "#546.[A]Town Mayor"
DATA "#547.[B]Governor"
DATA "#548.[C]Guild Master"
DATA "#549.[D]Sysop"
DATA "#550.[E]None"
DATA "##499."
DATA ":List user menu"
DATA "#551.[D]ayfile"
DATA "#552.[L]ist"
DATA "#553.[T]op ten"
DATA "#554.[U]sers"
DATA "##515."
DATA ":Statistics menu"
DATA "#601.[A]Strength"
DATA "#651.[B]Intelligence"
DATA "#652.[C]Wisdom"
DATA "#653.[D]Dexterity"
DATA "#654.[E]Constitution"
DATA "#655.[F]Piety"
DATA "#656.[G]Charisma"
DATA "#657.[H]Beauty"
DATA "#658.[I]Glamour"
DATA ";begin block 17 utilities menu:"
DATA "##17."
DATA ":Utilities edit menu"
DATA "#555.[H]otkeys"
DATA "#556.:[P]references"
DATA "#557.[S]hort menus"
DATA "##556."
DATA ":Utilities preferences edit menu"
DATA "#558.[1]Ansi"
DATA "#559.[2]Avatar"
DATA "#560.[3]Echo"
DATA "#561.[4]Linefeeds"
DATA "#562.[5]Linelength"
DATA "#563.[6]Pagelength"
DATA "#564.[7]Wordwrap"
DATA ";begin block 18 utilities menu:"
DATA "##18."
DATA ":Console edit menu"
DATA "#565.[P]ack files"
DATA "#566.[R]un utility program"
DATA "#567.:[S]ic edit"
DATA "#568.:[U]sage counter"
DATA "#569.[V]iew text file"
DATA "#570.[1]Dos shell"
DATA "#571.[2]Dos command"
DATA "#572.:[3]Spawn process"
DATA "##568."
DATA ":Usage editor menu"
DATA "#625.[1]Display runs\pack counters"
DATA "#626.[2]Increase runs counter"
DATA "#627.[3]Reset runs date\time"
DATA "#628.[4]Clear runs counter"
DATA "#629.[5]Reset pack date\time"
DATA "#630.[6]Clear pack counter"
DATA "#631.[7]Print runs counter"
DATA "##572."
DATA "#573.[1]Calc"
DATA "#574.[2]Notepad"
DATA "#575.[3]Other file"
DATA "#576.[P]Path for file"
DATA "##5."
DATA ":Help edit menu"
DATA "#602.[C]ontents"
DATA "#603.[P]rint"
DATA "#604.[S]earch"
DATA "#605.[T]opic"
DATA "##567."
DATA ":SIC edit menu"
DATA "#606.[A]dd"
DATA "#607.:[C]hange"
DATA "#608.[L]ist"
DATA "#619.[P]rint"
DATA "#609.[S]ort"
DATA "##607."
DATA ":SIC change menu"
DATA "#610.[A]Account name"
DATA "#611.[B]Password"
DATA "#612.[C]Group name"
DATA "#613.[D]Library name"
DATA "#614.[E]Maximum records"
DATA "#615.[F]Monthly time limit"
DATA "#616.[G]Printer select"
DATA "#617.[H]Maximum program run time"
DATA "#618.:[I]Command restrictions"
DATA "##618."
DATA ":SIC command restriction menu"
DATA "#620.[C]hange"
DATA "#621.[D]isable all"
DATA "#622.[E]nable all"
DATA "#623.[L]ist"
DATA "#624.[R]eset defaults"
DATA "##4."
DATA ":File editor menu"
DATA "#632.[1]Catalog"
DATA "#633.[2]Logoff"
DATA "#634.[3]Notice"
DATA "#635.[4]Prelog"
DATA "#636.[5]Welcome"
DATA "#648.[6]Other"
DATA "##638."
DATA ":Container traptype"
DATA "#639.[P]oison"
DATA "#640.:[H]its"
DATA "#641.:[E]xplodes"
DATA "#642.[W]eapon"
DATA "#643.[N]one"
DATA "##640."
DATA ":Container trap hits"
DATA "#644.[F]atigue"
DATA "#645.[V]itality"
DATA "##641."
DATA ":Container trap hits"
DATA "#646.[F]atigue"
DATA "#647.[V]itality"
DATA "##659."
DATA ":Nodelist edit"
DATA "#660.[A]ctivate"
DATA "#661.[D]eactivate"
DATA "#662.[L]ist nodes"
DATA "##663."
DATA ":User command list"
DATA "#664.[A]dd record"
DATA "#665.:[C]hange record"
DATA "#666.[L]ist record"
DATA "#667.[U]ser apply"
DATA "##665."
DATA ":User command list change"
DATA "#668.[A]ctivate command"
DATA "#669.[D]eactivate command"
DATA "#670.[L]evel restriction"
DATA "#671.:[R]estrict classtype"
DATA "#672.[T]ime restriction"
DATA "##671."
DATA ":User command list classtype"
DATA "#673.[A]Fighter"
DATA "#674.[B]Magic User"
DATA "#675.[C]Thief"
DATA "#676.[D]Cleric"
DATA "#677.[E]Paladin"
DATA "#678.[F]Ranger"
DATA "#679.[G]Druid"
DATA "#680.[H]Lady"
DATA "#681.[I]Assistant DM"
DATA "#682.[J]Dungeon Master"
DATA "#683.[X]None"
DATA "##701."
DATA ":User vehicles edit"
DATA "#702.[C]hange vehicle"
DATA "#703.[D]elete vehicle"
DATA "#704.[L]ist vehicle"
DATA ";end of blocks"

Write.Ports:
 DND.Path=Environ$("DNDBBS")
 If DND.Path<>Nul Then
    If Right$(DND.Path,1)<>"\" Then
       DND.Path=DND.Path+"\"
    Endif
 Endif
 Open DND.Path+"ports.dat" For Output As #1
 Restore Ports.Data
 For Temp=1 to 147
    Read Temp1$
    Print #1,Temp1$
 Next
 Color 14, 0
 Print "Ports data file written."
 Goto Terminate

Ports.Data:

Data ";Ports.dat filelist for Dndcnfg2.exe for Dndbbs Version v5.0a r1.0:"
Data ";"
Data ";List for nodes 0-99,A-Z:"
Data ";"
Data ";Line is Node,Com??,Port Address(Hex)"
Data ";"
Data "0,0,0"
Data "1,1,3F8"
Data "2,2,2F8"
Data "3,3,3E8"
Data "4,4,2E8"
Data "5,5,3E0"
Data "6,6,2E0"
Data "7,7,338"
Data "8,8,238"
Data ""
Data "9,1,3F8"
Data "10,2,2F8"
Data "11,3,3E8"
Data "12,4,2E8"
Data "13,5,3E0"
Data "14,6,2E0"
Data "15,7,338"
Data "16,8,238"
Data ""
Data "17,1,3F8"
Data "18,2,2F8"
Data "19,3,3E8"
Data "20,4,2E8"
Data "21,5,3E0"
Data "22,6,2E0"
Data "23,7,338"
Data "24,8,238"
Data ""
Data "25,1,3F8"
Data "26,2,2F8"
Data "27,3,3E8"
Data "28,4,2E8"
Data "29,5,3E0"
Data "30,6,2E0"
Data "31,7,338"
Data "32,8,238"
Data ""
Data "33,1,3F8"
Data "34,2,2F8"
Data "35,3,3E8"
Data "36,4,2E8"
Data "37,5,3E0"
Data "38,6,2E0"
Data "39,7,338"
Data "40,8,238"
Data ""
Data "41,1,3F8"
Data "42,2,2F8"
Data "43,3,3E8"
Data "44,4,2E8"
Data "45,5,3E0"
Data "46,6,2E0"
Data "47,7,338"
Data "48,8,238"
Data ""
Data "49,1,3F8"
Data "50,2,2F8"
Data "51,3,3E8"
Data "52,4,2E8"
Data "53,5,3E0"
Data "54,6,2E0"
Data "55,7,338"
Data "56,8,238"
Data ""
Data "57,1,3F8"
Data "58,2,2F8"
Data "59,3,3E8"
Data "60,4,2E8"
Data "61,5,3E0"
Data "62,6,2E0"
Data "63,7,338"
Data "64,8,238"
Data ""
Data "65,1,3F8"
Data "66,2,2F8"
Data "67,3,3E8"
Data "68,4,2E8"
Data "69,5,3E0"
Data "70,6,2E0"
Data "71,7,338"
Data "72,8,238"
Data ""
Data "73,1,3F8"
Data "74,2,2F8"
Data "75,3,3E8"
Data "76,4,2E8"
Data "77,5,3E0"
Data "78,6,2E0"
Data "79,7,338"
Data "80,8,238"
Data ""
Data "81,1,3F8"
Data "82,2,2F8"
Data "83,3,3E8"
Data "84,4,2E8"
Data "85,5,3E0"
Data "86,6,2E0"
Data "87,7,338"
Data "88,8,238"
Data ""
Data "89,1,3F8"
Data "90,2,2F8"
Data "91,3,3E8"
Data "92,4,2E8"
Data "93,5,3E0"
Data "94,6,2E0"
Data "95,7,338"
Data "96,8,238"
Data ""
Data "97,0,0"
Data "98,0,0"
Data "99,0,0"
Data ""
Data "A,0,0"
Data "B,0,0"
Data "C,0,0"
Data "D,0,0"
Data "E,0,0"
Data "F,0,0"
Data "G,0,0"
Data "H,0,0"
Data "I,0,0"
Data "J,0,0"
Data "K,0,0"
Data "L,0,0"
Data "M,0,0"
Data "N,0,0"
Data "O,0,0"
Data "P,0,0"
Data "Q,0,0"
Data "R,0,0"
Data "S,0,0"
Data "T,0,0"
Data "U,0,0"
Data "V,0,0"
Data "W,0,0"
Data "X,0,0"
Data "Y,0,0"
Data "Z,0,0"
Data ""
Data ";-end-"

Write.Copyright:
 DND.Path=Environ$("DNDBBS")
 If DND.Path<>Nul Then
    If Right$(DND.Path,1)<>"\" Then
       DND.Path=DND.Path+"\"
    Endif
 Endif
 Open DND.Path+"copyrite.dat" For Output As #1
 Restore Copyright.Data
 For Temp=1 to 12
    Read Temp1$
    Write #1,Temp1$
 Next
 Color 14, 0
 Print "Copyright data file written."
 Goto Terminate

Copyright.Data:

Data "Dungeons  And  Dragons  Bulletin  Board  System  Adventure  Game"
Data ""
Data ""
Data "Erik Jon Oredson"
Data "1615 S. 4th St. #M1605"
Data "Mpls. MN. 55454-1174"
Data "Alt-D Disk Log"
Data "Alt-P Print Log"
Data "Alt-W Toggle Display"
Data "Alt-X Dndpack"
Data "Alt-Y Screen Saver"
Data "Alt-Z Debug Mode"

Write.Guest:
 DND.Path=Environ$("DNDBBS")
 If DND.Path<>Nul Then
    If Right$(DND.Path,1)<>"\" Then
       DND.Path=DND.Path+"\"
    Endif
 Endif
 Open Data.Path1+"guest.cfg" For Output As #1
 Restore Guest.Data
 For Temp=1 to 71
    Read Temp1$
    Print #1,Temp1$
 Next
 Color 14, 0
 Print "Guest data file written."
 Goto Terminate

Guest.Data:
Data ";guest config file."
Data ""
Data ";open system settings."
Data "OpenSystem=-1"
Data ";OpenSystemTime1=00:00-04:00"
Data ";OpenSystemTime2=18:00-19:00"
Data ""
Data ";guest active setting."
Data "GuestAllowed=-1"
Data "GuestStartsAtLevel=1"
Data "GuestStartsInRoom=652"
Data "GuestTimeLeft=10"
Data "GuestClass=2"
Data "GuestRace=1"
Data "GuestAlign1=1"
Data "GuestAlign2=1"
Data "GuestProf=1"
Data "GuestStats=18,12,12,18,12,12,12,18,18"
Data "GuestCanTeleport=0"
Data "GuestRoomExit=661,717"
Data "GuestRoomResurrect=655"
Data "GuestAllowMail=0"
Data "GuestAllowCasino=0"
Data "GuestAllowBank=0"
Data "GuestAllowTeam=0"
Data ""
Data ";lines 1-127"
Data "GuestLine#1=612-xxx-xxxx"
Data "GuestLine#2=612-xxx-xxxx"
Data "GuestLine#3=612-xxx-xxxx"
Data "GuestLine#4=612-xxx-xxxx"
Data "GuestLine#5=612-xxx-xxxx"
Data "GuestLine#6=612-xxx-xxxx"
Data "GuestLine#7=612-xxx-xxxx"
Data "GuestLine#8=612-xxx-xxxx"
Data ""
Data "GuestLine#17=612-xxx-xxxx"
Data "GuestLine#64=612-xxx-xxxx"
Data ""
Data ";password settings."
Data ";DualPassword=-1"
Data ";PasswordQuestion=-1"
Data ";NoPasswordRequired=-1"
Data ""
Data ";newuser override settings."
Data ";AllowDMLogon=-1"
Data ";AllowLocalSysopLogon=-1"
Data ";AllowLocalMaxLogon=-1"
Data ""
Data ";system variables."
Data "SysopName=Sysop name here"
Data "SysopEmail=sysop@gmail.com"
Data "BBSname=Name of your bbs here"
Data "BBSdesc=Description of your bbs here"
Data ""
Data ";class type time restrictions"
Data ";SysopTime=00:01-23:59"
Data ";DMtime=00:01-23:59"
Data ";AsstDMtime=00:01-23:59"
Data ""
Data ";user time slots"
Data ";User=The_Bard,18:00-23:59"
Data ";User=The_Druid,18:00-23:59"
Data ""
Data ";sysop page time slots"
Data ";PageTimes=21:00-22:00,22:00-23:00,23:00-23:59"
Data ""
Data ";node bit mask override"
Data ";                                                1         2         3         4         5         6         7         8         9        9"
Data ";           -0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
Data "NodeBitMask=1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"

Write.Start:
 DND.Path=Environ$("DNDBBS")
 If DND.Path<>Nul Then
    If Right$(DND.Path,1)<>"\" Then
       DND.Path=DND.Path+"\"
    Endif
 Endif
 Open Data.Path1+"start.cfg" For Output As #1
 Restore Start.Data
 For Temp=1 to 21
    Read Temp1$
    Print #1,Temp1$
 Next
 Color 14, 0
 Print "Start data file written."
 Goto Terminate

Start.Data:
Data ";Sample startup file for dndbbs"
Data ""
Data "BypassSwitch=-1"
Data "BypassAuthor=-1"
Data ";DebugPack=0"
Data "LowMemUse=-1"
Data ";DisableShare=0"
Data ";ForceShare=-1"
Data "DebugMode=-1"
Data ";LoadWindows=-1"
Data "DisableBreak=0"
Data ";CheckMachineName=0"
Data "EquateDebug=-1"
Data ";DisableString=-1"
Data ""
Data ";Display settings"
Data ""
Data ";DisableANSI=0"
Data ";ForceANSI=-1"
Data ";DisableAvatar=0"
Data ";ForceAvatar=-1"

Boot.Error:
 ' display command line error
 Color 14, 0
 Print "Command line error. Type Dndutil /? Or Dndutil /W? for help."
 Goto Error.Resume0

Display.Command:
 ' display utility command line usage
 Color 14,0
 Print "Dndutil v"+Version$+" r"+Release$+" utility program."
 Color 15,0
 Print "Usage: DNDUTIL [/ACDELNPQRSTWXYZ]"
 Color 15,0
 Print "Where:"
 Color 14,0
 Print "   /A      realtime automaton mode."
 Print "   /C<:##> clears message file node(s)."
 Print "   /D      debug mode."
 Print "   /E      edit automaton file."
 Print "   /L      makes report files."
 Print "   /N:##   overrides node to 0-9,A-Z,10-99."
 Print "   /P      makes & prints reports."
 Print "   /P:#    makes & prints reports overrides with lpt#."
 Print "   /Q      quiet mode."
 Print "   /R      realtime message monitor."
 Print "   /S<:##> shutdown node(s)."
 Print "   /T<:##> terminate node(s)."
 Print "   /W<1-9> write data files. /W? write usage."
 Print "   /X:s    send system notice message."
 Print "   /Y      remove all vehicle owners."
 Print "   /Z      edit executable passwords."
 Print "     <:##> is optional, # is a number, s is a string."
 Goto Terminate

Display.Command2:
 ' display utility command line usage
 Color 14,0
 Print "Dndutil v"+Version$+" r"+Release$+" utility program."
 Color 15,0
 Print "Usage:"
 Color 14,0
 Print "   DNDUTIL [/W?]"
 Color 15,0
 Print "Where:"
 Color 14,0
 Print "   /W1  write profile data file."
 Print "   /W2  write modem data file."
 Print "   /W3  write template data file."
 Print "   /W4  write ports data file."
 Print "   /W5  write copyright data file."
 Print "   /W6  write dndutil data file."
 Print "   /W7  write guest.cfg file."
 Print "   /W8  write start.cfg file."
 Print "   /W9  write option.cfg file."
Terminate:
 If Debug.Mode>=False Then
    Color 14,0
    Print "Dndutil terminated normally."
 Endif
Error.Resume0:
 Color 7,0
 If Debug.Mode>=False Then
    Print "Returning to system."
 Endif
 End
Error.Trap0:
 Print "Error";Err
 Resume Error.Resume0

Get.Numeric:
 VarQ$=Nul
 Do
    TempX$=Left$(Command.Line$,1)
    If TempX$>="0" And TempX$<="9" Then
       VarQ$=VarQ$+TempX$
       Command.Line$=Mid$(Command.Line$,2)
    Else
       Exit Do
    Endif
 Loop
 Return

Sub Read.Util.Config
 On Local Error Goto Config.Error
 Data.Error=False
 DND.Path=Environ$("DNDBBS")
 If DND.Path<>Nul Then
    If Right$(DND.Path,1)<>"\" Then
       DND.Path=DND.Path+"\"
    Endif
 Endif
' Redim Race(1 To 8) As String*8
' Restore Config.Array.Data
' For Temp=1 To 8
'    Read Race(Temp)
' Next
 Close
 FileName=DND.Path+"dndutil.cfg"
 Open FileName For Input Shared As #1
 Var=0
 Do Until Eof(1)
    Line Input #1,Var$
    Var$=Rtrim$(Var$)
    Var$=Ltrim$(Var$)
    If Len(Var$) Then
       If Left$(Var$,1)=";" Or Left$(Var$,1)="'" Then
          Eat$=""
       Else
          Var=Var+1
          Temp.ArrayS(Var)=Var$
          If Var=13 Then
             Exit Do
          Endif
       Endif
    Endif
 Loop
 If Var<>13 Then
    Error 53
 Endif
Config.Resume:
 Exit Sub
Config.Error:
 Data.Error=Err
 Resume Config.Resume
End Sub

Sub Write.Util.Config
 On Local Error Goto Error.Trap1
 Close
 Restore Config.Data
 Var$=DND.Path
 If Var$=Nul Then
    Var$=Curdir$
 Endif
 If Right$(Var$,1)<>"\" Then
    Var$=Var$+"\"
 Endif
 FileName=DND.Path+"dndutil.cfg"
 Open FileName For Output Shared As #1
 For Temp=1 To 23
    Read Temp2$
    Select Case Temp
    Case 3,5,22 ' \dnddat
       Temp2$=Data.Path1+Temp2$
    Case Else
       If Left$(Temp2$,1)<>"'" Then
          Temp2$=Var$+Temp2$
       Endif
    End Select
    Print #1,Lcase$(Temp2$)
 Next
Error.Resume1:
 Exit Sub
Error.Trap1:
 Resume Error.Resume1
End Sub

Sub Remove.All.Vehicles
 On Local Error Goto Vehicle.Error
 Call Get.Config
 Close
 Call Open.RoomInv.File
 Call Open.Treasure.File
 Color 15,0
 Print "Removing owners and passengers for all vehicles.."
 TempZ!=SFalse
 For TempX!=1! To Lof(RoomInvFile)/MessWorkRecLen6
    Call Read.Record(RoomInvFile,TempX!)
    VarZ!=MessWorkRecord6.Index
    If VarZ!>SFalse And VarZ!<=Lof(TreasureFile)/TreasureRecLen Then
       Call Read.Record(TreasureFile,VarZ!)
       If TreasureRecord.Vehicle Then
          If MessWorkRecord6.User>SFalse Then
             MessWorkRecord6.User=SFalse
             For Temp=1 To 4
                MessWorkRecord6.Temp(Temp)=False
             Next
             Call Write.Record(RoomInvFile,TempX!)
             TempZ!=TempZ!+1!
          Endif
       Endif
    Endif
 Next
 Color 14,0
 Select Case TempZ!
 Case SFalse
    Print "No vehicles removed."
 Case 1!
    Print TempZ!;"vehicle removed."
 case Else
    Print TempZ!;"vehicles removed."
 End Select
Vehicle.Exit:
 Exit Sub
Vehicle.Error:
 Resume Vehicle.Exit
End Sub

Sub User.List
 On Local Error Goto UserList.Error
 VarQ=Quiet.Mode
 Close
 Data.Error=False
 Open Temp.ArrayS(1) For Random Shared As #1 Len=Len(UserRecord)
 Open Temp.ArrayS(3) For Output Shared As #2
 Open Temp.ArrayS(4) For Output Shared As #3
 If VarQ=False Then
    If Print.Log Then
       Print "Printing "+Temp.ArrayS(3)+" bulletin file to "+PrinterPort
    Else
       Print "Writing "+Temp.ArrayS(3)+" bulletin file."
    Endif
 Endif
 Strng="DNDBBS Version v"+Version$+" User List For "+Left$(Fclock$,15)+"."
 Print #2,Strng
 Strng=Chr$(27)+"[0;1;37m"+Strng
 Print #3,Strng
 Strng=Nul
 Print #2,Strng
 Print #3,Strng
 Strng="Number   User Name                      Class Name           Race     Level DM"
 Print #2,Strng
 Strng=Chr$(27)+"[0;1;33m"+Strng
 Print #3,Strng
 Strng=String$(78,"-")
 Print #2,Strng
 Strng=Chr$(27)+"[0;1;33m"+Strng
 Print #3,Strng
 For Temp.User.Index=1! To Lof(1)/Len(UserRecord)
    Get 1,Temp.User.Index,UserRecord
    Strng=UserRecord.CodeName
    Call Valid.Name(Strng)
    If TempA Then
       If (UserRecord.Flags And Locked.User)=False Then
          Strng=Mid$(Str$(Temp.User.Index),2)
          Strng=Strng+Space$(9-Len(Strng))
          Out2=UserRecord.CodeName
          Out2=Lcase$(Out2)
          Mid$(Out2,1,1)=Ucase$(Mid$(Out2,1,1))
          Strng=Strng+Out2+" "
          Out2=UserRecord.ClassName
          Strng=Strng+Out2+" "
          If UserRecord.Race<1 Then
             UserRecord.Race=1
          Endif
          Out2=Race(UserRecord.Race)
          Out2=Rtrim$(Out2)
          Out2=Out2+Space$(8-Len(Out2))
          Strng=Strng+Out2
          If UserRecord.Level<=False Then
             Out2=" -dead-"
          Else
             If UserRecord.Level>32000 Then
                Out2=" Ghod"
             Else
                Out2=Str$(UserRecord.Level)
             Endif
          Endif
          Out2=Out2+Space$(7-Len(Out2))
          If UserRecord.ClassType>8 Then
             Out2=Out2+"*"
          Endif
          Strng=Strng+Out2
          Print #2,Strng
          Strng=Chr$(27)+"[0;1;35m"+Strng
          Print #3,Strng
       Endif
    Endif
 Next
 Strng=Chr$(27)+"[0m"
 Print #3,Strng
 Close
 If Print.Log Then
    Open Temp.ArrayS(3) For Input Shared As #1
    Open PrinterPort For Output As #TempFile2
    Do Until Eof(1)
       Line Input #1,Strng
       Print #TempFile2,Strng
    Loop
    Print #TempFile2,Chr$(12);
 Endif
UserList.Resume:
 If Data.Error Then
    If Data.Error=25 Then
       VarX$=" device fault"
    Else
       VarX$=Str$(Data.Error)
    Endif
    If Print.Log Then
       Strng="Error"+VarX$+" printing "+Temp.ArrayS(3)+" to "+PrinterPort
    Else
       Strng="Error"+Str$(Data.Error)+" opening "+Temp.ArrayS(3)+". Edit DNDUTIL.CFG."
    Endif
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
UserList.Error:
 Data.Error=Err
 Resume UserList.Resume
End Sub

Sub Top.Ten
 On Local Error Goto TopTen.Error
 VarQ=Quiet.Mode
 If VarQ=False Then
    If Print.Log Then
       Print "Printing "+Temp.ArrayS(5)+" bulletin file to "+PrinterPort
    Else
       Print "Writing "+Temp.ArrayS(5)+" bulletin file."
    Endif
 Endif

 Close
 Data.Error=False
 Open Temp.ArrayS(1) For Random Shared As #1 Len=Len(UserRecord)
 Open Temp.ArrayS(5) For Output Shared As #2
 Open Temp.ArrayS(6) For Output Shared As #3

 TempX!=Lof(1)/Len(UserRecord)

 FileName="SORT3.SWP"
 Open FileName For Random Shared As #4 Len=12
 Field #4,4 As U$,8 As Z$

 Strng="DNDBBS Version v"+Version$+" Top Ten Player Rankings For "+Left$(Fclock$,15)+"."
 Print #2,Strng
 Strng=Chr$(27)+"[0;1;37m"+Strng
 Print #3,Strng
 Strng=Nul
 Print #2,Strng
 Print #3,Strng
 TempZ!=SFalse
 For Temp.User.Index=1! To TempX!
    Get 1,Temp.User.Index,UserRecord
    Strng=UserRecord.CodeName
    If (UserRecord.Flags And Locked.User)=False Then
       Call Valid.Name(Strng)
       If TempA Then
          If UserRecord.Level>=1 Then
             TempA#=Cdbl(UserRecord.PermanentKilled*UserRecord.Level*10)+ _
             Cdbl(UserRecord.PlayersKilled*UserRecord.Level*2)+ _
             Cdbl(UserRecord.MonstersKilled*UserRecord.Level)
             If TempA#>DFalse Then
                TempZ!=TempZ!+1!
                Lset U$=Mks$(Temp.User.Index)
                Lset Z$=Mkd$(TempA#)
                Put #4,TempZ!
             Endif
          Endif
       Endif
    Endif
 Next
 If TempZ!>SFalse Then
    For VarS1!=1! To TempZ!
       For VarS2!=VarS1!+1! To TempZ!
          Get #4,VarS1!
          TempZ1!=Cvs(U$)
          TempX1#=Cvd(Z$)
          Get #4,VarS2!
          TempZ2!=Cvs(U$)
          TempX2#=Cvd(Z$)
          If TempX1#<TempX2# Then
             Lset U$=Mks$(TempZ1!)
             Lset Z$=Mkd$(TempX1#)
             Put #4,VarS2!
             Lset U$=Mks$(TempZ2!)
             Lset Z$=Mkd$(TempX2#)
             Put #4,VarS1!
          Endif
       Next
    Next
 Endif
 If TempZ!>10! Then
    TempZ!=10!
 Endif
 Strng="Username                       Level Classname            Ranking"
 Print #2,Strng
 Strng=Chr$(27)+"[0;1;33m"+Strng
 Print #3,Strng
 Strng=String$(65,"-")
 Print #2,Strng
 Strng=Chr$(27)+"[0;1;33m"+Strng
 Print #3,Strng
 TempX=False
 For Temp1!=1! To TempZ!
    Get #4,Temp1!
    TempX!=Cvs(U$)
    TempZ#=Cvd(Z$)
    Get 1,TempX!,UserRecord
    Strng=UserRecord.CodeName
    Strng=Lcase$(Strng)
    Mid$(Strng,1,1)=Ucase$(Mid$(Strng,1,1))
    If UserRecord.Level>32000 Then
       Strng=Strng+"Ghod   "
    Else
       Strng=Strng+Str$(UserRecord.Level)
       Strng=Strng+Space$(7-Len(Str$(UserRecord.Level)))
    Endif
    Out2=UserRecord.ClassName
    Strng=Strng+Out2
    TempX=True
    Strng=Strng+Str$(TempZ#)
    Print #2,Strng
    Strng=Chr$(27)+"[0;1;35m"+Strng
    Print #3,Strng
 Next
 If TempX=False Then
    Strng="No users have top scores."
    Print #2,Strng
    Strng=Chr$(27)+"[0;1;35m"+Strng
    Print #3,Strng
 Endif
 Strng=Chr$(27)+"[0m"
 Print #3,Strng
 Close
 If Dir$(FileName)<>"" Then
    Kill FileName
 Endif
 If Print.Log Then
    Open Temp.ArrayS(5) For Input Shared As #1
    Open PrinterPort For Output As #TempFile2
    Do Until Eof(1)
       Line Input #1,Strng
       Print #TempFile2,Strng
    Loop
    Print #TempFile2,Chr$(12);
 Endif
TopTen.Resume:
 If Data.Error Then
    If Data.Error=25 Then
       VarX$=" device fault"
    Else
       VarX$=Str$(Data.Error)
    Endif
    If Print.Log Then
       Strng="Error"+VarX$+" printing "+Temp.ArrayS(5)+" to "+PrinterPort
    Else
       Strng="Error"+Str$(Data.Error)+" opening "+Temp.ArrayS(5)+". Edit DNDUTIL.CFG."
    Endif
    Print Strng
    Print "TempZ=";TempZ!
    Color 7,0
    End
 Endif
 Exit Sub
TopTen.Error:
 Data.Error=Err
 Resume TopTen.Resume
End Sub

Sub Last.User
 On Local Error Goto LastUser.Error
 VarQ=Quiet.Mode
 Close
 Data.Error=False
 Open Temp.ArrayS(2) For Random Shared As #1 Len=80
 Field 1,80 As Last$
 Open Temp.ArrayS(7) For Output Shared As #2
 Open Temp.ArrayS(8) For Output Shared As #3
 If VarQ=False Then
    If Print.Log Then
       Print "Printing "+Temp.ArrayS(7)+" bulletin file to "+PrinterPort
    Else
       Print "Writing "+Temp.ArrayS(7)+" bulletin file."
    Endif
 Endif
 Strng="DNDBBS Version v"+Version$+" Last User List For "+Left$(Fclock$,15)+"."
 Print #2,Strng
 Strng=Chr$(27)+"[0;1;37m"+Strng
 Print #3,Strng
 Strng=Nul
 Print #2,Strng
 Print #3,Strng
 Strng="Username                       Timeon   Timeoff  Total    Score"
 Print #2,Strng
 Strng=Chr$(27)+"[0;1;33m"+Strng
 Print #3,Strng
 Strng=String$(70,"-")
 Print #2,Strng
 Strng=Chr$(27)+"[0;1;33m"+Strng
 Print #3,Strng
 For Var!=1! To Lof(1)/80
    Get 1,Var!
    Strng=Rtrim$(Last$)
    Strng=Mid$(Strng,12)
    Print #2,Strng
    Strng=Chr$(27)+"[0;1;35m"+Strng
    Print #3,Strng
 Next
 Strng=Chr$(27)+"[0m"
 Print #3,Strng
 Close
 If Print.Log Then
    Open Temp.ArrayS(7) For Input Shared As #1
    Open PrinterPort For Output As #TempFile2
    Do Until Eof(1)
       Line Input #1,Strng
       Print #TempFile2,Strng
    Loop
    Print #TempFile2,Chr$(12);
 Endif
LastUser.Resume:
 If Data.Error Then
    If Data.Error=25 Then
       VarX$=" device fault"
    Else
       VarX$=Str$(Data.Error)
    Endif
    If Print.Log Then
       Strng="Error"+VarX$+" printing "+Temp.ArrayS(7)+" to "+PrinterPort
    Else
       Strng="Error"+Str$(Data.Error)+" opening "+Temp.ArrayS(7)+". Edit DNDUTIL.CFG."
    Endif
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
LastUser.Error:
 Data.Error=Err
 Resume LastUser.Resume
End Sub

Sub Team.List
 On Local Error Goto TeamList.Error
 VarQ=Quiet.Mode
 Close
 Data.Error=False
 Open Temp.ArrayS(1) For Random Shared As #1 Len=Len(UserRecord)
 Open Temp.ArrayS(13) For Random Shared As #2 Len=Len(TeamRecord)
 Open Temp.ArrayS(9) For Output Shared As #3
 Open Temp.ArrayS(10) For Output Shared As #4
 If VarQ=False Then
    If Print.Log Then
       Print "Printing "+Temp.ArrayS(9)+" bulletin file to "+PrinterPort
    Else
       Print "Writing "+Temp.ArrayS(9)+" bulletin file."
    Endif
 Endif
 Strng="DNDBBS Version v"+Version$+" Team Lists For "+_
 Left$(Fclock$,15)+"."
 Print #3,Strng
 Strng=Chr$(27)+"[0;1;37m"+Strng
 Print #4,Strng
 Strng=Nul
 Print #3,Strng
 Print #4,Strng
 TempX=False
 For Var1!=1! To Lof(2)/Len(TeamRecord)
    Get 2,Var1!,TeamRecord
    Out2=Rtrim$(TeamRecord.Name)
    If Out2<>Nul And TeamRecord.Deleted=False Then
       Var3=False
       For Var2=1 To 5
          VarY!=TeamRecord.UserIndex(Var2)
          If VarY!>SFalse And VarY!<=Lof(1)/Len(UserRecord) Then
             Get 1,VarY!,UserRecord
             Strng=UserRecord.CodeName
             Strng=Lcase$(Strng)
             Call Valid.Name(Strng)
             If TempA Then
                Var3=True
                Exit For
             Endif
          Endif
       Next
       If Var3 Then
          Out2=Lcase$(Out2)
          Call Capitalize(Out2)
          Strng="Team Name: "+Out2
          Print #3,Strng
          Strng=Chr$(27)+"[0;1;33m"
          Strng=Strng+"Team Name: "
          Strng=Strng+Chr$(27)+"[0;1;37m"
          Strng=Strng+Out2
          Print #4,Strng
          Strng=String$(31,"=")
          Print #3,Strng
          Strng=Chr$(27)+"[0;1;33m"+Strng
          Print #4,Strng
          Strng="User Name                      Classname            Level"
          Print #3,Strng
          Strng=Chr$(27)+"[0;1;33m"+Strng
          Print #4,Strng
          Strng=String$(57,"-")
          Print #3,Strng
          Strng=Chr$(27)+"[0;1;33m"+Strng
          Print #4,Strng
          For Var2=1 To 5
             VarY!=TeamRecord.UserIndex(Var2)
             If VarY!>SFalse And VarY!<=Lof(1)/Len(UserRecord) Then
                Get 1,VarY!,UserRecord
                Strng=UserRecord.CodeName
                Strng=Lcase$(Strng)
                Call Valid.Name(Strng)
                If TempA Then
                   Mid$(Strng,1,1)=Ucase$(Mid$(Strng,1,1))
                   Strng=Strng+" "+UserRecord.Classname
                   If UserRecord.Level>32000 Then
                      Strng=Strng+" Ghod"
                   Else
                      Strng=Strng+Str$(UserRecord.Level)
                   Endif
                   TempX=True
                   Print #3,Strng
                   Strng=Chr$(27)+"[0;1;35m"+Strng
                   Print #4,Strng
                Endif
             Endif
          Next
       Endif
    Endif
 Next
 If TempX=False Then
    Strng="Team list"
    Print #3,Strng
    Strng=Chr$(27)+"[0;1;33m"+Strng
    Print #4,Strng

    Strng="---------"
    Print #3,Strng
    Strng=Chr$(27)+"[0;1;33m"+Strng
    Print #4,Strng

    Strng="No teams on file."
    Print #3,Strng
    Strng=Chr$(27)+"[0;1;35m"+Strng
    Print #4,Strng
 Endif
 Strng=Chr$(27)+"[0m"
 Print #4,Strng
 Close
 If Print.Log Then
    Open Temp.ArrayS(9) For Input Shared As #1
    Open PrinterPort For Output As #TempFile2
    Do Until Eof(1)
       Line Input #1,Strng
       Print #TempFile2,Strng
    Loop
    Print #TempFile2,Chr$(12);
 Endif
TeamList.Resume:
 If Data.Error Then
    If Data.Error=25 Then
       VarX$=" device fault"
    Else
       VarX$=Str$(Data.Error)
    Endif
    If Print.Log Then
       Strng="Error"+VarX$+" printing "+Temp.ArrayS(9)+" to "+PrinterPort
    Else
       Strng="Error"+Str$(Data.Error)+" opening "+Temp.ArrayS(9)+". Edit DNDUTIL.CFG."
    Endif
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
TeamList.Error:
 Data.Error=Err
 Resume TeamList.Resume
End Sub

Sub Top.Ten.Teams
 On Local Error Goto TopTenTeams.Error
 VarQ=Quiet.Mode
 If VarQ=False Then
    If Print.Log Then
       Print "Printing "+Temp.ArrayS(11)+" bulletin file to "+PrinterPort
    Else
       Print "Writing "+Temp.ArrayS(11)+" bulletin file."
    Endif
 Endif
 Close
 Data.Error=False
 Open Temp.ArrayS(1) For Random Shared As #1 Len=Len(UserRecord)
 Open Temp.ArrayS(13) For Random Shared As #2 Len=Len(TeamRecord)
 Open Temp.ArrayS(11) For Output Shared As #3
 Open Temp.ArrayS(12) For Output Shared As #4

 TempX!=Lof(2)/Len(TeamRecord)

 FileName="SORT3.SWP"
 Open FileName For Random Shared As #5 Len=12
 Field #5,4 As U$,8 As Z$

 Strng="DNDBBS Version v"+Version$+" Top Ten Team Rankings For "+Left$(Fclock$,15)+"."
 Print #3,Strng
 Strng=Chr$(27)+"[0;1;37m"+Strng
 Print #4,Strng
 Strng=Nul
 Print #3,Strng
 Print #4,Strng
 TempZ!=SFalse
 For Var1!=1! To TempX!
    Get 2,Var1!,TeamRecord
    Strng=Rtrim$(TeamRecord.Name)
    Strng=Lcase$(Strng)
    If Len(Strng) And TeamRecord.Deleted=False Then
       TempQ=False
       TempA#=DFalse
       For Var2=1 To MaxTeamMembers
          Temp2$=Rtrim$(TeamRecord.Member(Var2))
          If Len(Temp2$) Then
             VarY!=TeamRecord.UserIndex(Var2)
             If VarY!>SFalse And VarY!<=Lof(1)/Len(UserRecord) Then
                Get 1,VarY!,UserRecord
                Strng=UserRecord.CodeName
                Call Valid.Name(Strng)
                If TempA Then
                   TempQ=True
                   If UserRecord.Level>=1 Then
                      TempA#=TempA#+Cdbl(UserRecord.PermanentKilled*UserRecord.Level*10)+ _
                      Cdbl(UserRecord.PlayersKilled*UserRecord.Level*2)+ _
                      Cdbl(UserRecord.MonstersKilled*UserRecord.Level)
                   Endif
                Endif
             Endif
          Endif
       Next
       If TempQ Then
          If TempA#>False Then
             TempZ!=TempZ!+1!
             Lset U$=Mks$(Var1!)
             Lset Z$=Mkd$(TempA#)
             Put #5,TempZ!
          Endif
       Endif
    Endif
 Next
 If TempZ!>SFalse Then
    For VarS1!=1! To TempZ!
       For VarS2!=VarS1!+1! To TempZ!
          Get #5,VarS1!
          TempZ1!=Cvs(U$)
          TempX1#=Cvd(Z$)
          Get #5,VarS2!
          TempZ2!=Cvs(U$)
          TempX2#=Cvd(Z$)
          If TempX1#<TempX2# Then
             Lset U$=Mks$(TempZ1!)
             Lset Z$=Mkd$(TempX1#)
             Put #5,VarS2!
             Lset U$=Mks$(TempZ2!)
             Lset Z$=Mkd$(TempX2#)
             Put #5,VarS1!
          Endif
       Next
    Next
 Endif
 If TempZ!>10! Then
    TempZ!=10!
 Endif
 Strng="Teamname                       Members Gold               Ranking"
 Print #3,Strng
 Strng=Chr$(27)+"[0;1;33m"+Strng
 Print #4,Strng
 Strng=String$(65,"-")
 Print #3,Strng
 Strng=Chr$(27)+"[0;1;33m"+Strng
 Print #4,Strng
 TempX=False
 For Temp1!=1! To TempZ!
    Get #5,Temp1!
    TempX!=Cvs(U$)
    TempZ#=Cvd(Z$)
    Get 2,TempX!,TeamRecord
    If Rtrim$(Strng)<>Nul Then
       Strng=TeamRecord.Name
       Strng=Rtrim$(Strng)
       Strng=Lcase$(Strng)
       Call Capitalize(Strng)
       Strng=Strng+Space$(30-Len(Strng))
       TempQ=False
       For Temp2=1 To MaxTeamMembers
          VarY!=TeamRecord.UserIndex(Temp2)
          If VarY!>SFalse And VarY!<=Lof(1)/Len(UserRecord) Then
             Get 1,VarY!,UserRecord
             Var2$=UserRecord.CodeName
             Call Valid.Name(Var2$)
             If TempA Then
                TempQ=TempQ+1
             Endif
          Endif
       Next
       Strng=Strng+Str$(TempQ)
       Strng=Strng+Space$(8-Len(Str$(TempQ)))
       Temp#=TeamRecord.Gold
       Strng=Strng+Str$(Temp#)
       Strng=Strng+Space$(19-Len(Str$(Temp#)))
       Strng=Strng+Str$(TempZ#)
       TempX=True
       Print #3,Strng
       Strng=Chr$(27)+"[0;1;35m"+Strng
       Print #4,Strng
    Endif
 Next
 If TempX=False Then
    Strng="No teams have top scores."
    Print #3,Strng
    Strng=Chr$(27)+"[0;1;35m"+Strng
    Print #4,Strng
 Endif
 Strng=Chr$(27)+"[0m"
 Print #4,Strng
 Close
 If Dir$(FileName)<>"" Then
    Kill FileName
 Endif
 If Print.Log Then
    Open Temp.ArrayS(11) For Input Shared As #1
    Open PrinterPort For Output As #TempFile2
    Do Until Eof(1)
       Line Input #1,Strng
       Print #TempFile2,Strng
    Loop
    Print #TempFile2,Chr$(12);
 Endif
TopTenTeams.Resume:
 If Data.Error Then
    If Data.Error=25 Then
       VarX$=" device fault"
    Else
       VarX$=Str$(Data.Error)
    Endif
    If Print.Log Then
       Strng="Error"+VarX$+" printing "+Temp.ArrayS(11)+" to "+PrinterPort
    Else
       Strng="Error"+Str$(Data.Error)+" opening "+Temp.ArrayS(11)+". Edit DNDUTIL.CFG."
    Endif
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
TopTenTeams.Error:
 Data.Error=Err
 Resume TopTenTeams.Resume
End Sub

Sub Capitalize(Var3$)
 On Local Error Goto ErrorTrap24x4
 Var3$=Rtrim$(Ltrim$(Var3$))
 If Var3$=Nul Then
    Exit Sub
 Endif
 Mid$(Var3$,1,1)=Ucase$(Mid$(Var3$,1,1))
 Var=Instr(Var3$," ")
 If Var<Len(Var3$) Then
    Do While Var
       Mid$(Var3$,Var+1,1)=Ucase$(Mid$(Var3$,Var+1,1))
       Var=Instr(Var+1,Var3$," ")
    Loop
 Endif
 Exit Sub
ErrorResume24x4:
 Exit Sub
ErrorTrap24x4:
 Resume ErrorResume24x4
End Sub

Config.Data:
 Data "' Editable configure file for dndutil:"
 Data "' Datapath\filename for users.dat data file"
 Data "users.dat"
 Data "' Datapath\filename for lastuser.dat data file"
 Data "lastuser.dat"
 Data "' Datapath\filename for user list bulletin files"
 Data "userlist.txt"
 Data "userlist.ans"
 Data "' Datapath\filename for rank list bulletin files"
 Data "ranklist.txt"
 Data "ranklist.ans"
 Data "' Datapath\filename for last list bulletin files"
 Data "lastlist.txt"
 Data "lastlist.ans"
 Data "' Datapath\filename for team list bulletin files"
 Data "teamlist.txt"
 Data "teamlist.ans"
 Data "' Datapath\filename for team rank bulletin files"
 Data "teamrank.txt"
 Data "teamrank.ans"
 Data "' Datapath\filename for teamuser.dat data file"
 Data "teamuser.dat"
 Data "' End of configure file."

Config.Array.Data:
 Data "Human","Elf","Gnome","Dwarf","Halfling","Half-elf","Half-orc","Ogre"

Sub Zero.Nodes
 On Local Error Goto Error.Trap3
 Call Get.Config
 Close
 Call Open.Mess1.File
 Call Init.Nodes
 For Var1=False To MaxNodes
    Call Clear.Nodes
    Call Write.Message.Record(MessWorkFile1,Var1+1)
 Next
Error.Resume3:
 Exit Sub
Error.Trap3:
 Resume Error.Resume3
End Sub

Sub Init.Nodes
 On Local Error Goto Error.Trap4
 Var1=Lof(MessWorkFile1)/MessWorkRecLen1
 If Var1<MaxNodes+1 Then
    Var1=Var1+1
    For Var2=Var1 To MaxNodes+1
       Call Clear.Nodes
       Call Write.Message.Record(MessWorkFile1,Var2)
    Next
 Endif
Error.Resume4:
 Exit Sub
Error.Trap4:
 Resume Error.Resume4
End Sub

Sub Clear.Nodes
 On Local Error Goto Error.Trap5
 MessWorkRecord1.ActiveNode=False
 MessWorkRecord1.PortNumber=False
 MessWorkRecord1.Monitor=False
 MessWorkRecord1.UserName=Offline$
 MessWorkRecord1.ClassName=Offline$
 MessWorkRecord1.SendFlags=String$(MaxNodes+1,"1")
 MessWorkRecord1.ReceiveFlags=String$(MaxNodes+1,"1")
Error.Resume5:
 Exit Sub
Error.Trap5:
 Resume Error.Resume5
End Sub

Sub Get.Config
 On Local Error Goto Config.File.Error
 Redim Direction(1 To MaxDirections) As String*9
 Redim Race(1 To MaxRaces) As String*8
' Restore DirectionData
' For Var=1 To MaxDirections
'    Read Direction(Var)
' Next
 DND.Path=Environ$("DNDBBS")
 If DND.Path<>Nul Then
    If Right$(DND.Path,1)<>"\" Then
       DND.Path=DND.Path+"\"
    Endif
 Endif
 FileName=DND.Path+"DND"+NodeX+".CFG"
 Data.Error=False
 Close #TempFile1
 Open FileName For Input Shared As #TempFile1
 Line Input #TempFile1,Data.Path3 ' d:\
 For Temp1=1 To MaxConfigArray1
    Input #TempFile1,Var!
 Next
 For Temp1=1 To MaxConfigArray2
    Input #TempFile1,Var%
    Select Case Temp1
    Case 132
       Time.Override=Var%
    Case 133
       Max.Lines=Var%
    End Select
 Next
 For Temp1=1 To MaxConfigArray3
    Line Input #TempFile1,Temp$
    Select Case Temp1
    Case 11
       Data.Path1=Rtrim$(Temp$) ' \dnddat
    Case 15
       File.Extension1=Rtrim$(Temp$) ' .dat
    Case 16
       Room.Filename=Rtrim$(Temp$) ' rooms.dat
    Case 18
       Monster.Filename=Rtrim$(Temp$) ' monsters.dat
    Case 19
       Treasure.FileName=Rtrim$(Temp$) ' treasure.dat
    Case 68
       MessWork.Filename=Rtrim$(Temp$) ' messwork?.
    Case 82
       If PrinterPort=Nul Then
          PrinterPort=Rtrim$(Temp$) ' lpt1:
       Endif
    Case 89
       RoomInv.FileName=Rtrim$(Temp$) ' roominv.dat
    Case 90
       MonInv.FileName=Rtrim$(Temp$) ' moninv.dat
    End Select
 Next
 For Temp1=1 To 40
    Input #TempFile1,Var%
 Next
 For Temp1=1 To 30
    Input #TempFile1,Var$
 Next
 For Temp1=1 To 8
    Line Input #TempFile1,Var$
    Race(Temp1)=Var$
 Next
 For Temp1=1 To 17
    Line Input #TempFile1,Var$
 Next
 For Temp1=1 To 12
    Line Input #TempFile1,Var$
    Direction(Temp1)=Var$
 Next
 Close #TempFile1
 Out2=Environ$("DNDDAT")
 If Out2<>Nul Then
    If Right$(Out2,1)<>"\" Then
       Out2=Out2+"\"
    Endif
    Data.Path1=Out2
 Endif
 ' check share
 Call Plex.Function(16)
 If (OutregsX.AX And &HFF)=&HFF Then
    Share.Installed=True
 Else
    Share.Installed=False
 Endif
 ' check os
 Call StdinInt(48,0)
 ' store dos version
 DOS.Major=OutregsX.AX And &HFF
 DOS.Minor=(OutregsX.AX And &HFF00)/256
 ' check os/2
 If DOS.Major=10 Or DOS.Major=20 Then
    OS2.Detected=True
 Else
    OS2.Detected=False
 Endif
 ' check windows
 Windows.Detected = False
 Call Plex.Function(22)
 If OutregsX.BX>0 Then
    Windows.Detected = True
    Win.Minor=OutregsX.BX And &HFF
    Win.Major=(OutregsX.BX And &HFF00)/256
 Endif
Config.File.Resume:
 If Data.Error Then
    Color 15,0
    Strng="Error"+Str$(Data.Error)+": "+FileName+" not found. Run DNDCNFG."
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
DirectionData:
 Data "north","east","south","west"
 Data "northeast","southeast","southwest","northwest"
 Data "up","down","in","out"
Config.File.Error:
 Data.Error=Err
 Resume Config.File.Resume
End Sub

Sub Open.Mess1.File
 On Local Error Goto Open.Error1
 Data.Error=False
 MessWorkRecLen1=Len(MessWorkRecord1)
 FileName=Data.Path3+MessWork.Filename+"1"+File.Extension1
 Open FileName For Random Shared As #MessWorkFile1 Len=MessWorkRecLen1
Open.Resume1:
 If Data.Error Then
    Color 15,0
    Strng="Error"+Str$(Data.Error)+" opening files."
    Print Strng
    Strng="Increase files= in config.sys or config.nt file, or run Dndcnfg."
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
Open.Error1:
 Data.Error=Err
 Resume Open.Resume1
End Sub

Sub Open.Mess2.File
 On Local Error Goto Open.Error2
 Data.Error=False
 MessWorkRecLen2=Len(MessWorkRecord2)
 FileName=Data.Path3+MessWork.Filename+"2"+File.Extension1
 Open FileName For Random Shared As #MessWorkFile2 Len=MessWorkRecLen2
Open.Resume2:
 If Data.Error Then
    Color 15,0
    Strng="Error"+Str$(Data.Error)+" opening files."
    Print Strng
    Strng="Increase files= in config.sys or config.nt file, or run Dndcnfg."
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
Open.Error2:
 Data.Error=Err
 Resume Open.Resume2
End Sub

Sub Open.Mess3.File
 On Local Error Goto Open.Error3
 Data.Error=False
 MessWorkRecLen3=Len(MessWorkRecord3)
 FileName=Data.Path3+MessWork.Filename+"3"+File.Extension1
 Open FileName For Random Shared As #MessWorkFile3 Len=MessWorkRecLen3
Open.Resume3:
 If Data.Error Then
    Color 15,0
    Strng="Error"+Str$(Data.Error)+" opening files."
    Print Strng
    Strng="Increase files= in config.sys or config.nt file, or run Dndcnfg."
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
Open.Error3:
 Data.Error=Err
 Resume Open.Resume3
End Sub

Sub Open.Mess5.File
 On Local Error Goto Open.Error5
 Data.Error=False
 MessWorkRecLen5=Len(MessWorkRecord5)
 FileName=Data.Path3+MessWork.Filename+"5"+File.Extension1
 Open FileName For Random Shared As #MessWorkFile5 Len=MessWorkRecLen5
Open.Resume5:
 If Data.Error Then
    Color 15,0
    Strng="Error"+Str$(Data.Error)+" opening files."
    Print Strng
    Strng="Increase files= in config.sys or config.nt file, or run Dndcnfg."
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
Open.Error5:
 Data.Error=Err
 Resume Open.Resume5
End Sub

Sub Open.RoomInv.File
 On Local Error Goto Open.Error6
 Data.Error=False
 MessWorkRecLen6=Len(MessWorkRecord6)
 FileName=Data.Path1+RoomInv.Filename+File.Extension1
 Open FileName For Random Shared As #RoomInvFile Len=MessWorkRecLen6
Open.Resume6:
 If Data.Error Then
    Color 15,0
    Strng="Error"+Str$(Data.Error)+" opening files."
    Print Strng
    Strng="Increase files= in config.sys or config.nt file, or run Dndcnfg."
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
Open.Error6:
 Data.Error=Err
 Resume Open.Resume6
End Sub

Sub Open.Room.File
 On Local Error Goto Open.Error2a
 Data.Error=False
 RoomRecLen=Len(RoomRecord)
 FileName=Data.Path1+Room.Filename+File.Extension1
 Open FileName For Random Shared As #RoomFile Len=RoomRecLen
Open.Resume2a:
 If Data.Error Then
    Color 15,0
    Strng="Error"+Str$(Data.Error)+" opening files."
    Print Strng
    Strng="Increase files= in config.sys or config.nt file, or run Dndcnfg."
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
Open.Error2a:
 Data.Error=Err
 Resume Open.Resume2a
End Sub

Sub Open.Moninv.File
 On Local Error Goto Open.Error2b
 Data.Error=False
 MessWorkRecLen6=Len(MessWorkRecord6)
 FileName=Data.Path1+MonInv.Filename+File.Extension1
 Open FileName For Random Shared As #MonInvFile Len=MessWorkRecLen6
Open.Resume2b:
 If Data.Error Then
    Color 15,0
    Strng="Error"+Str$(Data.Error)+" opening files."
    Print Strng
    Strng="Increase files= in config.sys or config.nt file, or run Dndcnfg."
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
Open.Error2b:
 Data.Error=Err
 Resume Open.Resume2b
End Sub

Sub Open.Automate.File
 On Local Error Goto Open.Error2c
 Data.Error=False
 AutomateRecLen=Len(AutomateRecord)
 FileName=Data.Path1+"Automate"+File.Extension1
 Open FileName For Random Shared As #AutomateFile Len=AutomateRecLen
Open.Resume2c:
 If Data.Error Then
    Color 15,0
    Strng="Error"+Str$(Data.Error)+" opening files."
    Print Strng
    Strng="Increase files= in config.sys or config.nt file, or run Dndcnfg."
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
Open.Error2c:
 Data.Error=Err
 Resume Open.Resume2c
End Sub

Sub Open.Monster.File
 On Local Error Goto Open.Error2d
 Data.Error=False
 MonsterRecLen=Len(MonsterRecord)
 FileName=Data.Path1+Monster.Filename+File.Extension1
 Open FileName For Random Shared As #MonsterFile Len=MonsterRecLen
Open.Resume2d:
 If Data.Error Then
    Color 15,0
    Strng="Error"+Str$(Data.Error)+" opening files."
    Print Strng
    Strng="Increase files= in config.sys or config.nt file, or run Dndcnfg."
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
Open.Error2d:
 Data.Error=Err
 Resume Open.Resume2d
End Sub

Sub Open.Treasure.File
 On Local Error Goto Open.Error2e
 Data.Error=False
 TreasureRecLen=Len(TreasureRecord)
 FileName=Data.Path1+Treasure.Filename+File.Extension1
 Open FileName For Random Shared As #TreasureFile Len=TreasureRecLen
Open.Resume2e:
 If Data.Error Then
    Color 15,0
    Strng="Error"+Str$(Data.Error)+" opening files."
    Print Strng
    Strng="Increase files= in config.sys or config.nt file, or run Dndcnfg."
    Print Strng
    Color 7,0
    End
 Endif
 Exit Sub
Open.Error2e:
 Data.Error=Err
 Resume Open.Resume2e
End Sub

' shutdown node Var1.
Sub Shutdown.Node(Var1)
 On Local Error Goto Error.Trap8a
 Call Read.Message.Record(MessWorkFile1,Var1+1)
 Strng=Rtrim$(MessWorkRecord1.UserName)
 Call Valid.Name(Strng)
 If TempA Then
    Var!=MessWorkRecord1.UserIndex
    Call Send.Mess(33,Var!,SFalse,"Shutdown")
 Endif
Error.Resume8a:
 Exit Sub
Error.Trap8a:
 Resume Error.Resume8a
End Sub

' shutdown all nodes.
Sub Shutdown.Nodes
 On Local Error Goto Error.Trap8b
 Call Write.Node(32,"shutting down.")
Error.Resume8b:
 Exit Sub
Error.Trap8b:
 Resume Error.Resume8b
End Sub

' abort node Var1.
Sub Terminate.Node(Var1)
 On Local Error Goto Error.Trap8c
 Call Read.Message.Record(MessWorkFile1,Var1+1)
 If MessWorkRecord1.ActiveNode Then
    MessWorkRecord1.ActiveNode=False
    Call Write.Message.Record(MessWorkFile1,Var1+1)
 Endif
 Strng=Rtrim$(MessWorkRecord1.UserName)
 Call Valid.Name(Strng)
 If TempA Then
    Var!=MessWorkRecord1.UserIndex
    Call Send.Mess(14,Var!,SFalse,"Abort")
 Endif
Error.Resume8c:
 Exit Sub
Error.Trap8c:
 Resume Error.Resume8c
End Sub

' abort all nodes.
Sub Terminate.Nodes
 On Local Error Goto Error.Trap8b
 For Temp=1 To MaxNodes
    Call Read.Message.Record(MessWorkFile1,Temp+1)
    MessWorkRecord1.ActiveNode=False
    Call Write.Message.Record(MessWorkFile1,Temp+1)
 Next
 Call Write.Node(12,"terminating node.")
Error.Resume8d:
 Exit Sub
Error.Trap8d:
 Resume Error.Resume8d
End Sub

' sends a system message to all nodes.
Sub Write.Node(Var1,Var1$)
 On Local Error Goto Error.Trap9
 Temp$=String$(MaxNodes+1,"0")
 For Temp4=False To MaxNodes
    Call Read.Message.Record(MessWorkFile1,Temp4+1)
    Var=False
    If MessWorkRecord1.UserIndex=False Then
       If MessWorkRecord1.RoomNumber=False Then
          Var=True
       Endif
    Endif
    If Var Then
       Mid$(Temp$,Temp4+1,1)="1"
    Endif
 Next
 If Var1=5 Then
    If Temp$=String$(MaxNodes+1,"1") Then
       Exit Sub
    Endif
 Endif
 For Var!=1 To Lof(MessWorkFile2)/MessWorkRecLen2
    Call Read.Record(MessWorkFile2,Var!)
    If MessWorkRecord2.Flags=String$(MaxNodes+1,"1") Then
       Exit For
    Endif
    VarX!=Csng(Time.Override)
    If VarX!>SFalse Then
       If Time.Elapsed(MessWorkRecord2.MessageTime,VarX!) Then
          Exit For
       Endif
    Endif
 Next
 Var1$="System: "+Var1$
 MessWorkRecord2.Flags=Temp$
 MessWorkRecord2.Message=Var1$
 MessWorkRecord2.MessageTime=Timeit!
 MessWorkRecord2.MessageType=Var1
 MessWorkRecord2.Node=False
 MessWorkRecord2.RoomNumber=False
 MessWorkRecord2.UserIndex=False
 MessWorkRecord2.UserName="<monitor>"
 MessWorkRecord2.Message2=Nul
 Call Write.Record(MessWorkFile2,Var!)
Error.Resume9:
 Exit Sub
Error.Trap9:
 Resume Error.Resume9
End Sub

REM Sends a message to specific user or users in a room.

' Var1 = message type
' Var2! = User.Index
' Var3! = Room.Number
' Var1$ = Message
Sub Send.Mess(Var1,Var2!,Var3!,Var1$)
 On Local Error Goto ErrorTrap15
 Temp$=String$(MaxNodes+1,"0")
 For Temp4=False To MaxNodes
    Call Read.Message.Record(MessWorkFile1,Temp4+1)
    Var=False
    If Var2!>SFalse And MessWorkRecord1.UserIndex<>Var2! Then
       Var=True
    Endif
    If Var3!>SFalse And MessWorkRecord1.RoomNumber<>Var3! Then
       Var=True
    Endif
    If MessWorkRecord1.UserIndex=SFalse Then
       If MessWorkRecord1.RoomNumber=SFalse Then
          Var=True
       Endif
    Endif
    If Var Then
       Mid$(Temp$,Temp4+1,1)="1"
    Endif
 Next
 If Temp$=String$(MaxNodes+1,"1") Then
    Exit Sub
 Endif
 For Var!=1! To Lof(MessWorkFile2)/MessWorkRecLen2
    Call Read.Record(MessWorkFile2,Var!)
    If MessWorkRecord2.Flags=String$(MaxNodes+1,"1") Then
       Exit For
    Endif
    VarX!=Csng(Time.Override)
    If VarX!>SFalse Then
       If Time.Elapsed(MessWorkRecord2.MessageTime,VarX!) Then
          Exit For
       Endif
    Endif
 Next
 If Var3!=SFalse Then
    Var1$="System: "+Var1$
 Endif
 MessWorkRecord2.Flags=Temp$
 MessWorkRecord2.Message=Var1$
 MessWorkRecord2.MessageTime=Timeit!
 MessWorkRecord2.MessageType=Var1
 MessWorkRecord2.Node=False
 MessWorkRecord2.RoomNumber=False
 MessWorkRecord2.UserIndex=False
 MessWorkRecord2.UserName="<monitor>"
 MessWorkRecord2.Message2=Nul
 Call Write.Record(MessWorkFile2,Var!)
ErrorResume15:
 Exit Sub
ErrorTrap15:
 Resume ErrorResume15
End Sub

' realtime node monitoring menu.
Sub Read.Nodes
 On Local Error Goto Error.Trap6
 Call Get.Config
 Close
 Call Read.Dndpage.Cfg(Vpage,Vpage2,Var3!)
 Call Open.Mess1.File
 Call Open.Mess2.File
 Call Init.Nodes
 Call Util.Screen(Var3!)
 Var5!=Timeit!
 Do
    Color 14,0
    Locate 8,27
    Print Space$(10);
    Locate 8,27 ' maximum
    Print Str$(Lof(MessWorkFile2)/MessWorkRecLen2);
    TempX=False
    TempY=False
    TempZ=False
    TempQ=False
    TempR=False
    For Temp!=1! To Lof(MessWorkFile2)/MessWorkRecLen2
       Call Read.Record(MessWorkFile2,Temp!)
       If MessWorkRecord2.Flags=String$(MaxNodes+1,"1") Then
          TempX=TempX+1
       Else
          TempY=TempY+1
       Endif
    Next
    For Temp=1 To MaxNodes
       Call Read.Message.Record(MessWorkFile1,Temp+1)
       If MessWorkRecord1.ActiveNode Then
          TempZ=TempZ+1
       Endif
       If MessWorkRecord1.Monitor Then
          TempQ=TempQ+1
       Endif
       If MessWorkRecord1.Chatting Then
          TempR=TempR+1
       Endif
    Next

    ' empty
    Locate 10,25
    Print Space$(10);
    Locate 10,25
    Print Str$(TempX);

    ' in use
    Locate 12,26
    Print Space$(10);
    Locate 12,26
    Print Str$(TempY);

    ' active
    Locate 8,53
    Print Space$(10);
    Locate 8,53
    Print Str$(TempZ);

    ' monitoring
    Locate 10,57
    Print Space$(10);
    Locate 10,57
    Print Str$(TempQ);

    ' chatting
    Locate 12,55
    Print Space$(10);
    Locate 12,55
    Print Str$(TempR);

    Color 15,1
    Locate 22,40
    Print Space$(21);
    Locate 22,40
    Select Case Vpage
    Case 1
       Print "Display: Usernames";
    Case 2
       Print "Display: Classnames";
    Case 3
       Print "Display: Last Command";
    Case 4
       Print "Display: User Level";
    Case 5
       Print "Display: Room Number";
    Case 6
       Print "Display: Machine Name";
    Case 7
       Print "Display: Port Number";
    End Select

    ' display node info (Note: node <nul> not listed).
    Call Util.Screen4(Vpage2)
    Color 13,1
    For Temp5=0 To 8
       Select Case Vpage2
       Case 1 ' 2-10
          Call Read.Message.Record(MessWorkFile1,Temp5+2)
       Case 2 ' 20-28
          Call Read.Message.Record(MessWorkFile1,Temp5+20)
       Case 3 ' 38-46
          Call Read.Message.Record(MessWorkFile1,Temp5+38)
       Case 4 ' 56-64
          Call Read.Message.Record(MessWorkFile1,Temp5+56)
       Case 5 ' 74-82
          Call Read.Message.Record(MessWorkFile1,Temp5+74)
       Case 6 ' 92-100
          Call Read.Message.Record(MessWorkFile1,Temp5+92)
       Case 7 ' 110-118
          Call Read.Message.Record(MessWorkFile1,Temp5+110)
       End Select
       Locate Temp5+13,9
       Print Space$(30);
       Locate Temp5+13,9
       Gosub Display.User
    Next
    For Temp5=0 To 8
       Select Case Vpage2
       Case 1 ' 11-19
          Call Read.Message.Record(MessWorkFile1,Temp5+11)
       Case 2 ' 29-37
          Call Read.Message.Record(MessWorkFile1,Temp5+29)
       Case 3 ' 47-55
          Call Read.Message.Record(MessWorkFile1,Temp5+47)
       Case 4 ' 65-73
          Call Read.Message.Record(MessWorkFile1,Temp5+65)
       Case 5 ' 83-91
          Call Read.Message.Record(MessWorkFile1,Temp5+83)
       Case 6 ' 101-109
          Call Read.Message.Record(MessWorkFile1,Temp5+101)
       Case 7 ' 119-127
          Call Read.Message.Record(MessWorkFile1,Temp5+119)
       End Select
       Locate Temp5+13,47
       Print Space$(30);
       Locate Temp5+13,47
       Gosub Display.User
    Next
    Var4!=Timeit!
    Var$=Nul
    Do Until Time.Elapsed(Var4!,Var3!)
       Var$=Inkey$
       If Len(Var$) Then
          Exit Do
       Endif
       If Time.Elapsed(Var5!,1!) Then
          Var5!=Timeit!
          Color 14,1
          Locate 23,70
          Time1$=Format$(Now,"hh:mm:ss")
          Print Time1$;
       Endif
       Call Release.Time(1)
    Loop
    If Var$=Chr$(27) Then ' escape
       Exit Do
    Endif
    If Var$=Chr$(3) Then ' ctrl-c
       Exit Do
    Endif
    If Len(Var$)=2 Then
       Var2=Asc(Right$(Var$,1))
       Select Case Var2
       Case 0 ' Control-Break
          Exit Do
       Case 71, 119 ' home
          Vpage2=1
       Case 79, 117 ' end
          Vpage2=7
       Case 132 ' Control-Page Up
          If Vpage2>1 Then
             Vpage2=Vpage2-1
          Endif
       Case 118 ' Control-Page Down
          If Vpage2<7 Then
             Vpage2=Vpage2+1
          Endif
       Case 73 ' Page Up
          If Vpage>1 Then
             Vpage=Vpage-1
          Endif
       Case 81 ' Page Down
          If Vpage<7 Then
             Vpage=Vpage+1
          Endif
       Case 72 ' Up
          If Var3!<30! Then
             Var3!=Var3!+1!
             Gosub Display.Delay
          Endif
       Case 80 ' Down
          If Var3!>1! Then
             Var3!=Var3!-1!
             Gosub Display.Delay
          Endif
       End Select
    Endif
 Loop
 Exit Sub
Display.User:
 VarX$=Offline$
 Select Case Vpage
 Case 1
    VarX$=Rtrim$(MessWorkRecord1.UserName)
    VarX$=Lcase$(VarX$)
    Mid$(VarX$,1,1)=Ucase$(Mid$(VarX$,1,1))
 Case 2
    If MessWorkRecord1.UserIndex Then
       If MessWorkRecord1.RoomNumber Then
          VarX$=Rtrim$(MessWorkRecord1.ClassName)
          VarX$=Lcase$(VarX$)
          Mid$(VarX$,1,1)=Ucase$(Mid$(VarX$,1,1))
       Endif
    Endif
 Case 3
    If MessWorkRecord1.UserIndex Then
       If MessWorkRecord1.RoomNumber Then
          VarX$=Rtrim$(MessWorkRecord1.LastCommand)
          Select Case Left$(VarX$,1)
          Case "a" To "z", "A" To "Z"
             VarX$=Lcase$(VarX$)
             Mid$(VarX$,1,1)=Ucase$(Mid$(VarX$,1,1))
          End Select
       Endif
    Endif
 Case 4
    If MessWorkRecord1.UserIndex Then
       If MessWorkRecord1.RoomNumber Then
          Select Case MessWorkRecord1.Level
          Case 0
             VarZ$="-dead-"
          Case Is>=32000
             VarZ$="Ghod"
          Case Else
             VarZ$=Form$(Cdbl(MessWorkRecord1.Level))
          End Select
          VarX$="Level: "+VarZ$
       Endif
    Endif
 Case 5
    If MessWorkRecord1.UserIndex Then
       If MessWorkRecord1.RoomNumber Then
          VarX$="Room: "+Form$(Cdbl(MessWorkRecord1.RoomNumber))
       Endif
    Endif
 Case 6
    If MessWorkRecord1.ActiveNode Then
       VarX$=Rtrim$(MessWorkRecord1.MachineName)
       If VarX$=Nul Then VarX$="<unknown>"
    Endif
 Case 7
    If MessWorkRecord1.ActiveNode Then
       If MessWorkRecord1.PortNumber=0 Then
          VarX$="Port: Local"
       Else
          VarX$="Port:"+Str$(MessWorkRecord1.PortNumber)
       Endif
    Endif
 End Select
 Print VarX$;
 Return
Error.Resume6:
 Exit Sub
Display.Delay:
 Color 14,1
 Locate 22,3
 Print Space$(30);
 Locate 22,3
 Print "(";Mid$(Str$(Var3!),2);" sec. delay, Up/Down).";
 Return
Error.Trap6:
 Color 7,0
 Cls
 Color 14,0
 Print "Dndutil realtime monitor terminated with error"+Str$(Err)+"."
 Color 7,0
 Print "Returning to system."
 End
End Sub

' reads in config file for realtime utility.
Sub Read.Dndpage.Cfg(VarX1,VarY,Var3!)
 On Local Error Goto ErrResume13x2
 VarX1=1
 VarY=1
 Var3!=1!
 Filename=DND.Path+"DNDPAGE.CFG"
 If Dir$(Filename)=Nul Then Exit Sub
 Close #TempFile1
 Open Filename For Input As #TempFile1
 Do
    If Eof(TempFile1) Then
       Exit Do
    Endif
    Line Input #TempFile1,Data.Line$
    Data.Line$=Rtrim$(Ltrim$(Ucase$(Data.Line$)))
    If Len(Data.Line$) Then
       If Left$(Data.Line$,1)<>";" Then
          VarX=Instr(Data.Line$,"=")
          If VarX Then
             Var1$=Left$(Data.Line$,VarX-1)
             Var2$=Mid$(Data.Line$,VarX+1)
             Var1$=Rtrim$(Ltrim$(Var1$))
             Var2$=Rtrim$(Ltrim$(Var2$))
             If Var1$="PAGELEVEL" Then
                If Int(Val(Var2$)) >= 1 And Int(Val(Var2$)) <= 7 Then
                   VarX1 = Int(Val(Var2$))
                Endif
             Endif
             If Var1$="PAGELEVEL2" Then
                If Int(Val(Var2$)) >= 1 And Int(Val(Var2$)) <= 7 Then
                   VarY = Int(Val(Var2$))
                Endif
             Endif
             If Var1$="PAGETIMER" Then ' 1-30
                If Int(Val(Var2$)) >= 1 And Int(Val(Var2$)) <= 30 Then
                   Var3! = Csng(Int(Val(Var2$)))
                Endif
             Endif
          Endif
       Endif
    Endif
 Loop
ErrExit13x2:
 Exit Sub
ErrResume13x2:
 Resume ErrExit13x2
End Sub

Sub Release.Time(Var)
 On Local Error Goto Error.Trap7
 If Supported.Call=False Then
    For Var1=1 To Var
       Call Slice.Function
       If (OutregsX.AX And &HFF)=&H80 Then
          Supported.Call=True
          Exit For
       Endif
    Next
 Endif
Error.Resume7:
 Exit Sub
Error.Trap7:
 Resume Error.Resume7
End Sub

' menu screen for realtime node monitoring utility.
Sub Util.Screen(Var3!)
 On Local Error Goto Error.Trap8
 VarX$=Ucase$(Environ$("BLIND"))
 If VarX$="ON" Or VarX$="-1" Or VarX$="TRUE" Then
    Border.Off=-1
 Endif
 VarX$=Ucase$(Environ$("BLINDCHAR"))
 If VarX$="ON" Or VarX$="-1" Or VarX$="TRUE" Then
    Border.Char=-1
 Endif
 Color 14,1
 Cls
 Color 15,1
 Locate 2,2
 If Border.Off=0 Then
    If Border.Char=0 Then
       Print Chr$(201); String$(76, 205); Chr$(187);
    Else
       Print "+"; String$(76, "-"); "+";
    Endif
    For Temp5=3 To 23
       Locate Temp5,2
       If Border.Char=0 Then
          Print Chr$(186);
       Else
          Print "|";
       Endif
       Locate Temp5,79
       If Border.Char=0 Then
          Print Chr$(186);
       Else
          Print "|";
       Endif
    Next
    Locate 24,2
    If Border.Char=0 Then
       Print Chr$(200); String$(76, 205); Chr$(188);
    Else
       Print "+"; String$(76, "-"); "+";
    Endif
 Endif
 Color 14,1
 Locate 4,3
 Print "Dungeons  And  Dragons  Bulletin  Board  System  Adventure  Game";
 Print " ("+YearRelease$+")";
 Locate 6,3
 Print "Dndutil  Realtime  Monitor  Utility.  (v"+Version$+" r"+Release$+"). ";
 If NodeX=Nul Then
    Print "Node: - ";
 Else
    Print "Node: ";NodeX;
 Endif
 Print " ("+Format$(Now,"mm-dd-yyyy")+")";
 If Debug.Mode Then
    If Debug.Mode Then Debug.Mode=1
    Color 15,1
    Locate 7,5
    Var#=Cdbl(Fre(-1)+Fre(a$))
    Var$=Format$(Var#,"#,##0")
    Print "Available RAM: "+Var$
 Endif
 Color 14,1
 Locate 8,3
 Print "Maximum message records:";
 Locate 10,3
 Print "Empty message records:";
 Locate 12,3
 Print "Message records in use:";
 Locate 8,40
 Print "Active nodes:";
 Locate 10,40
 Print "Nodes monitoring:";
 Locate 12,40
 Print "Nodes chatting:";
 For Temp5=0 To 8
    Call Read.Message.Record(MessWorkFile1,Temp5+2)
    If MessWorkRecord1.ActiveNode Then
       Color 15,1
    Else
       Color 14,1
    Endif
    Locate Temp5+13,3
    Print "N#";Mid$(Str$(Temp5),2);
 Next
 For Temp5=0 To 8
    Call Read.Message.Record(MessWorkFile1,Temp5+11)
    If MessWorkRecord1.ActiveNode Then
       Color 15,1
    Else
       Color 14,1
    Endif
    Locate Temp5+13,40
    If Temp5=0 Then
       Print "N#9";
    Else
       Print "N#";Chr$(Temp5+64);
    Endif
 Next
 Locate 22,3
 Print "("+Mid$(Str$(Var3!),2)+" sec. delay, Up/Down).";
 Locate 23,3
 Print "(<esc> exits)  PageUp/Down=display   Control-PageUp/Down=nodelist";
Error.Resume8:
 Exit Sub
Error.Trap8:
 Resume Error.Resume8
End Sub

' menu for realtime automaton moving utility.
Sub Util.Screen2
 On Local Error Goto Error.Trap8x
 VarX$=Ucase$(Environ$("BLIND"))
 If VarX$="ON" Or VarX$="-1" Or VarX$="TRUE" Then
    Border.Off=-1
 Endif
 VarX$=Ucase$(Environ$("BLINDCHAR"))
 If VarX$="ON" Or VarX$="-1" Or VarX$="TRUE" Then
    Border.Char=-1
 Endif
 Color 14,1
 Cls
 Color 15,1
 Locate 2,2
 If Border.Off=0 Then
    If Border.Char=0 Then
       Print Chr$(201); String$(76, 205); Chr$(187);
    Else
       Print "+"; String$(76, "-"); "+";
    Endif
    For Temp5=3 To 23
       Locate Temp5,2
       If Border.Char=0 Then
          Print Chr$(186);
       Else
          Print "|";
       Endif
       Locate Temp5,79
       If Border.Char=0 Then
          Print Chr$(186);
       Else
          Print "|";
       Endif
    Next
    Locate 24,2
    If Border.Char=0 Then
       Print Chr$(200); String$(76, 205); Chr$(188);
    Else
       Print "+"; String$(76, "-"); "+";
    Endif
 Endif
 Color 14,1
 Locate 4,4
 Print "Dungeons  And  Dragons  Bulletin  Board  System  Adventure  Game";
 Locate 6,4
 Print "Dndutil  Realtime  Automaton  Mover  Utility. ";
 Print "(v"+Version$+" r"+Release$+"). ";
 If NodeX=Nul Then
    Print "Node: - ";
 Else
    Print "Node: ";NodeX;
 Endif
 If Debug.Mode Then
    If Debug.Mode Then Debug.Mode=1
    Color 15,1
    Locate 7,5
    Var#=Cdbl(Fre(-1)+Fre(a$))
    Var$=Format$(Var#,"#,##0")
    Print "Available RAM: "+Var$
 Endif
 Color 14,1
 Locate 8,4
 Print "Maximum automaton records:";
 Locate 10,4
 Print "Automaton records in use: ";
 Color 15,1
 Print "0";

 Color 14,1
 Locate 8,40
 Print "Monsters/second: ";
 Color 15,1
 Print "0";

 Color 14,1
 Locate 9,9
 Print "Monsters died: ";
 Color 15,1
 Print "0";

 Color 14,1
 Locate 9,45
 Print "Monsters moved: ";
 Color 15,1
 Print "0";

 Color 14,1
 Locate 10,40
 Print "Message level:";
 Locate 23,4
 Print "Press <esc> to exit automaton mover. <up>/<down> changes message level.";
 Color 7,0
 For Temp5=12 To 20
    Locate Temp5,4
    Print Space$(74);
 Next
Error.Resume8x:
 Exit Sub
Error.Trap8x:
 Resume Error.Resume8x
End Sub

' menu box scrolling subroutine for realtime automaton moving utility.
Sub Util.Screen3(Var$)
 On Local Error Goto Error.Trap8xa
 Call VideoInt2 ' scroll display area
 Var$=Left$(Var$,74)
 Color 7,0
 Locate 20,4
 Print Var$;
Error.Resume8xa:
 Exit Sub
Error.Trap8xa:
 Resume Error.Resume8xa
End Sub

' screen display for realtime node monitoring utility.
Sub Util.Screen4(Var)
 On Local Error Goto Error.Trap8xb
 Color 14,1
 Select Case Var
 Case 1
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+2)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,3
       Print "N#";Mid$(Str$(Temp5),2);" ";
    Next
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+11)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,40
       If Temp5=0 Then
          Print "N#9 ";
       Else
          Print "N#";Chr$(Temp5+64);" ";
       Endif
    Next
 Case 2
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+20)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,3
       Print "N#";Chr$(Temp5+73);" ";
    Next
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+29)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,40
       Print "N#";Chr$(Temp5+82);" ";
    Next
 Case 3
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+38)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,3
       Print "N#";Mid$(Str$(Temp5+10),2);
    Next
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+47)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,40
       Print "N#";Mid$(Str$(Temp5+19),2);
    Next
 Case 4
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+56)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,3
       Print "N#";Mid$(Str$(Temp5+28),2);
    Next
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+65)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,40
       Print "N#";Mid$(Str$(Temp5+37),2);
    Next
 Case 5
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+74)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,3
       Print "N#";Mid$(Str$(Temp5+46),2);
    Next
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+83)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,40
       Print "N#";Mid$(Str$(Temp5+55),2);
    Next
 Case 6
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+92)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,3
       Print "N#";Mid$(Str$(Temp5+64),2);
    Next
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+101)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,40
       Print "N#";Mid$(Str$(Temp5+73),2);
    Next
 Case 7
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+110)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,3
       Print "N#";Mid$(Str$(Temp5+82),2);
    Next
    For Temp5=0 To 8
       Call Read.Message.Record(MessWorkFile1,Temp5+119)
       If MessWorkRecord1.ActiveNode Then
          Color 15,1
       Else
          Color 14,1
       Endif
       Locate Temp5+13,40
       Print "N#";Mid$(Str$(Temp5+91),2);
    Next
 End Select
Error.Resume8xb:
 Exit Sub
Error.Trap8xb:
 Resume Error.Resume8xb
End Sub

' automaton database editor.
Sub Edit.Automatons
 On Local Error Goto Error.Trap10
 Call Get.Config
 Close
 Gosub Get.Config
 Call Open.Automate.File
 Call Open.Monster.File
 If Lof(AutomateFile)=SFalse Then
    Call Add.Automate
 Endif
 Call Edit.Status.Line
 Do
    Color 15,0
    Print "Automaton edit utility v"+Left$(Version$,4)+" menu: ";
    Color 14,0
    If Debug.Mode Then
       Print "(records:";Str$(Lof(AutomateFile)/AutomateRecLen);")";
    Endif
    Print
    If option1 then
       Print "[A]dd"
    Else
       Print "[*]dd"
    Endif
    If option2 then
       Print "[C]hange"
    Else
       Print "[*]hange"
    Endif
    If option3 then
       Print "[D]elete"
    Else
       Print "[*]elete"
    Endif
    If option4 then
       Print "[L]ist"
    Else
       Print "[*]ist"
    Endif
    If option5 then
       Print "[P]ack"
    Else
       Print "[*]ack"
    Endif
    If option6 then
       Print "[U]ndelete"
    Else
       Print "[*]ndelete"
    Endif
    If option7 then
       Print "[X]create"
    Else
       Print "[*]create"
    Endif
    Color 15,0
    Print "Automaton edit option(q to quit)? ";
    Out2 = KeyboardLine$
    If Out2=Chr$(0)+Chr$(79) Then ' end
       Out2="q"
    Endif
    If Out2=Nul Then
       Out2="q"
       Print "q";
    Endif
    Print
    Select Case Ucase$(Out2)
    Case "A"
       If option1=0 Then
          Print "Option restricted."
       Else
          If Lof(AutomateFile)/AutomateRecLen>=MaxAutomatons Then
             Print "Already"+Str$(MaxAutomatons)+" records.."
          Else
             Call Add.Automate
             Temp5!=Lof(AutomateFile)/AutomateRecLen
             Call Modify.Automate
          Endif
       Endif
    Case "C"
       If option2=0 Then
          Print "Option restricted."
       Else
          Call Change.Automate
       Endif
    Case "D"
       If option3=0 Then
          Print "Option restricted."
       Else
          Call Remove.Automate
       Endif
    Case "L"
       If option4=0 Then
          Print "Option restricted."
       Else
          Call List.Automate
       Endif
    Case "P"
       If option5=0 Then
          Print "Option restricted."
       Else
          Call Pack.Automate
       Endif
    Case "U"
       If option6=0 Then
          Print "Option restricted."
       Else
          Call Undelete.Automate
       Endif
    Case "X"
       If option7=0 Then
          Print "Option restricted."
       Else
          Call Create.Automatons
       Endif
    Case "Q"
       Exit Do
    End Select
 Loop
Error.Resume10:
 Call Clear.Status.Line
 Exit Sub

' reads in config file for edit utility.
Get.Config:
 option1=-1
 option2=-1
 option3=-1
 option4=-1
 option5=-1
 option6=-1
 option7=-1
 Filename=DND.Path+"DNDAUTO.CFG"
 If Dir$(Filename)=Nul Then Return
 Close #TempFile1
 Open Filename For Input As #TempFile1
 Do
    If Eof(TempFile1) Then
       Exit Do
    Endif
    Line Input #TempFile1,Data.Line$
    Data.Line$=Rtrim$(Ltrim$(Lcase$(Data.Line$)))
    If Len(Data.Line$) Then
       If Left$(Data.Line$,1)<>";" Then
          VarX=Instr(Data.Line$,"=")
          If VarX Then
             Var1$=Left$(Data.Line$,VarX-1)
             Var2$=Mid$(Data.Line$,VarX+1)
             Var1$=Rtrim$(Ltrim$(Var1$))
             Var2$=Rtrim$(Ltrim$(Var2$))
             If Var1$="option1" Or Var1$="add.menu" Then
                Select Case Var2$
                Case "0","false","off"
                   option1=0
                End Select
             Endif
             If Var1$="option2" Or Var1$="change.menu" Then
                Select Case Var2$
                Case "0","false","off"
                   option2=0
                End Select
             Endif
             If Var1$="option3" Or Var1$="delete.menu" Then
                Select Case Var2$
                Case "0","false","off"
                   option3=0
                End Select
             Endif
             If Var1$="option4" Or Var1$="list.menu" Then
                Select Case Var2$
                Case "0","false","off"
                   option4=0
                End Select
             Endif
             If Var1$="option5" Or Var1$="pack.menu" Then
                Select Case Var2$
                Case "0","false","off"
                   option5=0
                End Select
             Endif
             If Var1$="option6" Or Var1$="undelete.menu" Then
                Select Case Var2$
                Case "0","false","off"
                   option6=0
                End Select
             Endif
             If Var1$="option7" Or Var1$="create.menu" Then
                Select Case Var2$
                Case "0","false","off"
                   option7=0
                End Select
             Endif
          Endif
       Endif
    Endif
 Loop
 Return
Error.Trap10:
 Resume Error.Resume10
End Sub

Sub Create.Automatons
 On Local Error Goto Error.Trap11x
 Gosub Get.Config1
 If option1!>SFalse And option1!<Lof(MonsterFile)/MonsterRecLen Then
    Eat$=""
 Else
    option1!=1!
 Endif
 If option2!>SFalse And option2!<Lof(MonsterFile)/MonsterRecLen Then
    Eat$=""
 Else
    option2!=10!
 Endif
 Print "Enter automatons to add to file(1-"+Mid$(Str$(MaxAutomatons),2)+"):";
 Out2 = KeyboardLine$
 If Out2=Chr$(0)+Chr$(79) Then ' end
    Print
    Exit Sub
 Endif
 X! = CSNG(INT(VAL(Out2)))
 Print
 If X!>=1! And X!<=MaxAutomatons Then
    If Lof(AutomateFile)/AutomateRecLen+X!>MaxAutomatons Then
       X!=MaxAutomatons-Lof(AutomateFile)/AutomateRecLen
    Endif
 Endif
 If X!<=SFalse Then
    Print "Already"+Str$(MaxAutomatons)+" automatons.."
    Exit Sub
 Endif
 If X!>=1! And X!<=MaxAutomatons Then
    Print "Creating";X!;"automatons.."
    For Var1!=1! To X!
       Call Add.Automate
       Temp5!=Lof(AutomateFile)/AutomateRecLen
       AutomateRecord.MonsterIndex=Random1!(option1!,option2!)
       AutomateRecord.Deleted=False
       AutomateRecord.StartRoom=Random1!(option3!,option4!)
       AutomateRecord.StopRoom=Random1!(option5!,option6!)
       AutomateRecord.StartEvery=TimeSerial(optionx1,optionx2,optionx3)
       AutomateRecord.StartTime=TimeSerial(options1,options2,options3)
       AutomateRecord.StopTime=TimeSerial(options4,options5,options6)
       AutomateRecord.StopAfterMoves=option7
       AutomateRecord.MoveEvery=TimeSerial(options7,options8,options9)
       AutomateRecord.MovesRandomly=True
       AutomateRecord.NumberMoves=False
       AutomateRecord.LastMoveTime=DFalse
       AutomateRecord.MessWorkIndex=SFalse
       Call Write.Record(AutomateFile,Temp5!)
    Next
 Endif
 Exit Sub

' reads in config file for create utility.
Get.Config1:
  ' monster index
 option1!=1!
 option2!=10!
 ' start room
 option3!=1!
 option4!=100!
 ' stop room
 option5!=1!
 option6!=100!
 ' stop after moves
 option7=3
 ' start every
 optionx1=0 : optionx2=0 : optionx3=10
 ' start time
 options1=15 : options2=0 : options3=0
 ' stop time
 options4=23 : options5=59 : options6=59
 ' moves every
 options7=0 : options8=0 : options9=3
 ' read config file
 Filename=DND.Path+"AUTOMATE.CFG"
 If Dir$(Filename)=Nul Then Return
 Close #TempFile1
 Open Filename For Input As #TempFile1
 Do
    If Eof(TempFile1) Then
       Exit Do
    Endif
    Line Input #TempFile1,Data.Line$
    Data.Line$=Rtrim$(Ltrim$(Lcase$(Data.Line$)))
    If Len(Data.Line$) Then
       If Left$(Data.Line$,1)<>";" Then
          VarX=Instr(Data.Line$,"=")
          If VarX Then
             Var1$=Left$(Data.Line$,VarX-1)
             Var2$=Mid$(Data.Line$,VarX+1)
             Var1$=Rtrim$(Ltrim$(Var1$))
             Var2$=Rtrim$(Ltrim$(Var2$))

             If Var1$="monstermin" Then
                option1!=int(val(Var2$))
             Endif
             If Var1$="monstermax" Then
                option2!=int(val(Var2$))
             Endif

             If Var1$="startmin" Then
                option3!=int(val(Var2$))
             Endif
             If Var1$="startmax" Then
                option4!=int(val(Var2$))
             Endif

             If Var1$="stopmin" Then
                option5!=int(val(Var2$))
             Endif
             If Var1$="stopmax" Then
                option6!=int(val(Var2$))
             Endif

             If Var1$="stopaftermoves" Then
                If int(val(Var2$))>=1 And int(val(Var2$))<=32767 Then
                   option7=int(val(Var2$))
                Endif
             Endif

             If Var1$="startevery" Then
                If Valid.Time(Var2$) Then
                   optionx1=val(mid$(var2$,1,2))
                   optionx2=val(mid$(var2$,4,2))
                   optionx3=val(mid$(var2$,7,2))
                Endif
             Endif

             If Var1$="starttime" Then
                If Valid.Time(Var2$) Then
                   options1=val(mid$(var2$,1,2))
                   options2=val(mid$(var2$,4,2))
                   options3=val(mid$(var2$,7,2))
                Endif
             Endif

             If Var1$="stoptime" Then
                If Valid.Time(Var2$) Then
                   options4=val(mid$(var2$,1,2))
                   options5=val(mid$(var2$,4,2))
                   options6=val(mid$(var2$,7,2))
                Endif
             Endif

             If Var1$="moveevery" Then
                If Valid.Time(Var2$) Then
                   options7=val(mid$(var2$,1,2))
                   options8=val(mid$(var2$,4,2))
                   options9=val(mid$(var2$,7,2))
                Endif
             Endif

          Endif
       Endif
    Endif
 Loop
 Return
Error.Resume11x:
 Exit Sub
Error.Trap11x:
 Resume Error.Resume11x
End Sub

' returns a random number between 2 values
FUNCTION Random1! (x!, y!)
   If x! < 1! Then x! = 1! ' check boundaries
   IF y! < x! THEN y! = x!
   z! = y! - x! + 1! ' difference
   Random1! = INT(RND * z! + x!)
END FUNCTION

' 00:00:00 - 23:59:59
Function Valid.Time(V$)
 Valid.Time=-1
 If Len(V$)<>8 Then
    Valid.Time=0
    Exit Function
 Endif
 ' scan all chars first
 For V=1 To 8
    Select Case V
    Case 3, 6
       If Mid$(V$,V,1)<>":" Then
          Valid.Time=0
          Exit Function
       Endif
    Case Else
       If Mid$(V$,V,1)>="0" And Mid$(V$,V,1)<="9" Then
          Eat$ = Nul
       Else
          Valid.Time=0
          Exit Function
       Endif
    End Select
 Next
 ' scan values next
 If Val(Mid$(V$,1,2))>=0 And Val(Mid$(V$,1,2))<=23 Then
    Eat$=""
 Else
    Valid.Time=0
    Exit Function
 Endif
 If Val(Mid$(V$,4,2))>=0 And Val(Mid$(V$,4,2))<=59 Then
    Eat$=""
 Else
    Valid.Time=0
    Exit Function
 Endif
 If Val(Mid$(V$,7,2))>=0 And Val(Mid$(V$,7,2))<=59 Then
    Eat$=""
 Else
    Valid.Time=0
    Exit Function
 Endif
End Function

Sub Add.Automate
 On Local Error Goto Error.Trap11
 Temp5!=Lof(AutomateFile)/AutomateRecLen+1!
 Call Delete.Automate
 Call Write.Record(AutomateFile,Temp5!)
Error.Resume11:
 Exit Sub
Error.Trap11:
 Resume Error.Resume11
End Sub

Sub Change.Automate
 On Local Error Goto Error.Trap12
 Call Find.Automate
 If Temp5! Then
    Call Modify.Automate
 Endif
Error.Resume12:
 Exit Sub
Error.Trap12:
 Resume Error.Resume12
End Sub

Sub Find.Automate
 On Local Error Goto Error.Trap13
 Var3!=Lof(AutomateFile)/AutomateRecLen
 Var$=Mid$(Str$(Var3!),2)
 If Var3!=SFalse Then
    Var$="(0-"+Var$+")"
 Else
    Var$="(1-"+Var$+")"
 Endif
 Color 15,0
 Print "Automaton name, or number"+Var$+"? ";
 Out2 = KeyboardLine$
 If Out2=Chr$(0)+Chr$(79) Then ' end
    Out2=Nul
 Endif
 If Out2=Nul Then
    Out2="1"
    Print "1";
 Endif
 Print
 Temp7!=SFalse
 Out2=Lcase$(Out2)
 If Instr(Out2,"#") Then
    Temp7!=Int(Val(Mid$(Out2,Instr(Out2,"#")+1)))
    Out2=Left$(Out2,Instr(Out2,"#")-1)
 Endif
 TempX!=SFalse
 No.Input=False
 For Temp5!=1! To Lof(AutomateFile)/AutomateRecLen
    Call Read.Record(AutomateFile,Temp5!)
    Var2!=AutomateRecord.MonsterIndex
    If Var2!>SFalse And Var2!<Lof(MonsterFile)/MonsterRecLen Then
       Call Read.Record(MonsterFile,Var2!)
       Strng=Rtrim$(MonsterRecord.MonsterName)
       Var3$=Out2
       Call InstrSUB1(VarX,Var3$,0)
       If VarX Then
          TempX!=TempX!+1!
          If Temp7!=SFalse Or TempX!=Temp7! Then
             No.Input=True
             Exit For
          Endif
       Endif
    Endif
 Next
 If No.Input=False Then
    Temp5!=Int(Val(Out2))
    If Temp5!>SFalse And Temp5!<=Lof(AutomateFile)/AutomateRecLen Then
       Call Read.Record(AutomateFile,Temp5!)
       No.Input=True
    Endif
 Endif
 If No.Input=False Then
    Temp5!=False
    Print "Automaton name not found."
 Endif
Error.Resume13:
 Exit Sub
Error.Trap13:
 Resume Error.Resume13
End Sub

Sub Remove.Automate
 On Local Error Goto Error.Trap14
 Call Find.Automate
 If Temp5!=SFalse Then
    Exit Sub
 Endif
 Call Read.Record(AutomateFile,Temp5!)
 If AutomateRecord.Deleted Then
    Print "Automaton already deleted."
    Exit Sub
 Endif
 Print "Delete automaton"+Str$(Temp5!)+". Are you sure(y/n)? ";
 Out2 = KeyboardLine$
 If Out2=Chr$(0)+Chr$(79) Then ' end
    Out2=Nul
 Endif
 If Out2=Nul Then
    Out2="n"
    Print "n";
 Endif
 Print
 If Ucase$(Out2)="Y" Then
    AutomateRecord.Deleted=True
    Call Write.Record(AutomateFile,Temp5!)
    Print "Automaton"+Str$(Temp5!)+" deleted."
 Endif
Error.Resume14:
 Exit Sub
Error.Trap14:
 Resume Error.Resume14
End Sub

Sub Undelete.Automate
 On Local Error Goto Error.Trap15
 Call Find.Automate
 If Temp5!=SFalse Then
    Exit Sub
 Endif
 Call Read.Record(AutomateFile,Temp5!)
 If AutomateRecord.Deleted=False Then
    Print "Automaton not deleted."
    Exit Sub
 Endif
 AutomateRecord.Deleted=False
 Call Write.Record(AutomateFile,Temp5!)
 Print "Automaton"+Str$(Temp5!)+" undeleted."
Error.Resume15:
 Exit Sub
Error.Trap15:
 Resume Error.Resume15
End Sub

Sub Delete.Automate
 On Local Error Goto Error.Trap16
 AutomateRecord.MonsterIndex=SFalse
 AutomateRecord.Deleted=False
 AutomateRecord.StartRoom=SFalse
 AutomateRecord.StopRoom=SFalse
 AutomateRecord.StartEvery=DFalse
 AutomateRecord.StartTime=DFalse
 AutomateRecord.StopTime=DFalse
 AutomateRecord.StopAfterMoves=False
 AutomateRecord.MoveEvery=DFalse
 AutomateRecord.MovesRandomly=False
 AutomateRecord.NumberMoves=False
 AutomateRecord.LastMoveTime=DFalse
 AutomateRecord.MessWorkIndex=SFalse
Error.Resume16:
 Exit Sub
Error.Trap16:
 Resume Error.Resume16
End Sub

Sub Modify.Automate
 On Local Error Goto Error.Trap17
 Call Read.Record(AutomateFile,Temp5!)
 Do
    For VarQ=1 To 10
       Call Display.Automate(True,VarQ)
    Next
    Color 15,0
    Print "Automaton change option(q to quit)? ";
    Out2 = Ucase$(KeyboardLine$)
    If Out2=Chr$(0)+Chr$(79) Then ' end
       Out2=Nul
    Endif
    If Out2=Nul Then
       Out2="Q"
       Print "q";
    Endif
    Print
    Color 14,0
    Select Case Out2
    Case "A"
       TempZ!=Lof(MonsterFile)/MonsterRecLen
       Print "Enter monster name/number(1-"+Mid$(Str$(TempZ!),2)+"):";
       Out3 = Lcase$(KeyboardLine$)
       If Out3=Chr$(0)+Chr$(79) Then ' end
          Out3=Nul
       Endif
       Print
       Var=False
       If Len(Out3) Then
          Temp!=Int(Val(Out3))
          If Temp!>=1! And Temp!<=Lof(MonsterFile)/MonsterRecLen Then
             Var=True
          Else
             VarZ2=0
             VarZ=Instr(Out3,"#")
             If VarZ Then
                VarZ2=Int(Val(Mid$(Out3,VarZ+1)))
                Out3=Left$(Out3,VarZ-1)
             Endif
             VarZ1=0   
             For Temp!=1! To Lof(MonsterFile)/MonsterRecLen
                Call Read.Record(MonsterFile,Temp!)
                Strng=Rtrim$(MonsterRecord.MonsterName)
                Var3$=Out3
                Call InstrSUB1(VarX,Var3$,0)
                If VarX Then
                   If VarZ2 Then
                      VarZ1=VarZ1+1
                      If VarZ1=VarZ2 Then
                         Var=True
                         Exit For
                      Endif
                   Else
                      Var=True
                      Exit For
                   Endif
                Endif
             Next
          Endif
       Endif
       If Var Then
          AutomateRecord.MonsterIndex=Temp!
          Print "Monster number changed."
       Else
          Print "Monster name not found."
       Endif
    Case "B"
       Print "Enter start room number:";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       X! = CSNG(INT(VAL(Out2)))
       Print
       If X!>=SFalse Then
          AutomateRecord.StartRoom=X!
       Endif
    Case "C"
       Print "Enter stop room number:";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       X! = CSNG(INT(VAL(Out2)))
       Print
       If X!>=SFalse Then
          AutomateRecord.StopRoom=X!
       Endif
    Case "D"
       Print "Enter every starting hour:";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       H = INT(VAL(Out2))
       Print
       Print "Enter every starting minute:";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       M = INT(VAL(Out2))
       Print
       Print "Enter every starting second:";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       S = INT(VAL(Out2))
       Print
       AutomateRecord.StartEvery=TimeSerial(H,M,S)
    Case "E"
       Print "Enter starting hour:";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       H = INT(VAL(Out2))
       Print
       Print "Enter starting minute:";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       M = INT(VAL(Out2))
       Print
       Print "Enter starting second:";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       S = INT(VAL(Out2))
       Print
       AutomateRecord.StartTime=TimeSerial(H,M,S)
    Case "F"
       Print "Enter stopping hour:";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       H = INT(VAL(Out2))
       Print
       Print "Enter stopping minute:";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       M = INT(VAL(Out2))
       Print
       Print "Enter stopping second:";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       S = INT(VAL(Out2))
       Print
       AutomateRecord.StopTime=TimeSerial(H,M,S)
    Case "G"
       Print "Stops after moves(0-32767):";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       X = INT(VAL(Out2))
       Print
       If X>=False And X<=32767 Then
          AutomateRecord.StopAfterMoves=X
       Endif
    Case "H"
       Print "Moves every hour(0-23):";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       H = INT(VAL(Out2))
       Print
       Print "Moves every minutes(0-60):";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       M = INT(VAL(Out2))
       Print
       Print "Moves every seconds(0-60):";
       Out2 = KeyboardLine$
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       S = INT(VAL(Out2))
       Print
       AutomateRecord.MoveEvery=TimeSerial(H,M,S)
    Case "I"
       Print "Moves randomly(y/n)?";
       Out2 = KeyboardLine$
       Print
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Print
          Exit Do
       Endif
       X$ = Ucase$(Out2)
       If Left$(X$,1)="Y" Then
          AutomateRecord.MovesRandomly=True
       Else
          AutomateRecord.MovesRandomly=False
       Endif
    Case "Q"
       Exit Do
    End Select
 Loop
 Call Write.Record(AutomateFile,Temp5!)
 Print "Automaton"+Str$(Temp5!)+" modified."
Error.Resume17:
 Exit Sub
Error.Trap17:
 Resume Error.Resume17
End Sub

Sub List.Automate
 On Local Error Goto Error.Trap18
 Temp3!=Lof(AutomateFile)/AutomateRecLen
 Print "Enter range:"
 Var$=Mid$(Str$(Temp3!),2)
 If Temp3!=SFalse Then
    Strng="From(0-"+Var$+")? "
 Else
    Strng="From(1-"+Var$+")? "
 Endif
 Print Strng;
 Out2 = KeyboardLine$
 If Out2=Chr$(0)+Chr$(79) Then ' end
    Out2=Nul
 Endif
 If Out2=Nul Then
    Temp!=1!
    Print "1";
 Else
    Temp!=Int(Val(Out2))
 Endif
 Print
 If Temp!<1! Then
    Temp!=1!
 Endif
 If Temp!>Temp3! Then
    Temp!=Temp3!
 Endif
 Print "To("+Mid$(Str$(Temp!),2)+"-"+Var$+")? ";
 Out2 = KeyboardLine$
 If Out2=Chr$(0)+Chr$(79) Then ' end
    Out2=Nul
 Endif
 If Out2=Nul Then
    Temp2!=Temp3!
    Print Temp2!;
 Else
    Temp2!=Int(Val(Out2))
 Endif
 Print
 If Temp2!<Temp! Then
    Temp2!=Temp!
 Endif
 If Temp2!>Temp3! Then
    Temp2!=Temp3!
 Endif
 c=0
 For Temp5!=Temp! To Temp2!
    Call Read.Record(AutomateFile,Temp5!)
    For VarQ=1 To 10
       Call Display.Automate(False,VarQ)
    Next
    If c=0 Then
       Color 15,0
       Print "More(y/n/c)? ";
       Out2 = Ucase$(KeyboardLine$)
       If Out2=Chr$(0)+Chr$(79) Then ' end
          Out2="N"
       Endif
       If Out2=Nul Then
          Print "y";
       Endif
       Print
       If Out2="C" Then
          c=-1
       Endif
       If Out2="N" Then
          Exit For
       Endif
    Endif
 Next
Error.Resume18:
 Exit Sub
Error.Trap18:
 Resume Error.Resume18
End Sub

Sub Display.Automate(TempX,Var)
 On Local Error Goto Error.Trap19
 Select Case Var
 Case 1
 Color 15,0
 Strng="Automaton number"+Str$(Temp5!)+":"
 If AutomateRecord.Deleted Then
    Strng=Strng+" (deleted)"
 Endif
 Print Strng
 Color 14,0
 Case 2
 Strng=Nul
 If TempX Then
    Strng="[A]"
 Endif
 Strng=Strng+"Monster: "
 Var2!=AutomateRecord.MonsterIndex
 If Var2!>SFalse And Var2!<=Lof(MonsterFile)/MonsterRecLen Then
    Call Read.Record(MonsterFile,Var2!)
    Strng=Strng+Rtrim$(MonsterRecord.MonsterName)
    Strng=Strng+" ("+Mid$(Str$(Var2!),2)+")"
 Endif
 Print Strng
 Case 3
 Strng=Nul
 If TempX Then
    Strng="[B]"
 Endif
 Print Strng;"Start room number:";AutomateRecord.StartRoom
 Case 4
 Strng=Nul
 If TempX Then
    Strng="[C]"
 Endif
 Print Strng;"Stop room number:";AutomateRecord.StopRoom
 Case 5
 Strng=Nul
 If TempX Then
    Strng="[D]"
 Endif
 Print Strng;"Starts every(HH:MM:SS): ";
 Print Right$(Str$(Hour(AutomateRecord.StartEvery)+100),2);":";
 Print Right$(Str$(Minute(AutomateRecord.StartEvery)+100),2);":";
 Print Right$(Str$(Second(AutomateRecord.StartEvery)+100),2)
 Case 6
 Strng=Nul
 If TempX Then
    Strng="[E]"
 Endif
 Print Strng;"Starting time(HH:MM:SS): ";
 Print Right$(Str$(Hour(AutomateRecord.StartTime)+100),2);":";
 Print Right$(Str$(Minute(AutomateRecord.StartTime)+100),2);":";
 Print Right$(Str$(Second(AutomateRecord.StartTime)+100),2)
 Case 7
 Strng=Nul
 If TempX Then
    Strng="[F]"
 Endif
 Print Strng;"Stopping time(HH:MM:SS): ";
 Print Right$(Str$(Hour(AutomateRecord.StopTime)+100),2);":";
 Print Right$(Str$(Minute(AutomateRecord.StopTime)+100),2);":";
 Print Right$(Str$(Second(AutomateRecord.StopTime)+100),2)
 Case 8
 Strng=Nul
 If TempX Then
    Strng="[G]"
 Endif
 Print Strng;"Stops after:";
 Print AutomateRecord.StopAfterMoves;"moves."
 Case 9
 Strng=Nul
 If TempX Then
    Strng="[H]"
 Endif
 Print Strng;"Moves every(HH:MM:SS): ";
 Print Right$(Str$(Hour(AutomateRecord.MoveEvery)+100),2);":";
 Print Right$(Str$(Minute(AutomateRecord.MoveEvery)+100),2);":";
 Print Right$(Str$(Second(AutomateRecord.MoveEvery)+100),2)
 Case 10
 Strng=Nul
 If TempX Then
    Strng="[I]"
 Endif
 Print Strng;"Moves randomly: ";
 If AutomateRecord.MovesRandomly Then
    Print "Yes"
 Else
    Print "No"
 Endif
 End Select
Error.Resume19:
 Exit Sub
Error.Trap19:
 Resume Error.Resume19
End Sub

Sub Pack.Automate
 On Local Error Goto Error.Trap20
 Print "Pack automaton file(y/n)? ";
 Out2 = KeyboardLine$
 If Out2=Chr$(0)+Chr$(79) Then ' end
    Out2="N"
 Endif
 If Out2=Nul Then
    Out2="y"
    Print "y";
 Endif
 Print
 If Ucase$(Out2)<>"Y" Then
    Exit Sub
 Endif
 Print "Making backup of automaton file."
 FileName=Data.Path1+"Automate.bak"
 Close #TempFile1
 If Dir$(Filename)="AUTOMATE.BAK" Then
    Kill FileName
 Endif
 Open FileName For Random Shared As #TempFile1 Len=AutomateRecLen
 For Temp3!=1! To Lof(AutomateFile)/AutomateRecLen
    Call Read.Record(AutomateFile,Temp3!)
    Call Write.Record(TempFile1,Temp3!)
 Next
 Temp1!=SFalse
 FileName=Data.Path1+"Automate"+File.Extension1
 Close #AutomateFile
 Kill FileName
 Open FileName For Random Shared As #AutomateFile Len=AutomateRecLen
 For Temp3!=1! To Lof(TempFile1)/AutomateRecLen
    Call Read.Record(TempFile1,Temp3!)
    If AutomateRecord.Deleted=False Then
       Temp1!=Temp1!+1!
       Call Write.Record(AutomateFile,Temp1!)
    Endif
 Next
 Close #TempFile1
 Print "Automaton file packed."
Error.Resume20:
 Exit Sub
Error.Trap20:
 Resume Error.Resume20
End Sub

' reads in config file for automaton moving utility.
Sub Read.Dndmove.Cfg
 On Local Error Goto ErrResume13x
 Temp1=False
 Filename=DND.Path+"DNDMOVE.CFG"
 If Dir$(Filename)=Nul Then Exit Sub
 Close #TempFile1
 Open Filename For Input As #TempFile1
 Do
    If Eof(TempFile1) Then
       Exit Do
    Endif
    Line Input #TempFile1,Data.Line$
    Data.Line$=Rtrim$(Ltrim$(Ucase$(Data.Line$)))
    If Len(Data.Line$) Then
       If Left$(Data.Line$,1)<>";" Then
          VarX=Instr(Data.Line$,"=")
          If VarX Then
             Var1$=Left$(Data.Line$,VarX-1)
             Var2$=Mid$(Data.Line$,VarX+1)
             Var1$=Rtrim$(Ltrim$(Var1$))
             Var2$=Rtrim$(Ltrim$(Var2$))
             If Var1$="MESSAGELEVEL" Then
                Select Case Var2$
                Case "OFF", "0"
                   Temp1=False
                Case "LOW", "1"
                   Temp1=1
                Case "MEDIUM", "2"
                   Temp1=2
                Case "HIGH", "3"
                   Temp1=3
                End Select
             Endif
          Endif
       Endif
    Endif
 Loop
ErrExit13x:
 Exit Sub
ErrResume13x:
 Resume ErrExit13x
End Sub

' realtime automaton moving utility menu.
Sub Move.Automatons
 On Local Error Goto Error.Trap21
 ' read config file
 Call Get.Config
 ' open files
 Close
 Call Open.Mess1.File
 Call Open.Mess2.File
 Call Open.Mess3.File
 Call Open.Mess5.File
 Call Open.Automate.File
 Call Open.Moninv.File
 Call Open.Monster.File
 Call Open.Room.File
 Call Open.Treasure.File
 ' check record length
 If Lof(AutomateFile)=SFalse Then
    Exit Sub
 Endif
 ' display menu
 Call Util.Screen2
 ' reset counter
 Temp0=False
 ' get max automatons
 If Lof(AutomateFile)/AutomateRecLen>MaxAutomatons Then
    VarZ=MaxAutomatons
 Else
    VarZ=Lof(AutomateFile)/AutomateRecLen
 Endif
 ' redimension arrays
 Redim AutomateArray(1 To VarZ) As AutomateType
 Redim Temp.Array1(1 To VarZ) As Integer ' IsActive
 Redim Temp.Array3(1 To VarZ) As Single ' CurrentRoom
 ' display startup value
 Locate 8,30
 Color 15,1
 Print VarZ;
 ' reset message level
 Temp1=3
 Temp0!=0!
 Temp1!=0!
 TempB!=0!
 TempC!=0!
 ' read .cfg file
 Call Read.Dndmove.Cfg
 Gosub Display.Level
 ' read in automate.dat file
 For VarY=1 To VarZ
    VarQ!=Csng(VarY)
    Call Read.Record(AutomateFile,VarQ!)
    AutomateArray(VarY)=AutomateRecord ' 64 bytes
    AutomateArray(VarY).LastMoveTime=Now ' init timing
 Next
 VarT1!=Timeit!
 VarT2!=SFalse
 ' start automaton loop
 Do
    ' process automaton array
    For VarY=1 To VarZ
       ' get keystroke
       X$=Inkey$
       If Len(X$) Then
          ' check exit key
          If X$=Chr$(27) Then ' escape
             Exit Do
          Endif
          If X$=Chr$(3) Then ' ctrl-c
             Exit Do
          Endif
          ' check up/down key
          If Len(X$)=2 Then
             X=Asc(Right$(X$,1))
             Select Case X
             Case 0 ' Control-Break
                Exit Do
             Case 71, 119 ' home
                Temp1=0
                Gosub Display.Level
             Case 79, 117 ' end
                Temp1=3
                Gosub Display.Level
             CASE 77, 72, 116, 157, 141, 132, 152 ' right/up
                If Temp1<3 Then
                   Temp1=Temp1+1
                   Gosub Display.Level
                Endif
             CASE 15, 75, 80, 115, 155, 145, 118, 160 ' shift-tab/left/down
                If Temp1>False Then
                   Temp1=Temp1-1
                   Gosub Display.Level
                Endif
             End Select
          Endif
       Endif
       ' display monsters moved per second
       If Time.Elapsed(VarT1!,1!) Then
          VarT1!=Timeit!
          Locate 8,56
          Color 15,1
          If VarT2!>SFalse Then
             Print VarT2!;"    ";
             VarT2!=SFalse
          Endif
          Locate 9,23
          Color 15,1
          VarQ#=TempC!*1000000+Temp1!
          Print VarQ#;"          ";
          Locate 9,60
          Color 15,1
          VarQ#=TempB!*1000000+Temp0!
          Print VarQ#;"           ";
          VarT2!=SFalse
          Locate 4,69
          Time1$=Format$(Now,"hh:mm:ss")
          Print Time1$;
       Endif
       If AutomateArray(VarY).Deleted=False Then
          ' check for dead monster
          If Temp.Array1(VarY) Then
             Choice=AutomateArray(VarY).MessWorkIndex
             Call Read.Record(MessWorkFile3,Choice)
             If MessWorkRecord3.IsAutomaton=VarY Then
                If MessWorkRecord3.Room=SFalse Then
                   Call Dead.Automaton(VarY)
                Endif
             Endif
          Endif
          ' check inactive monsters
          If Temp.Array1(VarY)=False Then
             ' start automaton
             If AutomateArray(VarY).StartTime=Now Then
                Call Start.Monster(VarY)
             Else
                If SerialTimeElapsed(AutomateArray(VarY).LastMoveTime, _
                AutomateArray(VarY).StartEvery) Then
                   Call Start.Monster(VarY)
                Endif
             Endif
          Endif
          ' check active monsters
          If Temp.Array1(VarY) Then
             ' stop automaton
             If AutomateArray(VarY).StopTime=Now Then
                Call Stop.Monster(VarY,False)
             Else
                If AutomateArray(VarY).StopAfterMoves>False Then
                   If AutomateArray(VarY).NumberMoves> _
                   AutomateArray(VarY).StopAfterMoves Then
                      Call Stop.Monster(VarY,False)
                   Endif
                Endif
             Endif
          Endif
          ' recheck active monsters
          If Temp.Array1(VarY) Then
             ' move automaton
             If SerialTimeElapsed(AutomateArray(VarY).LastMoveTime, _
             AutomateArray(VarY).MoveEvery) Then
                VarT2!=VarT2!+1!
                Call Move.Monster(VarY)
             Endif
          Endif
       Endif
    Next
    Call Release.Time(1)
 Loop
 ' remove all automatons
 For VarY=1 To VarZ
    If AutomateArray(VarY).Deleted=False Then
       If Temp.Array1(VarY) Then
          Call Stop.Monster(VarY,True)
       Endif
    Endif
 Next
 Exit Sub
Display.Level:
 Locate 10,55
 Color 15,1
 Select Case Temp1
 Case 0
    Print "Off    ";
 Case 1
    Print "Low    ";
 Case 2
    Print "Medium ";
 Case 3
    Print "High   ";
 End Select
 Return
Error.Resume21:
 Exit Sub
Error.Trap21:
 Color 7,0
 Cls
 Color 14,0
 Print "Dndutil automaton mover terminated with error"+Str$(Err)+"."
 Color 7,0
 Print "Returning to system."
 End
End Sub

Sub Dead.Automaton(VarY)
 On Local Error Goto Error.Trap21x
 Temp1!=Temp1!+1!
 If Temp1!>=999999! Then
    TempC!=TempC!+1!
    Temp1!=0!
 Endif
 MessWorkRecord3.IsAutomaton=False
 Call Write.Record(MessWorkFile3,Choice)
 AutomateArray(VarY).LastMoveTime=Now
 Temp.Array1(VarY)=False
 Temp0=Temp0-1
 Locate 10,29
 Color 15,1
 Print Temp0;
 If Temp1>False Then
    Var$="Monster died:"+Str$(VarY)+", index:"+Str$(AutomateArray(VarY).MonsterIndex)
    Call Util.Screen3(Var$)
 Endif
Error.Resume21x:
 Exit Sub
Error.Trap21x:
 Resume Error.Resume21x
End Sub

Sub Start.Monster(VarY)
 On Local Error Goto Error.Trap21a
 Room=AutomateArray(VarY).StartRoom
 If Room>SFalse And Room<=Lof(RoomFile)/RoomRecLen Then
    Choice=AutomateArray(VarY).MonsterIndex
    Call Get.Monster(VarY)
    If Temp5! Then
       Temp0=Temp0+1
       Locate 10,29
       Color 15,1
       Print Temp0;
       Temp.Array1(VarY)=True
       Temp.Array3(VarY)=Room
       AutomateArray(VarY).NumberMoves=False
       AutomateArray(VarY).LastMoveTime=Now
       AutomateArray(VarY).MessWorkIndex=Temp5!
    Endif
 Endif
Error.Resume21a:
 Exit Sub
Error.Trap21a:
 Resume Error.Resume21a
End Sub

Sub Stop.Monster(VarY,VarZ)
 On Local Error Goto Error.Trap21b
 If VarZ=False Then
    If Temp1>False Then
       Var$="Monster stop:"+Str$(VarY)+", index:"+Str$(AutomateArray(VarY).MonsterIndex)
       Call Util.Screen3(Var$)
    Endif
 Endif
 Choice=AutomateArray(VarY).MonsterIndex
 Call Remove.Monster(VarZ)
 Room=Temp.Array3(VarY)
 Temp.Array1(VarY)=False
 TempX!=AutomateArray(VarY).MessWorkIndex
 Call Read.Record(MessWorkFile3,TempX!)
 MessWorkRecord3.Room=SFalse
 MessWorkRecord3.AutoMode=False
 MessWorkRecord3.IsAutomaton=False
 Call Write.Record(MessWorkFile3,TempX!)
 Call Remove.Monster.Inventory
 AutomateArray(VarY).LastMoveTime=Now
 Temp0=Temp0-1
 If VarZ=False Then
    Locate 10,29
    Color 15,1
    Print Temp0;
 Endif
Error.Resume21b:
 Exit Sub
Error.Trap21b:
 Resume Error.Resume21b
End Sub

Sub Move.Monster(VarY)
 On Local Error Goto Error.Trap21c
 If AutomateArray(VarY).MovesRandomly Then
    If Temp1>False Then
       Var$="Moving monster:"+Str$(VarY)+", index:"+Str$(AutomateArray(VarY).MonsterIndex)
       Call Util.Screen3(Var$)
    Endif
    Room=Temp.Array3(VarY)
    Choice=AutomateArray(VarY).MonsterIndex
    Call Auto.Move.Monster(VarY)
    If AutomateArray(VarY).NumberMoves<32767 Then
       AutomateArray(VarY).NumberMoves=AutomateArray(VarY).NumberMoves+1
    Endif
    AutomateArray(VarY).LastMoveTime=Now
    Temp0!=Temp0!+1!
    If Temp0!>=999999! Then
       TempB!=TempB!+1!
       Temp0!=0!
    Endif
 Endif
Error.Resume21c:
 Exit Sub
Error.Trap21c:
 Resume Error.Resume21c
End Sub

Sub Get.Monster(VarY)
 On Local Error Goto ErrorTrap23
 Temp5!=SFalse
 If Choice>SFalse And Choice<=Lof(MonsterFile)/MonsterRecLen Then
    Call Read.Record(MonsterFile,Choice)
    If MonsterRecord.Permanent=False Then
       Call Get.Monster.Stats(VarY)
       VarX$=Rtrim$(MonsterRecord.MonsterName)
       If Instr("aeiou",Left$(VarX$,1)) Then
          Strng="An "
       Else
          Strng="A "
       Endif
       Out2=Strng+VarX$+" enters the room!"
       Call Send.Mess(8,SFalse,Room,Out2)
    Endif
 Endif
ErrorResume23:
 Exit Sub
ErrorTrap23:
 Resume ErrorResume23
End Sub

Sub Get.Monster.Stats(VarY)
 On Local Error Goto ErrorTrap24
 For Temp5!=1! To Lof(MessWorkFile3)/MessWorkRecLen3
    Call Read.Record(MessWorkFile3,Temp5!)
    If MessWorkRecord3.IsAutomaton=False Then
       If MessWorkRecord3.Room=SFalse Then
          Exit For
       Endif
    Endif
 Next
 MessWorkRecord3.Index=Choice
 MessWorkRecord3.Monster=MonsterRecord
 MessWorkRecord3.Monster.Follow=False
 MessWorkRecord3.Room=Room
 MessWorkRecord3.AutoMode=False
 MessWorkRecord3.IsAutomaton=VarY
 Call Write.Record(MessWorkFile3,Temp5!)
 Call Add.Monster.Inventory(VarY)
ErrorResume24:
 Exit Sub
ErrorTrap24:
 Resume ErrorResume24
End Sub

Sub Add.Monster.Inventory(VarY)
 On Local Error Goto ErrorTrap24a
 Var!=MonsterRecord.First
 VarX!=SFalse
 If Var! Then
    Do
       Call Read.Record(MonInvFile,Var!)
       Var1!=MessWorkRecord6.Next
       Temp6!=MessWorkRecord6.User
       If Temp6! Then
          If Rnd>.5 Then
             Temp7=False
             If Temp6!>SFalse And Temp6!<=Lof(TreasureFile)/TreasureRecLen Then
                Call Read.Record(TreasureFile,Temp6!)
                Temp7=TreasureRecord.Charges
             Endif
             For Temp9!=1! To Lof(MessWorkFile5)/MessWorkRecLen5
                Call Read.Record(MessWorkFile5,Temp9!)
                If MessWorkRecord5.Index=SFalse Then
                   Exit For
                Endif
             Next
             MessWorkRecord5.Index=Choice
             MessWorkRecord5.Inv=Temp6!
             MessWorkRecord5.Charges=Temp7
             MessWorkRecord5.Room=Room
             MessWorkRecord5.Monster=Temp5!
             MessWorkRecord5.IsAutomaton=-1
             Call Write.Record(MessWorkFile5,Temp9!)
             VarX!=VarX!+1!
          Endif
       Endif
       If Var1!=SFalse Then
          Exit Do
       Endif
       Var!=Var1!
    Loop
 Endif
 For Var=1 To 3
    MessWorkRecord3.Weapon(Var).Index=0!
    MessWorkRecord3.Weapon(Var).Charges=0
    MessWorkRecord3.Weapon(Var).Plus=0
 Next
 Var!=MonsterRecord.Weapon
 If Var!>SFalse And Var!<=Lof(TreasureFile)/TreasureRecLen Then
    Call Read.Record(TreasureFile,Var!)
    MessWorkRecord3.Weapon(1).Index=Var!
    MessWorkRecord3.Weapon(1).Charges=TreasureRecord.Charges
    MessWorkRecord3.Weapon(1).Plus=TreasureRecord.Plus
 Endif
 Var!=MonsterRecord.Shield
 If Var!>SFalse And Var!<=Lof(TreasureFile)/TreasureRecLen Then
    Call Read.Record(TreasureFile,Var!)
    MessWorkRecord3.Weapon(2).Index=Var!
    MessWorkRecord3.Weapon(2).Charges=TreasureRecord.Charges
    MessWorkRecord3.Weapon(2).Plus=TreasureRecord.Plus
 Endif
 Var!=MonsterRecord.Armor
 If Var!>SFalse And Var!<=Lof(TreasureFile)/TreasureRecLen Then
    Call Read.Record(TreasureFile,Var!)
    MessWorkRecord3.Weapon(3).Index=Var!
    MessWorkRecord3.Weapon(3).Charges=TreasureRecord.Charges
    MessWorkRecord3.Weapon(3).Plus=TreasureRecord.Plus
 Endif
 Call Write.Record(MessWorkFile3,Temp5!)
 If Temp1>False Then
    Var$="Monster start:"+Str$(VarY)+", index:"+Str$(AutomateArray(VarY).MonsterIndex)
    Call Util.Screen3(Var$)
 Endif
 If Temp1>=2 Then
    Var$="Starts in room"+Str$(Room)+", stops in room"+Str$(AutomateArray(VarY).StopRoom)
    Call Util.Screen3(Var$)
 Endif
 If Temp1=3 Then
    Var$="Moves every "+ _
     Right$(Str$(Hour(AutomateArray(VarY).MoveEvery)+100),2)+":"+ _
     Right$(Str$(Minute(AutomateArray(VarY).MoveEvery)+100),2)+":"+ _
     Right$(Str$(Second(AutomateArray(VarY).MoveEvery)+100),2)
    Call Util.Screen3(Var$)
    Var$=Str$(VarX!)+" inventory added."
    Call Util.Screen3(Var$)
 Endif
ErrorResume24a:
 Exit Sub
ErrorTrap24a:
 Resume ErrorResume24a
End Sub

Sub Remove.Monster(VarZ)
 On Local Error Goto ErrorTrap23a
 If VarZ Then
    Exit Sub
 Endif
 If Choice>SFalse And Choice<=Lof(MonsterFile)/MonsterRecLen Then
    Call Read.Record(MonsterFile,Choice)
    VarX$=Rtrim$(MonsterRecord.MonsterName)
    If Instr("aeiou",Left$(VarX$,1)) Then
       Strng="An "
    Else
       Strng="A "
    Endif
    Out2=Strng+VarX$+" exits the room!"
    Call Send.Mess(8,SFalse,Room,Out2)
 Endif
ErrorResume23a:
 Exit Sub
ErrorTrap23a:
 Resume ErrorResume23a
End Sub

Sub Move.Monster.Inventory
 On Local Error Goto ErrorTrap4x
 VarX!=SFalse
 For Temp2!=1! To Lof(MessWorkFile5)/MessWorkRecLen5
    Call Read.Record(MessWorkFile5,Temp2!)
    If MessWorkRecord5.Monster=TempX! Then ' messworkrecord3 index
       If MessWorkRecord5.Index=Choice Then ' monster.dat index
          If MessWorkRecord5.Room=Next.Room Then
             MessWorkRecord5.Room=Room
             Call Write.Record(MessWorkFile5,Temp2!)
             VarX!=VarX!+1!
          Endif
       Endif
    Endif
 Next
 If VarX!>SFalse Then
    If Temp1=3 Then
       Var$=Str$(VarX!)+" inventory moved to room"+Str$(Room)
       Call Util.Screen3(Var$)
    Endif
 Endif
ErrorResume4x:
 Exit Sub
ErrorTrap4x:
 Resume ErrorResume4x
End Sub

Sub Remove.Monster.Inventory
 On Local Error Goto ErrorTrap4
 VarX!=SFalse
 For Temp2!=1! To Lof(MessWorkFile5)/MessWorkRecLen5
    Call Read.Record(MessWorkFile5,Temp2!)
    If MessWorkRecord5.Monster=TempX! Then ' messworkrecord3 index
       If MessWorkRecord5.Index=Choice Then ' monster.dat index
          If MessWorkRecord5.Room=Room Then
             MessWorkRecord5.Index=SFalse
             MessWorkRecord5.IsAutomaton=0
             Call Write.Record(MessWorkFile5,Temp2!)
             VarX!=VarX!+1!
          Endif
       Endif
    Endif
 Next
 If VarX!>SFalse Then
    If Temp1=3 Then
       Var$=Str$(VarX!)+" inventory deleted at room"+Str$(Room)
       Call Util.Screen3(Var$)
    Endif
 Endif
ErrorResume4:
 Exit Sub
ErrorTrap4:
 Resume ErrorResume4
End Sub

' subroutine to move monster in random direction.
Sub Auto.Move.Monster(VarY)
 On Local Error Goto ErrTrap18
 Call Read.Record(RoomFile,Room)

 ' see if there is any direction
 VarA=False
 For Var2=1 To 14
    If RoomRecord.Direct(Var2) Then
       VarA=True
       Exit For
    Endif
 Next

 ' check if monster is stuck
 If VarA=False Then
    Call Stop.Monster(VarY,False)
    Exit Sub
 Endif

 ' find next direction randomly
 Var = 0
 Do
    Var = Var + 1
    If Var > 1024 Then ' avoid endless loop
       ' force direction if loop
       For TempA=1 To 14
          If RoomRecord.Direct(TempA) Then
             Exit Do
          Endif
       Next
       ' otherwise monster stuck
       Call Stop.Monster(VarY,False)
       Exit Sub
    Endif
    TempA=Int(Rnd*14+1)
    If RoomRecord.Direct(TempA) Then
       Exit Do
    Endif
 Loop

 ' store direction
 Out3=Nul
 If TempA=13 Then
    Out3=RoomRecord.Custom1
 Else
    If TempA=14 Then
       Out3=RoomRecord.Custom2
    Else
       Out3=Direction(TempA)
    Endif
 Endif
 Out3=Rtrim$(Out3)
 Out3=Lcase$(Out3)

 ' store room variables
 Next.Room=Room ' store current room
 Room=RoomRecord.Direct(TempA) ' get new room

 ' get monster record
 Call Read.Record(MonsterFile,Choice)

 ' store monster name
 VarX$=Rtrim$(MonsterRecord.MonsterName)
 If Instr("aeiou",Left$(VarX$,1)) Then
    Strng="An "+VarX$
 Else
    Strng="A "+VarX$
 Endif
 If Temp1>=2 Then
    If Len(Out3) Then
       Var$=Strng+" moves: "+Out3+" from room"+Str$(Next.Room)+" to room"+Str$(Room)
       Call Util.Screen3(Var$)
    Endif
 Endif

 ' move monster inventory
 TempX!=AutomateArray(VarY).MessWorkIndex
 If Next.Room<>Room Then
    Call Move.Monster.Inventory
 Endif

 ' move monster arrays
 Temp.Array3(VarY)=Room
 Temp5!=AutomateArray(VarY).MessWorkIndex
 Call Read.Record(MessWorkFile3,Temp5!)

 ' check dead monster
 If MessWorkRecord3.Room=SFalse Then
    Exit Sub
 Endif

 ' move monster indexes
 MessWorkRecord3.Room=Room
 Call Write.Record(MessWorkFile3,Temp5!)

 ' monster exits game
 If AutomateArray(VarY).StopRoom=Room Then
    Out2=Strng+" exits the room!"
    Call Send.Mess(8,SFalse,Next.Room,Out2)
    Call Stop.Monster(VarY,True)
    Exit Sub
 Endif

 ' check similar rooms
 If Next.Room=Room Then
    Exit Sub
 Endif

 ' display message to previous room
 If Len(Out3) Then
    Out2=Strng+" moves "+Out3+"!"
    Call Send.Mess(8,SFalse,Next.Room,Out2)
 Endif

 ' display message to new room
 Out2=Strng+" enters the room!"
 Call Send.Mess(8,SFalse,Room,Out2)

ErrResume18:
 Exit Sub
ErrTrap18:
 Resume ErrResume18
End Sub

Sub Save.Screen(Var)
 On Local Error Goto Error.Trap62
 If Var=False Then
    VarX2=(Max.Row-1)*80+80
    Redim Temp.ArrayY(VarX2) As Long
    Redim Temp.ArrayZ(VarX2) As Double
    For Var1=1 To Max.Row
       For Var2=1 To 80
          VarX2=(Var1-1)*80+Var2
          TempZ1=Screen(Var1,Var2)
          TempZ2=Screen(Var1,Var2,1)
          Temp.ArrayY(VarX2)=TempZ1
          Temp.ArrayZ(VarX2)=TempZ2
       Next
    Next
 Else
    Cls
    For VarX=Max.Row To 1 Step -1
       For VarY=1 To 80
          VarX2=(VarX-1)*80+VarY
          If Temp.ArrayY(VarX2)<>32 Then
             Goto Next.Line
          Endif
       Next
    Next
Next.Line:
    TempX=VarX
    For Var1=1 To VarX ' Max.Row
       For Var2=1 To 80
          VarX2=(Var1-1)*80+Var2
          VarB=Int(Temp.ArrayZ(VarX2)/16)
          VarF=Temp.ArrayZ(VarX2) Mod 16
          TempZ1=Temp.ArrayY(VarX2)
          Locate Var1,Var2,0
          Color VarF,VarB
          Print Chr$(TempZ1);
       Next
    Next
 Endif
Error.Resume62:
 Exit Sub
Error.Trap62:
 Resume Error.Resume62
End Sub

Sub Valid.Name(Var$)
 On Local Error Goto Error.Trap58
 TempA=True
 Select Case Lcase$(Rtrim$(Var$))
 Case "login", "logoff", "relogin"
    TempA=False
 Case "deleted", "offline", "enter"
    TempA=False
 Case "dead", "null", "range error"
    TempA=False
 Case Deleted$, Offline$, Enter$
    TempA=False
 Case Dead$, None$, Range$
    TempA=False
 End Select
Error.Resume58:
 Exit Sub
Error.Trap58:
 Resume Error.Resume58
End Sub

' function to match case-sensitive substring with ?, * characters in substring
SUB InstrSUB1(Var,Var$,VarQ)

' see if Var$ matches in Strng
On Local Error Goto ErrorTrap24x5

' store case-sensitive string match variables
If VarQ Then
   S2$ = Var$
   S3$ = Strng
Else
   S2$ = Lcase$(Var$)
   S3$ = Lcase$(Strng)
Endif

' check default instr
IF INSTR(S2$, "*") = 0 THEN
   IF INSTR(S2$, "?") = 0 THEN
      Var = INSTR(S3$, S2$)
      EXIT SUB
   END IF
END IF

Var = -1 ' assume match

' see if S2$ matches in S3$ with substrings
FOR S3 = 1 TO LEN(S3$)
   S1$ = MID$(S3$, S3)
   P1 = 1 ' pointer to S1$
   P2 = 1 ' pointer to S2$
   DO
      ' check match
      IF P2 > LEN(S2$) THEN
         EXIT SUB
      END IF

      ' check character in S2$ at P2
      V$ = MID$(S2$, P2, 1)
      SELECT CASE V$
      CASE "*" ' global character
         ' scan to next char
         IF P2 > LEN(S2$) THEN
            EXIT DO
         END IF
         S4$ = MID$(S2$, P2 + 1, 1)
         SELECT CASE S4$
         CASE "*", "?"
            P2 = P2 + 1
         CASE ELSE
            DO
               IF MID$(S1$, P1, 1) = S4$ THEN
                  EXIT DO
               END IF
               IF P1 >= LEN(S1$) THEN
                  EXIT DO
               END IF
               P1 = P1 + 1
            LOOP
            P2 = P2 + 1
         END SELECT
      CASE "?" ' wildcard character
         P1 = P1 + 1
         P2 = P2 + 1
      CASE ELSE ' ascii character
         IF MID$(S1$, P1, 1) <> V$ THEN ' no match
            EXIT DO
         END IF
         P1 = P1 + 1
         P2 = P2 + 1
      END SELECT
   LOOP
NEXT
Var = 0 ' no match
EXIT SUB
ErrorResume24x5:
 Exit Sub
ErrorTrap24x5:
 Resume ErrorResume24x5
End Sub
