gamefic 1.6.0 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +16 -0
  5. data/.solargraph.yml +5 -0
  6. data/CHANGELOG.md +6 -0
  7. data/Gemfile +7 -0
  8. data/LICENSE +20 -0
  9. data/README.md +28 -0
  10. data/Rakefile +10 -0
  11. data/gamefic.gemspec +27 -0
  12. data/lib/gamefic.rb +11 -8
  13. data/lib/gamefic/action.rb +68 -58
  14. data/lib/gamefic/active.rb +331 -0
  15. data/lib/gamefic/actor.rb +8 -0
  16. data/lib/gamefic/command.rb +9 -7
  17. data/lib/gamefic/core_ext/array.rb +27 -49
  18. data/lib/gamefic/core_ext/string.rb +25 -16
  19. data/lib/gamefic/describable.rb +37 -22
  20. data/lib/gamefic/element.rb +47 -0
  21. data/lib/gamefic/entity.rb +24 -48
  22. data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
  23. data/lib/gamefic/messaging.rb +43 -45
  24. data/lib/gamefic/node.rb +14 -5
  25. data/lib/gamefic/plot.rb +73 -85
  26. data/lib/gamefic/plot/darkroom.rb +80 -0
  27. data/lib/gamefic/plot/host.rb +42 -46
  28. data/lib/gamefic/plot/snapshot.rb +14 -214
  29. data/lib/gamefic/query.rb +15 -17
  30. data/lib/gamefic/query/base.rb +51 -42
  31. data/lib/gamefic/query/children.rb +0 -0
  32. data/lib/gamefic/query/descendants.rb +2 -2
  33. data/lib/gamefic/query/external.rb +18 -0
  34. data/lib/gamefic/query/family.rb +3 -7
  35. data/lib/gamefic/query/matches.rb +75 -67
  36. data/lib/gamefic/query/parent.rb +0 -0
  37. data/lib/gamefic/query/siblings.rb +0 -0
  38. data/lib/gamefic/query/text.rb +12 -12
  39. data/lib/gamefic/query/tree.rb +17 -0
  40. data/lib/gamefic/scene.rb +1 -5
  41. data/lib/gamefic/scene/{active.rb → activity.rb} +4 -6
  42. data/lib/gamefic/scene/base.rb +77 -13
  43. data/lib/gamefic/scene/conclusion.rb +0 -2
  44. data/lib/gamefic/scene/custom.rb +0 -2
  45. data/lib/gamefic/scene/multiple_choice.rb +18 -16
  46. data/lib/gamefic/scene/multiple_scene.rb +29 -20
  47. data/lib/gamefic/scene/pause.rb +7 -2
  48. data/lib/gamefic/scene/yes_or_no.rb +21 -9
  49. data/lib/gamefic/scriptable.rb +88 -0
  50. data/lib/gamefic/serialize.rb +223 -0
  51. data/lib/gamefic/subplot.rb +47 -51
  52. data/lib/gamefic/syntax.rb +15 -13
  53. data/lib/gamefic/version.rb +3 -3
  54. data/lib/gamefic/world.rb +18 -0
  55. data/lib/gamefic/world/callbacks.rb +135 -0
  56. data/lib/gamefic/world/commands.rb +184 -0
  57. data/lib/gamefic/world/entities.rb +98 -0
  58. data/lib/gamefic/{plot → world}/playbook.rb +245 -236
  59. data/lib/gamefic/world/players.rb +37 -0
  60. data/lib/gamefic/world/scenes.rb +226 -0
  61. metadata +40 -108
  62. data/bin/gamefic +0 -9
  63. data/lib/gamefic/character.rb +0 -232
  64. data/lib/gamefic/character/state.rb +0 -12
  65. data/lib/gamefic/engine.rb +0 -7
  66. data/lib/gamefic/engine/base.rb +0 -66
  67. data/lib/gamefic/engine/tty.rb +0 -24
  68. data/lib/gamefic/grammar.rb +0 -13
  69. data/lib/gamefic/grammar/conjugator.rb +0 -20
  70. data/lib/gamefic/grammar/gender.rb +0 -11
  71. data/lib/gamefic/grammar/person.rb +0 -10
  72. data/lib/gamefic/grammar/plural.rb +0 -13
  73. data/lib/gamefic/grammar/pronouns.rb +0 -105
  74. data/lib/gamefic/grammar/tense.rb +0 -6
  75. data/lib/gamefic/grammar/verb_set.rb +0 -43
  76. data/lib/gamefic/grammar/verbs.rb +0 -26
  77. data/lib/gamefic/grammar/word_adapter.rb +0 -49
  78. data/lib/gamefic/plot/articles.rb +0 -22
  79. data/lib/gamefic/plot/callbacks.rb +0 -127
  80. data/lib/gamefic/plot/commands.rb +0 -121
  81. data/lib/gamefic/plot/entities.rb +0 -88
  82. data/lib/gamefic/plot/players.rb +0 -15
  83. data/lib/gamefic/plot/scenes.rb +0 -149
  84. data/lib/gamefic/plot/theater.rb +0 -73
  85. data/lib/gamefic/plot/you_mount.rb +0 -22
  86. data/lib/gamefic/script.rb +0 -13
  87. data/lib/gamefic/script/base.rb +0 -42
  88. data/lib/gamefic/script/file.rb +0 -14
  89. data/lib/gamefic/script/text.rb +0 -14
  90. data/lib/gamefic/shell.rb +0 -76
  91. data/lib/gamefic/source.rb +0 -14
  92. data/lib/gamefic/source/base.rb +0 -12
  93. data/lib/gamefic/source/file.rb +0 -23
  94. data/lib/gamefic/source/text.rb +0 -16
  95. data/lib/gamefic/tester.rb +0 -19
  96. data/lib/gamefic/text.rb +0 -8
  97. data/lib/gamefic/text/ansi.rb +0 -53
  98. data/lib/gamefic/text/html.rb +0 -68
  99. data/lib/gamefic/text/html/conversions.rb +0 -250
  100. data/lib/gamefic/text/html/entities.rb +0 -9
  101. data/lib/gamefic/tty.rb +0 -10
  102. data/lib/gamefic/user.rb +0 -8
  103. data/lib/gamefic/user/base.rb +0 -15
  104. data/lib/gamefic/user/buffer.rb +0 -32
  105. data/lib/gamefic/user/tty.rb +0 -54
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'gamefic'
4
- require 'gamefic/shell'
5
-
6
- # Make play the default command if the first argument is an existing file
7
- args = %w(play help).include?(ARGV[0]) || ARGV.count.zero? || !File.exist?(ARGV[0]) ? ARGV : ARGV.dup.unshift('play')
8
-
9
- Gamefic::Shell.start(args)
@@ -1,232 +0,0 @@
1
- #require 'gamefic/director'
2
-
3
-
4
- module Gamefic
5
- class NotConclusionError < Exception
6
- end
7
-
8
- class Character < Entity
9
- autoload :State, 'gamefic/character/state'
10
-
11
- attr_reader :queue, :user
12
- # @return [Gamefic::Action]
13
- attr_reader :last_action
14
- # @return [Entity,nil]
15
- attr_reader :last_object
16
- attr_accessor :object_of_pronoun
17
- attr_reader :scene
18
- attr_reader :next_scene
19
- attr_accessor :playbook
20
-
21
- include Character::State
22
-
23
- def initialize(args = {})
24
- super
25
- @queue = Array.new
26
- @messages = ''
27
- @buffer_stack = 0
28
- @buffer = ""
29
- end
30
-
31
- # Connect a User.
32
- #
33
- # @param user [User]
34
- def connect(user)
35
- @user = user
36
- end
37
-
38
- # Disconnect the current User.
39
- #
40
- def disconnect
41
- @user = nil
42
- end
43
-
44
- # Send a message to the entity.
45
- # This method will automatically wrap the message in HTML paragraphs.
46
- # To send a message without paragraph formatting, use #stream instead.
47
- #
48
- # @param message [String]
49
- def tell(message)
50
- if @buffer_stack > 0
51
- @buffer += message
52
- else
53
- super
54
- end
55
- end
56
-
57
- # Send a message to the Character as raw text.
58
- # Unlike #tell, this method will not wrap the message in HTML paragraphs.
59
- #
60
- # @param message [String]
61
- def stream(message)
62
- if @buffer_stack > 0
63
- @buffer += message
64
- else
65
- super
66
- end
67
- end
68
-
69
- # Perform a command.
70
- # The command can be specified as a String or a set of tokens. Either form
71
- # should yield the same result, but using tokens can yield better
72
- # performance since it bypasses the parser.
73
- #
74
- # The command will be executed immediately regardless of game state.
75
- #
76
- # @example Send a command as a string
77
- # character.perform "take the key"
78
- #
79
- # @example Send a command as a set of tokens
80
- # character.perform :take, @key
81
- #
82
- def perform(*command)
83
- #Director.dispatch(self, *command)
84
- actions = playbook.dispatch(self, *command)
85
- a = actions.first
86
- okay = true
87
- unless a.meta?
88
- playbook.validators.each { |v|
89
- result = v.call(self, a.verb, a.parameters)
90
- okay = (result != false)
91
- break if not okay
92
- }
93
- end
94
- if okay
95
- performance_stack.push actions
96
- proceed
97
- performance_stack.pop
98
- end
99
- a
100
- end
101
-
102
- # Quietly perform a command.
103
- # This method executes the command exactly as #perform does, except it
104
- # buffers the resulting output instead of sending it to the user.
105
- #
106
- # @return [String] The output that resulted from performing the command.
107
- def quietly(*command)
108
- if @buffer_stack == 0
109
- @buffer = ""
110
- end
111
- @buffer_stack += 1
112
- self.perform *command
113
- @buffer_stack -= 1
114
- @buffer
115
- end
116
-
117
- # Proceed to the next Action in the current stack.
118
- # This method is typically used in Action blocks to cascade through
119
- # multiple implementations of the same verb.
120
- #
121
- # @example Proceed through two implementations of a verb
122
- # introduction do |actor|
123
- # actor[:has_eaten] = false # Initial value
124
- # end
125
- # respond :eat do |actor|
126
- # actor.tell "You eat something."
127
- # actor[:has_eaten] = true
128
- # end
129
- # respond :eat do |actor|
130
- # # This version will be executed first because it was implemented last
131
- # if actor[:has_eaten]
132
- # actor.tell "You already ate."
133
- # else
134
- # actor.proceed # Execute the previous implementation
135
- # end
136
- # end
137
- #
138
- def proceed quietly: false
139
- #Director::Delegate.proceed_for self
140
- return if performance_stack.empty?
141
- a = performance_stack.last.shift
142
- unless a.nil?
143
- if quietly
144
- if @buffer_stack == 0
145
- @buffer = ""
146
- end
147
- @buffer_stack += 1
148
- end
149
- a.execute
150
- if quietly
151
- @buffer_stack -= 1
152
- @buffer
153
- end
154
- end
155
- end
156
-
157
- # Immediately start a new scene for the character.
158
- # Use #prepare if you want to declare a scene to be started at the
159
- # beginning of the next turn.
160
- #
161
- def cue new_scene
162
- @next_scene = nil
163
- if new_scene.nil?
164
- @scene = nil
165
- else
166
- @scene = new_scene.new(self)
167
- @scene.start
168
- end
169
- end
170
-
171
- # Prepare a scene to be started for this character at the beginning of the
172
- # next turn.
173
- #
174
- def prepare s
175
- @next_scene = s
176
- end
177
-
178
- # Return true if the character is expected to be in the specified scene on
179
- # the next turn.
180
- #
181
- # @return [Boolean]
182
- def will_cue? scene
183
- (@scene.class == scene and @next_scene.nil?) or @next_scene == scene
184
- end
185
-
186
- # Cue a conclusion. This method works like #cue, except it will raise a
187
- # NotConclusionError if the scene is not a Scene::Conclusion.
188
- #
189
- def conclude scene
190
- raise NotConclusionError unless scene <= Scene::Conclusion
191
- cue scene
192
- end
193
-
194
- # True if the character is in a conclusion.
195
- #
196
- # @return [Boolean]
197
- def concluded?
198
- !scene.nil? and scene.kind_of?(Scene::Conclusion)
199
- end
200
-
201
- def performed order
202
- order.freeze
203
- @last_action = order
204
- end
205
-
206
- # Get the prompt that the user should see for the current scene.
207
- #
208
- # @return [String]
209
- #def prompt
210
- # scene.nil? ? '>' : scene.prompt
211
- #end
212
-
213
- def accessible?
214
- false
215
- end
216
-
217
- def inspect
218
- to_s
219
- end
220
-
221
- private
222
-
223
- def delegate_stack
224
- @delegate_stack ||= []
225
- end
226
-
227
- def performance_stack
228
- @performance_stack ||= []
229
- end
230
- end
231
-
232
- end
@@ -1,12 +0,0 @@
1
- module Gamefic
2
- class Character
3
- module State
4
- def state
5
- @state = {}
6
- @state.merge! scene.state unless scene.nil?
7
- @state.merge! output: messages
8
- @state
9
- end
10
- end
11
- end
12
- end
@@ -1,7 +0,0 @@
1
- module Gamefic
2
-
3
- module Engine
4
- autoload :Base, 'gamefic/engine/base'
5
- end
6
-
7
- end
@@ -1,66 +0,0 @@
1
- module Gamefic
2
-
3
- # Basic functionality for running a single-player game from a console.
4
- #
5
- class Engine::Base
6
- attr_writer :user_class
7
- attr_reader :plot
8
-
9
- def initialize(plot)
10
- @plot = plot
11
- post_initialize
12
- end
13
-
14
- def post_initialize
15
- # Override in subclasses
16
- end
17
-
18
- def user_class
19
- @user_class ||= Gamefic::User::Base
20
- end
21
-
22
- def connect
23
- @character = @plot.make Character, name: 'yourself', synonyms: 'self myself you me', proper_named: true
24
- @user = user_class.new
25
- @character.connect @user
26
- @character
27
- end
28
-
29
- def run
30
- connect
31
- @plot.introduce @character
32
- @user.update @character.state
33
- turn until @character.concluded?
34
- #print @user.flush
35
- end
36
-
37
- def turn
38
- @plot.ready
39
- unless @character.state[:options].nil?
40
- list = '<ol class="multiple_choice">'
41
- @character.state[:options].each { |o|
42
- list += "<li><a href=\"#\" rel=\"gamefic\" data-command=\"#{o}\">#{o}</a></li>"
43
- }
44
- list += "</ol>"
45
- @character.tell list
46
- end
47
- #print @user.flush
48
- @user.update @character.state
49
- #@character.flush
50
- if @character.queue.empty?
51
- receive
52
- end
53
- @plot.update
54
- #print @user.flush
55
- @user.update @character.state
56
- #@character.flush
57
- end
58
-
59
- def receive
60
- print @character.scene.prompt + ' '
61
- input = STDIN.gets
62
- @character.queue.push input unless input.nil?
63
- end
64
- end
65
-
66
- end
@@ -1,24 +0,0 @@
1
- require 'gamefic/user/tty'
2
-
3
- module Gamefic
4
-
5
- # Extend Engine::Base to connect with User::Tty, which provides ANSI
6
- # formatting for HTML.
7
- #
8
- # @note Due to their dependency on io/console, User::Tty and Engine::Tty are
9
- # not included in the core Gamefic library. `require gamefic/tty` if you
10
- # need them.
11
- #
12
- class Engine::Tty < Engine::Base
13
- def post_initialize
14
- self.user_class = Gamefic::User::Tty
15
- end
16
-
17
- def self.start plot
18
- engine = self.new(plot)
19
- engine.connect
20
- engine.run
21
- end
22
- end
23
-
24
- end
@@ -1,13 +0,0 @@
1
- module Gamefic
2
- module Grammar
3
- autoload :Tense, 'gamefic/grammar/tense'
4
- autoload :Pronouns, 'gamefic/grammar/pronouns'
5
- autoload :Conjugator, 'gamefic/grammar/conjugator'
6
- autoload :Verbs, 'gamefic/grammar/verbs'
7
- autoload :VerbSet, 'gamefic/grammar/verb_set'
8
- autoload :Person, 'gamefic/grammar/person'
9
- autoload :Plural, 'gamefic/grammar/plural'
10
- autoload :Gender, 'gamefic/grammar/gender'
11
- autoload :WordAdapter, 'gamefic/grammar/word_adapter'
12
- end
13
- end
@@ -1,20 +0,0 @@
1
- require 'gamefic'
2
- require 'gamefic/grammar'
3
-
4
- module Gamefic::Grammar
5
- module Conjugator
6
- module ClassMethods
7
- @@conjugated_verbs = {}
8
- def conjugate infinitive, tense, *forms
9
- @@conjugated_verbs[infinitive] ||= {}
10
- @@conjugated_verbs[infinitive][tense] = VerbSet.new(infinitive, *forms)
11
- end
12
- def conjugated_verbs
13
- @@conjugated_verbs
14
- end
15
- end
16
- #def self.included(base)
17
- # base.extend ClassMethods
18
- #end
19
- end
20
- end
@@ -1,11 +0,0 @@
1
- require 'gamefic/grammar'
2
-
3
- module Gamefic::Grammar
4
- module Gender
5
- attr_writer :gender
6
- def gender
7
- # Supported values are "male", "female", "other", and "neutral"
8
- @gender ||= (self.kind_of?(Character) ? "other" : "neutral")
9
- end
10
- end
11
- end
@@ -1,10 +0,0 @@
1
- require 'gamefic/grammar'
2
-
3
- module Gamefic::Grammar
4
- module Person
5
- attr_writer :person
6
- def person
7
- @person ||= 3
8
- end
9
- end
10
- end
@@ -1,13 +0,0 @@
1
- require 'gamefic/grammar'
2
-
3
- module Gamefic::Grammar
4
- module Plural
5
- attr_writer :plural
6
- def plural?
7
- if @plural.nil?
8
- @plural = false
9
- end
10
- @plural
11
- end
12
- end
13
- end
@@ -1,105 +0,0 @@
1
- require 'gamefic'
2
- require 'gamefic/grammar'
3
-
4
- module Gamefic::Grammar
5
- class Pronouns
6
- def initialize object
7
- @object = object
8
- end
9
-
10
- # Get the subjective pronoun (I, you, we, etc.)
11
- #
12
- # @return [String]
13
- def subj
14
- Pronouns.get_pronoun_set(@object)[0]
15
- end
16
-
17
- # Get the objective pronoun (me, them, us, etc.)
18
- #
19
- # @return [String]
20
- def obj
21
- Pronouns.get_pronoun_set(@object)[1]
22
- end
23
-
24
- # Get the possessive pronoun (my, your, our, etc.)
25
- #
26
- # @return [String]
27
- def poss
28
- Pronouns.get_pronoun_set(@object)[2]
29
- end
30
-
31
- # Get the reflexive pronoun (myself, yourself, etc.)
32
- #
33
- # @return [String]
34
- def reflex
35
- Pronouns.get_pronoun_set(@object)[3]
36
- end
37
-
38
- # Get the capitalized subjective pronoun
39
- #
40
- # @return [String]
41
- def Subj
42
- subj.cap_first
43
- end
44
-
45
- # Get the capitalized objective pronoun
46
- #
47
- # @return [String]
48
- def Obj
49
- obj.cap_first
50
- end
51
-
52
- # Get the capitalized possessive pronoun
53
- #
54
- # @return [String]
55
- def Poss
56
- obj.cap_first
57
- end
58
-
59
- # Get the capitalized reflexive pronoun
60
- #
61
- # @return [String]
62
- def Reflex
63
- reflex.cap_first
64
- end
65
-
66
- # Get a an array of pronouns based on the person, gender, and plurality of
67
- # the provided object.
68
- #
69
- # @param obj An object that responds to #person, #gender, and #plural
70
- # @return [Array<String>]
71
- def self.get_pronoun_set(obj)
72
- set = Pronouns.sets["#{obj.person}"]
73
- if set.nil?
74
- set = Pronouns.sets["#{obj.person}:#{obj.plural? ? 'plural' : 'singular'}"]
75
- end
76
- if set.nil?
77
- set = Pronouns.sets["#{obj.person}:#{obj.plural? ? 'plural' : 'singular'}:#{obj.gender}"]
78
- end
79
- if set.nil?
80
- raise "Pronoun set could not be determined"
81
- end
82
- set
83
- end
84
-
85
- # TODO Consider implementing method_missing to determine correct pronoun
86
- # from example, e.g., "he" would change to "she" for female entities
87
- def self.sets
88
- if @sets.nil?
89
- @sets = {}
90
- @sets["1:singular"] = ["I", "me", "my", "myself"]
91
- @sets["2"] = ["you", "you", "your", "yourself"]
92
- @sets["3:singular:male"] = ["he", "him", "his", "himself"]
93
- @sets["3:singular:female"] = ["she", "her", "her", "herself"]
94
- # "other" refers to a person or living being that is neither
95
- # male or female or for whom gender is unspecified. It's
96
- # typically used to avoid referring to a person as "it."
97
- @sets["3:singular:other"] = ["they", "them", "their", "themselves"]
98
- @sets["3:singular:neutral"] = ["it", "it", "its", "itself"]
99
- @sets["1:plural"] = ["we", "us", "our", "ourselves"]
100
- @sets["3:plural"] = ["they", "them", "their", "themselves"]
101
- end
102
- @sets
103
- end
104
- end
105
- end