story-teller 1.1.3

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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +623 -0
  3. data/README.md +188 -0
  4. data/Rakefile +58 -0
  5. data/config/database.yml +37 -0
  6. data/exe/inform.rb +6 -0
  7. data/game/config.yml +5 -0
  8. data/game/example.inf +90 -0
  9. data/game/example.rb +105 -0
  10. data/game/forms/example_form.rb +2 -0
  11. data/game/grammar/admin.inf.rb +185 -0
  12. data/game/grammar/builder.inf.rb +310 -0
  13. data/game/grammar/game_grammar.inf.rb +6 -0
  14. data/game/grammar/meta.inf.rb +41 -0
  15. data/game/languages/english.rb +571 -0
  16. data/game/models/example_model.rb +2 -0
  17. data/game/modules/example_module.rb +9 -0
  18. data/game/modules/parser_extensions.rb +264 -0
  19. data/game/rules/example_state.rb +2 -0
  20. data/game/scripts/example_script.rb +2 -0
  21. data/game/topics/example_topic.rb +2 -0
  22. data/game/verbs/game_verbs.rb +35 -0
  23. data/game/verbs/metaverbs.rb +2066 -0
  24. data/lib/story_teller/application.rb +82 -0
  25. data/lib/story_teller/cli.rb +35 -0
  26. data/lib/story_teller/color.rb +144 -0
  27. data/lib/story_teller/config.rb +61 -0
  28. data/lib/story_teller/curses_adapter.rb +30 -0
  29. data/lib/story_teller/database.rb +527 -0
  30. data/lib/story_teller/game/loader.rb +276 -0
  31. data/lib/story_teller/game.rb +22 -0
  32. data/lib/story_teller/inform/models.rb +42 -0
  33. data/lib/story_teller/inform/relational/link.rb +239 -0
  34. data/lib/story_teller/inform/relational/module.rb +203 -0
  35. data/lib/story_teller/inform/relational/object.rb +546 -0
  36. data/lib/story_teller/inform/relational/tag.rb +152 -0
  37. data/lib/story_teller/options.rb +151 -0
  38. data/lib/story_teller/persistence.rb +340 -0
  39. data/lib/story_teller/player_character.rb +99 -0
  40. data/lib/story_teller/privileges.rb +55 -0
  41. data/lib/story_teller/runtime.rb +381 -0
  42. data/lib/story_teller/snapshots.rb +412 -0
  43. data/lib/story_teller/terminal.rb +58 -0
  44. data/lib/story_teller/version.rb +24 -0
  45. data/lib/story_teller_cli.rb +34 -0
  46. metadata +158 -0
@@ -0,0 +1,412 @@
1
+ # lib/story_teller/snapshots.rb
2
+ # encoding: utf-8
3
+ # frozen_string_literal: false
4
+
5
+ # Copyright Nels Nelson 2008-2025 but freely usable (see license)
6
+ #
7
+ # This file is part of the StoryTeller.
8
+ #
9
+ # The StoryTeller is free software: you can redistribute it and/or
10
+ # modify it under the terms of the GNU General Public License as published
11
+ # by the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # The StoryTeller is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with the StoryTeller. If not, see <http://www.gnu.org/licenses/>.
21
+
22
+ require 'json'
23
+ require 'zlib'
24
+ require 'time'
25
+ require 'date'
26
+ require 'base64'
27
+
28
+ begin
29
+ # Helps Postgres JSON/JSONB accept Hash/Array inserts in many setups.
30
+ require 'sequel/extensions/pg_json'
31
+ rescue LoadError
32
+ nil
33
+ end
34
+
35
+ # The StoryTeller module
36
+ module StoryTeller
37
+ # The Snapshots module
38
+ module Snapshots
39
+ GzipMagicBytes = [0x1F, 0x8B].freeze
40
+
41
+ # Adjust exclusions as needed
42
+ TableExclusions = %i[
43
+ schema_migrations
44
+ ar_internal_metadata
45
+ sequel_migrations
46
+ ].freeze
47
+
48
+ # Snapshot JSON schema:
49
+ # {
50
+ # version: 1,
51
+ # created_at: "ISO8601",
52
+ # tables: [
53
+ # {
54
+ # name: "object",
55
+ # primary_key: "id",
56
+ # self_foreign_key: "parent_id", # optional
57
+ # rows: [ { col: val, ... }, ... ]
58
+ # },
59
+ # ...
60
+ # ]
61
+ # }
62
+
63
+ module_function
64
+
65
+ def gzipped?(filename)
66
+ File.binread(filename, 2).bytes == GzipMagicBytes
67
+ end
68
+
69
+ def read_snapshot_file(filename)
70
+ data = if gzipped?(filename)
71
+ Zlib::GzipReader.open(filename, &:read)
72
+ else
73
+ File.read(filename)
74
+ end
75
+
76
+ JSON.parse(data, symbolize_names: true)
77
+ end
78
+
79
+ def world_tables(db)
80
+ db.tables.reject { |t| TableExclusions.include?(t) }
81
+ end
82
+
83
+ def ordered_world_tables(db, only_tables: nil)
84
+ tables = only_tables ? only_tables.map(&:to_sym) : world_tables(db)
85
+ deps = table_dependencies(db, tables)
86
+ topo_sort_tables(tables, deps)
87
+ end
88
+
89
+ # rubocop: disable Metrics/AbcSize
90
+ # rubocop: disable Metrics/CyclomaticComplexity
91
+ # rubocop: disable Metrics/MethodLength
92
+ # rubocop: disable Metrics/PerceivedComplexity
93
+ def table_dependencies(db, tables)
94
+ table_set = tables.to_h { |t| [t, true] }
95
+ deps = {}
96
+ tables.each { |t| deps[t] = {} }
97
+
98
+ tables.each do |table|
99
+ fks = safe_foreign_key_list(db, table)
100
+ fks.each do |fk|
101
+ parent = fk[:table]&.to_sym
102
+ next unless parent
103
+ next unless table_set[parent]
104
+ next if parent == table # self-FK handled at row ordering level
105
+
106
+ deps[table][parent] = true
107
+ end
108
+ end
109
+
110
+ deps
111
+ end
112
+ # rubocop: enable Metrics/AbcSize
113
+ # rubocop: enable Metrics/CyclomaticComplexity
114
+ # rubocop: enable Metrics/MethodLength
115
+ # rubocop: enable Metrics/PerceivedComplexity
116
+
117
+ # rubocop: disable Metrics/AbcSize
118
+ # rubocop: disable Metrics/CyclomaticComplexity
119
+ # rubocop: disable Metrics/MethodLength
120
+ # rubocop: disable Metrics/PerceivedComplexity
121
+ def topo_sort_tables(tables, deps)
122
+ remaining = tables.to_h { |t| [t, true] }
123
+ incoming = deps.transform_values(&:dup)
124
+
125
+ ordered = []
126
+ loop do
127
+ roots = remaining.keys.select { |t| incoming[t].empty? }.sort_by(&:to_s)
128
+ break if roots.empty?
129
+
130
+ roots.each do |root|
131
+ ordered << root
132
+ remaining.delete(root)
133
+ incoming.delete(root)
134
+ incoming.each_value { |parents| parents.delete(root) }
135
+ end
136
+ end
137
+
138
+ return ordered if remaining.empty?
139
+
140
+ cycle = remaining.keys.sort_by(&:to_s)
141
+ raise "Foreign key table dependency cycle detected among: #{cycle.join(', ')}"
142
+ end
143
+ # rubocop: enable Metrics/AbcSize
144
+ # rubocop: enable Metrics/CyclomaticComplexity
145
+ # rubocop: enable Metrics/MethodLength
146
+ # rubocop: enable Metrics/PerceivedComplexity
147
+
148
+ def safe_foreign_key_list(db, table)
149
+ db.foreign_key_list(table)
150
+ rescue Sequel::NotImplemented
151
+ []
152
+ end
153
+
154
+ def primary_key_for_table(db, table)
155
+ pk = db.primary_key(table)
156
+ return pk if pk.is_a?(Symbol)
157
+
158
+ # Fallback: detect first PK column from schema
159
+ schema = db.schema(table)
160
+ pk_cols = schema.map { |name, info| info[:primary_key] ? name : nil }.compact
161
+ pk_cols.length == 1 ? pk_cols.first : nil
162
+ end
163
+
164
+ def self_fk_column(db, table)
165
+ fks = safe_foreign_key_list(db, table)
166
+ self_fk = fks.find { |fk| fk[:table].to_sym == table.to_sym }
167
+ return nil unless self_fk
168
+
169
+ cols = Array(self_fk[:columns])
170
+ cols.length == 1 ? cols.first : nil
171
+ end
172
+
173
+ # rubocop: disable Metrics/MethodLength
174
+ def normalize_value(value)
175
+ case value
176
+ when Time
177
+ value.utc.iso8601
178
+ when DateTime
179
+ value.to_time.utc.iso8601
180
+ when Date
181
+ value.iso8601
182
+ when BigDecimal
183
+ value.to_s("F")
184
+ when Sequel::SQL::Blob
185
+ { __blob__: Base64.strict_encode64(value) }
186
+ else
187
+ value
188
+ end
189
+ end
190
+ # rubocop: enable Metrics/MethodLength
191
+
192
+ def normalize_row(row)
193
+ row.transform_values { |v| normalize_value(v) }
194
+ end
195
+
196
+ # rubocop: disable Metrics/CyclomaticComplexity
197
+ # rubocop: disable Metrics/MethodLength
198
+ def parse_value(value, type)
199
+ return nil if value.nil?
200
+
201
+ if value.is_a?(Hash) && value.key?(:__blob__)
202
+ return Sequel::SQL::Blob.new(Base64.decode64(value[:__blob__]))
203
+ end
204
+
205
+ case type
206
+ when :datetime, :timestamp
207
+ value.is_a?(String) ? Time.iso8601(value) : value
208
+ when :date
209
+ value.is_a?(String) ? Date.iso8601(value) : value
210
+ else
211
+ value
212
+ end
213
+ rescue ArgumentError
214
+ # If parsing fails, keep original value to avoid hard failure.
215
+ value
216
+ end
217
+ # rubocop: enable Metrics/CyclomaticComplexity
218
+ # rubocop: enable Metrics/MethodLength
219
+
220
+ def denormalize_row(db, table, row)
221
+ schema = db.schema(table)
222
+ types = schema.to_h.transform_values { |info| info[:type] }
223
+
224
+ out = {}
225
+ row.each do |k, v|
226
+ type = types[k.to_sym]
227
+ out[k.to_sym] = parse_value(v, type)
228
+ end
229
+ out
230
+ end
231
+
232
+ # rubocop: disable Metrics/AbcSize
233
+ # rubocop: disable Metrics/CyclomaticComplexity
234
+ # rubocop: disable Metrics/MethodLength
235
+ # rubocop: disable Metrics/PerceivedComplexity
236
+ def order_rows_by_self_fk(rows, pk_col, parent_col)
237
+ return rows if rows.empty?
238
+ return rows unless pk_col && parent_col
239
+
240
+ by_id = {}
241
+ rows.each { |r| by_id[r[pk_col]] = r }
242
+
243
+ in_degree = Hash.new(0)
244
+ children = Hash.new { |h, k| h[k] = [] }
245
+
246
+ rows.each do |r|
247
+ id = r[pk_col]
248
+ parent_id = r[parent_col]
249
+ next if parent_id.nil?
250
+ next unless by_id.key?(parent_id)
251
+
252
+ in_degree[id] += 1
253
+ children[parent_id] << id
254
+ end
255
+
256
+ queue = rows.select { |r| in_degree[r[pk_col]].zero? }.map { |r| r[pk_col] }.sort
257
+
258
+ ordered = []
259
+ until queue.empty?
260
+ id = queue.shift
261
+ row = by_id[id]
262
+ ordered << row if row
263
+
264
+ children[id].sort.each do |child_id|
265
+ in_degree[child_id] -= 1
266
+ queue << child_id if in_degree[child_id].zero?
267
+ end
268
+ end
269
+
270
+ # If cycle or missing nodes, append remaining deterministically.
271
+ remaining_ids = by_id.keys - ordered.map { |r| r[pk_col] }
272
+ remaining_ids.sort.each { |id| ordered << by_id[id] }
273
+
274
+ ordered
275
+ end
276
+ # rubocop: enable Metrics/AbcSize
277
+ # rubocop: enable Metrics/CyclomaticComplexity
278
+ # rubocop: enable Metrics/MethodLength
279
+ # rubocop: enable Metrics/PerceivedComplexity
280
+
281
+ # rubocop: disable Metrics/AbcSize
282
+ # rubocop: disable Metrics/CyclomaticComplexity
283
+ # rubocop: disable Metrics/MethodLength
284
+ def export_to_file(filename)
285
+ db = Sequel::Model.db
286
+ tables = ordered_world_tables(db)
287
+
288
+ tables_payload = tables.map do |table|
289
+ pk = primary_key_for_table(db, table)
290
+ s_fk = self_fk_column(db, table)
291
+
292
+ rows = db[table].all
293
+ # Stable base order, then parent-first for self-FK if applicable.
294
+ rows = rows.sort_by { |r| r[pk] || 0 } if pk
295
+ rows = order_rows_by_self_fk(rows, pk, s_fk)
296
+ rows = rows.map { |r| normalize_row(r) }
297
+
298
+ {
299
+ name: table.to_s,
300
+ primary_key: pk&.to_s,
301
+ self_foreign_key: s_fk&.to_s,
302
+ rows: rows
303
+ }
304
+ end
305
+
306
+ data = {
307
+ version: 1,
308
+ created_at: Time.now.utc.iso8601,
309
+ tables: tables_payload
310
+ }
311
+
312
+ File.open(filename, 'wb') do |file|
313
+ Zlib::GzipWriter.wrap(file) { |gz| gz.write(JSON.generate(data)) }
314
+ end
315
+
316
+ filename
317
+ end
318
+ # rubocop: enable Metrics/AbcSize
319
+ # rubocop: enable Metrics/CyclomaticComplexity
320
+ # rubocop: enable Metrics/MethodLength
321
+
322
+ # rubocop: disable Metrics/MethodLength
323
+ def truncate_tables(db, tables)
324
+ if db.database_type == :postgres
325
+ existing = tables.select { |t| db.table_exists?(t) }
326
+ return if existing.empty?
327
+
328
+ table_list = existing.map { |t| db.literal(t) }.join(", ")
329
+ db.run("TRUNCATE TABLE #{table_list} RESTART IDENTITY CASCADE")
330
+ return
331
+ end
332
+
333
+ # For other DBs, truncate/delete children first (reverse dependency order).
334
+ tables.reverse_each do |table|
335
+ next unless db.table_exists?(table)
336
+ db[table].truncate
337
+ end
338
+ end
339
+ # rubocop: enable Metrics/MethodLength
340
+
341
+ # rubocop: disable Metrics/AbcSize
342
+ # rubocop: disable Metrics/CyclomaticComplexity
343
+ # rubocop: disable Metrics/MethodLength
344
+ # rubocop: disable Metrics/PerceivedComplexity
345
+ def import_from_file(filename)
346
+ db = Sequel::Model.db
347
+ data = read_snapshot_file(filename)
348
+
349
+ unless data[:version] == 1
350
+ raise "Unsupported snapshot version: #{data[:version].inspect}"
351
+ end
352
+
353
+ snapshot_tables = Array(data[:tables])
354
+ table_rows_by_name = snapshot_tables.to_h do |t|
355
+ [t[:name].to_s, Array(t[:rows])]
356
+ end
357
+
358
+ table_syms = snapshot_tables.map { |t| t[:name].to_sym }
359
+ ordered = ordered_world_tables(db, only_tables: table_syms)
360
+
361
+ db.transaction do
362
+ truncate_tables(db, ordered)
363
+
364
+ ordered.each do |table|
365
+ rows = table_rows_by_name[table.to_s] || []
366
+ next if rows.empty?
367
+ next unless db.table_exists?(table)
368
+
369
+ denorm = rows.map { |r| denormalize_row(db, table, r) }
370
+ db[table].multi_insert(denorm) unless denorm.empty?
371
+ end
372
+
373
+ reseed_sequences(db, ordered)
374
+ end
375
+
376
+ filename
377
+ end
378
+ # rubocop: enable Metrics/AbcSize
379
+ # rubocop: enable Metrics/CyclomaticComplexity
380
+ # rubocop: enable Metrics/MethodLength
381
+ # rubocop: enable Metrics/PerceivedComplexity
382
+
383
+ SelectSerialSequence = 'SELECT pg_get_serial_sequence(?, ?) AS seq'.freeze
384
+ SelectSetvalTemplate = 'SELECT setval(%<sequence>s, %<max_id>s, true)'.freeze
385
+
386
+ # rubocop: disable Metrics/CyclomaticComplexity
387
+ # rubocop: disable Metrics/MethodLength
388
+ def reseed_sequences(db, tables)
389
+ return unless db.database_type == :postgres
390
+
391
+ tables.each do |table|
392
+ next unless db.table_exists?(table)
393
+
394
+ pk = primary_key_for_table(db, table)
395
+ next unless pk
396
+
397
+ max_id = db[table].max(pk)
398
+ next unless max_id
399
+
400
+ seq_row = db.fetch(SelectSerialSequence, table.to_s, pk.to_s).first
401
+ seq_name = seq_row && seq_row[:seq]
402
+ next unless seq_name
403
+
404
+ db.run(format(SelectSetvalTemplate, sequence: db.literal(seq_name), max_id: max_id))
405
+ end
406
+ end
407
+ # rubocop: enable Metrics/CyclomaticComplexity
408
+ # rubocop: enable Metrics/MethodLength
409
+ end
410
+ # module Snapshots
411
+ end
412
+ # module StoryTeller
@@ -0,0 +1,58 @@
1
+ # lib/story_teller/terminal.rb
2
+ # encoding: utf-8
3
+ # frozen_string_literal: false
4
+
5
+ # Copyright Nels Nelson 2008-2025 but freely usable (see license)
6
+ #
7
+ # This file is part of the StoryTeller.
8
+ #
9
+ # The StoryTeller is free software: you can redistribute it and/or
10
+ # modify it under the terms of the GNU General Public License as published
11
+ # by the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # The StoryTeller is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with the StoryTeller. If not, see <http://www.gnu.org/licenses/>.
21
+
22
+ require 'io/console'
23
+
24
+ # module StoryTeller
25
+ module StoryTeller
26
+ # module Terminal
27
+ module Terminal
28
+ module_function
29
+
30
+ MINIMUM_WORD_WRAP = 1
31
+
32
+ def word_wrap(output = $stdout)
33
+ terminal_columns = columns(output)
34
+ return default_word_wrap if terminal_columns.nil? || terminal_columns.to_i <= 0
35
+
36
+ [terminal_columns.to_i - 1, MINIMUM_WORD_WRAP].max.tap do |n|
37
+ log.debug "word_wrap: #{n}"
38
+ end
39
+ end
40
+
41
+ def default_word_wrap
42
+ StoryTeller::Config.defaults[:default_word_wrap]
43
+ end
44
+
45
+ def columns(output = $stdout)
46
+ return output.winsize[1] if output.respond_to?(:winsize) && output.tty?
47
+
48
+ console = ::IO.console
49
+ return console.winsize[1] if console.respond_to?(:winsize)
50
+
51
+ nil
52
+ rescue IOError, SystemCallError
53
+ nil
54
+ end
55
+ end
56
+ # module Terminal
57
+ end
58
+ # module StoryTeller
@@ -0,0 +1,24 @@
1
+ # lib/story_teller/version.rb
2
+ # encoding: utf-8
3
+ # frozen_string_literal: false
4
+
5
+ # Copyright Nels Nelson 2008-2026 but freely usable (see license)
6
+ #
7
+ # This file is part of the StoryTeller.
8
+ #
9
+ # The StoryTeller is free software: you can redistribute it and/or
10
+ # modify it under the terms of the GNU General Public License as published
11
+ # by the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # The StoryTeller is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with the StoryTeller. If not, see <http://www.gnu.org/licenses/>.
21
+
22
+ module StoryTellerCli
23
+ VERSION = '1.1.3'.freeze
24
+ end
@@ -0,0 +1,34 @@
1
+ # lib/story_teller_cli.rb
2
+ # encoding: utf-8
3
+ # frozen_string_literal: false
4
+
5
+ # Copyright Nels Nelson 2008-2025 but freely usable (see license)
6
+ #
7
+ # This file is part of the StoryTeller.
8
+ #
9
+ # The StoryTeller is free software: you can redistribute it and/or
10
+ # modify it under the terms of the GNU General Public License as published
11
+ # by the Free Software Foundation, either version 3 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # The StoryTeller is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License
20
+ # along with the StoryTeller. If not, see <http://www.gnu.org/licenses/>.
21
+
22
+ require 'story_teller'
23
+
24
+ require_relative 'story_teller/application'
25
+ require_relative 'story_teller/config'
26
+ require_relative 'story_teller/options'
27
+ require_relative 'story_teller/privileges'
28
+ require_relative 'story_teller/runtime'
29
+ require_relative 'story_teller/game'
30
+ require_relative 'story_teller/persistence'
31
+ require_relative 'story_teller/inform/models'
32
+ require_relative 'story_teller/snapshots'
33
+ require_relative 'story_teller/terminal'
34
+ require_relative 'story_teller/cli'
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: story-teller
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Nels Nelson
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: curses
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: 1.4.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: 1.4.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: inform-runtime
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 1.5.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 1.5.0
40
+ - !ruby/object:Gem::Dependency
41
+ name: nokogiri
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 1.13.1
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 1.13.1
54
+ - !ruby/object:Gem::Dependency
55
+ name: pg
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: 1.6.2
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: 1.6.2
68
+ - !ruby/object:Gem::Dependency
69
+ name: sequel
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 5.58.0
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 5.58.0
82
+ description: The Story Teller is a command-line interface software runtime program
83
+ for playing interactive-fiction games which use the Inform 6 Ruby Port.
84
+ email: nels@nelsnelson.org
85
+ executables:
86
+ - inform.rb
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - LICENSE
91
+ - README.md
92
+ - Rakefile
93
+ - config/database.yml
94
+ - exe/inform.rb
95
+ - game/config.yml
96
+ - game/example.inf
97
+ - game/example.rb
98
+ - game/forms/example_form.rb
99
+ - game/grammar/admin.inf.rb
100
+ - game/grammar/builder.inf.rb
101
+ - game/grammar/game_grammar.inf.rb
102
+ - game/grammar/meta.inf.rb
103
+ - game/languages/english.rb
104
+ - game/models/example_model.rb
105
+ - game/modules/example_module.rb
106
+ - game/modules/parser_extensions.rb
107
+ - game/rules/example_state.rb
108
+ - game/scripts/example_script.rb
109
+ - game/topics/example_topic.rb
110
+ - game/verbs/game_verbs.rb
111
+ - game/verbs/metaverbs.rb
112
+ - lib/story_teller/application.rb
113
+ - lib/story_teller/cli.rb
114
+ - lib/story_teller/color.rb
115
+ - lib/story_teller/config.rb
116
+ - lib/story_teller/curses_adapter.rb
117
+ - lib/story_teller/database.rb
118
+ - lib/story_teller/game.rb
119
+ - lib/story_teller/game/loader.rb
120
+ - lib/story_teller/inform/models.rb
121
+ - lib/story_teller/inform/relational/link.rb
122
+ - lib/story_teller/inform/relational/module.rb
123
+ - lib/story_teller/inform/relational/object.rb
124
+ - lib/story_teller/inform/relational/tag.rb
125
+ - lib/story_teller/options.rb
126
+ - lib/story_teller/persistence.rb
127
+ - lib/story_teller/player_character.rb
128
+ - lib/story_teller/privileges.rb
129
+ - lib/story_teller/runtime.rb
130
+ - lib/story_teller/snapshots.rb
131
+ - lib/story_teller/terminal.rb
132
+ - lib/story_teller/version.rb
133
+ - lib/story_teller_cli.rb
134
+ homepage: https://rubygems.org/gems/story-teller
135
+ licenses:
136
+ - GPL-3.0-or-later
137
+ metadata:
138
+ source_code_uri: https://gitlab.com/nelsnelson/story-teller
139
+ bug_tracker_uri: https://gitlab.com/nelsnelson/story-teller/issues
140
+ rubygems_mfa_required: 'true'
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: 4.0.0
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubygems_version: 4.0.11
156
+ specification_version: 4
157
+ summary: Story Teller for playing Inform 6 Ruby games
158
+ test_files: []