gamefic 0.6.1 → 1.0.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/bin/gamefic +3 -0
- data/lib/gamefic/character.rb +42 -6
- data/lib/gamefic/director/parser.rb +25 -25
- data/lib/gamefic/engine/tty.rb +14 -2
- data/lib/gamefic/engine.rb +8 -7
- data/lib/gamefic/grammar/gender.rb +1 -1
- data/lib/gamefic/grammar/pronouns.rb +53 -8
- data/lib/gamefic/grammar/verbs.rb +1 -0
- data/lib/gamefic/grammar/word_adapter.rb +31 -18
- data/lib/gamefic/html.rb +27 -13
- data/lib/gamefic/plot/article_mount.rb +1 -1
- data/lib/gamefic/plot/scene_mount.rb +27 -110
- data/lib/gamefic/{snapshots.rb → plot/snapshot.rb} +60 -29
- data/lib/gamefic/plot/you_mount.rb +1 -1
- data/lib/gamefic/plot.rb +56 -29
- data/lib/gamefic/query/base.rb +3 -1
- data/lib/gamefic/scene/active.rb +11 -18
- data/lib/gamefic/scene/base.rb +21 -0
- data/lib/gamefic/scene/conclusion.rb +11 -0
- data/lib/gamefic/scene/custom.rb +21 -0
- data/lib/gamefic/scene/multiple_choice/input.rb +11 -0
- data/lib/gamefic/scene/multiple_choice.rb +73 -0
- data/lib/gamefic/scene/passive.rb +17 -0
- data/lib/gamefic/scene/pause.rb +24 -0
- data/lib/gamefic/scene/question.rb +21 -0
- data/lib/gamefic/scene/yes_or_no.rb +30 -0
- data/lib/gamefic/scene.rb +10 -120
- data/lib/gamefic/script/base.rb +7 -2
- data/lib/gamefic/script.rb +4 -0
- data/lib/gamefic/shell/command/base.rb +38 -0
- data/lib/gamefic/shell/command/play.rb +51 -0
- data/lib/gamefic/shell/command.rb +4 -0
- data/lib/gamefic/shell/registry.rb +13 -0
- data/lib/gamefic/shell.rb +14 -71
- data/lib/gamefic/source/file.rb +1 -1
- data/lib/gamefic/source.rb +5 -0
- data/lib/gamefic/tester.rb +0 -1
- data/lib/gamefic/version.rb +1 -1
- data/lib/gamefic.rb +1 -6
- metadata +69 -61
- data/lib/gamefic/scene/concluded.rb +0 -22
- data/lib/gamefic/scene/multiplechoice.rb +0 -74
- data/lib/gamefic/scene/paused.rb +0 -26
- data/lib/gamefic/scene/yesorno.rb +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42ffeca62def8cbb07d70245a06be065a602c28d
|
4
|
+
data.tar.gz: 49c299be7bafa2aef310aeef08f84b17631b3ce4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2b1a5142f260633c4b3d59f2c2cd48896926566554e67fe89f09ad0cdd15f98ff2305a09ca8022af3bbc1ae7d50108a9e6a7ead77a1aa48c2445837e918e8b4
|
7
|
+
data.tar.gz: 199e10aa6fd28ed8c4d29dbd17eb6420ff5350fa3689e23ba9f4468af75febf9a115af328e6652365a585c3df70bf2a81cf8b803270cf28ff4810a3346fdead0
|
data/bin/gamefic
CHANGED
data/lib/gamefic/character.rb
CHANGED
@@ -7,7 +7,9 @@ module Gamefic
|
|
7
7
|
attr_reader :last_order
|
8
8
|
# @return [Entity,nil]
|
9
9
|
attr_reader :last_object
|
10
|
-
attr_accessor :object_of_pronoun
|
10
|
+
attr_accessor :object_of_pronoun
|
11
|
+
|
12
|
+
serialize :scene
|
11
13
|
|
12
14
|
def initialize(plot, args = {})
|
13
15
|
@queue = Array.new
|
@@ -87,7 +89,7 @@ module Gamefic
|
|
87
89
|
#
|
88
90
|
# @param message [String]
|
89
91
|
def stream(message)
|
90
|
-
user.stream.send message
|
92
|
+
user.stream.send message if !user.nil?
|
91
93
|
end
|
92
94
|
|
93
95
|
def destroy
|
@@ -106,20 +108,54 @@ module Gamefic
|
|
106
108
|
# actor[:has_eaten] = false # Initial value
|
107
109
|
# end
|
108
110
|
# respond :eat do |actor|
|
111
|
+
# actor.tell "You eat something."
|
109
112
|
# actor[:has_eaten] = true
|
110
113
|
# end
|
111
114
|
# respond :eat do |actor|
|
112
115
|
# # This version will be executed first because it was implemented last
|
113
|
-
# actor
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
116
|
+
# if actor[:has_eaten]
|
117
|
+
# actor.tell "You already ate."
|
118
|
+
# else
|
119
|
+
# actor.proceed # Execute the previous implementation
|
120
|
+
# end
|
117
121
|
# end
|
118
122
|
#
|
119
123
|
def proceed
|
120
124
|
return if delegate_stack.last.nil?
|
121
125
|
delegate_stack.last.proceed
|
122
126
|
end
|
127
|
+
|
128
|
+
def cue scene_name
|
129
|
+
@scene = scene_name
|
130
|
+
@next_scene = nil
|
131
|
+
plot.scenes[scene_name].start self
|
132
|
+
end
|
133
|
+
|
134
|
+
def prepare scene_name
|
135
|
+
@next_scene = scene_name
|
136
|
+
end
|
137
|
+
|
138
|
+
def conclude scene_name
|
139
|
+
scene = plot.scenes[scene_name]
|
140
|
+
raise "#{scene_name} is not a conclusion" unless scene.kind_of?(Scene::Conclusion)
|
141
|
+
cue scene_name
|
142
|
+
end
|
143
|
+
|
144
|
+
# Get the name of the character's current scene
|
145
|
+
#
|
146
|
+
# @return [Symbol] The name of the scene
|
147
|
+
def scene
|
148
|
+
@scene
|
149
|
+
end
|
150
|
+
|
151
|
+
# Alias for Character#cue key
|
152
|
+
def scene= key
|
153
|
+
cue key.to_sym
|
154
|
+
end
|
155
|
+
|
156
|
+
def next_scene
|
157
|
+
@next_scene
|
158
|
+
end
|
123
159
|
|
124
160
|
private
|
125
161
|
def delegate_stack
|
@@ -5,33 +5,33 @@ module Gamefic
|
|
5
5
|
|
6
6
|
module Parser
|
7
7
|
def self.from_tokens(actor, tokens)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
index += 1
|
21
|
-
}
|
22
|
-
if valid
|
23
|
-
arguments = []
|
24
|
-
tokens.each { |token|
|
25
|
-
arguments.push [token]
|
26
|
-
}
|
27
|
-
options.push Order.new(actor, action, arguments)
|
8
|
+
options = []
|
9
|
+
command = tokens.shift
|
10
|
+
actions = actor.plot.actions_with_verb(command.to_sym)
|
11
|
+
actions.each { |action|
|
12
|
+
if action.queries.length == tokens.length
|
13
|
+
valid = true
|
14
|
+
index = 0
|
15
|
+
action.queries.each { |query|
|
16
|
+
if query.validate(actor, tokens[index]) == false
|
17
|
+
valid = false
|
18
|
+
break
|
28
19
|
end
|
20
|
+
index += 1
|
21
|
+
}
|
22
|
+
if valid
|
23
|
+
arguments = []
|
24
|
+
tokens.each { |token|
|
25
|
+
arguments.push [token]
|
26
|
+
}
|
27
|
+
options.push Order.new(actor, action, arguments)
|
29
28
|
end
|
30
|
-
}
|
31
|
-
if options.length == 0
|
32
|
-
tokens.unshift command
|
33
29
|
end
|
34
|
-
|
30
|
+
}
|
31
|
+
if options.length == 0
|
32
|
+
tokens.unshift command
|
33
|
+
end
|
34
|
+
options.sort{ |a,b| b.action.specificity <=> a.action.specificity }
|
35
35
|
end
|
36
36
|
def self.from_string(actor, command)
|
37
37
|
options = []
|
@@ -45,7 +45,7 @@ module Gamefic
|
|
45
45
|
options.concat bind_contexts_in_result(actor, match.arguments, action)
|
46
46
|
}
|
47
47
|
}
|
48
|
-
options
|
48
|
+
options.sort{ |a,b| b.action.specificity <=> a.action.specificity }
|
49
49
|
end
|
50
50
|
class << self
|
51
51
|
private
|
data/lib/gamefic/engine/tty.rb
CHANGED
@@ -2,6 +2,7 @@ require 'gamefic/engine'
|
|
2
2
|
require 'rexml/document'
|
3
3
|
require 'gamefic/ansi'
|
4
4
|
require 'gamefic/html'
|
5
|
+
require 'json'
|
5
6
|
|
6
7
|
begin
|
7
8
|
require 'io/console'
|
@@ -31,7 +32,12 @@ module Gamefic
|
|
31
32
|
def ansi
|
32
33
|
@stream.ansi
|
33
34
|
end
|
34
|
-
def save filename,
|
35
|
+
def save filename, snapshot
|
36
|
+
data = {
|
37
|
+
:metadata => @character.plot.metadata,
|
38
|
+
:entities => snapshot
|
39
|
+
}
|
40
|
+
json = JSON.generate data
|
35
41
|
if json.nil?
|
36
42
|
@character.tell "Nothing to save."
|
37
43
|
end
|
@@ -53,11 +59,17 @@ module Gamefic
|
|
53
59
|
end
|
54
60
|
if filename != ''
|
55
61
|
if File.exists?(filename)
|
56
|
-
|
62
|
+
data = JSON.parse File.read(filename), symbolize_names: true
|
63
|
+
if (data[:metadata] != @character.plot.metadata)
|
64
|
+
@character.tell "The save file is not compatible with this version of the game."
|
65
|
+
else
|
66
|
+
return data[:entities]
|
67
|
+
end
|
57
68
|
else
|
58
69
|
@character.tell "File \"#{filename}\" not found."
|
59
70
|
end
|
60
71
|
end
|
72
|
+
nil
|
61
73
|
end
|
62
74
|
end
|
63
75
|
class UserStream < Gamefic::UserStream
|
data/lib/gamefic/engine.rb
CHANGED
@@ -11,7 +11,7 @@ module Gamefic
|
|
11
11
|
def run
|
12
12
|
@plot.introduce @user.character
|
13
13
|
print @user.state.output
|
14
|
-
while @user.character
|
14
|
+
while !@plot.concluded?(@user.character)
|
15
15
|
turn
|
16
16
|
end
|
17
17
|
print @user.state.output
|
@@ -19,12 +19,13 @@ module Gamefic
|
|
19
19
|
def turn
|
20
20
|
@plot.ready
|
21
21
|
print @user.state.output
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
if !@user.character[:test_queue].nil? and @user.character[:test_queue].length > 0
|
23
|
+
test_command = @user.character[:test_queue].shift
|
24
|
+
@user.character.tell "[TESTING] #{@plot.scenes[@user.character.scene].prompt} #{test_command}"
|
25
|
+
@user.character.queue.push test_command
|
26
|
+
else
|
27
|
+
@user.stream.select @plot.scenes[@user.character.scene].prompt
|
28
|
+
@user.state.input
|
28
29
|
end
|
29
30
|
@plot.update
|
30
31
|
print @user.state.output
|
@@ -6,24 +6,68 @@ module Gamefic::Grammar
|
|
6
6
|
def initialize object
|
7
7
|
@object = object
|
8
8
|
end
|
9
|
+
|
10
|
+
# Get the subjective pronoun (I, you, we, etc.)
|
11
|
+
#
|
12
|
+
# @return [String]
|
9
13
|
def subj
|
10
14
|
Pronouns.get_pronoun_set(@object)[0]
|
11
15
|
end
|
16
|
+
|
17
|
+
# Get the objective pronoun (me, them, us, etc.)
|
18
|
+
#
|
19
|
+
# @return [String]
|
12
20
|
def obj
|
13
21
|
Pronouns.get_pronoun_set(@object)[1]
|
14
22
|
end
|
23
|
+
|
24
|
+
# Get the possessive pronoun (my, your, our, etc.)
|
25
|
+
#
|
26
|
+
# @return [String]
|
15
27
|
def poss
|
16
28
|
Pronouns.get_pronoun_set(@object)[2]
|
17
29
|
end
|
30
|
+
|
31
|
+
# Get the reflexive pronoun (myself, yourself, etc.)
|
32
|
+
#
|
33
|
+
# @return [String]
|
34
|
+
def reflex
|
35
|
+
Pronouns.get_pronoun_set(@object)[3]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get the capitalized subjective pronoun
|
39
|
+
#
|
40
|
+
# @return [String]
|
18
41
|
def Subj
|
19
42
|
subj.cap_first
|
20
43
|
end
|
44
|
+
|
45
|
+
# Get the capitalized objective pronoun
|
46
|
+
#
|
47
|
+
# @return [String]
|
21
48
|
def Obj
|
22
49
|
obj.cap_first
|
23
50
|
end
|
51
|
+
|
52
|
+
# Get the capitalized possessive pronoun
|
53
|
+
#
|
54
|
+
# @return [String]
|
24
55
|
def Poss
|
25
56
|
obj.cap_first
|
26
57
|
end
|
58
|
+
|
59
|
+
# Get the capitalized reflexive pronoun
|
60
|
+
#
|
61
|
+
# @return [String]
|
62
|
+
def Reflex
|
63
|
+
reflex.cap_first
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get a an array of pronouns based on the person, gender, and plurality of
|
67
|
+
# the provided object.
|
68
|
+
#
|
69
|
+
# @param obj An object that responds to #person, #gender, and #plural
|
70
|
+
# @return [Array<String>]
|
27
71
|
def self.get_pronoun_set(obj)
|
28
72
|
set = Pronouns.sets["#{obj.person}"]
|
29
73
|
if set.nil?
|
@@ -37,22 +81,23 @@ module Gamefic::Grammar
|
|
37
81
|
end
|
38
82
|
set
|
39
83
|
end
|
84
|
+
|
40
85
|
# TODO Consider implementing method_missing to determine correct pronoun
|
41
86
|
# from example, e.g., "he" would change to "she" for female entities
|
42
87
|
def self.sets
|
43
88
|
if @sets.nil?
|
44
89
|
@sets = {}
|
45
|
-
@sets["1:singular"] = ["I", "me", "my"]
|
46
|
-
@sets["2"] = ["you", "you", "your"]
|
47
|
-
@sets["3:singular:male"] = ["he", "him", "his"]
|
48
|
-
@sets["3:singular:female"] = ["she", "her", "her"]
|
90
|
+
@sets["1:singular"] = ["I", "me", "my", "myself"]
|
91
|
+
@sets["2"] = ["you", "you", "your", "yourself"]
|
92
|
+
@sets["3:singular:male"] = ["he", "him", "his", "himself"]
|
93
|
+
@sets["3:singular:female"] = ["she", "her", "her", "herself"]
|
49
94
|
# "other" refers to a person or living being that is neither
|
50
95
|
# male or female or for whom gender is unspecified. It's
|
51
96
|
# typically used to avoid referring to a person as "it."
|
52
|
-
@sets["3:singular:other"] = ["they", "them", "their"]
|
53
|
-
@sets["3:singular:neutral"] = ["it", "it", "its"]
|
54
|
-
@sets["1:plural"] = ["we", "us", "our"]
|
55
|
-
@sets["3:plural"] = ["they", "them", "their"]
|
97
|
+
@sets["3:singular:other"] = ["they", "them", "their", "themselves"]
|
98
|
+
@sets["3:singular:neutral"] = ["it", "it", "its", "itself"]
|
99
|
+
@sets["1:plural"] = ["we", "us", "our", "ourselves"]
|
100
|
+
@sets["3:plural"] = ["they", "them", "their", "themselves"]
|
56
101
|
end
|
57
102
|
@sets
|
58
103
|
end
|
@@ -8,29 +8,42 @@ module Gamefic::Grammar
|
|
8
8
|
include Plural
|
9
9
|
# @return [Gamefic::Grammar::Pronouns]
|
10
10
|
def pronoun
|
11
|
-
@pronoun ||= Grammar::Pronouns.new(self)
|
11
|
+
@pronoun ||= Gamefic::Grammar::Pronouns.new(self)
|
12
12
|
end
|
13
13
|
# @return [Gamefic::Grammar::Verbs]
|
14
14
|
def verb
|
15
|
-
@verb ||= Grammar::Verbs.new(self)
|
15
|
+
@verb ||= Gamefic::Grammar::Verbs.new(self)
|
16
16
|
end
|
17
17
|
def contract words
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
18
|
+
contractions[words] || words
|
19
|
+
end
|
20
|
+
private
|
21
|
+
def contractions
|
22
|
+
if @contractions.nil?
|
23
|
+
@contractions ||= {
|
24
|
+
"I am" => "I'm",
|
25
|
+
"you are" => "you're",
|
26
|
+
"he is" => "he's",
|
27
|
+
"she is" => "she's",
|
28
|
+
"it is" => "it's",
|
29
|
+
"we are" => "we're",
|
30
|
+
"they are" => "they're",
|
31
|
+
"am not" => "am not",
|
32
|
+
"are not" => "aren't",
|
33
|
+
"is not" => "isn't",
|
34
|
+
"do not" => "don't",
|
35
|
+
"does not" => "doesn't",
|
36
|
+
"can not" => "can't"
|
37
|
+
}
|
38
|
+
capitalized = {}
|
39
|
+
@contractions.each_pair { |k,v |
|
40
|
+
if k[0] != k[0].capitalize
|
41
|
+
capitalized[k.cap_first] = v.cap_first
|
42
|
+
end
|
43
|
+
}
|
44
|
+
@contractions.merge! capitalized
|
45
|
+
end
|
46
|
+
@contractions
|
34
47
|
end
|
35
48
|
end
|
36
49
|
end
|
data/lib/gamefic/html.rb
CHANGED
@@ -2,7 +2,11 @@
|
|
2
2
|
|
3
3
|
module Gamefic
|
4
4
|
|
5
|
-
module Html
|
5
|
+
module Html
|
6
|
+
# Convert ampersands to &
|
7
|
+
#
|
8
|
+
# @param text [String]
|
9
|
+
# @return [String]
|
6
10
|
def self.fix_ampersands(text)
|
7
11
|
codes = []
|
8
12
|
ENTITIES.keys.each { |e|
|
@@ -12,34 +16,44 @@ module Gamefic
|
|
12
16
|
re = Regexp.new("&(?!(#{piped}))")
|
13
17
|
text.gsub(re, '&\1')
|
14
18
|
end
|
19
|
+
|
20
|
+
# Encode a String with HTML entities
|
21
|
+
#
|
22
|
+
# @param text [String]
|
23
|
+
# @return [String]
|
15
24
|
def self.encode(text)
|
16
25
|
Gamefic::Html::ENTITIES.each { |k, v|
|
17
|
-
|
18
|
-
text[v] = k
|
19
|
-
end
|
26
|
+
text = text.gsub(v, k)
|
20
27
|
}
|
21
28
|
text
|
22
29
|
end
|
30
|
+
|
31
|
+
# Decode a String's HTML entities
|
32
|
+
#
|
33
|
+
# @param text [String]
|
34
|
+
# @return [String]
|
23
35
|
def self.decode(text)
|
24
36
|
Gamefic::Html::ENTITIES.each { |k, v|
|
25
|
-
|
26
|
-
text[k] = v
|
27
|
-
end
|
37
|
+
text = text.gsub(k, v)
|
28
38
|
}
|
29
39
|
text
|
30
40
|
end
|
41
|
+
|
42
|
+
# Parse a String into an XML document
|
43
|
+
#
|
44
|
+
# @param code [String]
|
45
|
+
# @return [REXML::Document]
|
31
46
|
def self.parse(code)
|
32
47
|
code = fix_ampersands(code).strip
|
33
48
|
last = nil
|
34
49
|
begin
|
35
50
|
doc = REXML::Document.new code
|
36
51
|
rescue REXML::ParseException => e
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
52
|
+
# Convert invalid < characters to <
|
53
|
+
if e.source.buffer != last and e.source.buffer[0,1] == '<'
|
54
|
+
code = code[0,(code.length - e.source.buffer.length)] + '<' + e.source.buffer[1..-1]
|
55
|
+
last = e.source.buffer
|
56
|
+
retry
|
43
57
|
end
|
44
58
|
raise e
|
45
59
|
end
|