root/trunk/lib/fm/spirit.rb

Revision 287, 5.8 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::Spirit class. Instances of this class are
4# the permanent record of a player's accomplishments in the world, and also the
5# means by which she controls characters or other AnimatedObjects in the world.
6#
7# == Subversion ID
8#
9# $Id$
10#
11# == Authors
12#
13# * Michael Granger <ged@FaerieMUD.org>
14#
15# :include: LICENSE
16#
17#---
18#
19# Please see the file LICENSE for licensing details.
20#
21
22require 'fm/mixins'
23require 'fm/exceptions'
24require 'fm/commandparser'
25require 'fm/statistic'
26
27### Object class for maintaining a permanent record of a player's
28### accomplishments in the world. Also the means by which she controls
29### FaerieMUD::Character objects or other instances of FaerieMUD::AnimatedObject
30### in the world.
31class FaerieMUD::Spirit < FaerieMUD::Entity
32        include FaerieMUD::AccessorFunctions
33       
34        contributors :ged
35
36        # SVN Revision
37        SVNRev = %q$Rev$
38
39        # SVN Id
40        SVNId = %q$Id$
41
42
43
44        #################################################################
45        ###     C O N S T I T U E N T   C L A S S E S
46        #################################################################
47
48        ### The spirit's interface to the currently-animated object's physical
49        ### constituent.
50        class PhysicalAspect < FaerieMUD::Entity
51                include FaerieMUD::ComposedObject::Constituent
52                contributors :ged
53                def_statistic :incarnativity, :presence
54        end
55
56
57        ### The spirit's interface to the currently-animated object's mental
58        ### constituent.
59        class MentalAspect < FaerieMUD::Entity
60                include FaerieMUD::ComposedObject::Constituent
61                contributors :ged
62                def_statistic :kenning, :cognizance
63        end
64
65       
66        ### The spirit's interface to the currently-animated object's creative
67        ### constituent.
68        class CreativeAspect < FaerieMUD::Entity
69                include FaerieMUD::ComposedObject::Constituent
70                contributors :ged
71                def_statistic :divinity, :influence
72        end
73
74
75        ### Define accessor methods for the given aspect.
76        def self::def_aspect_methods( *aspects )
77                aspects.each do |aspect|
78                        aspectClass = const_get( "#{aspect.to_s.capitalize}Aspect" ) or
79                                raise FaerieMUD::ConstituentError,
80                                "Cannot define aspect methods for non-existent aspect '#{aspect}'"
81
82                        # The aspect itself
83                        define_method( aspect ) { @aspects[aspect] }
84                        define_method( "#{aspect}=" ) {|arg|
85                                check_type( arg, aspectClass )
86                                @aspects[aspect] = arg
87                        }
88
89                        # Statistic accessors
90                        develAspect, linearAspect = aspectClass.statistic_names
91                        develAspectEq = "#{develAspect}=".intern
92                        linearAspectEq = "#{linearAspect}=".intern
93
94                        self.log.debug "Defining statistic accessors: %s/%s" %
95                                [ develAspect, linearAspect ]
96
97                        define_method( develAspect ) {
98                                self.send(aspect).statistic.developmental
99                        }
100                        define_method( develAspectEq ) {|arg|
101                                self.send(aspect).statistic.developmental = arg
102                        }
103                        define_method( linearAspect ) {
104                                self.send(aspect).statistic.linear
105                        }
106                        define_method( linearAspectEq ) {|arg|
107                                self.send(aspect).statistic.linear = arg
108                        }
109                end
110        end
111       
112
113        #################################################################
114        ###     I N S T A N C E   M E T H O D S
115        #################################################################
116
117        ### Create a new FaerieMUD::Spirit object.
118        def initialize( args={} )
119                @animated_object = nil
120                @parser = FaerieMUD::CommandParser::new
121
122                @io_handlers = {
123                        :input          => nil,
124                        :output         => nil,
125                }
126
127                @aspects = {
128                        :physical => PhysicalAspect::new,
129                        :mental => MentalAspect::new,
130                        :creative => CreativeAspect::new,
131                }
132
133                @input_thread = nil
134
135                super
136        end
137
138
139        ######
140        public
141        ######
142
143        # The Spirit's FaerieMUD::CommandParser object.
144        attr_locked_reader :parser
145
146        # The AnimatedObject controlled by this Spirit.
147        attr_locked_reader :animated_object
148
149        # IO Callbacks
150        attr_reader :io_handlers
151
152        # Pseudo-ComposedObject accessors
153        def_aspect_methods :physical, :mental, :creative
154
155        # The input thread
156        attr_reader :input_thread
157
158       
159        ### Provide a +handler+ for output events.
160        def on_output( &handler )
161                @io_handlers[:output] = handler
162        end
163
164
165        ### Provide a +callback+ which will be called when the spirit is ready for
166        ### input.
167        def on_input_ready( &handler )
168                @io_handlers[:input] = handler
169        end
170
171
172        ### Add the specified +object+ (FaerieMUD::AnimatedObject) to the list
173        ### being controlled by the Spirit.
174        def animate( object )
175                self.writelocked do
176                        object.controller = self
177                        @animated_object = object
178                        self.update_parser
179                end
180        end
181
182
183        ### Remove the specified +object+ (FaerieMUD::AnimatedObject) from the
184        ### list being controlled by the Spirit.
185        def deanimate
186                return if @animated_object.nil?
187
188                self.writelocked do
189                        @animated_object.controller = nil
190                        @animated_object = nil
191                end
192        end
193        alias_method :unanimate, :deanimate
194
195       
196        ### Synchronize the parser's verbset with the set of verbs from the
197        ### currently-animated objects.
198        def update_parser
199                verbs = @animated_object.verbs
200                @parser.update_verb_set( *verbs )
201        end
202
203
204        ### Output the given +message+ to the player controlling the Spirit.
205        def output( message )
206                @io_handlers[:output].call( message )
207        end
208
209
210        ### Start the input loop of the spirit to fetch user input.
211        def start
212                self.log.debug "Starting input thread"
213                @input_thread = Thread::new( &(method(:input_loop)) )
214                self.log.debug "Input thread started: %p" % [@input_thread]
215        end
216
217
218        ### Returns +true+ if the Spirit's input thread is running
219        def running?
220                @input_thread.alive? && @input_thread[:running]
221        end
222
223
224
225        #########
226        protected
227        #########
228
229        ### Wait for input from the Player and pass it to the CommandParser for
230        ### action.
231        def input_loop
232                self.log.debug "  in input loop on %p" % [self]
233                Thread.current.abort_on_exception = true
234
235                Thread.current[:running] = true
236                while Thread.current[:running]
237                        self.log.debug "  calling input iohandler"
238                        rawInput = @io_handlers[:input].call or next
239
240                        self.log.debug "  Got input %p" % [rawInput]
241                        @parser.parse( rawInput )
242                end
243        end
244
245end # class FaerieMUD::Spirit
Note: See TracBrowser for help on using the browser.