| 1 | #!/usr/bin/ruby |
|---|
| 2 | # |
|---|
| 3 | # This file contains the FaerieMUD::ComposedObject class, the |
|---|
| 4 | # FaerieMUD::ComposedObject::Constituent mixin, and the |
|---|
| 5 | # FaerieMUD::ComposedObject::Bifurcation class. |
|---|
| 6 | # |
|---|
| 7 | # FaerieMUD::ComposedObject is a derivative of FaerieMUD::Entity which is an |
|---|
| 8 | # attempt to model game objects which possess a high degree of individuality and |
|---|
| 9 | # internal complexity in a balanceable, predictable, and reusable way. This is |
|---|
| 10 | # done by dividing the functionality of the object into a number of subordinate |
|---|
| 11 | # parts, each with a statistic, and then giving the primary container object a |
|---|
| 12 | # balanced number of traits that derive from one or more of the statistics. |
|---|
| 13 | # |
|---|
| 14 | # FaerieMUD::ComposedObject::Constituent is a mixin which adds the necessary data |
|---|
| 15 | # structures to an object class to make it capable of being used as a constituent |
|---|
| 16 | # in a ComposedObject instance. |
|---|
| 17 | # |
|---|
| 18 | # == Synopsis |
|---|
| 19 | # |
|---|
| 20 | # class ComposedThingie < FaerieMUD::ComposedObject |
|---|
| 21 | # class Alpha < FaerieMUD::Entity |
|---|
| 22 | # include FaerieMUD::ComposedObject::Constituent |
|---|
| 23 | # def_statistic :foo, :bar |
|---|
| 24 | # end |
|---|
| 25 | # class Beta < FaerieMUD::Entity |
|---|
| 26 | # include FaerieMUD::ComposedObject::Constituent |
|---|
| 27 | # def_statistic :jim, :bob |
|---|
| 28 | # end |
|---|
| 29 | # class Gamma < FaerieMUD::Entity |
|---|
| 30 | # include FaerieMUD::ComposedObject::Constituent |
|---|
| 31 | # def_statistic :foo, :bar |
|---|
| 32 | # end |
|---|
| 33 | # |
|---|
| 34 | # def_constituents :alpha => Alpha, :beta => Beta, :gamma => Gamma |
|---|
| 35 | # end |
|---|
| 36 | # |
|---|
| 37 | # == Subversion ID |
|---|
| 38 | # |
|---|
| 39 | # $Id$ |
|---|
| 40 | # |
|---|
| 41 | # == Authors |
|---|
| 42 | # |
|---|
| 43 | # * Michael Granger <ged@FaerieMUD.org> |
|---|
| 44 | # |
|---|
| 45 | # :include: LICENSE |
|---|
| 46 | # |
|---|
| 47 | #--- |
|---|
| 48 | # |
|---|
| 49 | # Please see the file LICENSE for licensing details. |
|---|
| 50 | # |
|---|
| 51 | |
|---|
| 52 | require 'fm/mixins' |
|---|
| 53 | require 'fm/exceptions' |
|---|
| 54 | require 'fm/statistic' |
|---|
| 55 | require 'fm/entity' |
|---|
| 56 | |
|---|
| 57 | ### The (abstract) ComposedObject class. Instances of derivatives of this |
|---|
| 58 | ### class are aggregate objects which contain a balanced set of subordinate |
|---|
| 59 | ### parts which together define the behaviour of the whole. |
|---|
| 60 | class FaerieMUD::ComposedObject < FaerieMUD::Entity |
|---|
| 61 | include FaerieMUD::AbstractClass |
|---|
| 62 | |
|---|
| 63 | contributors :ged, :scotus |
|---|
| 64 | |
|---|
| 65 | ### Mixin that designates an object class as a constituent of a |
|---|
| 66 | ### ComposedObject. Adds a 'statistic' attribute and accessors. |
|---|
| 67 | module Constituent |
|---|
| 68 | |
|---|
| 69 | ### Methods which are added to including classes as class methods. |
|---|
| 70 | module ClassMethods |
|---|
| 71 | # A two-element Array containing the names of the aspects of |
|---|
| 72 | # the statistic that belongs to the Constituent -- |
|---|
| 73 | # developmental and linear. |
|---|
| 74 | attr_reader :statistic_names |
|---|
| 75 | |
|---|
| 76 | ### Create accessors for the constituent's |
|---|
| 77 | ### FaerieMUD::Statistic object with the given +devel_name+, |
|---|
| 78 | ### which will return the Statistic's developmental aspect, |
|---|
| 79 | ### and +linear_name+, which will return the Statistic's |
|---|
| 80 | ### linear constituent. |
|---|
| 81 | def def_statistic( devel_name, linear_name ) |
|---|
| 82 | devel = devel_name.to_s.intern |
|---|
| 83 | linear = linear_name.to_s.intern |
|---|
| 84 | |
|---|
| 85 | self.statistic_names.replace([ devel_name, linear_name ]) |
|---|
| 86 | |
|---|
| 87 | define_method( devel ) { self.statistic.developmental } |
|---|
| 88 | define_method( "#{devel}=" ) {|arg| |
|---|
| 89 | self.statistic.developmental = arg |
|---|
| 90 | } |
|---|
| 91 | |
|---|
| 92 | define_method( linear ) { self.statistic.linear } |
|---|
| 93 | define_method( "#{linear}=" ) {|arg| |
|---|
| 94 | self.statistic.linear = arg |
|---|
| 95 | } |
|---|
| 96 | end |
|---|
| 97 | end |
|---|
| 98 | |
|---|
| 99 | |
|---|
| 100 | ### Inclusion callback -- Install a 'def_statistic' function in |
|---|
| 101 | ### including classes to auto-generate statistic accessors. |
|---|
| 102 | def self::included( klass ) |
|---|
| 103 | klass.instance_variable_set( :@statistic_names, [] ) |
|---|
| 104 | klass.extend( ClassMethods ) |
|---|
| 105 | end |
|---|
| 106 | |
|---|
| 107 | |
|---|
| 108 | ############################################################# |
|---|
| 109 | ### I N S T A N C E M E T H O D S |
|---|
| 110 | ############################################################# |
|---|
| 111 | |
|---|
| 112 | ### Initialize the "statistic" part of a Constituent object. It uses |
|---|
| 113 | ### the ':statistic' member of the argument hash, if one exists. |
|---|
| 114 | def initialize( args={} ) |
|---|
| 115 | raise RuntimeError, "No statistic defined for #{self.class.inspect}" if |
|---|
| 116 | self.class.statistic_names.empty? |
|---|
| 117 | |
|---|
| 118 | # If the statistic is defined in the args, remove it from the |
|---|
| 119 | # arglist and use it as the element. If it's not in the arglist, |
|---|
| 120 | # just use Hydrogen. |
|---|
| 121 | element = args.delete( :statistic ) || 1 |
|---|
| 122 | devel, linear = self.class.statistic_names |
|---|
| 123 | @statistic = FaerieMUD::Statistic.new( element, devel, linear ) |
|---|
| 124 | |
|---|
| 125 | super |
|---|
| 126 | end |
|---|
| 127 | |
|---|
| 128 | |
|---|
| 129 | ### Copy constructor -- clone the +original+ object's statistic. |
|---|
| 130 | def initialize_copy( original ) |
|---|
| 131 | super |
|---|
| 132 | @statistic = original.statistic.clone |
|---|
| 133 | end |
|---|
| 134 | |
|---|
| 135 | |
|---|
| 136 | ###### |
|---|
| 137 | public |
|---|
| 138 | ###### |
|---|
| 139 | |
|---|
| 140 | # The FaerieMUD::Statistic associated with the receiver. |
|---|
| 141 | attr_accessor :statistic |
|---|
| 142 | |
|---|
| 143 | ### Return a String containing a human-readable version of the |
|---|
| 144 | ### statistic. |
|---|
| 145 | def to_s |
|---|
| 146 | "<%s: %s>" % [ self.class.name, self.statistic.to_s ] |
|---|
| 147 | end |
|---|
| 148 | |
|---|
| 149 | end # module Constituent |
|---|
| 150 | |
|---|
| 151 | |
|---|
| 152 | ### Abstract base class for bifurcations of one of a ComposedObject's |
|---|
| 153 | ### constituents. Bifurcations provide a container for constituents and |
|---|
| 154 | ### a "picking" function which returns the constituent which is |
|---|
| 155 | ### "current" according to some encapsulated criteria. Concrete |
|---|
| 156 | ### derivatives are required to implement the #current method, which |
|---|
| 157 | ### should return one of the constituents it contains. Bifurcation |
|---|
| 158 | ### objects will autoload delegator methods for any method that its |
|---|
| 159 | ### constituents respond to as they are called; creating a Bifurcation |
|---|
| 160 | ### with Constituents of unrelated classes may cause this mechanism to |
|---|
| 161 | ### break. |
|---|
| 162 | class Bifurcation < FaerieMUD::Entity |
|---|
| 163 | include FaerieMUD::AbstractClass |
|---|
| 164 | |
|---|
| 165 | contributors :ged |
|---|
| 166 | |
|---|
| 167 | ### Initialize a new Bifucation that will delegate the containing |
|---|
| 168 | ### ComposedObject's interaction with the given +constituents+. |
|---|
| 169 | def initialize( *constituents ) # :notnew: |
|---|
| 170 | raise ArgumentError, "Bifurcation requires two or more Constituents" unless |
|---|
| 171 | constituents.length >= 2 |
|---|
| 172 | check_each_type( constituents, FaerieMUD::Entity ) |
|---|
| 173 | @constituents = constituents |
|---|
| 174 | end |
|---|
| 175 | |
|---|
| 176 | |
|---|
| 177 | ### Copy initializer -- initialize a cloned or duped Bifurcation |
|---|
| 178 | ### with cloned values from the +original+. |
|---|
| 179 | def initialize_copy( original ) |
|---|
| 180 | super |
|---|
| 181 | @constituents = original.constituents.collect {|constituent| |
|---|
| 182 | constituent.clone |
|---|
| 183 | } |
|---|
| 184 | end |
|---|
| 185 | |
|---|
| 186 | |
|---|
| 187 | ###### |
|---|
| 188 | public |
|---|
| 189 | ###### |
|---|
| 190 | |
|---|
| 191 | # The constituents which make up the bifurcation |
|---|
| 192 | attr_accessor :constituents |
|---|
| 193 | |
|---|
| 194 | |
|---|
| 195 | ### Picker method: should return the "current" constituent. |
|---|
| 196 | virtual :current |
|---|
| 197 | |
|---|
| 198 | |
|---|
| 199 | ######### |
|---|
| 200 | protected |
|---|
| 201 | ######### |
|---|
| 202 | |
|---|
| 203 | ### Autoloading method delegation -- create delegator methods for |
|---|
| 204 | ### anything which the underlying objects support. |
|---|
| 205 | def method_missing( sym, *args ) |
|---|
| 206 | constituent = self.current |
|---|
| 207 | if constituent.respond_to?( sym ) |
|---|
| 208 | eval %{def #{sym}( *args ) self.current.#{sym}(*args) end} |
|---|
| 209 | constituent.send( sym, *args ) |
|---|
| 210 | else |
|---|
| 211 | super |
|---|
| 212 | end |
|---|
| 213 | end |
|---|
| 214 | end # class Bifurcation |
|---|
| 215 | |
|---|
| 216 | |
|---|
| 217 | |
|---|
| 218 | ############################################################# |
|---|
| 219 | ### C O N S T A N T S |
|---|
| 220 | ############################################################# |
|---|
| 221 | |
|---|
| 222 | # SVN Revision |
|---|
| 223 | SVNRev = %q$Rev$ |
|---|
| 224 | |
|---|
| 225 | # SVN Id |
|---|
| 226 | SVNId = %q$Id$ |
|---|
| 227 | |
|---|
| 228 | # The factor which is applied to traits' modifiers in the calculation of |
|---|
| 229 | # Effective Trait Levels. |
|---|
| 230 | TraitWeightingFactor = 1.0 |
|---|
| 231 | |
|---|
| 232 | |
|---|
| 233 | ############################################################# |
|---|
| 234 | ### C L A S S M E T H O D S |
|---|
| 235 | ############################################################# |
|---|
| 236 | |
|---|
| 237 | ### Inheritance callback -- adds two class instance variables to each |
|---|
| 238 | ### inheriting class to keep track of its constituents and traits. |
|---|
| 239 | def self::inherited( klass ) |
|---|
| 240 | klass.add_structure_variables |
|---|
| 241 | super |
|---|
| 242 | end |
|---|
| 243 | |
|---|
| 244 | |
|---|
| 245 | ### Add structure-defining instance variables to subclasses |
|---|
| 246 | def self::add_structure_variables |
|---|
| 247 | # Don't clobber variables that are already there. |
|---|
| 248 | unless self.instance_variables.include?( "@constituent_map" ) && |
|---|
| 249 | self.instance_variables.include?( "@trait_map" ) |
|---|
| 250 | |
|---|
| 251 | # Create class instance variables |
|---|
| 252 | self.instance_variable_set( :@constituent_map, {} ) |
|---|
| 253 | self.instance_variable_set( :@trait_map, {} ) |
|---|
| 254 | |
|---|
| 255 | class << self |
|---|
| 256 | attr_accessor :constituent_map |
|---|
| 257 | attr_accessor :trait_map |
|---|
| 258 | end |
|---|
| 259 | end |
|---|
| 260 | end |
|---|
| 261 | |
|---|
| 262 | |
|---|
| 263 | ### Define constituent accessors for the calling class. Each entry in the |
|---|
| 264 | ### +constituent_hash+ should be of the form: |
|---|
| 265 | ### |
|---|
| 266 | ### :name => <em>ConstituentClass<em> |
|---|
| 267 | ### |
|---|
| 268 | ### ComposedObject derivatives must have three or more constituents, and |
|---|
| 269 | ### there must be an odd number of them. |
|---|
| 270 | def self::def_constituents( constituent_hash ) |
|---|
| 271 | self.add_structure_variables |
|---|
| 272 | |
|---|
| 273 | # Check the hash for sanity |
|---|
| 274 | if constituent_hash.length < 3 |
|---|
| 275 | raise FaerieMUD::ConstituentError, |
|---|
| 276 | "Insufficient number of constituents." |
|---|
| 277 | elsif (constituent_hash.length % 2).zero? |
|---|
| 278 | raise FaerieMUD::ConstituentError, |
|---|
| 279 | "There must be an odd number of constituents." |
|---|
| 280 | end |
|---|
| 281 | |
|---|
| 282 | create_constituent_methods( constituent_hash ) |
|---|
| 283 | self.constituent_map.replace( constituent_hash ) |
|---|
| 284 | end |
|---|
| 285 | |
|---|
| 286 | |
|---|
| 287 | ### Check the given hash of constituent classes (like that passed to |
|---|
| 288 | ### def_constituents) for collision. |
|---|
| 289 | def self::check_constituent_hash( constituent_hash ) |
|---|
| 290 | |
|---|
| 291 | # Keep track of statistic aspect names we've already seen. |
|---|
| 292 | statAspects = {} |
|---|
| 293 | |
|---|
| 294 | # Check the hash for valid members and collisions between the names |
|---|
| 295 | # of the aspects of the constituents' statistics. |
|---|
| 296 | constituent_hash.each {|name, klass| |
|---|
| 297 | unless klass < FaerieMUD::ComposedObject::Constituent |
|---|
| 298 | raise FaerieMUD::ConstituentError, |
|---|
| 299 | "Invalid constituent '#{klass.name}': Does not "\ |
|---|
| 300 | "include ComposedObject::Constituent" |
|---|
| 301 | end |
|---|
| 302 | |
|---|
| 303 | # Make sure none of the aspect-names for the constituents' |
|---|
| 304 | # statistics collide with each other. |
|---|
| 305 | develAspect, linearAspect = klass.statistic_names |
|---|
| 306 | if statAspects.key?( develAspect ) |
|---|
| 307 | raise FaerieMUD::ConstituentError, |
|---|
| 308 | "Developmental aspect '%s' of %s "\ |
|---|
| 309 | "collides with the %s aspect of %s" % [ |
|---|
| 310 | develAspect, |
|---|
| 311 | klass, |
|---|
| 312 | statAspects[develAspect][:type], |
|---|
| 313 | statAspects[develAspect][:class], |
|---|
| 314 | ] |
|---|
| 315 | elsif statAspects.key?( linearAspect ) |
|---|
| 316 | raise FaerieMUD::ConstituentError, |
|---|
| 317 | "Linear aspect '%s' of %s "\ |
|---|
| 318 | "collides with the %s aspect of %s" % [ |
|---|
| 319 | linearAspect, |
|---|
| 320 | klass, |
|---|
| 321 | statAspects[linearAspect][:type], |
|---|
| 322 | statAspects[linearAspect][:class], |
|---|
| 323 | ] |
|---|
| 324 | end |
|---|
| 325 | statAspects[develAspect] = |
|---|
| 326 | { :type => 'developmental', :class => klass } |
|---|
| 327 | statAspects[linearAspect] = |
|---|
| 328 | { :type => 'linear', :class => klass } |
|---|
| 329 | } |
|---|
| 330 | |
|---|
| 331 | return true |
|---|
| 332 | end |
|---|
| 333 | |
|---|
| 334 | |
|---|
| 335 | ### Create accessors for both the constituents themselves, and delegated |
|---|
| 336 | ### ones for the 2 aspects of their statistics. |
|---|
| 337 | def self::create_constituent_methods( constituent_hash ) |
|---|
| 338 | check_constituent_hash( constituent_hash ) |
|---|
| 339 | |
|---|
| 340 | constituent_hash.each {|name, klass| |
|---|
| 341 | |
|---|
| 342 | # Gather symbols for the methods we'll be creating |
|---|
| 343 | nameEq = "#{name}=" |
|---|
| 344 | develAspect, linearAspect = klass.statistic_names |
|---|
| 345 | develAspectEq = "#{develAspect}=".intern |
|---|
| 346 | linearAspectEq = "#{linearAspect}=".intern |
|---|
| 347 | |
|---|
| 348 | # Create the constituent accessors |
|---|
| 349 | define_method( name ) { @constituents[name] } |
|---|
| 350 | define_method( nameEq ) {|arg| |
|---|
| 351 | check_type( arg, self.class.constituent_map[name] ) |
|---|
| 352 | @constituents[ name ] = arg |
|---|
| 353 | } |
|---|
| 354 | |
|---|
| 355 | # Create the delegated accessors for the statistics of the two |
|---|
| 356 | # aspects of the constituent. |
|---|
| 357 | define_method( develAspect ) { |
|---|
| 358 | self.send(name).statistic.developmental |
|---|
| 359 | } |
|---|
| 360 | define_method( develAspectEq ) {|arg| |
|---|
| 361 | self.send(name).statistic.developmental = arg |
|---|
| 362 | } |
|---|
| 363 | define_method( linearAspect ) { |
|---|
| 364 | self.send(name).statistic.linear |
|---|
| 365 | } |
|---|
| 366 | define_method( linearAspectEq ) {|arg| |
|---|
| 367 | self.send(name).statistic.linear = arg |
|---|
| 368 | } |
|---|
| 369 | } |
|---|
| 370 | end |
|---|
| 371 | |
|---|
| 372 | |
|---|
| 373 | ### Define a trait with the specified +name+ (a Symbol) in the calling |
|---|
| 374 | ### class. The trait will be derived from the statistics of the named |
|---|
| 375 | ### +constituents+ (also Symbols), which must be given in the order in |
|---|
| 376 | ### which they are used, with the primary constituent first. |
|---|
| 377 | def self::def_trait( name, *constituents ) |
|---|
| 378 | self.add_structure_variables |
|---|
| 379 | |
|---|
| 380 | raise FaerieMUD::ConstituentError, |
|---|
| 381 | "Trait must be derived from at least one constituent." if |
|---|
| 382 | constituents.empty? |
|---|
| 383 | |
|---|
| 384 | self.trait_map[ name ] = constituents |
|---|
| 385 | code = self.build_trait_methods( name, *constituents ) |
|---|
| 386 | |
|---|
| 387 | module_eval( code ) |
|---|
| 388 | end |
|---|
| 389 | |
|---|
| 390 | |
|---|
| 391 | ### Generate the source for trait methods to derive the value for the |
|---|
| 392 | ### specified +trait+ (a Symbol) that uses the statistics of the given |
|---|
| 393 | ### +constituents+ (Symbols). |
|---|
| 394 | def self::build_trait_methods( trait, *constituents ) |
|---|
| 395 | # Generate statistic-fetching code for each relevant constituent |
|---|
| 396 | fetchStats = '' |
|---|
| 397 | constituents.each_with_index do |constituent, i| |
|---|
| 398 | fetchStats += |
|---|
| 399 | "linear[#{i}] = @constituents[ :#{constituent} ].statistic."\ |
|---|
| 400 | "linear.to_f\n"\ |
|---|
| 401 | "devel[#{i}] = @constituents[ :#{constituent} ].statistic."\ |
|---|
| 402 | "developmental.to_f\n" |
|---|
| 403 | end |
|---|
| 404 | |
|---|
| 405 | |
|---|
| 406 | # Set the algorithm based on the number of constituents. 'stat' is the |
|---|
| 407 | # array of values derived from the statistic. |
|---|
| 408 | case constituents.length |
|---|
| 409 | |
|---|
| 410 | # ETL = (T + S1)/2 |
|---|
| 411 | when 1 |
|---|
| 412 | algorithm = "(( tmod * twf ) + stat[0]) / 2" |
|---|
| 413 | |
|---|
| 414 | # ETL = (T/2) + (2S1/6) + (S2/6) |
|---|
| 415 | when 2 |
|---|
| 416 | algorithm = "(tmod * twf)/2 + (2*stat[0])/6 + (stat[1]/6)" |
|---|
| 417 | |
|---|
| 418 | # ETL = (T/2) + (S1/4) + (S2/6) + (S3/12) |
|---|
| 419 | when 3 |
|---|
| 420 | algorithm = "(tmod * twf)/2 + (stat[0]/4) + (stat[1]/6) + (stat[2]/12)" |
|---|
| 421 | |
|---|
| 422 | # :TODO: Fill in the rest -- should really be algorithmic |
|---|
| 423 | # code-generation, but I can't see how to do it yet. |
|---|
| 424 | else |
|---|
| 425 | raise ConstituentError, |
|---|
| 426 | "Unhandled composite: Can't yet handle #{constituents.length} "\ |
|---|
| 427 | "statistical inputs." |
|---|
| 428 | end |
|---|
| 429 | |
|---|
| 430 | # For the trait value, the statistic's value is (linear+devel)/2; |
|---|
| 431 | # for the trait_max, it's just the developmental part. |
|---|
| 432 | return %Q{ |
|---|
| 433 | def #{trait} |
|---|
| 434 | linear = []; devel = [] |
|---|
| 435 | #{fetchStats} |
|---|
| 436 | stat = [] |
|---|
| 437 | linear.nitems.times {|i| |
|---|
| 438 | stat[i] = (linear[i] + devel[i]) / 2 |
|---|
| 439 | } |
|---|
| 440 | twf = TraitWeightingFactor |
|---|
| 441 | tmod = @traitModifiers[:#{trait}].to_f |
|---|
| 442 | |
|---|
| 443 | return Integer( #{algorithm} ) |
|---|
| 444 | end |
|---|
| 445 | def #{trait}_max |
|---|
| 446 | linear = []; devel = [] |
|---|
| 447 | #{fetchStats} |
|---|
| 448 | stat = devel |
|---|
| 449 | twf = TraitWeightingFactor |
|---|
| 450 | tmod = @traitModifiers[:#{trait}].to_f |
|---|
| 451 | |
|---|
| 452 | return Integer( #{algorithm} ) |
|---|
| 453 | end |
|---|
| 454 | } |
|---|
| 455 | end |
|---|
| 456 | |
|---|
| 457 | |
|---|
| 458 | ############################################################# |
|---|
| 459 | ### I N S T A N C E M E T H O D S |
|---|
| 460 | ############################################################# |
|---|
| 461 | |
|---|
| 462 | ### Create a new ComposedObject. |
|---|
| 463 | def initialize( args={} ) |
|---|
| 464 | raise RuntimeError, "No constituents defined for #{self.class.name}" if |
|---|
| 465 | self.class.constituent_map.empty? |
|---|
| 466 | |
|---|
| 467 | # Set up default constituents |
|---|
| 468 | @constituents = {} |
|---|
| 469 | self.class.constituent_map.each {|key,klass| |
|---|
| 470 | @constituents[key] = klass.new |
|---|
| 471 | } |
|---|
| 472 | |
|---|
| 473 | # Set up default trait modifiers |
|---|
| 474 | @traitModifiers = {} |
|---|
| 475 | self.class.trait_map.keys.each {|trait| |
|---|
| 476 | @traitModifiers[trait] = |
|---|
| 477 | FaerieMUD::PeriodicObject.new( :lithium ) |
|---|
| 478 | } |
|---|
| 479 | |
|---|
| 480 | super |
|---|
| 481 | end |
|---|
| 482 | |
|---|
| 483 | |
|---|
| 484 | ### Copy initializer -- initialize a cloned or duped ComposedObject with |
|---|
| 485 | ### cloned values from the +original+ object. |
|---|
| 486 | def initialize_copy( original ) |
|---|
| 487 | super |
|---|
| 488 | |
|---|
| 489 | @constituents = {} |
|---|
| 490 | original.constituents.each {|k,v| |
|---|
| 491 | @constituents[k] = v.clone |
|---|
| 492 | } |
|---|
| 493 | @traitModifiers = original.traitModifiers.clone |
|---|
| 494 | end |
|---|
| 495 | |
|---|
| 496 | |
|---|
| 497 | ###### |
|---|
| 498 | public |
|---|
| 499 | ###### |
|---|
| 500 | |
|---|
| 501 | # The Hash of constituents that the receiver delegates to, keyed by |
|---|
| 502 | # Symbol. |
|---|
| 503 | attr_accessor :constituents |
|---|
| 504 | |
|---|
| 505 | # The Hash of trait modifiers for this composed object, keyed by trait |
|---|
| 506 | # name. |
|---|
| 507 | attr_accessor :traitModifiers |
|---|
| 508 | |
|---|
| 509 | |
|---|
| 510 | ### Return a snapshot of the current values of the ComposedObject's traits, |
|---|
| 511 | ### keyed by name. |
|---|
| 512 | def traits |
|---|
| 513 | rval = {} |
|---|
| 514 | |
|---|
| 515 | self.class.trait_map.keys.each do |trait| |
|---|
| 516 | rval[ trait ] = self.send( trait ) |
|---|
| 517 | end |
|---|
| 518 | |
|---|
| 519 | return rval |
|---|
| 520 | end |
|---|
| 521 | |
|---|
| 522 | |
|---|
| 523 | end # class FaerieMUD::ComposedObject |
|---|