gamefic 1.4.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gamefic.rb +1 -2
  3. data/lib/gamefic/character.rb +31 -45
  4. data/lib/gamefic/director/delegate.rb +46 -24
  5. data/lib/gamefic/director/order.rb +7 -0
  6. data/lib/gamefic/director/parser.rb +11 -11
  7. data/lib/gamefic/engine/base.rb +2 -3
  8. data/lib/gamefic/entity.rb +8 -26
  9. data/lib/gamefic/plot.rb +38 -242
  10. data/lib/gamefic/plot/callbacks.rb +101 -0
  11. data/lib/gamefic/plot/command_mount.rb +70 -40
  12. data/lib/gamefic/plot/entities.rb +82 -0
  13. data/lib/gamefic/plot/host.rb +46 -0
  14. data/lib/gamefic/plot/playbook.rb +192 -0
  15. data/lib/gamefic/plot/players.rb +15 -0
  16. data/lib/gamefic/plot/scene_mount.rb +69 -31
  17. data/lib/gamefic/plot/snapshot.rb +20 -5
  18. data/lib/gamefic/scene/active.rb +8 -1
  19. data/lib/gamefic/scene/base.rb +4 -26
  20. data/lib/gamefic/scene/custom.rb +53 -3
  21. data/lib/gamefic/scene/multiple_choice.rb +1 -0
  22. data/lib/gamefic/scene/yes_or_no.rb +1 -1
  23. data/lib/gamefic/scene_data/multiple_scene.rb +0 -4
  24. data/lib/gamefic/shell.rb +0 -1
  25. data/lib/gamefic/source/file.rb +1 -1
  26. data/lib/gamefic/stage.rb +10 -2
  27. data/lib/gamefic/subplot.rb +70 -53
  28. data/lib/gamefic/syntax.rb +9 -2
  29. data/lib/gamefic/tester.rb +1 -1
  30. data/lib/gamefic/text.rb +8 -0
  31. data/lib/gamefic/{ansi.rb → text/ansi.rb} +12 -15
  32. data/lib/gamefic/text/html.rb +68 -0
  33. data/lib/gamefic/text/html/conversions.rb +250 -0
  34. data/lib/gamefic/text/html/entities.rb +9 -0
  35. data/lib/gamefic/tty.rb +2 -0
  36. data/lib/gamefic/user/tty.rb +2 -4
  37. data/lib/gamefic/version.rb +1 -1
  38. metadata +12 -8
  39. data/lib/gamefic/direction.rb +0 -46
  40. data/lib/gamefic/html.rb +0 -68
  41. data/lib/gamefic/html_to_ansi.rb +0 -185
  42. data/lib/gamefic/plot/entity_mount.rb +0 -45
  43. data/lib/gamefic/rule.rb +0 -18
@@ -3,31 +3,6 @@ require 'gamefic/action'
3
3
  module Gamefic
4
4
 
5
5
  module Plot::CommandMount
6
- # Create a Meta Action that responds to a command.
7
- # Meta Actions are very similar to standard Actions, except the Plot
8
- # understands them to be commands that operate above and/or outside of the
9
- # actual game world. Examples of Meta Actions are commands that report the
10
- # player's current score, save and restore saved games, or list the game's
11
- # credits.
12
- #
13
- # @example A simple Meta Action
14
- # meta :credits do |actor|
15
- # actor.tell "This game was written by John Smith."
16
- # end
17
- #
18
- # @param command [Symbol] An imperative verb for the command
19
- # @param *queries [Array<Query::Base>] Queries to filter the command's tokens
20
- # @yieldparam [Character]
21
- def meta(command, *queries, &proc)
22
- act = self.action(command, *queries, &proc)
23
- act.meta = true
24
- act
25
- end
26
- def action(command, *queries, &proc)
27
- act = Action.new(command, *queries, &proc)
28
- add_action act
29
- act
30
- end
31
6
  # Create an Action that responds to a command.
32
7
  # An Action uses the command argument to identify the imperative verb that
33
8
  # triggers the action.
@@ -51,8 +26,59 @@ module Gamefic
51
26
  # @param *queries [Array<Query::Base>] Queries to filter the command's tokens
52
27
  # @yieldparam [Character]
53
28
  def respond(command, *queries, &proc)
54
- self.action(command, *queries, &proc)
29
+ playbook.respond(command, *queries, &proc)
55
30
  end
31
+
32
+ # Create a Meta Action that responds to a command.
33
+ # Meta Actions are very similar to standard Actions, except the Plot
34
+ # understands them to be commands that operate above and/or outside of the
35
+ # actual game world. Examples of Meta Actions are commands that report the
36
+ # player's current score, save and restore saved games, or list the game's
37
+ # credits.
38
+ #
39
+ # @example A simple Meta Action
40
+ # meta :credits do |actor|
41
+ # actor.tell "This game was written by John Smith."
42
+ # end
43
+ #
44
+ # @param command [Symbol] An imperative verb for the command
45
+ # @param *queries [Array<Query::Base>] Queries to filter the command's tokens
46
+ # @yieldparam [Character]
47
+ def meta(command, *queries, &proc)
48
+ playbook.meta command, *queries, &proc
49
+ end
50
+
51
+ # @deprecated
52
+ def action(command, *queries, &proc)
53
+ respond command, *queries, &proc
54
+ end
55
+
56
+ # Declare a dismabiguation response for actions.
57
+ # The disambigurator is executed when an action expects an argument to
58
+ # reference a specific entity but its query matched more than one. For
59
+ # example, "red" might refer to either a red key or a red book.
60
+ #
61
+ # If a disambiguator is not defined, the playbook will use its default
62
+ # implementation.
63
+ #
64
+ # @example Tell the player the list of ambiguous entities.
65
+ # disambiguate do |actor, entities|
66
+ # actor.tell "I don't know which you mean: #{entities.join_or}."
67
+ # end
68
+ #
69
+ # @yieldparam [Gamefic::Character]
70
+ # @yieldparam [Array<Gamefic::Entity>]
71
+ def disambiguate &block
72
+ playbook.disambiguate &block
73
+ end
74
+
75
+ # Validate an order before a character can execute its command.
76
+ #
77
+ # @yieldparam [Gamefic::Director::Order]
78
+ def validate &block
79
+ playbook.validate &block
80
+ end
81
+
56
82
  # Create an alternate Syntax for an Action.
57
83
  # The command and its translation can be parameterized.
58
84
  #
@@ -68,23 +94,27 @@ module Gamefic
68
94
  # @param translation [String] The format of the translated command
69
95
  # @return [Syntax] the Syntax object
70
96
  def interpret command, translation
71
- xlate command, translation
97
+ playbook.interpret command, translation
72
98
  end
73
- def syntax(*args)
74
- xlate(*args)
99
+
100
+ # @deprecated
101
+ def xlate command, translation
102
+ interpret command, translation
75
103
  end
76
- def xlate(*args)
77
- syn = Syntax.new(*args)
78
- add_syntax syn
79
- syn
104
+
105
+ # Get an Array of available verbs.
106
+ # If the to_s parameter is true, convert Symbols to Strings.
107
+ #
108
+ # @return [Array<Symbol|String>]
109
+ def verbs to_s: false
110
+ to_s ? playbook.verbs.map { |v| v.to_s } : playbook.verbs
80
111
  end
81
- def commandwords
82
- words = Array.new
83
- syntaxes.each { |s|
84
- word = s.first_word
85
- words.push(word) if !word.nil?
86
- }
87
- words.uniq
112
+
113
+ # Get an Array of all Actions defined in the Plot.
114
+ #
115
+ # @return [Array<Action>]
116
+ def actions
117
+ playbook.actions
88
118
  end
89
119
  end
90
120
 
@@ -0,0 +1,82 @@
1
+ module Gamefic
2
+
3
+ class Plot
4
+ module Entities
5
+ # Make a new Entity with the provided properties.
6
+ #
7
+ # @example Create an Entity
8
+ # chair = make Entity, name: 'red chair'
9
+ # chair.name #=> 'red chair'
10
+ #
11
+ # @param cls [Class] The Class of the Entity to be created.
12
+ # @param args [Hash] The entity's properties.
13
+ # @return The Entity instance.
14
+ def make cls, args = {}, &block
15
+ 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?
21
+ ent.playbook = playbook if ent.kind_of?(Character)
22
+ ent
23
+ end
24
+
25
+ def destroy entity
26
+ if p_dynamic.include?(entity)
27
+ p_entities.delete entity
28
+ p_dynamic.delete entity
29
+ p_players.delete entity
30
+ end
31
+ entity.parent = nil
32
+ end
33
+
34
+ # Pick an entity based on its description.
35
+ # The description provided must match exactly one entity; otherwise
36
+ # an error is raised.
37
+ #
38
+ # @example Select the Entity that matches the description
39
+ # red_chair = make Entity, :name => 'red chair'
40
+ # blue_chair make Entity, :name => 'blue chair'
41
+ # pick "red chair" #=> red_chair
42
+ # pick "blue chair" #=> blue_chair
43
+ # pick "chair" #=> IndexError: description is ambiguous
44
+ #
45
+ # @param @description [String] The description of the entity
46
+ # @return [Entity] The entity that matches the description
47
+ def pick(description)
48
+ query = Gamefic::Query::Base.new
49
+ result = query.match(description, entities)
50
+ if result.objects.length == 0
51
+ raise IndexError.new("Unable to find entity from '#{description}'")
52
+ elsif result.objects.length > 1
53
+ raise IndexError.new("Ambiguous entities found from '#{description}'")
54
+ end
55
+ result.objects[0]
56
+ end
57
+
58
+ def entities
59
+ p_entities.clone
60
+ end
61
+
62
+ def players
63
+ p_players.clone
64
+ end
65
+
66
+ private
67
+
68
+ def p_entities
69
+ @p_entities ||= []
70
+ end
71
+
72
+ def p_players
73
+ @p_players ||= []
74
+ end
75
+
76
+ def p_dynamic
77
+ @p_dynamic ||= []
78
+ end
79
+ end
80
+ end
81
+
82
+ end
@@ -0,0 +1,46 @@
1
+ require 'gamefic/subplot'
2
+
3
+ module Gamefic
4
+
5
+ module Plot::Host
6
+ # Get an array of all the current subplots.
7
+ #
8
+ # @return [Array<Subplot>]
9
+ def subplots
10
+ p_subplots.clone
11
+ end
12
+
13
+ # Start a new subplot based on the provided class.
14
+ #
15
+ # @param [Class] The class of the subplot to be created (Subplot by default)
16
+ # @return [Subplot]
17
+ def branch subplot_class = Gamefic::Subplot, introduce: nil
18
+ subplot = subplot_class.new(self, introduce: introduce)
19
+ p_subplots.push subplot
20
+ subplot
21
+ end
22
+
23
+ # Get the player's current subplot or nil if none exists.
24
+ #
25
+ # @return [Subplot]
26
+ def subplot_for player
27
+ subplots.each { |s|
28
+ return s if s.players.include?(player)
29
+ }
30
+ nil
31
+ end
32
+
33
+ # Determine whether the player is involved in a subplot.
34
+ #
35
+ # @return [Boolean]
36
+ def subbed? player
37
+ !subplot_for(player).nil?
38
+ end
39
+
40
+ private
41
+ def p_subplots
42
+ @p_subplots ||= []
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,192 @@
1
+ module Gamefic
2
+
3
+ class Plot
4
+ class Playbook
5
+ def initialize commands: {}, syntaxes: [], validators: [], disambiguator: nil
6
+ @commands = commands
7
+ @syntaxes = syntaxes
8
+ @validators = validators
9
+ @disambiguator = disambiguator
10
+ end
11
+
12
+ def syntaxes
13
+ @syntaxes
14
+ end
15
+
16
+ def actions
17
+ @commands.values.flatten
18
+ end
19
+
20
+ def verbs
21
+ @commands.keys
22
+ end
23
+
24
+ def validators
25
+ @validators
26
+ end
27
+
28
+ def disambiguator
29
+ @disambiguator ||= Action.new(nil, Query::Base.new) do |actor, entities|
30
+ definites = []
31
+ entities.each { |entity|
32
+ definites.push entity.definitely
33
+ }
34
+ actor.tell "I don't know which you mean: #{definites.join_or}."
35
+ end
36
+ end
37
+
38
+ def disambiguate &block
39
+ @disambiguator = Action.new(nil, Query::Base.new, &block)
40
+ @disambiguator.meta = true
41
+ @disambiguator
42
+ end
43
+
44
+ def validate &block
45
+ @validators.push block
46
+ end
47
+
48
+ # Get an Array of all Actions associated with the specified verb.
49
+ #
50
+ # @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
51
+ # @return [Array<Action>] The verb's associated Actions
52
+ def actions_for verb
53
+ @commands[verb] || []
54
+ end
55
+
56
+ # Create an Action that responds to a command.
57
+ # An Action uses the command argument to identify the imperative verb that
58
+ # triggers the action.
59
+ # It can also accept queries to tokenize the remainder of the input and
60
+ # filter for particular entities or properties.
61
+ # The block argument contains the code to be executed when the input
62
+ # matches all of the Action's criteria (i.e., verb and queries).
63
+ #
64
+ # @example A simple Action.
65
+ # respond :salute do |actor|
66
+ # actor.tell "Hello, sir!"
67
+ # end
68
+ # # The command "salute" will respond "Hello, sir!"
69
+ #
70
+ # @example An Action that accepts a Character
71
+ # respond :salute, Use.visible(Character) do |actor, character|
72
+ # actor.tell "#{The character} returns your salute."
73
+ # end
74
+ #
75
+ # @param command [Symbol] An imperative verb for the command
76
+ # @param *queries [Array<Query::Base>] Queries to filter the command's tokens
77
+ # @yieldparam [Character]
78
+ def respond(command, *queries, &proc)
79
+ act = Action.new(command, *queries, &proc)
80
+ add_action act
81
+ act
82
+ end
83
+
84
+ # Create a Meta Action that responds to a command.
85
+ # Meta Actions are very similar to standard Actions, except the Plot
86
+ # understands them to be commands that operate above and/or outside of the
87
+ # actual game world. Examples of Meta Actions are commands that report the
88
+ # player's current score, save and restore saved games, or list the game's
89
+ # credits.
90
+ #
91
+ # @example A simple Meta Action
92
+ # meta :credits do |actor|
93
+ # actor.tell "This game was written by John Smith."
94
+ # end
95
+ #
96
+ # @param command [Symbol] An imperative verb for the command
97
+ # @param *queries [Array<Query::Base>] Queries to filter the command's tokens
98
+ # @yieldparam [Character]
99
+ def meta(command, *queries, &proc)
100
+ act = respond(command, *queries, &proc)
101
+ act.meta = true
102
+ act
103
+ end
104
+
105
+ # Create an alternate Syntax for an Action.
106
+ # The command and its translation can be parameterized.
107
+ #
108
+ # @example Create a synonym for the Inventory Action.
109
+ # interpret "catalogue", "inventory"
110
+ # # The command "catalogue" will be translated to "inventory"
111
+ #
112
+ # @example Create a parameterized synonym for the Look Action.
113
+ # interpret "scrutinize :entity", "look :entity"
114
+ # # The command "scrutinize chair" will be translated to "look chair"
115
+ #
116
+ # @param command [String] The format of the original command
117
+ # @param translation [String] The format of the translated command
118
+ # @return [Syntax] the Syntax object
119
+ def interpret(*args)
120
+ syn = Syntax.new(*args)
121
+ add_syntax syn
122
+ syn
123
+ end
124
+
125
+ # Duplicate the playbook.
126
+ # This method will duplicate the commands hash and the syntax array so
127
+ # the new playbook can be modified without affecting the original.
128
+ #
129
+ # @return [Playbook]
130
+ def dup
131
+ Playbook.new commands: @commands.dup, syntaxes: @syntaxes.dup
132
+ end
133
+
134
+ def freeze
135
+ @commands.freeze
136
+ @syntaxes.freeze
137
+ end
138
+
139
+ private
140
+
141
+ def add_action(action)
142
+ @commands[action.verb] ||= []
143
+ @commands[action.verb].unshift action
144
+ @commands[action.verb].sort! { |a, b|
145
+ if a.specificity == b.specificity
146
+ # Newer action takes precedence
147
+ b.order_key <=> a.order_key
148
+ else
149
+ # Higher specificity takes precedence
150
+ b.specificity <=> a.specificity
151
+ end
152
+ }
153
+ generate_default_syntax action
154
+ end
155
+
156
+ def generate_default_syntax action
157
+ user_friendly = action.verb.to_s.gsub(/_/, ' ')
158
+ args = []
159
+ used_names = []
160
+ action.queries.each { |c|
161
+ num = 1
162
+ new_name = ":var"
163
+ while used_names.include? new_name
164
+ num = num + 1
165
+ new_name = ":var#{num}"
166
+ end
167
+ used_names.push new_name
168
+ user_friendly += " #{new_name}"
169
+ args.push new_name
170
+ }
171
+ add_syntax Syntax.new(user_friendly.strip, "#{action.verb} #{args.join(' ')}")
172
+ end
173
+
174
+ def add_syntax syntax
175
+ if @commands[syntax.verb] == nil
176
+ raise "No actions exist for \"#{syntax.verb}\""
177
+ end
178
+ @syntaxes.unshift syntax
179
+ @syntaxes.uniq
180
+ @syntaxes.sort! { |a, b|
181
+ if a.token_count == b.token_count
182
+ # For syntaxes of the same length, length of action takes precedence
183
+ b.first_word <=> a.first_word
184
+ else
185
+ b.token_count <=> a.token_count
186
+ end
187
+ }
188
+ end
189
+ end
190
+ end
191
+
192
+ end