gamefic 0.2.0 → 0.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 (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
@@ -0,0 +1,25 @@
1
+ module Gamefic
2
+
3
+ class ActiveSceneManager < SceneManager
4
+ def scene_class
5
+ ActiveScene
6
+ end
7
+ def state
8
+ @state ||= "Active"
9
+ end
10
+ end
11
+
12
+ class ActiveScene < Scene
13
+ def finish actor, input
14
+ @data.input = input
15
+ if @finish.nil?
16
+ last_order = actor.perform data.input
17
+ # HACK Set the last_order here so inline performs don't set it
18
+ actor.send(:last_order=, last_order)
19
+ else
20
+ @finish.call actor, data
21
+ end
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,22 @@
1
+ module Gamefic
2
+
3
+ class ConcludedSceneManager < SceneManager
4
+ def scene_class
5
+ ConcludedScene
6
+ end
7
+ def state
8
+ @state ||= "Concluded"
9
+ end
10
+ def prompt
11
+ @prompt ||= "GAME OVER"
12
+ end
13
+ end
14
+
15
+ class ConcludedScene < Scene
16
+ # TODO: This class might need some logic for closing the game. Then again,
17
+ # maybe not. The Concluded state might be enough for the plot to know what
18
+ # to do. In fact, if the plot detects the Concluded state, it might never
19
+ # get around to calling the scene's finish proc.
20
+ end
21
+
22
+ end
@@ -0,0 +1,74 @@
1
+ module Gamefic
2
+
3
+ class MultipleChoiceSceneManager < SceneManager
4
+ def data_class
5
+ MultipleChoiceSceneData
6
+ end
7
+ def scene_class
8
+ MultipleChoiceScene
9
+ end
10
+ def state
11
+ @state ||= "MultipleChoice"
12
+ end
13
+ def prompt
14
+ @prompt ||= "Enter a choice:"
15
+ end
16
+ end
17
+
18
+ class MultipleChoiceSceneData < SceneData
19
+ attr_accessor :index, :selection, :options
20
+ def options
21
+ @options ||= []
22
+ end
23
+ end
24
+
25
+ class MultipleChoiceScene < Scene
26
+ def initialize manager, key
27
+ super
28
+ @prompt ||= "Enter a choice:"
29
+ end
30
+ def start actor
31
+ super
32
+ list = '<ol class="multiple_choice">'
33
+ @data.options.each { |o|
34
+ list += "<li>#{o}</li>"
35
+ }
36
+ list += "</ol>"
37
+ actor.tell list
38
+ @data.prompt ||= @prompt
39
+ end
40
+ def finish actor, input
41
+ @data.input = input
42
+ @data.index = nil
43
+ @data.selection = nil
44
+ if @data.input.strip =~ /[0-9]+/
45
+ if input.to_i > 0
46
+ @data.index = input.to_i - 1
47
+ @data.selection = @data.options[@data.index]
48
+ @data.index = nil if @data.selection.nil?
49
+ end
50
+ else
51
+ i = 0
52
+ @data.options.each { |o|
53
+ if o.casecmp(@data.input).zero?
54
+ @data.index = i
55
+ @data.selection = o
56
+ break
57
+ end
58
+ i += 1
59
+ }
60
+ end
61
+ if @data.selection.nil?
62
+ # TODO: Consider allowing for an error block to customize this
63
+ # response.
64
+ actor.tell "That's not a valid selection."
65
+ else
66
+ @data.next_cue ||= :active
67
+ if !@finish.nil?
68
+ @finish.call actor, @data
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,26 @@
1
+ module Gamefic
2
+
3
+ class PausedSceneManager < SceneManager
4
+ def scene_class
5
+ PausedScene
6
+ end
7
+ def data_class
8
+ PausedSceneData
9
+ end
10
+ def state
11
+ @state ||= "Paused"
12
+ end
13
+ def prompt
14
+ @prompt ||= "Press enter to continue..."
15
+ end
16
+ end
17
+
18
+ class PausedSceneData < SceneData
19
+ attr_accessor :next_cue
20
+ end
21
+
22
+ class PausedScene < Scene
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,43 @@
1
+ module Gamefic
2
+
3
+ class YesOrNoSceneManager < SceneManager
4
+ def scene_class
5
+ YesOrNoScene
6
+ end
7
+ def data_class
8
+ YesOrNoSceneData
9
+ end
10
+ def state
11
+ @state ||= "YesOrNo"
12
+ end
13
+ def prompt
14
+ @prompt ||= "Enter Yes Or No:"
15
+ end
16
+ end
17
+
18
+ class YesOrNoSceneData < SceneData
19
+ # @!attribute [rw] answer
20
+ # @return [String] The answer provided by the user, normalized to either "yes" or "no"
21
+ attr_accessor :answer
22
+ end
23
+
24
+ class YesOrNoScene < Scene
25
+ def finish actor, input
26
+ @data.input = input
27
+ # The input in a YesOrNoScene gets normalized to "yes" or "no"
28
+ @data.answer = nil
29
+ if input.downcase[0, 1] == "y"
30
+ @data.answer = "yes"
31
+ elsif input.downcase[0, 1] == "n"
32
+ @data.answer = "no"
33
+ end
34
+ if @data.answer.nil?
35
+ actor.tell "Please enter Yes or No."
36
+ else
37
+ return if @finish.nil?
38
+ @finish.call actor, data
39
+ end
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,125 @@
1
+ module Gamefic
2
+
3
+ # SceneManagers handle the creation and execution of player scenes.
4
+ #
5
+ # @example Create a scene that lets the player select a name.
6
+ # scene_managers[:get_name] = SceneManager.new do |manager|
7
+ # manager.state = "Active" # Tell the Engine that this scene accepts input
8
+ # manager.prompt = "Enter your name:"
9
+ # manager.start do |actor, data|
10
+ # actor.tell "Let's start with a formal introduction."
11
+ # end
12
+ # manager.finish do |actor, data|
13
+ # actor[:name] = data.input
14
+ # actor.tell "Howdy, #{actor[:name]}!"
15
+ # actor.next_cue = :active # Proceed to the default :active scene
16
+ # end
17
+ # end
18
+ #
19
+ class SceneManager
20
+ attr_accessor :state
21
+ attr_writer :prompt
22
+
23
+ def initialize &block
24
+ yield self if block_given?
25
+ end
26
+
27
+ # Get the SceneData class that provide data about the current event to a
28
+ # Scene instance.
29
+ #
30
+ def data_class
31
+ SceneData
32
+ end
33
+
34
+ # Get the name that describes this scene's state.
35
+ # Two common values for the state are Active and Passive. If a scene is
36
+ # Active, it is capable of accepting user input. If it is Passive, it
37
+ # is probably not interactive (e.g., a cutscene) and will usually cue
38
+ # an Active scene in order to continue gameplay.
39
+ #
40
+ # @return [String] The name of the state.
41
+ def state
42
+ @state ||= 'Passive'
43
+ end
44
+
45
+ # Get the Scene class that the SceneManager uses to prepare a scene for
46
+ # the plot.
47
+ #
48
+ def scene_class
49
+ Scene
50
+ end
51
+
52
+ # Define a Block to be executed when the scene starts. The Engine should
53
+ # execute this block before the player is queried for input.
54
+ #
55
+ # @yieldparam [Character]
56
+ # @yieldparam [SceneData]
57
+ def start &block
58
+ @start = block
59
+ end
60
+
61
+ # Define a Block to be executed when the scene finishes. The engine should
62
+ # process user input in this block.
63
+ #
64
+ # @yieldparam [Character]
65
+ # @yieldparam [SceneData]
66
+ def finish &block
67
+ @finish = block
68
+ end
69
+
70
+ # Prepare a new Scene for execution.
71
+ #
72
+ # @return [Scene]
73
+ def prepare key
74
+ scene_class.new(self, key)
75
+ end
76
+
77
+ # Get the prompt to display to the user when requesting input.
78
+ #
79
+ # @return [String]
80
+ def prompt
81
+ @prompt ||= ">"
82
+ end
83
+ end
84
+
85
+ class SceneData
86
+ attr_accessor :input, :prompt, :next_cue
87
+ end
88
+
89
+ class Scene
90
+ attr_reader :data, :state, :key
91
+
92
+ def initialize(manager, key)
93
+ @manager = manager
94
+ @start = manager.instance_variable_get(:@start)
95
+ @finish = manager.instance_variable_get(:@finish)
96
+ @state = manager.state
97
+ @data = manager.data_class.new
98
+ @data.prompt = manager.prompt
99
+ @key = key
100
+ end
101
+
102
+ # Start the scene. This method is typically called by the Plot.
103
+ #
104
+ # @param actor [Character] The Scene's Character.
105
+ def start actor
106
+ return if @start.nil?
107
+ @data.input = nil
108
+ @start.call actor, @data
109
+ end
110
+
111
+ # Finish the scene. This method is typically called by the Plot.
112
+ # The Finish method is responsible for processing any input received
113
+ # from players.
114
+ #
115
+ # @param actor [Character] The Scene's Character.
116
+ # @param input [String] Input received from the Character (e.g., a player command).
117
+ def finish actor, input
118
+ @data.next_cue ||= :active
119
+ return if @finish.nil?
120
+ @data.input = input
121
+ @finish.call actor, @data
122
+ end
123
+ end
124
+
125
+ end
@@ -0,0 +1,33 @@
1
+ module Gamefic
2
+
3
+ class Script::Base
4
+ def initialize
5
+ raise "#initialize must be defined in subclasses"
6
+ end
7
+ # Get the script's text.
8
+ # The text must be source code suitable for evaluation via Plot#stage.
9
+ #
10
+ # @return [String]
11
+ def read
12
+ raise "#read must be defined in subclasses"
13
+ end
14
+ # Get the script's path
15
+ #
16
+ # @return [String]
17
+ def path
18
+ raise "#path must be defined in subclasses"
19
+ end
20
+ # Get the absolute path of the script's original file
21
+ #
22
+ # @return [String]
23
+ def absolute_path
24
+ raise "#absolute_path must be defined in subclasses"
25
+ end
26
+ # @param other[Script::Base]
27
+ # @return [Boolean]
28
+ def==(other)
29
+ path == other.path
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,14 @@
1
+ module Gamefic
2
+
3
+ class Script::File < Script::Base
4
+ attr_reader :path, :absolute_path
5
+ def initialize filename, path
6
+ @absolute_path = filename.gsub(/\/+/, '/')
7
+ @path = path
8
+ end
9
+ def read
10
+ File.read(@absolute_path)
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,14 @@
1
+ module Gamefic
2
+
3
+ class Script::Text < Script::Base
4
+ attr_reader :path, :absolute_path
5
+ def initialize path, code, absolute_path = nil
6
+ @path = path
7
+ @code = code
8
+ @absolute_path = absolute_path || path
9
+ end
10
+ def read
11
+ @code
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ module Gamefic
2
+
3
+ module Script
4
+ autoload :Base, 'gamefic/script/base'
5
+ autoload :File, 'gamefic/script/file'
6
+ autoload :Text, 'gamefic/script/text'
7
+ end
8
+
9
+ end
@@ -0,0 +1,24 @@
1
+ module Gamefic
2
+ module Serialized
3
+ def serialized_attributes
4
+ self.class.serializer.keys
5
+ end
6
+ module ClassMethods
7
+ def serialize *args
8
+ args.each { |a|
9
+ serializer[a] = nil
10
+ }
11
+ end
12
+ def serializer
13
+ @@serialized_attributes ||= from_superclass(:serializer, {}).dup
14
+ end
15
+ private
16
+ def from_superclass(m, default = nil)
17
+ superclass.respond_to?(m) ? superclass.send(m) : default
18
+ end
19
+ end
20
+ def self.included(base)
21
+ base.extend(Gamefic::Serialized::ClassMethods)
22
+ end
23
+ end
24
+ end