gamefic 0.2.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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