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