gamefic 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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