gamefic 1.5.1 → 1.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gamefic.rb +1 -3
  3. data/lib/gamefic/action.rb +140 -79
  4. data/lib/gamefic/character.rb +120 -53
  5. data/lib/gamefic/character/state.rb +12 -0
  6. data/lib/gamefic/core_ext/array.rb +53 -11
  7. data/lib/gamefic/core_ext/string.rb +1 -0
  8. data/lib/gamefic/describable.rb +37 -11
  9. data/lib/gamefic/engine/base.rb +17 -4
  10. data/lib/gamefic/engine/tty.rb +4 -0
  11. data/lib/gamefic/entity.rb +4 -15
  12. data/lib/gamefic/matchable.rb +50 -0
  13. data/lib/gamefic/messaging.rb +45 -0
  14. data/lib/gamefic/node.rb +16 -0
  15. data/lib/gamefic/plot.rb +27 -33
  16. data/lib/gamefic/plot/{article_mount.rb → articles.rb} +22 -22
  17. data/lib/gamefic/plot/callbacks.rb +30 -4
  18. data/lib/gamefic/plot/{command_mount.rb → commands.rb} +121 -121
  19. data/lib/gamefic/plot/entities.rb +3 -3
  20. data/lib/gamefic/plot/host.rb +3 -3
  21. data/lib/gamefic/plot/playbook.rb +74 -30
  22. data/lib/gamefic/plot/scenes.rb +149 -0
  23. data/lib/gamefic/plot/snapshot.rb +14 -39
  24. data/lib/gamefic/plot/theater.rb +73 -0
  25. data/lib/gamefic/query.rb +6 -19
  26. data/lib/gamefic/query/base.rb +127 -246
  27. data/lib/gamefic/query/children.rb +6 -7
  28. data/lib/gamefic/query/descendants.rb +15 -0
  29. data/lib/gamefic/query/family.rb +19 -7
  30. data/lib/gamefic/query/itself.rb +13 -0
  31. data/lib/gamefic/query/matches.rb +67 -11
  32. data/lib/gamefic/query/parent.rb +6 -7
  33. data/lib/gamefic/query/siblings.rb +10 -7
  34. data/lib/gamefic/query/text.rb +39 -35
  35. data/lib/gamefic/scene.rb +1 -1
  36. data/lib/gamefic/scene/active.rb +12 -6
  37. data/lib/gamefic/scene/base.rb +56 -5
  38. data/lib/gamefic/scene/conclusion.rb +3 -0
  39. data/lib/gamefic/scene/custom.rb +0 -83
  40. data/lib/gamefic/scene/multiple_choice.rb +54 -32
  41. data/lib/gamefic/scene/multiple_scene.rb +11 -6
  42. data/lib/gamefic/scene/pause.rb +3 -4
  43. data/lib/gamefic/scene/yes_or_no.rb +23 -9
  44. data/lib/gamefic/script/base.rb +4 -0
  45. data/lib/gamefic/subplot.rb +22 -19
  46. data/lib/gamefic/syntax.rb +7 -15
  47. data/lib/gamefic/user/base.rb +7 -13
  48. data/lib/gamefic/user/buffer.rb +7 -0
  49. data/lib/gamefic/user/tty.rb +13 -12
  50. data/lib/gamefic/version.rb +1 -1
  51. metadata +11 -37
  52. data/lib/gamefic/director.rb +0 -23
  53. data/lib/gamefic/director/delegate.rb +0 -126
  54. data/lib/gamefic/director/order.rb +0 -17
  55. data/lib/gamefic/director/parser.rb +0 -137
  56. data/lib/gamefic/keywords.rb +0 -67
  57. data/lib/gamefic/plot/query_mount.rb +0 -9
  58. data/lib/gamefic/plot/scene_mount.rb +0 -182
  59. data/lib/gamefic/query/ambiguous_children.rb +0 -5
  60. data/lib/gamefic/query/expression.rb +0 -47
  61. data/lib/gamefic/query/many_children.rb +0 -7
  62. data/lib/gamefic/query/plural_children.rb +0 -14
  63. data/lib/gamefic/query/self.rb +0 -10
  64. data/lib/gamefic/scene_data.rb +0 -10
  65. data/lib/gamefic/scene_data/base.rb +0 -12
  66. data/lib/gamefic/scene_data/multiple_choice.rb +0 -19
  67. data/lib/gamefic/scene_data/multiple_scene.rb +0 -21
  68. data/lib/gamefic/scene_data/yes_or_no.rb +0 -18
  69. data/lib/gamefic/serialized.rb +0 -24
  70. data/lib/gamefic/stage.rb +0 -106
@@ -1,10 +1,9 @@
1
- module Gamefic::Query
2
- class Children < Base
3
- def base_specificity
4
- 50
5
- end
6
- def context_from(subject)
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
@@ -0,0 +1,15 @@
1
+ module Gamefic
2
+ module Query
3
+ class Descendants < Children
4
+ def context_from(subject)
5
+ result = []
6
+ children = super
7
+ result.concat children
8
+ children.each { |c|
9
+ result.concat subquery_accessible(c)
10
+ }
11
+ result
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,10 +1,22 @@
1
- module Gamefic::Query
2
- class Family < Base
3
- def base_specificity
4
- 40
5
- end
6
- def context_from(subject)
7
- subject.children + subject.parent.children #+ [subject.parent]
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
@@ -0,0 +1,13 @@
1
+ module Gamefic
2
+ module Query
3
+ class Itself < Base
4
+ def context_from(subject)
5
+ [subject]
6
+ end
7
+
8
+ def include?(subject, object)
9
+ return false unless accept?(object) and subject == object
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,11 +1,67 @@
1
- module Gamefic::Query
2
- class Matches
3
- attr_reader :objects, :matching_text, :remainder
4
- def initialize(objects, matching_text, remainder)
5
- @objects = objects
6
- @matching_text = matching_text
7
- @remainder = remainder
8
- @@last_match = self
9
- end
10
- end
11
- end
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
@@ -1,10 +1,9 @@
1
- module Gamefic::Query
2
- class Parent < Base
3
- def base_specificity
4
- 30
5
- end
6
- def context_from(subject)
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::Query
2
- class Siblings < Base
3
- def base_specificity
4
- 40
5
- end
6
- def context_from(subject)
7
- (subject.parent.children - [subject])
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
@@ -1,43 +1,47 @@
1
- module Gamefic::Query
2
- class Text < Base
3
- def base_specificity
4
- 10
5
- end
6
- def validate(subject, description)
7
- return false unless description.kind_of?(String)
8
- valid = false
9
- words = description.split_words
10
- words.each { |word|
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
- keywords = Keywords.new(description)
23
- args = Keywords.new(@arguments)
24
- found = Array.new
25
- remainder = keywords.clone
26
- while remainder.length > 0
27
- if args.include?(remainder.first)
28
- found.push remainder.shift
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
- break
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
- if found.length > 0
34
- return Matches.new(found, found.join(' '), remainder.join(' '))
35
- else
36
- return Matches.new([], '', description)
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
@@ -1,4 +1,4 @@
1
- require 'gamefic/scene_data'
1
+ #require 'gamefic/scene_data'
2
2
 
3
3
  module Gamefic
4
4
 
@@ -5,16 +5,22 @@ module Gamefic
5
5
  # a Plot.
6
6
  #
7
7
  class Scene::Active < Scene::Base
8
- attr_reader :plot
9
-
10
- def initialize plot
11
- @plot = plot
8
+ def post_initialize
9
+ self.type = 'Active'
12
10
  end
13
11
 
14
- def finish actor, input
15
- o = actor.perform input.strip
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
@@ -4,21 +4,72 @@ module Gamefic
4
4
  # should inherit from it.
5
5
  #
6
6
  class Scene::Base
7
- def start actor
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 finish actor, input
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 prompt_for actor
17
- '>'
57
+ def prompt
58
+ @prompt ||= '>'
18
59
  end
19
60
 
20
61
  def type
21
- self.class.to_s.split('::').last
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
 
@@ -3,6 +3,9 @@ module Gamefic
3
3
  # A Conclusion ends the Plot (or the character's participation in it).
4
4
  #
5
5
  class Scene::Conclusion < Scene::Custom
6
+ def type
7
+ @type ||= 'Conclusion'
8
+ end
6
9
  end
7
10
 
8
11
  end
@@ -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