Changeset 188

Show
Ignore:
Timestamp:
11/18/04 14:13:19 (4 years ago)
Author:
ged
Message:

Epic4: Events.
-- Added acceptance/epic4.rb
-- Removed redundant constants from the perceptual event classes.
-- Added append operator to Locus.
-- Resurrected type-check functions, as they're proving useful for debugging and

flow-checking.

-- Made Verbs singletons (may have to revisit this later if it turns out that

two or more instances of a Verb should have individual states.

Location:
branches/simplest-thing
Files:
1 added
8 modified

Legend:

Unmodified
Added
Removed
  • branches/simplest-thing/lib/fm/entity.rb

    r179 r188  
    5151class FaerieMUD::Entity < FaerieMUD::GameObject 
    5252    include FaerieMUD::Hookable, 
    53         FaerieMUD::Debuggable 
     53        FaerieMUD::Debuggable, 
     54        FaerieMUD::ArgCheckFunctions 
    5455 
    5556    # SVN Revision 
     
    9798    ### a FaerieMUD::UnhandledEventError is raised. 
    9899    def handleEvents( *events ) 
     100        checkEachType( events, FaerieMUD::Event ) 
    99101        results = [] 
    100102 
     
    111113    ### Get a handler as a callable object for the given +event+. 
    112114    def getHandlerForEvent( event ) 
     115        checkType( event, FaerieMUD::Event ) 
    113116        handler = @eventHandlerCache[event.class] 
    114117 
  • branches/simplest-thing/lib/fm/events/perceptual.rb

    r172 r188  
    3030module FaerieMUD 
    3131 
     32    ################################################################# 
     33    ### B A S E   P E R C E P T U A L   E V E N T   T Y P E S 
     34    ################################################################# 
     35 
    3236    ### The abstract base class for all events which interact with 
    3337    ### FaerieMUD::Perception objects. 
    3438    class PerceptionEvent < FaerieMUD::Event 
    35  
    36         # SVN Revision 
    37         SVNRev = %q$Rev$ 
    38  
    39         # SVN Id 
    40         SVNId = %q$Id$ 
    41  
    42         # SVN URL 
    43         SVNURL = %q$URL$ 
    44  
    45         @properties = [  ] 
    46  
    4739    end # class PerceptionEvent 
    4840 
     
    5042    ### The class for events which represent visual (sight) information. 
    5143    class VisualEvent < FaerieMUD::PerceptionEvent 
    52  
    53         # SVN Revision 
    54         SVNRev = %q$Rev$ 
    55  
    56         # SVN Id 
    57         SVNId = %q$Id$ 
    58  
    59         # SVN URL 
    60         SVNURL = %q$URL$ 
    61  
    6244    end # class VisualPerceptionEvent 
    6345 
     
    6547    ### The class of events which represent olfactory (smell) information. 
    6648    class OlfactoryEvent < FaerieMUD::PerceptionEvent 
    67  
    68         # SVN Revision 
    69         SVNRev = %q$Rev$ 
    70  
    71         # SVN Id 
    72         SVNId = %q$Id$ 
    73  
    74         # SVN URL 
    75         SVNURL = %q$URL$ 
    76  
    7749    end 
    7850 
     
    8052    ### The class of events which represents auditory (heard) information. 
    8153    class AuditoryEvent < FaerieMUD::PerceptionEvent 
    82  
    83         # SVN Revision 
    84         SVNRev = %q$Rev$ 
    85  
    86         # SVN Id 
    87         SVNId = %q$Id$ 
    88  
    89         # SVN URL 
    90         SVNURL = %q$URL$ 
    91  
    9254    end 
    9355 
     
    9557    ### The class of events which represent tactile (felt) information. 
    9658    class TactileEvent < FaerieMUD::PerceptionEvent 
     59    end 
    9760 
    98         # SVN Revision 
    99         SVNRev = %q$Rev$ 
    10061 
    101         # SVN Id 
    102         SVNId = %q$Id$ 
    10362 
    104         # SVN URL 
    105         SVNURL = %q$URL$ 
     63    ################################################################# 
     64    ### S P E C I A L I Z E D   E V E N T   T Y P E S 
     65    ################################################################# 
    10666 
     67    # :TODO: Interesting problem: some advanced FaerieMUD languages are composed 
     68    # of two or more perceptual events together. How to confer that context to 
     69    # the observer? I suspect the events won't be delivered any differently, but 
     70    # interpreters for such languages will be heavily context-driven... 
     71 
     72    ### The class for events which represent spoken language 
     73    class SpeechEvent < FaerieMUD::AuditoryEvent 
     74 
     75        ### Create a new SpeechEvent  
     76        def initialize( args={} ) 
     77            checkForHashArgs( args, :instigator, :language, :words ) 
     78            super 
     79        end 
     80 
     81 
     82        ###### 
     83        public 
     84        ###### 
     85 
     86        # The words that were spoken 
     87        attr_accessor :words 
     88 
     89        # The language the words were spoken in 
     90        attr_accessor :language 
     91 
     92        # The speaker 
     93        alias_method :speaker, :instigator 
     94 
     95 
     96        ### Return the event as an english sentence. 
     97        def to_s 
     98            prelude = super 
     99            return %q{%s, "%s"} % [ prelude, @words ] 
     100        end 
    107101    end 
     102 
    108103 
    109104 
  • branches/simplest-thing/lib/fm/gameobject.rb

    r172 r188  
    3030### An abstract base class for all objects in the FaerieMUD game world. 
    3131class FaerieMUD::GameObject 
    32     include FaerieMUD::Propertied, FaerieMUD::Loggable 
     32    include FaerieMUD::Propertied, 
     33        FaerieMUD::Loggable, 
     34        FaerieMUD::Debuggable, 
     35        FaerieMUD::ArgCheckFunctions 
    3336 
    3437    ### Class constants 
  • branches/simplest-thing/lib/fm/locus.rb

    r172 r188  
    9898# 
    9999 
     100require 'sync' 
     101 
    100102require 'fm/entity' 
    101103require 'fm/mixins' 
     
    164166 
    165167        return objects 
     168    end 
     169 
     170 
     171    ### Append operator -- add the specified ojbect to the contents of the 
     172    ### receiver and return the receiver. 
     173    def <<( object ) 
     174        self.addContents( object ) 
     175        return self 
    166176    end 
    167177 
     
    235245    ### The fallback handler for Loci just immerses any events it receives. 
    236246    def handleAnyEvent( event ) 
     247        checkType( event, FaerieMUD::Event ) 
    237248        self.log.debug( "Fallback handler: Immersing a #{event.class.name}" ) 
    238249        self.immerseEvents( event ) 
     
    243254    ### container/s). 
    244255    def disperseEvents( *events ) 
     256        checkEachType( events, FaerieMUD::Event ) 
    245257        results = [] 
    246258 
     
    265277    ### contents). 
    266278    def immerseEvents( *events ) 
     279        checkEachType( events, FaerieMUD::Event ) 
    267280        results = [] 
    268281 
  • branches/simplest-thing/lib/fm/mixins.rb

    r172 r188  
    5656 
    5757module FaerieMUD 
     58 
     59    ### Mixin that adds some type-checking functions to the current scope 
     60    module ArgCheckFunctions 
     61 
     62        ############### 
     63        module_function 
     64        ############### 
     65 
     66        ### Check <tt>anObject</tt> to make sure it's one of the specified 
     67        ### <tt>validTypes</tt>. If the object is not one of the specified value 
     68        ### types, and an optional block is given it is called with the object being 
     69        ### tested and the array of valid types. If no handler block is given, a 
     70        ### <tt>TypeError</tt> is raised. 
     71        def checkType( anObject, *validTypes ) # :yields: object, *validTypes 
     72            validTypes.flatten! 
     73            validTypes.compact! 
     74 
     75            unless validTypes.empty? 
     76 
     77                ### Compare the object against the array of valid types, and either 
     78                ### yield to the error block if given or generate our own exception 
     79                ### if not. 
     80                unless validTypes.find {|type| anObject.kind_of?( type ) } then 
     81                    typeList = validTypes.collect {|type| type.name}.join(" or ") 
     82 
     83                    if block_given? then 
     84                        yield( anObject, [ *validTypes ].flatten ) 
     85                    else 
     86                        raise TypeError,  
     87                            "Argument must be of type #{typeList}, not a #{anObject.class.name}", 
     88                            caller(1).find_all {|frame| !frame.include?(__FILE__)} 
     89                    end 
     90                end 
     91            else 
     92                if anObject.nil? then 
     93                    if block_given? then 
     94                        yield( anObject, *validTypes ) 
     95                    else 
     96                        raise ArgumentError,  
     97                            "Argument missing.", 
     98                            caller(1).find_all {|frame| !frame.include?(__FILE__)} 
     99                    end 
     100                end 
     101            end 
     102 
     103            return true 
     104        end 
     105 
     106        ### Check each object in the specified <tt>objectArray</tt> with a call 
     107        ### to #checkType with the specified validTypes array. 
     108        def checkEachType( objectArray, *validTypes, &errBlock ) # :yields: object, *validTypes 
     109            raise ScriptError, 
     110                "First argument must be Enumerable" unless 
     111                objectArray.is_a?( Enumerable ) 
     112 
     113            objectArray.each do |anObject| 
     114                if block_given? then 
     115                    checkType anObject, validTypes, &errBlock 
     116                else 
     117                    checkType( anObject, *validTypes ) {|obj, vTypes| 
     118                        typeList = vTypes.collect {|type| type.name}.join(" or ") 
     119                        emsg = "Argument must be of type %p, not a %s" % 
     120                            [ typeList, obj.class.name ] 
     121                        trace = caller(1). 
     122                            find_all {|frame| !frame.include?(__FILE__)} 
     123                        raise TypeError, emsg, trace 
     124                    } 
     125                end 
     126            end 
     127 
     128            return true 
     129        end 
     130 
     131 
     132        ### Check <tt>anObject</tt> for implementations of <tt>requiredMethods</tt>. 
     133        ### If one of the methods is unimplemented, and an optional block is given it 
     134        ### is called with the method that failed the responds_to? test and the object 
     135        ### being checked. If no handler block is given, a <tt>TypeError</tt> is 
     136        ### raised. 
     137        def checkResponse( anObject, *requiredMethods ) # yields method, anObject 
     138            requiredMethods.flatten! 
     139            requiredMethods.compact! 
     140 
     141            if requiredMethods.size > 0 then 
     142                requiredMethods.each do |method| 
     143                    next if anObject.respond_to?( method ) 
     144 
     145                    if block_given? then 
     146                        yield( method, anObject ) 
     147                    else 
     148                        raise TypeError, "Argument '#{anObject.inspect}' does "\ 
     149                            "not answer the '#{method}()' method", 
     150                            caller(1).find_all {|frame| frame.include?(__FILE__)} 
     151                    end 
     152                end 
     153            end 
     154 
     155            return true 
     156        end 
     157 
     158 
     159        ### Check each object of <tt>anArray</tt> for implementations of 
     160        ### <tt>requiredMethods</tt>, calling the optional <tt>errBlock</tt> if 
     161        ### specified, or raising a <tt>TypeError</tt> if one of the methods is 
     162        ### unimplemented. 
     163        def checkEachResponse( anArray, *requiredMethods, &errBlock ) # :yeilds: method, object 
     164            raise ScriptError, 
     165                "First argument must be Enumerable" unless 
     166                anArray.is_a?( Enumerable ) 
     167 
     168            anArray.each do |anObject| 
     169                if block_given? then 
     170                    checkResponse anObject, *requiredMethods, &errBlock 
     171                else 
     172                    checkResponse( anObject, *requiredMethods ) {|method, object| 
     173                        raise TypeError, "Argument '#{anObject.inspect}' does "\ 
     174                            "not answer the '#{method}()' method", 
     175                            caller(1).find_all {|frame| frame.include?(__FILE__)} 
     176                    } 
     177                end 
     178            end 
     179 
     180            return true 
     181        end 
     182 
     183    end # module TypeCheckFunctions 
     184 
    58185 
    59186    ### A mixin that adds logging to its including class. 
     
    847974            func.call( :class => "hash" ) { items } 
    848975        end 
    849     end 
     976    end # module HTML 
    850977 
    851978end # module FaerieMUD 
  • branches/simplest-thing/lib/fm/verb.rb

    r181 r188  
    2121# 
    2222 
     23require 'singleton' 
     24 
    2325require 'fm/mixins' 
    2426require 'fm/exceptions' 
     
    2830### Game verb class  
    2931class FaerieMUD::Verb < FaerieMUD::GameObject 
     32    include Singleton 
    3033 
    3134    # SVN Revision 
     
    4447 
    4548    ### Event interface: "run" the verb, generating the event/s necessary to 
    46     ### express the action in the game world. The given +instigator+ and 
    47     ### +origin+ will be used when building event/s. See FaerieMUD::Event::new 
    48     ### for definitions  
    49     def happen( instigator, origin ) 
     49    ### express the action in the game world. The given +instigator+, +origin+, 
     50    ### and +target+ will be used when building event/s. See 
     51    ### FaerieMUD::Event::new for definitions 
     52    def happen( instigator, origin, target=nil ) 
    5053        raise NotImplementedError, 
    5154            "Required method #happen not implemented in %s" % 
    5255            [self.class.name] 
    5356    end 
    54  
    5557 
    5658end # class FaerieMUD::Verb 
  • branches/simplest-thing/test.rb

    r172 r188  
    5050            FaerieMUD::Logger::global.outputters << 
    5151                FaerieMUD::Logger::Outputter::create( 'file', $stderr, "STDERR" ) 
    52             Arrow::Logger::global.level = :debug 
     52            FaerieMUD::Logger::global.level = :debug 
    5353            $DEBUG = true 
    5454            debugMsg "Turned debugging on globally." 
  • branches/simplest-thing/tests/event.tests.rb

    r180 r188  
    4343        include Complex 
    4444    end 
    45      
     45 
    4646 
    4747    # Set up instances of associated classes for testing 
     
    5656    def teardown 
    5757        super 
    58         @entity = @locus = nil 
     58        @entity = @locus = @verb = nil 
    5959    end 
    6060 
     
    6767    ### Instance test 
    6868    def test_00_Instance 
     69        printTestHeader "Event: Instantiation" 
    6970        rval = derivClass = derivObject = nil 
    7071 
     
    115116    ### Test the generation of applicable handler names 
    116117    def test_10_HandlerNames 
     118        printTestHeader "Event: Handler names" 
    117119        rval = ev = nil 
    118120