// $Id: command.inc,v 1.17 2001/05/11 14:25:08 druid Exp $
{$F+}
{ The complete quit procedure, which even logs off NPCs! - Grimlord }
procedure do_quit(ch : GCharacter; param : string);
var
   timer : GTimer;
begin
  if (ch.IS_NPC) then
    exit;
  if (ch.position = POS_FIGHTING) then
    begin
    ch.sendBuffer('You are fighting! You can''t quit!'#13#10);
    exit;
    end;
  timer := hasTimer(ch, TIMER_COMBAT);
  if (timer <> nil) then
    begin
    ch.sendBuffer('You have recently fled out of combat or have encountered a member'#13#10);
    ch.sendBuffer('of the opposite alignment. Therefor you are not allowed to quit.'#13#10);
    ch.sendBuffer('Please wait another '+inttostr(round(timer.counter / CPULSE_TICK))+' gameticks to quit.'#13#10);
    exit;
    end;
  if (auction_good.seller = ch) or (auction_good.buyer = ch)
   or (auction_evil.seller = ch) or (auction_evil.buyer = ch) then
    begin
    ch.sendBuffer('Please wait till the current auction has been concluded.'#13#10);
    exit;
    end;
  if (ch.snooped_by <> nil) then
    interpret(ch.snooped_by, 'snoop self');
  act(AT_REPORT, '$n has logged off.', false, ch, nil, nil, TO_ROOM);
  if (ch.conn <> nil) then
    GConnection(ch.conn).send('Thanks for playing! Please visit this MUD again!'#13#10);
  GPlayer(ch).quit;
end;
procedure do_save(ch : GCharacter; param : string);
begin
  if (ch.IS_NPC) then
    exit;
  GPlayer(ch).save(ch.name^);
  ch.sendBuffer('Ok.'#13#10);
end;
procedure do_afk(ch : GCharacter; param : string);
begin
  GConnection(ch.conn).afk := true;
  ch.sendBuffer('You are now listed as AFK. Hitting ENTER will cease this.'#13#10);
end;
type 
  THelpKeyword = class
                   keyword : string;
                   phelp : GHelp;
                   constructor Create(str : string; p : GHelp);
                 end;
constructor THelpKeyword.Create(str : string; p : GHelp);
begin
  inherited Create();
  keyword := str;
  phelp := p;
end;
{ Xenon 22/Apr/2001: helper function for do_help() and do_apropos() }
procedure insertAlphabetical(var ll : GDLinkedList; hk : THelpKeyword);
var
  node, ins : GListNode;
  s : THelpKeyword;
begin
  ins := nil;
  node := ll.head;
  if (ll.head = nil) then
  begin
    ll.insertLast(hk);
    exit;
  end;
  while (node <> nil) do
  begin
    s := node.element;
    if (AnsiCompareStr(hk.keyword, s.keyword) > 0) then
    begin
      ins := node;
    end
    else
    begin
      ll.insertBefore(node, hk);
      exit;
    end;
    
    node := node.next;
  end;
  ll.insertAfter(ins, hk)
end;
{ Revised help - Nemesis }
{ Xenon 16/Apr/2001: - help without arguments now gives sorted keywordlist
                     - help with an arg that matches multiple keywords will show matching keywords}
procedure do_help(ch : GCharacter; param : string);
var buf, s, keyword : string;
    help : GHelp;
    counter : integer;
    node : GListNode;
    keywordlist : GDLinkedList;
    hk : THelpKeyword;
    done : boolean;
begin
  keywordlist := nil; hk := nil;
  done := false;
  counter := 0;
  
  if (length(param) = 0) then
  begin
    buf := ch.ansiColor(3) + ' ' + add_chars(78, '---- Available help keywords ', '-') + ch.ansiColor(7) + #13#10#13#10;
    keywordlist := GDLinkedList.Create();
    
    node := help_files.head;
    while (node <> nil) do
    begin
      help := node.element;
      keyword := help.keywords;
      while (length(keyword) > 0) do
      begin
        keyword := one_argument(keyword, s);
        if ((s[length(s)] <> '_') and (help.level <= ch.level)) then
        begin
          hk := THelpKeyword.Create(lowercase(s), help);
          insertAlphabetical(keywordlist, hk);
        end;
          
      end;
      node := node.next;
    end;
    node := keywordlist.head;
    while (node <> nil) do
    begin
      buf := buf + pad_string(THelpKeyword(node.element).keyword, 19);
      inc(counter);
      if (counter = 4) then
      begin
        buf := buf + #13#10;
        counter := 0;
      end;      
      node := node.next;
    end;
    keywordlist.Clean();
    keywordlist.Free();
    ch.sendPager(buf + #13#10);
    exit;
  end;
  keywordlist := GDLinkedList.Create();
  node := help_files.head;
  while (node <> nil) do
  begin
    help := node.element;
    keyword := help.keywords;
    while (length(keyword) > 0) do
    begin
      keyword := one_argument(keyword, s);
      if (s[length(s)] <> '_') then
      begin
        if ((pos(uppercase(param), s) = 1) and (help.level <= ch.level)) then
        begin
          hk := THelpKeyword.Create(lowercase(s), help);
          insertAlphabetical(keywordlist, hk);
        end;
        if ((uppercase(param) = s) and (help.level <= ch.level)) then // if it's a 1-on-1 match, stop right away
        begin
          keywordlist.Clean();
          keywordlist.Free();
          keywordlist := GDLinkedList.Create();
          keywordlist.insertLast(THelpKeyword.Create(lowercase(s), help));
          done := true;
          break;
        end;
      end;
    end;
    if (done) then
      break;
      
    node := node.next;
  end;
  
  if (keywordlist.getSize() = 0) then
  begin
    ch.sendBuffer('No help on that word.'#13#10)
  end
  else
  if (keywordlist.getSize() = 1) then
  begin
    help := THelpKeyword(keywordlist.head.element).phelp;
    buf := '$A$3 ' + add_chars(78, '---- Help topic ', '-') + '$A$7'#13#10#13#10 +
           'Name:    $A$3' + help.keywords + #13#10 + '$A$7' +
           'Type:    $A$3' + help.helptype + #13#10 + '$A$7' +
           'Syntax:  $A$3' + help.syntax + #13#10 + '$A$7' +
           'Related: $A$3' + help.related + #13#10 + '$A$7' +
           #13#10 + help.text;
    ch.sendPager(act_string(buf, ch, nil, nil, nil));
  end
  else
  if (keywordlist.getSize() > 1) then
  begin
    buf := Format('Your help query ''$B$7%s$A$7'' matched $B$7%d$A$7 keywords:'#13#10#13#10, [param, keywordlist.getSize()]);
    node := keywordlist.head;
    while (node <> nil) do
    begin
      buf := buf + Format('  $B$7%s$A$7'#13#10, [THelpKeyword(node.element).keyword]);
      node := node.next;
    end;
    ch.sendPager(act_string(buf, ch, nil, nil, nil));
  end;
  keywordlist.Clean();
  keywordlist.Free();
end;
procedure do_remort(ch : GCharacter; param : string);
begin
  ch.sendBuffer('Disfunctional. Sorry.'#13#10);
end;
{ Revised - Nemesis }
procedure do_delete(ch: GCharacter; param : string);
var f:file;
begin
  if (ch.IS_NPC) then
    begin
    ch.sendBuffer('NPCs cannot delete.'#13#10);
    exit;
    end;
  if (not MD5Match(GPlayer(ch).md5_password, MD5String(param))) then
    begin
    ch.sendBuffer('Type DELETE <password> to delete. WARNING: This is irreversible!'#13#10);
    exit;
    end;
  GConnection(ch.conn).send('You feel yourself dissolving, atom by atom...'#13#10);
  write_console(ch.name^ + ' has deleted');
  GPlayer(ch).quit;
  assignfile(f, 'players\' + ch.name^ + '.usr');
  rename(f, 'backup\' + ch.name^ + '.usr');
end;
procedure do_wimpy(ch : GCharacter; param : string);
var wimpy:integer;
begin
  if (length(param) = 0) or (ch.IS_NPC) then
    begin
    ch.sendBuffer('Set wimpy to what?'#13#10);
    exit;
    end;
  try
    wimpy := strtoint(param);
  except
    ch.sendBuffer('That is not a valid number.'#13#10);
    exit;
  end;
  if (wimpy > ch.max_hp div 3) then
    ch.sendBuffer('Your wimpy cannot be higher than 1/3 of your total hps!'#13#10)
  else
    begin
    GPlayer(ch).wimpy := wimpy;
    ch.sendBuffer('Wimpy set to '+inttostr(wimpy)+'.'+#13#10);
    end;
end;
{ Allow players to lock their keyboard - Nemesis }
procedure do_keylock(ch : GCharacter; param: string);
begin
  GConnection(ch.conn).afk := true;
  GConnection(ch.conn).keylock := true;
  ch.sendBuffer('You are now away from keyboard.'#13#10);
  ch.sendBuffer('Enter your password to unlock.'#13#10);
end;
// Handle Notes - Nemesis
procedure do_note(ch : GCharacter; param : string);
var arg1, arg2, buf : string;
    number, counter : integer;
    note : GNote;
    node : GListNode;
begin
  if (ch.IS_NPC) then
    exit;
  param := one_argument(param, arg1);
  one_argument(param, arg2);
  if (length(arg1) = 0) then
    begin
    ch.sendBuffer('Usage: NOTE READ <number>'#13#10);
    ch.sendBuffer('       NOTE WRITE'#13#10);
    ch.sendBuffer('       NOTE LIST'#13#10);
    if (ch.IS_IMMORT) then
      ch.sendBuffer('       NOTE DELETE <number>'#13#10);
    exit;
    end
  else
  if (uppercase(arg1) = 'WRITE') then
    begin
    ch.substate := SUB_SUBJECT;
    ch.sendBuffer('You are now away from keyboard.'#13#10#13#10);
    act(AT_REPORT,'$n has left $s keyboard and starts writing a note.',false,ch,nil,nil,to_room);
    ch.sendBuffer('You start writing a note on the ' + board_names[GPlayer(ch).active_board] + ' board.'#13#10);
    GPlayer(ch).startEditing('');
    exit;
    end
  else
  if (uppercase(arg1) = 'LIST') then
    begin
    counter := 0;
    node := notes.head;
    if (node = nil) then
      begin
      ch.sendBuffer('There are no notes on this board.'#13#10);
      exit;
      end;
    while (node <> nil) do
      begin
      note := node.element;
      if (note.board = GPlayer(ch).active_board) then
        begin
        inc(counter);
        buf := buf + ch.ansiColor(15) + ' ' + pad_integer_front(note.number,3) + ch.ansiColor(7) + '>   ' + pad_string(note.author,20) + ' ' + note.subject + #13#10;
        end;
      node := node.next;
      end;
    if (counter = 0) then
      buf := ch.ansiColor(7) + 'There are no notes on this board.' + #13#10;
    buf := ch.ansiColor(8) + '[' + ch.ansiColor(9) + 'Num ' + ch.ansiColor(8) + '] ' +
           ch.ansiColor(8) + '[' + ch.ansiColor(9) + 'Author            ' + ch.ansiColor(8) + '] ' +
           ch.ansiColor(8) + '[' + ch.ansiColor(9) + 'Subject               ' + ch.ansiColor(8) + ']' + #13#10 + #13#10 + buf;
    ch.sendPager(buf);
    exit;
    end
  else
  if (uppercase(arg1) = 'READ') then
    begin
    if (length(arg2) = 0) then
      begin
      ch.sendBuffer('Usage: NOTE READ <number>'#13#10);
      exit;
      end;
    try
      number := strtoint(arg2);
    except
      ch.sendBuffer('Usage: NOTE READ <number>'#13#10);
      exit;
    end;
    note := findNote(GPlayer(ch).active_board, number);
    if (note = nil) then
      begin
      ch.sendBuffer('That is not a valid number.'#13#10);
      exit;
      end;
    buf := ch.ansiColor(3) + add_chars(76, '---- Note by ' + note.author, '-') + #13#10 +
           ch.ansiColor(7) + 'Date:    ' + ch.ansiColor(7) + note.date + #13#10 +
           ch.ansiColor(7) + 'Subject: ' + ch.ansiColor(7) + note.subject + #13#10 +
           ch.ansiColor(3) + add_chars(76, '', '-') + #13#10#13#10 +
           ch.ansiColor(7) + note.text +
           ch.ansiColor(3) + add_chars(76, '', '-') + #13#10;
    ch.sendPager(buf);
    GPlayer(ch).boards[GPlayer(ch).active_board] := number;
    exit;
    end
  else
  if (uppercase(arg1) = 'DELETE') then
    begin
    if (length(arg2) = 0) then
      begin
      ch.sendBuffer('Usage: NOTE DELETE <number>'#13#10);
      exit;
      end;
    try
      number := strtoint(arg2);
    except
      ch.sendBuffer('Usage: NOTE DELETE <number>'#13#10);
      exit;
    end;
    node := notes.head;
    if (node = nil) then
      begin
      ch.sendBuffer('There are no notes to delete.'#13#10);
      exit;
      end;
    while (node <> nil) do
      begin
      note := node.element;
      if (note.board = GPlayer(ch).active_board) and (note.number = number) then
        begin
        notes.remove(node);
        save_notes;
        ch.sendBuffer('Note succesfully deleted.'#13#10);
        exit;
        end;
      node := node.next;
      end;
    ch.sendBuffer('Not a valid number.'#13#10);
    exit;
    end
  else
    begin
    ch.sendBuffer('Usage: NOTE READ <number>'#13#10);
    ch.sendBuffer('       NOTE WRITE'#13#10);
    ch.sendBuffer('       NOTE LIST'#13#10);
    if (ch.IS_IMMORT) then
      ch.sendBuffer('       NOTE DELETE <number>'#13#10);
    exit;
    end
end;
// Bulletinboard - Nemesis
procedure do_board(ch : GCharacter; param : string);
var node : GListNode;
    i, counter, boardnumber : integer;
    note : GNote;
    arg1 : string;
begin
  if (ch.IS_NPC) then
    exit;
  param := one_argument(param, arg1);
  if (length(arg1) > 0) then
    begin
    try
      boardnumber := strtoint(arg1);
      if (boardnumber < BOARD1) or (boardnumber >= BOARD_MAX) or ((boardnumber = BOARD_IMM) and (not ch.IS_IMMORT)) then
        begin
        ch.sendBuffer('That board is not available.'#13#10);
        exit;
        end;
      GPlayer(ch).active_board := boardnumber;
      ch.sendBuffer('Current board changed to ' + board_names[GPlayer(ch).active_board] + '.'#13#10);
      exit;
    except
      arg1 := '';
    end;
    end;
  if (length(arg1) = 0) then
    begin
    act(AT_REPORT, '$8[$B$1Num$A$8] [$B$1Name      $A$8] [$B$1New$A$8] [$B$1Description$A$8]'#13#10,false,ch,nil,nil,TO_CHAR);
    for i:=1 to BOARD_MAX-1 do
      begin
      counter := 0;
      node := notes.head;
      while (node <> nil) do
        begin
        note := node.element;
        if (note.board = i) then
          begin
          if (note.number > counter) then
            counter := note.number;
          end;
        node := node.next;
        end;
      counter := counter - GPlayer(ch).boards[i];
      if (i = BOARD_IMM) then
        begin
        if (ch.IS_IMMORT) then
          act(AT_REPORT, '$B$7 ' + pad_integer_front(i, 2) + '$A$7>   ' + pad_string(board_names[i],10) + '   ' + pad_integer(counter,3) + '   ' + board_descr[i],false,ch,nil,nil,TO_CHAR);
        end
      else
        act(AT_REPORT, '$B$7 ' + pad_integer_front(i, 2) + '$A$7>   ' + pad_string(board_names[i],10) + '   ' + pad_integer(counter,3) + '   ' + board_descr[i],false,ch,nil,nil,TO_CHAR);
      end;
    ch.sendBuffer(#13#10 + ch.ansiColor(7) + 'Your current board is ' + board_names[GPlayer(ch).active_board] + '.'#13#10);
    if (GPlayer(ch).active_board = BOARD_NEWS) and (not ch.IS_IMMORT) then
      ch.sendBuffer(ch.ansiColor(7) + 'You can only read from this board.'#13#10)
    else
      ch.sendBuffer(ch.ansiColor(7) + 'You can both read and write on this board.'#13#10);
    end;
end;
{ Xenon 23/Apr/2001: apropos searches through all helpfiles for a match on user input }
procedure do_apropos(ch : GCharacter; param : string);
var
  node : GListNode;
  help : GHelp;
  matchlist : GDLinkedList;
  hk : THelpKeyword;
  s : string;
begin
  if (ch.IS_NPC) then
    exit;
  if (length(param) = 0) then
  begin
    ch.sendBuffer('Usage: APROPOS <text>'#13#10#13#10);
    ch.sendBuffer('Apropos searches for ''text'' in the online helpfiles.'#13#10);
    exit;
  end;
  matchlist := GDLinkedList.Create();
  
  node := help_files.head;
  while (node <> nil) do
  begin
    help := node.element;
    s := help.keywords;
    
    if ((s[length(s)] <> '_') and (pos(param, help.text) > 0) and (help.level <= ch.level)) then
    begin
      hk := THelpKeyword.Create(lowercase(s), help);
      insertAlphabetical(matchlist, hk);
    end;
    node := node.next;
  end;
  if (matchlist.getSize() = 0) then
  begin
    ch.sendBuffer(Format('No matches found for your query ''%s''.'#13#10, [param]));
    exit;
  end
  else
  begin
    ch.sendPager(act_string(Format('Found $B$7%d$A$7 helpfiles that match your query ''$B$7%s$A$7'':'#13#10#13#10, [matchlist.getSize(), param]), ch, nil, nil, nil));
    
    node := matchlist.head;
    while (node <> nil) do
    begin
      hk := node.element;
    
      ch.sendPager(act_string(Format('  $B$7%s$A$7'#13#10, [hk.keyword]), ch, nil, nil, nil));
      node := node.next;
    end;
  end;
  matchlist.Clean();
  matchlist.Free();
end;
{$I include\cmd_imm.inc}
{$I include\cmd_info.inc}
{$I include\cmd_move.inc}
{$I include\cmd_fight.inc}
{$I include\cmd_magic.inc}
{$I include\cmd_skill.inc}
{$I include\cmd_comm.inc}
{$I include\cmd_obj.inc}
{$I include\cmd_shops.inc}
{$I include\cmd_build.inc}