gamefic 0.2.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gamefic/action.rb +87 -56
  3. data/lib/gamefic/ansi.rb +55 -0
  4. data/lib/gamefic/character.rb +130 -76
  5. data/lib/gamefic/command.rb +19 -0
  6. data/lib/gamefic/core_ext/array.rb +51 -40
  7. data/lib/gamefic/core_ext/string.rb +4 -0
  8. data/lib/gamefic/describable.rb +108 -46
  9. data/lib/gamefic/direction.rb +46 -0
  10. data/lib/gamefic/director/delegate.rb +91 -0
  11. data/lib/gamefic/director/order.rb +10 -0
  12. data/lib/gamefic/director/parser.rb +119 -0
  13. data/lib/gamefic/director.rb +16 -197
  14. data/lib/gamefic/engine/cgi.rb +221 -0
  15. data/lib/gamefic/engine/tty.rb +237 -0
  16. data/lib/gamefic/engine.rb +88 -67
  17. data/lib/gamefic/entity.rb +96 -69
  18. data/lib/gamefic/grammar/conjugator.rb +20 -0
  19. data/lib/gamefic/grammar/gender.rb +11 -0
  20. data/lib/gamefic/grammar/person.rb +10 -0
  21. data/lib/gamefic/grammar/plural.rb +13 -0
  22. data/lib/gamefic/grammar/pronouns.rb +60 -0
  23. data/lib/gamefic/grammar/tense.rb +6 -0
  24. data/lib/gamefic/grammar/verb_set.rb +43 -0
  25. data/lib/gamefic/grammar/verbs.rb +25 -0
  26. data/lib/gamefic/grammar/word_adapter.rb +36 -0
  27. data/lib/gamefic/grammar.rb +13 -0
  28. data/lib/gamefic/html.rb +53 -0
  29. data/lib/gamefic/keywords.rb +51 -33
  30. data/lib/gamefic/node.rb +65 -58
  31. data/lib/gamefic/plot/article_mount.rb +22 -0
  32. data/lib/gamefic/plot/command_mount.rb +88 -0
  33. data/lib/gamefic/plot/entity_mount.rb +45 -0
  34. data/lib/gamefic/plot/query_mount.rb +9 -0
  35. data/lib/gamefic/plot/scene_mount.rb +181 -0
  36. data/lib/gamefic/plot/you_mount.rb +22 -0
  37. data/lib/gamefic/plot.rb +296 -247
  38. data/lib/gamefic/query/ambiguous_children.rb +5 -0
  39. data/lib/gamefic/query/base.rb +265 -0
  40. data/lib/gamefic/query/children.rb +10 -0
  41. data/lib/gamefic/query/expression.rb +47 -0
  42. data/lib/gamefic/query/family.rb +10 -0
  43. data/lib/gamefic/query/many_children.rb +7 -0
  44. data/lib/gamefic/query/matches.rb +11 -0
  45. data/lib/gamefic/query/parent.rb +10 -0
  46. data/lib/gamefic/query/plural_children.rb +14 -0
  47. data/lib/gamefic/query/self.rb +10 -0
  48. data/lib/gamefic/query/siblings.rb +10 -0
  49. data/lib/gamefic/query/text.rb +43 -0
  50. data/lib/gamefic/query.rb +19 -203
  51. data/lib/gamefic/rule.rb +18 -0
  52. data/lib/gamefic/scene/active.rb +25 -0
  53. data/lib/gamefic/scene/concluded.rb +22 -0
  54. data/lib/gamefic/scene/multiplechoice.rb +74 -0
  55. data/lib/gamefic/scene/paused.rb +26 -0
  56. data/lib/gamefic/scene/yesorno.rb +43 -0
  57. data/lib/gamefic/scene.rb +125 -0
  58. data/lib/gamefic/script/base.rb +33 -0
  59. data/lib/gamefic/script/file.rb +14 -0
  60. data/lib/gamefic/script/text.rb +14 -0
  61. data/lib/gamefic/script.rb +9 -0
  62. data/lib/gamefic/serialized.rb +24 -0
  63. data/lib/gamefic/shell.rb +9 -247
  64. data/lib/gamefic/snapshots.rb +134 -0
  65. data/lib/gamefic/source/base.rb +12 -0
  66. data/lib/gamefic/source/file.rb +23 -0
  67. data/lib/gamefic/source/text.rb +16 -0
  68. data/lib/gamefic/source.rb +9 -0
  69. data/lib/gamefic/stage.rb +75 -0
  70. data/lib/gamefic/syntax.rb +106 -124
  71. data/lib/gamefic/tester.rb +20 -0
  72. data/lib/gamefic/version.rb +3 -0
  73. data/lib/gamefic.rb +18 -12
  74. metadata +102 -70
  75. data/lib/gamefic/base.rb +0 -10
  76. data/lib/gamefic/before.rb +0 -12
  77. data/lib/gamefic/import/basics/actions/close.rb +0 -16
  78. data/lib/gamefic/import/basics/actions/commands.rb +0 -3
  79. data/lib/gamefic/import/basics/actions/drop-in.rb +0 -17
  80. data/lib/gamefic/import/basics/actions/drop-on.rb +0 -16
  81. data/lib/gamefic/import/basics/actions/drop.rb +0 -30
  82. data/lib/gamefic/import/basics/actions/enter.rb +0 -16
  83. data/lib/gamefic/import/basics/actions/go.rb +0 -35
  84. data/lib/gamefic/import/basics/actions/inventory.rb +0 -8
  85. data/lib/gamefic/import/basics/actions/leave.rb +0 -29
  86. data/lib/gamefic/import/basics/actions/look-in-at.rb +0 -27
  87. data/lib/gamefic/import/basics/actions/look-under.rb +0 -3
  88. data/lib/gamefic/import/basics/actions/look.rb +0 -71
  89. data/lib/gamefic/import/basics/actions/nil.rb +0 -25
  90. data/lib/gamefic/import/basics/actions/open.rb +0 -23
  91. data/lib/gamefic/import/basics/actions/quit.rb +0 -3
  92. data/lib/gamefic/import/basics/actions/take.rb +0 -107
  93. data/lib/gamefic/import/basics/entities/container.rb +0 -8
  94. data/lib/gamefic/import/basics/entities/entity.rb +0 -11
  95. data/lib/gamefic/import/basics/entities/fixture.rb +0 -5
  96. data/lib/gamefic/import/basics/entities/item.rb +0 -5
  97. data/lib/gamefic/import/basics/entities/portal.rb +0 -40
  98. data/lib/gamefic/import/basics/entities/room.rb +0 -30
  99. data/lib/gamefic/import/basics/entities/scenery.rb +0 -5
  100. data/lib/gamefic/import/basics/entities/supporter.rb +0 -6
  101. data/lib/gamefic/import/basics/entities/thing.rb +0 -16
  102. data/lib/gamefic/import/basics/queries/reachable.rb +0 -38
  103. data/lib/gamefic/import/basics/queries/room.rb +0 -8
  104. data/lib/gamefic/import/basics/queries/visible.rb +0 -32
  105. data/lib/gamefic/import/basics/rules/has-enough-light.rb +0 -14
  106. data/lib/gamefic/import/basics.old/actions/container.rb +0 -112
  107. data/lib/gamefic/import/basics.old/actions/inventory.rb +0 -50
  108. data/lib/gamefic/import/basics.old/actions/look.rb +0 -53
  109. data/lib/gamefic/import/basics.old/actions/meta.rb +0 -6
  110. data/lib/gamefic/import/basics.old/actions/traversal.rb +0 -35
  111. data/lib/gamefic/import/basics.old/actions.rb +0 -1
  112. data/lib/gamefic/import/basics.old/entities/container.rb +0 -3
  113. data/lib/gamefic/import/basics.old/entities/fixture.rb +0 -3
  114. data/lib/gamefic/import/basics.old/entities/item.rb +0 -3
  115. data/lib/gamefic/import/basics.old/entities/portal.rb +0 -43
  116. data/lib/gamefic/import/basics.old/entities/room.rb +0 -27
  117. data/lib/gamefic/import/basics.old/entities/scenery.rb +0 -3
  118. data/lib/gamefic/import/basics.old/entities/supporter.rb +0 -3
  119. data/lib/gamefic/import/basics.old/entities.rb +0 -1
  120. data/lib/gamefic/import/basics.old/room_modes.rb +0 -48
  121. data/lib/gamefic/import/basics.rb +0 -6
  122. data/lib/gamefic/import/room_modes.rb +0 -48
  123. data/lib/gamefic/import/standard.rb +0 -1
  124. data/lib/gamefic/meta.rb +0 -12
  125. data/lib/gamefic/optionset.rb +0 -114
  126. data/lib/gamefic/requirement.rb +0 -14
  127. data/lib/gamefic/story.rb +0 -14
  128. data/lib/gamefic/thing.rb +0 -7
data/lib/gamefic/node.rb CHANGED
@@ -1,62 +1,69 @@
1
+ # Exception raised when setting a node's parent would cause
2
+ # a circular reference, e.g., A -> A or A -> B -> A
3
+ class CircularNodeReferenceError < Exception
4
+ end
5
+
1
6
  module Gamefic
2
7
 
3
- module Node
4
- def children
5
- if @children == nil
6
- @children = Array.new
7
- end
8
- @children.clone
9
- end
10
- def flatten
11
- array = Array.new
12
- children.each { |child|
13
- array = array + recurse_flatten(child)
14
- }
15
- return array
16
- end
17
- protected
18
- def add_child(node)
19
- children
20
- @children.push(node)
21
- end
22
- def rem_child(node)
23
- children
24
- @children.delete(node)
25
- end
26
- def concat_children(children)
27
- children
28
- @children.concat(children)
29
- end
30
- private
31
- def recurse_flatten(node)
32
- array = Array.new
33
- array.push(node)
34
- node.children.each { |child|
35
- array = array + recurse_flatten(child)
36
- }
37
- return array
38
- end
39
- end
40
-
41
- module Branch
42
- include Node
43
- def parent
44
- @parent
45
- end
46
- def parent=(node)
47
- if node == self
48
- raise "Entity cannot be its own parent"
49
- end
50
- if @parent != node
51
- if @parent != nil
52
- @parent.send(:rem_child, self)
53
- end
54
- @parent = node
55
- if @parent != nil
56
- @parent.send(:add_child, self)
57
- end
58
- end
59
- end
60
- end
8
+ module Node
9
+ # @return [Array]
10
+ def children
11
+ @children ||= []
12
+ @children.clone
13
+ end
14
+ def flatten
15
+ array = Array.new
16
+ children.each { |child|
17
+ array = array + recurse_flatten(child)
18
+ }
19
+ return array
20
+ end
21
+ def parent
22
+ @parent
23
+ end
24
+ def parent=(node)
25
+ return if node == @parent
26
+ if node == self
27
+ raise CircularNodeReferenceError.new("Node cannot be its own parent")
28
+ end
29
+ # Do not permit circular references
30
+ if node != nil and node.parent == self
31
+ node.parent = nil
32
+ end
33
+ if node != nil and flatten.include?(node)
34
+ raise CircularNodeReferenceError.new("Node cannot be a child of a descendant")
35
+ end
36
+ if @parent != node
37
+ if @parent != nil
38
+ @parent.send(:rem_child, self)
39
+ end
40
+ @parent = node
41
+ if @parent != nil
42
+ @parent.send(:add_child, self)
43
+ end
44
+ end
45
+ end
46
+ protected
47
+ def add_child(node)
48
+ children
49
+ @children.push(node)
50
+ end
51
+ def rem_child(node)
52
+ children
53
+ @children.delete(node)
54
+ end
55
+ def concat_children(children)
56
+ children.concat(children)
57
+ end
58
+ private
59
+ def recurse_flatten(node)
60
+ array = Array.new
61
+ array.push(node)
62
+ node.children.each { |child|
63
+ array = array + recurse_flatten(child)
64
+ }
65
+ return array
66
+ end
67
+ end
61
68
 
62
69
  end
@@ -0,0 +1,22 @@
1
+ module Gamefic
2
+ module ArticleMount
3
+ def a(entity)
4
+ entity.indefinitely
5
+ end
6
+ def an(entity)
7
+ entity.indefinitely
8
+ end
9
+ def the(entity)
10
+ entity.definitely
11
+ end
12
+ def A(entity)
13
+ entity.indefinitely.cap_first
14
+ end
15
+ def An(entity)
16
+ entity.indefinitely.cap_first
17
+ end
18
+ def The(entity)
19
+ entity.definitely.cap_first
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,88 @@
1
+ require 'gamefic/action'
2
+
3
+ module Gamefic
4
+
5
+ module Plot::CommandMount
6
+ # Create a Meta Action that responds to a command.
7
+ # Meta Actions are very similar to standard Actions, except the Plot
8
+ # understands them to be commands that operate above and/or outside of the
9
+ # actual game world. Examples of Meta Actions are commands that report the
10
+ # player's current score, save and restore saved games, or list the game's
11
+ # credits.
12
+ #
13
+ # @example A simple Meta Action
14
+ # meta :credits do |actor|
15
+ # actor.tell "This game was written by John Smith."
16
+ # end
17
+ #
18
+ # @param command [Symbol] An imperative verb for the command
19
+ # @param *queries [Array<Query::Base>] Queries to filter the command's tokens
20
+ # @yieldparam [Character]
21
+ def meta(command, *queries, &proc)
22
+ act = self.action(command, *queries, &proc)
23
+ act.meta = true
24
+ act
25
+ end
26
+ def action(command, *queries, &proc)
27
+ Action.new(self, command, *queries, &proc)
28
+ end
29
+ # Create an Action that responds to a command.
30
+ # An Action uses the command argument to identify the imperative verb that
31
+ # triggers the action.
32
+ # It can also accept queries to tokenize the remainder of the input and
33
+ # filter for particular entities or properties.
34
+ # The block argument contains the code to be executed when the input
35
+ # matches all of the Action's criteria (i.e., verb and queries).
36
+ #
37
+ # @example A simple Action.
38
+ # respond :salute do |actor|
39
+ # actor.tell "Hello, sir!"
40
+ # end
41
+ # # The command "salute" will respond "Hello, sir!"
42
+ #
43
+ # @example An Action that accepts a Character
44
+ # respond :salute, Use.visible(Character) do |actor, character|
45
+ # actor.tell "#{The character} returns your salute."
46
+ # end
47
+ #
48
+ # @param command [Symbol] An imperative verb for the command
49
+ # @param *queries [Array<Query::Base>] Queries to filter the command's tokens
50
+ # @yieldparam [Character]
51
+ def respond(command, *queries, &proc)
52
+ self.action(command, *queries, &proc)
53
+ end
54
+ # Create an alternate Syntax for an Action.
55
+ # The command and its translation can be parameterized.
56
+ #
57
+ # @example Create a synonym for the Inventory Action.
58
+ # interpret "catalogue", "inventory"
59
+ # # The command "catalogue" will be translated to "inventory"
60
+ #
61
+ # @example Create a parameterized synonym for the Look Action.
62
+ # interpret "scrutinize :entity", "look :entity"
63
+ # # The command "scrutinize chair" will be translated to "look chair"
64
+ #
65
+ # @param command [String] The format of the original command
66
+ # @param translation [String] The format of the translated command
67
+ # @return [Syntax] the Syntax object
68
+ def interpret command, translation
69
+ xlate command, translation
70
+ end
71
+ def syntax(*args)
72
+ xlate(*args)
73
+ end
74
+ def xlate(*args)
75
+ syn = Syntax.new(self, *args)
76
+ syn
77
+ end
78
+ def commandwords
79
+ words = Array.new
80
+ syntaxes.each { |s|
81
+ word = s.first_word
82
+ words.push(word) if !word.nil?
83
+ }
84
+ words.uniq
85
+ end
86
+ end
87
+
88
+ end
@@ -0,0 +1,45 @@
1
+ module Gamefic
2
+
3
+ module Plot::EntityMount
4
+ # Make a new Entity with the provided properties.
5
+ #
6
+ # @example Create an Entity
7
+ # chair = make Entity, :name => 'red chair'
8
+ # chair.name #=> 'red chair'
9
+ #
10
+ # @param cls [Class] The Class of the Entity to be created.
11
+ # @param args [Hash] The entity's properties.
12
+ # @return The Entity instance.
13
+ def make(cls, args = {}, &block)
14
+ ent = cls.new(self, args, &block)
15
+ if ent.kind_of?(Entity) == false
16
+ raise "Invalid entity class"
17
+ end
18
+ ent
19
+ end
20
+ # Pick an entity based on its description.
21
+ # The description provided must match exactly one entity; otherwise
22
+ # an error is raised.
23
+ #
24
+ # @example Select the Entity that matches the description
25
+ # red_chair = make Entity, :name => 'red chair'
26
+ # blue_chair make Entity, :name => 'blue chair'
27
+ # pick "red chair" #=> red_chair
28
+ # pick "blue chair" #=> blue_chair
29
+ # pick "chair" #=> IndexError: description is ambiguous
30
+ #
31
+ # @param @description [String] The description of the entity
32
+ # @return [Entity] The entity that matches the description
33
+ def pick(description)
34
+ query = Gamefic::Query::Base.new
35
+ result = query.match(description, entities)
36
+ if result.objects.length == 0
37
+ raise IndexError.new("Unable to find entity from '#{description}'")
38
+ elsif result.objects.length > 1
39
+ raise IndexError.new("Ambiguous entities found from '#{description}'")
40
+ end
41
+ result.objects[0]
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1,9 @@
1
+ module Gamefic
2
+ module Plot::QueryMount
3
+ end
4
+ module Query
5
+ def self.siblings *arguments
6
+ Siblings.new *arguments
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,181 @@
1
+ class NotConclusionError < Exception
2
+ end
3
+
4
+ module Gamefic
5
+
6
+ module Plot::SceneMount
7
+ # Get a Hash of SceneManager objects.
8
+ #
9
+ # @return [Hash<Symbol, SceneManager>]
10
+ def scene_managers
11
+ if @scene_managers.nil?
12
+ @scene_managers ||= {}
13
+ @scene_managers[:active] = ActiveSceneManager.new
14
+ @scene_managers[:concluded] = ConcludedSceneManager.new
15
+ end
16
+ @scene_managers
17
+ end
18
+
19
+ # Create a multiple-choice scene.
20
+ # The user will be required to make a valid choice to continue
21
+ #
22
+ # @yieldparam [Character]
23
+ # @yieldparam [MultipleChoiceSceneData]
24
+ def multiple_choice key, *args, &block
25
+ scene_managers[key] = MultipleChoiceSceneManager.new do |config|
26
+ config.start do |actor, data|
27
+ data.options = args
28
+ end
29
+ config.finish(&block)
30
+ end
31
+ end
32
+
33
+ # Create a yes-or-no scene.
34
+ # The user will be required to answer Yes or No to continue.
35
+ #
36
+ # @yieldparam [Character]
37
+ # @yieldparam [YesOrNoSceneData]
38
+ def yes_or_no key, prompt = nil, &block
39
+ manager = YesOrNoSceneManager.new do |config|
40
+ config.prompt = prompt
41
+ config.finish do |actor, data|
42
+ if data.answer.nil?
43
+ actor.tell "Please answer Yes or No."
44
+ else
45
+ data.next_cue ||= :active
46
+ block.call(actor, data)
47
+ end
48
+ end
49
+ end
50
+ scene_managers[key] = manager
51
+ end
52
+
53
+ # Create a scene with a prompt.
54
+ # This scene will use the provided block to process arbitrary input
55
+ # from the user.
56
+ #
57
+ # @param key [Symbol] A unique name for the scene.
58
+ # @param prompt [String] The prompt message to display to the user.
59
+ # @yieldparam [Character]
60
+ # @yieldparam [SceneData]
61
+ def prompt key, prompt, &block
62
+ scene_managers[key] = SceneManager.new do |config|
63
+ config.prompt = prompt
64
+ config.finish do |actor, data|
65
+ data.next_cue ||= :active
66
+ block.call actor, data
67
+ end
68
+ end
69
+ scene_managers[key].state = "Prompted"
70
+ end
71
+
72
+ # Create a scene that pauses the game.
73
+ # This scene will execute the specified block and wait for input
74
+ # from the user (e.g., pressing Enter) to continue.
75
+ #
76
+ # @param key [Symbol] A unique name for the scene.
77
+ # @yieldparam [Character]
78
+ # @yieldparam [SceneData]
79
+ def pause key, &block
80
+ manager = PausedSceneManager.new do |config|
81
+ config.start do |actor, data|
82
+ data.next_cue = :active
83
+ block.call actor, data
84
+ end
85
+ end
86
+ scene_managers[key] = manager
87
+ end
88
+
89
+ # Create a conclusion.
90
+ # The game will end after this scene is complete.
91
+ #
92
+ # @param key [Symbol] A unique name for the scene.
93
+ # @yieldparam [Character]
94
+ # @yieldparam [SceneData]
95
+ def conclusion key, &block
96
+ manager = ConcludedSceneManager.new do |config|
97
+ config.start(&block)
98
+ end
99
+ scene_managers[key] = manager
100
+ end
101
+
102
+ # Create a generic scene.
103
+ # After the scene is complete, it will automatically start the next cue.
104
+ #
105
+ # @param [Symbol] A unique name for the scene.
106
+ # @yieldparam [Character]
107
+ # @yieldparam [SceneData]
108
+ def scene key, &block
109
+ scene = SceneManager.new do |manager|
110
+ manager.start do |actor, data|
111
+ data.next_cue = :active
112
+ block.call(actor, data) if !block.nil?
113
+ cue actor, data.next_cue
114
+ actor.scene.start actor
115
+ end
116
+ # Since generic scenes always cue a new scene, there's no reason to
117
+ # define a finish block.
118
+ end
119
+ scene_managers[key] = scene
120
+ end
121
+
122
+ # Branch to a new scene based on a list of options.
123
+ # This is a specialized type of multiple-choice scene that determines
124
+ # which scene to cue based on a Hash of choices and scene keys.
125
+ #
126
+ # @example Select a scene
127
+ # branch :select_one_or_two, { "one" => :scene_one, "two" => :scene_two }
128
+ # scene :scene_one do |actor|
129
+ # actor.tell "You went to scene one"
130
+ # end
131
+ # scene :scene_two do |actor|
132
+ # actor.tell "You went to scene two"
133
+ # end
134
+ # introduction do |actor|
135
+ # cue actor, :select_one_or_two # The actor will be prompted to select "one" or "two" and get sent to the corresponding scene
136
+ # end
137
+ #
138
+ # @param key [Symbol] A unique name for the scene.
139
+ # @param options [Hash] A Hash of options and associated scene keys.
140
+ def branch key, options
141
+ multiple_choice key, *options.keys do |actor, data|
142
+ cue actor, options[data.selection]
143
+ end
144
+ end
145
+
146
+ # Cue a Character's next scene
147
+ #
148
+ # @param actor [Character] The character being cued
149
+ # @param key [Symbol] The name of the scene
150
+ def cue actor, key
151
+ if !actor.scene.nil? and actor.scene.state == "Concluded"
152
+ return
153
+ end
154
+ if key.nil?
155
+ raise "Cueing scene with nil key"
156
+ end
157
+ manager = scene_managers[key]
158
+ if manager.nil?
159
+ raise "No '#{key}' scene found"
160
+ else
161
+ actor.scene = manager.prepare key
162
+ end
163
+ @scene
164
+ end
165
+
166
+ # This is functionally identical to #cue, but it also raises an
167
+ # exception if the selected scene is not a Concluded state.
168
+ #
169
+ # @param actor [Character] The character being cued
170
+ # @param key [Symbol] The name of the scene
171
+ def conclude actor, key
172
+ key = :concluded if key.nil?
173
+ manager = scene_managers[key]
174
+ if manager.state != "Concluded"
175
+ raise NotConclusionError("Cued scene '#{key}' is not a conclusion")
176
+ end
177
+ cue actor, key
178
+ end
179
+ end
180
+
181
+ end
@@ -0,0 +1,22 @@
1
+ require 'gamefic'
2
+ require 'gamefic/grammar'
3
+
4
+ module Gamefic
5
+ module YouMount
6
+ class YouGrammarSet
7
+ include Grammar::Gender
8
+ include Grammar::Person
9
+ include Grammar::Plural
10
+ include Grammar::WordAdapter
11
+ end
12
+ # @return [YouGrammarSet]
13
+ def you
14
+ if @you.nil?
15
+ @you = YouGrammarSet.new
16
+ @you.person = 2
17
+ end
18
+ @you
19
+ end
20
+ end
21
+
22
+ end