Module: StoryTeller::InstanceMethods
- Included in:
- Runtime
- Defined in:
- lib/story_teller/runtime.rb
Overview
module InstanceMethods
Constant Summary collapse
- UndefinedMainPattern =
%r{undefined method 'Main' for an instance of Object}.freeze
Instance Attribute Summary collapse
-
#main_object ⇒ Object
Returns the value of attribute main_object.
-
#session ⇒ Object
Returns the value of attribute session.
Instance Method Summary collapse
- #apply_invocation_privileges(subject) ⇒ Object
- #apply_privileges(klass, privileges: StoryTeller::Privileges) ⇒ Object
- #bind_session(inflib = inform_library) ⇒ Object
-
#canonical_location_candidate ⇒ Object
rubocop: disable Metrics/CyclomaticComplexity.
- #config_file_path ⇒ Object
- #configure!(options) ⇒ Object
- #configure_output ⇒ Object
- #default_config ⇒ Object
-
#ensuring_player_location(&block) ⇒ Object
TODO: Ensuring the location of the player object requires that this method be invoked before the move @player, @location operation in the InformLibrary#play method.
- #flush_session_output ⇒ Object
- #game_components ⇒ Object
- #game_dir_name ⇒ Object
- #game_file_path ⇒ Object
- #game_path ⇒ Object
- #grammar_module_path ⇒ Object
- #identity ⇒ Object
- #inform_library ⇒ Object
- #inspect ⇒ Object
- #install_persisted_startup_location_hook ⇒ Object
- #invocation_context ⇒ Object
- #invocation_identity ⇒ Object
- #invocation_properties ⇒ Object
- #invoke_main ⇒ Object
- #location_candidate ⇒ Object
- #manage_privileges ⇒ Object
- #persist? ⇒ Boolean
- #persisted_location_candidate ⇒ Object
- #persistence ⇒ Object
- #play_game ⇒ Object
- #privileged_identities(privilege) ⇒ Object
- #project_dir_path ⇒ Object
- #quit_game ⇒ Object
- #read ⇒ Object
- #read_eval_print_loop ⇒ Object
- #reset_config_cache ⇒ Object
- #restart_game ⇒ Object
-
#restore_game(callback = nil) ⇒ Object
rubocop: disable Metrics/AbcSize rubocop: disable Metrics/MethodLength.
-
#restore_player_location ⇒ Object
rubocop: enable Metrics/CyclomaticComplexity.
-
#save_game(callback = nil) ⇒ Object
rubocop: disable Metrics/AbcSize rubocop: disable Metrics/MethodLength.
-
#to_s ⇒ Object
rubocop: enable Metrics/AbcSize rubocop: enable Metrics/MethodLength.
-
#use_persistent_selfobj ⇒ Object
Initially, inform_library defaults to using Inform::Parser::SelfObj.
- #write_output(value) ⇒ Object
Instance Attribute Details
#main_object ⇒ Object
Returns the value of attribute main_object.
30 31 32 |
# File 'lib/story_teller/runtime.rb', line 30 def main_object @main_object end |
#session ⇒ Object
Returns the value of attribute session.
30 31 32 |
# File 'lib/story_teller/runtime.rb', line 30 def session @session end |
Instance Method Details
#apply_invocation_privileges(subject) ⇒ Object
207 208 209 210 |
# File 'lib/story_teller/runtime.rb', line 207 def apply_invocation_privileges(subject) StoryTeller::PrivilegeGrants.grant_session(subject, :admin) if @options[:admin] StoryTeller::PrivilegeGrants.grant_session(subject, :builder) if @options[:builder] end |
#apply_privileges(klass, privileges: StoryTeller::Privileges) ⇒ Object
212 213 214 215 216 |
# File 'lib/story_teller/runtime.rb', line 212 def apply_privileges(klass, privileges: StoryTeller::Privileges) return if klass.nil? || klass < privileges klass.include(privileges) end |
#bind_session(inflib = inform_library) ⇒ Object
218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/story_teller/runtime.rb', line 218 def bind_session(inflib = inform_library) @session ||= StoryTeller::IO::Session.new( machine: inflib, player: inflib.selfobj, state: :playing, settings: default_config ).tap do |session| apply_invocation_privileges(session) end @session.expose_to(inflib) @session end |
#canonical_location_candidate ⇒ Object
rubocop: disable Metrics/CyclomaticComplexity
148 149 150 151 152 |
# File 'lib/story_teller/runtime.rb', line 148 def canonical_location_candidate inform_library&.location || inform_library&.player&.location || inform_library&.player&.spawn_point end |
#config_file_path ⇒ Object
38 39 40 |
# File 'lib/story_teller/runtime.rb', line 38 def config_file_path @config_file_path ||= game.config_file_path end |
#configure!(options) ⇒ Object
78 79 80 81 82 83 |
# File 'lib/story_teller/runtime.rb', line 78 def configure!() @options.merge!() reset_config_cache @game = StoryTeller::Game.new(@options) self end |
#configure_output ⇒ Object
241 242 243 244 245 |
# File 'lib/story_teller/runtime.rb', line 241 def configure_output return unless @options[:curses] StoryTeller::IO.default_output = StoryTeller::CursesAdapter.new end |
#default_config ⇒ Object
32 33 34 35 36 |
# File 'lib/story_teller/runtime.rb', line 32 def default_config @default_config ||= StoryTeller::Config.defaults.merge(@options).tap do |config| config[:word_wrap] ||= StoryTeller::Terminal.word_wrap end end |
#ensuring_player_location(&block) ⇒ Object
TODO: Ensuring the location of the player object requires that this method be invoked before the move @player, @location operation in the InformLibrary#play method. This means that ensuring the location must take place either in the game- defined Initialise() method, or else it can happen in a provided LibraryExtension method, but in that case it would be at risk of being overridden by a game-defined Initialise() method. TODO: Figure out what the best approach here is. I think that if a multi-player game is to supply an entrypoint module, it will have to handle the location ensurance itself.
127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/story_teller/runtime.rb', line 127 def ensuring_player_location(&block) if persist? install_persisted_startup_location_hook persisted_location = StoryTeller::Engine.player_object&.location StoryTeller::PersistedStartupLocation.with(persisted_location) { block.call } else block.call location = location_candidate raise "InformLibrary location may not be left unset" if location.nil? inform_library&.PlayerTo(location, 1) end end |
#flush_session_output ⇒ Object
256 257 258 |
# File 'lib/story_teller/runtime.rb', line 256 def flush_session_output write_output(session.drain_output) end |
#game_components ⇒ Object
58 59 60 |
# File 'lib/story_teller/runtime.rb', line 58 def game_components @game_components ||= @options.fetch(:game_components, '').split.map(&:to_sym) end |
#game_dir_name ⇒ Object
54 55 56 |
# File 'lib/story_teller/runtime.rb', line 54 def game_dir_name @game_dir_name ||= @options[:game_dir_name] end |
#game_file_path ⇒ Object
50 51 52 |
# File 'lib/story_teller/runtime.rb', line 50 def game_file_path @game_file_path ||= game.file_path end |
#game_path ⇒ Object
42 43 44 |
# File 'lib/story_teller/runtime.rb', line 42 def game_path @game_path ||= game.path end |
#grammar_module_path ⇒ Object
46 47 48 |
# File 'lib/story_teller/runtime.rb', line 46 def grammar_module_path @grammar_module_path ||= File.join(game_path, @options[:game_grammar_module_name]) end |
#identity ⇒ Object
186 187 188 |
# File 'lib/story_teller/runtime.rb', line 186 def identity self.object_id end |
#inform_library ⇒ Object
182 183 184 |
# File 'lib/story_teller/runtime.rb', line 182 def inform_library @inform_library ||= StoryTeller::Engine.library end |
#inspect ⇒ Object
334 335 336 |
# File 'lib/story_teller/runtime.rb', line 334 def inspect to_s end |
#install_persisted_startup_location_hook ⇒ Object
106 107 108 109 110 |
# File 'lib/story_teller/runtime.rb', line 106 def install_persisted_startup_location_hook return if InformLibrary < StoryTeller::PersistedStartupMove InformLibrary.prepend(StoryTeller::PersistedStartupMove) end |
#invocation_context ⇒ Object
74 75 76 |
# File 'lib/story_teller/runtime.rb', line 74 def invocation_context @invocation_context ||= Struct.new(*invocation_properties) end |
#invocation_identity ⇒ Object
190 191 192 |
# File 'lib/story_teller/runtime.rb', line 190 def invocation_identity @invocation_identity ||= StoryTeller::InvocationIdentity.new(self) end |
#invocation_properties ⇒ Object
70 71 72 |
# File 'lib/story_teller/runtime.rb', line 70 def invocation_properties @invocation_properties ||= @options.fetch(:properties, '').split.map(&:to_sym) end |
#invoke_main ⇒ Object
95 96 97 98 99 100 101 102 103 104 |
# File 'lib/story_teller/runtime.rb', line 95 def invoke_main ::Object.new.send(:Main) rescue NameError => e if UndefinedMainPattern.match?(e.) log.fatal "Main() definition is missing" else log.fatal "Fatal error invoking Main(): #{e.}" end abort 'Terminating' end |
#location_candidate ⇒ Object
141 142 143 144 145 |
# File 'lib/story_teller/runtime.rb', line 141 def location_candidate return persisted_location_candidate if persist? canonical_location_candidate end |
#manage_privileges ⇒ Object
198 199 200 201 202 203 204 205 |
# File 'lib/story_teller/runtime.rb', line 198 def manage_privileges apply_privileges(self.class, privileges: StoryTeller::Privileges) apply_privileges(InformLibrary, privileges: StoryTeller::Privileges) if defined?(InformLibrary) apply_privileges(StoryTeller::IO::Session, privileges: StoryTeller::Privileges) StoryTeller::PrivilegeGrants.mode = @options.fetch(:privilege_mode, :session_only) apply_invocation_privileges(self) end |
#persist? ⇒ Boolean
112 113 114 |
# File 'lib/story_teller/runtime.rb', line 112 def persist? @options[:persist] end |
#persisted_location_candidate ⇒ Object
154 155 156 157 158 |
# File 'lib/story_teller/runtime.rb', line 154 def persisted_location_candidate inform_library&.player&.location || inform_library&.location || inform_library&.player&.spawn_point end |
#persistence ⇒ Object
232 233 234 |
# File 'lib/story_teller/runtime.rb', line 232 def persistence StoryTeller::Persistence.instance end |
#play_game ⇒ Object
270 271 272 273 274 275 276 277 278 |
# File 'lib/story_teller/runtime.rb', line 270 def play_game log.trace "#{self}#play_game" configure_output use_persistent_selfobj bind_session ensuring_player_location { invoke_main } flush_session_output read_eval_print_loop end |
#privileged_identities(privilege) ⇒ Object
194 195 196 |
# File 'lib/story_teller/runtime.rb', line 194 def privileged_identities(privilege) [invocation_identity].select { |identity| identity.privileged?(privilege) } end |
#project_dir_path ⇒ Object
62 63 64 65 66 67 68 |
# File 'lib/story_teller/runtime.rb', line 62 def project_dir_path @project_dir_path ||= begin story_dir_path = File.(__dir__) lib_dir_path = File.(File.dirname(story_dir_path)) File.(File.dirname(lib_dir_path)) end end |
#quit_game ⇒ Object
280 281 282 283 284 285 286 |
# File 'lib/story_teller/runtime.rb', line 280 def quit_game write_output "[Hit enter to exit.]" $stdin.getc # Curses.getch # Curses.close_screen exit end |
#read ⇒ Object
236 237 238 239 |
# File 'lib/story_teller/runtime.rb', line 236 def read flush_session_output unless session.nil? $stdin.gets(chomp: true) end |
#read_eval_print_loop ⇒ Object
260 261 262 263 264 265 266 267 268 |
# File 'lib/story_teller/runtime.rb', line 260 def read_eval_print_loop loop do prompt unless session.state == :more flush_session_output write_output(session.process(read, inform_library)) end rescue Interrupt => e write_output("\n#{e.class.name}\n") end |
#reset_config_cache ⇒ Object
85 86 87 88 89 90 91 92 93 |
# File 'lib/story_teller/runtime.rb', line 85 def reset_config_cache @config_file_path = nil @default_config = nil @game_components = nil @game_dir_name = nil @game_file_path = nil @game_path = nil @grammar_module_path = nil end |
#restart_game ⇒ Object
288 289 290 |
# File 'lib/story_teller/runtime.rb', line 288 def restart_game L__M(:Restart, 2) end |
#restore_game(callback = nil) ⇒ Object
rubocop: disable Metrics/AbcSize rubocop: disable Metrics/MethodLength
314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/story_teller/runtime.rb', line 314 def restore_game(callback = nil) filename = format('%s.sav', game.name) files = Dir.glob(File.join(format('%s_*.sav', game.name))) filename = files.max unless files.empty? println "Enter a file name." print "Default is \"#{filename}\": " flush_session_output response = $stdin.gets&.strip.to_s filename = response unless response.empty? StoryTeller::Snapshots.import_from_file(filename) restore_player_location inform_library.println(callback&.call) end |
#restore_player_location ⇒ Object
rubocop: enable Metrics/CyclomaticComplexity
161 162 163 164 165 166 |
# File 'lib/story_teller/runtime.rb', line 161 def restore_player_location inform_library.PlayerTo( inform_library.player.location, -1 # Quietly ) end |
#save_game(callback = nil) ⇒ Object
rubocop: disable Metrics/AbcSize rubocop: disable Metrics/MethodLength
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/story_teller/runtime.rb', line 294 def save_game(callback = nil) = Time.now.utc.strftime('%Y-%m-%d_%H%M%S') filename = format( '%<game_name>s_%<timestamp>s.sav', game_name: game.name, timestamp: ) println "Enter a file name." print "Default is \"#{filename}\": " flush_session_output response = $stdin.gets&.strip.to_s filename = response unless response.empty? StoryTeller::Snapshots.export_to_file(filename) inform_library.println(callback&.call) end |
#to_s ⇒ Object
rubocop: enable Metrics/AbcSize rubocop: enable Metrics/MethodLength
330 331 332 |
# File 'lib/story_teller/runtime.rb', line 330 def to_s "#<#{self.class.name}:#{object_id}>" end |
#use_persistent_selfobj ⇒ Object
Initially, inform_library defaults to using Inform::Parser::SelfObj. However, that SelfObj is by default an instance of an Inform::Ephemeral::Object. Of course, persistence is not really required for a play session of a single player story game, since the snapshot file save feature is implemented. Nevertheless, it is the intention of this application to demonstrate persistence of Inform-esque data into a PostgreSQL database. And so the default selfobj is overridden here with a non-ephemeral version.
176 177 178 179 180 |
# File 'lib/story_teller/runtime.rb', line 176 def use_persistent_selfobj require_relative 'player_character' StoryTeller::Engine.player_object = StoryTeller::PlayerCharacter("(self object)") end |
#write_output(value) ⇒ Object
247 248 249 250 251 252 253 254 |
# File 'lib/story_teller/runtime.rb', line 247 def write_output(value) return if value.nil? || value.empty? output = StoryTeller::IO.default_output || $stdout output.write(value) $stdout.flush if output.equal?($stdout) end |