gamefic 1.7.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +16 -0
- data/.solargraph.yml +5 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +10 -0
- data/gamefic.gemspec +27 -0
- data/lib/gamefic.rb +7 -7
- data/lib/gamefic/action.rb +66 -60
- data/lib/gamefic/active.rb +331 -280
- data/lib/gamefic/actor.rb +8 -5
- data/lib/gamefic/command.rb +9 -7
- data/lib/gamefic/core_ext/array.rb +27 -49
- data/lib/gamefic/core_ext/string.rb +25 -16
- data/lib/gamefic/describable.rb +21 -23
- data/lib/gamefic/element.rb +47 -31
- data/lib/gamefic/entity.rb +6 -12
- data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
- data/lib/gamefic/messaging.rb +43 -44
- data/lib/gamefic/node.rb +14 -5
- data/lib/gamefic/plot.rb +69 -91
- data/lib/gamefic/plot/darkroom.rb +80 -264
- data/lib/gamefic/plot/host.rb +42 -48
- data/lib/gamefic/plot/snapshot.rb +14 -19
- data/lib/gamefic/query.rb +15 -18
- data/lib/gamefic/query/base.rb +50 -37
- data/lib/gamefic/query/children.rb +0 -0
- data/lib/gamefic/query/descendants.rb +2 -2
- data/lib/gamefic/query/external.rb +18 -14
- data/lib/gamefic/query/family.rb +3 -7
- data/lib/gamefic/query/matches.rb +75 -67
- data/lib/gamefic/query/parent.rb +0 -0
- data/lib/gamefic/query/siblings.rb +0 -0
- data/lib/gamefic/query/text.rb +12 -12
- data/lib/gamefic/query/tree.rb +17 -0
- data/lib/gamefic/scene.rb +0 -2
- data/lib/gamefic/scene/activity.rb +24 -26
- data/lib/gamefic/scene/base.rb +71 -10
- data/lib/gamefic/scene/conclusion.rb +1 -3
- data/lib/gamefic/scene/multiple_choice.rb +19 -14
- data/lib/gamefic/scene/multiple_scene.rb +29 -20
- data/lib/gamefic/scene/pause.rb +8 -3
- data/lib/gamefic/scene/yes_or_no.rb +22 -10
- data/lib/gamefic/scriptable.rb +88 -0
- data/lib/gamefic/serialize.rb +223 -0
- data/lib/gamefic/subplot.rb +38 -35
- data/lib/gamefic/syntax.rb +15 -13
- data/lib/gamefic/version.rb +3 -3
- data/lib/gamefic/world.rb +18 -0
- data/lib/gamefic/world/callbacks.rb +135 -0
- data/lib/gamefic/world/commands.rb +184 -0
- data/lib/gamefic/{plot → world}/entities.rb +33 -35
- data/lib/gamefic/{plot → world}/playbook.rb +245 -240
- data/lib/gamefic/world/players.rb +37 -0
- data/lib/gamefic/world/scenes.rb +226 -0
- metadata +37 -88
- data/bin/gamefic +0 -9
- data/lib/gamefic/engine.rb +0 -7
- data/lib/gamefic/engine/base.rb +0 -59
- data/lib/gamefic/engine/tty.rb +0 -24
- data/lib/gamefic/grammar.rb +0 -13
- data/lib/gamefic/grammar/conjugator.rb +0 -20
- data/lib/gamefic/grammar/gender.rb +0 -11
- data/lib/gamefic/grammar/person.rb +0 -10
- data/lib/gamefic/grammar/plural.rb +0 -13
- data/lib/gamefic/grammar/pronouns.rb +0 -106
- data/lib/gamefic/grammar/tense.rb +0 -6
- data/lib/gamefic/grammar/verb_set.rb +0 -43
- data/lib/gamefic/grammar/verbs.rb +0 -26
- data/lib/gamefic/grammar/word_adapter.rb +0 -49
- data/lib/gamefic/plot/articles.rb +0 -22
- data/lib/gamefic/plot/callbacks.rb +0 -126
- data/lib/gamefic/plot/commands.rb +0 -120
- data/lib/gamefic/plot/players.rb +0 -15
- data/lib/gamefic/plot/scenes.rb +0 -187
- data/lib/gamefic/plot/theater.rb +0 -73
- data/lib/gamefic/plot/you_mount.rb +0 -22
- data/lib/gamefic/scene/custom.rb +0 -9
- data/lib/gamefic/script.rb +0 -13
- data/lib/gamefic/script/base.rb +0 -42
- data/lib/gamefic/script/file.rb +0 -14
- data/lib/gamefic/script/text.rb +0 -14
- data/lib/gamefic/shell.rb +0 -76
- data/lib/gamefic/source.rb +0 -14
- data/lib/gamefic/source/base.rb +0 -12
- data/lib/gamefic/source/file.rb +0 -23
- data/lib/gamefic/source/text.rb +0 -16
- data/lib/gamefic/tester.rb +0 -19
- data/lib/gamefic/text.rb +0 -8
- data/lib/gamefic/text/ansi.rb +0 -53
- data/lib/gamefic/text/html.rb +0 -68
- data/lib/gamefic/text/html/conversions.rb +0 -250
- data/lib/gamefic/text/html/entities.rb +0 -9
- data/lib/gamefic/tty.rb +0 -10
- data/lib/gamefic/user.rb +0 -7
- data/lib/gamefic/user/base.rb +0 -29
- data/lib/gamefic/user/tty.rb +0 -38
data/lib/gamefic/query.rb
CHANGED
@@ -1,18 +1,15 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
autoload :
|
7
|
-
autoload :
|
8
|
-
autoload :
|
9
|
-
autoload :
|
10
|
-
autoload :
|
11
|
-
autoload :
|
12
|
-
autoload :
|
13
|
-
autoload :
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
1
|
+
module Gamefic
|
2
|
+
module Query
|
3
|
+
autoload :Base, 'gamefic/query/base'
|
4
|
+
autoload :Children, 'gamefic/query/children'
|
5
|
+
autoload :Descendants, 'gamefic/query/descendants'
|
6
|
+
autoload :External, 'gamefic/query/external'
|
7
|
+
autoload :Family, 'gamefic/query/family'
|
8
|
+
autoload :Tree, 'gamefic/query/tree'
|
9
|
+
autoload :Itself, 'gamefic/query/itself'
|
10
|
+
autoload :Matches, 'gamefic/query/matches'
|
11
|
+
autoload :Parent, 'gamefic/query/parent'
|
12
|
+
autoload :Siblings, 'gamefic/query/siblings'
|
13
|
+
autoload :Text, 'gamefic/query/text'
|
14
|
+
end
|
15
|
+
end
|
data/lib/gamefic/query/base.rb
CHANGED
@@ -9,6 +9,13 @@ module Gamefic
|
|
9
9
|
@arguments = args
|
10
10
|
end
|
11
11
|
|
12
|
+
# Determine whether the query allows ambiguous entity references.
|
13
|
+
# If false, actions that use this query will only be valid if the token
|
14
|
+
# passed into it resolves to a single entity. If true, actions will
|
15
|
+
# accept an array of matching entities instead.
|
16
|
+
# Queries are not ambiguous by default (ambiguous? == false).
|
17
|
+
#
|
18
|
+
# @return [Boolean]
|
12
19
|
def ambiguous?
|
13
20
|
false
|
14
21
|
end
|
@@ -36,8 +43,8 @@ module Gamefic
|
|
36
43
|
return Matches.new(drill, token, '') unless drill.length != 1
|
37
44
|
return Matches.new([], '', token)
|
38
45
|
end
|
39
|
-
result = available.select{ |e| e.
|
40
|
-
result = available.select{ |e| e.
|
46
|
+
result = available.select{ |e| e.specified?(token) }
|
47
|
+
result = available.select{ |e| e.specified?(token, fuzzy: true) } if result.empty?
|
41
48
|
result.keep_if{ |e| accept? e }
|
42
49
|
Matches.new(result, (result.empty? ? '' : token), (result.empty? ? token : ''))
|
43
50
|
end
|
@@ -48,22 +55,18 @@ module Gamefic
|
|
48
55
|
result.include?(object)
|
49
56
|
end
|
50
57
|
|
58
|
+
# A ranking of how precise the query's arguments are.
|
59
|
+
#
|
60
|
+
# Query precision is a factor in calculating Action#rank.
|
61
|
+
#
|
62
|
+
# @return [Integer]
|
51
63
|
def precision
|
52
64
|
if @precision.nil?
|
53
65
|
@precision = 1
|
54
66
|
arguments.each { |a|
|
55
|
-
|
56
|
-
# @precision += 1
|
57
|
-
#elsif a.kind_of?(Class)
|
58
|
-
# @precision += (count_superclasses(a) * 100)
|
59
|
-
#elsif a.kind_of?(Module)
|
60
|
-
# @precision += 10
|
61
|
-
#elsif a.kind_of?(Object)
|
62
|
-
# @precision += 1000
|
63
|
-
#end
|
64
|
-
if a.kind_of?(Class)
|
67
|
+
if a.is_a?(Class)
|
65
68
|
@precision += 100
|
66
|
-
elsif a.
|
69
|
+
elsif a.is_a?(Gamefic::Entity)
|
67
70
|
@precision += 1000
|
68
71
|
end
|
69
72
|
}
|
@@ -71,52 +74,63 @@ module Gamefic
|
|
71
74
|
end
|
72
75
|
@precision
|
73
76
|
end
|
74
|
-
|
75
|
-
def rank
|
76
|
-
precision
|
77
|
-
end
|
77
|
+
alias rank precision
|
78
78
|
|
79
79
|
def signature
|
80
|
-
"#{self.class.to_s.downcase}(#{
|
80
|
+
"#{self.class.to_s.split('::').last.downcase}(#{simplify_arguments.join(', ')})"
|
81
81
|
end
|
82
82
|
|
83
|
+
# Determine whether the specified entity passes the query's arguments.
|
84
|
+
#
|
85
|
+
# @return [Boolean]
|
83
86
|
def accept?(entity)
|
84
87
|
result = true
|
85
|
-
arguments.each
|
86
|
-
if a.
|
87
|
-
|
88
|
-
elsif a.
|
89
|
-
|
90
|
-
elsif a.is_a?(Module)
|
91
|
-
|
88
|
+
arguments.each do |a|
|
89
|
+
result = if a.is_a?(Symbol)
|
90
|
+
(entity.send(a) != false)
|
91
|
+
elsif a.is_a?(Regexp)
|
92
|
+
!entity.to_s.match(a).nil?
|
93
|
+
elsif a.is_a?(Module) || a.is_a?(Class)
|
94
|
+
entity.is_a?(a)
|
92
95
|
else
|
93
|
-
|
96
|
+
(entity == a)
|
94
97
|
end
|
95
98
|
break if result == false
|
96
|
-
|
99
|
+
end
|
97
100
|
result
|
98
101
|
end
|
99
102
|
|
100
103
|
protected
|
101
|
-
|
102
|
-
# Return an array of the entity's children. If the child is
|
104
|
+
|
105
|
+
# Return an array of the entity's children. If the child is accessible,
|
103
106
|
# recursively append its children.
|
104
107
|
# The result will NOT include the original entity itself.
|
105
108
|
#
|
106
109
|
# @return [Array<Object>]
|
107
110
|
def subquery_accessible entity
|
111
|
+
return [] if entity.nil?
|
108
112
|
result = []
|
109
113
|
if entity.accessible?
|
110
|
-
entity.children.each
|
114
|
+
entity.children.each do |c|
|
111
115
|
result.push c
|
112
116
|
result.concat subquery_accessible(c)
|
113
|
-
|
117
|
+
end
|
114
118
|
end
|
115
119
|
result
|
116
120
|
end
|
117
121
|
|
118
122
|
private
|
119
123
|
|
124
|
+
def simplify_arguments
|
125
|
+
arguments.map do |a|
|
126
|
+
if a.is_a?(Class) || a.is_a?(Object)
|
127
|
+
a.to_s.split('::').last.downcase
|
128
|
+
else
|
129
|
+
a.to_s.downcase
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
120
134
|
def nested?(token)
|
121
135
|
!token.match(NEST_REGEXP).nil?
|
122
136
|
end
|
@@ -124,13 +138,12 @@ module Gamefic
|
|
124
138
|
def denest(objects, token)
|
125
139
|
parts = token.split(NEST_REGEXP)
|
126
140
|
current = parts.pop
|
127
|
-
last_result = objects.select{ |e| e.
|
128
|
-
last_result = objects.select{ |e| e.
|
129
|
-
|
130
|
-
while parts.length > 0
|
141
|
+
last_result = objects.select { |e| e.specified?(current) }
|
142
|
+
last_result = objects.select { |e| e.specified?(current, fuzzy: true) } if last_result.empty?
|
143
|
+
until parts.empty?
|
131
144
|
current = "#{parts.last} #{current}"
|
132
|
-
result = last_result.select{ |e| e.
|
133
|
-
result = last_result.select{ |e| e.
|
145
|
+
result = last_result.select { |e| e.specified?(current) }
|
146
|
+
result = last_result.select { |e| e.specified?(current, fuzzy: true) } if result.empty?
|
134
147
|
break if result.empty?
|
135
148
|
parts.pop
|
136
149
|
last_result = result
|
File without changes
|
@@ -1,14 +1,18 @@
|
|
1
|
-
module Gamefic
|
2
|
-
module Query
|
3
|
-
class External < Base
|
4
|
-
def initialize objects, *args
|
5
|
-
super(*args)
|
6
|
-
@objects = objects
|
7
|
-
end
|
8
|
-
|
9
|
-
def context_from subject
|
10
|
-
@objects
|
11
|
-
end
|
12
|
-
|
13
|
-
|
14
|
-
|
1
|
+
module Gamefic
|
2
|
+
module Query
|
3
|
+
class External < Base
|
4
|
+
def initialize objects, *args
|
5
|
+
super(*args)
|
6
|
+
@objects = objects
|
7
|
+
end
|
8
|
+
|
9
|
+
def context_from subject
|
10
|
+
@objects
|
11
|
+
end
|
12
|
+
|
13
|
+
def accept?(entity)
|
14
|
+
@objects.include?(entity) && super(entity)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/gamefic/query/family.rb
CHANGED
@@ -1,15 +1,11 @@
|
|
1
1
|
module Gamefic
|
2
2
|
module Query
|
3
|
+
# Query to retrieve the subject's siblings and all accessible descendants.
|
4
|
+
#
|
3
5
|
class Family < Base
|
4
6
|
def context_from(subject)
|
5
7
|
result = []
|
6
|
-
|
7
|
-
unless top.nil?
|
8
|
-
#until top.parent.nil?
|
9
|
-
# top = top.parent
|
10
|
-
#end
|
11
|
-
result.concat subquery_accessible(top)
|
12
|
-
end
|
8
|
+
result.concat subquery_accessible(subject.parent)
|
13
9
|
result.delete subject
|
14
10
|
subject.children.each { |c|
|
15
11
|
result.push c
|
@@ -1,67 +1,75 @@
|
|
1
|
-
module Gamefic
|
2
|
-
module Query
|
3
|
-
class Matches
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
1
|
+
module Gamefic
|
2
|
+
module Query
|
3
|
+
class Matches
|
4
|
+
# The resolved tokens
|
5
|
+
# @return [Array<Object>]
|
6
|
+
attr_reader :objects
|
7
|
+
|
8
|
+
# The matching string
|
9
|
+
# @return [String]
|
10
|
+
attr_reader :matching
|
11
|
+
|
12
|
+
# The remaining (unmatched) string
|
13
|
+
# @return [String]
|
14
|
+
attr_reader :remaining
|
15
|
+
|
16
|
+
def initialize objects, matching, remaining
|
17
|
+
@objects = objects
|
18
|
+
@matching = matching
|
19
|
+
@remaining = remaining
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.execute objects, description, continued: false
|
23
|
+
if continued
|
24
|
+
match_with_remainder objects, description
|
25
|
+
else
|
26
|
+
match_without_remainder objects, description
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class << self
|
31
|
+
private
|
32
|
+
|
33
|
+
def match_without_remainder objects, description
|
34
|
+
matches = objects.select{ |e| e.specified?(description) }
|
35
|
+
if matches.empty?
|
36
|
+
matching = ''
|
37
|
+
remaining = description
|
38
|
+
else
|
39
|
+
matching = description
|
40
|
+
remaining = ''
|
41
|
+
end
|
42
|
+
Matches.new(matches, matching, remaining)
|
43
|
+
end
|
44
|
+
|
45
|
+
def match_with_remainder objects, description
|
46
|
+
matching_objects = objects
|
47
|
+
matching_text = []
|
48
|
+
words = description.split(Keywords::SPLIT_REGEXP)
|
49
|
+
i = 0
|
50
|
+
words.each { |w|
|
51
|
+
cursor = inner_match matching_objects, words, matching_text, i, w
|
52
|
+
break if cursor.empty? or (cursor & matching_objects).empty?
|
53
|
+
matching_objects = (cursor & matching_objects)
|
54
|
+
i += 1
|
55
|
+
}
|
56
|
+
objects = matching_objects
|
57
|
+
matching = matching_text.uniq.join(' ')
|
58
|
+
remaining = words[i..-1].join(' ')
|
59
|
+
Matches.new(objects, matching, remaining)
|
60
|
+
end
|
61
|
+
|
62
|
+
def inner_match matching_objects, words, matching_text, i, w
|
63
|
+
cursor = []
|
64
|
+
matching_objects.each { |o|
|
65
|
+
if o.specified?(words[0..i].join(' '), fuzzy: true)
|
66
|
+
cursor.push o
|
67
|
+
matching_text.push w
|
68
|
+
end
|
69
|
+
}
|
70
|
+
cursor
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/gamefic/query/parent.rb
CHANGED
File without changes
|
File without changes
|
data/lib/gamefic/query/text.rb
CHANGED
@@ -2,15 +2,17 @@ module Gamefic
|
|
2
2
|
module Query
|
3
3
|
class Text < Base
|
4
4
|
def initialize *arguments
|
5
|
-
arguments.each
|
6
|
-
if (a.kind_of?(Symbol)
|
5
|
+
arguments.each do |a|
|
6
|
+
if (a.kind_of?(Symbol) || a.kind_of?(String)) && !a.to_s.end_with?('?')
|
7
7
|
raise ArgumentError.new("Text query arguments can only be boolean method names (:method?) or regular expressions")
|
8
8
|
end
|
9
|
-
|
9
|
+
end
|
10
10
|
super
|
11
11
|
end
|
12
|
-
|
13
|
-
|
12
|
+
|
13
|
+
def resolve _subject, token, continued: false
|
14
|
+
return Matches.new([], '', token) unless accept?(token)
|
15
|
+
parts = token.split(Keywords::SPLIT_REGEXP)
|
14
16
|
cursor = []
|
15
17
|
matches = []
|
16
18
|
i = 0
|
@@ -21,20 +23,18 @@ module Gamefic
|
|
21
23
|
}
|
22
24
|
if continued
|
23
25
|
Matches.new([matches.join(' ')], matches.join(' '), parts[i..-1].join(' '))
|
26
|
+
elsif matches.length == parts.length
|
27
|
+
Matches.new([matches.join(' ')], matches.join(' '), '')
|
24
28
|
else
|
25
|
-
|
26
|
-
Matches.new([matches.join(' ')], matches.join(' '), '')
|
27
|
-
else
|
28
|
-
Matches.new([], '', parts.join(' '))
|
29
|
-
end
|
29
|
+
Matches.new([], '', parts.join(' '))
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
def include?
|
33
|
+
def include? _subject, token
|
34
34
|
accept?(token)
|
35
35
|
end
|
36
36
|
|
37
|
-
def accept?
|
37
|
+
def accept? entity
|
38
38
|
return false unless entity.kind_of?(String) and !entity.empty?
|
39
39
|
super
|
40
40
|
end
|