gamefic 1.4.1 → 1.5.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.
- checksums.yaml +4 -4
- data/lib/gamefic.rb +1 -2
- data/lib/gamefic/character.rb +31 -45
- data/lib/gamefic/director/delegate.rb +46 -24
- data/lib/gamefic/director/order.rb +7 -0
- data/lib/gamefic/director/parser.rb +11 -11
- data/lib/gamefic/engine/base.rb +2 -3
- data/lib/gamefic/entity.rb +8 -26
- data/lib/gamefic/plot.rb +38 -242
- data/lib/gamefic/plot/callbacks.rb +101 -0
- data/lib/gamefic/plot/command_mount.rb +70 -40
- data/lib/gamefic/plot/entities.rb +82 -0
- data/lib/gamefic/plot/host.rb +46 -0
- data/lib/gamefic/plot/playbook.rb +192 -0
- data/lib/gamefic/plot/players.rb +15 -0
- data/lib/gamefic/plot/scene_mount.rb +69 -31
- data/lib/gamefic/plot/snapshot.rb +20 -5
- data/lib/gamefic/scene/active.rb +8 -1
- data/lib/gamefic/scene/base.rb +4 -26
- data/lib/gamefic/scene/custom.rb +53 -3
- data/lib/gamefic/scene/multiple_choice.rb +1 -0
- data/lib/gamefic/scene/yes_or_no.rb +1 -1
- data/lib/gamefic/scene_data/multiple_scene.rb +0 -4
- data/lib/gamefic/shell.rb +0 -1
- data/lib/gamefic/source/file.rb +1 -1
- data/lib/gamefic/stage.rb +10 -2
- data/lib/gamefic/subplot.rb +70 -53
- data/lib/gamefic/syntax.rb +9 -2
- data/lib/gamefic/tester.rb +1 -1
- data/lib/gamefic/text.rb +8 -0
- data/lib/gamefic/{ansi.rb → text/ansi.rb} +12 -15
- data/lib/gamefic/text/html.rb +68 -0
- data/lib/gamefic/text/html/conversions.rb +250 -0
- data/lib/gamefic/text/html/entities.rb +9 -0
- data/lib/gamefic/tty.rb +2 -0
- data/lib/gamefic/user/tty.rb +2 -4
- data/lib/gamefic/version.rb +1 -1
- metadata +12 -8
- data/lib/gamefic/direction.rb +0 -46
- data/lib/gamefic/html.rb +0 -68
- data/lib/gamefic/html_to_ansi.rb +0 -185
- data/lib/gamefic/plot/entity_mount.rb +0 -45
- 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
|
-
|
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
|
-
|
97
|
+
playbook.interpret command, translation
|
72
98
|
end
|
73
|
-
|
74
|
-
|
99
|
+
|
100
|
+
# @deprecated
|
101
|
+
def xlate command, translation
|
102
|
+
interpret command, translation
|
75
103
|
end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|