gamefic 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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