root/trunk/lib/fm/locus.rb

Revision 287, 8.4 kB (checked in by ged, 3 months ago)

Checkpoint commit

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Rev Author URL Id
Line 
1#!/usr/bin/ruby
2#
3# This file contains the FaerieMUD::Locus class, a derivative of
4# FaerieMUD::Entity. Instances of this class and its derivatives are entities
5# which occupy a location, have geometry and orientation, are capable of
6# colliding with each other, have relationships of containment with one another,
7# have componented construction/internal location, and have their own Mana
8# potential. They also introduce event propagation, which is the system which
9# causes events to move from one object to another based on their relationships.
10#
11# == Location
12#
13# Location in the world is defined by two attributes: containment and local
14# coordinates.
15#
16# === Containment
17#
18# Containment is the expression of a relation of proximity or enclosure between
19# one object and another. Containment affects the way events are propagated, and
20# is a factor in determining an object's perceptual salience in the description
21# engine.
22#
23# Containment at the Locus level is the simplest kind of containment; it
24# expresses a relationship of purely hierarchical enclosure of one object within
25# another's physical geometry. In physics terms, it means that one object's
26# geometry is contained within another's transform group.
27#
28# Some derivatives of Locus will probably need to define additional behaviours
29# and attributes to express their ideas of what containment means to them.
30#
31# === Local Coordinates
32#
33# Loci also have a relationship of location with their container, which provides
34# a local coordinate system for finer-grained directed event propagation and
35# description. The form the coordinate system takes is up to the specific
36# implementation of the container -- it can be cartesian coordinates (in which
37# case the container might also use an ODE::Space to represent its inner space),
38# or a simple location list for smaller objects.
39#
40# === Collision
41#
42# When two loci try to occupy the same space, a collision results. Most of
43# FaerieMUD's collision system is contained in this class. Each Locus contains a
44# FaerieMUD::Physics::Geometry and a FaerieMUD::Physics::TransformGroup. The
45# Geometry is the object which contains the object's location in the game world
46# relative to its container. The TransformGroup aggregates groups of objects
47# together so they can be transformed (ie., moved, rotated, etc.) as one
48# object. Objects which are added to the contents will be added to the
49# TransformGroup.
50#
51# ==== Componented Construction
52#
53# Componented construction is a behaviour which maintains a list of parts which
54# make up an integral whole, each of which may be or may not be a unique object
55# instance. This is used to model the detail of a component system for objects
56# without having to maintain an instance of each component in the game
57# world. See http://docs.faeriemud.org/bin/view/Dream/Components for more
58# information.
59#
60# == Event Propagation
61#
62# Loci also are capable of propagating events to the objects which contain or
63# are contained by them. This propagation is purely heirarchical in the Locus
64# class itself, but may be overridden to introduce more complex propagation
65# behaviours in derivatives.
66#
67# There are two kinds of event propagation: immersion and dispersal.
68#
69# === Immersion
70#
71# Event immersion is the propagation of events from an object to its
72# contents. Objects are only affected by events during this kind of
73# propagation. Immersion is controlled by an object's handlers, which must
74# propagate events (or modified clones of them) further inward if it is called
75# for.
76#
77# === Dispersal
78#
79# Dispersal is the propagation of events from an object to its
80# container/s. Objects should not be affected by event they disperse; if an
81# event is to affect an object, it must be reflected back by one of the object's
82# containers. Events typically are dispersed to the widest extent they can
83# affect in a given tick before being reflected back.
84#
85# == Subversion ID
86#
87# $Id$
88#
89# == Authors
90#
91# * Michael Granger <ged@FaerieMUD.org>
92#
93# :include: LICENSE
94#
95#---
96#
97# Please see the file LICENSE for licensing details.
98#
99
100require 'monitor'
101
102require 'fm/entity'
103require 'fm/mixins'
104require 'fm/exceptions'
105
106### Object class for Entities which occupy a location, have geometry and
107### orientation, are capable of colliding with each other, have relationships of
108### containment with one another, and have their own Mana potential. Also
109### contains the base event propagation subsystem.
110class FaerieMUD::Locus < FaerieMUD::Entity
111        contributors :ged
112
113        # SVN Revision
114        SVNRev = %q$Rev$
115
116        # SVN Id
117        SVNId = %q$Id$
118
119
120
121        ### Create a new Locus object.
122        def initialize( args={} )
123                @containers     = []
124                @contents       = []
125
126                super
127        end
128
129
130
131        ######
132        public
133        ######
134
135        # The Array of objects which contain the receiver, if any; may be empty
136        # (if it is not contained by anything) or one or more FaerieMUD::Locus
137        # objects.
138        attr_reader :containers
139
140        # The Array of FaerieMUD::Locus objects which are contained within the
141        # receiver.
142        attr_reader :contents
143
144
145
146        #############################################################
147        ###     C O N T A I N M E N T
148        #############################################################
149       
150        ### Adds the specified objects (FaerieMUD::Locus objects) to the
151        ### contents of the receiver.
152        def add_contents( *objects )
153                synchronize do
154                        objects.each do |obj|
155                                if obj.contains?( self )
156                                        raise FaerieMUD::ContainmentError,
157                                              "%s cannot both be contained by and contain %s" %
158                                              [ obj.to_s, self.to_s ]
159                                end
160                                obj.add_container(self)
161                        end
162                        synchronize( Sync::EX ) { @contents |= objects }
163                end
164
165                return objects
166        end
167
168
169        ### Append operator -- add the specified ojbect to the contents of the
170        ### receiver and return the receiver.
171        def <<( object )
172                self.add_contents( object )
173                return self
174        end
175
176
177        ### Remove the given objects, or if a class was given, all objects of
178        ### the given class or its derivatives from the contents of the
179        ### receiver.
180        def remove_contents( *objects )
181                removedObjects = nil
182
183                # Expand classes into the objects they match
184                synchronize( Sync::SH ) do
185                        expanded = objects.collect do |obj|
186                                if obj.is_a?( Class )
187                                        @contents.find_all {|cobj| cobj.is_a?(obj)}
188                                else
189                                        obj
190                                end
191                        end.flatten.uniq
192
193                        # Get the objects that'll be removed by intersecting the expanded
194                        # list with the current contents; then remove 'em.
195                        removedObjects = @contents & expanded
196                        removedObjects.each {|obj| obj.remove_container(self) }
197                        synchronize( Sync::EX ) { @contents -= removedObjects }
198                end
199
200                return removedObjects
201        end
202
203
204        ### Add the specified object to the list of the receiver's containers.
205        def add_container( object )
206                synchronize( Sync::SH ) {
207                        if self.contains?( object )
208                                raise FaerieMUD::ContainmentError,
209                                        "receiver is %s's container" % object.to_s
210                        end
211
212                        synchronize( Sync::EX ) {
213                                @containers |= [object]
214                        }
215                }
216        end
217
218
219        ### Remove the specified +object+ from the list of the receiver's
220        ### containers.
221        def remove_container( object )
222                synchronize( Sync::EX ) { @containers -= [object] }
223        end
224
225
226        ### Return +true+ if the receiver contains the specified object.
227        def contains?( obj )
228                return (@contents.include?( obj ) || @contents.find {|l| l.contains?(obj) }) ?
229                        true :
230                        false
231        end
232        alias_method :include?, :contains?
233
234
235        ### Return +true+ if the receiver is contained by the specified object.
236        def contained_by?( obj )
237                return @containers.include?( obj )
238        end
239
240
241        #############################################################
242        ###     E V E N T S
243        #############################################################
244
245        ### The fallback handler for Loci just immerses any events it receives.
246        def handle_any_event( event )
247                self.log.debug( "Fallback handler: Immersing a #{event.class.name}" )
248                self.immerse_events( event )
249        end
250
251
252        ### Event dispersal: propagate events outward (to the receiver's
253        ### container/s).
254        def disperse_events( *events )
255                results = []
256
257                # Pass on all of the given events to each of this object's
258                # containers.
259                @containers.each do |obj|
260                        results << obj.disperse_events( *events )
261                end
262
263                return results.flatten
264        end
265
266
267
268        #########
269        protected
270        #########
271
272        ### Event immersion: propagate events inward (to the receiver's
273        ### contents).
274        def immerse_events( *events )
275                self.log.debug "Immersing %d event" % [events.length]
276                results = []
277
278                # Distribute all the events to each contained item, saving any
279                # resulting events for return.
280                @contents.each do |obj|
281                        results << obj.handle_events( *events )
282                end
283
284                return results.flatten
285        end
286
287
288
289end # class FaerieMUD::Locus
Note: See TracBrowser for help on using the browser.