gamefic 3.6.0 → 4.0.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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -3
  3. data/CHANGELOG.md +19 -0
  4. data/Rakefile +1 -0
  5. data/gamefic.gemspec +1 -1
  6. data/lib/gamefic/action.rb +68 -54
  7. data/lib/gamefic/active/cue.rb +84 -6
  8. data/lib/gamefic/active/messaging.rb +8 -0
  9. data/lib/gamefic/active/narratives.rb +101 -0
  10. data/lib/gamefic/active.rb +80 -92
  11. data/lib/gamefic/binding.rb +44 -0
  12. data/lib/gamefic/chapter.rb +30 -46
  13. data/lib/gamefic/command.rb +22 -40
  14. data/lib/gamefic/core_ext/array.rb +7 -7
  15. data/lib/gamefic/core_ext/string.rb +2 -2
  16. data/lib/gamefic/describable.rb +13 -0
  17. data/lib/gamefic/dispatcher.rb +35 -55
  18. data/lib/gamefic/entity.rb +6 -5
  19. data/lib/gamefic/expression.rb +1 -11
  20. data/lib/gamefic/logging.rb +3 -10
  21. data/lib/gamefic/match.rb +23 -0
  22. data/lib/gamefic/messenger.rb +1 -1
  23. data/lib/gamefic/narrative.rb +38 -74
  24. data/lib/gamefic/narrator.rb +77 -0
  25. data/lib/gamefic/node.rb +40 -8
  26. data/lib/gamefic/order.rb +53 -0
  27. data/lib/gamefic/plot.rb +41 -59
  28. data/lib/gamefic/props/default.rb +5 -17
  29. data/lib/gamefic/props/multiple_choice.rb +5 -2
  30. data/lib/gamefic/props/multiple_partial.rb +16 -0
  31. data/lib/gamefic/props/output.rb +7 -5
  32. data/lib/gamefic/props/yes_or_no.rb +2 -2
  33. data/lib/gamefic/props.rb +1 -0
  34. data/lib/gamefic/proxy/attr.rb +11 -0
  35. data/lib/gamefic/proxy/base.rb +3 -15
  36. data/lib/gamefic/proxy/config.rb +2 -2
  37. data/lib/gamefic/proxy/pick.rb +3 -3
  38. data/lib/gamefic/proxy/pick_ex.rb +11 -0
  39. data/lib/gamefic/proxy.rb +3 -71
  40. data/lib/gamefic/query/ascendants.rb +16 -0
  41. data/lib/gamefic/query/base.rb +47 -73
  42. data/lib/gamefic/query/children.rb +15 -0
  43. data/lib/gamefic/query/descendants.rb +17 -0
  44. data/lib/gamefic/query/extended.rb +20 -0
  45. data/lib/gamefic/query/family.rb +27 -0
  46. data/lib/gamefic/query/global.rb +22 -0
  47. data/lib/gamefic/query/integer.rb +32 -0
  48. data/lib/gamefic/query/myself.rb +13 -0
  49. data/lib/gamefic/query/parent.rb +13 -0
  50. data/lib/gamefic/query/result.rb +1 -1
  51. data/lib/gamefic/query/siblings.rb +12 -0
  52. data/lib/gamefic/query/subqueries.rb +17 -0
  53. data/lib/gamefic/query/text.rb +8 -9
  54. data/lib/gamefic/query.rb +11 -3
  55. data/lib/gamefic/request.rb +60 -0
  56. data/lib/gamefic/response.rb +46 -72
  57. data/lib/gamefic/scanner/nesting.rb +6 -6
  58. data/lib/gamefic/scanner/result.rb +3 -0
  59. data/lib/gamefic/scanner/strict.rb +14 -4
  60. data/lib/gamefic/scanner.rb +11 -6
  61. data/lib/gamefic/scene/active_choice.rb +75 -0
  62. data/lib/gamefic/scene/activity.rb +7 -3
  63. data/lib/gamefic/scene/base.rb +123 -0
  64. data/lib/gamefic/scene/conclusion.rb +4 -1
  65. data/lib/gamefic/scene/multiple_choice.rb +14 -11
  66. data/lib/gamefic/scene/pause.rb +5 -1
  67. data/lib/gamefic/scene/yes_or_no.rb +9 -0
  68. data/lib/gamefic/scene.rb +2 -1
  69. data/lib/gamefic/scriptable/hooks.rb +161 -0
  70. data/lib/gamefic/scriptable/queries.rb +38 -29
  71. data/lib/gamefic/scriptable/responses.rb +70 -0
  72. data/lib/gamefic/scriptable/scenes.rb +88 -115
  73. data/lib/gamefic/scriptable/seeds.rb +69 -0
  74. data/lib/gamefic/scriptable/syntaxes.rb +29 -0
  75. data/lib/gamefic/scriptable.rb +14 -199
  76. data/lib/gamefic/{scriptable → scripting}/entities.rb +22 -22
  77. data/lib/gamefic/scripting/hooks.rb +45 -0
  78. data/lib/gamefic/{scriptable → scripting}/proxies.rb +5 -3
  79. data/lib/gamefic/scripting/responses.rb +21 -0
  80. data/lib/gamefic/scripting/scenes.rb +57 -0
  81. data/lib/gamefic/scripting/seeds.rb +10 -0
  82. data/lib/gamefic/scripting/syntaxes.rb +13 -0
  83. data/lib/gamefic/scripting.rb +43 -0
  84. data/lib/gamefic/subplot.rb +11 -22
  85. data/lib/gamefic/syntax.rb +39 -24
  86. data/lib/gamefic/version.rb +1 -1
  87. data/lib/gamefic.rb +6 -7
  88. metadata +38 -41
  89. data/lib/gamefic/active/epic.rb +0 -74
  90. data/lib/gamefic/active/take.rb +0 -67
  91. data/lib/gamefic/block.rb +0 -28
  92. data/lib/gamefic/callback.rb +0 -16
  93. data/lib/gamefic/proxy/plot_pick.rb +0 -11
  94. data/lib/gamefic/query/abstract.rb +0 -12
  95. data/lib/gamefic/query/general.rb +0 -41
  96. data/lib/gamefic/query/scoped.rb +0 -27
  97. data/lib/gamefic/rulebook/calls.rb +0 -86
  98. data/lib/gamefic/rulebook/events.rb +0 -65
  99. data/lib/gamefic/rulebook/hooks.rb +0 -57
  100. data/lib/gamefic/rulebook/scenes.rb +0 -68
  101. data/lib/gamefic/rulebook.rb +0 -125
  102. data/lib/gamefic/scene/default.rb +0 -88
  103. data/lib/gamefic/scope/base.rb +0 -44
  104. data/lib/gamefic/scope/children.rb +0 -16
  105. data/lib/gamefic/scope/descendants.rb +0 -16
  106. data/lib/gamefic/scope/family.rb +0 -43
  107. data/lib/gamefic/scope/myself.rb +0 -13
  108. data/lib/gamefic/scope/parent.rb +0 -13
  109. data/lib/gamefic/scope/siblings.rb +0 -14
  110. data/lib/gamefic/scope.rb +0 -9
  111. data/lib/gamefic/scriptable/actions.rb +0 -137
  112. data/lib/gamefic/scriptable/events.rb +0 -71
  113. data/lib/gamefic/scriptable/plot_proxies.rb +0 -29
  114. data/lib/gamefic/snapshot.rb +0 -44
  115. data/lib/gamefic/stage.rb +0 -51
  116. data/lib/gamefic/syntax/template.rb +0 -67
  117. data/lib/gamefic/vault.rb +0 -52
@@ -1,88 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- module Scene
5
- # The base class for scenes. Authors can instantiate this class directly
6
- # and customize it with on_start and on_finish blocks.
7
- #
8
- class Default
9
- # @return [Symbol]
10
- attr_reader :name
11
-
12
- # @param name [Symbol]
13
- # @param narrative [Narrative]
14
- # @param on_start [Proc, nil]
15
- # @param on_finish [Proc, nil]
16
- # @yieldparam [self]
17
- def initialize name, narrative, on_start: nil, on_finish: nil
18
- @name = name
19
- @narrative = narrative
20
- @start_blocks = []
21
- @finish_blocks = []
22
- @start_blocks.push on_start if on_start
23
- @finish_blocks.push on_finish if on_finish
24
- yield(self) if block_given?
25
- end
26
-
27
- # @return [String]
28
- def type
29
- @type ||= self.class.to_s.sub(/^Gamefic::Scene::/, '')
30
- end
31
-
32
- def new_props(**context)
33
- self.class.props_class.new(self, **context)
34
- end
35
-
36
- def on_start &block
37
- @start_blocks.push block
38
- end
39
-
40
- def on_finish &block
41
- @finish_blocks.push block
42
- end
43
-
44
- # @param actor [Gamefic::Actor]
45
- # @param props [Props::Default]
46
- # @return [void]
47
- def start actor, props
48
- props.output[:scene] = to_hash
49
- props.output[:prompt] = props.prompt
50
- end
51
-
52
- # @param actor [Gamefic::Actor]
53
- # @param props [Props::Default]
54
- # @return [void]
55
- def finish actor, props
56
- props.input = actor.queue.shift&.strip
57
- end
58
-
59
- def run_start_blocks actor, props
60
- @start_blocks.each { |blk| Stage.run(@narrative, actor, props, &blk) }
61
- end
62
-
63
- def run_finish_blocks actor, props
64
- @finish_blocks.each { |blk| Stage.run(@narrative, actor, props, &blk) }
65
- end
66
-
67
- def self.props_class
68
- @props_class ||= Props::Default
69
- end
70
-
71
- def conclusion?
72
- is_a?(Conclusion)
73
- end
74
-
75
- def to_hash
76
- { name: name, type: type }
77
- end
78
-
79
- class << self
80
- protected
81
-
82
- def use_props_class klass
83
- @props_class = klass
84
- end
85
- end
86
- end
87
- end
88
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- module Scope
5
- # The base class for a Scoped query's scope.
6
- #
7
- class Base
8
- attr_reader :context
9
-
10
- # @param [Gamefic::Entity]
11
- def initialize context
12
- @context = context
13
- end
14
-
15
- # @param [Array<Gamefic::Entity>]
16
- def matches
17
- []
18
- end
19
-
20
- # @param [Gamefic::Entity]
21
- def self.matches context
22
- new(context).matches
23
- end
24
-
25
- def self.precision
26
- 0
27
- end
28
-
29
- private
30
-
31
- # Return an array of the entity's accessible descendants.
32
- #
33
- # @param [Entity]
34
- # @return [Array<Entity>]
35
- def subquery_accessible entity
36
- return [] unless entity&.accessible?
37
-
38
- entity.children.flat_map do |c|
39
- [c] + subquery_accessible(c)
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- module Scope
5
- # The Children scope returns an entity's children and all accessible
6
- # descendants.
7
- #
8
- class Children < Base
9
- def matches
10
- context.children.flat_map do |c|
11
- [c] + subquery_accessible(c)
12
- end
13
- end
14
- end
15
- end
16
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- module Scope
5
- # The Descendants scope returns an entity's children and accessible
6
- # descendants.
7
- #
8
- class Descendants < Base
9
- def matches
10
- context.children.flat_map do |child|
11
- [child] + subquery_accessible(child)
12
- end
13
- end
14
- end
15
- end
16
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- module Scope
5
- # The Family scope returns an entity's ascendants, descendants, siblings,
6
- # and siblings' descendants.
7
- #
8
- class Family < Base
9
- def matches
10
- match_ascendants + match_descendants + match_siblings
11
- end
12
-
13
- private
14
-
15
- def match_ascendants
16
- [].tap do |result|
17
- here = context.parent
18
- while here
19
- result.push here
20
- here = here.parent
21
- end
22
- end
23
- end
24
-
25
- def match_descendants
26
- context.children.flat_map do |child|
27
- [child] + subquery_accessible(child)
28
- end
29
- end
30
-
31
- def match_siblings
32
- return [] unless context.parent
33
-
34
- context.parent
35
- .children
36
- .that_are_not(context)
37
- .flat_map do |child|
38
- [child] + subquery_accessible(child)
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- module Scope
5
- # The Myself scope returns the entity itself.
6
- #
7
- class Myself < Base
8
- def matches
9
- [context]
10
- end
11
- end
12
- end
13
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- module Scope
5
- # A query scope that can only match the entity's parent.
6
- #
7
- class Parent < Base
8
- def matches
9
- [context.parent].compact
10
- end
11
- end
12
- end
13
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- module Scope
5
- # A query scope that matches the entity's siblings, i.e., the other
6
- # entities that share its parent.
7
- #
8
- class Siblings < Base
9
- def matches
10
- context.parent.children - [context]
11
- end
12
- end
13
- end
14
- end
data/lib/gamefic/scope.rb DELETED
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'gamefic/scope/base'
4
- require 'gamefic/scope/children'
5
- require 'gamefic/scope/descendants'
6
- require 'gamefic/scope/family'
7
- require 'gamefic/scope/parent'
8
- require 'gamefic/scope/siblings'
9
- require 'gamefic/scope/myself'
@@ -1,137 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- module Scriptable
5
- # Scriptable methods related to creating actions.
6
- #
7
- module Actions
8
- include Queries
9
-
10
- # Create a response to a command.
11
- # A Response uses the `verb` argument to identify the imperative verb
12
- # that triggers the action. It can also accept queries to tokenize the
13
- # remainder of the input and filter for particular entities or
14
- # properties. The `block`` argument is the proc to execute when the input
15
- # matches all of the Response's criteria (i.e., verb and queries).
16
- #
17
- # @example A simple Response.
18
- # respond :wave do |actor|
19
- # actor.tell "Hello!"
20
- # end
21
- # # The command "wave" will respond "Hello!"
22
- #
23
- # @example A Response that accepts a Character
24
- # respond :salute, available(Character) do |actor, character|
25
- # actor.tell "#{The character} returns your salute."
26
- # end
27
- #
28
- # @param verb [Symbol] An imperative verb for the command
29
- # @param args [Array<Object>] Filters for the command's tokens
30
- # @yieldparam [Gamefic::Actor]
31
- # @return [Symbol]
32
- def respond(verb, *args, &proc)
33
- rulebook.calls.add_response Response.new(verb, self, *args, &proc)
34
- verb
35
- end
36
-
37
- # Create a meta response for a command.
38
- # Meta responses are very similar to standard responses, except they're
39
- # flagged as meta (`Response#meta?`) to indicate that they provide a
40
- # feature that is not considered an in-game action, such as displaying
41
- # help documentation or a scoreboard.
42
- #
43
- # @example A simple meta Response
44
- # meta :credits do |actor|
45
- # actor.tell "This game was written by John Smith."
46
- # end
47
- #
48
- # @param verb [Symbol] An imperative verb for the command
49
- # @param args [Array<Object>] Filters for the command's tokens
50
- # @yieldparam [Gamefic::Actor]
51
- # @return [Symbol]
52
- def meta(verb, *args, &proc)
53
- rulebook.calls.add_response Response.new(verb, self, *args, meta: true, &proc)
54
- verb
55
- end
56
-
57
- # Add a proc to be evaluated before a character executes an action.
58
- # When verbs are specified, the proc will only be evaluated if the
59
- # action's verb matches them.
60
- #
61
- # @param verbs [Array<Symbol>]
62
- # @yieldparam [Gamefic::Action]
63
- # @return [Action::Hook]
64
- def before_action *verbs, &block
65
- rulebook.hooks.before_action self, *verbs, &block
66
- end
67
-
68
- # Add a proc to be evaluated after a character executes an action.
69
- # When a verbs are specified, the proc will only be evaluated if the
70
- # action's verb matches them.
71
- #
72
- # @param verbs [Array<Symbol>]
73
- # @yieldparam [Gamefic::Action]
74
- # @return [Action::Hook]
75
- def after_action *verbs, &block
76
- rulebook.hooks.after_action self, *verbs, &block
77
- end
78
-
79
- # Create an alternate Syntax for a response.
80
- # The command and its translation can be parameterized.
81
- #
82
- # @example Create a synonym for an `inventory` response.
83
- # interpret "catalogue", "inventory"
84
- # # The command "catalogue" will be translated to "inventory"
85
- #
86
- # @example Create a parameterized synonym for a `look` response.
87
- # interpret "scrutinize :entity", "look :entity"
88
- # # The command "scrutinize chair" will be translated to "look chair"
89
- #
90
- # @param command [String] The format of the original command
91
- # @param translation [String] The format of the translated command
92
- # @return [Syntax] the Syntax object
93
- def interpret command, translation
94
- rulebook.calls.add_syntax Syntax.new(command, translation)
95
- end
96
-
97
- # Verbs are the symbols that have responses defined in the rulebook.
98
- #
99
- # @example
100
- # class MyPlot < Gamefic::Plot
101
- # script do
102
- # respond :think { |actor| actor.tell 'You think.' }
103
- #
104
- # verbs #=> [:think]
105
- # end
106
- # end
107
- #
108
- # @return [Array<Symbol>]
109
- def verbs
110
- rulebook.verbs
111
- end
112
-
113
- # Synonyms are a combination of the rulebook's concrete verbs plus the
114
- # alternative variants defined in syntaxes.
115
- #
116
- # @example
117
- # class MyPlot < Gamefic::Plot
118
- # respond :think { |actor| actor.tell 'You think.' }
119
- # interpret 'ponder', 'think'
120
- #
121
- # verbs #=> [:think]
122
- # synonyms #=> [:think, :ponder]
123
- # end
124
- # end
125
- #
126
- # @return [Array<Symbol>]
127
- def synonyms
128
- rulebook.synonyms
129
- end
130
-
131
- # @return [Array<Syntax>]
132
- def syntaxes
133
- rulebook.syntaxes
134
- end
135
- end
136
- end
137
- end
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- module Scriptable
5
- # Scriptable methods related to creating event callbacks.
6
- #
7
- module Events
8
- # Add a block to be executed on preparation of every turn.
9
- #
10
- # @example Increment a turn counter
11
- # turn = 0
12
- # on_ready do
13
- # turn += 1
14
- # end
15
- #
16
- def on_ready &block
17
- rulebook.events.on_ready(Callback.new(self, block))
18
- end
19
-
20
- # Add a block to be executed for each player at the beginning of a turn.
21
- #
22
- # @example Tell the player how many turns they've played.
23
- # on_player_ready do |player|
24
- # player[:turns] ||= 1
25
- # player.tell "Turn #{player[:turns]}"
26
- # player[:turns] += 1
27
- # end
28
- #
29
- # @yieldparam [Gamefic::Actor]
30
- def on_player_ready &block
31
- wrapper = proc do
32
- players.each { |player| Stage.run(self, player, &block) }
33
- end
34
- on_ready &wrapper
35
- end
36
-
37
- # Add a block to be executed after the Plot is finished updating a turn.
38
- #
39
- def on_update &block
40
- rulebook.events.on_update(Callback.new(self, block))
41
- end
42
-
43
- # Add a block to be executed for each player at the end of a turn.
44
- #
45
- # @yieldparam [Gamefic::Actor]
46
- def on_player_update &block
47
- wrapper = proc do
48
- players.each { |player| Stage.run(self, player, &block) }
49
- end
50
- on_update &wrapper
51
- end
52
-
53
- def on_conclude &block
54
- rulebook.events.on_conclude(Callback.new(self, block))
55
- end
56
-
57
- # @yieldparam [Actor]
58
- # @return [Proc]
59
- def on_player_conclude &block
60
- rulebook.events.on_player_conclude(Callback.new(self, block))
61
- end
62
-
63
- # @yieldparam [Actor]
64
- # @yieldparam [Hash]
65
- # @return [Proc]
66
- def on_player_output &block
67
- rulebook.events.on_player_output Callback.new(self, block)
68
- end
69
- end
70
- end
71
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- module Scriptable
5
- module PlotProxies
6
- def lazy_plot key
7
- Logging.logger.warn "#{caller.first ? "#{caller.first}: " : ''}`lazy_plot` is deprecated. Use `plot_pick`, `plot_pick!, or pass the entity from the plot in a `config` option instead."
8
- Proxy.new(:attr, [:plot, key])
9
- end
10
- alias _plot lazy_plot
11
-
12
- def attr_plot attr
13
- define_method(attr) { plot.send(attr) }
14
- end
15
-
16
- def plot_pick *args
17
- Proxy::PlotPick.new(*args)
18
- end
19
- alias lazy_plot_pick plot_pick
20
- alias _plot_pick plot_pick
21
-
22
- def plot_pick! *args
23
- Proxy::PlotPick.new(*args)
24
- end
25
- alias lazy_plot_pick! plot_pick!
26
- alias _plot_pick! plot_pick!
27
- end
28
- end
29
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'corelib/marshal' if RUBY_ENGINE == 'opal' # Required in browser
4
- require 'base64'
5
-
6
- module Gamefic
7
- # Save and restore plots.
8
- #
9
- module Snapshot
10
- # Save a base64-encoded snapshot of a plot.
11
- #
12
- # @param plot [Plot]
13
- # @return [String]
14
- def self.save plot
15
- cache = plot.detach
16
- binary = Marshal.dump(plot)
17
- plot.attach cache
18
- Base64.encode64(binary)
19
- end
20
-
21
- # Restore a plot from a base64-encoded string.
22
- #
23
- # @param snapshot [String]
24
- # @return [Plot]
25
- def self.restore snapshot
26
- binary = Base64.decode64(snapshot)
27
- Marshal.load(binary).tap do |plot|
28
- plot.hydrate
29
- # @todo Opal marshal dumps are not idempotent
30
- next if RUBY_ENGINE == 'opal' || match?(plot, snapshot)
31
-
32
- Logging.logger.warn "Scripts modified #{plot.class} data. Snapshot may not have restored properly"
33
- end
34
- end
35
-
36
- # True if the plot's state matches the snapshot.
37
- #
38
- # @param plot [Plot]
39
- # @param snapshot [String]
40
- def self.match?(plot, snapshot)
41
- save(plot) == snapshot
42
- end
43
- end
44
- end
data/lib/gamefic/stage.rb DELETED
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- # A safe execution environment for narrative code.
5
- #
6
- module Stage
7
- module_function
8
-
9
- # @param narrative [Narrative]
10
- def run(narrative, *args, &code)
11
- container = narrative.clone
12
- narrative.instance_exec(*args, &code).tap { validate_changes narrative, container, code }
13
- end
14
-
15
- OVERWRITEABLE_CLASSES = [String, Numeric, Symbol].freeze
16
-
17
- SWAPPABLE_VALUES = [true, false].freeze
18
-
19
- class << self
20
- private
21
-
22
- def validate_changes narrative, container, code
23
- container.instance_variables.each do |var|
24
- next unless narrative.instance_variables.include?(var)
25
-
26
- cval = container.instance_variable_get(var)
27
-
28
- nval = narrative.instance_variable_get(var)
29
- next if cval == nval
30
-
31
- validate_overwriteable(cval, nval, "Unsafe reassignment of #{var} in #{code}")
32
- end
33
- end
34
-
35
- def validate_overwriteable cval, nval, error
36
- raise error unless overwriteable?(cval, nval)
37
- end
38
-
39
- def overwriteable? cval, nval
40
- return true if cval.nil? || swappable?(cval, nval)
41
-
42
- allowed = OVERWRITEABLE_CLASSES.find { |klass| cval.is_a?(klass) }
43
- allowed && cval.is_a?(allowed)
44
- end
45
-
46
- def swappable? *values
47
- values.all? { |val| SWAPPABLE_VALUES.include?(val) }
48
- end
49
- end
50
- end
51
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gamefic
4
- class Syntax
5
- # Template data for syntaxes.
6
- #
7
- class Template
8
- PARAM_REGEXP = /^:[a-z0-9_]+$/i.freeze
9
-
10
- # @return [String]
11
- attr_reader :text
12
-
13
- # @return [Array<String>]
14
- attr_reader :params
15
-
16
- def initialize text
17
- @text = text.normalize
18
- @params = @text.keywords.select { |word| word.start_with?(':') }
19
- end
20
-
21
- def keywords
22
- text.keywords
23
- end
24
-
25
- def to_s
26
- text
27
- end
28
-
29
- def regexp
30
- @regexp ||= Regexp.new("^#{make_tokens.join(' ')}$", Regexp::IGNORECASE)
31
- end
32
-
33
- def verb
34
- @verb ||= Syntax.literal_or_nil(keywords.first)
35
- end
36
-
37
- def compare other
38
- if keywords.length == other.keywords.length
39
- other.verb <=> verb
40
- else
41
- other.keywords.length <=> keywords.length
42
- end
43
- end
44
-
45
- # @param tmpl_or_str [Template, String]
46
- # @return [Template]
47
- def self.to_template tmpl_or_str
48
- return tmpl_or_str if tmpl_or_str.is_a?(Template)
49
-
50
- Template.new(tmpl_or_str)
51
- end
52
-
53
- private
54
-
55
- # @return [Array<String>]
56
- def make_tokens
57
- keywords.map.with_index do |word, idx|
58
- next word unless word.match?(PARAM_REGEXP)
59
-
60
- next nil if idx.positive? && keywords[idx - 1].match?(PARAM_REGEXP)
61
-
62
- '([\w\W\s\S]*?)'
63
- end.compact
64
- end
65
- end
66
- end
67
- end