gamefic 0.2.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gamefic/action.rb +87 -56
  3. data/lib/gamefic/ansi.rb +55 -0
  4. data/lib/gamefic/character.rb +130 -76
  5. data/lib/gamefic/command.rb +19 -0
  6. data/lib/gamefic/core_ext/array.rb +51 -40
  7. data/lib/gamefic/core_ext/string.rb +4 -0
  8. data/lib/gamefic/describable.rb +108 -46
  9. data/lib/gamefic/direction.rb +46 -0
  10. data/lib/gamefic/director/delegate.rb +91 -0
  11. data/lib/gamefic/director/order.rb +10 -0
  12. data/lib/gamefic/director/parser.rb +119 -0
  13. data/lib/gamefic/director.rb +16 -197
  14. data/lib/gamefic/engine/cgi.rb +221 -0
  15. data/lib/gamefic/engine/tty.rb +237 -0
  16. data/lib/gamefic/engine.rb +88 -67
  17. data/lib/gamefic/entity.rb +96 -69
  18. data/lib/gamefic/grammar/conjugator.rb +20 -0
  19. data/lib/gamefic/grammar/gender.rb +11 -0
  20. data/lib/gamefic/grammar/person.rb +10 -0
  21. data/lib/gamefic/grammar/plural.rb +13 -0
  22. data/lib/gamefic/grammar/pronouns.rb +60 -0
  23. data/lib/gamefic/grammar/tense.rb +6 -0
  24. data/lib/gamefic/grammar/verb_set.rb +43 -0
  25. data/lib/gamefic/grammar/verbs.rb +25 -0
  26. data/lib/gamefic/grammar/word_adapter.rb +36 -0
  27. data/lib/gamefic/grammar.rb +13 -0
  28. data/lib/gamefic/html.rb +53 -0
  29. data/lib/gamefic/keywords.rb +51 -33
  30. data/lib/gamefic/node.rb +65 -58
  31. data/lib/gamefic/plot/article_mount.rb +22 -0
  32. data/lib/gamefic/plot/command_mount.rb +88 -0
  33. data/lib/gamefic/plot/entity_mount.rb +45 -0
  34. data/lib/gamefic/plot/query_mount.rb +9 -0
  35. data/lib/gamefic/plot/scene_mount.rb +181 -0
  36. data/lib/gamefic/plot/you_mount.rb +22 -0
  37. data/lib/gamefic/plot.rb +296 -247
  38. data/lib/gamefic/query/ambiguous_children.rb +5 -0
  39. data/lib/gamefic/query/base.rb +265 -0
  40. data/lib/gamefic/query/children.rb +10 -0
  41. data/lib/gamefic/query/expression.rb +47 -0
  42. data/lib/gamefic/query/family.rb +10 -0
  43. data/lib/gamefic/query/many_children.rb +7 -0
  44. data/lib/gamefic/query/matches.rb +11 -0
  45. data/lib/gamefic/query/parent.rb +10 -0
  46. data/lib/gamefic/query/plural_children.rb +14 -0
  47. data/lib/gamefic/query/self.rb +10 -0
  48. data/lib/gamefic/query/siblings.rb +10 -0
  49. data/lib/gamefic/query/text.rb +43 -0
  50. data/lib/gamefic/query.rb +19 -203
  51. data/lib/gamefic/rule.rb +18 -0
  52. data/lib/gamefic/scene/active.rb +25 -0
  53. data/lib/gamefic/scene/concluded.rb +22 -0
  54. data/lib/gamefic/scene/multiplechoice.rb +74 -0
  55. data/lib/gamefic/scene/paused.rb +26 -0
  56. data/lib/gamefic/scene/yesorno.rb +43 -0
  57. data/lib/gamefic/scene.rb +125 -0
  58. data/lib/gamefic/script/base.rb +33 -0
  59. data/lib/gamefic/script/file.rb +14 -0
  60. data/lib/gamefic/script/text.rb +14 -0
  61. data/lib/gamefic/script.rb +9 -0
  62. data/lib/gamefic/serialized.rb +24 -0
  63. data/lib/gamefic/shell.rb +9 -247
  64. data/lib/gamefic/snapshots.rb +134 -0
  65. data/lib/gamefic/source/base.rb +12 -0
  66. data/lib/gamefic/source/file.rb +23 -0
  67. data/lib/gamefic/source/text.rb +16 -0
  68. data/lib/gamefic/source.rb +9 -0
  69. data/lib/gamefic/stage.rb +75 -0
  70. data/lib/gamefic/syntax.rb +106 -124
  71. data/lib/gamefic/tester.rb +20 -0
  72. data/lib/gamefic/version.rb +3 -0
  73. data/lib/gamefic.rb +18 -12
  74. metadata +102 -70
  75. data/lib/gamefic/base.rb +0 -10
  76. data/lib/gamefic/before.rb +0 -12
  77. data/lib/gamefic/import/basics/actions/close.rb +0 -16
  78. data/lib/gamefic/import/basics/actions/commands.rb +0 -3
  79. data/lib/gamefic/import/basics/actions/drop-in.rb +0 -17
  80. data/lib/gamefic/import/basics/actions/drop-on.rb +0 -16
  81. data/lib/gamefic/import/basics/actions/drop.rb +0 -30
  82. data/lib/gamefic/import/basics/actions/enter.rb +0 -16
  83. data/lib/gamefic/import/basics/actions/go.rb +0 -35
  84. data/lib/gamefic/import/basics/actions/inventory.rb +0 -8
  85. data/lib/gamefic/import/basics/actions/leave.rb +0 -29
  86. data/lib/gamefic/import/basics/actions/look-in-at.rb +0 -27
  87. data/lib/gamefic/import/basics/actions/look-under.rb +0 -3
  88. data/lib/gamefic/import/basics/actions/look.rb +0 -71
  89. data/lib/gamefic/import/basics/actions/nil.rb +0 -25
  90. data/lib/gamefic/import/basics/actions/open.rb +0 -23
  91. data/lib/gamefic/import/basics/actions/quit.rb +0 -3
  92. data/lib/gamefic/import/basics/actions/take.rb +0 -107
  93. data/lib/gamefic/import/basics/entities/container.rb +0 -8
  94. data/lib/gamefic/import/basics/entities/entity.rb +0 -11
  95. data/lib/gamefic/import/basics/entities/fixture.rb +0 -5
  96. data/lib/gamefic/import/basics/entities/item.rb +0 -5
  97. data/lib/gamefic/import/basics/entities/portal.rb +0 -40
  98. data/lib/gamefic/import/basics/entities/room.rb +0 -30
  99. data/lib/gamefic/import/basics/entities/scenery.rb +0 -5
  100. data/lib/gamefic/import/basics/entities/supporter.rb +0 -6
  101. data/lib/gamefic/import/basics/entities/thing.rb +0 -16
  102. data/lib/gamefic/import/basics/queries/reachable.rb +0 -38
  103. data/lib/gamefic/import/basics/queries/room.rb +0 -8
  104. data/lib/gamefic/import/basics/queries/visible.rb +0 -32
  105. data/lib/gamefic/import/basics/rules/has-enough-light.rb +0 -14
  106. data/lib/gamefic/import/basics.old/actions/container.rb +0 -112
  107. data/lib/gamefic/import/basics.old/actions/inventory.rb +0 -50
  108. data/lib/gamefic/import/basics.old/actions/look.rb +0 -53
  109. data/lib/gamefic/import/basics.old/actions/meta.rb +0 -6
  110. data/lib/gamefic/import/basics.old/actions/traversal.rb +0 -35
  111. data/lib/gamefic/import/basics.old/actions.rb +0 -1
  112. data/lib/gamefic/import/basics.old/entities/container.rb +0 -3
  113. data/lib/gamefic/import/basics.old/entities/fixture.rb +0 -3
  114. data/lib/gamefic/import/basics.old/entities/item.rb +0 -3
  115. data/lib/gamefic/import/basics.old/entities/portal.rb +0 -43
  116. data/lib/gamefic/import/basics.old/entities/room.rb +0 -27
  117. data/lib/gamefic/import/basics.old/entities/scenery.rb +0 -3
  118. data/lib/gamefic/import/basics.old/entities/supporter.rb +0 -3
  119. data/lib/gamefic/import/basics.old/entities.rb +0 -1
  120. data/lib/gamefic/import/basics.old/room_modes.rb +0 -48
  121. data/lib/gamefic/import/basics.rb +0 -6
  122. data/lib/gamefic/import/room_modes.rb +0 -48
  123. data/lib/gamefic/import/standard.rb +0 -1
  124. data/lib/gamefic/meta.rb +0 -12
  125. data/lib/gamefic/optionset.rb +0 -114
  126. data/lib/gamefic/requirement.rb +0 -14
  127. data/lib/gamefic/story.rb +0 -14
  128. data/lib/gamefic/thing.rb +0 -7
@@ -1,40 +1,76 @@
1
- require "gamefic/keywords"
1
+ require 'gamefic/keywords'
2
+ require 'gamefic/grammar'
2
3
 
3
4
  module Gamefic
4
5
 
5
- module Describable
6
+ # Add a variety of text properties for naming, describing, and properly
7
+ # referencing objects.
8
+ module Describable
9
+ include Grammar::Person, Grammar::Plural
6
10
  attr_reader :name
7
- attr_accessor :synonyms, :indefinite_article
11
+ attr_accessor :synonyms, :indefinite_article
8
12
  attr_writer :definite_article
9
- def keywords
10
- Keywords.new "#{name} #{synonyms}"
11
- end
12
- def keywords=(value)
13
- @keywords = value
14
- end
13
+
14
+ # Get a set of Keywords associated with the object.
15
+ # Keywords are typically the words in the object's name plus its synonyms.
16
+ #
17
+ # @return [Keywords]
18
+ def keywords
19
+ Keywords.new "#{name} #{synonyms}"
20
+ end
21
+
22
+ # Get the name of the object with an indefinite article.
23
+ # Note: proper-named objects never append an article, though an article
24
+ # may be included in its proper name.
25
+ #
26
+ # @return [String]
15
27
  def indefinitely
16
- (proper_named? ? '' : "#{indefinite_article} ") + name
28
+ ((proper_named? or indefinite_article == '') ? '' : "#{indefinite_article} ") + name
17
29
  end
30
+
31
+ # Get the name of the object with a definite article.
32
+ # Note: proper-named objects never append an article, though an article
33
+ # may be included in its proper name.
18
34
  def definitely
19
- (proper_named? ? '' : "#{definite_article} ") + name
35
+ ((proper_named? or definite_article == '') ? '' : "#{definite_article} ") + name
20
36
  end
37
+
38
+ # Get the definite article for this object.
39
+ #
40
+ # @return [String]
21
41
  def definite_article
22
42
  @definite_article || "the"
23
43
  end
44
+
45
+ # Is the object proper-named?
46
+ # Proper-named objects typically do not add articles to their names when
47
+ # referenced #definitely or #indefinitely, e.g., "Jane Doe" instead of
48
+ # "a Jane Doe" or "the Jane Doe."
49
+ #
50
+ # @return [Boolean]
24
51
  def proper_named?
25
52
  (@proper_named == true)
26
53
  end
27
- def proper_named=(value)
28
- if value == true
54
+
55
+ # Set whether the object has a proper name.
56
+ #
57
+ # @param [Boolean]
58
+ def proper_named=(bool)
59
+ if bool == true
29
60
  if @definite_article != nil
30
61
  @name = "#{@definite_article} #{@name}"
31
62
  @definite_article = nil
32
63
  end
33
64
  end
34
- @proper_named = value
65
+ @proper_named = bool
35
66
  end
67
+
68
+ # Set the name of the object.
69
+ # Setting the name performs some magic to determine how to handle
70
+ # articles ("an object" and "the object").
71
+ #
72
+ # @param value [String]
36
73
  def name=(value)
37
- # TODO: Split article from name
38
74
  words = value.split_words
39
75
  if ['a','an'].include?(words[0].downcase)
40
76
  @indefinite_article = words[0].downcase
@@ -42,8 +78,12 @@ module Gamefic
42
78
  value = value[words[0].length+1..-1].strip
43
79
  else
44
80
  if words[0].downcase == 'the'
45
- @definite_article = 'the'
46
- value = value[4..-1].strip
81
+ if proper_named?
82
+ @definite_article = nil
83
+ else
84
+ @definite_article = 'the'
85
+ value = value[4..-1].strip
86
+ end
47
87
  end
48
88
  # Try to guess the indefinite article
49
89
  if ['a','e','i','o','u'].include?(value[0,1].downcase)
@@ -54,34 +94,56 @@ module Gamefic
54
94
  end
55
95
  @name = value
56
96
  end
57
- def description
58
- @description.to_s != '' ? @description : "Nothing special."
59
- end
60
- def description=(value)
61
- @description = value
62
- end
63
- def to_s
64
- indefinitely
65
- end
66
- end
67
-
68
- def self.a(entity)
69
- entity.indefinitely
70
- end
71
- def self.an(entity)
72
- entity.indefinitely
73
- end
74
- def self.the(entity)
75
- entity.definitely
76
- end
77
- def self.A(entity)
78
- entity.indefinitely.cap_first
79
- end
80
- def self.An(entity)
81
- entity.indefinitely.cap_first
82
- end
83
- def self.The(entity)
84
- entity.definitely.cap_first
97
+
98
+ # Does the object have a description?
99
+ #
100
+ # @return [Boolean]
101
+ def has_description?
102
+ (@description.to_s != '')
103
+ end
104
+
105
+ # Get the object's description.
106
+ #
107
+ # @return [String]
108
+ def description
109
+ @description || (Describable.default_description % { :name => self.definitely, :Name => self.definitely.capitalize_first })
110
+ end
111
+
112
+ # Set the object's description.
113
+ #
114
+ # @param text [String]
115
+ def description=(text)
116
+ if text != (Describable.default_description % { :name => self.definitely, :Name => self.definitely.capitalize_first })
117
+ @description = text
118
+ else
119
+ @description = nil
120
+ end
121
+ end
122
+
123
+ # Set the object's default description.
124
+ # The default description is typically set in an object's initialization
125
+ # to ensure that a non-empty string is available when a instance-specific
126
+ # description is not provided
127
+ #
128
+ # @param text [String]
129
+ def self.default_description=(text)
130
+ @default_description = text
131
+ end
132
+
133
+ # Get the object's default description.
134
+ #
135
+ # @return [String]
136
+ def self.default_description
137
+ @default_description || "There's nothing special about %{name}."
138
+ end
139
+
140
+ # Get a String representation of the object. By default, this is the
141
+ # object's name with an indefinite article, e.g., "a person" or "a red
142
+ # dog."
143
+ #
144
+ # @return [String]
145
+ def to_s
146
+ indefinitely
147
+ end
85
148
  end
86
-
87
149
  end
@@ -0,0 +1,46 @@
1
+ module Gamefic
2
+
3
+ class Direction
4
+ attr_accessor :name, :adjective, :adverb, :reverse
5
+ def initialize args = {}
6
+ args.each { |key, value|
7
+ send "#{key}=", value
8
+ }
9
+ if !reverse.nil?
10
+ reverse.reverse = self
11
+ end
12
+ proper_named = true
13
+ end
14
+ def adjective
15
+ @adjective || @name
16
+ end
17
+ def adverb
18
+ @adverb || @name
19
+ end
20
+ def reverse=(dir)
21
+ @reverse = dir
22
+ end
23
+ def synonyms
24
+ "#{adjective} #{adverb}"
25
+ end
26
+ def to_s
27
+ @name
28
+ end
29
+ def self.find(str)
30
+ x = { "north" => NORTH, "south" => SOUTH, "west" => WEST, "east" => EAST, "up" => UP, "down" => DOWN, "northwest" => NORTHWEST, "northeast" => NORTHEAST, "southwest" => SOUTHWEST, "southeast" => SOUTHEAST }
31
+ x[str]
32
+ end
33
+ end
34
+
35
+ NORTH = Direction.new(:name => 'north', :adjective => 'northern')
36
+ SOUTH = Direction.new(:name => 'south', :adjective => 'southern', :reverse => NORTH)
37
+ WEST = Direction.new(:name => 'west', :adjective => 'western')
38
+ EAST = Direction.new(:name => 'east', :adjective => 'eastern', :reverse => WEST)
39
+ NORTHWEST = Direction.new(:name => 'northwest', :adjective => 'northwestern')
40
+ SOUTHEAST = Direction.new(:name => 'southeast', :adjective => 'southeastern', :reverse => NORTHWEST)
41
+ NORTHEAST = Direction.new(:name => 'northeast', :adjective => 'northeastern')
42
+ SOUTHWEST = Direction.new(:name => 'southwest', :adjective => 'southwestern', :reverse => NORTHEAST)
43
+ UP = Direction.new(:name => 'up', :adjective => 'upwards')
44
+ DOWN = Direction.new(:name => 'down', :adjective => 'downwards', :reverse => UP)
45
+
46
+ end
@@ -0,0 +1,91 @@
1
+ module Gamefic
2
+ module Director
3
+ class Delegate
4
+ # If we use Query::Base.new in the @disambiguator declaration, Opal
5
+ # passes the block to the query instead of the action.
6
+ base = Query::Base.new
7
+ @@disambiguator = Action.new nil, nil, base do |actor, entities|
8
+ definites = []
9
+ entities.each { |entity|
10
+ definites.push entity.definitely
11
+ }
12
+ actor.tell "I don't know which you mean: #{definites.join_or}."
13
+ end
14
+ @@disambiguator.meta = true
15
+ def initialize(actor, orders)
16
+ @actor = actor
17
+ @orders = orders
18
+ end
19
+ def proceed
20
+ return if @orders.length == 0
21
+ @actor.send(:delegate_stack).push self
22
+ executed = false
23
+ while !executed
24
+ order = @orders.shift
25
+ break if order.nil?
26
+ arg_i = 0
27
+ final_arguments = []
28
+ order.arguments.each { |argument|
29
+ if argument.length > 1 and !order.action.queries[arg_i].allow_many?
30
+ if argument[0].kind_of?(Array)
31
+ # This thing refers to multiple items. Just keep going.
32
+ final_arguments = nil
33
+ break
34
+ else
35
+ ambiguous = argument
36
+ end
37
+ order = Order.new(@actor, @@disambiguator, [])
38
+ final_arguments = [ambiguous]
39
+ break
40
+ end
41
+ valid = []
42
+ if order.action.queries[arg_i].allow_ambiguous?
43
+ valid = argument.flatten
44
+ else
45
+ argument.each { |m|
46
+ if order.action.queries[arg_i].validate(@actor, m)
47
+ valid.push m
48
+ else
49
+ final_arguments = nil
50
+ break
51
+ end
52
+ }
53
+ end
54
+ if order.action == @@disambiguator or final_arguments.nil?
55
+ break
56
+ end
57
+ if order.action.queries[arg_i].allow_many?
58
+ final_arguments.push valid.flatten
59
+ elsif valid.length == 1
60
+ final_arguments.push valid[0]
61
+ else
62
+ final_arguments = nil
63
+ break
64
+ end
65
+ arg_i += 1
66
+ }
67
+ if !final_arguments.nil?
68
+ # The actor is always the first argument to an Action proc
69
+ final_arguments.unshift @actor
70
+ order.action.execute(*final_arguments)
71
+ executed = true
72
+ end
73
+ end
74
+ @actor.send(:delegate_stack).pop
75
+ end
76
+ def execute
77
+ return if @orders.length == 0
78
+ if !@orders[0].action.meta?
79
+ @actor.plot.asserts.each_pair { |name, rule|
80
+ result = rule.test(@actor, @orders[0].action.verb, @orders[0].arguments)
81
+ if result == false
82
+ return
83
+ end
84
+ }
85
+ end
86
+ proceed
87
+ end
88
+ private
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,10 @@
1
+ module Gamefic
2
+ class Director::Order
3
+ attr_reader :actor, :action, :arguments
4
+ def initialize(actor, action, arguments)
5
+ @actor = actor
6
+ @action = action
7
+ @arguments = arguments
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,119 @@
1
+ require 'gamefic/director/order'
2
+
3
+ module Gamefic
4
+ module Director
5
+
6
+ module Parser
7
+ def self.from_tokens(actor, tokens)
8
+ options = []
9
+ command = tokens.shift
10
+ actions = actor.plot.actions_with_verb(command.to_sym)
11
+ actions.each { |action|
12
+ if action.queries.length == tokens.length
13
+ valid = true
14
+ index = 0
15
+ action.queries.each { |query|
16
+ if query.validate(actor, tokens[index]) == false
17
+ valid = false
18
+ break
19
+ end
20
+ index += 1
21
+ }
22
+ if valid
23
+ arguments = []
24
+ tokens.each { |token|
25
+ arguments.push [token]
26
+ }
27
+ options.push Order.new(actor, action, arguments)
28
+ end
29
+ end
30
+ }
31
+ if options.length == 0
32
+ tokens.unshift command
33
+ end
34
+ options
35
+ end
36
+ def self.from_string(actor, command)
37
+ options = []
38
+ if command.to_s == ''
39
+ return options
40
+ end
41
+ matches = Syntax.tokenize(command, actor.plot.syntaxes)
42
+ matches.each { |match|
43
+ actions = actor.plot.actions_with_verb(match.verb)
44
+ actions.each { |action|
45
+ options.concat bind_contexts_in_result(actor, match.arguments, action)
46
+ }
47
+ }
48
+ options
49
+ end
50
+ class << self
51
+ private
52
+ def bind_contexts_in_result(actor, handler, action)
53
+ queries = action.queries.clone
54
+ objects = execute_query(actor, handler.clone, queries, action)
55
+ num_nil = 0
56
+ while objects.length == 0 and queries.last.optional?
57
+ num_nil +=1
58
+ queries.pop
59
+ objects = execute_query(actor, handler.clone, queries, action, num_nil)
60
+ end
61
+ return objects
62
+ end
63
+ def execute_query(actor, arguments, queries, action, num_nil = 0)
64
+ # If the action verb is nil, treat the first argument as a query
65
+ arguments.shift unless action.verb.nil?
66
+ prepared = Array.new
67
+ objects = Array.new
68
+ valid = true
69
+ last_remainder = nil
70
+ queries.clone.each { |context|
71
+ arg = arguments.shift.to_s
72
+ if last_remainder.to_s > ''
73
+ arg = (last_remainder + " " + arg).strip
74
+ end
75
+ if arg.nil? or arg == ''
76
+ valid = false
77
+ next
78
+ end
79
+ if context == String
80
+ prepared.push [arg]
81
+ elsif context.kind_of?(Query::Base)
82
+ result = context.execute(actor, arg)
83
+ if result.objects.length == 0
84
+ valid = false
85
+ next
86
+ else
87
+ prepared.push result.objects
88
+ last_remainder = result.remainder
89
+ end
90
+ else
91
+ raise TypeError.new("Action parameters must inherit from Query::Base")
92
+ end
93
+ }
94
+ if valid == true
95
+ if last_remainder.nil? or last_remainder.empty?
96
+ prepared.each { |p|
97
+ p.uniq!
98
+ }
99
+ num_nil.times do
100
+ prepared.push [nil]
101
+ end
102
+ objects.push Order.new(actor, action, prepared)
103
+ else
104
+ if !action.queries.last.allow_many? or action.queries.last.allow_ambiguous?
105
+ misunderstood = Action.new nil, nil, Query::Text.new do |actor, text|
106
+ actor.tell "I understand the first part of your command but not \"#{text}.\""
107
+ end
108
+ misunderstood.meta = true
109
+ objects.push Order.new(actor, misunderstood, [[last_remainder]])
110
+ end
111
+ end
112
+ end
113
+ objects
114
+ end
115
+ end
116
+ end
117
+
118
+ end
119
+ end
@@ -1,204 +1,23 @@
1
1
  module Gamefic
2
2
 
3
- class Director
4
- def self.dispatch(actor, command)
5
- command.strip!
6
- if command.to_s == ''
7
- return
3
+ module Director
4
+ autoload :Parser, 'gamefic/director/parser'
5
+ autoload :Delegate, 'gamefic/director/delegate'
6
+ autoload :Order, 'gamefic/director/order'
7
+
8
+ def self.dispatch(actor, *args)
9
+ orders = []
10
+ if args.length > 1
11
+ orders = Parser.from_tokens(actor, args)
8
12
  end
9
- begin
10
- handlers = Syntax.match(command, actor.plot)
11
- rescue Exception => e
12
- puts "#{e}"
13
- return
13
+ if orders.length == 0
14
+ orders = Parser.from_string(actor, args.join(' ').strip)
14
15
  end
15
- befores = Array.new
16
- options = Array.new
17
- handlers.each { |handler|
18
- actions = actor.plot.commands[handler.command]
19
- if actions != nil
20
- actions.each { |action|
21
- if action.queries.length == 0 and handler.arguments.length > 0
22
- next
23
- end
24
- orders = bind_contexts_in_result(actor, handler, action)
25
- orders.each { |order|
26
- args = Array.new
27
- args.push actor
28
- order.arguments.each { |a|
29
- if a.length > 1
30
- longnames = Array.new
31
- a.each { |b|
32
- longnames.push "#{b.definitely}"
33
- }
34
- actor.tell "I don't know which you mean: #{longnames.join_and(', ', ' or ')}."
35
- return
36
- end
37
- args.push a[0]
38
- }
39
- if order.action.kind_of?(Before)
40
- befores.push [order.action, args]
41
- else
42
- options.push [order.action, args]
43
- end
44
- }
45
- }
46
- end
47
- }
48
- del = Delegate.new(actor, befores, options)
49
- del.execute
50
- end
51
- private
52
- def self.bind_contexts_in_result(actor, handler, action)
53
- queries = action.queries.clone
54
- objects = self.execute_query(actor, handler.arguments.clone, queries, action)
55
- num_nil = 0
56
- while objects.length == 0 and queries.last.optional?
57
- num_nil +=1
58
- queries.pop
59
- objects = self.execute_query(actor, handler.arguments.clone, queries, action, num_nil)
60
- end
61
- return objects
62
- end
63
- def self.execute_query(actor, arguments, queries, action, num_nil = 0)
64
- prepared = Array.new
65
- objects = Array.new
66
- valid = true
67
- queries.clone.each { |context|
68
- arg = arguments.shift
69
- if arg == nil or arg == ''
70
- valid = false
71
- next
72
- end
73
- if context == String
74
- prepared.push [arg]
75
- elsif context.kind_of?(Query::Base)
76
- if arg == 'it' and actor.object_of_pronoun != nil
77
- result = context.execute(actor, "#{actor.object_of_pronoun.name}")
78
- else
79
- result = context.execute(actor, arg)
80
- end
81
- if result.objects.length == 0
82
- valid = false
83
- next
84
- else
85
- prepared.push result.objects
86
- if result.remainder
87
- arguments.push result.remainder
88
- end
89
- end
90
- else
91
- # TODO: Better message
92
- raise "Invalid object"
93
- end
94
- }
95
- if valid == true
96
- prepared.each { |p|
97
- p.uniq!
98
- }
99
- num_nil.times do
100
- prepared.push [nil]
101
- end
102
- objects.push Order.new(action, prepared)
103
- end
104
- objects
16
+ first_order = orders[0]
17
+ del = Delegate.new(actor, orders)
18
+ del.execute
19
+ first_order
105
20
  end
106
- end
107
-
108
- class Director
109
- class Delegate
110
- @@assertion_stack = Array.new
111
- @@delegation_stack = Array.new
112
- def initialize(actor, befores, actions)
113
- @actor = actor
114
- @befores = befores
115
- @actions = actions
116
- end
117
- def execute
118
- @@assertion_stack.push Hash.new
119
- @@delegation_stack.push @befores
120
- handle @befores
121
- @@delegation_stack.pop
122
- if @@assertion_stack.last[:everything] == false
123
- @@assertion_stack.pop
124
- return
125
- end
126
- result = true
127
- @@delegation_stack.push @actions
128
- # Nil commands pass assertions to facilitate error messages.
129
- if @@assertion_stack.last[:everything] != true and Director::Delegate.next_command != nil
130
- @actor.plot.rules.each { |k, v|
131
- if @@assertion_stack.last[k] == true
132
- next
133
- elsif @@assertion_stack.last[k] == false
134
- result = false
135
- break
136
- end
137
- result = v.test(@actor)
138
- if result == false
139
- break
140
- end
141
- result = true
142
- }
143
- end
144
- @@assertion_stack.pop
145
- if result == true
146
- handle @actions
147
- end
148
- @@delegation_stack.pop
149
- end
150
- def handle options
151
- if options.length > 0
152
- opt = options.shift
153
- if opt[1].length == 1
154
- opt[0].execute(opt[1][0])
155
- opt[1][0].object_of_pronoun = nil
156
- else
157
- if opt[1].length == 2 and opt[1][1].kind_of?(Entity) and opt[1][0].parent != opt[1][1]
158
- opt[1][0].object_of_pronoun = opt[1][1]
159
- elsif opt[1][0].parent == opt[1][1]
160
- opt[1][0].object_of_pronoun = nil
161
- end
162
- opt[0].execute(opt[1])
163
- end
164
- end
165
- end
166
- def self.pass requirement
167
- @@assertion_stack.last[requirement] = true
168
- end
169
- def self.deny requirement
170
- @@assertion_stack.last[requirement] = false
171
- end
172
- def self.next_command
173
- return nil if @@delegation_stack.last.nil? or @@delegation_stack.last[0].length == 0
174
- return @@delegation_stack.last[0][0].command
175
- end
176
- def self.passthru
177
- if @@delegation_stack.last != nil
178
- if @@delegation_stack.last.length > 0
179
- opt = @@delegation_stack.last.shift
180
- if opt[1].length == 1
181
- opt[0].execute(opt[1][0])
182
- opt[1][0].object_of_pronoun = nil
183
- else
184
- if opt[1].length == 2 and opt[1][1].kind_of?(Entity) and opt[1][0].parent != opt[1][1]
185
- opt[1][0].object_of_pronoun = opt[1][1]
186
- elsif opt[1][0].parent == opt[1][1]
187
- opt[1][0].object_of_pronoun = nil
188
- end
189
- opt[0].execute(opt[1])
190
- end
191
- end
192
- end
193
- end
194
- end
195
- class Order
196
- attr_reader :action, :arguments
197
- def initialize(action, arguments)
198
- @action = action
199
- @arguments = arguments
200
- end
201
- end
202
- end
21
+ end
203
22
 
204
23
  end