 Rem File: Dndbbs for Dndbbs Version v5.0a Release r2.0 Tweak t5.0a
 Rem Main operating module.

 ' read include files.
 Rem $Include: 'dndbbs.inc'

 ' declare error routine.
 On Error Goto Main.Error

 ' increase stack for recursion.
 Stack 4096

 ' reseed random number generator.
 Randomize Timer

 ' read stdin
 Call Stdin(Var$)

 ' check program start type.
 Select Case Chained
 Case False
    Call Read.Config(True)
    Call Start.Clock
    If BBS.Mode=False Then
       Call Passwrd3(Var,True)
       If Var=False Then
          Call Zero.Node(0)
          Out2=Nul
          Out3=Nul
          If Lost.Carrier=False Then
             Out2="Illegal password!"
             Out3="Program terminated!"
          Endif
          Call Terminate.Program(0)
          End 0
       Endif
       Call Copyright(True)
       Node.Abort=False
    Endif
 Case True
    Call Read.Config(False)
    Call Restore.Active
    Local.Mode=Remote
    If Shelled Then
       If Shell.Type=-1 Then
          Call Save.Screen(True)
       Endif
       Call Get.User.Record
       Call Reset.Cursor
       If Shell.Type=-2 Then
          Graphics.On=False
          Graphics.Off=True
          Strng="Returning from shell."
          Call IO.O
          Call More.Prompt
       Endif
    Endif
    New.Room=-2
    ' fixed 06/29/2021 for console function keys.
 Case -2 ' chain.command
    If Remote=-2 Then
       Local.Mode=0 ' online user
    Endif
 End Select

 ' begin main cycling loop
 Do
    If Chained>-2 Then
       If Shelled=False Then
          If Relogging=False Then
             Call Init.Drivers
             Call Get.Command
             Call Read.Door
             Call Init.Port
             Call Read.Guest
             Call Valid.Port
             Call Restore.Active
             Call Open.Modem
          Endif
          Relogging=False
          Call Read.Guest
          If BBS.Mode Then
             Call Passwrd3(Var,False)
             If Var=False Then
                Call Zero.Node(0)
                Out2=Nul
                Out3=Nul
                If Lost.Carrier=False Then
                   Out2="Illegal password!"
                   Out3="Program terminated!"
                Endif
                Call Terminate.Program(0)
                End 0
             Endif
          Endif
          Call Login
          If Logged.In=False Then
             Call Init.Node
             Call Zero.Node(-1)
          Endif
          If Logged.In Then
             Call Read.User.Inventory
             Call Get.User.Record
             If Lost.Carrier=False Then
                Call Status.Line(1)
             Endif
          Endif
       Endif
       If Chained=False Then
          New.Room=True
       Endif
    Endif
    If Chained=-2 Then
       If Shell.Type2 Then ' QuickEdit/HexCalc
          Shell.Type2 = 0
          Call Playwav(10)
          Call Save.Screen(True)
          Call Reset.Cursor
          Graphics.Off=False
          Call Restore.Color
          Strng=Action.Prompt
          Call Convert.String
          Call Convert.ANSI(False,VarQ,VarR)
          Graphics.On=True
          Call IO.O
          Graphics.On=False
          If Next.Room Then
             Action.Recurse=0
             Call Enter.Room
             If New.Room Then
                Graphics.Off=False
                Strng="You are moved to room #"+Mid$(Str$(Room),2)+"!"
                Call IO.O
                Call Enter.New.Room
             Endif
          Endif
       Endif
       Call Reset.Status.Line
    Endif
    Chained=False
    Shelled=False
    If Lost.Carrier=-2 Then
       Lost.Carrier=False
       Call Hang.up(4)
       If Node.Abort Then
          Exit Do
       Endif
    Endif
    If Lost.Carrier=-3 Then
       Lost.Carrier=False
       Call Hang.up(1)
       If Node.Abort Then
          Exit Do
       Endif
    Endif
    Do
       Call Check.Program(Var)
       If Var Then
          Exit Do
       Endif
       If New.Room Then
          Call Enter.New.Room
       Endif
       If GuestLogin Then
          Var$=","+Rtrim$(GuestRecord.GuestRoomExit)+","
          Var2$=","+Mid$(Str$(Room),2)+","
          If Instr(Var$,Var2$) Then
             Call Hang.Up(17)
             Exit Do
          Endif
       Endif
       If Start.Loop Then
          Start.Loop=False
          If Len(Func.Buffer) Then
             VarX=Asc(Func.Buffer)
             Func.Buffer=Nul
             Call Function.Key(VarX)
          Endif
          Call Chain.Command(999)
       Endif
       If New.Room=False Then
          Logged.Node=True
          Graphics.Off=False
          Strng=Action.Prompt
          If Instr(Strng,ANSIPrefixChar)=0 Then
             Call Send.ANSI ' rotate color prompt
          Endif
          Call Convert.String ' convert macros first
          Call Convert.ANSI(False,VarQ,VarR) ' convert prefixes next
          Upper.Case=True
          Allow.Break=True
          Graphics.On=True
          Logged.In=-2
          Call IO.I
          Logged.In=-1
          Graphics.On=False
          Allow.Break=False
          If Break Then
             Strng="*break*"
             Call IO.O
             Call Playwav(1)
          Endif
          Break=False
          Logged.Node=False
          Upper.Case=False
          Start.Loop=True
          Call Command.Loop
       Endif
    Loop
    If OutZ<>Nul Then
       If Rtrim$(NodeX)=Nul Then
          OutZ=OutZ+" (Node -)."
       Else
          OutZ=OutZ+" (Node "+NodeX+")."
       Endif
       If Node>False Then
          Call Send.Mess(4,SFalse,SFalse,OutZ)
       Endif
       Call Update.DayFile(OutZ,True,False)
    Endif
    Call Status.Line(-2)
    If Logged.In Then
       Call Zero.Node(-1)
       If User.Index>SFalse Then
          Call Put.User.Record
          Call Clean.Room.Node(Room)
          Call Remove.Vehicle
          Call Update.LastUser
          Call Record.Time(True)
          Call Reset.Keys
       Endif
    Endif
    If Node.Abort Then
       Exit Do
    Endif
    Graphics.Off=False
    If User.Index>SFalse Then
       If Lost.Carrier=0 Then
          If Config2(75) Then
             Strng="Your time on stats:"
             Call IO.O
             Call Time.Online(False)
             Call More.Prompt
          Endif
          If Config2(76) Then
             Call Shell.Program3 ' top.ten
          Endif
          If Config2(77) Then
             Call Out.File(Logoff.FileName)
             Strng=Nul
             Call IO.O
          Endif
       Endif
       If Config2(78) Then
          Call Logoff.Prompt
       Endif
    Endif
    Lost.Carrier=False
    If Relogging=False Then
       If BBS.Mode Then
          Exit Do
       Endif
       If Bypass.Login Then
	  Exit Do
       Endif
       If Bypass.Screen Then
	  Exit Do
       Endif
    Endif
 Loop
 ' end program
Main.Error:
 Lost.Carrier=False
 Call Zero.Node(0)
 Call Exit.To.DOS
 End 1

' end with message
Sub Terminate.Program(Var)
' Var = 1  warm boot
' Var = 2  cold boot
 On Local Error Goto Error.Trap1
 Call Status.Line(-2)
 CALL ScreenANSI(1,33,0)
 CALL ScreenANSI(4,33,30)
 Call Clear.Screen
 Locate Max.Row-1,1,1
 If Node.Abort=False Then
    If Len(Out2) Then
       Color 15
       Print Out2
       Color 7
    Endif
    If Len(Out3) Then
       Color 15
       Print Out3
       Color 7
    Endif
 Endif
 If Port=-1 Then
    Eat$=Nul
 Else
    Call Zero.Node(0)
 Endif
 Call Exit.To.DOS
 If Var=1 Then ' warm boot
    OUT &H64, &HF0
 Endif

 If Var=2 Then ' cold boot
    DEF SEG = &HFFFF
    CALL Absolute(0)
    DEF SEG
 Endif

Error.Resume1:
 End 2
Error.Trap1:
 Resume Error.Resume1
End Sub

' prepare for end
Sub Exit.To.DOS
 On Local Error Goto Error.Trap2

 ' write exit file
 DND.Path=Environ$("DNDBBS")
 If DND.Path<>Nul Then
    If Right$(DND.Path,1)<>"\" Then
       DND.Path=DND.Path+"\"
    Endif
 Endif
 Close #TempFile1
 Open DND.Path+"DEXIT"+NodeX+".DEF" For Output As #TempFile1
 Var$="EXIT "+Fclock$+" Node "
 If Rtrim$(NodeX)=Nul Then
    Var$=Var$+"-"
 Else
    Var$=Var$+NodeX
 Endif
 Print #TempFile1,Var$
 Close #TempFile1

 ' display exit message
 Call ScreenANSI(1,37,7)
 Color 7,0
 Locate ,,1
 If Node.Abort=False Then
    Strng="Returning you to "
    If BBS.Mode Then
       If Rtrim$(BBS.Name)=Nul Then
          Strng=Strng+"System."
       Else
          Strng=Strng+BBS.Name+"."
       Endif
       If Port=-1 Then
          Eat$=Nul
       Else
          Call Put.Modem(Strng)
       Endif
    Else
       If Windows.Detected Then
          Strng=Strng+"Windows DOS."
       Else
          Strng=Strng+"DOS."
       Endif
    Endif
    Print
    Locate Max.Row-1,1,1
    Print Strng
 Endif
 If Mouse.Present Then
    Call Mouse.Function(2,0)
 Endif
 If Port=-1 Then
    Eat$=Nul
 Else
    Call Zero.Node(0)
 Endif
 Call Decrease.Files
 Call Drop.DTR

 ' clear control-break flag
 DEF SEG = &H40
 POKE &H71, &H0
 DEF SEG

 Call ScreenANSI(1,37,7)
 Color 7,0
 Locate ,,1
Error.Resume2:
 Exit Sub
Error.Trap2:
 Resume Error.Resume2
End Sub

Sub Hang.Up(Var)
 On Local Error Goto Error.Trap30
 If Logged.In Then
    Logged.In=-1
    Call Write.Record(UserFile,User.Index)
 Endif
 Buffer=Nul
 Func.Buffer=Nul
 Out2=Nul
 Strng=Nul
 Timeon=Timeit!
 Time.Left=180!
 Select Case Var
 Case 1
    OutZ="Connect timeout"
    Time.Left=30!
 Case 2
    OutZ="Time limit exceeded"
    Time.Left=30!
    If Logged.In Then
       Call Record.Time(False)
    Endif
 Case 3
    Lost.Carrier=True
    OutZ="Lost carrier"
 Case 4
    Lost.Carrier=-2
    OutZ="Forced logoff"
 Case 5
    OutZ="Call limit exceeded"
 Case 6
    OutZ="Call time restriction"
 Case 7
    OutZ="Illegal login attempt"
 Case 8
    OutZ="Password verification"
 Case 9
    OutZ="Not resurrected"
 Case 10
    OutZ="Nondescriptive data files"
 Case 11
    Node.Abort=True
    OutZ="Program task abort"
 Case 12
    OutZ="Disabled login"
 Case 13
    OutZ="Bad bypass login"
 Case 14
    Lost.Carrier=True
    OutZ="Illegal character entry"
 Case 15
    OutZ="Codename in use"
 Case 16
    Node.Abort=True
    OutZ="Node task abort"
 Case 17
    OutZ="Guest exit"
 Case 18
    OutZ="Time restriction"
 Case 19
    OutZ="Bad node access"
 Case 20
    OutZ="Committed suicide"
 Case Else
    OutZ="Unknown logoff"
 End Select
 Graphics.Off=True
 If Lost.Carrier=False Then
    Call Logoff.Notice(Var)
 Endif
 Graphics.Off=False
 Call Logoff.Prompt
 Lost.Carrier=True
 Security.Guard=True
 If BBS.Mode Then
    Security.Guard=False
 Endif
 If Bypass.Login Then
    Security.Guard=False
 Endif
 If Var=20 Then ' suicide
    UserRecord.CodeName=Deleted$
    Call Write.Record(UserFile,User.Index)
    OutZ=Nul
 Endif
 Call Restore.Active
 Call ScreenANSI(1,37,7)
 Color 7,0
 Locate ,,1
Error.Resume30:
 Exit Sub
Error.Trap30:
 Resume Error.Resume30
End Sub

Sub Logoff.Notice(Var)
 On Local Error Goto Error.Trap30a
 VarX=False
 Strng=Nul
 Call IO.O
 Graphics.On=False
 Graphics.Off=False
 Call Status.Line(-2)
 Call Pre.Clock(Var$)
 Strng="Dndbbs offline at "+Var$
 Call IO.O
 Close #HelpFile3
 Open Logoff.Help.FileName For Random Shared As #HelpFile3 Len=HelpRecLen3
 If Lof(HelpFile3)>False Then
    For TempX=Var*2 To Var*2+1
       If TempX>False And TempX<=Lof(HelpFile3)/HelpRecLen3 Then
          Call Read.HelpFile(HelpFile3,TempX)
          Strng=Rtrim$(HelpRecord3.Text)
          If Len(Strng) Then
             Graphics.Off=True
             Call IO.O
             VarX=True
          Endif
       Endif
    Next
 Endif
 If VarX=False Then
    Graphics.Off=True
    Strng="You are being logged off: reason; "+OutZ+"."
    Call IO.O
 Endif
 Call IO.O
Error.Resume30a:
 Exit Sub
Error.Trap30a:
 Resume Error.Resume30a
End Sub

Sub Logoff.Prompt
 On Local Error Goto Error.Trap31
 If Lost.Carrier Then
    Goto Error.Resume31
 Endif
 If Node.Abort Then
    Goto Error.Resume31
 Endif
 Strng="[xon]"
 Call Convert.String
 If BBS.Mode=False Then
    Graphics.On=True
    Graphics.Off=False
    If Local.Mode=False Then
       Call Modem.ANSI(1,37,7)
    Endif
 Endif
 Line.Length=1
 No.Echo=True
 Carriage.Return=True
 Logged.In=False
 Strng="Press "+Enter$+" to exit the Adventure:"
 Call IO.O
 Call Playwav(7) ' complete.wav
 Graphics.On=False
 Timer.Temp!=Timeit!
 Do Until Time.Elapsed(Timer.Temp!,10!)
    Call Check.Carrier
    If Lost.Carrier Then
       Exit Do
    Endif
    Call Get.Modem(False)
    If Len(Buffer) Then
       Exit Do
    Endif
    Call Read.Keyboard(VarX$)
    If Len(VarX$) Then
       Exit Do
    Endif
 Loop
 If Lost.Carrier=-2 Then
    Call IO.O
 Endif
 Call IO.O
 Call ScreenANSI(2,Max.Row,1)
 Locate Max.Row,1,1
Error.Resume31:
 Call Restore.Active
 Exit Sub
Error.Trap31:
 Resume Error.Resume31
End Sub

Sub Quit.Program
 On Local Error Goto Error.Trap3
 Allow.Break=True
 Graphics.Off=True
 If Config2(74) Then
    Strng="Are you sure you want to quit(y/n)? "
    No.Input.Out="N"
    Call IO.I
 Endif
 Allow.Break=False
 If Yes Or Config2(74)=False Or Break Then
    Break=False
    Call Update.Mess(3)
    OutZ="Logged off"
    Security.Guard=False
 Endif
Error.Resume3:
 Exit Sub
Error.Trap3:
 Resume Error.Resume3
End Sub

Sub Sleep.Second2(Var!)
 On Local Error Goto Error.Trap5a
 Timer.Temp!=Timeit!
 Do
    If Time.Elapsed(Timer.Temp!,Var!) Then
       Exit Do
    Endif
    If Inkey$<>"" Then
       Exit Do
    Endif
    Call Release.Time(1)
 Loop
Error.Resume5a:
 Exit Sub
Error.Trap5a:
 Resume Error.Resume5a
End Sub

Sub MouseDriver
 ' fixed 01/19/2021, 04/18/2021
 On Local Error Goto Error.Trap14
 Mouse.Button=False
 Call Mouse.Function(5,0)
 If (OutregsX.AX AND 1)=1 Then
    If OutregsX.BX>False Then
       Mouse.Button=True
       Mouse.Row=Int((OutregsX.DX)/8+1)
       Mouse.Column=Int((OutregsX.CX)/8+1)
       Exit Sub
    Endif
 Endif
 Mouse.Button2=False
 Call Mouse.Function(5,1)
 If (OutregsX.AX AND 2)=2 Then
    If OutregsX.BX>False Then
       Mouse.Button2=True
       Exit Sub
    Endif
 Endif
 Mouse.Button3=False
 Call Mouse.Function(5,2)
 If (OutregsX.AX AND 4)=4 Then
    If OutregsX.BX>False Then
       Mouse.Button3=True
       Exit Sub
    Endif
 Endif
 Call Mouse.Function(3,0)
 Var2=Int((OutregsX.CX)/8+1)
 Var3=Int((OutregsX.DX)/8+1)
 Mouse.Row=False
 Mouse.Column=False
 If Var3<>Mouse.X Or Var2<>Mouse.Y Then
    Mouse.X=Var3
    Mouse.Y=Var2
    Mouse.Row=Mouse.X
    Mouse.Column=Mouse.Y
 Endif
Error.Resume14:
 Exit Sub
Error.Trap14:
 Resume Error.Resume14
End Sub

Sub MouseSelect(VarX)
 On Local Error Goto Error.Trap15
 For Var=1 To 10
    If Mouse.Row=Menu2(Var,1) Then
       If Mouse.Column>=Menu2(Var,2) Then
          Var1=Len(Rtrim$(Menu1(Var)))+2
          If Var=10 Then
             Var1=Var1+1
          Endif
          If Mouse.Column<=Menu2(Var,2)+Var1 Then
             Select Case VarX
             Case 1
                If Var=Selection Then
                   TempX$=Chr$(0)+Chr$(Var+58)
                Endif
             Case 2
                If Var<>Selection Or Blank.Menu Then
                   TempX$=Chr$(0)+Chr$(Var+240)
                Endif
             End Select
             Exit For
          Endif
       Endif
    Endif
 Next
Error.Resume15:
 Exit Sub
Error.Trap15:
 Resume Error.Resume15
End Sub

Sub Function.Key(Var)
 On Local Error Goto Error.Trap29
 If Local.Mode Then
    Exit Sub
 Endif
 Call Modem.ANSI(1,40,15)
 Call ScreenANSI(1,40,15)
 Call Modem.ANSI(4,37,15)
 Call ScreenANSI(4,37,15)
 Strng=Nul
 Out2=Nul
 Select Case Var
 Case 59 ' F1
    Call Playwav(8)
    Strng="System Operator is using the editor. Please wait.."
    Call IO.O
    TempC=False
    TempD=False
    Call Chain.Program("EDIT.EXE",True,True)
 Case 60 ' F2
    Remote=-2
    Local.Mode=True
    If OS2.Detected=0 Then ' has no freespace
       TempX!=Fre(-1)+Fre(a$) ' free far+temp
    Endif
    Call Chain.Command(202) ' Call Display.Memory
    Local.Mode=False
 Case 61 ' F3
    Remote=-2
    Local.Mode=True
    TempA$="DIS"
    Call Format.Command(6)
    Strng=Action.Prompt
    Call Convert.String
    Call Convert.ANSI(False,VarQ,VarR)
    If Config3(9)=Nul Then
       Strng=Strng+TempA$+" "
    Else
       Strng=Strng+Config3(9)+TempA$+" "
    Endif
    Call IO.I
    Parsed.Command1=Ucase$(Out2)
    Call Chain.Command(186) ' Call Discard.Object
    Local.Mode=False
 Case 62 ' F4
    Remote=-2
    Local.Mode=True
    TempA$="REDU"
    Call Format.Command(16)
    Strng=Action.Prompt
    Call Convert.String
    Call Convert.ANSI(False,VarQ,VarR)
    If Config3(9)=Nul Then
       Strng=Strng+TempA$+" "
    Else
       Strng=Strng+Config3(9)+TempA$+" "
    Endif
    Call IO.I
    Parsed.Command1=Out2
    Call Chain.Command(204) ' Call Reduce.Monsters
    Local.Mode=False
 Case 63 ' F5
    Remote=-2
    Local.Mode=True
    TempA$="CALL"
    Call Format.Command(4)
    Strng=Action.Prompt
    Call Convert.String
    Call Convert.ANSI(False,VarQ,VarR)
    If Config3(9)=Nul Then
       Strng=Strng+TempA$+" "
    Else
       Strng=Strng+Config3(9)+TempA$+" "
    Endif
    Call IO.I
    Local.Mode=False
    Parsed.Command1=Out2
    If Out2=Nul Then
       Call Chain.Command(184) ' Call Call.Monster
    Else
       Call Chain.Command(185) ' Call Summon.Monster
    Endif
 Case 64 ' F6
    Remote=-2
    Local.Mode=True
    TempA$="KILL"
    Call Format.Command(13)
    Strng=Action.Prompt
    Call Convert.String
    Call Convert.ANSI(False,VarQ,VarR)
    If Config3(9)=Nul Then
       Strng=Strng+TempA$+" "
    Else
       Strng=Strng+Config3(9)+TempA$+" "
    Endif
    Call IO.I
    Local.Mode=False
    Parsed.Command1=Ucase$(Out2)
    Parsed.Command2=Parsed.Command1
    ' Call Kill.Monster
    TempX=True
    Call Chain.Command(0) ' attack.monster
 Case 65 ' F7
    Remote=-2
    Local.Mode=True
    TempA$="TELE"
    Call Format.Command(18)
    Strng=Action.Prompt
    Call Convert.String
    Call Convert.ANSI(False,VarQ,VarR)
    If Config3(9)=Nul Then
       Strng=Strng+TempA$+" "
    Else
       Strng=Strng+Config3(9)+TempA$+" "
    Endif
    Call IO.I
    Local.Mode=False
    Parsed.Command1=Out2
    Parsed.Command2=Parsed.Command1
    Call Chain.Command(158) ' Call Teleport.Function
 Case 66 ' F8
    Call Invisibility
 Case 67 ' F9
    Remote=-2
    Local.Mode=True
    TempA$="GET"
    Call Format.Command(9)
    Strng=Action.Prompt
    Call Convert.String
    Call Convert.ANSI(False,VarQ,VarR)
    If Config3(9)=Nul Then
       Strng=Strng+TempA$+" "
    Else
       Strng=Strng+Config3(9)+TempA$+" "
    Endif
    Call IO.I
    Local.Mode=False
    Out2=Ucase$(Out2)
    Stored.Parsed.Command2=Out2
    Parsed.Command1=Stored.Parsed.Command2
    Call Numeric
    Out2=Parsed.Command1
    Out2=Lcase$(Out2)
    TempX=True
    Call Chain.Command(157) ' Call Drop(True)
 Case 68 ' F10
    Remote=-2
    Local.Mode=True
    Call Chain.Command(160) ' Call Link.Room
    Local.Mode=False
 Case 71 ' home
    Buffer=Nul
    Func.Buffer=Nul
    If Local.Mode=False Then
       TempX=1
       Chat=True
       Call Chain.Command(4) ' Call Enter.Chat
    Else
       Graphics.Off=False
       Call IO.O
    Endif
    Call Redisplay.Prompt
    Chat=False
 Case 82 ' insert
    Call Adjust.Level(1)
 Case 83 ' delete
    Call Adjust.Level(2)
 Case 146 ' ctrl-insert
    Call Adjust.Level(3)
 Case 76, 143 ' keypad-5/control-keypad-5
    Call Adjust.Time(1)
 Case 72, 152 ' up/alt-up
    Call Adjust.Time(2)
 Case 80, 160 ' down/alt-dn
    Call Adjust.Time(3)
 Case 141 ' ctrl-up
    Call Adjust.Time(4)
 Case 145 ' ctrl-down
    Call Adjust.Time(5)
 Case 73, 132 ' pageup/ctrl-pageup = quickedit
    Call Remote.Edit(0)
 Case 81, 118 ' pagedown/ctrl-pagedn = hexcalc
    Call Remote.Edit(1)
 Case 133 ' F11
    Call Playwav(8)
    Strng="System Operator is using config. Please wait.."
    Call IO.O
    Call Chain.Program("DNDCNFG.EXE",True,True)
 Case 134 ' F12
    Call Playwav(8)
    Strng="System Operator is using the editor. Please wait.."
    Call IO.O
    Call Chain.Program("DNDEDIT.EXE",True,True)
 Case 31 ' Alt-S
    If Logged.Node Then
       ' call windows screen saver
       Filename=DND.Path+"scrnsave.exe"
       If Dir$(Filename)<>"" Then
          Call Shell.Program("SCRNSAVE.EXE","")
       Endif
    Endif
 Case 32 ' Alt-D
    Call Flag.Toggles(7)
    Local.Mode=True
    If Disk.Log Then
       Strng="Disk log open."
    Else
       Strng="Disk log closed."
    Endif
    Call IO.O
    Local.Mode=False
 Case 25 ' Alt-P
    Call Flag.Toggles(8)
    Local.Mode=True
    If Print.Log Then
       Strng="Print log open."
    Else
       Strng="Print log closed."
    Endif
    Call IO.O
    Local.Mode=False
 Case 17 ' Alt-W
    Local.Mode=True
    If Print.Log=False Then
       Strng=Nul
       Call IO.O
       Strng="Print log not open."
       Call IO.O
    Else
       Call Print.Screen
    Endif
    Local.Mode=False
 Case 45 ' Alt-X
    Local.Mode=True
    If Disk.Log=False Then
       Strng=Nul
       Call IO.O
       Strng="Disk log not open."
       Call IO.O
    Else
       Call Print.LogFile
    Endif
    Local.Mode=False
 Case 44 ' Alt-Z
    Debug.Mode=Not Debug.Mode
    Local.Mode=True
    If Debug.Mode Then
       Strng="Debug mode on."
    Else
       Strng="Debug mode off."
    Endif
    Call IO.O
    Local.Mode=False
 Case 33 ' Alt-F
    Local.Mode=Not Local.Mode
    If Local.Mode Then
       Strng="Local mode on."
    Else
       Strng="Local mode off."
    Endif
    Call IO.O
 Case 131 ' Alt-=
    Local.Mode=True
    Graphics.Off=True
    Strng=Nul
    Call IO.O
    Filename=DND.Path+"funckeys.txt"
    Call Out.File(FileName)
    Local.Mode=False
 End Select
 Strng=Nul
 Out2=Nul
 Exit Sub
Error.Resume29:
 Exit Sub
Error.Trap29:
 Resume Error.Resume29
End Sub

Sub Init.Port
 On Local Error Goto Error.Trap18
 ' check COM0:
 If BBS.Mode=False Then
    If Config2(46)=0 Then
       Local.Mode=True
       Port=True
       Exit Sub
    Endif
 Endif
 ' get default port
 If Port.Override Then
    Port=Port.Override-1
 Else
    If BBS.Mode=False Then
       Port=Config2(46)-1
    Endif
 Endif
Error.Resume18:
 Exit Sub
Error.Trap18:
 Resume Error.Resume18
End Sub

Sub Init.Comm.Driver
 On Local Error Goto Error.Trap17
 Fossil.Active=False
 Fossil.Version=False
 If Local.Mode Then
    Exit Sub
 Endif
 Call Extended.Driver2(28,0) ' x00
 If OutregsX.AX=&H1954 Then
    Fossil.Active=True
    Fossil.Version=1
 Else
    Call Extended.Driver2(4,0) ' fossil
    If OutregsX.AX=&H1954 Then
       Fossil.Active=True
       Fossil.Version=2
    Endif
 Endif
 ' check fossil override
 Select Case Fossil.Mode
 Case -1 ' off
    Fossil.Active=False
    Fossil.Version=0
 Case 1 ' v1 x00
    Fossil.Active=True
    Fossil.Version=1
 Case 2 ' v2 fossil
    Fossil.Active=True
    Fossil.Version=2
 End Select
Error.Resume17:
 Exit Sub
Error.Trap17:
 Resume Error.Resume17
End Sub

Sub Recycle.DTR
 On Local Error Goto Error.Trap46
 ' check door mode
 If BBS.Mode Then
    Exit Sub
 Endif
 ' check fossil
 Display.Timer!=Timeit!
 If Fossil.Active=False Then
    ' get modem control register port address
    Call Get.MCR(VarZ)
    If VarZ=0 Then
       Exit Sub
    Endif
    ' wait for carrier drop
    Do
       Strng="Cycle DTR off"
       If Rtrim$(ANSIPrefixChar)<>"" Then
          Strng=ANSIPrefixChar+"FE"+Strng
       Endif
       Call Modem.Status
       Out VarZ,0 ' drop dtr/rts
       Time.Temp!=Timeit!
       Do
          If Time.Elapsed(Time.Temp!,Csng(Config2(84))) Then
             Exit Do
          Endif
          Gosub DisplayTimer
       Loop
       Gosub DisplayTimer
       Var2=1
       If Config2(80) Then ' rts/cts
          Var2=3
       Endif
       Strng="Cycle DTR on"
       If Rtrim$(ANSIPrefixChar)<>"" Then
          Strng=ANSIPrefixChar+"FE"+Strng
       Endif
       Call Modem.Status
       Out VarZ,Var2 ' raise dtr/set rts
       Time.Temp!=Timeit!
       Do
          If Time.Elapsed(Time.Temp!,Csng(Config2(84))) Then
             Exit Do
          Endif
          Gosub DisplayTimer
       Loop
       Call Driver(3) ' get line status
       If (OutregsX.AX And &HFF)=&HFF Then ' modem offline
          Exit Do
       Endif
       If (OutregsX.AX And &H80)=False Then ' carrier detect
          Exit Do
       Endif
       ' count resets
       If Config2(49)>False Then
          Var1=Var1+1
          If Var1>Config2(49) Then
             Exit Do
          Endif
       Endif
    Loop
    Exit Sub
 Endif
 ' drop dtr using fossil,
 '    wait for carrier drop
 Do
    For Var=0 To 1 ' drop/raise
       If Var=0 Then
          Strng="Cycle DTR off"
       Else
          Strng="Cycle DTR on"
       Endif
       If Rtrim$(ANSIPrefixChar)<>"" Then
          Strng=ANSIPrefixChar+"FE"+Strng
       Endif
       Call Modem.Status
       If Fossil.Version=1 Then
          Call Extended.Driver2(31,Var) ' x00 1Fh
       Else
          Call Extended.Driver2(6,Var) ' fossil set dtr
       Endif
       Time.Temp!=Timeit!
       Do
	  If Time.Elapsed(Time.Temp!,Csng(Config2(84))) Then
	     Exit Do
	  Endif
          Gosub DisplayTimer
       Loop
       Gosub DisplayTimer
    Next
    Call Driver(3) ' get line status
    If (OutregsX.AX And &HFF)=&HFF Then ' modem offline
       Exit Do
    Endif
    If (OutregsX.AX And &H80)=False Then ' carrier detect
       Exit Do
    Endif
    ' count resets
    If Config2(49)>False Then
       Var1=Var1+1
       If Var1>Config2(49) Then
          Exit Do
       Endif
    Endif
 Loop
 Exit Sub
DisplayTimer:
 If Time.Elapsed(Display.Timer!,1!) Then
    Display.Timer!=Timeit!
    Call Display.Time
    TempX$=Inkey$
    If TempX$=Chr$(27) Then
       Exit Sub
    Endif
 Endif
 Return
Error.Resume46:
 Exit Sub
Error.Trap46:
 Resume Error.Resume46
End Sub

Sub Drop.DTR
 On Local Error Goto Error.Trap46a
 ' check door mode
 If BBS.Mode Then
    Exit Sub
 Endif
 ' check local node
 If Port=-1 Then
    Exit Sub
 Endif
 ' check local login
 If Local.Mode Then
    Exit Sub
 Endif
 ' check fossil
 If Fossil.Active=False Then
    Call Get.MCR(VarZ)
    If VarZ>0 Then
       ' drop dtr
       Out VarZ,0
    Endif
    Exit Sub
 Endif
 ' drop dtr using fossil
 If Fossil.Version=1 Then
    Call Extended.Driver2(31,0) ' x00 1Fh
 Else
    Call Extended.Driver2(6,0) ' fossil set dtr
 Endif
Error.Resume46a:
 Exit Sub
Error.Trap46a:
 Resume Error.Resume46a
End Sub

' get modem control register port address
Sub Get.MCR(VarZ)
 On Local Error Goto Error.Trap46b
 VarX=Base.Address
 If VarX>0 Then
    ' set mcr address
    VarZ=VarX+4
    Exit Sub
 Endif
 ' default port address
 Call Get.BIOS.Port(VarY)
 ' set mcr address
 If VarY=0 Then
    VarZ=0
    Exit Sub
 Endif
 VarZ=VarY+4
Error.Resume46b:
 Exit Sub
Error.Trap46b:
 Resume Error.Resume46b
End Sub

SUB Get.BIOS.Port(VarZ)
 ' get port address from bios
 VarZ = 0
 SELECT CASE Port
 CASE 0
    DEF SEG = 0
    p = PEEK(&H400)
    q = PEEK(&H401)
    VarZ = q * &H100 + p
    DEF SEG
    If p=0 and q=0 Then
       VarZ = &H3F8
    Endif
 CASE 1
    DEF SEG = 0
    p = PEEK(&H402)
    q = PEEK(&H403)
    VarZ = q * &H100 + p
    DEF SEG
    If p=0 and q=0 Then
       VarZ = &H2F8
    Endif
 CASE 2
    DEF SEG = 0
    p = PEEK(&H404)
    q = PEEK(&H405)
    VarZ = q * &H100 + p
    DEF SEG
    If p=0 and q=0 Then
       VarZ = &H3E8
    Endif
 CASE 3
    DEF SEG = 0
    p = PEEK(&H406)
    q = PEEK(&H407)
    VarZ = q * &H100 + p
    DEF SEG
    If p=0 and q=0 Then
       VarZ = &H2E8
    Endif
 ' no bios so hardcode
 CASE 4
    VarZ = &H3E0
 CASE 5
    VarZ = &H2E0
 CASE 6
    VarZ = &H338
 CASE 7
    VarZ = &H238
 END SELECT
END SUB

Sub Init.Fossil.Port
 On Local Error Goto Error.Trap47
 OutregsX.AX=&H0000
 If Fossil.Active=False Then
    Exit Sub
 Endif
 If BBS.Mode=False Then
    Call Extended.Driver(29) ' deactivate
 Endif
 Call Extended.Driver(28) ' activate
 Call Extended.Driver(9) ' purge output
 Call Extended.Driver(10) ' purge input
 Call Extended.Driver(15) ' flow control
 Call Extended.Driver(16) ' enable break
Error.Resume47:
 Exit Sub
Error.Trap47:
 Resume Error.Resume47
End Sub

Sub Set.Driver
 On Local Error Goto Error.Trap48
 Select Case Modem.Baud ' n,8,1
 Case 300!
    InregsX.AX=&H0043
 Case 600!
    InregsX.AX=&H0063
 Case 1200!
    InregsX.AX=&H0083
 Case 2400!
    InregsX.AX=&H00A3
 Case 4800!
    InregsX.AX=&H00C3
 Case 9600!
    InregsX.AX=&H00E3
 Case 19200!
    If Fossil.Active Then
       InregsX.AX=&H0003
    Else
       Call SetBPS(1)
       Exit Sub
    Endif
 Case 38400!
    If Fossil.Active Then
       InregsX.AX=&H0023
    Else
       Call SetBPS(2)
       Exit Sub
    Endif
 Case Else
    If Fossil.Active=0 Then
       Select Case Modem.Baud
       Case 57600!
          Call SetBPS(3)
          Exit Sub
       Case 115200!
          Call SetBPS(4)
          Exit Sub
       End Select
    Endif
    If Fossil.Active=0 Then
       InregsX.AX=&H00E3 ' 9600
    Else
       InregsX.AX=&H0023 ' 38400
    Endif
 End Select
 ' set baud rate and parity
 Call Driver(0)
Error.Resume48:
 Exit Sub
Error.Trap48:
 Resume Error.Resume48
End Sub

' sets port dlab (Divisor Latch Access Bit)
Sub SetBPS(Var)
 On Local Error Goto Error.Trap48a
 Select Case Var
 Case 1 ' 19200
    LByte=6
 Case 2 ' 38400
    LByte=3
 Case 3 ' 57600
    LByte=2
 Case 4 ' 115200
    LByte=1
 End Select
 Var2=Base.Address
 If Var2=0 Then
    Call Get.BIOS.Port(Var2)
    If Var2=0 Then
       Exit Sub
    Endif
 Endif
 ' divisor latch low
 LSB=Var2 ' 3F8x
 ' divisor latch high
 MSB=Var2+1 ' 3F9x
 ' line control register
 LCR=Var2+3 ' 3FBx
 MSBSave=INP(MSB)
 OUT MSB,0
 OUT LCR,128 ' dlab
 ' set baud rate
 OUT LSB,LByte ' least significant byte
 OUT MSB,0 ' most significant byte
 OUT LCR,3 ' n,8,1
 OUT MSB,MSBSave
Error.Resume48a:
 Exit Sub
Error.Trap48a:
 Resume Error.Resume48a
End Sub

Sub Clear.Screen
 On Local Error Goto Error.Trap52
 If Mouse.Present Then
    Call Mouse.Function(2,0)
 Endif
 Color 7,0
 Call ScreenANSI(1,37,7)
 Cls
 Call ScreenANSI(3,0,0)
 Locate Max.Row-1,1,1
 Call ScreenANSI(2,Max.Row,1)
Error.Resume52:
 Exit Sub
Error.Trap52:
 Resume Error.Resume52
End Sub

Sub Chain.Program(Var$,Var1,Var2)
 On Local Error Goto Error.Trap60
 Close #TempFile1
 Close #TempFile2
 Shell.Type=False
 If Var$="DNDEDIT.EXE" Or Var$="DNDCNFG.EXE" Then
    If Logged.In Then
       Shell.Type=True
    Endif
 Endif
 If Var$="DNDSIC.EXE" Then
    Shell.Type=-1
 Endif
 Select Case Logged.In
 Case IS<=True
    Call Write.User.Record
    Call Status.Line(-2)
    If Var2 Then
       If Shell.Type Then
          Call Save.Screen(False)
       Endif
    Endif
 Case False
    Call Clear.Screen
 End Select
 Chained=True
 Remote=Local.Mode
 Local.Mode=Var1
 Shelled=Var2
 Erase Monster.Class, Monster.Percent, Monster.Rate, NonPlayer.Rooms, _
 Menu1, Menu2, Node.Work.Array1, Node.Work.Array2
 InterestRate1=Config1(10)
 InterestRate2=Config1(30)
 Allow.High.Class=Config2(48)
 Allow.Remote.Edit=Config2(81)
 Var2$=DND.Path+Var$
 If Dir$(Var2$)<>Nul Then
    If TempX$="-1" Then
       TempX$=Nul
       Goto NextChain
    Endif
    TempX$=Nul
    If Var$="DNDSIC.EXE" Then
       If Sysop Then
          No.Input.Out="N"
          Strng="Override sic account(y/n)?"
          Call IO.I
          If Yes Then
             Do While Lost.Carrier=0
                Strng="Enter account:"
                Call IO.I
                Out2=Ltrim$(Rtrim$(Out2))
                If Out2=Nul Then Exit Do
                If Len(Out2)=8 Then
                   TempX$=Ucase$(Out2)
                   Exit Do
                Endif
                Strng="Account must be 8 chars.."
                Call IO.O
             Loop
          Endif
       Endif
    Endif
NextChain:
    If Var$="DNDSIC.EXE" Then
       Close
    Endif
    'Print "Chained: "; Var2$
    Chain Var2$
 Endif
 VarX=53 ' file not found
 Local.Mode=Remote
 If Shell.Type=-1 Then
    Call Save.Screen(True)
 Endif
 Call Read.Config(False)
 Call Reset.Status.Line
 Call Reset.Cursor
 Graphics.Off=False
 Call Restore.Color
Error.Resume60:
 If Debug.Mode Then
    Graphics.Off=True
    Call Modem.ANSI(1,37,7)
    Call ScreenANSI(1,37,15)
    Call Error.Message(VarX,Strng2)
    Strng="Error:"+Strng2+" chaining to "+Var2$+". Reinstall Dndbbs."
    Call IO.O
 Endif
 Exit Sub
Error.Trap60:
 VarX=Err
 Resume Error.Resume60
End Sub

Sub Chain.Command(Var1)
 On Local Error Goto Error.Trap61
 Call Get.Config2x(Option1$,Var1)
 Close #TempFile1
 Close #TempFile2
 Chained=-2
 If Remote=-2 Then ' Console function keys.
    '
 Else
    Remote=Local.Mode
 Endif
 Shelled=False
 Shell.Type=False
 Last.Module.Number=Var1
 Var2$=Option1$+"CMD"+Right$(Str$(Var1+1000),3)+".EXE"
 If Sysop And Local.Mode And Debug.Mode Then
    If Var1<>999 Then
       Strng="Debug running - " + Var2$
       Call IO.O
    Endif
 Endif
 If Dir$(Var2$)<>Nul Then
    Chain Var2$
 Endif
 VarX=53 ' file not found
Error.Resume61:
 If Var1=999 Then
    Exit Sub
 Endif
 If Debug.Mode Then
    Graphics.Off=True
    Call Modem.ANSI(1,37,7)
    Call ScreenANSI(1,37,15)
    Call Error.Message(VarX,Strng2)
    Strng="Error:"+Strng2+" chaining to "+Var2$+". Reinstall Dndbbs."
    Call IO.O
 Endif
 Exit Sub
Error.Trap61:
 VarX=Err
 Resume Error.Resume61
End Sub

Sub Get.Config2x(Option1$,Var1)
 On Local Error Goto Error.Trap61a
 VarX$="CMD"+Right$(Str$(Var1+1000),3)
 Option1$=DND.Path
 VarX=0
 Do
    VarX=VarX+1
    If VarX>Options.Lines Then
       Exit Do
    Endif
    Var$=Ltrim$(Rtrim$(Options.Array(VarX)))
    If Ucase$(Var$)="[RAMDRIVE]" Then
       Do
          VarX=VarX+1
          If VarX>Options.Lines Then
             Exit Sub
          Endif
          Var$=Ltrim$(Rtrim$(Options.Array(VarX)))
          If Ucase$(Var$)="[END]" Then
             Exit Sub
          Endif
          VarZ=Instr(Var$,"=")
          If VarZ Then
             Var2$=Ltrim$(Rtrim$(Left$(Var$,VarZ-1)))
             Var3$=Ltrim$(Rtrim$(Mid$(Var$,VarZ+1)))
             If Ucase$(Var2$)=VarX$ Or Ucase$(Var2$)="CMD*" Then
                ' force cmd module to ramdrive
                If Var3$="-1" Then
                   Option1$=Data.Path3
                Else
                   Var3$=Ucase$(Var3$)
                   If Len(Var3$) Then
                      If Left$(Var3$,1)>="A" And Left$(Var3$,1)<="Z" Then
                         Select Case Len(Var3$)
                         Case 1
                            Option1$=Var3$+":\"
                         Case 2
                            If Right$(Var3$,1)=":" Then
                               Option1$=Var3$+"\"
                            Endif
                         Case 3
                            If Right$(Var3$,2)=":\" Then
                               Option1$=Var3$
                            Endif
                         End Select
                      Endif
                   Endif
                Endif
             Endif
          Endif
       Loop
    Endif
 Loop
Error.Resume61a:
 Exit Sub
Error.Trap61a:
 VarX=Err
 Resume Error.Resume61a
End Sub

' Var=0  store screen
' Var=-1 restore screen
Sub Save.Screen(Var)
 On Local Error Goto Error.Trap62
 Filename=DND.Path+"SCRN"+NodeX+".DEF"

 ' store screen
 If Var=False Then
    Close #TempFile1
    Open Filename For Output Shared As #TempFile1
    For Var1=1 To Max.Row
       For Var2=1 To User.LineLength
          TempZ1=Screen(Var1,Var2) ' char
          TempZ2=Screen(Var1,Var2,1) ' color
          Print #TempFile1,TempZ1,TempZ2
       Next
    Next
    Exit Sub
 Endif

 ' restore screen remote
 If Local.Mode=False Then
    Close #TempFile1
    Open Filename For Input Shared As #TempFile1

    For Var1=1 To Max.Row-2

       ' restore colors
       Call Modem.ANSI(4,37,15)
       Call Modem.ANSI(4,40,0)

       ' current colors
       B1=0 ' background
       F1=15 ' foreground

       ' locate to row 2 remote
       Call Modem.ANSI(2,Var1+1,1)

       For Var2=1 To User.LineLength
          Input #TempFile1,TempZ1,TempZ2

          ' get character color
          VarB=Int(TempZ2/16) ' background
          VarF=TempZ2 Mod 16 ' foreground

          ' change foreground
          If VarF<>F1 Then
             F1=VarF
             Gosub GetForeGround2
             If VarZ1 Then ' intensity
                Call Modem.ANSI(1,VarF1,VarF2) ' low
             Else
                Call Modem.ANSI(4,VarF1,VarF2) ' high
             Endif
          Endif

          ' change background
          If VarB<>B1 Then
             B1=VarB
             Gosub GetBackGround2
             Call Modem.ANSI(5,VarB1,VarB2)
          Endif

          ' get character
          Var$=Chr$(TempZ1)
          Call Put.Modem(Var$)
       Next
    Next
 Endif

 ' restore screen local
 Close #TempFile1
 Open Filename For Input Shared As #TempFile1
 For Var1=1 To Max.Row-1
    For Var2=1 To User.LineLength
       Input #TempFile1,TempZ1,TempZ2
       VarB=Int(TempZ2/16)
       VarF=TempZ2 Mod 16
       Locate Var1,Var2,0
       Color VarF,VarB
       Print Chr$(TempZ1);
    Next
 Next

 ' close/locate/kill
 Close #TempFile1
 Locate ,,1
 Kill Filename
 Exit Sub

GetBackGround2:
 Select Case VarB
 Case 0
    VarB1=40
    VarB2=0
 Case 1
    VarB1=44
    VarB2=16
 Case 2
    VarB1=42
    VarB2=32
 Case 3
    VarB1=46
    VarB2=48
 Case 4
    VarB1=41
    VarB2=64
 Case 5
    VarB1=45
    VarB2=80
 Case 6
    VarB1=43
    VarB2=96
 Case 7
    VarB1=47
    VarB2=112
 Case Else
    VarB1=40
    VarB2=0
 End Select
 Return

GetForeGround2:
 Select Case VarF
 ' high-intensity
 Case 8
    VarF1=30
    VarF2=8
    VarZ1=0
 Case 12
    VarF1=31
    VarF2=12
    VarZ1=0
 Case 10
    VarF1=32
    VarF2=10
    VarZ1=0
 Case 14
    VarF1=33
    VarF2=14
    VarZ1=0
 Case 9
    VarF1=34
    VarF2=9
    VarZ1=0
 Case 13
    VarF1=35
    VarF2=13
    VarZ1=0
 Case 11
    VarF1=36
    VarF2=11
    VarZ1=0
 Case 15
    VarF1=37
    VarF2=15
    VarZ1=0
 ' low-intensity
 Case 0
    VarF1=30
    VarF2=0
    VarZ1=-1
 Case 4
    VarF1=31
    VarF2=4
    VarZ1=-1
 Case 2
    VarF1=32
    VarF2=2
    VarZ1=-1
 Case 6
    VarF1=33
    VarF2=6
    VarZ1=-1
 Case 1
    VarF1=34
    VarF2=1
    VarZ1=-1
 Case 5
    VarF1=35
    VarF2=5
    VarZ1=-1
 Case 3
    VarF1=36
    VarF2=3
    VarZ1=-1
 Case 7
    VarF1=37
    VarF2=7
    VarZ1=-1
 Case Else
    VarF1=37
    VarF2=15
    VarZ1=0
 End Select
 Return
Error.Resume62:
 Exit Sub
Error.Trap62:
 Resume Error.Resume62
End Sub

Sub Check.Program(Var)
 On Local Error Goto Error.Trap63
 Var=False
 If Security.Guard=False Then
    Var=True
 Endif
 If Lost.Carrier Then
    Var=True
 Endif
 If Node.Abort Then
    Var=True
 Endif
Error.Resume63:
 Exit Sub
Error.Trap63:
 Resume Error.Resume63
End Sub

' make sure node is still active
Sub Restore.Active
 On Local Error Goto Error.Trap63a
 VarX2=Node+1
 Call Read.Message.Record(MessWorkFile1,VarX2)
 MessWorkRecord1.ActiveNode=True
 MessWorkRecord1.SuspendNode=False
 Call Write.Message.Record(MessWorkFile1,VarX2)
Error.Resume63a:
 Exit Sub
Error.Trap63a:
 Resume Error.Resume63a
End Sub

Sub Stdin(Var$)
 Var$=Nul
 ' check stdin length
 InregsX.AX = &H4202 ' eof
 InregsX.BX = 0 ' stdin
 InregsX.CX = 0
 InregsX.DX = 0
 CALL InterruptX(&H21, InregsX, OutregsX)
 IF OutregsX.AX = 0 THEN ' no input found
     EXIT SUB
 END IF
 Print "Illegal standard input attempted."
 END
End Sub
