CoralMUD-0.15/
CoralMUD-0.15/core/
CoralMUD-0.15/data/
CoralMUD-0.15/data/areas/
CoralMUD-0.15/data/help/
CoralMUD-0.15/data/players/
CoralMUD-0.15/lib/automap/
CoralMUD-0.15/lib/items/
# Generates separators for output to players.
# example is in cmd_commands
def format_generator length=5
  Fiber.new do
    count = 0
    loop do
      count += 1
      if count == length
        count = 0
        Fiber.yield ENDL
      else
        Fiber.yield ""
      end
    end
  end
end

$security_flags = []

class Command
  attr :cmd_name, :cmd_funct, :cmd_args, :must_type_full, :hidden
  attr_writer :cmd_args
  def initialize n, a, full=false, h=false
    @cmd_name = n
    @cmd_funct = ("cmd_"+n).to_sym

    # load this particular command now.
    Kernel::load("lib/commands/" + n + ".rb")

    @cmd_args = a
    @hidden = h
    @must_type_full = full
  end

  def hash
    [cmd_name].hash
  end

  def eql?(other)
    [cmd_name].eql?([other.cmd_name])
  end

  # returns array of strings to print out for syntax.
  def syntax
    t = @cmd_args
    t = [[t]] if !t.is_a? Array

    args = []      
    count = 0
    t.each do |expected_array|
      count += 1
      if count == 1
        str = "Syntax:  #{@cmd_name}"
      else
        str = "         #{@cmd_name}"
      end
      expected_array.each do |expected|
        # each expected arg.
        str += case expected
          when :arg_none then ""
          when :arg_dir! then " <direction>"
          when :arg_str! then " <string literal>"
          when :arg_word!then " <word>"
          when :arg_int! then " <#>"
          when :arg_obj_inv! then " <item>"
          when :arg_obj_room! then " <item>"
          when :arg_obj_inv_or_room! then " <item>"
          when :arg_class! then " <Class>"
          when :arg_player_in_game! then " <player in game>"
          when :arg_actor_room! then " <npc/player>"
          when String then " " + expected          
          else ""
               
        end
      end 
      args << str
    end
    return args
  end
end



#is instanced off for player command tables.
class CommandTable
  attr_accessor :cmds
  def initialize 
    @cmds = {:secure=>[], :unsecure=>[]}
    init_cmd_table
  end

  # This is the initialize function that must be called manually to grab refernces from the global table.
  def init_cmd_table
    # Right now just adding all commands into the copied table. Maybe later a check for if is immortal or not.
    @cmds[:unsecure] = $tabCmd
    @cmds[:secure] = $tabWizCmd    
    return true
  end

  def cmd_lookup com, security = {}
    com.downcase!
    @cmds[:unsecure].each do |c|
      if c.must_type_full == true
        return c if com == c.cmd_name
      else
        return c if is_prefix com, c.cmd_name
      end
    end

    # if no security set then just go ahead and fail.
    return nil if security.empty? 
    @cmds[:secure].each do |c|
      next if !security.is_set?(c.cmd_name.to_sym)
      if c.must_type_full
        return c if com == c.cmd_name
      else
        return c if is_prefix com, c.cmd_name
      end
    end

    ### fail, if we found partial match we should do something about it here later.
    return nil
  end
end

#
# The command table, very simple, but easy to extend.
# This table is the prototype for the global table every 
# character loads with. The reference the elements, not copy.
#
$tabCmd += [
  Command.new("north",    :arg_none),
  Command.new("east",     :arg_none),
  Command.new("south",    :arg_none),
  Command.new("west",     :arg_none),
  Command.new("quit",     :arg_none,  true),
  Command.new("who",      :arg_none),
  Command.new("look",     [[:arg_none],[:arg_int!],["at",:arg_obj_inv_or_room!], [:arg_obj_inv_or_room!],["into", :arg_obj_inv_or_room!]]),

  Command.new("equipment",:arg_none),
  Command.new("wear",     :arg_obj_inv!),
  Command.new("put",    [[:arg_obj_inv!, "into", :arg_obj_inv_or_room!]]),
  Command.new("drop",   :arg_obj_inv!),
  Command.new("get",    [[:arg_obj_room!],[:arg_obj_inv_or_room!]]),

  Command.new("commands", :arg_none),
  Command.new("help",     [[:arg_word!], [:arg_none]]),
  Command.new("say",      :arg_str!),

  Command.new("gossip",   :arg_str!),

  Command.new("save",     :arg_none),
  Command.new("tell",     [[:arg_player_in_game!, :arg_str!],[:arg_word!, :arg_str!]]),
  Command.new("track",    :arg_player_in_game!),
  Command.new("open",     :arg_dir!),
  Command.new("close",    :arg_dir!),
  Command.new("inventory", :arg_none),

  Command.new("inews",    :arg_str!),
  Command.new("iruby",     :arg_str!),
  Command.new("ichat",     [[:arg_str!], [:arg_none]]),
  Command.new("icode",     [[:arg_str!], [:arg_none]]),
  Command.new("igame",     [[:arg_str!], [:arg_none]]),
  Command.new("ichannels", :arg_str!),
  Command.new("inventory", :arg_none), 
  Command.new("filter",   :arg_none),
#imm commands moved to new table.  Most players will not need to access other table.
]

$tabCmd.uniq!

$tabWizCmd += [
  Command.new("wizhelp",  :arg_none),
  Command.new("goto",  [[:arg_player_in_game!], [:arg_str!]]),
  Command.new("sockets",  :arg_none),
  Command.new("linkdead", :arg_none),
  Command.new("reboot", :arg_none, true),
  Command.new("shutdown", :arg_none, true),
  Command.new("buildwalk",  :arg_none),
  Command.new("asave",   :arg_none),
  Command.new("vlist", [[:arg_str!], [:arg_none]]),
  Command.new("vtag", :arg_str!),
  Command.new("instance", :arg_str!),
  Command.new("create", [[:arg_class!], [:arg_str!]]),
  Command.new("edit",   [[:arg_player_in_game!],[:arg_str!],[:arg_none]]),
  Command.new("source", :arg_word!),
  Command.new("snoop",  :arg_player_in_game!),
  Command.new("purge",  :arg_none),
  Command.new("hit",      :arg_actor_room!),
  Command.new("test",     :arg_none),
]

$tabWizCmd.uniq!

$tabWizCmd.each do |cmd|
  $security_flags.push cmd.cmd_name.to_sym
end
$security_flags.uniq!

$global_command_table  = CommandTable.new


class String
  ### check to see if expected args exist for this string.
  ### expected must be a single dimension array.
  def check_args expected, p
    s = self.dup
    product = []
    expected.each do |arg_expected|
      return false if arg_expected == :arg_none and s != ''
      if arg_expected.is_a? String
        one_word = ""
        one_arg!(s, one_word)
        if arg_expected.start_with?(one_word.strip)
          processed = arg_expected
        else
          processed = nil
        end
      else
        processed = s.send(arg_expected, p)
      end
      return false if (processed == nil and arg_expected != :arg_none) or processed == false
      product << processed
    end

    # if the string isn't empty it's probably a fail.
    return false if !s.strip.empty?

    return product
  end
end
class Player
  ### Function to execute a command just as though it were typed.
  ### example:   player.execute("look")
  ###            player.execute("look", "at Retnur");
  def execute_command comm, arg=""
    if @editing.empty? == false
      begin
        while !@editing.empty?
          if !@editing[0].respond_to?(:class_editor)
            text_to_player "#{@editing[0].class} class has no editor defined for it." + ENDL
            @editing.shift
          else
            break
          end
        end
        return if @editing.empty?
        edit_arr = @editing[0].class_editor.find_command(comm)
        if edit_arr[0]
          # act on the command found.  
          new_arg = edit_arr[0].filter(arg, self) # returns what the players arg translates into

          if new_arg == nil || (arg.empty? && edit_arr[0].arg_type != :arg_none)
            text_to_player "Incorrect format." + ENDL
          else
            edit_arr[0].call_fun(self, @editing[0], new_arg)
            execute_command("show") if edit_arr[0].name != "show" && @editing[0]
          end
          return
        end

      rescue Exception=>e
        text_to_player "Editor command failed." + ENDL
        log_exception e
      end
    end

    if (c = $global_command_table.cmd_lookup(comm, @security))
      args_to_pass = [c] #First arg is always command table lookup, and once args populate the array we'll splat it.
      failure = true
      ### If it's an array passed it has multiple arguments possibly.
      ### If not it represents a single argument.   These are defined on the table.
      if c.cmd_args.is_a?(Array) == false
        c.cmd_args = [[c.cmd_args]]
      end

      c.cmd_args.each do |each_arr|
      ### okay, since there is arrays involved it will use the full format
      ###  Like [[arg_str], [arg_none]]
      ### for each array contained we must check to see if it's valid from start to end.
      ### If not valid we go to the next.  All arrays must fail for it really to fail the checks.
        processed = arg.check_args each_arr, self
        next if processed == false          
        failure = false ### found a buyer.
        args_to_pass = args_to_pass + processed  ### This is how we're passing it. win
        break
      end
 
      ### if we failed let's report our failure.
      if failure == true
        if c.respond_to? :arg_failure_msg
          # Failure message defined.  Pass to this method which arg failed.
          text_to_player c.arg_failure_msg
        else
          text_to_player "Bad arguments (#{arg}) for #{c.cmd_name} command." + ENDL
          view c.syntax.join(ENDL) + ENDL
        end
        return 
      end
      ### dispatched to cmd_function
      begin
        self.send(c.cmd_funct, *args_to_pass)
      rescue Exception=>e
        log_exception e
        text_to_player "Command failed." + ENDL
      end
    else
      self.text_to_player "No such command." + ENDL
    end
  end

  def goto_make_room vnum
    if vnum <= 0
      text_to_player "Invalid room. Cannot create." + ENDL
      return
    end

    r = Room.new(vnum)
    text_to_player "Created." + ENDL
    return r
  end

  # automatically creates new rooms and digs them out in a direction.
  def buildwalk(dir)
    m = Automap.new(in_room, [(-1..1),(-1..1)], {:full_traverse=>true})
    found = m.find(Automap.offset([0,0], dir))

    if !found
      r = nil 
    else
      r = found[:room]
    end

    if r == nil
      new_room = Room.dig_rooms(in_room.vnum, Vnum.gen_vnum, dir)
      new_room.sector = in_room.sector # same sector as old room
      new_room.namespace = in_room.namespace
      new_room.assign_tag Tag.gen_generic_tag(new_room), in_room.namespace
    else
      Room.dig_rooms(in_room.vnum, r.vnum, dir)
    end
  end
end