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