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.
- checksums.yaml +4 -4
- data/lib/gamefic/action.rb +87 -56
- data/lib/gamefic/ansi.rb +55 -0
- data/lib/gamefic/character.rb +130 -76
- data/lib/gamefic/command.rb +19 -0
- data/lib/gamefic/core_ext/array.rb +51 -40
- data/lib/gamefic/core_ext/string.rb +4 -0
- data/lib/gamefic/describable.rb +108 -46
- data/lib/gamefic/direction.rb +46 -0
- data/lib/gamefic/director/delegate.rb +91 -0
- data/lib/gamefic/director/order.rb +10 -0
- data/lib/gamefic/director/parser.rb +119 -0
- data/lib/gamefic/director.rb +16 -197
- data/lib/gamefic/engine/cgi.rb +221 -0
- data/lib/gamefic/engine/tty.rb +237 -0
- data/lib/gamefic/engine.rb +88 -67
- data/lib/gamefic/entity.rb +96 -69
- data/lib/gamefic/grammar/conjugator.rb +20 -0
- data/lib/gamefic/grammar/gender.rb +11 -0
- data/lib/gamefic/grammar/person.rb +10 -0
- data/lib/gamefic/grammar/plural.rb +13 -0
- data/lib/gamefic/grammar/pronouns.rb +60 -0
- data/lib/gamefic/grammar/tense.rb +6 -0
- data/lib/gamefic/grammar/verb_set.rb +43 -0
- data/lib/gamefic/grammar/verbs.rb +25 -0
- data/lib/gamefic/grammar/word_adapter.rb +36 -0
- data/lib/gamefic/grammar.rb +13 -0
- data/lib/gamefic/html.rb +53 -0
- data/lib/gamefic/keywords.rb +51 -33
- data/lib/gamefic/node.rb +65 -58
- data/lib/gamefic/plot/article_mount.rb +22 -0
- data/lib/gamefic/plot/command_mount.rb +88 -0
- data/lib/gamefic/plot/entity_mount.rb +45 -0
- data/lib/gamefic/plot/query_mount.rb +9 -0
- data/lib/gamefic/plot/scene_mount.rb +181 -0
- data/lib/gamefic/plot/you_mount.rb +22 -0
- data/lib/gamefic/plot.rb +296 -247
- data/lib/gamefic/query/ambiguous_children.rb +5 -0
- data/lib/gamefic/query/base.rb +265 -0
- data/lib/gamefic/query/children.rb +10 -0
- data/lib/gamefic/query/expression.rb +47 -0
- data/lib/gamefic/query/family.rb +10 -0
- data/lib/gamefic/query/many_children.rb +7 -0
- data/lib/gamefic/query/matches.rb +11 -0
- data/lib/gamefic/query/parent.rb +10 -0
- data/lib/gamefic/query/plural_children.rb +14 -0
- data/lib/gamefic/query/self.rb +10 -0
- data/lib/gamefic/query/siblings.rb +10 -0
- data/lib/gamefic/query/text.rb +43 -0
- data/lib/gamefic/query.rb +19 -203
- data/lib/gamefic/rule.rb +18 -0
- data/lib/gamefic/scene/active.rb +25 -0
- data/lib/gamefic/scene/concluded.rb +22 -0
- data/lib/gamefic/scene/multiplechoice.rb +74 -0
- data/lib/gamefic/scene/paused.rb +26 -0
- data/lib/gamefic/scene/yesorno.rb +43 -0
- data/lib/gamefic/scene.rb +125 -0
- data/lib/gamefic/script/base.rb +33 -0
- data/lib/gamefic/script/file.rb +14 -0
- data/lib/gamefic/script/text.rb +14 -0
- data/lib/gamefic/script.rb +9 -0
- data/lib/gamefic/serialized.rb +24 -0
- data/lib/gamefic/shell.rb +9 -247
- data/lib/gamefic/snapshots.rb +134 -0
- data/lib/gamefic/source/base.rb +12 -0
- data/lib/gamefic/source/file.rb +23 -0
- data/lib/gamefic/source/text.rb +16 -0
- data/lib/gamefic/source.rb +9 -0
- data/lib/gamefic/stage.rb +75 -0
- data/lib/gamefic/syntax.rb +106 -124
- data/lib/gamefic/tester.rb +20 -0
- data/lib/gamefic/version.rb +3 -0
- data/lib/gamefic.rb +18 -12
- metadata +102 -70
- data/lib/gamefic/base.rb +0 -10
- data/lib/gamefic/before.rb +0 -12
- data/lib/gamefic/import/basics/actions/close.rb +0 -16
- data/lib/gamefic/import/basics/actions/commands.rb +0 -3
- data/lib/gamefic/import/basics/actions/drop-in.rb +0 -17
- data/lib/gamefic/import/basics/actions/drop-on.rb +0 -16
- data/lib/gamefic/import/basics/actions/drop.rb +0 -30
- data/lib/gamefic/import/basics/actions/enter.rb +0 -16
- data/lib/gamefic/import/basics/actions/go.rb +0 -35
- data/lib/gamefic/import/basics/actions/inventory.rb +0 -8
- data/lib/gamefic/import/basics/actions/leave.rb +0 -29
- data/lib/gamefic/import/basics/actions/look-in-at.rb +0 -27
- data/lib/gamefic/import/basics/actions/look-under.rb +0 -3
- data/lib/gamefic/import/basics/actions/look.rb +0 -71
- data/lib/gamefic/import/basics/actions/nil.rb +0 -25
- data/lib/gamefic/import/basics/actions/open.rb +0 -23
- data/lib/gamefic/import/basics/actions/quit.rb +0 -3
- data/lib/gamefic/import/basics/actions/take.rb +0 -107
- data/lib/gamefic/import/basics/entities/container.rb +0 -8
- data/lib/gamefic/import/basics/entities/entity.rb +0 -11
- data/lib/gamefic/import/basics/entities/fixture.rb +0 -5
- data/lib/gamefic/import/basics/entities/item.rb +0 -5
- data/lib/gamefic/import/basics/entities/portal.rb +0 -40
- data/lib/gamefic/import/basics/entities/room.rb +0 -30
- data/lib/gamefic/import/basics/entities/scenery.rb +0 -5
- data/lib/gamefic/import/basics/entities/supporter.rb +0 -6
- data/lib/gamefic/import/basics/entities/thing.rb +0 -16
- data/lib/gamefic/import/basics/queries/reachable.rb +0 -38
- data/lib/gamefic/import/basics/queries/room.rb +0 -8
- data/lib/gamefic/import/basics/queries/visible.rb +0 -32
- data/lib/gamefic/import/basics/rules/has-enough-light.rb +0 -14
- data/lib/gamefic/import/basics.old/actions/container.rb +0 -112
- data/lib/gamefic/import/basics.old/actions/inventory.rb +0 -50
- data/lib/gamefic/import/basics.old/actions/look.rb +0 -53
- data/lib/gamefic/import/basics.old/actions/meta.rb +0 -6
- data/lib/gamefic/import/basics.old/actions/traversal.rb +0 -35
- data/lib/gamefic/import/basics.old/actions.rb +0 -1
- data/lib/gamefic/import/basics.old/entities/container.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/fixture.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/item.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/portal.rb +0 -43
- data/lib/gamefic/import/basics.old/entities/room.rb +0 -27
- data/lib/gamefic/import/basics.old/entities/scenery.rb +0 -3
- data/lib/gamefic/import/basics.old/entities/supporter.rb +0 -3
- data/lib/gamefic/import/basics.old/entities.rb +0 -1
- data/lib/gamefic/import/basics.old/room_modes.rb +0 -48
- data/lib/gamefic/import/basics.rb +0 -6
- data/lib/gamefic/import/room_modes.rb +0 -48
- data/lib/gamefic/import/standard.rb +0 -1
- data/lib/gamefic/meta.rb +0 -12
- data/lib/gamefic/optionset.rb +0 -114
- data/lib/gamefic/requirement.rb +0 -14
- data/lib/gamefic/story.rb +0 -14
- data/lib/gamefic/thing.rb +0 -7
data/lib/gamefic/describable.rb
CHANGED
|
@@ -1,40 +1,76 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'gamefic/keywords'
|
|
2
|
+
require 'gamefic/grammar'
|
|
2
3
|
|
|
3
4
|
module Gamefic
|
|
4
5
|
|
|
5
|
-
|
|
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
|
-
|
|
11
|
+
attr_accessor :synonyms, :indefinite_article
|
|
8
12
|
attr_writer :definite_article
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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 =
|
|
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
|
-
|
|
46
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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,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
|
data/lib/gamefic/director.rb
CHANGED
|
@@ -1,204 +1,23 @@
|
|
|
1
1
|
module Gamefic
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
10
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|