gamefic 2.0.2 → 2.2.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/.rubocop.yml +4 -1
- data/CHANGELOG.md +15 -1
- data/lib/gamefic/action.rb +37 -41
- data/lib/gamefic/active.rb +49 -76
- data/lib/gamefic/core_ext/array.rb +4 -1
- data/lib/gamefic/core_ext/string.rb +2 -8
- data/lib/gamefic/dispatcher.rb +76 -0
- data/lib/gamefic/element.rb +11 -12
- data/lib/gamefic/entity.rb +3 -3
- data/lib/gamefic/plot.rb +2 -9
- data/lib/gamefic/query/base.rb +25 -24
- data/lib/gamefic/query/descendants.rb +2 -2
- data/lib/gamefic/query/family.rb +2 -0
- data/lib/gamefic/query/text.rb +10 -11
- data/lib/gamefic/query/tree.rb +17 -0
- data/lib/gamefic/query.rb +1 -0
- data/lib/gamefic/scene/activity.rb +1 -3
- data/lib/gamefic/scene/base.rb +4 -2
- data/lib/gamefic/scene/conclusion.rb +1 -1
- data/lib/gamefic/scene/multiple_choice.rb +2 -12
- data/lib/gamefic/scene/pause.rb +1 -1
- data/lib/gamefic/scene/yes_or_no.rb +1 -1
- data/lib/gamefic/scene.rb +0 -1
- data/lib/gamefic/serialize.rb +15 -26
- data/lib/gamefic/subplot.rb +3 -12
- data/lib/gamefic/syntax.rb +25 -18
- data/lib/gamefic/version.rb +1 -1
- data/lib/gamefic/world/commands.rb +7 -26
- data/lib/gamefic/world/entities.rb +0 -10
- data/lib/gamefic/world/playbook.rb +42 -88
- data/lib/gamefic/world/players.rb +18 -2
- data/lib/gamefic/world/scenes.rb +11 -11
- data/lib/gamefic/world.rb +2 -0
- data/lib/gamefic.rb +2 -2
- metadata +18 -18
- data/lib/gamefic/plot/index.rb +0 -92
- data/lib/gamefic/scene/custom.rb +0 -7
data/lib/gamefic/element.rb
CHANGED
@@ -7,17 +7,20 @@ module Gamefic
|
|
7
7
|
#
|
8
8
|
class Element
|
9
9
|
include Gamefic::Describable
|
10
|
-
# include Gamefic::Index
|
11
10
|
include Gamefic::Serialize
|
12
11
|
|
13
|
-
# @todo It would be nice if this initialization wasn't necessary.
|
14
12
|
def initialize(args = {})
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
13
|
+
klass = self.class
|
14
|
+
defaults = {}
|
15
|
+
while klass <= Element
|
16
|
+
defaults = klass.default_attributes.merge(defaults)
|
17
|
+
klass = klass.superclass
|
18
|
+
end
|
19
|
+
defaults.merge(args).each_pair do |k, v|
|
20
|
+
public_send "#{k}=", v
|
21
|
+
end
|
22
|
+
post_initialize
|
23
|
+
yield self if block_given?
|
21
24
|
end
|
22
25
|
|
23
26
|
def post_initialize
|
@@ -38,10 +41,6 @@ module Gamefic
|
|
38
41
|
def default_attributes
|
39
42
|
@default_attributes ||= {}
|
40
43
|
end
|
41
|
-
|
42
|
-
def inherited subclass
|
43
|
-
subclass.set_default default_attributes
|
44
|
-
end
|
45
44
|
end
|
46
45
|
end
|
47
46
|
end
|
data/lib/gamefic/entity.rb
CHANGED
@@ -13,10 +13,10 @@ module Gamefic
|
|
13
13
|
|
14
14
|
# Set the Entity's parent.
|
15
15
|
#
|
16
|
-
# @param node [Gamefic::Entity] The new parent.
|
16
|
+
# @param node [Gamefic::Entity, nil] The new parent.
|
17
17
|
def parent=(node)
|
18
|
-
if node
|
19
|
-
raise "Entity's parent must be an Entity"
|
18
|
+
if node && node.is_a?(Entity) == false
|
19
|
+
raise ArgumentError, "Entity's parent must be an Entity"
|
20
20
|
end
|
21
21
|
super
|
22
22
|
end
|
data/lib/gamefic/plot.rb
CHANGED
@@ -27,7 +27,6 @@ module Gamefic
|
|
27
27
|
|
28
28
|
exclude_from_serial [:@static]
|
29
29
|
|
30
|
-
# @param structure [Gamefic::Structure]
|
31
30
|
# @param metadata [Hash]
|
32
31
|
def initialize metadata: {}
|
33
32
|
@metadata = metadata
|
@@ -35,11 +34,6 @@ module Gamefic
|
|
35
34
|
@static = [self] + scene_classes + entities
|
36
35
|
end
|
37
36
|
|
38
|
-
def player_class cls = nil
|
39
|
-
@player_class = cls unless cls.nil?
|
40
|
-
@player_class ||= Gamefic::Actor
|
41
|
-
end
|
42
|
-
|
43
37
|
# Get an Array of the Plot's current Syntaxes.
|
44
38
|
#
|
45
39
|
# @return [Array<Syntax>]
|
@@ -80,7 +74,6 @@ module Gamefic
|
|
80
74
|
entities.each { |e| e.flush }
|
81
75
|
call_before_player_update
|
82
76
|
players.each do |p|
|
83
|
-
p.performed nil
|
84
77
|
next unless p.scene
|
85
78
|
p.last_input = p.queue.last
|
86
79
|
p.last_prompt = p.scene.prompt
|
@@ -93,8 +86,8 @@ module Gamefic
|
|
93
86
|
end
|
94
87
|
call_player_update
|
95
88
|
call_update
|
96
|
-
subplots.
|
97
|
-
subplots.
|
89
|
+
subplots.delete_if(&:concluded?)
|
90
|
+
subplots.each(&:update)
|
98
91
|
end
|
99
92
|
|
100
93
|
# Send a message to a group of entities.
|
data/lib/gamefic/query/base.rb
CHANGED
@@ -55,13 +55,18 @@ module Gamefic
|
|
55
55
|
result.include?(object)
|
56
56
|
end
|
57
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]
|
58
63
|
def precision
|
59
64
|
if @precision.nil?
|
60
65
|
@precision = 1
|
61
66
|
arguments.each { |a|
|
62
|
-
if a.
|
67
|
+
if a.is_a?(Class)
|
63
68
|
@precision += 100
|
64
|
-
elsif a.
|
69
|
+
elsif a.is_a?(Gamefic::Entity)
|
65
70
|
@precision += 1000
|
66
71
|
end
|
67
72
|
}
|
@@ -69,10 +74,7 @@ module Gamefic
|
|
69
74
|
end
|
70
75
|
@precision
|
71
76
|
end
|
72
|
-
|
73
|
-
def rank
|
74
|
-
precision
|
75
|
-
end
|
77
|
+
alias rank precision
|
76
78
|
|
77
79
|
def signature
|
78
80
|
"#{self.class.to_s.split('::').last.downcase}(#{simplify_arguments.join(', ')})"
|
@@ -83,18 +85,18 @@ module Gamefic
|
|
83
85
|
# @return [Boolean]
|
84
86
|
def accept?(entity)
|
85
87
|
result = true
|
86
|
-
arguments.each
|
87
|
-
if a.
|
88
|
-
|
89
|
-
elsif a.
|
90
|
-
|
91
|
-
elsif a.is_a?(Module)
|
92
|
-
|
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)
|
93
95
|
else
|
94
|
-
|
96
|
+
(entity == a)
|
95
97
|
end
|
96
98
|
break if result == false
|
97
|
-
|
99
|
+
end
|
98
100
|
result
|
99
101
|
end
|
100
102
|
|
@@ -109,10 +111,10 @@ module Gamefic
|
|
109
111
|
return [] if entity.nil?
|
110
112
|
result = []
|
111
113
|
if entity.accessible?
|
112
|
-
entity.children.each
|
114
|
+
entity.children.each do |c|
|
113
115
|
result.push c
|
114
116
|
result.concat subquery_accessible(c)
|
115
|
-
|
117
|
+
end
|
116
118
|
end
|
117
119
|
result
|
118
120
|
end
|
@@ -121,7 +123,7 @@ module Gamefic
|
|
121
123
|
|
122
124
|
def simplify_arguments
|
123
125
|
arguments.map do |a|
|
124
|
-
if a.
|
126
|
+
if a.is_a?(Class) || a.is_a?(Object)
|
125
127
|
a.to_s.split('::').last.downcase
|
126
128
|
else
|
127
129
|
a.to_s.downcase
|
@@ -136,13 +138,12 @@ module Gamefic
|
|
136
138
|
def denest(objects, token)
|
137
139
|
parts = token.split(NEST_REGEXP)
|
138
140
|
current = parts.pop
|
139
|
-
last_result = objects.select{ |e| e.specified?(current) }
|
140
|
-
last_result = objects.select{ |e| e.specified?(current, fuzzy: true) } if last_result.empty?
|
141
|
-
|
142
|
-
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?
|
143
144
|
current = "#{parts.last} #{current}"
|
144
|
-
result = last_result.select{ |e| e.specified?(current) }
|
145
|
-
result = last_result.select{ |e| e.specified?(current, fuzzy: true) } if result.empty?
|
145
|
+
result = last_result.select { |e| e.specified?(current) }
|
146
|
+
result = last_result.select { |e| e.specified?(current, fuzzy: true) } if result.empty?
|
146
147
|
break if result.empty?
|
147
148
|
parts.pop
|
148
149
|
last_result = result
|
data/lib/gamefic/query/family.rb
CHANGED
data/lib/gamefic/query/text.rb
CHANGED
@@ -2,14 +2,15 @@ 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
|
-
|
12
|
+
|
13
|
+
def resolve _subject, token, continued: false
|
13
14
|
return Matches.new([], '', token) unless accept?(token)
|
14
15
|
parts = token.split(Keywords::SPLIT_REGEXP)
|
15
16
|
cursor = []
|
@@ -22,20 +23,18 @@ module Gamefic
|
|
22
23
|
}
|
23
24
|
if continued
|
24
25
|
Matches.new([matches.join(' ')], matches.join(' '), parts[i..-1].join(' '))
|
26
|
+
elsif matches.length == parts.length
|
27
|
+
Matches.new([matches.join(' ')], matches.join(' '), '')
|
25
28
|
else
|
26
|
-
|
27
|
-
Matches.new([matches.join(' ')], matches.join(' '), '')
|
28
|
-
else
|
29
|
-
Matches.new([], '', parts.join(' '))
|
30
|
-
end
|
29
|
+
Matches.new([], '', parts.join(' '))
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
34
|
-
def include?
|
33
|
+
def include? _subject, token
|
35
34
|
accept?(token)
|
36
35
|
end
|
37
36
|
|
38
|
-
def accept?
|
37
|
+
def accept? entity
|
39
38
|
return false unless entity.kind_of?(String) and !entity.empty?
|
40
39
|
super
|
41
40
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Gamefic
|
2
|
+
module Query
|
3
|
+
# Query to retrieve all of the subject's ancestors, siblings, and descendants.
|
4
|
+
#
|
5
|
+
class Tree < Family
|
6
|
+
def context_from(subject)
|
7
|
+
result = super
|
8
|
+
parent = subject.parent
|
9
|
+
until parent.nil?
|
10
|
+
result.unshift parent
|
11
|
+
parent = parent.parent
|
12
|
+
end
|
13
|
+
result
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/gamefic/query.rb
CHANGED
@@ -5,6 +5,7 @@ module Gamefic
|
|
5
5
|
autoload :Descendants, 'gamefic/query/descendants'
|
6
6
|
autoload :External, 'gamefic/query/external'
|
7
7
|
autoload :Family, 'gamefic/query/family'
|
8
|
+
autoload :Tree, 'gamefic/query/tree'
|
8
9
|
autoload :Itself, 'gamefic/query/itself'
|
9
10
|
autoload :Matches, 'gamefic/query/matches'
|
10
11
|
autoload :Parent, 'gamefic/query/parent'
|
data/lib/gamefic/scene/base.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Gamefic
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# An abstract class for building different types of scenes. It can be
|
3
|
+
# extended either through concrete subclasses or by creating anonymous
|
4
|
+
# subclasses through a scene helper method like
|
5
|
+
# `Gamefic::World::Scenes#custom`.
|
4
6
|
#
|
5
7
|
class Scene::Base
|
6
8
|
include Gamefic::Serialize
|
@@ -6,7 +6,7 @@ module Gamefic
|
|
6
6
|
# The finish block's input parameter receives a MultipleChoice::Input object
|
7
7
|
# instead of a String.
|
8
8
|
#
|
9
|
-
class Scene::MultipleChoice < Scene::
|
9
|
+
class Scene::MultipleChoice < Scene::Base
|
10
10
|
# The zero-based index of the selected option.
|
11
11
|
#
|
12
12
|
# @return [Integer]
|
@@ -14,7 +14,7 @@ module Gamefic
|
|
14
14
|
|
15
15
|
# The one-based index of the selected option.
|
16
16
|
#
|
17
|
-
# @return [
|
17
|
+
# @return [Integer]
|
18
18
|
attr_reader :number
|
19
19
|
|
20
20
|
# The full text of the selected option.
|
@@ -33,7 +33,6 @@ module Gamefic
|
|
33
33
|
get_choice
|
34
34
|
if selection.nil?
|
35
35
|
actor.tell invalid_message
|
36
|
-
tell_options
|
37
36
|
else
|
38
37
|
super
|
39
38
|
end
|
@@ -77,14 +76,5 @@ module Gamefic
|
|
77
76
|
}
|
78
77
|
end
|
79
78
|
end
|
80
|
-
|
81
|
-
def tell_options
|
82
|
-
list = '<ol class="multiple_choice">'
|
83
|
-
options.each { |o|
|
84
|
-
list += "<li><a href=\"#\" rel=\"gamefic\" data-command=\"#{o}\">#{o}</a></li>"
|
85
|
-
}
|
86
|
-
list += "</ol>"
|
87
|
-
actor.tell list
|
88
|
-
end
|
89
79
|
end
|
90
80
|
end
|
data/lib/gamefic/scene/pause.rb
CHANGED
@@ -4,7 +4,7 @@ module Gamefic
|
|
4
4
|
# block. After the scene is finished, the :active scene will be cued if no
|
5
5
|
# other scene has been prepared or cued.
|
6
6
|
#
|
7
|
-
class Scene::YesOrNo < Scene::
|
7
|
+
class Scene::YesOrNo < Scene::Base
|
8
8
|
attr_writer :invalid_message
|
9
9
|
|
10
10
|
def post_initialize
|
data/lib/gamefic/scene.rb
CHANGED
data/lib/gamefic/serialize.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Gamefic
|
2
4
|
module Serialize
|
3
5
|
def to_serial(index = [])
|
@@ -30,13 +32,6 @@ module Gamefic
|
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
33
|
-
def self.instances
|
34
|
-
GC.start
|
35
|
-
result = []
|
36
|
-
ObjectSpace.each_object(Gamefic::Serialize) { |obj| result.push obj }
|
37
|
-
result
|
38
|
-
end
|
39
|
-
|
40
35
|
# @param string [String]
|
41
36
|
# @return [Object]
|
42
37
|
def self.string_to_constant string
|
@@ -69,7 +64,7 @@ class Object
|
|
69
64
|
end
|
70
65
|
|
71
66
|
def from_serial(index = [])
|
72
|
-
if self.is_a?(Hash)
|
67
|
+
if self.is_a?(Hash)
|
73
68
|
if self['instance']
|
74
69
|
elematch = self['instance'].match(/^#<ELE_([\d]+)>$/)
|
75
70
|
object = index[elematch[1].to_i]
|
@@ -83,6 +78,8 @@ class Object
|
|
83
78
|
return object
|
84
79
|
elsif self['class'] == 'Class'
|
85
80
|
return Gamefic::Serialize.string_to_constant(self['name'])
|
81
|
+
elsif self['class'] == 'Set'
|
82
|
+
return Set.new(self['data'].map { |el| el.from_serial(index) })
|
86
83
|
else
|
87
84
|
elematch = self['class'].match(/^#<ELE_([\d]+)>$/)
|
88
85
|
if elematch
|
@@ -106,25 +103,8 @@ class Object
|
|
106
103
|
return index.index(match[1].to_i) if match
|
107
104
|
match = self.match(/#<SYM:([a-z0-9_\?\!]+)>/i)
|
108
105
|
return match[1].to_sym if match
|
109
|
-
|
106
|
+
return nil if self == '#<UNKNOWN>'
|
110
107
|
self
|
111
|
-
elsif self.is_a?(Hash)
|
112
|
-
result = {}
|
113
|
-
unknown = false
|
114
|
-
self.each_pair do |k, v|
|
115
|
-
k2 = k.from_serial(index)
|
116
|
-
v2 = v.from_serial(index)
|
117
|
-
if k2 == "#<UNKNOWN>" || v2 == "#<UNKNOWN>"
|
118
|
-
unknown = true
|
119
|
-
break
|
120
|
-
end
|
121
|
-
result[k2] = v2
|
122
|
-
end
|
123
|
-
result = "#<UNKNOWN>" if unknown
|
124
|
-
result
|
125
|
-
elsif self && self != true
|
126
|
-
STDERR.puts "Unable to unserialize #{self.class}"
|
127
|
-
nil
|
128
108
|
else
|
129
109
|
# true, false, or nil
|
130
110
|
self
|
@@ -208,3 +188,12 @@ class Hash
|
|
208
188
|
result
|
209
189
|
end
|
210
190
|
end
|
191
|
+
|
192
|
+
class Set
|
193
|
+
def to_serial(index = [])
|
194
|
+
{
|
195
|
+
'class' => 'Set',
|
196
|
+
'data' => to_a.map { |el| el.to_serial(index) }
|
197
|
+
}
|
198
|
+
end
|
199
|
+
end
|