gamefic 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/gamefic.rb +1 -3
- data/lib/gamefic/action.rb +140 -79
- data/lib/gamefic/character.rb +120 -53
- data/lib/gamefic/character/state.rb +12 -0
- data/lib/gamefic/core_ext/array.rb +53 -11
- data/lib/gamefic/core_ext/string.rb +1 -0
- data/lib/gamefic/describable.rb +37 -11
- data/lib/gamefic/engine/base.rb +17 -4
- data/lib/gamefic/engine/tty.rb +4 -0
- data/lib/gamefic/entity.rb +4 -15
- data/lib/gamefic/matchable.rb +50 -0
- data/lib/gamefic/messaging.rb +45 -0
- data/lib/gamefic/node.rb +16 -0
- data/lib/gamefic/plot.rb +27 -33
- data/lib/gamefic/plot/{article_mount.rb → articles.rb} +22 -22
- data/lib/gamefic/plot/callbacks.rb +30 -4
- data/lib/gamefic/plot/{command_mount.rb → commands.rb} +121 -121
- data/lib/gamefic/plot/entities.rb +3 -3
- data/lib/gamefic/plot/host.rb +3 -3
- data/lib/gamefic/plot/playbook.rb +74 -30
- data/lib/gamefic/plot/scenes.rb +149 -0
- data/lib/gamefic/plot/snapshot.rb +14 -39
- data/lib/gamefic/plot/theater.rb +73 -0
- data/lib/gamefic/query.rb +6 -19
- data/lib/gamefic/query/base.rb +127 -246
- data/lib/gamefic/query/children.rb +6 -7
- data/lib/gamefic/query/descendants.rb +15 -0
- data/lib/gamefic/query/family.rb +19 -7
- data/lib/gamefic/query/itself.rb +13 -0
- data/lib/gamefic/query/matches.rb +67 -11
- data/lib/gamefic/query/parent.rb +6 -7
- data/lib/gamefic/query/siblings.rb +10 -7
- data/lib/gamefic/query/text.rb +39 -35
- data/lib/gamefic/scene.rb +1 -1
- data/lib/gamefic/scene/active.rb +12 -6
- data/lib/gamefic/scene/base.rb +56 -5
- data/lib/gamefic/scene/conclusion.rb +3 -0
- data/lib/gamefic/scene/custom.rb +0 -83
- data/lib/gamefic/scene/multiple_choice.rb +54 -32
- data/lib/gamefic/scene/multiple_scene.rb +11 -6
- data/lib/gamefic/scene/pause.rb +3 -4
- data/lib/gamefic/scene/yes_or_no.rb +23 -9
- data/lib/gamefic/script/base.rb +4 -0
- data/lib/gamefic/subplot.rb +22 -19
- data/lib/gamefic/syntax.rb +7 -15
- data/lib/gamefic/user/base.rb +7 -13
- data/lib/gamefic/user/buffer.rb +7 -0
- data/lib/gamefic/user/tty.rb +13 -12
- data/lib/gamefic/version.rb +1 -1
- metadata +11 -37
- data/lib/gamefic/director.rb +0 -23
- data/lib/gamefic/director/delegate.rb +0 -126
- data/lib/gamefic/director/order.rb +0 -17
- data/lib/gamefic/director/parser.rb +0 -137
- data/lib/gamefic/keywords.rb +0 -67
- data/lib/gamefic/plot/query_mount.rb +0 -9
- data/lib/gamefic/plot/scene_mount.rb +0 -182
- data/lib/gamefic/query/ambiguous_children.rb +0 -5
- data/lib/gamefic/query/expression.rb +0 -47
- data/lib/gamefic/query/many_children.rb +0 -7
- data/lib/gamefic/query/plural_children.rb +0 -14
- data/lib/gamefic/query/self.rb +0 -10
- data/lib/gamefic/scene_data.rb +0 -10
- data/lib/gamefic/scene_data/base.rb +0 -12
- data/lib/gamefic/scene_data/multiple_choice.rb +0 -19
- data/lib/gamefic/scene_data/multiple_scene.rb +0 -21
- data/lib/gamefic/scene_data/yes_or_no.rb +0 -18
- data/lib/gamefic/serialized.rb +0 -24
- data/lib/gamefic/stage.rb +0 -106
@@ -1,10 +1,9 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
subject.children
|
1
|
+
module Gamefic
|
2
|
+
module Query
|
3
|
+
class Children < Base
|
4
|
+
def context_from(subject)
|
5
|
+
subject.children
|
6
|
+
end
|
8
7
|
end
|
9
8
|
end
|
10
9
|
end
|
data/lib/gamefic/query/family.rb
CHANGED
@@ -1,10 +1,22 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
module Gamefic
|
2
|
+
module Query
|
3
|
+
class Family < Base
|
4
|
+
def context_from(subject)
|
5
|
+
result = []
|
6
|
+
top = subject.parent
|
7
|
+
unless top.nil?
|
8
|
+
#until top.parent.nil?
|
9
|
+
# top = top.parent
|
10
|
+
#end
|
11
|
+
result.concat subquery_accessible(top)
|
12
|
+
end
|
13
|
+
result.delete subject
|
14
|
+
subject.children.each { |c|
|
15
|
+
result.push c
|
16
|
+
result.concat subquery_accessible(c)
|
17
|
+
}
|
18
|
+
result
|
19
|
+
end
|
8
20
|
end
|
9
21
|
end
|
10
22
|
end
|
@@ -1,11 +1,67 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
module Gamefic
|
2
|
+
module Query
|
3
|
+
class Matches
|
4
|
+
attr_accessor :objects, :matching, :remaining
|
5
|
+
|
6
|
+
def initialize objects, matching, remaining
|
7
|
+
@objects = objects
|
8
|
+
@matching = matching
|
9
|
+
@remaining = remaining
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.execute objects, description, continued: false
|
13
|
+
if continued
|
14
|
+
match_with_remainder objects, description
|
15
|
+
else
|
16
|
+
match_without_remainder objects, description
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
private
|
22
|
+
|
23
|
+
def match_without_remainder objects, description
|
24
|
+
matches = objects.select{ |e| e.match?(description) }
|
25
|
+
if matches.empty?
|
26
|
+
matching = ''
|
27
|
+
remaining = description
|
28
|
+
else
|
29
|
+
matching = description
|
30
|
+
remaining = ''
|
31
|
+
end
|
32
|
+
Matches.new(matches, matching, remaining)
|
33
|
+
end
|
34
|
+
|
35
|
+
def match_with_remainder objects, description
|
36
|
+
matching_objects = objects
|
37
|
+
matching_text = []
|
38
|
+
words = description.split(Matchable::SPLIT_REGEXP)
|
39
|
+
i = 0
|
40
|
+
#cursor = []
|
41
|
+
words.each { |w|
|
42
|
+
cursor = inner_match matching_objects, words, matching_text, i, w
|
43
|
+
break if cursor.empty? or (cursor & matching_objects).empty?
|
44
|
+
matching_objects = (cursor & matching_objects)
|
45
|
+
i += 1
|
46
|
+
}
|
47
|
+
objects = matching_objects
|
48
|
+
matching = matching_text.uniq.join(' ')
|
49
|
+
remaining = words[i..-1].join(' ')
|
50
|
+
m = Matches.new(objects, matching, remaining)
|
51
|
+
m
|
52
|
+
end
|
53
|
+
|
54
|
+
def inner_match matching_objects, words, matching_text, i, w
|
55
|
+
cursor = []
|
56
|
+
matching_objects.each { |o|
|
57
|
+
if o.match?(words[0..i].join(' '), fuzzy: true)
|
58
|
+
cursor.push o
|
59
|
+
matching_text.push w
|
60
|
+
end
|
61
|
+
}
|
62
|
+
cursor
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/gamefic/query/parent.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
[subject.parent]
|
1
|
+
module Gamefic
|
2
|
+
module Query
|
3
|
+
class Parent < Base
|
4
|
+
def context_from(subject)
|
5
|
+
subject.parent.nil? ? [] : [subject.parent]
|
6
|
+
end
|
8
7
|
end
|
9
8
|
end
|
10
9
|
end
|
@@ -1,10 +1,13 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
module Gamefic
|
2
|
+
module Query
|
3
|
+
class Siblings < Base
|
4
|
+
def context_from(subject)
|
5
|
+
result = []
|
6
|
+
unless subject.parent.nil?
|
7
|
+
result.concat(subject.parent.children - [subject])
|
8
|
+
end
|
9
|
+
result
|
10
|
+
end
|
8
11
|
end
|
9
12
|
end
|
10
13
|
end
|
data/lib/gamefic/query/text.rb
CHANGED
@@ -1,43 +1,47 @@
|
|
1
|
-
module Gamefic
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
if description.include?(word)
|
12
|
-
valid = true
|
13
|
-
break
|
14
|
-
end
|
15
|
-
}
|
16
|
-
valid
|
17
|
-
end
|
18
|
-
def execute(subject, description)
|
19
|
-
if @arguments.length == 0
|
20
|
-
return Matches.new([description], description, '')
|
1
|
+
module Gamefic
|
2
|
+
module Query
|
3
|
+
class Text < Base
|
4
|
+
def initialize *arguments
|
5
|
+
arguments.each { |a|
|
6
|
+
if (a.kind_of?(Symbol) or a.kind_of?(String)) and !a.to_s.end_with?('?')
|
7
|
+
raise ArgumentError.new("Text query arguments can only be boolean method names (:method?) or regular expressions")
|
8
|
+
end
|
9
|
+
}
|
10
|
+
super
|
21
11
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
12
|
+
def resolve(subject, token, continued: false)
|
13
|
+
parts = token.split(Matchable::SPLIT_REGEXP)
|
14
|
+
cursor = []
|
15
|
+
matches = []
|
16
|
+
i = 0
|
17
|
+
parts.each { |w|
|
18
|
+
cursor.push w
|
19
|
+
matches = cursor if accept?(cursor.join(' '))
|
20
|
+
i += 1
|
21
|
+
}
|
22
|
+
if continued
|
23
|
+
Matches.new([matches.join(' ')], matches.join(' '), parts[i..-1].join(' '))
|
29
24
|
else
|
30
|
-
|
25
|
+
if matches.length == parts.length
|
26
|
+
Matches.new([matches.join(' ')], matches.join(' '), '')
|
27
|
+
else
|
28
|
+
Matches.new([], '', parts.join(' '))
|
29
|
+
end
|
31
30
|
end
|
32
31
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
|
33
|
+
def include?(subject, token)
|
34
|
+
accept?(token)
|
35
|
+
end
|
36
|
+
|
37
|
+
def accept?(entity)
|
38
|
+
return false unless entity.kind_of?(String) and !entity.empty?
|
39
|
+
super
|
40
|
+
end
|
41
|
+
|
42
|
+
def precision
|
43
|
+
0
|
37
44
|
end
|
38
|
-
end
|
39
|
-
def test_arguments arguments
|
40
|
-
# No test for text
|
41
45
|
end
|
42
46
|
end
|
43
47
|
end
|
data/lib/gamefic/scene.rb
CHANGED
data/lib/gamefic/scene/active.rb
CHANGED
@@ -5,16 +5,22 @@ module Gamefic
|
|
5
5
|
# a Plot.
|
6
6
|
#
|
7
7
|
class Scene::Active < Scene::Base
|
8
|
-
|
9
|
-
|
10
|
-
def initialize plot
|
11
|
-
@plot = plot
|
8
|
+
def post_initialize
|
9
|
+
self.type = 'Active'
|
12
10
|
end
|
13
11
|
|
14
|
-
def finish
|
15
|
-
|
12
|
+
def finish
|
13
|
+
super
|
14
|
+
o = nil
|
15
|
+
o = actor.perform input.strip unless input.nil?
|
16
16
|
actor.performed o
|
17
17
|
end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def type
|
21
|
+
'Active'
|
22
|
+
end
|
23
|
+
end
|
18
24
|
end
|
19
25
|
|
20
26
|
end
|
data/lib/gamefic/scene/base.rb
CHANGED
@@ -4,21 +4,72 @@ module Gamefic
|
|
4
4
|
# should inherit from it.
|
5
5
|
#
|
6
6
|
class Scene::Base
|
7
|
-
|
7
|
+
attr_reader :actor
|
8
|
+
attr_writer :type
|
9
|
+
attr_writer :prompt
|
10
|
+
attr_reader :input
|
11
|
+
|
12
|
+
def initialize actor
|
13
|
+
@actor = actor
|
14
|
+
post_initialize
|
15
|
+
end
|
16
|
+
|
17
|
+
def post_initialize
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_finish &block
|
21
|
+
@finish_block = block
|
8
22
|
end
|
9
23
|
|
10
|
-
def
|
24
|
+
def update
|
25
|
+
@input = actor.queue.shift
|
26
|
+
finish
|
27
|
+
end
|
28
|
+
|
29
|
+
def start
|
30
|
+
self.class.initialize_block.call @actor, self unless self.class.initialize_block.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def finish
|
34
|
+
@finish_block.call @actor, self unless @finish_block.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def flush
|
38
|
+
@state.clear
|
39
|
+
end
|
40
|
+
|
41
|
+
def state
|
42
|
+
{
|
43
|
+
scene: type, prompt: prompt, input: input #, output: actor.messages, busy: !actor.queue.empty?
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.subclass &block
|
48
|
+
c = Class.new(self) do
|
49
|
+
on_initialize &block
|
50
|
+
end
|
51
|
+
c
|
11
52
|
end
|
12
53
|
|
13
54
|
# Get the prompt to be displayed to the user when accepting input.
|
14
55
|
#
|
15
56
|
# @return [String] The text to be displayed.
|
16
|
-
def
|
17
|
-
'>'
|
57
|
+
def prompt
|
58
|
+
@prompt ||= '>'
|
18
59
|
end
|
19
60
|
|
20
61
|
def type
|
21
|
-
|
62
|
+
@type ||= 'Scene'
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.on_initialize &block
|
66
|
+
@initialize_block = block
|
67
|
+
end
|
68
|
+
|
69
|
+
class << self
|
70
|
+
def initialize_block
|
71
|
+
@initialize_block
|
72
|
+
end
|
22
73
|
end
|
23
74
|
end
|
24
75
|
|
data/lib/gamefic/scene/custom.rb
CHANGED
@@ -4,89 +4,6 @@ module Gamefic
|
|
4
4
|
# instantiation. It is suitable for direct instantiation or subclassing.
|
5
5
|
#
|
6
6
|
class Scene::Custom < Scene::Base
|
7
|
-
def initialize
|
8
|
-
yield self if block_given?
|
9
|
-
end
|
10
|
-
|
11
|
-
def data_class
|
12
|
-
SceneData::Base
|
13
|
-
end
|
14
|
-
|
15
|
-
# Define a block to be executed at the start of the scene.
|
16
|
-
# Unlike on_finish, start blocks may be executed more than once per turn,
|
17
|
-
# and more than one scene may be started in a single turn.
|
18
|
-
# It always gets executed in a plot's on_ready event and whenever it gets
|
19
|
-
# cued. (If the character is already in the scene being cued, on_start
|
20
|
-
# does not get repeated.)
|
21
|
-
#
|
22
|
-
# @yieldparam [Character]
|
23
|
-
# @yieldparam [SceneData::Base]
|
24
|
-
def on_start &block
|
25
|
-
@start = block
|
26
|
-
end
|
27
|
-
|
28
|
-
# Define a block to be executed at the end of the scene.
|
29
|
-
# The scene data passed to this block will include the character's input
|
30
|
-
# for this turn.
|
31
|
-
# Unlike on_start, finish only gets executed once per turn, during the
|
32
|
-
# plot's on_update event.
|
33
|
-
#
|
34
|
-
# @yieldparam [Character]
|
35
|
-
# @yieldparam [SceneData::Base]
|
36
|
-
def on_finish &block
|
37
|
-
@finish = block
|
38
|
-
end
|
39
|
-
|
40
|
-
# Start the scene.
|
41
|
-
# This method typically gets called from the plot during the on_ready
|
42
|
-
# event and whenever a character cues a scene.
|
43
|
-
#
|
44
|
-
def start actor
|
45
|
-
data = start_data_for(actor)
|
46
|
-
do_start_block actor, data
|
47
|
-
data
|
48
|
-
end
|
49
|
-
|
50
|
-
# End the scene.
|
51
|
-
# This method typically gets called from the plot during the on_update
|
52
|
-
# event.
|
53
|
-
#
|
54
|
-
def finish actor, input
|
55
|
-
data = finish_data_for(actor, input)
|
56
|
-
do_finish_block actor, data
|
57
|
-
end
|
58
|
-
|
59
|
-
# Get the text to be displayed to the user when receiving input.
|
60
|
-
#
|
61
|
-
# @return [String]
|
62
|
-
def prompt_for actor
|
63
|
-
return character_data[actor].prompt unless character_data[actor].nil?
|
64
|
-
'>'
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
def do_start_block actor, data
|
70
|
-
@start.call actor, data unless @start.nil?
|
71
|
-
end
|
72
|
-
|
73
|
-
def do_finish_block actor, data
|
74
|
-
@finish.call actor, data unless @finish.nil?
|
75
|
-
end
|
76
|
-
|
77
|
-
def character_data
|
78
|
-
@character_data ||= {}
|
79
|
-
end
|
80
|
-
|
81
|
-
def start_data_for actor
|
82
|
-
character_data[actor] ||= data_class.new
|
83
|
-
end
|
84
|
-
|
85
|
-
def finish_data_for actor, input
|
86
|
-
data = character_data[actor]
|
87
|
-
data.input = input.strip
|
88
|
-
data
|
89
|
-
end
|
90
7
|
end
|
91
8
|
|
92
9
|
end
|