znmud-0.0.1/benchmark/
znmud-0.0.1/cmd/
znmud-0.0.1/cmd/emotes/
znmud-0.0.1/cmd/objects/
znmud-0.0.1/cmd/tiny/
znmud-0.0.1/doc/
znmud-0.0.1/farts/
znmud-0.0.1/lib/
znmud-0.0.1/lib/combat/
znmud-0.0.1/lib/core/bodytypes/
znmud-0.0.1/lib/engine/
znmud-0.0.1/lib/farts/
znmud-0.0.1/logs/
#
# file::    body.rb
# Author:   Craig Smith

# This source code copyright (C) 2009 Craig Smith
# All rights reserved.
#
# Released under the terms of the TeensyMUD Public License
# See LICENSE file for additional information.
#

$:.unshift "lib" if !$:.include? "lib"

require 'gettext'
require 'core/gameobject'
require 'core/bodypart'
require 'storage/properties'
require 'core/bodytypes/humanbody'
require 'core/bodytypes/quadbody'

# Body class is used as a container of body parts.  It is also used to
# create a "body" of different parts.

class Body < GameObject
  include GetText
  include HumanBody
  include QuadTailBody
  bindtextdomain("core")
  logger 'DEBUG'

  property :bodyparts, :type, :severed

  # Create a new Body
  # This not meant to be called directly from a player but the game
  # system itself, so there are no owners.
  # [+type+]    Type of body to create
  # [+return+]  A handle to the new Container.
  def initialize(type, owner)
    super("#{type.to_s} body", owner)
    self.bodyparts = []	# Array of all the parts that make up this body
    self.type = type
    self.severed = []	# Array of severed limbs
    case type
    when :human, "", nil
	make_body(HUMAN_DNA, HUMAN_CORE, HUMAN_DNA_DETAILS)
    when :quad_tail
	make_body(QUADTAIL_DNA, QUADTAIL_CORE, QUADTAIL_DNA_DETAILS)
    end
    put_object(self)	
  end

  # Rebuild any severed limbs..
  def reset
    if severed
    	self.severed.clear
    else
	self.severed = []
    end
    case type
    when :human, "", nil
	make_body(HUMAN_DNA, HUMAN_CORE, HUMAN_DNA_DETAILS)
    when :quad_tail
	make_body(QUADTAIL_DNA, QUADTAIL_CORE, QUADTAIL_DNA_DETAILS)
    end
  end

  # Checks if body has the part specifyed by the string name
  # [+partname+] String of the body part name
  # [+return+] True if it exists
  def has_body_part?(partname)
	bodyparts.each do |bpid|
		return true if get_object(bpid).name == partname
	end
	false
  end

  # Gets the body part given a part name
  # [+partname+] String of the name to search for
  # [+return+] BodyPart or nil if not found
  def get_bodypart(partname)
	bodyparts.each do |bpid|
		bp = get_object(bpid)
		if not bp
			log.error "Body #{id} contains an invalid body part id #{bpid}"
		else
			return bp if bp.name == partname
		end
	end
	return nil
  end

  # Creates/Recreates the body based on DNA
  # [+dna+] The map of parts
  # [+core+] Array of strings that are core parts
  # [+details+] Special details about the body parts
  # [+return+] the rebuilt body
  def make_body(dna, core, details)
	newparts = []
	dna.each do |partname, headname|
		if not has_body_part?(partname)
			newparts << BodyPart.new(partname, owner)
		end
	end
	# Add the new parts but in a disconnected state
	newparts.each { |newbp| self.bodyparts << newbp.id }
	# Connect the new parts
	newparts.each do |newbp|
		# Is this a core body part?
		newbp.core = true if core.include? newbp.name
		# Add special attribute details
		if details[newbp.name]
			details[newbp.name].each do |bpattr|
				newbp.add_attribute bpattr
			end
		end
		# Attach it to the body
		headname = dna[newbp.name]
		if headname
			bp = get_bodypart(headname)
			if bp
				newbp.setHead(bp.id)
				bp.addTail(newbp.id)
			else
				log.error "Could not get head body part named #{headname}"
			end
		end
	end
	# Reset all the body parts to make sure they are all cleaned up
	bodyparts.each { |bpid| get_object(bpid).reset }
	bodyparts
	if severed
		self.severed.clear
	else
		self.severed = []
	end
  end

  # Attempts to wear an object
  # [+oid+] Object ID to wear
  # [+return+] true if successful
  def wear(oid)
	o = get_object(oid)
	#double check object
	if o.val.has_key? "worn"
		where = o.val["worn"]
		self.bodyparts.each do |partid|
			# Looks for a body part that has that location attribute
			# and does not currently have anything there
			part = get_object(partid)
			if part.has_attribute? where and not part.worn.has_key? where
				part.wearItem(o.id,where)
				return true
			end
		end
	end
	false
  end

  # Removes an article of clothing/armor
  # [+oid+] oid of item to remove
  # [+return+] oid of item removed or nil if uncesseful
  def remove(oid)
	o = get_object(oid)
	self.bodyparts.each do |partid|
		part = get_object(partid)
		part.wearing.each do |a|
			if a == oid
				return part.removeItem(o.val["worn"])
			end
		end
	end
	nil		
  end

  # Returns an array of oid that is currently being worn on the body
  # [+return+] Array of worn Oids
  def wearing
	clothes = []
	self.bodyparts.each do |partid|
		part = get_object(partid)
		part.wearing.each do |a|
			clothes << a
		end
	end
	clothes
  end

  # Find something worn on the body
  # [+what+] what to find
  # [+return+] Returns an array of found oids
  def find(what)
	all = false
	if what=~/^all\.(.*)/
		all = true
		what = $2
	end
	clothes = []
	wearing.each do |c|
		gotname = false
		o = get_object(c)
		if o.name=~/^#{what}$/
			gotname = true
		elsif o.aliases.size > 0
			o.aliases.each do |a|
				gotname = true if a=~/^#{what}$/
			end
		end
		if gotname
			clothes << c
			return clothes if not all
		end
	end
	clothes
  end

  # return an array of body part ids based on part name
  # [+part+] Part name to search for (supports plurals: feet, hands, etc.)
  # [+return+] array of matching bodypart ids
  def getbodyid(part)
	foot = _("foot")
	hand = _("hand")
	arm = _("arm")
	leg = _("leg")
	bpids = []
	bodyparts.each do |bpid|
		bp = get_object(bpid)
		case part
		when _("feet")
			bpids << bp.id if bp.name =~/#{foot}/
		when _("hands")
			bpids << bp.id if bp.name =~/#{hand}/
		when _("arms")
			bpids << bp.id if bp.name =~/#{arm}/
		when _("legs")
			bpids << bp.id if bp.name =~/#{leg}/
		else
			bpids << bp.id if bp.name =~/#{part}/
		end
	end
	bpids
  end

  # returns the maxhp this body can hold
  # [+return+] Total possible hp for this body
  def maxhp
	total = 0
	bodyparts.each {|bpid| total += get_object(bpid).maxhp}
	total
  end

  # Assign health to all the body parts
  # [+max+] is the max health for the entire body
  # [+return+] undefined
  def assignhp(max)
	chunk = (max / bodyparts.size).to_i
	extra = max - (bodyparts.size * chunk)
	bodyparts.each do |bpid|
		bp = get_object(bpid)
		bp.health = chunk
		if extra > 0
			bp.health += 1
			extra -= 1
		end
		bp.maxhp = bp.health
	end
  end

  # Sets a bodies hp to a set value
  # [+hp+] Health to set
  def set_hp(hp)
	hp = maxhp if hp > maxhp
	# First clear the bodyparts
	bodyparts.each {|bpid| get_object(bpid).health = 0}
	# Assign each point to each part (probably should just do the math...)
	while hp > 0
		bodyparts.each do |bpid|
			bp = get_object(bpid)
			bp.health += 1
			hp -= 1
			return if hp < 1
		end
	end
  end

  # Scans the body parts and returns total health
  # [+return+] total body health
  def health
	hp = 0
	bodyparts.each {|bp| hp += get_object(bp).health }
	hp
  end

  # Add health to the body
  # [+amt+] Amount of health to add
  # [+return+] healed
  def heal(amt)
  	healed = false
  	while amt > 0 and healed == false do
		healed = true
		# keep looping until until we stop healing
		bodyparts.each do |bpid|
			bp = get_object(bpid)
			if bp.health < bp.maxhp and amt > 0
				bp.health += 1
				amt -= 1
				healed = false
			end
		end
	end
	healed
  end

  # Causes damage to a bodypart
  # [+part+] can be the name of the part or the bodyid
  # [+amt+] Amount of damage
  # [+return+] true if they are still alive
  def damage(part, amt)
	alive = true
	if not part # No part specified.  Pick random part
		part = bodyparts[rand(bodyparts.size)]
	end
	if part.is_a? String
		partid = getbodyid(part)
		if not partid
			log.warn("damage() could not locate bodypart #{part}")
			return alive
		else
			part = partid
		end
	end
	bp = get_object(part)
	if bp.health > 0
		if bp.health - amt > 0
			bp.health -= amt
			amt = 0
		else
			amt -= bp.health
			bp.health = 0
		end
	end
	# If there is still damage left over, distribute it
	while amt > 0 and health > 0 do
		bodyparts.each do |bpid|
			bp = get_object(bpid)
			if bp.health > 0
				if bp.health - amt > 0
					bp.health -= amt
					amt = 0
				else
					amt -= bp.health
					bp.health = 0
				end
			end
		end
	end
	return false if health <= 0
	true
  end

  # Wield something as a weapon
  # [+oid+] oid of what to wield.  Possible to wield two things (if 2 hands)
  # [+return+] true on success
  def wield(oid)
	hand = _("hand")
	bodyparts.each do |bpid|
		bp = get_object(bpid)
		if bp.name=~/#{hand}/ and not bp.wield
			bp.wield = oid
			return true
		end
	end
	false
  end

  # Remove a wielded item
  # [+oname+] Object name or OID of object
  # [+return+] the oid of the removed item or nil
  def unwield(oname)
	bodyparts.each do |bpid|
		bp = get_object(bpid)
		if bp.wield
			if oname.is_a? String
				obj = get_object(bp.wield)
				if obj.name=~/^#{oname}$/i
					bp.wield = nil
					return obj.id
				end
				obj.aliases.each do |a|
					if a=~/^#{oname}$/i
						bp.wield = nil
						return obj.id
					end
				end
			else	# We assume oname is an oid
				if bp.wield == oname	
					bp.wield = nil
					return oname
				end
			end
		end
	end
	nil
  end

  # Returns items the characters is wielding
  # [+oid+] Option oid to check for
  # [+return+] List of wielded oids
  def wielding?(oid=nil)
  	weapons = []
	bodyparts.each do |bpid|
		bp = get_object(bpid)
		if bp.wield
			if oid
				weapons << bp.wield if oid == bp.wield
			else
				weapons << bp.wield
			end
		end
	end
	weapons
  end

  # Sever a limb
  # [+partid+] bodypart id to sever the head of
  # [+return+] true if fatal
  def sever(partid)
	part = get_object(partid)
	if part.head
		attached = get_object(part.head)
		attached.tail.delete(part.head)
		part.head = nil
	elsif part.tail.size == 1
		attached = get_object(part.tail[0])
		if attached.head == partid
			attached.head = nil
		else
			log.error "BodyPart #{attached.id} does not have head of #{part.id}"
			return
		end
		part.tail.delete part.tail[0]
	else
		log.error "Could not sever body part #{partid}"	
	end
	self.severed = [] if not severed
	self.severed << partid
	self.bodyparts.delete(partid)
	part.tail.each {|tid| self.bodyparts.delete(tid) }
	return true if part.core
	false
  end
end