#
# 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