CoralMUD-0.26/
CoralMUD-0.26/core/
CoralMUD-0.26/data/
CoralMUD-0.26/data/help/
CoralMUD-0.26/data/socials/
CoralMUD-0.26/lib/automap/
CoralMUD-0.26/lib/items/
class Object
  # everything can have an owner.
  attr_accessor :owner

  # easier definition for accessing a metaclass.
  def metaclass
    class << self; self; end
  end

  # simply mark this item as destroyed for intents of the game engine.
  def recycle
    @recycled = true
  end
  # ist his item recycled?
  def recycled?
    @recycled == true 
  end
end

# Storable items can be placed in inventory.
module Storable
end

$editable_classes = {}


module CoralMUD

  module LivingEntity
    include HealthPool

    def inventory
      stuff.select{|o| o.is_a? Item} - worn_items
    end

    def each_stuff_not_worn
      inventory.each {|item| yield item}
    end

    def each_stuff_worn
      self.worn_items.each { |item| yield item, item.worn_on }
    end
   
 
    def in_room
      @owner
    end
    attr_accessor :wearing

    def is_npc?
      return is_a?(NPC)
    end

    def is_blind?
      false
    end

    def is_mute?
      false
    end

    def is_numb?
      false
    end

    def is_deaf?
      false
    end

    def can_hear? something
      case something
        when String
          return !is_deaf? 
        when NpcFacade, Player, ItemFacade
          return !is_deaf?  # it's possible we will have ways to communicate without having to hear it.
      end
    end

    def can_see? thing
      if is_blind?
        false
      end
      true
    end


    def short_desc
      return @name
    end

    # just return the items being worn right now.
    def worn_items
      if !@wearing
        return []
      end

      return @wearing.values
    end

    def remove obj
      @wearing = {} if !@wearing
      @wearing.delete_if {|v,k| k == obj }
    end

    # wear an obj in one of the locations checking them in order for empty spots.
    def wear obj, locations=obj.worn_locs
      locations = [locations].flatten

      @wearing = {} if !@wearing

      # find the first location with nothing in it.
      locations.each do |a_loc|
        if !@wearing[a_loc] 
          @wearing[a_loc] = obj
          obj.worn_on = a_loc
          return true # bingo
        end
      end
      return false
    end

    # get an object from the room
    def get obj
      if obj.is_a? Array
        obj.each do |o|
          get(o) 
        end
      else
        obj.owner.remove obj
        accept obj
      end
    end

    # drop some this thing has
    def drop obj
      if obj.is_a? Array
        obj.each do |o|
          drop(o)
        end
      else
        obj.owner.remove obj
        in_room.accept obj
      end
    end

    def to_room  room  ### anything including this should may be placed into a room.
      if room.is_a? Integer
        r = Vnum.get_room_index(room)
        r = goto_make_room room if !r
        room = r
      end
      room.accept_player(self)
    end

    def from_room
      if in_room != nil
        in_room.remove_player(self)
      end
      in_room = nil
    end
    def is_imm?
      if @level
        return @level >= LEVEL_IMM
      end
      false
    end

    def is_admin?
      return @level == LEVEL_ADMIN if @level
      false
    end

    def listen thing, author
      if can_hear?(author)
        "#{thing}"
      else
        "something"
      end
    end

    def peek thing, a=true, full=true, just_path=false
      orig_thing = thing
      name = ""
      if thing.is_a? Array
        if thing.empty?
          return "nothing"
        end
        # it's an array of things to peek at, at once.
        found = []
        thing.flatten!
        paths = []
        thing.each do |element|
          found << peek(element, false, false)
          paths << peek(element, false, true, true)
        end
        paths.uniq!
        name += (found.en.conjunction).gsub(/\ban?\s+([A-Z])/, '\1')
        if full
          if paths.count == 1
            name += paths[0]
          else
            name += " in #{"source".en.quantify(paths.count)}"
          end
        end
      else
        count = 0
        # do while loop.
        loop do
          count += 1
          a = true if count == 2
          begin
            a = false if thing.is_a? Player
            s = a ? thing.short_desc.en.a : (thing.short_desc + if thing.socket then "" else "(linkless)" end)
          rescue
            s = "#{thing}"
          end
 
          break if thing == self || !thing.owner && thing != orig_thing
          name += case count
            when 1 then ""
            when 2 
              " from "
            else
              " in "
          end
          name += s if !just_path || count > 1
          thing = thing.owner
          break if !full
          break unless thing
        end
      end
      return name
    end

    def view obj
      if @socket
        case obj
        when String then @socket.text_to_socket obj
        when CoralMUD::LivingEntity # anything that supports this module.
          view "#{peek(obj).capitalize} has no distinguishing features." + ENDL

          if !obj.worn_items.empty?
            view "#{peek(obj).capitalize} is wearing:" + ENDL
            obj.each_stuff_worn do |item, location|
              view "#{location} " + peek(item, true, false) 
            end
          end
  
          view "#{peek(obj).capitalize} is carrying:" + ENDL
          found = []
          obj.each_stuff_not_worn do |item|
            found << item
          end
          view peek(found, true, false) + ENDL
        when ItemFacade,Player, NpcFacade # only reaches here if not defined when above here.  
          # description would eventually be here.
          view "#{peek(obj).capitalize} has no distinguishing features." + ENDL
        else
          view "#{obj}"
        end
      end
    end

    def text_to_player txt
      if @socket
        @socket.text_to_socket txt
      end
    end

    def go_anywhere
      found = nil

      log :info, "Moving someone."

      in_room.exit_list.each do |ex|
        next if ex == nil
        ex.enter(self)
        return
      end

      in_room.remove_player(self)
      r = Vnum.get_room_index(1)
      if r == nil
        r = goto_make_room 1
      end
      r.accept_player(self)
    end
  end

  module HasStuff    
    # put anything in this room in the generic list for stuff.
    def accept thing
      if self.respond_to? :can_accept?
        if !self.can_accept? thing
          return false
        end
      end
      @stuff = [] if !@stuff
      @stuff << thing
      thing.owner = self
      return true
    end

    def stuff
      @stuff || []
    end

    # remove anything from this room.
    def remove thing
      if self.respond_to? :can_remove?
        if !self.can_remove? thing
          return false
        end
      end

      thing.owner = nil

      @stuff.delete thing if @stuff
      @stuff = nil if @stuff && @stuff.empty?
      return true
    end

    

    def each_stuff of_type=Object
      @stuff.each do |thing| 
        case thing
        when *of_type
          yield thing 
        end
      end
    end

    def count_stuff of_type=Object
      c = 0
      @stuff.each {|thing| c += 1 if thing.is_a?(of_type)}
      return c
    end
  end

  module VirtualTags
    attr_accessor :vtag, :namespace
    def assign_tag tag_str, namespace=nil
      # do minimal checking to make sure the tag doesn't already exist.
      if @vtag 
        Tag.clear_tag(self)
      end
      @vtag = Tag.new(tag_str, self, namespace) # generate a tag.
      @namespace = namespace
    end
    def reassociate_tag namespace=nil
      @vtag.reassociate(self, namespace)

      @namespace = namespace
    end


    # Note: Most of the code involving the VirtualTag system is in tags.rb.
  end

  # If class can be written to file.
  module FileIO
    # Save any class to a file. Path should be given to the directory to save in.
    # example:  "player/Retnur.yml"
    def save_to_database
      begin 
        str_to_write = YAML::dump gen_configure
        self.extra_data = str_to_write
        self.save
      rescue Exception=>e
        log :error, "Unable to write to database: #{self}"
        log_exception e
      end
    end

    # Load and configure a class from their database definition.
    def load_from_database
      begin
        a = YAML::load self.extra_data
        @when = Time.now.to_i 
      rescue Exception=>e
        log_exception e
        log :error, "unable to load YAML."
        return nil
      end
      begin
        configure(a)
        return self
      rescue Exception=>e
        log_exception e
        log :error, "Unable to configure: #{dir}"
        return nil
      end
    end


    def save_to_file file
      begin
        config = gen_configure
        str_to_write = YAML::dump config
        File.open( file, 'w' ) do |out|
          out.puts str_to_write
        end
      rescue Exception=>e
        log :error, "Unable to write: #{file}"
        log_exception e
      end
    end
 
    # Load and configure a class from a file.   Path should be given to the directory to load from.
    # example:  "player/Retnur.yml"
    def load_from_file dir
      begin 
        a = YAML::load_file dir
        @when = Time.now.to_i # For finding out later if it needs to be reloaded.
                              # Class may or may not even make use of this.   Adding it at the time for help file support.
      rescue Exception=>e
        log_exception e
        log :error, "unable to load YAML."
        return nil
      end
      begin
        configure(a)
        return self
      rescue Exception=>e
        log_exception e
        log :error, "Unable to configure: #{dir}"
        return nil
      end
    end

    # convert map to instance.
    def configure data
      version = data[:version]

      # Note: These hooks do *not* need to be defined. 
      # They're only defined if you're using version controlling
      # or if you want more control on how data is transformed beyond generics.
      # hook for calling version_control
      if self.respond_to?:version_control
        data = self.version_control(version, data)
      end

      # Hook for calling data_transform
      if self.respond_to?:data_transform_on_load
        data = self.data_transform_on_load(version, data)
      end

      # load hash into object directly.   
      data.each do |key, value|
        if value.is_a?(String) && value == DEFAULT_STRING
          self.instance_variable_set(key, DEFAULT_STRING) # save a little memory.
        else
          self.instance_variable_set(key, value) # sets all instance variables by name of key.
        end
      
      end
    end

    # generate the hash we save.
    def gen_configure
      if respond_to?(:to_configure_properties) 
        #If this method is defined it will return an array of variables we must add to this hash.
        config_list = to_configure_properties

        data = {}
        # for each item we must configure...
        config_list.each do |item|
          val = instance_variable_get(item) # grab the value for this key
          data[item] = val # sets the value in our hash
        end

        # hook for transforming the data if need be.
        if respond_to?(:data_transform_on_save)
          data = data_transform_on_save(data)
        end
        return data
      end
    end
  end
end