 Rem File: Cmd143.bas
 Rem Sub: Chat.Loop
 Rem $Include: 'dndbbs.inc'
 Common Shared Temp.Prompt As String
 If Chained<>-2 Then
    Color 15,0
    Print "DNDBBS Command 143 -- Chat.Loop."
    Color 7,0
    End
 Endif
 Var1=Hidden.Player
 Call Chat.Loop
 Hidden.Player=Var1
Main.Resume:
 Chain DND.Path+"DNDBBS.EXE"
 End
Main.Error:
 Resume Main.Resume

Sub Chat.Loop
 On Local Error Goto Err.Trap24
 Gosub Get.Config
 Dim Alarm.Flags(1 to Max.Alarms) As Integer
 Dim Alarm.Start(1 to Max.Alarms) As Single
 Dim Alarm.Time(1 to Max.Alarms) As Single
 Dim Alarm.Type(1 to Max.Alarms) As Integer
 Dim Alarm.Beep(1 to Max.Alarms) As Integer
 Dim Alarm.Color(1 to Max.Alarms) As Integer
 Dim Alarm.Mess(1 to Max.Alarms) As String
 For VarZ=1 To Max.Alarms
    Alarm.Flags(VarZ)=0
    Alarm.Start(VarZ)=0!
    Alarm.Time(VarZ)=0!
    Alarm.Type(VarZ)=0
    Alarm.Beep(VarZ)=0
    Alarm.Color(VarZ)=0
    Alarm.Mess(VarZ)=""
 Next
 If Chat.Mode=True Then
    Var$="Press <ctrl-break>/<ctrl-c> to interrupt"
    If Local.Mode=False Then
       Var$="Press <control-c>/<control-k> to interrupt"
    Endif
    Select Case Last.Command.Number ' 1, 4, 14
    Case AutoCommand
       Strng="Chat Auto-mode."
    Case ChatCommand
       Strng="Chat Talk-mode."
       If Len(Config3(78)) Then
          Var$="Enter "+Config3(78)+"Quit to exit. Type "+Config3(78)+"? for Help"
       Endif
    Case WaitCommand
       Strng="Chat Wait-mode."
    Case Else
       Strng="Chat mode."
    End Select
    Strng=Strng+" "+Var$+"."
    Graphics.Off=False
    Call IO.O
 Endif
 Allow.Break=True
 Auto.Break=False
 Break=False
 Var1!=Timeit!
 Var2=False
 Var3=Last.Command.Number
 Var4=Command.Time.Limit
 Var5=Var4
 VarZ#=0#
 Last.Command="Chatting"
 Call Update.Mess(4)
 Do While Lost.Carrier=False
    Allow.Break=True
    If New.Room Then
       Call Enter.New.Room
    Endif
    If Var3=AutoCommand Or Var3=WaitCommand Then
       VarQ!=Timeit!
       Do While Lost.Carrier=False
          If option1!>0! Then
             If Time.Elapsed(VarQ!,Option1!) Then
                Exit Do
             Endif
          Endif
          Call Keyboard(Var1)
          If Break Then
             If Option4 Then
                Strng="<escape>"
                Call IO.O
             Endif
             Exit Do
          Endif
          Call Get.Modem(False)
          If Len(Buffer) Then
             If Instr(Buffer,Chr$(27)) Then
                Buffer=Nul
                If Option4 Then
                   Strng="<escape>"
                   Call IO.O
                Endif
                Exit Do
             Endif
             Buffer=Nul
          Endif
          Call Check.Break
          If Auto.Break Then
             'Strng="*break*"
             'Call IO.O
             Exit Do
          Endif
          Call Check.Carrier
          Call Read.Mess

          ' online user entered /chat and started talking.
          If Chat.Mode=1 Then
             Chat.Mode=True
             ' switch from /wait mode to /chat mode.
             If Var3=WaitCommand Then
                If option2 Then
                   Strng="*switch*"
                   Call IO.O
                Endif
                Var3=ChatCommand
                Var4=Var5
                Exit Do
             Endif
          Endif

          Call IO.Timing
          Call Read.Node.Mess("")
       Loop
    Endif
    If Var4>False Then
       If Time.Elapsed(Var1!,Csng(Var4)) Then
          Var2=True
          Exit Do
       Endif
    Endif
    If Option1#>0# Then
       VarZ#=VarZ#+1#
       If VarZ#>Option1# Then
          Var2=-2
          Exit Do
       Endif
    Endif
    If Var3=ChatCommand Then
       Graphics.Off=True
       Graphics.On=True
       If Rtrim$(NodeX)=Nul Then
          Strng="#>"
       Else
          Strng=NodeX+">"
       Endif
       Call Modem.ANSI(4,37,15)
       Call ScreenANSI(4,37,15)
       Gosub IO.IX ' special
       If VarZ Then
          Call More.Prompt
       Endif
       If Lost.Carrier Then
          Exit Do
       Endif
       If Node.Abort Then
          Exit Do
       Endif
       If Auto.Break Then
          Call IO.O
          Exit Do
       Endif
       Graphics.Off=False
       If Len(Out2) Then
          ' parse mode command
          VarZ=0
          If Lcase$(Out2)="!break" Then
             VarZ=-1
             Option3=Not Option3
             If Option3 Then
                Strng="Break display on."
             Else
                Strng="Break display off."
             Endif
             Call IO.O
          Endif
          If Lcase$(Out2)="!switch" Then
             VarZ=-1
             Option2=Not Option2
             If Option2 Then
                Strng="Switch display on."
             Else
                Strng="Switch display off."
             Endif
             Call IO.O
          Endif
          If Lcase$(Out2)="!alarmcolor" Then
             VarZ=-1
             Strng = "Enter alarm(1-" + LTrim$(Str$(Max.Alarms)) + ")? "
             Call IO.I : Var$ = Out2
             VarZZ = Int(Val(Var$)+.5)
StartColor:
             If VarZZ >= 1 And VarZZ <= Max.Alarms Then
                If Alarm.Flags(VarZZ) Then
                   Strng = "Enter alarm color(?=Help, 0-clear, 1-7)? "
                   Call IO.I : Var2$ = Out2
                   VarZZ2 = Int(Val(Var2$)+.5)
                   If Var2$ = "?" Then
                      Strng = "Foreground intensity:" : Call IO.O
                      Strng = "  0  -  Clear color" : Call IO.O
                      Strng = "  1  -  Blue" : Call IO.O
                      Strng = "  2  -  Green" : Call IO.O
                      Strng = "  3  -  Cyan" : Call IO.O
                      Strng = "  4  -  Red" : Call IO.O
                      Strng = "  5  -  Magenta" : Call IO.O
                      Strng = "  6  -  Yellow" : Call IO.O
                      Strng = "  7  -  White" : Call IO.O
                      Call Modem.ANSI(4,33,14)
                      Call ScreenANSI(4,33,14)
                      Call More.Prompt
                      Goto StartColor
                   End If
                   If VarZZ2 = 0 Then
                      Alarm.Color(VarZZ) = 0
                      Strng = "Alarm" + Str$(VarZZ) + " color cleared." : Call IO.O
                   Else
                      If VarZZ2 >= 1 And VarZZ2 <= 7 Then
                         Alarm.Color(VarZZ) = VarZZ2 + 8
                         Strng = "Alarm" + Str$(VarZZ) + " color set." : Call IO.O
                      Else
                         Strng = "Illegal alarm color." : Call IO.O
                      End If
                   End If
                Else
                   Strng = "Alarm not set." : Call IO.O
                Endif
             Else
                Strng = "Unknown alarm." : Call IO.O
             End If
          Endif
          If Lcase$(Out2)="!alarmmessage" Then
             VarZ=-1
             Strng = "Enter alarm(1-" + LTrim$(Str$(Max.Alarms)) + ")? "
             Call IO.I : Var$ = Out2
             VarZZ = Int(Val(Var$)+.5)
             If VarZZ >= 1 And VarZZ <= Max.Alarms Then
                If Alarm.Flags(VarZZ) Then
                   Strng = "Enter text message? "
                   Call IO.I : Var$ = Out2
                   If Var$ = "" Then
                      Strng = "Text cleared."
                   Else
                      Strng = "Text set."
                   Endif
                   Call IO.O
                   Alarm.Mess(VarZZ) = Var$
                Else
                   Strng = "Alarm not set." : Call IO.O
                End If
             Else
                Strng = "Unknown alarm." : Call IO.O
             End If
          End If
          If Lcase$(Out2)="!alarmclear" Then
             For TempQ = 1 To Max.Alarms
                Alarm.Flags(TempQ)=0
                Alarm.Start(TempQ)=0!
                Alarm.Time(TempQ)=0!
                Alarm.Type(TempQ)=0
                Alarm.Color(TempQ)=0
                Alarm.Mess(TempQ)=""
             Next
             Strng="All alarms cleared."
             Call IO.O
          Endif
          If Lcase$(Out2)="!alarmhelp" Then
             VarZ=-1
             Graphics.On=False
             Graphics.Off=False
             Strng="Helplist for alarms.."
             Call IO.O
             Graphics.Off=True
             Strng="!alarm" : Call IO.O
             Strng="  sets seconds per alarm for"+Str$(Max.Alarms)+" alarms." : Call IO.O
             Strng="!alarmbeep" : Call IO.O
             Strng="  toggles alarm beeps." : Call IO.O
             Strng="!alarmcolor" : Call IO.O
             Strng="  sets alarm notice color." : Call IO.O
             Strng="!alarmlist" : Call IO.O
             Strng="  lists settings for all alarms set." : Call IO.O
             Strng="!alarmcount" : Call IO.O
             Strng="  displays number of alarms set." : Call IO.O
             Strng="!alarmtype" : Call IO.O
             Strng="  toggles alarm time display." : Call IO.O
             Strng="!alarmclear" : Call IO.O
             Strng="  clears all alarms." : Call IO.O
             Strng="!alarmmessage" : Call IO.O
             Strng="  sets alarm message." : Call IO.O
             Graphics.Off=False
             Call More.Prompt
          Endif
          If Lcase$(Out2)="!alarmcount" Then
             VarZ=-1
             Temp5=0
             For VarZZ=1 To Max.Alarms
                If Alarm.Flags(VarZZ) Then
                   Temp5=Temp5+1
                Endif
             Next
             If Temp5=0 Then
                Strng="No alarms are set."
                Call IO.O
             Else
                Strng="There are"+Str$(Temp5)+" alarms set."
                Call IO.O
             Endif
          Endif
          If Lcase$(Out2)="!alarmlist" Then
             Temp5=0 : TempC=0
             VarZ=-1
             VarZ1=0
             For VarZZ=1 To Max.Alarms
                If Alarm.Flags(VarZZ) Then
                   VarZ1=-1
                   Strng="Alarm"+Str$(VarZZ)+" set to"+Str$(Alarm.Time(VarZZ))+" seconds."
                   Elapsed!=Timeit!-Alarm.Start(VarZZ)
                   If Elapsed!<0! Then Elapsed!=Elapsed!+86400!
                   Remaining!=Alarm.Time(VarZZ)-Elapsed!
                   Strng=Strng+" ("+Ltrim$(Str$(Remaining!))+" seconds remaining)"
                   If Alarm.Type(VarZZ)=0 Then
                      Strng=Strng+" [time display on]"
                   Else
                      Strng=Strng+" [time display off]"
                   Endif
                   If Alarm.Beep(VarZZ)=0 Then
                      Strng=Strng+" {beep on}"
                   Else
                      Strng=Strng+" {beep off}"
                   Endif
                   If Len(Rtrim$(Alarm.Mess(VarZZ))) Then
                     Strng = Strng + " (message set)"
                   Endif
                   Call IO.O
                   If Alarm.Color(VarZZ)>0 Then
                      Select Case Alarm.Color(VarZZ)
                      Case 9 
                         Strng3="blue"
                      Case 10
                         Strng3="green"
                      Case 11
                         Strng3="cyan"
                      Case 12 
                         Strng3="red"
                      Case 13 
                         Strng3="magenta"
                      Case 14 
                         Strng3="yellow"
                      Case 15 
                         Strng3="white"
                      Case Else ' unknown
                         Strng3="<n/a>"
                      End Select
                      Strng = "Set: (color is"+Str$(Alarm.Color(VarZZ)-8)+" : "+Strng3 +")"
                      Call IO.O
                   Endif
                   Temp5=Temp5+1
                   If Temp5>=User.Pagelength-2 Then
                      If Continuous=False Then
                         Call More.Prompt
                         Temp5=False
                         If No Or Quit Then
                            Exit FOr
                         Endif
                      Endif
                   Endif
                Endif
             Next
             If Temp5 Then
                Call More.Prompt
             Endif
             If VarZ1=0 Then
                Strng="No alarms set."
                Call IO.O
             Endif
          Endif
          If Lcase$(Out2)="!alarmbeep" Then
             VarZ=-1
             Strng="Enter alarm(1-"+Ltrim$(Str$(Max.Alarms))+")? "
             Call IO.I
             VarZZ=Int(Val(Out2)+.5)
             If VarZZ>=1 And VarZZ<=Max.Alarms Then
                If Alarm.Flags(VarZZ) Then
                   Strng = "Enter number of beeps(on/off/<n>)? "
                   Call IO.I
                   Var$ = Out2
                   VarZZ2 = Int(Val(Out2)+.5)
                   Select Case Ucase$(Var$)
                   Case "ON"
                      Alarm.Beep(VarZZ) = 0
                      Strng = "Alarm beep on."
                      Call IO.O
                   Case "OFF"
                      Alarm.Beep(VarZZ) = -1
                      Strng = "Alarm beep off."
                      Call IO.O
                   Case Else
                      If VarZZ2 >= 1 Then
                         Alarm.Beep(VarZZ) = VarZZ2
                         Strng = "Alarm beep on."
                         Call IO.O
                      Else
                         Strng = "Alarm not set."
                         Call IO.O
                      Endif
                   End Select
                Else
                   Strng="Alarm not set."
                Endif
             Else
                Strng="Unknown alarm."
             Endif
             Call IO.O
          Endif
          If Lcase$(Out2)="!alarmtype" Then
             VarZ=-1
             Strng="Enter alarm(1-"+Ltrim$(Str$(Max.Alarms))+")? "
             Call IO.I
             VarZZ=Int(Val(Out2)+.5)
             If VarZZ>=1 And VarZZ<=Max.Alarms Then
                If Alarm.Flags(VarZZ) Then
                   Alarm.Type(VarZZ)=Not Alarm.Type(VarZZ)
                   If Alarm.Type(VarZZ)=0 Then
                      Strng="Alarm time display on."
                   Else
                      Strng="Alarm time display off."
                   Endif
                Else
                   Strng="Alarm not set."
                Endif
             Else
                Strng="Unknown alarm."
             Endif
             Call IO.O
          Endif
          If Lcase$(Out2)="!alarm" Then
             VarZ=-1
             Strng="Enter alarm(1-"+Ltrim$(Str$(Max.Alarms))+")? "
             Call IO.I
             VarZZ=Int(Val(Out2)+.5)
             If VarZZ>=1 And VarZZ<=Max.Alarms Then
                Strng="Enter alarm time(seconds)? "
                Call IO.I
                VarZZ2!=Int(Val(Out2)+.5)
                If VarZZ2!=0! Then
                   Alarm.Flags(VarZZ)=0
                   Strng="Alarm"+Str$(VarZZ)+" cleared." : Call IO.O
                Else
                   If VarZZ2!>0! And VarZZ2!<=86400! Then
                      Alarm.Flags(VarZZ)=-1
                      Alarm.Start(VarZZ)=Timeit!
                      Alarm.Time(VarZZ)=VarZZ2!
                      Alarm.Type(VarZZ)=Option5
                      Alarm.Beep(VarZZ)=Option6
                      Alarm.Color(VarZZ)=Option8
                      Alarm.Mess(VarZZ)=""
                      Strng="Alarm"+Str$(VarZZ)+" set." : Call IO.O
                   Else
                      Strng="Illegal alarm time." : Call IO.O
                   Endif
                Endif
             Endif
          Endif
          If Lcase$(Out2)="!maxnodes" Then
             VarZ=-1
             Strng="Maxnodes:"+Str$(MaxNodes+1)+"."
             Call IO.O
          Endif
          If Lcase$(Out2)="!time" Then
             VarZ=-1
             Call Time.Online(False)
          Endif
          If Lcase$(Out2)="!clock" Then
             VarZ=-1
             Strng="It is now: "+Fclock$
             Call IO.O
          Endif
          If Lcase$(Out2)="!prefix" Then
             VarZ=-1
             If Len(Config3(78)) Then
                Strng="Prefix is "+Config3(78)
                Call IO.O
             Endif
          Endif
          If Lcase$(Out2)="!count" Then
             VarZ=-1
             VarQ=0
             VarQ2=0
             VarQ3=0
             For VarX=False To MaxNodes
                Call Read.Message.Record(MessWorkFile1,VarX+1)
                Strng=Rtrim$(MessWorkRecord1.UserName)
                Call Valid.Name(Strng)
                If TempA Then
                   If MessWorkRecord1.Chatting Then
                      VarQ2=VarQ2+1
                   Endif
                   If MessWorkRecord1.Monitor Then
                      VarQ3=VarQ3+1
                   Endif
                   VarQ=VarQ+1
                Endif
             Next
             Strng="There are"+Str$(VarQ)+" users online and"+Str$(VarQ2)+" users chatting."
             Call IO.O
             If VarQ3 Then
                Strng="Users monitoring:"+Str$(VarQ3)+"."
                Call IO.O
             Endif
          Endif

          If Lcase$(Out2)="!monitor" Then
             VarZ=-1
             VarQ3=0
             For VarX=False To MaxNodes
                Call Read.Message.Record(MessWorkFile1,VarX+1)
                Strng=Rtrim$(MessWorkRecord1.UserName)
                Call Valid.Name(Strng)
                If TempA Then
                   If MessWorkRecord1.Monitor Then
                      VarQ3=VarQ3+1
                   Endif
                Endif
             Next
             Strng="Users monitoring:"+Str$(VarQ3)+"."
             Call IO.O
          Endif

          VarZZ=0
          If Lcase$(Out2)="!users" Then
             VarZZ=1
          Endif
          If Lcase$(Out2)="!userrooms" Then
             VarZZ=2
          Endif
          If Lcase$(Out2)="!userlevels" Then
             VarZZ=3
          Endif
          If VarZZ Then
             VarZ=-1
             VarQ=0
             VarQ2=0
             VarQ3=0
             For VarX=False To MaxNodes
                Call Read.Message.Record(MessWorkFile1,VarX+1)
                Strng=Rtrim$(MessWorkRecord1.UserName)
                Call Valid.Name(Strng)
                If TempA Then
                   If MessWorkRecord1.Monitor Then
                      VarQ3=VarQ3+1
                   Endif
                   If MessWorkRecord1.Chatting Then
                      VarQ2=VarQ2+1
                   Endif
                   VarQ=VarQ+1
                Endif
             Next
             Strng="Users online:"+Str$(VarQ)+", users chatting:"+Str$(VarQ2)+"."
             Call IO.O
             If VarQ3 Then
                Strng="Users monitoring:"+Str$(VarQ3)+"."
                Call IO.O
             Endif
             Strng=Nul
             Select Case VarZZ
             Case 1
                Strng="Userlist:"
             Case 2
                Strng="Userlist rooms:"
             Case 3
                Strng="Userlist levels:"
             Case Else
                Strng="Userlist:"
             End Select
             Call IO.O
             VarY=0
             Strng=Nul
             For VarX=False To MaxNodes
                Call Read.Message.Record(MessWorkFile1,VarX+1)
                Strng2=Rtrim$(MessWorkRecord1.UserName)
                Call Valid.Name(Strng2)
                If TempA Then
                   Strng2=Lcase$(Strng2)
                   Mid$(Strng2,1,1)=Ucase$(Mid$(Strng2,1,1))
                   If VarZZ=2 Then
                      Strng2=Strng2+" ("+Ltrim$(Str$(MessWorkRecord1.RoomNumber))+")"
                   Endif
                   If VarZZ=3 Then
                      Strng2=Strng2+" ["+Ltrim$(Str$(MessWorkRecord1.Level))+"]"
                   Endif
                   ' detect wordwrap and paginate.
                   If Len(Strng+Strng2+", ")>=User.LineLength Then
                      Call IO.O
                      Strng=Nul
                      VarY=VarY+1
                      If VarY>=User.PageLength-2 Then
                         VarY=0
                         Call More.Prompt
                      Endif
                      Strng=Strng2+", "
                   Else
                      Strng=Strng+Strng2+", "
                   Endif
                Endif
             Next
             If Len(Strng) Then
                Strng=Left$(Strng,Len(Strng)-2)+"."
                Call IO.O
             Endif
          Endif
          ' always allow /quit
          If Len(Config3(78))=0 Then
             If Lcase$(Out2)="/quit" Then
                VarZ=-1
                Exit Do
             Endif
             If Lcase$(Out2)="/q" Then
                VarZ=-1
                Exit Do
             Endif
          Endif
          ' parse node command
          If Len(Config3(78)) Then
             If VarZ=0 Then
                If Left$(Out2,Len(Config3(78)))=Config3(78) Then
                   VarZ=-1
                   Call Command.Loop
                   ' /quit
                   If Last.Command.Number=12 Then
                      Exit Do
                   Endif
                   ' enter /auto or /wait.
                   If Last.Command.Number=AutoCommand Or _
                      Last.Command.Number=WaitCommand Then
                      Var3=Last.Command.Number
                      Var4=Command.Time.Limit
                   Endif
                Endif
             Endif
          Endif
          If VarZ=0 Then
             ' send a /chat message to other nodes.
             If Len(Out2) Then
                Call Send.Mess(26,SFalse,SFalse,Out2)
                Out2=Nul
             Endif
          Endif
       Endif
    Endif
    If Auto.Break Then
       If Var3=ChatCommand Then
          Exit Do
       Endif
       If Option3 Then
          Strng="*break*"
          Call IO.O
       Endif
       Auto.Break=False
       Break=False
       Var3=ChatCommand
       Var4=Var5
       Strng="Chat Talk-mode."
       If Len(Config3(78)) Then
          Strng=Strng+" Enter "+Config3(78)+"Quit to exit. Type "+Config3(78)+"? for Help.."
       Endif
       Call IO.O
    Endif

    Call Get.Modem(False)
    If Len(Buffer) Then
       If Instr(Buffer,Chr$(27)) Then
          Buffer=Nul
          If Option4 Then
             Strng="<escape>"
             Call IO.O
          Endif
          Exit Do
       Endif
       Buffer=Nul
    Endif

    Call Keyboard(Var1)
    If Lost.Carrier Then
       Exit Do
    Endif
    If Node.Abort Then
       Exit Do
    Endif
    If Auto.Break Then
       'Strng="*break*"
       'Call IO.O
       Exit Do
    Endif
    Hidden.Player=True
    Call Write.Record(UserFile,User.Index)
    Timeout=Timeit!
    Call IO.Timing
    Call Read.Node.Mess("")
    Select Case Var3
    Case AutoCommand
       Last.Command="Chat Auto-Mode"
    Case ChatCommand
       Last.Command="Chat Talk-Mode"
    Case WaitCommand
       Last.Command="Chat Wait-Mode"
    Case Else
       Last.Command="Chat Mode"
    End Select
    Call Update.Mess(4)
    Call Read.Mess

    ' online user entered /chat and started talking.
    If Chat.Mode=1 Then
       Chat.Mode=True
       ' switch from /wait mode to /chat mode.
       If Var3=WaitCommand Then
          If option2 Then
             Strng="*switch*"
             Call IO.O
          Endif
          Var3=ChatCommand
          Var4=Var5
       Endif
    Endif
 Loop
 If Auto.Break Then
    If Option3 Then
       Strng="*break*"
       Call IO.O
    Endif
 Endif
 Allow.Break=False
 Auto.Break=False
 Chat.Mode=False
 Break=False
 Wait.Mode=False
 Last.Command="Exit Chat"
 Call Update.Mess(4)
 If Var2=True Then
    Strng="Command time limit!"
    Call IO.O
 Endif
 If Var2=-2 Then
    Strng="Command rounds exceeded!"
    Call IO.O
 Endif
 Exit Sub

' specialized io function.
'   sopports realtime alarms.
IO.IX:
 Esc=0
 VarZ=0
 If Lost.Carrier Then
    Return
 Endif
 If Carriage.Return=-2 Then
    Eat$=Nul
 Else
    Call Get.Cursor(VarC)
    If VarC>1 Then
       Call Line.Return
    Endif
 Endif

 ' preemptive warning
 Strng3=Strng
 Var$=Strng
 Call IO.Timing
 Strng=Var$

 ' display input prompt
Start.Loop:
 Carriage.Input=True
 Strng=Strng+Type.Ahead
 Temp.Prompt=Strng
 Call IO.O
 Out2=Type.Ahead
 Type.Ahead=Nul
 Var!=Timeit!
 Var2!=Timeit!
 VarZ=False

 ' check system messages
 Call Read.Node.Mess("")

 ' start i/o loop
 Call Modem.ANSI(4,37,15)
 Call ScreenANSI(4,37,15)
 Do
    Char$=Nul
    Timeout=Timeit!
    Do Until Len(Char$)
       Call Check.Carrier
       If Lost.Carrier Then
          Return
       Endif
       Call Check.Break
       Call Get.Modem(False)
       Call Keyboard(Var)
       If Var Then
	  Strng=Nul
	  Call IO.O
          Return
       Endif
       If Auto.Break Then
	  Exit Do
       Endif
       If Break Then
	  Exit Do
       Endif
       If Len(Buffer) Then
	  Char$=Left$(Buffer,1)
	  Buffer=Mid$(Buffer,2)
       Endif
       If Time.Elapsed(Var2!,1!) Then
          Var2!=Timeit!
          ' check alarms
          For VarZZ=1 To Max.Alarms
             If Alarm.Time(VarZZ)=0! Then
                Alarm.Flags(VarZZ)=0
             Endif
          Next
          For VarZZ=1 To Max.Alarms
             VarZ5=Alarm.Flags(VarZZ)
             VarZ2!=Alarm.Start(VarZZ)
             VarZ3!=Alarm.Time(VarZZ)
             If VarZ5 Then
                If VarZ2!>0! And VarZ3!>0! Then
                   If Time.Elapsed(VarZ2!,VarZ3!) Then
                      VarZ=-1
                      Alarm.Flags(VarZZ)=False
                      If Alarm.Beep(VarZZ)=0 Then
                         Call Put.Modem(Chr$(7))
                         Sound Option21, Option22
                      Endif
                      If Alarm.Beep(VarZZ)>0 Then
                         For VarQ = 1 To Alarm.Beep(VarZZ)
                            Call Put.Modem(Chr$(7))
                            Sound Option21, Option22
                            Sleep 1
                         Next
                      Endif
                      Call IO.O
                      If Alarm.Color(VarZZ) Then
                         TempZ=Alarm.Color(VarZZ)
                         Call Convert.Color2(TempZ)
                      Else
                         Call Modem.ANSI(4,33,14)
                         Call ScreenANSI(4,33,14)
                      End If
                      If Alarm.Type(VarZZ)=0 Then
                         Strng="Alarm"+Str$(VarZZ)+" notice! It is now "+Fclock$
                      Else
                         Strng="Alarm"+Str$(VarZZ)+" notice!"
                      Endif
                      Call IO.O
                      If Len(Rtrim$(Alarm.Mess(VarZZ))) Then
                         Strng = "  Message: "+Alarm.Mess(VarZZ) : Call IO.O
                      Endif
                      Call Modem.ANSI(4,37,15)
                      Call ScreenANSI(4,37,15)
                   Endif
                Endif
             Endif
          Next
       Endif
       If VarZ Then
          Type.Ahead=Out2
          Out2=Nul
          Strng=Strng3
          Goto Start.Loop
       Endif
       If Time.Elapsed(Var!,1!) Then
          Var!=Timeit!
          Call IO.Timing
          Call Read.Node.Mess("")
          If Config2(56)>False Then
             If Time.Elapsed(Timeout,Csng(Config2(56))) Then
                Call Hang.Up(1)
             Endif
          Endif
       Endif
    Loop
    If Auto.Break Then
       Exit Do
    Endif
    Char=Asc(Char$)
    Select Case Char
    Case 27 ' escape
       Esc=-1
       Esc$=Chr$(27)
    Case 3, 11
       Type.Error=False
       Call Set.Break
    Case 8
       Type.Error=False
       If Len(Out2) Then
	  Out2=Left$(Out2,Len(Out2)-1)
	  Call Back.Space
       Endif
    Case 13
       Type.Error=False
       If Out2=Nul Then
	  If Len(No.Input.Out) Then
	     Out2=No.Input.Out
	     OutY$=Lcase$(No.Input.Out)
             If Len(OutY$) Then
                Mid$(OutY$,1,1)=Ucase$(Mid$(OutY$,1,1))
             Endif
	     If User.Echo=False Then
		Call Put.Modem(OutY$)
	     Endif
	     Call Scrn(OutY$)
	  Endif
       Endif
       Call Line.Return
       Call Get.Row(VarX)
       If VarX<(Max.Row-1) Then
          Call Scrn(Chr$(10))
       Endif
       Exit Do
    Case 32 To 126
       ' escape enabled
       If Esc Then
          Esc$=Esc$+Ucase$(Char$)
          Select Case Len(Esc$)
          Case 1 ' escape
             Eat$ = Nul
          Case 2 ' escape[
             If Mid$(Esc$,2,1)="[" Then
                Eat$ = Nul
             Else ' reset
                Esc$=Nul
                Esc=0
             Endif
          Case 3 ' escape[X
             Esc2$=Mid$(Esc$,3,1)
             Select Case Esc2$
             Case "A" To "Z"
                ' ignore ansi sequence
                Eat$=Nul
             End Select
             Esc$=Nul
             Esc=0
          Case Else
             Esc$=Nul
             Esc=0
          End Select
       Else
          Type.Error=False
          If Out2=Nul Then
             If Upper.Case Then
                If Chat=False Then
                   Char$=Ucase$(Char$)
                Endif
             Endif
          Endif
          VarX$=Char$
          If No.Echo=False Then
             If Hidden Then
                VarX$=Mask$
             Endif
             If User.Echo=False Then
                Call Put.Modem(VarX$)
             Endif
          Endif
          Out2=Out2+Char$
          ' wordwrap
          If Word.Wrap=False Then
             Call Get.Cursor(VarC)
             Select Case VarC
             Case 1 To User.Linelength-1
                Call Scrn(VarX$)
             Case Else
                Call Line.Return
                Exit Do
             End Select
          Endif
          If Word.Wrap Then
             If No.Echo=False Then
                Call Get.Cursor(VarC)
                Select Case VarC
                Case 1 To User.LineLength-1
                   Call Scrn(VarX$)
                Case Else
                   VarL=False
                   VarW=False
                   For VarX=Len(Out2) To 1 Step -1
                      If Mid$(Out2,VarX,1)=" " Then
                         For VarB=1 To VarL
                            Call Back.Space
                         Next
                         Type.Ahead=Mid$(Out2,VarX+1)
                         Out2=Left$(Out2,VarX)
                         VarW=True
                         Exit For
                      Endif
                      VarL=VarL+1
                   Next
                   If VarW Then
                      Call Line.Return
                   Endif
                   Exit Do
                End Select
             Endif
          Endif
          If Line.Length>False Then
             If Len(Out2)>=Line.Length Then
                Call Line.Return
                Exit Do
             Endif
          Endif
       Endif
    Case Else ' garbage
       Type.Error=Type.Error+1
       If Type.Error>=MaxErrors Then
	  Type.Error=False
	  Call Hang.Up(14)
       Endif
    End Select
 Loop
 No.Input=(Out2=Nul)
 No=(Ucase$(Left$(Out2,1))="N")
 Yes=(Ucase$(Left$(Out2,1))="Y")
 Quit=(Ucase$(Left$(Out2,1))="Q")
 Continuous=(Ucase$(Left$(Out2,1))="C")
 Line.Length=False
 No.Echo=False
 No.Input.Out=Nul
 Strng=Nul
 Return
Get.Config:
 VarX=0
 Gosub Reset.Options
 Do
    VarX=VarX+1
    If VarX>Options.Lines Then
       Goto Next.Read
       Exit Do
    Endif
    Var$=Ltrim$(Rtrim$(Options.Array(VarX)))
    If Ucase$(Var$)="[CMD143]" Then
       Do
          VarX=VarX+1
          If VarX>Options.Lines Then
             Goto Next.Read
             Return
          Endif
          Var$=Ltrim$(Rtrim$(Options.Array(VarX)))
          If Ucase$(Var$)="[END]" Then
             Goto Next.Read
             Return
          Endif
          Var=Instr(Var$,"=")
          If Var Then
             Var2$=Ltrim$(Rtrim$(Left$(Var$,Var-1)))
             Var3$=Ltrim$(Rtrim$(Mid$(Var$,Var+1)))
             Gosub Parse.Options
          Endif
       Loop
    Endif
 Loop
 Return
Next.Read:
 Close #TempFile1
 Filename$=DND.Path+"CMD143.CFG"
 If Dir$(Filename$)="" Then
    Return
 Endif
 Open Filename$ For Input As #TempFile1
 Do
    If Eof(TempFile1) Then
       Exit Do
    Endif
    Line Input #TempFile1, Var$
    Var$=Ltrim$(Rtrim$(Var$))
    If Left$(Var$,1)<>";" Then
       VarX$=UserRecord.CodeName
       VarX$=Rtrim$(VarX$)
       VarX$=Ucase$(VarX$)
       If Ucase$(Var$)="["+VarX$+"]" Then
          VarZ=0
          Gosub Reset.Options
          Do
             If Eof(TempFile1) Then
                Exit Do
             Endif
             Line Input #TempFile1, Var$
             If Ucase$(Var$)="[END]" Then
                Return
             Endif
             Var=Instr(Var$,"=")
             If Var Then
                Var2$=Ltrim$(Rtrim$(Left$(Var$,Var-1)))
                Var3$=Ltrim$(Rtrim$(Mid$(Var$,Var+1)))
                Gosub Parse.Options
             Endif
          Loop
       Endif
    Endif
 Loop
 Return
Reset.Options:
 Option1#=-1#
 Option1!=-1!
 Option2=0
 Option3=-1
 Option4=-1
 Option5=0
 Option6=0
 Option8=0
 Max.Alarms=9
 ' sound frequency
 Option21 = 900
 ' sound duration
 Option22 = 6
 Return
Parse.Options:
 Var4=Int(Val(Var3$)+.5)
 Select Case Lcase$(Var2$)
 Case "escape.switch"
    Select Case Ucase$(Var3$)
    Case "0", "FALSE","OFF"
       Option4=0
    End Select
 Case "break.switch"
    Select Case Ucase$(Var3$)
    Case "0", "FALSE","OFF"
       Option3=0
    End Select
 Case "debug.switch"
    Select Case Ucase$(Var3$)
    Case "-1", "TRUE","ON"
       Option2=-1
    End Select
 Case "alarm.color"
    If Var4>=1 And Var4<=7 Then
       Option8=Var4+8
    Endif
 Case "alarm.type"
    Select Case Ucase$(Var3$)
    Case "0", "FALSE","OFF"
       Option5=-1
    End Select
 Case "alarm.beep"
    Select Case Ucase$(Var3$)
    Case "0", "FALSE","OFF"
       Option6=-1
    End Select
 Case "sound.frequency"
    If Var4>=37 And Var4<=32767 Then
       option21=Var4
    Endif
 Case "sound.duration"
    If Var4>=1 And Var4<=32767 Then
       option22=Var4
    Endif
 Case "maximum.alarms"
    If Var4>0 And Var4<=100 Then
       Max.Alarms=Var4
    Endif
 Case "maximum.rounds.counter"
    Option1#=Int(Val(Var3$)+.5)
 Case "round.time.delay"
    Option1!=Csng(Val(Var3$))
 End Select
 Return
Err.Resume24:
 Exit Sub
Err.Trap24:
 If Debug.Mode Then
    Data.Error=Err
    If Data.Error=5 Then
       Strng="Syntax error."
       Call IO.O
    Else
       If Data.Error=6 Then
          Strng="Overflow error."
          Call IO.O
       Else
          Strng="Error"+Str$(Data.Error)
          Call IO.O
       Endif
    Endif
 Endif
 Resume Err.Resume24
End Sub

' convert basic color code to ansi color code.
Sub Convert.Color2(VarX)
 On Local Error Goto ErrorRoutine28
 VarF=37
 VarF2=VarX
 Select Case VarX
 Case 9 ' blue
    VarF=34
 Case 10 ' green
    VarF=32
 Case 11 ' cyan
    VarF=36
 Case 12 ' red
    VarF=31
 Case 13 ' magenta
    VarF=35
 Case 14 ' yellow
    VarF=33
 Case 15 ' white
    VarF=37
 Case Else
    VarF=37
 End Select
 Call Modem.ANSI(4,VarF,VarF2)
 Call ScreenANSI(4,VarF,VarF2)
ErrorResume28:
 Exit Sub
ErrorRoutine28:
 Resume ErrorResume28
End Sub

Sub Command.Loop
 On Local Error Goto Err.Trp18
 Last.Command.Number=False
 Out2=Ltrim$(Out2)
 Var1=False
 VarX=False
 Stored.Parsed.Command1=Out2
 Out2=Ucase$(Out2)
 Stored.Parsed.Command2=Out2
 If Out2=Config3(78)+"?" Or Out2=Config3(78)+"HELP" Then
    If Brief.Messages Then
       FileName=Config3(52)+Config3(65)+"M"+File.Extension2 ' catalogm.doc
    Else
       FileName=Config3(52)+Config3(50)+"M"+File.Extension2 ' summarym.doc
    Endif
    If Len(Dir$(Filename)) Then
       Call Out.File2(Filename)
    Endif
    Strng=Nul : Call IO.O
    Strng="!time - time online." : Call IO.O
    Strng="!clock - current time." : Call IO.O
    Strng="!prefix - display prefix." : Call IO.O
    Strng="!users - display userlist." : Call IO.O
    Strng=" !userrooms - display userlist with roomnumber." : Call IO.O
    Strng=" !userlevels - display userlist with userlevel." : Call IO.O
    Strng="!count - display user count." : Call IO.O
    Strng="!maxnodes - display maximum nodes." : Call IO.O
    Strng="!monitor - display number of monitors." : Call IO.O
    Strng="!alarmhelp - display alarm functions." : Call IO.O
    Graphics.Off=False
    Strng="Chat Talk-Mode. Enter "+Config3(78)+"Quit to exit. Type "+Config3(78)+"? for Help.."
    Call IO.O
    Exit Sub
 Endif
 If Out2=Config3(10) Then
    User.Command=Previous.Command1
    Stored.Parsed.Command1=Previous.Command2
    Stored.Parsed.Command2=Previous.Command3
 Else
    User.Command=Out2
    Previous.Command1=User.Command
    Previous.Command2=Stored.Parsed.Command1
    Previous.Command3=Stored.Parsed.Command2
 Endif
 If User.Command=Nul Then
    Last.Command=Enter$
    Call Update.Mess(4)
    Exit Sub
 Endif
 Entry.Command=False
 Magic.Spell=False
 Temp.Command=User.Command
 If Instr(Temp.Command," ") Then
    Temp.Command=Left$(Temp.Command,Instr(Temp.Command," ")-1)
 Endif
 Parser=Instr(User.Command," ")
 If Parser Then
    Parsed.Command1=Mid$(User.Command,Parser+1)
    Parsed.Command2=Parsed.Command1
    VarX=Instr(Stored.Parsed.Command1," ")+1
    Stored.Parsed.Command1=Mid$(Stored.Parsed.Command1,VarX)
    Stored.Parsed.Command2=Mid$(Stored.Parsed.Command2,VarX)
    Call Numeric
 Endif
 TempY=Parse.Temp
 Call Numeric2
 TempX=Parse.Temp
 Parse.Temp=TempY
 TempQ=False
 Temp.Command=Mid$(Temp.Command,Len(Config3(78))+1)
 For Last.Command.Number=1 To MaxNodeCommands
    TempZ=Last.Command.Number+MaxUserCommands+MaxDMcommands+MaxSysopCommands
    Call Read.Message.Record(MessWorkFile8,TempZ)
    Last.Command=UserCommandRecord.CommandSet(1)
    Last.Command=Rtrim$(Left$(Last.Command,10))
    If Left$(Last.Command,Len(Temp.Command))=Temp.Command Then
       TempQ=True
       Exit For
    Endif
 Next
 If TempQ=False Then
    Strng="Unknown command!"
    Call IO.O
    Last.Command="<unknown>"
    Call Update.Mess(4)
    Exit Sub
 Endif

 ' check appended #<n> for /Sta and /Who
 If Last.Command.Number<>10 Then
    If Last.Command.Number<>15 Then
       If TempX Then
          Strng="Unknown command!"
          Call IO.O
          Last.Command="<unknown>"
          Call Update.Mess(4)
          Exit Sub
       Endif
    Endif
 Endif

 ' always allow /quit
 If Last.Command.Number=12 Then
    Goto Next.Line
 Endif

 ' check command restriction
 If Normal.User Then
    If UserRecord.ClassType<AsstDM Then
       If UserCommandRecord.CommandClass(UserRecord.ClassType) Then
	  Graphics.Off=True
	  Strng="Command restriction!"
	  Call IO.O
	  Graphics.Off=False
          Last.Command="<restrict>"
          Call Update.Mess(4)
	  Exit Sub
       Endif
    Endif
 Endif

 ' check command restriction
 Var1=MaxUserCommands+MaxDMCommands+MaxSysopCommands+Last.Command.Number
 Call Check.Restriction(Var1,Var2)
 If Var2 Then Exit Sub

 ' implement node command
Next.Line:
 Call Update.Mess(4)
 Command.Time.Limit=UserCommandRecord.CommandLimit
 Select Case Last.Command.Number
 Case 1 ' /auto
    Chat.Mode=True
    Var$="Press <ctrl-break>/<ctrl-c> to interrupt"
    If Local.Mode=False Then
       Var$="Press <control-c>/<control-k> to interrupt"
    Endif
    Strng="Chat Auto-mode. "+Var$+"."
    Call IO.O
 Case 2 ' /block
    Call Block
 Case 3 ' /brief
    Brief.Messages = NOT Brief.Messages
    If Brief.Messages Then
       Strng="Brief messaging on."
    Else
       Strng="Brief messaging off."
    Endif
    Call IO.O
 Case 4 ' /chat
    If Chat.Mode Then
       Strng="You are already in chat mode."
    Else
       Chat.Mode=True
       Strng="Chat Talk-mode. Enter "+Config3(78)+"Quit to exit. Type "+Config3(78)+"? for Help.."
    Endif
    Call IO.O
 Case 5 ' /echo
    Call Flag.Toggles(2)
 Case 6 ' /group
    Call Group.Send
 Case 7 ' /nosend
    Call No.Send
 Case 8 ' /say
    If Parser=False Then
       Strng="Say what? "
       Call IO.I
       If Out2=Nul Then
          Exit Sub
       Endif
       Stored.Parsed.Command1=Out2
    Endif
    Call Send.Mess(3,SFalse,Room,Stored.Parsed.Command1)
 Case 9 ' /send
    If Parser=False Then
       Strng="Send what? "
       Call IO.I
       If Out2=Nul Then
          Exit Sub
       Endif
       Stored.Parsed.Command1=Out2
    Endif
    Call Send.Mess(1,SFalse,SFalse,Stored.Parsed.Command1)
 Case 10 ' /status
    Call Read.Status
 Case 11 ' /sendto
    If Parser=False Then
       Strng="Send to whom? "
       Call IO.I
       If Out2=Nul Then
          Exit Sub
       Endif
       Stored.Parsed.Command1=Out2
    Endif
    Call Send.Message
 Case 12 ' /quit
    Auto.Break=False
    Chat.Mode=False
    Strng="Chat mode ended."
    Call IO.O
    Exit Sub
 Case 13 ' /whisper
    If Parser=False Then
       Strng="Whisper to whom? "
       Call IO.I
       If Out2=Nul Then
          Exit Sub
       Endif
       Stored.Parsed.Command1=Out2
    Endif
    Call Whisper
 Case 14 ' /wait
    Chat.Mode=True
    Var$="Press <ctrl-break>/<ctrl-c> to interrupt"
    If Local.Mode=False Then
       Var$="Press <control-c>/<control-k> to interrupt"
    Endif
    Strng="Chat Wait-mode. "+Var$+"."
    Call IO.O
 Case 15 ' /who
    Call Whos.On
 Case 16 ' /yell
    If Parser=False Then
       Strng="Yell what? "
       Call IO.I
       If Out2=Nul Then
          Exit Sub
       Endif
       Stored.Parsed.Command1=Out2
    Endif
    Call Send.Mess(2,SFalse,-1!,Stored.Parsed.Command1)
 End Select
Err.Rsm18:
 Exit Sub
Err.Trp18:
 Resume Err.Rsm18
End Sub

Sub Group.Send
 On Local Error Goto Err.Trp16
 Graphics.Off=False
 Strng="Enter grouped message. 9 lines max."
 Call IO.O
 Strng="Press "+Enter$+" on a blank line to send."
 Call IO.O
 Graphics.Off=True
 TempZ=False
 Redim Temp.ArrayS(1 To 9) As String
 For TempX=1 To 9
    Line.Length=79
    Strng="?"
    Call IO.I
    If Lost.Carrier Then
       Exit Sub
    Endif
    If Out2=Nul Then
       Exit For
    Endif
    TempZ=TempZ+1
    Temp.ArrayS(TempZ)=Out2
 Next
 If TempZ Then
    Do
       TempA$=Str$(Timer)
       ' find group message with same time
       VarX=False
       For Var!=1! To Lof(MessWorkFile2)/MessWorkRecLen2
          Call Read.Record(MessWorkFile2,Var!)
          If MessWorkRecord2.MessageType=25 Then
             TempQ$=MessWorkRecord2.Message2
             TempQ$=Mid$(TempQ$,3)
             TempQ$=Rtrim$(TempQ$)
             If TempA$=TempQ$ Then
                VarX=True
                Exit For
             Endif
          Endif
       Next
       If VarX=False Then Exit Do
    Loop
    ' send group message
    For TempX=1 To TempZ
       VarZ$=Temp.ArrayS(TempX)
       Call Send.Mess(25,SFalse,Room,VarZ$)
    Next
 Endif
 Graphics.Off=False
 If TempZ Then
    Strng="Grouped message sent."
 Else
    Strng="No grouped message sent."
 Endif
 Call IO.O
 Erase Temp.ArrayS
Err.Rsm16:
 Exit Sub
Err.Trp16:
 Resume Err.Rsm16
End Sub

Sub Block
 On Local Error Goto Err.Trp14
 If Lcase$(Parsed.Command1)="self" Then
    Var=Not UserRecord.Block1
    UserRecord.Block1=Var
    If Var Then
       Strng="User block on."
    Else
       Strng="User block off."
    Endif
    Call IO.O
    Exit Sub
 Endif
 Call Get.Node(Var!)
 If Var!=SFalse Then
    Strng="That player is not online!"
    Call IO.O
    Exit Sub
 Endif
 TempC=Temp
 If TempC=Node Then
    Strng="Can't block yourself!"
    Call IO.O
    Exit Sub
 Endif
 Out2=Lcase$(Strng)
 Mid$(Out2,1,1)=Ucase$(Mid$(Out2,1,1))
 VarX2=Node+1
 Call Read.Message.Record(MessWorkFile1,VarX2)
 TempA=Val(Mid$(MessWorkRecord1.ReceiveFlags,TempC+1,1))
 If TempA=False Then
    TempA=1
    Strng="No longer blocking "+Out2+"."
 Else
    TempA=False
    Strng="Now blocking "+Out2+"."
 Endif
 Call IO.O
 Mid$(MessWorkRecord1.ReceiveFlags,TempC+1,1)=Ltrim$(Str$(TempA))
 VarX2=Node+1
 Call Write.Message.Record(MessWorkFile1,VarX2)
Err.Rsm14:
 Exit Sub
Err.Trp14:
 Resume Err.Rsm14
End Sub

Sub No.Send
 On Local Error Goto Err.Trp15
 If Lcase$(Parsed.Command1)="self" Then
    Var=Not UserRecord.Block2
    UserRecord.Block2=Var
    If Var Then
       Strng="User nosend on."
    Else
       Strng="User nosend off."
    Endif
    Call IO.O
    Exit Sub
 Endif
 Call Get.Node(Var!)
 If Var!=SFalse Then
    Strng="That player is not online!"
    Call IO.O
    Exit Sub
 Endif
 TempC=Temp
 If TempC=Node Then
    Strng="Can't nosend yourself!"
    Call IO.O
    Exit Sub
 Endif
 Out2=Lcase$(Strng)
 Mid$(Out2,1,1)=Ucase$(Mid$(Out2,1,1))
 VarX2=Node+1
 Call Read.Message.Record(MessWorkFile1,VarX2)
 TempA=Val(Mid$(MessWorkRecord1.SendFlags,TempC+1,1))
 If TempA=False Then
    TempA=1
    Strng="Now sending to "+Out2+"."
 Else
    TempA=False
    Strng="No longer sending to "+Out2+"."
 Endif
 Call IO.O
 Mid$(MessWorkRecord1.SendFlags,TempC+1,1)=Ltrim$(Str$(TempA))
 VarX2=Node+1
 Call Write.Message.Record(MessWorkFile1,VarX2)
Err.Rsm15:
 Exit Sub
Err.Trp15:
 Resume Err.Rsm15
End Sub

Sub Send.Message
 On Local Error Goto Err.Trp13
 Call Get.Node(Var!)
 If Var!=SFalse Then
    Strng="That player is not online!"
    Call IO.O
    Exit Sub
 Endif
 TempC=Temp
 If TempC=Node Then
    Strng="Can't send to yourself!"
    Call IO.O
    Exit Sub
 Endif
 Strng="Send what? "
 Call IO.I
 If Len(Out2) Then
    Call Send.Mess(6,TempB!,SFalse,Out2)
 Endif
Err.Rsm13:
 Exit Sub
Err.Trp13:
 Resume Err.Rsm13
End Sub

Sub Whisper
 On Local Error Goto Err.Trp12
 Call Search.Mess(SFalse)
 If Temp!<>Room Then
    Strng="That player is not in this room!"
    Call IO.O
    Exit Sub
 Endif
 If TempC=Node Then
    Strng="Can't whisper to yourself!"
    Call IO.O
    Exit Sub
 Endif
 Strng="Whisper what? "
 Call IO.I
 If Len(Out2) Then
    Call Send.Mess(7,TempB!,Room,Out2)
 Endif
Err.Rsm12:
 Exit Sub
Err.Trp12:
 Resume Err.Rsm12
End Sub

Sub Read.Status
 On Local Error Goto ErrTrap8
 If Parser Then
'    Parsed.Command1=Stored.Parsed.Command1
    If Lcase$(Parsed.Command1)="self" Then
       TempC=Node
       Gosub Read.Status.Header
       Gosub Read.User.Status
       Exit Sub
    Endif
    Call Get.Node(Var!)
    If Var!=SFalse Then
       Strng="That player is not online!"
       Call IO.O
    Else
       TempC=Temp
       Gosub Read.Status.Header
       Gosub Read.User.Status
    Endif
    Exit Sub
 Endif
 Gosub Read.Status.Header
 If Node=False Then
    Temp5=Temp5+1
    TempC=False
    Strng=UserRecord.CodeName
    Call Make.Mess.Work1
    Gosub Read.User.Status
 Endif
 For TempC=1 To MaxNodes
    Call Read.Message.Record(MessWorkFile1,TempC+1)
    Strng=Rtrim$(MessWorkRecord1.Username)
    Call Valid.Name(Strng)
    If TempA Then
       Gosub Read.User.Status
       Temp5=Temp5+1
       If Temp5>=User.Pagelength-2 Then
          If Continuous=False Then
             Call More.Prompt
             Temp5=False
             If No Or Quit Then
                Exit For
             Endif
             Gosub Read.Status.Header
          Endif
       Endif
    Endif
 Next
 If Temp5 Then
    Call More.Prompt
 Endif
 Exit Sub

Read.Status.Header:
 Temp5=4
 Graphics.Off=True
 Strng="DNDBBS v"+Version$+" Status Report For "+Left$(Fclock$,15)+"."
 Call IO.O
 Strng=Nul
 Call IO.O
 Select Case TempX
 Case 3
    Strng="Port Y  H  Username                       Timeon   Last Command"
    Call IO.O
    Call Display.Line(63)
 Case 2
    Strng="Port Y  H  Username                       Timeon   Level"
    Call IO.O
    Call Display.Line(58)
 Case Else
    Strng="Port Y  H  Username                       Timeon"
    Call IO.O
    Call Display.Line(50)
 End Select
 Return

Read.User.Status:
 Select Case TempC
 Case 0
    Strng="P##"
 Case 1 To 10 ' 0-9
    Strng="P#"+Mid$(Str$(TempC-1),2)
 Case 11 To 36 ' A-Z
    Strng="P#"+Chr$(TempC+54)
 Case 37 To 126 ' 10-99
    Strng="P#"+Mid$(Str$(TempC-27),2)
 End Select
 Strng=Strng+Space$(5-Len(Strng))
 If TempC=Node Then
    Strng=Strng+"-- -- "
 Else
    VarX2=Node+1
    Call Read.Message.Record(MessWorkFile1,VarX2)
    Var1$=MessWorkRecord1.SendFlags
    Var2$=MessWorkRecord1.ReceiveFlags
    If Mid$(Var1$,TempC+1,1)="1" Then
       Strng=Strng+"S"
    Else
       Strng=Strng+" "
    Endif
    If Mid$(Var2$,TempC+1,1)="1" Then
       Strng=Strng+"R"
    Else
       Strng=Strng+" "
    Endif
    Strng=Strng+" "
    Call Read.Message.Record(MessWorkFile1,TempC+1)
    Var1$=MessWorkRecord1.SendFlags
    Var2$=MessWorkRecord1.ReceiveFlags
    If Mid$(MessWorkRecord1.SendFlags,Node+1,1)="1" Then
       Strng=Strng+"S"
    Else
       Strng=Strng+" "
    Endif
    If Mid$(MessWorkRecord1.ReceiveFlags,Node+1,1)="1" Then
       Strng=Strng+"R"
    Else
       Strng=Strng+" "
    Endif
    Strng=Strng+" "
 Endif
 Out3=Nul
 Out2=Lcase$(MessWorkRecord1.UserName)
 Mid$(Out2,1,1)=Ucase$(Mid$(Out2,1,1))
 If TempX=2 Then
    Out3=Ltrim$(Str$(MessWorkRecord1.Level))
 Else
    If TempX=3 Then
       Out3=MessWorkRecord1.LastCommand
       Select Case Left$(Out3,1)
       Case "a" To "z", "A" To "Z"
          Out3=Lcase$(Out3)
          Mid$(Out3,1,1)=Ucase$(Mid$(Out3,1,1))
       End Select
    Endif
 Endif
 Strng=Strng+Out2+" "+MessWorkRecord1.TimeOn+" "+Out3
 Call IO.O
 Return
ErrResume8:
 Exit Sub
ErrTrap8:
 Resume ErrResume8
End Sub

Sub Whos.On
 On Local Error Goto ErrorTrap19a
 If Parser Then
'    Parsed.Command1=Stored.Parsed.Command1
    If Lcase$(Parsed.Command1)="self" Then
       TempC=Node
       Gosub Read.Who.Header
       Gosub Read.Who.Status
       Exit Sub
    Endif
    Call Get.Node(Var!)
    If Var!=SFalse Then
       Strng="That player is not online!"
       Call IO.O
    Else
       TempC=Temp
       Gosub Read.Who.Header
       Gosub Read.Who.Status
    Endif
    Exit Sub
 Endif
 Gosub Read.Who.Header
 If Rtrim$(NodeX)=Nul Then
    Temp5=Temp5+1
    TempC=False
    Strng=UserRecord.CodeName
    Call Make.Mess.Work1
    Gosub Read.Who.Status
 Endif
 For TempC=1 To MaxNodes
    Call Read.Message.Record(MessWorkFile1,TempC+1)
    Strng=Rtrim$(MessWorkRecord1.Username)
    Call Valid.Name(Strng)
    If TempA Then
       Gosub Read.Who.Status
       Temp5=Temp5+1
       If Temp5>=User.Pagelength-2 Then
          If Continuous=False Then
             Call More.Prompt
             Temp5=False
             If No Or Quit Then
                Exit FOr
             Endif
             Gosub Read.Who.Header
          Endif
       Endif
    Endif
 Next
 If Temp5 Then
    Call More.Prompt
 Endif
 Exit Sub

Read.Who.Header:
 Temp5=4
 Graphics.Off=True
 Strng="DNDBBS v"+Version$+" Who Status Report For "+Left$(Fclock$,15)+"."
 Call IO.O
 Strng=Nul
 Call IO.O
 If TempX=2 Then
    Strng="Port Username                       Timeon   Room     Classname"
 Else
    Strng="Port Username                       Timeon   Room     Score"
 Endif
 Call IO.O
 Call Display.Line(71)
 Return

Read.Who.Status:
 Select Case TempC
 Case 0
    Strng="P##"
 Case 1 To 10 ' 0-9
    Strng="P#"+Mid$(Str$(TempC-1),2)
 Case 11 To 36 ' A-Z
    Strng="P#"+Chr$(TempC+54)
 Case 37 To 126 ' 10-99
    Strng="P#"+Mid$(Str$(TempC-27),2)
 End Select
 Strng=Strng+Space$(5-Len(Strng))
 Out2=Lcase$(MessWorkRecord1.UserName)
 Mid$(Out2,1,1)=Ucase$(Mid$(Out2,1,1))
 Out3=Mid$(Str$(MessWorkRecord1.RoomNumber),2)
 Out3=Out3+Space$(8-Len(Out3))
 If TempX=2 Then
    Out4=Rtrim$(MessWorkRecord1.Classname)
 Else
    VarX#=MessWorkRecord1.Score3*MessWorkRecord1.Level*10+ _
    MessWorkRecord1.Score1*MessWorkRecord1.Level*2+ _
    MessWorkRecord1.Score2*MessWorkRecord1.Level
    Out4=Mid$(Str$(VarX#),2)
 Endif
 Strng=Strng+Out2+" "+MessWorkRecord1.TimeOn+" "+Out3+" "+Out4
 Call IO.O
 Return
ErrorResume19a:
 Exit Sub
ErrorTrap19a:
 Resume ErrorResume19a
End Sub

Sub Get.Node(Var!)
 On Local Error Goto ErrorTrap1
 Temp=False
 Var!=SFalse
 Var$=Rtrim$(Ltrim$(Parsed.Command1))
 Select Case Len(Var$)
 Case 1
    Select Case Ucase$(Var$)
    Case "0" To "9" ' 1-10
       Temp=Val(Parsed.Command1)+1
    Case "A" To "Z" ' 11-36
       Temp=Asc(Ucase$(Var$))-54
    End Select
 Case 2
    VarX=Int(Val(Var$)+.5)
    If VarX>=10 And VarX<=99 Then
       Temp=VarX+27 ' 37-126
    Endif
 End Select
 If Temp Then
    Call Read.Message.Record(MessWorkFile1,Temp+1)
    Strng=Rtrim$(MessWorkRecord1.UserName)
    Call Valid.Name(Strng)
    If TempA Then
       Var!=MessWorkRecord1.UserIndex
       Exit Sub
    Endif
 Endif
 Call Search.Mess(SFalse)
 If Temp! Then
    Temp=TempC
    Var!=MessWorkRecord1.UserIndex
 Endif
ErrorResume1:
 Exit Sub
ErrorTrap1:
 Resume ErrorResume1
End Sub

Sub Numeric2
 On Local Error Goto ErrorTrp6a
 Var=False
 Var1=Instr(Temp.Command,"#")
 If Var1 Then
    Var=Int(Val(Mid$(Temp.Command,Var1+1))+.5)
    Temp.Command=Left$(Temp.Command,Var1-1)
    User.Command=Left$(User.Command,Var1-1)
 Endif
 Parse.Temp=Var
ErrorRsm6a:
 Exit Sub
ErrorTrp6a:
 Resume ErrorRsm6a
End Sub

 ' date/time logged on, time used.
Sub Time.Online(Var)
 On Local Error Goto Err.Trp1
 Graphics.Off=True
 Strng="It is now "+Fclock$
 Call IO.O
 ' calculate serialized date
 Trap=-1
 OnDate#=DateValue(Date.On)
 Strng2=Format$(OnDate#,"ddd dd mmm yyyy")
 ' calculate serialized time
 OnTime#=TimeValue(Time.On)
 Strng3=Format$(OnTime#,"hh:mma/p")
Resume1a:
 Trap=0
 If Trap.Err Then
    Strng="You logged on at "+Date.On+", "+Time.On+"."
 Else
    Strng="You logged on at "+Strng2+", "+Strng3+"."
 Endif
 Call IO.O
 Trap=-2
 d1$=format$(now,"mm-dd-yyyy")
 Var#=DateValue(d1$)-OnDate# ' serialized date
Resume2a:
 Trap=0
 If Var#>=2# Then
    Strng="You have been on for"+Str$(Var#)+" days!"
    Call IO.O
 Else
    ' time online
    Var1!=Timeit!-Timeon ' elapsed
    If Var1!<0! Then Var1!=Var1!+86400! ' midnight
    Strng="You have been on for"
    Strng2="."
    Gosub Time.Display
 Endif
 ' time remaining
 If UserRecord.TimeLimit<SFalse Then
    Strng="You have unlimited time remaining."
    Call IO.O
 Else
    If Var#<2# Then
       Var2!=Timeit!-Timeon ' elapsed
       If Var2!<0! Then Var2!=Var2!+86400! ' midnight
       Var1!=Time.Left-Var2! ' remaining
       If Var1!<0! Then Var1!=0!
       Strng="You have"
       Strng2=" remaining."
       Gosub Time.Display
    Endif
 Endif
 ' usage
 Strng="You have used "+Lcase$(ProgramName$)
 Var#=UserRecord.TimesLogon+1#
 Gosub Time.String
 Strng=Strng+Strng2
 Var#=UserRecord.TimesDied
 Gosub Time.String
 Strng=Strng+" and died"+Strng2+"."
 Call IO.O
 Exit Sub
Time.String:
 Select Case Var#
 Case 1
    Strng2=" once"
 Case 2 To 10
    Strng2=" "+Rtrim$(Numeral(Var#))+" times"
 Case Else
    Call Amount(Var#,VarQ$)
    Strng2=VarQ$+" times"
 End Select
 Return
Time.Display:
 Hours=Int(Var1!/3600!)
 Var1!=Var1!-Hours*3600!
 Minutes=Int(Var1!/60!)
 Var1!=Var1!-Minutes*60!
 Seconds=Int(Var1!)
 If Hours>=23 Then
    Strng=Strng+" a very long time"+Strng2
    Call IO.O
    Return
 Endif
 If Hours=False Then
    If Minutes=False Then
       If Seconds=False Then
          Strng=Strng+" 0 seconds"+Strng2
          Call IO.O
          Return
       Endif
    Endif
 Endif
 If Hours>False Then
    Strng=Strng+Str$(Hours)+" hour"
    If Hours>1 Then
       Strng=Strng+"s"
    Endif
    Strng=Strng+","
 Endif
 If Minutes>False Then
    Strng=Strng+Str$(Minutes)+" minute"
    If Minutes>1 Then
       Strng=Strng+"s"
    Endif
    Strng=Strng+","
 Else
    If Seconds=False Then
       Strng=Left$(Strng,Len(Strng)-1)
    Endif
 Endif
 If Seconds>False Then
    Strng=Strng+Str$(Seconds)+" second"
    If Seconds>1 Then
       Strng=Strng+"s"
    Endif
 Else
    Strng=Left$(Strng,Len(Strng)-1)
 Endif
 Strng=Strng+Strng2
 Call IO.O
 Return
Err.Rsm1:
 Exit Sub
 ' test for illegal function call when serialized date is beyond 2078.
Err.Trp1:
 If Trap=-1 Then
    Trap.Err=-1
    Resume Resume1a
 Endif
 If Trap=-2 Then
    Var#=0#
    Resume Resume2a
 Endif
 Resume Err.Rsm1
End Sub

' specialized file display function.
Sub Out.File2(Var$)
 On Local Error Goto ErrorTrap23x
 If Dir$(Var$)=Nul Then
    If Local.Mode And Debug.Mode Then
       Carriage.Return=False
       Strng="Filename: "+Var$+" <not found>"
       Call IO.O
    Endif
    Exit Sub
 Endif
 Var=1
 If Local.Mode And Debug.Mode Then
    Carriage.Return=False
    Var=Var+1
    Strng="Filename: "+Var$
    Call IO.O
 Endif
 Graphics.Off=False
 If Environ$("FILELISTHEADER")<>"" Then
    Strng=Environ$("FILELISTHEADER")
    If Left$(Strng,1)=Chr$(34) Then
       If Right$(Strng,1)=Chr$(34) Then
          Strng=Left$(Strng,Len(Strng)-1)
          Strng=Mid$(Strng,2)
       Endif
    Endif
    Select Case Ucase$(Strng)
    Case "OFF","FALSE","0"
       Eat$=Nul
    Case Else
       Call IO.O
       Var=Var+1
    End Select
 Else
    Strng="File list. "
    If Local.Mode Then
       Var2$="Press <ctrl-break>/<ctrl-c> to interrupt."
    Else
       Var2$="Press <control-c>/<control-k> to interrupt."
    Endif
    Strng=Strng+Var2$
    Call IO.O
    Var=Var+1
 Endif
 Allow.Break=True
 Break=False
 Continuous=False
 Graphics.Off=True
 VarQ=False
 VarR=False
 Close #TempFile1
 Open Var$ For Input Shared As #TempFile1
 Call Modem.ANSI(4,37,15)
 Call ScreenANSI(4,37,15)
 Do While Lost.Carrier=False
    Allow.Break=True
    If Eof(TempFile1) Then
       Exit Do
    Endif
    Line Input #TempFile1,Strng
    ' convert prefix string
    If Len(Strng) Then
       If Len(Config3(78)) Then
          VarP=0
          Do
             VarP=Instr(VarP+1,Strng,"/")
             If VarP Then
                Strng=Left$(Strng,VarP-1)+Config3(78)+Mid$(Strng,VarP+1)
                VarP=VarP+Len(Config3(78))
             Else
                Exit Do
             Endif
          Loop
       Endif
    Endif
    Strng2=Strng
    Graphics.On=True
    Call IO.O
    Graphics.On=False
    If Break Then
       Exit Do
    Endif
    Var=Var+1
    If Var>=User.Pagelength-2 Then
       Var=False
       If Continuous=False Then
          Graphics.Off=False
          Gosub MorePrompt
          If No Or Quit Then
             Exit Do
          Endif
       Endif
    Endif
 Loop
 If Var>1 Then
    If Break=False Then
       Graphics.Off=False
       Gosub MorePrompt
    Endif
 Endif
 Allow.Break=False
 Break=False
 Graphics.Off=True
 Exit Sub
MorePrompt:
 If Environ$("MOREPROMPT")<>"" Then
    Strng=Environ$("MOREPROMPT")
    If Left$(Strng,1)=Chr$(34) Then
       If Right$(Strng,1)=Chr$(34) Then
          Strng=Left$(Strng,Len(Strng)-1)
          Strng=Mid$(Strng,2)
       Endif
    Endif
    Graphics.On=False
    Word.Wrap=-1
    Call IO.I
    Word.Wrap=0
 Else
    Strng=More$
    Graphics.On=False
    Call IO.I
 Endif
 If Out2=Nul Then
    VarX$=Environ$("MOREPROMPTDEFAULT")
    If Len(VarX$) Then
       Select Case VarX$
       Case "Y", "YES"
          Yes=-1 : No=0 : Quit=0 : Continuous=0
       Case "N", "NO"
          Yes=0 : No=-1 : Quit=0 : Continuous=0
       Case "Q", "QUIT"
          Yes=0 : No=0 : Quit=-1 : Continuous=0
       Case "C", "CONTINUOUS"
          Yes=0 : No=0 : Quit=0 : Continuous=-1
       Case Else
          Yes=-1 : No=0 : Quit=0 : Continuous=0
       End Select
    Endif
 Endif
 Return
ErrorResume23x:
 Exit Sub
ErrorTrap23x:
 Resume ErrorResume23x
End Sub

' Var1=command number
' Var2=return value
Sub Check.Restriction(Var1,Var2)
 ' fixed 09/05/2019
 On Local Error Goto Err.Trp23
 ' return value
 Var2=False
 ' check command pointer
 Var!=UserRecord.NodeList2
 If Var!=SFalse Then
    Exit Sub
 Endif
 ' open restriction file
 FileName=DND.Path+"restrict.dat"
 Close #TempFile1
 Open FileName For Random Shared As #TempFile1 Len=13
 ' Byte 1/0, FromHour/FromMin/ToHour/ToMin, ClassType, Level.
 Field #TempFile1,1 As B$,2 As FH$,2 As FM$,2 As TH$,2 As TM$,2 As CL$,2 As LV$
 ' get record length of sum of all commands
 Records!=MaxUserCommands+MaxDMCommands+MaxSysopCommands+MaxNodeCommands
 RecLen!=Records!*13!
 ' check command restricted from user record pointer
 If Var!>0! And Var!<=Lof(TempFile1)/RecLen! Then
    VarN!=Csng(Var1)
    If VarN!>0! And VarN!<=Records! Then
       VarR!=(Var!-1!)*Records!+VarN!
       Get #TempFile1,VarR!
       ' command restricted.
       If B$="0" Then
          Strng="Command is currently restricted."
          Goto Restrict
       Endif
       ' command time restriction
       Var1!=Csng(Cvi(FH$))*3600!+Csng(Cvi(FM$))*60! ' from
       Var2!=Csng(Cvi(TH$))*3600!+Csng(Cvi(TM$))*60! ' to
       If Var1!>SFalse Or Var2!>SFalse Then
          Call Check.Time2(Var3,Var1!,Var2!)
          If Var3=False Then
             Graphics.Off=True
             Strng="Command time restriction!"
             Call IO.O
             Strng="Command is currently restricted from "

             Z1$=Ltrim$(Str$(Cvi(FH$))) ' from hour
             Z1$=Right$("00"+Z1$,2)
             Z2$=Ltrim$(Str$(Cvi(FM$))) ' from minute
             Z2$=Right$("00"+Z2$,2)

             Z3$=Ltrim$(Str$(Cvi(TH$))) ' to hour
             Z3$=Right$("00"+Z3$,2)
             Z4$=Ltrim$(Str$(Cvi(TM$))) ' to minute
             Z4$=Right$("00"+Z4$,2)
             Strng=Strng+Z1$+":"+Z2$+" to "+Z3$+":"+Z4$

             Goto Restrict
          Endif
       Endif
       ' classtype restriction.
       C=Cvi(CL$)
       If (C And 2^UserRecord.ClassType)=2^UserRecord.ClassType Then
          Strng="Command is currently restricted from your classtype."
          Goto Restrict
       Endif
       ' level restriction.
       L=Cvi(LV$)
       If UserRecord.Level<L Then
          Strng="Command is currently restricted to level "+Form$(Cdbl(L))+"."
          Goto Restrict
       Endif
    Endif
 Endif
 Exit Sub

Restrict:
 Graphics.Off=True
 Call IO.O
 Graphics.Off=False
 Last.Command="<restrict>"
 Call Update.Mess(4)
 Var2=True
 Exit Sub
Err.Rsm23:
 Exit Sub
Err.Trp23:
 Resume Err.Rsm23
End Sub
