gamefic 1.7.0 → 2.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 (99) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +13 -0
  5. data/.solargraph.yml +5 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE +20 -0
  8. data/README.md +28 -0
  9. data/Rakefile +10 -0
  10. data/gamefic.gemspec +27 -0
  11. data/lib/gamefic.rb +7 -6
  12. data/lib/gamefic/action.rb +38 -28
  13. data/lib/gamefic/active.rb +325 -280
  14. data/lib/gamefic/actor.rb +8 -5
  15. data/lib/gamefic/command.rb +9 -7
  16. data/lib/gamefic/core_ext/array.rb +24 -49
  17. data/lib/gamefic/core_ext/string.rb +25 -16
  18. data/lib/gamefic/describable.rb +21 -23
  19. data/lib/gamefic/element.rb +43 -31
  20. data/lib/gamefic/entity.rb +6 -12
  21. data/lib/gamefic/index.rb +121 -0
  22. data/lib/gamefic/{matchable.rb → keywords.rb} +52 -50
  23. data/lib/gamefic/messaging.rb +43 -44
  24. data/lib/gamefic/node.rb +14 -5
  25. data/lib/gamefic/plot.rb +69 -89
  26. data/lib/gamefic/plot/darkroom.rb +92 -264
  27. data/lib/gamefic/plot/host.rb +42 -48
  28. data/lib/gamefic/plot/snapshot.rb +5 -18
  29. data/lib/gamefic/query.rb +14 -18
  30. data/lib/gamefic/query/base.rb +30 -18
  31. data/lib/gamefic/query/children.rb +0 -0
  32. data/lib/gamefic/query/external.rb +18 -14
  33. data/lib/gamefic/query/family.rb +1 -7
  34. data/lib/gamefic/query/matches.rb +75 -67
  35. data/lib/gamefic/query/parent.rb +0 -0
  36. data/lib/gamefic/query/siblings.rb +0 -0
  37. data/lib/gamefic/query/text.rb +2 -1
  38. data/lib/gamefic/scene.rb +0 -2
  39. data/lib/gamefic/scene/activity.rb +24 -26
  40. data/lib/gamefic/scene/base.rb +64 -8
  41. data/lib/gamefic/scene/conclusion.rb +0 -2
  42. data/lib/gamefic/scene/custom.rb +0 -2
  43. data/lib/gamefic/scene/multiple_choice.rb +18 -3
  44. data/lib/gamefic/scene/multiple_scene.rb +29 -20
  45. data/lib/gamefic/scene/pause.rb +7 -2
  46. data/lib/gamefic/scene/yes_or_no.rb +21 -9
  47. data/lib/gamefic/scriptable.rb +87 -0
  48. data/lib/gamefic/serialize.rb +68 -0
  49. data/lib/gamefic/subplot.rb +29 -35
  50. data/lib/gamefic/syntax.rb +14 -13
  51. data/lib/gamefic/version.rb +3 -3
  52. data/lib/gamefic/world.rb +16 -0
  53. data/lib/gamefic/world/callbacks.rb +135 -0
  54. data/lib/gamefic/world/commands.rb +184 -0
  55. data/lib/gamefic/{plot → world}/entities.rb +30 -29
  56. data/lib/gamefic/{plot → world}/playbook.rb +255 -240
  57. data/lib/gamefic/world/players.rb +21 -0
  58. data/lib/gamefic/world/scenes.rb +226 -0
  59. metadata +41 -92
  60. data/bin/gamefic +0 -9
  61. data/lib/gamefic/engine.rb +0 -7
  62. data/lib/gamefic/engine/base.rb +0 -59
  63. data/lib/gamefic/engine/tty.rb +0 -24
  64. data/lib/gamefic/grammar.rb +0 -13
  65. data/lib/gamefic/grammar/conjugator.rb +0 -20
  66. data/lib/gamefic/grammar/gender.rb +0 -11
  67. data/lib/gamefic/grammar/person.rb +0 -10
  68. data/lib/gamefic/grammar/plural.rb +0 -13
  69. data/lib/gamefic/grammar/pronouns.rb +0 -106
  70. data/lib/gamefic/grammar/tense.rb +0 -6
  71. data/lib/gamefic/grammar/verb_set.rb +0 -43
  72. data/lib/gamefic/grammar/verbs.rb +0 -26
  73. data/lib/gamefic/grammar/word_adapter.rb +0 -49
  74. data/lib/gamefic/plot/articles.rb +0 -22
  75. data/lib/gamefic/plot/callbacks.rb +0 -126
  76. data/lib/gamefic/plot/commands.rb +0 -120
  77. data/lib/gamefic/plot/players.rb +0 -15
  78. data/lib/gamefic/plot/scenes.rb +0 -187
  79. data/lib/gamefic/plot/theater.rb +0 -73
  80. data/lib/gamefic/plot/you_mount.rb +0 -22
  81. data/lib/gamefic/script.rb +0 -13
  82. data/lib/gamefic/script/base.rb +0 -42
  83. data/lib/gamefic/script/file.rb +0 -14
  84. data/lib/gamefic/script/text.rb +0 -14
  85. data/lib/gamefic/shell.rb +0 -76
  86. data/lib/gamefic/source.rb +0 -14
  87. data/lib/gamefic/source/base.rb +0 -12
  88. data/lib/gamefic/source/file.rb +0 -23
  89. data/lib/gamefic/source/text.rb +0 -16
  90. data/lib/gamefic/tester.rb +0 -19
  91. data/lib/gamefic/text.rb +0 -8
  92. data/lib/gamefic/text/ansi.rb +0 -53
  93. data/lib/gamefic/text/html.rb +0 -68
  94. data/lib/gamefic/text/html/conversions.rb +0 -250
  95. data/lib/gamefic/text/html/entities.rb +0 -9
  96. data/lib/gamefic/tty.rb +0 -10
  97. data/lib/gamefic/user.rb +0 -7
  98. data/lib/gamefic/user/base.rb +0 -29
  99. data/lib/gamefic/user/tty.rb +0 -38
@@ -1,11 +1,9 @@
1
1
  require 'gamefic/command'
2
2
 
3
3
  module Gamefic
4
-
5
4
  class Syntax
6
5
  attr_reader :token_count, :first_word, :verb, :template, :command
7
- @@phrase = '([\w\W\s\S]*?)'
8
-
6
+
9
7
  def initialize template, command
10
8
  words = template.split_words
11
9
  @token_count = words.length
@@ -15,8 +13,8 @@ module Gamefic
15
13
  @token_count -= 1
16
14
  @first_word = ''
17
15
  else
18
- @verb = command_words[0].to_sym if !command_words[0].nil?
19
16
  @first_word = words[0].to_s
17
+ @verb = command_words[0].to_sym
20
18
  end
21
19
  @command = command_words.join(' ')
22
20
  @template = words.join(' ')
@@ -29,7 +27,7 @@ module Gamefic
29
27
  if last_token_is_reg
30
28
  next
31
29
  else
32
- tokens.push @@phrase
30
+ tokens.push '([\w\W\s\S]*?)'
33
31
  last_token_is_reg = true
34
32
  end
35
33
  else
@@ -50,11 +48,11 @@ module Gamefic
50
48
  @replace = subs.join(' ')
51
49
  @regexp = Regexp.new("^#{tokens.join(' ')}$", Regexp::IGNORECASE)
52
50
  end
53
-
51
+
54
52
  # Convert a String into a Command.
55
53
  #
56
54
  # @param text [String]
57
- # @return [Command]
55
+ # @return [Gamefic::Command]
58
56
  def tokenize text
59
57
  m = text.match(@regexp)
60
58
  return nil if m.nil?
@@ -71,7 +69,11 @@ module Gamefic
71
69
  }
72
70
  Command.new @verb, arguments
73
71
  end
74
-
72
+
73
+ # Determine if the specified text matches the syntax's expected pattern.
74
+ #
75
+ # @param text [String]
76
+ # @return [Boolean]
75
77
  def accept? text
76
78
  !text.match(@regexp).nil?
77
79
  end
@@ -82,16 +84,16 @@ module Gamefic
82
84
  def signature
83
85
  [@regexp, @replace]
84
86
  end
85
-
87
+
86
88
  def ==(other)
87
89
  signature == other.signature
88
90
  end
89
-
91
+
90
92
  # Tokenize an Array of Commands from the specified text.
91
93
  #
92
94
  # @param text [String] The text to tokenize.
93
- # @param syntaxes [Array<Syntax>] The Syntaxes to use.
94
- # @return [Array<Command>] The tokenized commands.
95
+ # @param syntaxes [Array<Gamefic::Syntax>] The Syntaxes to use.
96
+ # @return [Array<Gamefic::Command>] The tokenized commands.
95
97
  def self.tokenize text, syntaxes
96
98
  matches = []
97
99
  syntaxes.each { |syntax|
@@ -108,5 +110,4 @@ module Gamefic
108
110
  matches
109
111
  end
110
112
  end
111
-
112
113
  end
@@ -1,3 +1,3 @@
1
- module Gamefic
2
- VERSION = '1.7.0'
3
- end
1
+ module Gamefic
2
+ VERSION = '2.0.0'
3
+ end
@@ -0,0 +1,16 @@
1
+ module Gamefic
2
+ module World
3
+ autoload :Playbook, 'gamefic/world/playbook'
4
+ autoload :Entities, 'gamefic/world/entities'
5
+ autoload :Commands, 'gamefic/world/commands'
6
+ autoload :Callbacks, 'gamefic/world/callbacks'
7
+ autoload :Scenes, 'gamefic/world/scenes'
8
+ autoload :Players, 'gamefic/world/players'
9
+
10
+ include Entities
11
+ include Commands
12
+ include Callbacks
13
+ include Scenes
14
+ include Players
15
+ end
16
+ end
@@ -0,0 +1,135 @@
1
+ module Gamefic
2
+ module World
3
+ module Callbacks
4
+ # Add a block to be executed on preparation of every turn.
5
+ #
6
+ # @example Increment a turn counter
7
+ # turn = 0
8
+ # on_ready do
9
+ # turn += 1
10
+ # end
11
+ #
12
+ def on_ready &block
13
+ ready_procs.push block
14
+ end
15
+
16
+ # Add a block to be executed after the Plot is finished updating a turn.
17
+ #
18
+ def on_update &block
19
+ update_procs.push block
20
+ end
21
+
22
+ # Add a block to be executed for each player at the beginning of a turn.
23
+ #
24
+ # @example Tell the player how many turns they've played.
25
+ # on_player_ready do |player|
26
+ # player[:turns] ||= 0
27
+ # if player[:turns] > 0
28
+ # player.tell "Turn #{player[:turns]}"
29
+ # end
30
+ # player[:turns] += 1
31
+ # end
32
+ #
33
+ # @yieldparam [Gamefic::Actor]
34
+ def on_player_ready &block
35
+ player_ready_procs.push block
36
+ end
37
+
38
+ # Add a block to be executed for each player before an update.
39
+ #
40
+ # @yieldparam[Gamefic::Actor]
41
+ def before_player_update &block
42
+ before_player_update_procs.push block
43
+ end
44
+
45
+ # Add a block to be executed for each player at the end of a turn.
46
+ #
47
+ # @yieldparam [Gamefic::Actor]
48
+ def on_player_update &block
49
+ player_update_procs.push block
50
+ end
51
+
52
+ # Add a block to be executed at the conclusion of the plot.
53
+ #
54
+ # @yieldparam [Gamefic::Actor]
55
+ def on_player_conclude &block
56
+ player_conclude_procs.push block
57
+ end
58
+
59
+ def player_conclude_procs
60
+ @player_conclude_procs ||= []
61
+ end
62
+
63
+ def ready_procs
64
+ @ready_procs ||= []
65
+ end
66
+
67
+ def update_procs
68
+ @update_procs ||= []
69
+ end
70
+
71
+ def player_ready_procs
72
+ @player_ready_procs ||= []
73
+ end
74
+
75
+ def before_player_update_procs
76
+ @before_player_update_procs ||= []
77
+ end
78
+
79
+ def player_update_procs
80
+ @player_update_procs ||= []
81
+ end
82
+
83
+ private
84
+
85
+ # Execute the on_ready blocks. This method is typically called by the
86
+ # Plot while beginning a turn.
87
+ #
88
+ def call_ready
89
+ ready_procs.each { |p| p.call }
90
+ end
91
+
92
+ # Execute the on_update blocks. This method is typically called by the
93
+ # Plot while ending a turn.
94
+ #
95
+ def call_update
96
+ update_procs.each { |p| p.call }
97
+ end
98
+
99
+ # Execute the before_player_update blocks for each player. This method is
100
+ # typically called by the Plot while updating a turn, immediately before
101
+ # processing player input.
102
+ #
103
+ def call_before_player_update
104
+ players.each { |player|
105
+ player.flush
106
+ before_player_update_procs.each { |block| block.call player }
107
+ }
108
+ end
109
+
110
+ # Execute the on_player_ready blocks for each player. This method is
111
+ # typically called by the Plot while beginning a turn, immediately after
112
+ # the on_ready blocks.
113
+ #
114
+ def call_player_ready
115
+ players.each { |player|
116
+ unless player.next_scene.nil? || !player.scene.finished?
117
+ player.cue player.next_scene, **player.next_options
118
+ end
119
+ player.cue default_scene if player.scene.nil?
120
+ player_ready_procs.each { |block| block.call player }
121
+ }
122
+ end
123
+
124
+ # Execute the on_player_update blocks for each player. This method is
125
+ # typically called by the Plot while ending a turn, immediately before the
126
+ # on_ready blocks.
127
+ #
128
+ def call_player_update
129
+ players.each { |player|
130
+ player_update_procs.each { |block| block.call player }
131
+ }
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,184 @@
1
+ require 'gamefic/action'
2
+
3
+ module Gamefic
4
+ module World
5
+ module Commands
6
+ include Gamefic::World::Entities
7
+
8
+ # @return [Gamefic::World::Playbook]
9
+ def playbook
10
+ @playbook ||= Gamefic::World::Playbook.new
11
+ end
12
+
13
+ # Create an Action that responds to a command.
14
+ # An Action uses the command argument to identify the imperative verb that
15
+ # triggers the action.
16
+ # It can also accept queries to tokenize the remainder of the input and
17
+ # filter for particular entities or properties.
18
+ # The block argument contains the code to be executed when the input
19
+ # matches all of the Action's criteria (i.e., verb and queries).
20
+ #
21
+ # @example A simple Action.
22
+ # respond :salute do |actor|
23
+ # actor.tell "Hello, sir!"
24
+ # end
25
+ # # The command "salute" will respond "Hello, sir!"
26
+ #
27
+ # @example An Action that accepts a Character
28
+ # respond :salute, Use.visible(Character) do |actor, character|
29
+ # actor.tell "#{The character} returns your salute."
30
+ # end
31
+ #
32
+ # @param command [Symbol] An imperative verb for the command
33
+ # @param queries [Array<Query::Base>] Filters for the command's tokens
34
+ # @yieldparam [Gamefic::Actor]
35
+ # @return [Class] The resulting Action subclass
36
+ def respond(command, *queries, &proc)
37
+ playbook.respond(command, *map_response_args(queries), &proc)
38
+ end
39
+ alias action respond
40
+
41
+ # Parse a verb and a list of arguments into an action.
42
+ # This method serves as a shortcut to creating an action with one or more
43
+ # arguments that identify specific entities.
44
+ #
45
+ # @example
46
+ # @thing = make Entity, name: 'a thing'
47
+ # parse "use", "the thing" do |actor, thing|
48
+ # actor.tell "You use it."
49
+ # end
50
+ #
51
+ # @raise [ArgumentError] if tokens are unrecognized or ambiguous
52
+ #
53
+ # @param verb [String, Symbol] The command's verb
54
+ # @param tokens [Array<String>] The arguments passed to the action
55
+ # @return [Class] The resulting Action subclass
56
+ def parse verb, *tokens, &proc
57
+ query = Query::External.new(entities)
58
+ params = []
59
+ tokens.each do |arg|
60
+ matches = query.resolve(nil, arg)
61
+ raise ArgumentError, "Unable to resolve token '#{arg}'" if matches.objects.empty?
62
+ raise ArgumentError, "Ambiguous results for '#{arg}'" if matches.objects.length > 1
63
+ params.push Query::Family.new(matches.objects[0])
64
+ end
65
+ respond(verb.to_sym, *params, &proc)
66
+ end
67
+
68
+ # Tokenize and parse a command to create a new Action subclass.
69
+ #
70
+ # @param command [String] The command
71
+ # @yieldparam [Gamefic::Actor]
72
+ # @return [Class] the resulting Action subclass
73
+ def override(command, &proc)
74
+ cmd = Syntax.tokenize(command, playbook.syntaxes).first
75
+ raise "Unable to tokenize command '#{command}'" if cmd.nil?
76
+ parse cmd.verb, *cmd.arguments, &proc
77
+ end
78
+
79
+ # Create a Meta Action that responds to a command.
80
+ # Meta Actions are very similar to standard Actions, except the Plot
81
+ # understands them to be commands that operate above and/or outside of the
82
+ # actual game world. Examples of Meta Actions are commands that report the
83
+ # player's current score, save and restore saved games, or list the game's
84
+ # credits.
85
+ #
86
+ # @example A simple Meta Action
87
+ # meta :credits do |actor|
88
+ # actor.tell "This game was written by John Smith."
89
+ # end
90
+ #
91
+ # @param command [Symbol] An imperative verb for the command
92
+ # @param queries [Array<Query::Base>] Filters for the command's tokens
93
+ # @yieldparam [Gamefic::Actor]
94
+ def meta(command, *queries, &proc)
95
+ playbook.meta command, *queries, &proc
96
+ end
97
+
98
+ # Declare a dismabiguation response for actions.
99
+ # The disambigurator is executed when an action expects an argument to
100
+ # reference a specific entity but its query matched more than one. For
101
+ # example, "red" might refer to either a red key or a red book.
102
+ #
103
+ # If a disambiguator is not defined, the playbook will use its default
104
+ # implementation.
105
+ #
106
+ # @example Tell the player the list of ambiguous entities.
107
+ # disambiguate do |actor, entities|
108
+ # actor.tell "I don't know which you mean: #{entities.join_or}."
109
+ # end
110
+ #
111
+ # @yieldparam [Gamefic::Actor]
112
+ # @yieldparam [Array<Gamefic::Entity>]
113
+ def disambiguate &block
114
+ playbook.disambiguate &block
115
+ end
116
+
117
+ # Validate an order before a character can execute its command.
118
+ #
119
+ # @yieldparam [Gamefic::Director::Order]
120
+ def validate &block
121
+ playbook.validate &block
122
+ end
123
+
124
+ # Create an alternate Syntax for an Action.
125
+ # The command and its translation can be parameterized.
126
+ #
127
+ # @example Create a synonym for the Inventory Action.
128
+ # interpret "catalogue", "inventory"
129
+ # # The command "catalogue" will be translated to "inventory"
130
+ #
131
+ # @example Create a parameterized synonym for the Look Action.
132
+ # interpret "scrutinize :entity", "look :entity"
133
+ # # The command "scrutinize chair" will be translated to "look chair"
134
+ #
135
+ # @param command [String] The format of the original command
136
+ # @param translation [String] The format of the translated command
137
+ # @return [Syntax] the Syntax object
138
+ def interpret command, translation
139
+ playbook.interpret command, translation
140
+ end
141
+ alias xlate interpret
142
+
143
+ # Get an Array of available verbs.
144
+ #
145
+ # @return [Array<String>]
146
+ def verbs
147
+ playbook.verbs.map { |v| v.to_s }.reject{ |v| v.start_with?('_') }
148
+ end
149
+
150
+ # Get an Array of all Actions defined in the Plot.
151
+ #
152
+ # @return [Array<Action>]
153
+ def actions
154
+ playbook.actions
155
+ end
156
+
157
+ def get_default_query
158
+ @default_query_class ||= Gamefic::Query::Family
159
+ end
160
+
161
+ def set_default_query cls
162
+ @default_query_class = cls
163
+ end
164
+
165
+ private
166
+
167
+ def map_response_args queries
168
+ result = []
169
+ queries.each do |q|
170
+ if q.is_a?(Regexp)
171
+ result.push Gamefic::Query::Text.new(q)
172
+ elsif q.is_a?(Gamefic::Query::Base)
173
+ result.push q
174
+ elsif q.is_a?(Gamefic::Element) || (q.is_a?(Class) && q <= Gamefic::Element)
175
+ result.push get_default_query.new(q)
176
+ else
177
+ raise ArgumentError.new("Invalid argument for response: #{q}")
178
+ end
179
+ end
180
+ result
181
+ end
182
+ end
183
+ end
184
+ end
@@ -1,6 +1,5 @@
1
1
  module Gamefic
2
-
3
- class Plot
2
+ module World
4
3
  module Entities
5
4
  # Make a new Entity with the provided properties.
6
5
  #
@@ -8,16 +7,16 @@ module Gamefic
8
7
  # chair = make Entity, name: 'red chair'
9
8
  # chair.name #=> 'red chair'
10
9
  #
10
+ # @raise [ArgumentError] if class is not an Entity
11
+ #
11
12
  # @param cls [Class] The Class of the Entity to be created.
12
13
  # @param args [Hash] The entity's properties.
13
- # @return [Gamefic::Entity]
14
+ # @!macro [attach] make_entity
15
+ # @return [$1]
14
16
  def make cls, args = {}, &block
17
+ raise ArgumentError, "Invalid Entity class" unless cls.is_a?(Class) && cls <= Entity
15
18
  ent = cls.new args, &block
16
- if ent.kind_of?(Entity) == false
17
- raise "Invalid entity class"
18
- end
19
- p_entities.push ent
20
- p_dynamic.push ent if running?
19
+ entities.push ent
21
20
  ent
22
21
  end
23
22
 
@@ -34,13 +33,21 @@ module Gamefic
34
33
  ent
35
34
  end
36
35
 
36
+ # Safely remove an entity from a plot.
37
+ #
38
+ # If the entity is dynamic (e.g., created after a plot is already
39
+ # running), it is safe to delete it completely. Otherwise the entity
40
+ # will still be referenced in the entities array, but its parent will be
41
+ # set to nil.
42
+ #
43
+ # @param [Gamefic::Entity] The entity to remove
37
44
  def destroy entity
38
- if p_dynamic.include?(entity)
39
- p_entities.delete entity
40
- p_dynamic.delete entity
41
- p_players.delete entity
42
- end
43
45
  entity.parent = nil
46
+ index = entities.index(entity)
47
+ return if index.nil? || index < static_entity_length - 1
48
+ entities.delete_at index
49
+ players.delete entity
50
+ entity.destroy
44
51
  end
45
52
 
46
53
  # Pick an entity based on its description.
@@ -49,7 +56,7 @@ module Gamefic
49
56
  #
50
57
  # @example Select the Entity that matches the description
51
58
  # red_chair = make Entity, :name => 'red chair'
52
- # blue_chair make Entity, :name => 'blue chair'
59
+ # blue_chair = make Entity, :name => 'blue chair'
53
60
  # pick "red chair" #=> red_chair
54
61
  # pick "blue chair" #=> blue_chair
55
62
  # pick "chair" #=> IndexError: description is ambiguous
@@ -57,8 +64,7 @@ module Gamefic
57
64
  # @param description [String] The description of the entity
58
65
  # @return [Gamefic::Entity] The entity that matches the description
59
66
  def pick(description)
60
- query = Gamefic::Query::Base.new
61
- result = query.match(description, entities)
67
+ result = Query::Matches.execute(entities, description)
62
68
  if result.objects.length == 0
63
69
  raise IndexError.new("Unable to find entity from '#{description}'")
64
70
  elsif result.objects.length > 1
@@ -69,32 +75,27 @@ module Gamefic
69
75
 
70
76
  # Get an array of entities associated with this plot.
71
77
  #
72
- # @return [Array<Entity>]
78
+ # @return [Array<Gamefic::Entity>]
73
79
  def entities
74
- p_entities.clone
80
+ @entities ||= []
75
81
  end
76
82
 
77
83
  # Get an array of players associated with this plot.
78
84
  #
79
- # @return [Array<Character>]
85
+ # @return [Array<Gamefic::Actor>]
80
86
  def players
81
- p_players.clone
87
+ @players ||= []
82
88
  end
83
89
 
84
90
  private
85
91
 
86
- def p_entities
87
- @p_entities ||= []
88
- end
89
-
90
- def p_players
91
- @p_players ||= []
92
+ def mark_static_entities
93
+ @static_entity_length ||= entities.length
92
94
  end
93
95
 
94
- def p_dynamic
95
- @p_dynamic ||= []
96
+ def static_entity_length
97
+ @static_entity_length || 0
96
98
  end
97
99
  end
98
100
  end
99
-
100
101
  end