gamefic 1.5.1 → 1.6.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gamefic.rb +1 -3
  3. data/lib/gamefic/action.rb +140 -79
  4. data/lib/gamefic/character.rb +120 -53
  5. data/lib/gamefic/character/state.rb +12 -0
  6. data/lib/gamefic/core_ext/array.rb +53 -11
  7. data/lib/gamefic/core_ext/string.rb +1 -0
  8. data/lib/gamefic/describable.rb +37 -11
  9. data/lib/gamefic/engine/base.rb +17 -4
  10. data/lib/gamefic/engine/tty.rb +4 -0
  11. data/lib/gamefic/entity.rb +4 -15
  12. data/lib/gamefic/matchable.rb +50 -0
  13. data/lib/gamefic/messaging.rb +45 -0
  14. data/lib/gamefic/node.rb +16 -0
  15. data/lib/gamefic/plot.rb +27 -33
  16. data/lib/gamefic/plot/{article_mount.rb → articles.rb} +22 -22
  17. data/lib/gamefic/plot/callbacks.rb +30 -4
  18. data/lib/gamefic/plot/{command_mount.rb → commands.rb} +121 -121
  19. data/lib/gamefic/plot/entities.rb +3 -3
  20. data/lib/gamefic/plot/host.rb +3 -3
  21. data/lib/gamefic/plot/playbook.rb +74 -30
  22. data/lib/gamefic/plot/scenes.rb +149 -0
  23. data/lib/gamefic/plot/snapshot.rb +14 -39
  24. data/lib/gamefic/plot/theater.rb +73 -0
  25. data/lib/gamefic/query.rb +6 -19
  26. data/lib/gamefic/query/base.rb +127 -246
  27. data/lib/gamefic/query/children.rb +6 -7
  28. data/lib/gamefic/query/descendants.rb +15 -0
  29. data/lib/gamefic/query/family.rb +19 -7
  30. data/lib/gamefic/query/itself.rb +13 -0
  31. data/lib/gamefic/query/matches.rb +67 -11
  32. data/lib/gamefic/query/parent.rb +6 -7
  33. data/lib/gamefic/query/siblings.rb +10 -7
  34. data/lib/gamefic/query/text.rb +39 -35
  35. data/lib/gamefic/scene.rb +1 -1
  36. data/lib/gamefic/scene/active.rb +12 -6
  37. data/lib/gamefic/scene/base.rb +56 -5
  38. data/lib/gamefic/scene/conclusion.rb +3 -0
  39. data/lib/gamefic/scene/custom.rb +0 -83
  40. data/lib/gamefic/scene/multiple_choice.rb +54 -32
  41. data/lib/gamefic/scene/multiple_scene.rb +11 -6
  42. data/lib/gamefic/scene/pause.rb +3 -4
  43. data/lib/gamefic/scene/yes_or_no.rb +23 -9
  44. data/lib/gamefic/script/base.rb +4 -0
  45. data/lib/gamefic/subplot.rb +22 -19
  46. data/lib/gamefic/syntax.rb +7 -15
  47. data/lib/gamefic/user/base.rb +7 -13
  48. data/lib/gamefic/user/buffer.rb +7 -0
  49. data/lib/gamefic/user/tty.rb +13 -12
  50. data/lib/gamefic/version.rb +1 -1
  51. metadata +11 -37
  52. data/lib/gamefic/director.rb +0 -23
  53. data/lib/gamefic/director/delegate.rb +0 -126
  54. data/lib/gamefic/director/order.rb +0 -17
  55. data/lib/gamefic/director/parser.rb +0 -137
  56. data/lib/gamefic/keywords.rb +0 -67
  57. data/lib/gamefic/plot/query_mount.rb +0 -9
  58. data/lib/gamefic/plot/scene_mount.rb +0 -182
  59. data/lib/gamefic/query/ambiguous_children.rb +0 -5
  60. data/lib/gamefic/query/expression.rb +0 -47
  61. data/lib/gamefic/query/many_children.rb +0 -7
  62. data/lib/gamefic/query/plural_children.rb +0 -14
  63. data/lib/gamefic/query/self.rb +0 -10
  64. data/lib/gamefic/scene_data.rb +0 -10
  65. data/lib/gamefic/scene_data/base.rb +0 -12
  66. data/lib/gamefic/scene_data/multiple_choice.rb +0 -19
  67. data/lib/gamefic/scene_data/multiple_scene.rb +0 -21
  68. data/lib/gamefic/scene_data/yes_or_no.rb +0 -18
  69. data/lib/gamefic/serialized.rb +0 -24
  70. data/lib/gamefic/stage.rb +0 -106
@@ -8,54 +8,76 @@ module Gamefic
8
8
  # instead of a String.
9
9
  #
10
10
  class Scene::MultipleChoice < Scene::Custom
11
- def data_class
12
- SceneData::MultipleChoice
13
- end
11
+ attr_reader :index
12
+ attr_reader :number
13
+ attr_reader :selection
14
+ attr_writer :invalid_message
14
15
 
15
- def start actor
16
- data = start_data_for(actor)
17
- data.options.clear
18
- do_start_block actor, data
19
- tell_options actor, data
16
+ def post_initialize
17
+ self.type = 'MultipleChoice'
18
+ self.prompt = 'Enter a choice:'
20
19
  end
20
+
21
+ #def start actor
22
+ # data = start_data_for(actor)
23
+ # data.clear
24
+ # do_start_block actor, data
25
+ # tell_options
26
+ #end
21
27
 
22
- def finish actor, input
23
- data = finish_data_for(actor, input)
24
- get_choice data
25
- if data.selection.nil?
26
- actor.tell data.invalid_message
27
- tell_options actor, data
28
+ def start
29
+ super
30
+ raise "MultipleChoice scene has zero options" if options.empty?
31
+ end
32
+
33
+ def finish
34
+ #data = finish_data_for(actor, input)
35
+ get_choice
36
+ if selection.nil?
37
+ actor.tell invalid_message
38
+ tell_options
28
39
  else
29
- do_finish_block actor, data
40
+ super
30
41
  end
31
- data
42
+ end
43
+
44
+ def options
45
+ @options ||= []
46
+ end
47
+
48
+ def invalid_message
49
+ @invalid_message ||= 'That is not a valid choice.'
50
+ end
51
+
52
+ def state
53
+ super.merge options: options
32
54
  end
33
55
 
34
56
  private
35
-
36
- def get_choice data
37
- if data.input.strip =~ /[0-9]+/ and data.input.to_i > 0
38
- data.number = data.input.to_i
39
- data.index = data.number - 1
40
- data.selection = data.options[data.number - 1]
57
+
58
+ def get_choice
59
+ if input.strip =~ /^[0-9]+$/ and input.to_i > 0
60
+ @number = input.to_i
61
+ @index = number - 1
62
+ @selection = options[index]
41
63
  else
42
- index = 0
43
- data.options.each { |o|
44
- if o.casecmp(data.input).zero?
45
- data.selection = o
46
- data.index = index
47
- data.number = index + 1
64
+ i = 0
65
+ options.each { |o|
66
+ if o.casecmp(input).zero?
67
+ @selection = o
68
+ @index = i
69
+ @number = index + 1
48
70
  break
49
71
  end
50
- index += 1
72
+ i += 1
51
73
  }
52
74
  end
53
75
  end
54
76
 
55
- def tell_options actor, data
77
+ def tell_options
56
78
  list = '<ol class="multiple_choice">'
57
- data.options.each { |o|
58
- list += "<li>#{o}</li>"
79
+ options.each { |o|
80
+ list += "<li><a href=\"#\" rel=\"gamefic\" data-command=\"#{o}\">#{o}</a></li>"
59
81
  }
60
82
  list += "</ol>"
61
83
  actor.tell list
@@ -1,14 +1,19 @@
1
1
  module Gamefic
2
2
 
3
3
  class Scene::MultipleScene < Scene::MultipleChoice
4
- def data_class
5
- SceneData::MultipleScene
4
+ def option_map
5
+ @option_map ||= {}
6
6
  end
7
7
 
8
- def finish actor, input
9
- data = super
10
- unless data.selection.nil?
11
- actor.cue data.scene_for(data.selection)
8
+ def map option, scene
9
+ options.push option
10
+ option_map[option] = scene
11
+ end
12
+
13
+ def finish
14
+ get_choice
15
+ unless selection.nil?
16
+ actor.cue option_map[selection]
12
17
  end
13
18
  end
14
19
  end
@@ -3,10 +3,9 @@ module Gamefic
3
3
  # Pause for user input.
4
4
  #
5
5
  class Scene::Pause < Scene::Custom
6
- def start actor
7
- data = start_data_for(actor)
8
- data.prompt = 'Press enter to continue...'
9
- do_start_block actor, data
6
+ def post_initialize
7
+ self.type = 'Pause'
8
+ self.prompt = 'Press enter to continue...'
10
9
  end
11
10
  end
12
11
 
@@ -6,18 +6,32 @@ module Gamefic
6
6
  # other scene has been prepared or cued.
7
7
  #
8
8
  class Scene::YesOrNo < Scene::Custom
9
- def data_class
10
- SceneData::YesOrNo
9
+ def post_initialize
10
+ self.type = 'YesOrNo'
11
+ self.prompt = 'Yes or No?'
11
12
  end
12
13
 
13
- def finish actor, input
14
- data = finish_data_for(actor, input)
15
- if data.yes? or data.no?
16
- this_scene = actor.scene
17
- do_finish_block actor, data
18
- #actor.cue :active if (actor.scene == this_scene and actor.next_scene.nil?)
14
+ def yes?
15
+ input.to_s[0,1].downcase == 'y'
16
+ end
17
+
18
+ def no?
19
+ input.to_s[0,1].downcase == 'n'
20
+ end
21
+
22
+ def invalid_message
23
+ @invalid_message ||= 'Please enter Yes or No.'
24
+ end
25
+
26
+ def prompt
27
+ @prompt ||= 'Yes or No?'
28
+ end
29
+
30
+ def finish
31
+ if yes? or no?
32
+ super
19
33
  else
20
- actor.tell data.invalid_message
34
+ actor.tell invalid_message
21
35
  end
22
36
  end
23
37
  end
@@ -4,6 +4,7 @@ module Gamefic
4
4
  def initialize
5
5
  raise "#initialize must be defined in subclasses"
6
6
  end
7
+
7
8
  # Get the script's text.
8
9
  # The text must be source code suitable for evaluation via Plot#stage.
9
10
  #
@@ -11,12 +12,14 @@ module Gamefic
11
12
  def read
12
13
  raise "#read must be defined in subclasses"
13
14
  end
15
+
14
16
  # Get the script's path.
15
17
  #
16
18
  # @return [String]
17
19
  def path
18
20
  raise "#path must be defined in subclasses"
19
21
  end
22
+
20
23
  # Get the absolute path of the script's original file, or its URL for
21
24
  # sources that are not file-based.
22
25
  #
@@ -24,6 +27,7 @@ module Gamefic
24
27
  def absolute_path
25
28
  raise "#absolute_path must be defined in subclasses"
26
29
  end
30
+
27
31
  # Script objects are equal if their relative paths are the same. Note that
28
32
  # they are still considered equal if their absolute paths are different,
29
33
  # or even if they come from different types of sources.
@@ -3,42 +3,40 @@ require 'gamefic/plot'
3
3
  module Gamefic
4
4
 
5
5
  class Subplot
6
- include Stage
7
-
8
6
  attr_reader :plot
9
7
  attr_writer :denied_message
10
8
 
11
- # @!parse include Plot::Entities
12
- mount Plot::Entities
13
- # @!parse include Plot::CommandMount
14
- mount Plot::CommandMount
15
- # @!parse include Plot::Callbacks
16
- mount Plot::Callbacks
17
- # @!parse include Plot::SceneMount
18
- mount Plot::SceneMount
19
-
20
- expose :plot, :conclude
9
+ include Plot::Theater
10
+ include Plot::Entities
11
+ include Plot::Commands
12
+ include Plot::Callbacks
13
+ include Plot::Scenes
14
+ include Plot::Articles
21
15
 
22
16
  class << self
23
- def start_proc
24
- @start_proc
25
- end
17
+ attr_reader :start_proc
26
18
 
27
- private
19
+ protected
28
20
 
29
21
  def on_start &block
30
22
  @start_proc = block
31
23
  end
32
24
  end
33
25
 
34
- def initialize plot, introduce: nil
26
+ def initialize plot, introduce: nil, next_cue: nil, busy_cue: nil
35
27
  @plot = plot
28
+ @next_cue = next_cue
29
+ @busy_cue = busy_cue
36
30
  @concluded = false
37
31
  stage &self.class.start_proc unless self.class.start_proc.nil?
38
32
  playbook.freeze
39
33
  self.introduce introduce unless introduce.nil?
40
34
  end
41
35
 
36
+ def subplot
37
+ self
38
+ end
39
+
42
40
  def default_scene
43
41
  plot.default_scene
44
42
  end
@@ -62,7 +60,11 @@ module Gamefic
62
60
 
63
61
  def introduce player
64
62
  if plot.in_subplot?(player)
65
- player.tell denied_message
63
+ if @busy_cue.nil?
64
+ player.tell denied_message
65
+ else
66
+ player.cue @busy_cue
67
+ end
66
68
  else
67
69
  super
68
70
  end
@@ -70,6 +72,7 @@ module Gamefic
70
72
 
71
73
  def exeunt player
72
74
  player.playbook = plot.playbook
75
+ player.cue (@next_cue || default_scene)
73
76
  p_players.delete player
74
77
  end
75
78
 
@@ -100,5 +103,5 @@ module Gamefic
100
103
  call_update
101
104
  end
102
105
  end
103
-
106
+
104
107
  end
@@ -6,8 +6,7 @@ module Gamefic
6
6
  attr_reader :token_count, :first_word, :verb, :template, :command
7
7
  @@phrase = '([\w\W\s\S]*?)'
8
8
 
9
- def initialize template, *command
10
- command = command.join(' ')
9
+ def initialize template, command
11
10
  words = template.split_words
12
11
  @token_count = words.length
13
12
  command_words = command.split_words
@@ -99,19 +98,12 @@ module Gamefic
99
98
  result = syntax.tokenize text
100
99
  matches.push(result) if !result.nil?
101
100
  }
102
- # Sort matches having the most populated arguments first
103
- matches.sort! { |a, b|
104
- ca = 0
105
- cb = 0
106
- a.arguments.each { |t|
107
- break if t.nil?
108
- ca += 1
109
- }
110
- b.arguments.each { |t|
111
- break if t.nil?
112
- cb += 1
113
- }
114
- cb <=> ca
101
+ matches.sort! { |a,b|
102
+ if a.arguments.length == b.arguments.length
103
+ b.verb.to_s <=> a.verb.to_s
104
+ else
105
+ b.arguments.length <=> a.arguments.length
106
+ end
115
107
  }
116
108
  matches
117
109
  end
@@ -1,21 +1,15 @@
1
1
  module Gamefic
2
-
3
- # The base user provides methods for handling messages received from plots.
4
- #
5
- class User::Base
6
- def send message
7
- buffer.send message
2
+ class User::Base
3
+ def update state
4
+ raise 'Unimplemented'
8
5
  end
9
6
 
10
- def flush
11
- buffer.flush
7
+ def save filename, snapshot
8
+ raise 'Unimplemented'
12
9
  end
13
10
 
14
- private
15
-
16
- def buffer
17
- @buffer ||= User::Buffer.new
11
+ def restore filename
12
+ raise 'Unimplemented'
18
13
  end
19
14
  end
20
-
21
15
  end
@@ -12,6 +12,13 @@ module Gamefic
12
12
  @data += message
13
13
  end
14
14
 
15
+ # Read the current buffer without clearing it.
16
+ #
17
+ # @return [String]
18
+ def peek
19
+ @data
20
+ end
21
+
15
22
  # Get the current data and clear the buffer.
16
23
  #
17
24
  # @return [String]
@@ -6,10 +6,17 @@ module Gamefic
6
6
 
7
7
  # Extend User::Base to convert HTML into ANSI text.
8
8
  #
9
+ # @note Due to their dependency on io/console, User::Tty and Engine::Tty are
10
+ # not included in the core Gamefic library. `require gamefic/tty` if you
11
+ # need them.
12
+ #
9
13
  class User::Tty < User::Base
14
+ def update state
15
+ print Gamefic::Text::Html::Conversions.html_to_ansi(state[:output])
16
+ end
17
+
10
18
  def save filename, snapshot
11
- data = snapshot.merge(:metadata => @character.plot.metadata)
12
- json = JSON.generate data
19
+ json = JSON.generate snapshot
13
20
  if json.nil?
14
21
  @character.tell "Nothing to save."
15
22
  end
@@ -21,7 +28,6 @@ module Gamefic
21
28
  File.open(filename, 'w') do |f|
22
29
  f.write json
23
30
  end
24
- @character.tell "Game saved."
25
31
  end
26
32
  end
27
33
 
@@ -33,21 +39,16 @@ module Gamefic
33
39
  if filename != ''
34
40
  if File.exists?(filename)
35
41
  data = JSON.parse File.read(filename), symbolize_names: true
36
- if (data[:metadata] != @character.plot.metadata)
37
- @character.tell "The save file is not compatible with this version of the game."
38
- else
42
+ #if (data[:metadata] != @character.plot.metadata)
43
+ # @character.tell "The save file is not compatible with this version of the game."
44
+ #else
39
45
  return data
40
- end
46
+ #end
41
47
  else
42
48
  @character.tell "File \"#{filename}\" not found."
43
49
  end
44
50
  end
45
51
  nil
46
52
  end
47
-
48
- def flush
49
- Gamefic::Text::Html::Conversions.html_to_ansi(super)
50
- end
51
53
  end
52
-
53
54
  end
@@ -1,3 +1,3 @@
1
1
  module Gamefic
2
- VERSION = '1.5.1'
2
+ VERSION = '1.6.0'
3
3
  end