gamefic 1.3.2 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/gamefic.rb +1 -0
- data/lib/gamefic/character.rb +19 -13
- data/lib/gamefic/engine.rb +2 -103
- data/lib/gamefic/engine/base.rb +55 -0
- data/lib/gamefic/engine/tty.rb +20 -254
- data/lib/gamefic/html.rb +20 -19
- data/lib/gamefic/html_to_ansi.rb +185 -0
- data/lib/gamefic/plot.rb +21 -29
- data/lib/gamefic/plot/scene_mount.rb +103 -36
- data/lib/gamefic/scene.rb +3 -2
- data/lib/gamefic/scene/active.rb +1 -6
- data/lib/gamefic/scene/base.rb +34 -8
- data/lib/gamefic/scene/conclusion.rb +0 -3
- data/lib/gamefic/scene/custom.rb +28 -7
- data/lib/gamefic/scene/multiple_choice.rb +31 -38
- data/lib/gamefic/scene/multiple_scene.rb +15 -0
- data/lib/gamefic/scene/pause.rb +4 -15
- data/lib/gamefic/scene/yes_or_no.rb +8 -13
- data/lib/gamefic/scene_data.rb +9 -0
- data/lib/gamefic/scene_data/base.rb +12 -0
- data/lib/gamefic/scene_data/multiple_choice.rb +19 -0
- data/lib/gamefic/scene_data/multiple_scene.rb +25 -0
- data/lib/gamefic/scene_data/yes_or_no.rb +18 -0
- data/lib/gamefic/shell.rb +77 -78
- data/lib/gamefic/tester.rb +1 -1
- data/lib/gamefic/tty.rb +8 -0
- data/lib/gamefic/user.rb +8 -0
- data/lib/gamefic/user/base.rb +21 -0
- data/lib/gamefic/user/buffer.rb +25 -0
- data/lib/gamefic/user/tty.rb +55 -0
- data/lib/gamefic/version.rb +1 -1
- metadata +15 -6
- data/lib/gamefic/engine/cgi.rb +0 -221
- data/lib/gamefic/scene/multiple_choice/input.rb +0 -11
- data/lib/gamefic/scene/passive.rb +0 -17
- data/lib/gamefic/scene/question.rb +0 -21
data/lib/gamefic/html.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
require 'rexml/document'
|
2
2
|
|
3
|
-
module Gamefic
|
4
|
-
|
3
|
+
module Gamefic
|
4
|
+
|
5
5
|
module Html
|
6
6
|
# Convert ampersands to &
|
7
7
|
#
|
8
8
|
# @param text [String]
|
9
|
-
# @return [String]
|
10
|
-
def self.fix_ampersands(text)
|
11
|
-
codes = []
|
12
|
-
ENTITIES.keys.each { |e|
|
13
|
-
codes.push e[1..-1]
|
14
|
-
}
|
15
|
-
piped = codes.join('|')
|
16
|
-
re = Regexp.new("&(?!(#{piped}))")
|
17
|
-
text.gsub(re, '&\1')
|
9
|
+
# @return [String]
|
10
|
+
def self.fix_ampersands(text)
|
11
|
+
codes = []
|
12
|
+
ENTITIES.keys.each { |e|
|
13
|
+
codes.push e[1..-1]
|
14
|
+
}
|
15
|
+
piped = codes.join('|')
|
16
|
+
re = Regexp.new("&(?!(#{piped}))")
|
17
|
+
text.gsub(re, '&\1')
|
18
18
|
end
|
19
19
|
|
20
20
|
# Encode a String with HTML entities
|
@@ -22,10 +22,11 @@ module Gamefic
|
|
22
22
|
# @param text [String]
|
23
23
|
# @return [String]
|
24
24
|
def self.encode(text)
|
25
|
+
encoded = text
|
25
26
|
Gamefic::Html::ENTITIES.each { |k, v|
|
26
|
-
|
27
|
+
encoded = encoded.gsub(v, k)
|
27
28
|
}
|
28
|
-
|
29
|
+
encoded
|
29
30
|
end
|
30
31
|
|
31
32
|
# Decode a String's HTML entities
|
@@ -58,10 +59,10 @@ module Gamefic
|
|
58
59
|
raise e
|
59
60
|
end
|
60
61
|
end
|
61
|
-
end
|
62
|
-
|
63
|
-
module Html
|
64
|
-
ENTITIES = { """ => "\"", "&" => "&", "<" => "<", ">" => ">", " " => " ", "¡" => "¡", "¢" => "¢", "£" => "£", "¤" => "¤", "¥" => "¥", "¦" => "¦", "§" => "§", "¨" => "¨", "©" => "©", "ª" => "ª", "«" => "«", "¬" => "¬", "­" => "", "®" => "®", "¯" => "¯", "°" => "°", "±" => "±", "²" => "²", "³" => "³", "´" => "´", "µ" => "µ", "¶" => "¶", "·" => "·", "¸" => "¸", "¹" => "¹", "º" => "º", "»" => "»", "¼" => "¼", "½" => "½", "¾" => "¾", "¿" => "¿", "À" => "À", "Á" => "Á", "Â" => "Â", "Ã" => "Ã", "Ä" => "Ä", "Å" => "Å", "Æ" => "Æ", "Ç" => "Ç", "È" => "È", "É" => "É", "Ê" => "Ê", "Ë" => "Ë", "Ì" => "Ì", "Í" => "Í", "Î" => "Î", "Ï" => "Ï", "Ð" => "Ð", "Ñ" => "Ñ", "Ò" => "Ò", "Ó" => "Ó", "Ô" => "Ô", "Õ" => "Õ", "Ö" => "Ö", "×" => "×", "Ø" => "Ø", "Ù" => "Ù", "Ú" => "Ú", "Û" => "Û", "Ü" => "Ü", "Ý" => "Ý", "Þ" => "Þ", "ß" => "ß", "à" => "à", "á" => "á", "â" => "â", "ã" => "ã", "ä" => "ä", "å" => "å", "æ" => "æ", "ç" => "ç", "è" => "è", "é" => "é", "ê" => "ê", "ë" => "ë", "ì" => "ì", "í" => "í", "î" => "î", "ï" => "ï", "ð" => "ð", "ñ" => "ñ", "ò" => "ò", "ó" => "ó", "ô" => "ô", "õ" => "õ", "ö" => "ö", "÷" => "÷", "ø" => "ø", "ù" => "ù", "ú" => "ú", "û" => "û", "ü" => "ü", "ý" => "ý", "þ" => "þ", "ÿ" => "ÿ", "Œ" => "Œ", "œ" => "œ", "Š" => "Š", "š" => "š", "Ÿ" => "Ÿ", "ƒ" => "ƒ", "ˆ" => "ˆ", "˜" => "˜", "Α" => "Α", "Β" => "Β", "Γ" => "Γ", "Δ" => "Δ", "Ε" => "Ε", "Ζ" => "Ζ", "Η" => "Η", "Θ" => "Θ", "Ι" => "Ι", "Κ" => "Κ", "Λ" => "Λ", "Μ" => "Μ", "Ν" => "Ν", "Ξ" => "Ξ", "Ο" => "Ο", "Π" => "Π", "Ρ" => "Ρ", "Σ" => "Σ", "Τ" => "Τ", "Υ" => "Υ", "Φ" => "Φ", "Χ" => "Χ", "Ψ" => "Ψ", "Ω" => "Ω", "α" => "α", "β" => "β", "γ" => "γ", "δ" => "δ", "ε" => "ε", "ζ" => "ζ", "η" => "η", "θ" => "θ", "ι" => "ι", "κ" => "κ", "λ" => "λ", "μ" => "μ", "ν" => "ν", "ξ" => "ξ", "ο" => "ο", "π" => "π", "ρ" => "ρ", "ς" => "ς", "σ" => "σ", "τ" => "τ", "υ" => "υ", "φ" => "φ", "χ" => "χ", "ψ" => "ψ", "ω" => "ω", "ϑ" => "ϑ", "ϒ" => "ϒ", "ϖ" => "ϖ", " " => " ", " " => " ", " " => " ", "‌" => "", "‍" => "", "‎" => "", "‏" => "", "–" => "–", "—" => "—", "‘" => "‘", "’" => "’", "‚" => "‚", "“" => "“", "”" => "”", "„" => "„", "†" => "†", "‡" => "‡", "•" => "•", "…" => "…", "‰" => "‰", "′" => "′", "″" => "″", "‹" => "‹", "›" => "›", "‾" => "‾", "⁄" => "⁄", "€" => "€", "ℑ" => "ℑ", "℘" => "℘", "ℜ" => "ℜ", "™" => "™", "ℵ" => "ℵ", "←" => "←", "↑" => "↑", "→" => "→", "↓" => "↓", "↔" => "↔", "↵" => "↵", "⇐" => "⇐", "⇑" => "⇑", "⇒" => "⇒", "⇓" => "⇓", "⇔" => "⇔", "∀" => "∀", "∂" => "∂", "∃" => "∃", "∅" => "∅", "∇" => "∇", "∈" => "∈", "∉" => "∉", "∋" => "∋", "∏" => "∏", "∑" => "∑", "−" => "−", "∗" => "∗", "√" => "√", "∝" => "∝", "∞" => "∞", "∠" => "∠", "∧" => "∧", "∨" => "∨", "∩" => "∩", "∪" => "∪", "∫" => "∫", "∴" => "∴", "∼" => "∼", "≅" => "≅", "≈" => "≈", "≠" => "≠", "≡" => "≡", "≤" => "≤", "≥" => "≥", "⊂" => "⊂", "⊃" => "⊃", "⊄" => "⊄", "⊆" => "⊆", "⊇" => "⊇", "⊕" => "⊕", "⊗" => "⊗", "⊥" => "⊥", "⋅" => "⋅", "⌈" => "⌈", "⌉" => "⌉", "⌊" => "⌊", "⌋" => "⌋", "⟨" => "〈", "⟩" => "〉", "◊" => "◊", "♠" => "♠", "♣" => "♣", "♥" => "♥", "♦" => "♦" }
|
65
62
|
end
|
66
|
-
|
67
|
-
|
63
|
+
|
64
|
+
module Html
|
65
|
+
ENTITIES = { """ => "\"", "&" => "&", "<" => "<", ">" => ">", " " => " ", "¡" => "¡", "¢" => "¢", "£" => "£", "¤" => "¤", "¥" => "¥", "¦" => "¦", "§" => "§", "¨" => "¨", "©" => "©", "ª" => "ª", "«" => "«", "¬" => "¬", "­" => "", "®" => "®", "¯" => "¯", "°" => "°", "±" => "±", "²" => "²", "³" => "³", "´" => "´", "µ" => "µ", "¶" => "¶", "·" => "·", "¸" => "¸", "¹" => "¹", "º" => "º", "»" => "»", "¼" => "¼", "½" => "½", "¾" => "¾", "¿" => "¿", "À" => "À", "Á" => "Á", "Â" => "Â", "Ã" => "Ã", "Ä" => "Ä", "Å" => "Å", "Æ" => "Æ", "Ç" => "Ç", "È" => "È", "É" => "É", "Ê" => "Ê", "Ë" => "Ë", "Ì" => "Ì", "Í" => "Í", "Î" => "Î", "Ï" => "Ï", "Ð" => "Ð", "Ñ" => "Ñ", "Ò" => "Ò", "Ó" => "Ó", "Ô" => "Ô", "Õ" => "Õ", "Ö" => "Ö", "×" => "×", "Ø" => "Ø", "Ù" => "Ù", "Ú" => "Ú", "Û" => "Û", "Ü" => "Ü", "Ý" => "Ý", "Þ" => "Þ", "ß" => "ß", "à" => "à", "á" => "á", "â" => "â", "ã" => "ã", "ä" => "ä", "å" => "å", "æ" => "æ", "ç" => "ç", "è" => "è", "é" => "é", "ê" => "ê", "ë" => "ë", "ì" => "ì", "í" => "í", "î" => "î", "ï" => "ï", "ð" => "ð", "ñ" => "ñ", "ò" => "ò", "ó" => "ó", "ô" => "ô", "õ" => "õ", "ö" => "ö", "÷" => "÷", "ø" => "ø", "ù" => "ù", "ú" => "ú", "û" => "û", "ü" => "ü", "ý" => "ý", "þ" => "þ", "ÿ" => "ÿ", "Œ" => "Œ", "œ" => "œ", "Š" => "Š", "š" => "š", "Ÿ" => "Ÿ", "ƒ" => "ƒ", "ˆ" => "ˆ", "˜" => "˜", "Α" => "Α", "Β" => "Β", "Γ" => "Γ", "Δ" => "Δ", "Ε" => "Ε", "Ζ" => "Ζ", "Η" => "Η", "Θ" => "Θ", "Ι" => "Ι", "Κ" => "Κ", "Λ" => "Λ", "Μ" => "Μ", "Ν" => "Ν", "Ξ" => "Ξ", "Ο" => "Ο", "Π" => "Π", "Ρ" => "Ρ", "Σ" => "Σ", "Τ" => "Τ", "Υ" => "Υ", "Φ" => "Φ", "Χ" => "Χ", "Ψ" => "Ψ", "Ω" => "Ω", "α" => "α", "β" => "β", "γ" => "γ", "δ" => "δ", "ε" => "ε", "ζ" => "ζ", "η" => "η", "θ" => "θ", "ι" => "ι", "κ" => "κ", "λ" => "λ", "μ" => "μ", "ν" => "ν", "ξ" => "ξ", "ο" => "ο", "π" => "π", "ρ" => "ρ", "ς" => "ς", "σ" => "σ", "τ" => "τ", "υ" => "υ", "φ" => "φ", "χ" => "χ", "ψ" => "ψ", "ω" => "ω", "ϑ" => "ϑ", "ϒ" => "ϒ", "ϖ" => "ϖ", " " => " ", " " => " ", " " => " ", "‌" => "", "‍" => "", "‎" => "", "‏" => "", "–" => "–", "—" => "—", "‘" => "‘", "’" => "’", "‚" => "‚", "“" => "“", "”" => "”", "„" => "„", "†" => "†", "‡" => "‡", "•" => "•", "…" => "…", "‰" => "‰", "′" => "′", "″" => "″", "‹" => "‹", "›" => "›", "‾" => "‾", "⁄" => "⁄", "€" => "€", "ℑ" => "ℑ", "℘" => "℘", "ℜ" => "ℜ", "™" => "™", "ℵ" => "ℵ", "←" => "←", "↑" => "↑", "→" => "→", "↓" => "↓", "↔" => "↔", "↵" => "↵", "⇐" => "⇐", "⇑" => "⇑", "⇒" => "⇒", "⇓" => "⇓", "⇔" => "⇔", "∀" => "∀", "∂" => "∂", "∃" => "∃", "∅" => "∅", "∇" => "∇", "∈" => "∈", "∉" => "∉", "∋" => "∋", "∏" => "∏", "∑" => "∑", "−" => "−", "∗" => "∗", "√" => "√", "∝" => "∝", "∞" => "∞", "∠" => "∠", "∧" => "∧", "∨" => "∨", "∩" => "∩", "∪" => "∪", "∫" => "∫", "∴" => "∴", "∼" => "∼", "≅" => "≅", "≈" => "≈", "≠" => "≠", "≡" => "≡", "≤" => "≤", "≥" => "≥", "⊂" => "⊂", "⊃" => "⊃", "⊄" => "⊄", "⊆" => "⊆", "⊇" => "⊇", "⊕" => "⊕", "⊗" => "⊗", "⊥" => "⊥", "⋅" => "⋅", "⌈" => "⌈", "⌉" => "⌉", "⌊" => "⌊", "⌋" => "⌋", "⟨" => "〈", "⟩" => "〉", "◊" => "◊", "♠" => "♠", "♣" => "♣", "♥" => "♥", "♦" => "♦" }
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'gamefic/ansi'
|
2
|
+
require 'gamefic/html'
|
3
|
+
require 'io/console'
|
4
|
+
|
5
|
+
module Gamefic
|
6
|
+
|
7
|
+
module HtmlToAnsi
|
8
|
+
include Ansi
|
9
|
+
include Ansi::Code
|
10
|
+
|
11
|
+
def html_to_ansi text, wrap: true, width: nil
|
12
|
+
return '' if text.strip == ''
|
13
|
+
output = ''
|
14
|
+
begin
|
15
|
+
doc = Html.parse("<body>#{text.strip}</body>")
|
16
|
+
format_recursively doc
|
17
|
+
texts = REXML::XPath.match(doc, './/text()')
|
18
|
+
output = texts.join('').gsub(/'/, "'").gsub(/"/, '"').gsub(/</, '<').gsub(/>/, '>')
|
19
|
+
output += Ansi.graphics_mode(Attribute::NORMAL)
|
20
|
+
output = Html.decode(output)
|
21
|
+
rescue REXML::ParseException => e
|
22
|
+
output = Html.encode(text) + "\n\n"
|
23
|
+
#output = data
|
24
|
+
end
|
25
|
+
output.gsub!(/(\n\n)+/, "\n\n")
|
26
|
+
calc_width = size[0] || width
|
27
|
+
if calc_width.nil? or !wrap
|
28
|
+
output
|
29
|
+
else
|
30
|
+
terminalize(output, calc_width - 1)
|
31
|
+
end
|
32
|
+
output
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def format_recursively(top, stack = nil)
|
38
|
+
ol_index = 1
|
39
|
+
stack ||= [Attribute::NORMAL]
|
40
|
+
top.elements.each { |element|
|
41
|
+
formats = [Attribute::NORMAL]
|
42
|
+
classes = element.attribute('class').to_s.split(' ')
|
43
|
+
if classes.include?('hint')
|
44
|
+
formats.push Foreground::YELLOW
|
45
|
+
end
|
46
|
+
case element.name
|
47
|
+
when 'strong', 'b'
|
48
|
+
formats.push Attribute::BOLD
|
49
|
+
when 'em', 'i', 'u'
|
50
|
+
formats.push Attribute::UNDERSCORE
|
51
|
+
when 'a'
|
52
|
+
if element.attributes['rel'].to_s == 'gamefic'
|
53
|
+
element.attributes['href'] = element.attributes['href'][5..-1]
|
54
|
+
formats.push [Attribute::UNDERSCORE, Extra::COMMAND]
|
55
|
+
else
|
56
|
+
formats.push [Attribute::UNDERSCORE, Extra::HREF]
|
57
|
+
end
|
58
|
+
when 'li'
|
59
|
+
if top.name == 'ol'
|
60
|
+
element.text = "#{ol_index}. #{element.text}"
|
61
|
+
ol_index += 1
|
62
|
+
else
|
63
|
+
element.text = "* #{element.text}"
|
64
|
+
end
|
65
|
+
formats.push [Extra::LINE]
|
66
|
+
when 'img'
|
67
|
+
formats.push [Extra::IGNORED]
|
68
|
+
when 'body', 'p', 'ol', 'ul'
|
69
|
+
formats.push Extra::BLOCK
|
70
|
+
when 'pre'
|
71
|
+
formats.push [Extra::BLOCK, Extra::PRE]
|
72
|
+
when 'nav'
|
73
|
+
formats.push Extra::BLOCK
|
74
|
+
when 'h1', 'h2', 'h3', 'h4', 'h5'
|
75
|
+
formats.push [Attribute::BOLD, Extra::BLOCK, Extra::UPPERCASE]
|
76
|
+
when 'kbd'
|
77
|
+
formats.push [Foreground::GREEN]
|
78
|
+
end
|
79
|
+
if has_code?(stack, Extra::IGNORED)
|
80
|
+
element.parent.delete_element(element)
|
81
|
+
end
|
82
|
+
stack.push formats
|
83
|
+
if has_code?(stack, Extra::UPPERCASE)
|
84
|
+
element.texts.each { |text|
|
85
|
+
text.value.upcase!
|
86
|
+
}
|
87
|
+
end
|
88
|
+
element.texts.each { |text|
|
89
|
+
text.value = "#{Ansi.graphics_mode(*stack)}#{text.value}"
|
90
|
+
}
|
91
|
+
if has_code?(stack.last, Extra::IMAGE)
|
92
|
+
element.text = "#{element.attribute('alt') ? element.attribute('alt') : '[Image]'}"
|
93
|
+
end
|
94
|
+
format_recursively element, stack
|
95
|
+
if has_code?(stack.last, Extra::COMMAND)
|
96
|
+
if element.attribute('data-command').to_s != ''
|
97
|
+
tmp = stack.pop
|
98
|
+
element.add_text "#{Ansi.graphics_mode(*stack)}"
|
99
|
+
element.add_text " [#{element.attribute('data-command')}]"
|
100
|
+
stack.push tmp
|
101
|
+
element.add_text "#{Ansi.graphics_mode(*stack)}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
if has_code?(stack.last, Extra::BLOCK) and !has_code?(stack.last, Extra::PRE)
|
105
|
+
element.texts.first.value.lstrip! unless element.texts.first.nil?
|
106
|
+
element.texts.last.value.rstrip! unless element.texts.last.nil?
|
107
|
+
element.texts.each { |t|
|
108
|
+
t.value = t.value.gsub(/ +/, ' ').strip
|
109
|
+
}
|
110
|
+
end
|
111
|
+
if has_code?(stack.last, Extra::BLOCK)
|
112
|
+
element.add_text("\n\n")
|
113
|
+
elsif has_code?(stack.last, Extra::LINE)
|
114
|
+
if !has_code?(stack[-2], Extra::BLOCK) || element != top.elements.to_a.last
|
115
|
+
element.add_text("\n")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
if has_code?(stack.last, Extra::HREF)
|
119
|
+
if element.attribute('href').to_s != "#"
|
120
|
+
tmp = stack.pop
|
121
|
+
element.add_text "#{Ansi.graphics_mode(*stack)}"
|
122
|
+
element.add_text(" [#{element.attribute('href')}]")
|
123
|
+
stack.push tmp
|
124
|
+
element.add_text "#{Ansi.graphics_mode(*stack)}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
if has_code?(stack.last, Extra::IMAGE)
|
128
|
+
element.add_text(" [#{element.attribute('src')}]")
|
129
|
+
if !has_code?(stack, Extra::BLOCK)
|
130
|
+
element.add_text("\n\n")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
stack.pop
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
def terminalize string, max_length
|
138
|
+
i = 0
|
139
|
+
output = ''
|
140
|
+
line_length = 0
|
141
|
+
while i < string.length
|
142
|
+
line_length += 1
|
143
|
+
char = string[i,1]
|
144
|
+
if char == "\e"
|
145
|
+
# Right now, graphics modes are the only supported ANSI sequences.
|
146
|
+
end_of_seq = string.index("m", i)
|
147
|
+
output += string[i..end_of_seq]
|
148
|
+
i = end_of_seq + 1
|
149
|
+
elsif char == " "
|
150
|
+
next_space = string.index(/[\s]/, i + 1)
|
151
|
+
if !next_space.nil? and line_length + (next_space - i) > max_length
|
152
|
+
output += "\n"
|
153
|
+
line_length = 0
|
154
|
+
else
|
155
|
+
output += char
|
156
|
+
end
|
157
|
+
i += 1
|
158
|
+
else
|
159
|
+
if char == "\n"
|
160
|
+
line_length = 0
|
161
|
+
end
|
162
|
+
output += char
|
163
|
+
i += 1
|
164
|
+
end
|
165
|
+
end
|
166
|
+
output #output.strip
|
167
|
+
end
|
168
|
+
|
169
|
+
def has_code?(fmt, code)
|
170
|
+
if fmt.kind_of?(Array)
|
171
|
+
return fmt.flatten.include?(code)
|
172
|
+
end
|
173
|
+
return fmt == code
|
174
|
+
end
|
175
|
+
|
176
|
+
def size
|
177
|
+
begin
|
178
|
+
return STDOUT.winsize.reverse
|
179
|
+
rescue
|
180
|
+
return [nil,nil]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
data/lib/gamefic/plot.rb
CHANGED
@@ -67,7 +67,7 @@ module Gamefic
|
|
67
67
|
|
68
68
|
# Get an Array of all Actions defined in the Plot.
|
69
69
|
#
|
70
|
-
# @return Array
|
70
|
+
# @return [Array<Action>]
|
71
71
|
def actions
|
72
72
|
@commands.values.flatten
|
73
73
|
end
|
@@ -75,7 +75,7 @@ module Gamefic
|
|
75
75
|
# Get an Array of all Actions associated with the specified verb.
|
76
76
|
#
|
77
77
|
# @param verb [Symbol] The Symbol for the verb (e.g., :go or :look)
|
78
|
-
# @return Array<Action> The verb's associated Actions
|
78
|
+
# @return [Array<Action>] The verb's associated Actions
|
79
79
|
def actions_with_verb(verb)
|
80
80
|
@commands[verb].clone || []
|
81
81
|
end
|
@@ -202,30 +202,20 @@ module Gamefic
|
|
202
202
|
# Update the Plot's current turn of gameplay.
|
203
203
|
# This method is typically called by the Engine that manages game execution.
|
204
204
|
def update
|
205
|
-
# Update the plot.
|
206
205
|
@players.each { |player|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
player.cue this_scene
|
215
|
-
player.queue.shift
|
216
|
-
else
|
206
|
+
@before_player_update_procs.each { |p| p.call player }
|
207
|
+
this_scene = player.next_scene || player.scene
|
208
|
+
player.prepare nil
|
209
|
+
if this_scene != player.scene
|
210
|
+
player.cue this_scene
|
211
|
+
player.queue.shift
|
212
|
+
else
|
217
213
|
process_input player
|
218
214
|
end
|
219
215
|
}
|
220
|
-
@entities.each { |e|
|
221
|
-
|
222
|
-
}
|
223
|
-
@players.each { |player|
|
224
|
-
update_player player
|
225
|
-
}
|
226
|
-
@update_procs.each { |p|
|
227
|
-
p.call
|
228
|
-
}
|
216
|
+
@entities.each { |e| e.update }
|
217
|
+
@players.each { |player| update_player player }
|
218
|
+
@update_procs.each { |p| p.call }
|
229
219
|
end
|
230
220
|
|
231
221
|
def tell entities, message, refresh = false
|
@@ -294,13 +284,14 @@ module Gamefic
|
|
294
284
|
end
|
295
285
|
def rem_entity(entity)
|
296
286
|
@entities.delete(entity)
|
287
|
+
@players.delete(entity)
|
297
288
|
end
|
298
289
|
def recursive_update(entity)
|
299
290
|
entity.update
|
300
291
|
entity.children.each { |e|
|
301
292
|
recursive_update e
|
302
293
|
}
|
303
|
-
end
|
294
|
+
end
|
304
295
|
def add_syntax syntax
|
305
296
|
if @commands[syntax.verb] == nil
|
306
297
|
raise "Action \"#{syntax.verb}\" does not exist."
|
@@ -317,12 +308,10 @@ module Gamefic
|
|
317
308
|
else
|
318
309
|
b.token_count <=> a.token_count
|
319
310
|
end
|
320
|
-
}
|
311
|
+
}
|
321
312
|
end
|
322
313
|
def add_action(action)
|
323
|
-
|
324
|
-
@commands[action.verb] = Array.new
|
325
|
-
end
|
314
|
+
@commands[action.verb] ||= []
|
326
315
|
@commands[action.verb].unshift action
|
327
316
|
@commands[action.verb].sort! { |a, b|
|
328
317
|
if a.specificity == b.specificity
|
@@ -333,9 +322,12 @@ module Gamefic
|
|
333
322
|
b.specificity <=> a.specificity
|
334
323
|
end
|
335
324
|
}
|
325
|
+
generate_default_syntax action
|
326
|
+
end
|
327
|
+
def generate_default_syntax action
|
336
328
|
user_friendly = action.verb.to_s.gsub(/_/, ' ')
|
337
|
-
args =
|
338
|
-
used_names =
|
329
|
+
args = []
|
330
|
+
used_names = []
|
339
331
|
action.queries.each { |c|
|
340
332
|
num = 1
|
341
333
|
new_name = ":var"
|
@@ -5,15 +5,19 @@ module Gamefic
|
|
5
5
|
|
6
6
|
module Plot::SceneMount
|
7
7
|
# Create a multiple-choice scene.
|
8
|
-
# The user will be required to make a valid choice to continue
|
8
|
+
# The user will be required to make a valid choice to continue.
|
9
9
|
#
|
10
10
|
# @yieldparam [Character]
|
11
|
-
# @yieldparam [
|
12
|
-
def multiple_choice key,
|
13
|
-
scenes[key] = Scene::MultipleChoice.new
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
# @yieldparam [Scene::Data::MultipleChoice]
|
12
|
+
def multiple_choice key, *choices, &block
|
13
|
+
scenes[key] = Scene::MultipleChoice.new
|
14
|
+
scenes[key].on_start do |actor, data|
|
15
|
+
data.options.push *choices
|
16
|
+
end
|
17
|
+
scenes[key].on_finish do |actor, data|
|
18
|
+
block.call actor, data unless block.nil?
|
19
|
+
actor.cue :active if actor.scene == key and actor.next_scene.nil?
|
20
|
+
end
|
17
21
|
end
|
18
22
|
|
19
23
|
# Create a yes-or-no scene.
|
@@ -22,50 +26,113 @@ module Gamefic
|
|
22
26
|
# @yieldparam [Character]
|
23
27
|
# @yieldparam [String] "yes" or "no"
|
24
28
|
def yes_or_no key, prompt = nil, &block
|
25
|
-
scenes[key] = Scene::YesOrNo.new
|
29
|
+
scenes[key] = Scene::YesOrNo.new
|
30
|
+
unless prompt.nil?
|
31
|
+
scenes[key].on_start do |actor, data|
|
32
|
+
data.prompt = prompt
|
33
|
+
end
|
34
|
+
end
|
35
|
+
scenes[key].on_finish &block
|
26
36
|
end
|
27
37
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
scenes[key] = Scene::Question.new prompt, &block
|
38
|
+
def question key, prompt = 'What is your answer?', &block
|
39
|
+
scenes[key] = Scene::Custom.new
|
40
|
+
scenes[key].on_start do |actor, data|
|
41
|
+
data.prompt = prompt
|
42
|
+
end
|
43
|
+
scenes[key].on_finish do |actor, data|
|
44
|
+
block.call actor, data unless block.nil?
|
45
|
+
actor.cue :active if actor.scene == key and actor.next_scene.nil?
|
46
|
+
end
|
38
47
|
end
|
39
|
-
|
48
|
+
|
40
49
|
# Create a scene that pauses the game.
|
41
|
-
# This scene will execute the specified block and wait for input
|
42
|
-
#
|
50
|
+
# This scene will execute the specified block and wait for input from the
|
51
|
+
# the user (e.g., pressing Enter) to continue.
|
43
52
|
#
|
44
53
|
# @param key [Symbol] A unique name for the scene.
|
54
|
+
# @param prompt [String] The text to display when prompting the user to continue.
|
45
55
|
# @yieldparam [Character]
|
46
|
-
|
47
|
-
|
56
|
+
# @yieldparam [Scene::Data::Base]
|
57
|
+
def pause key, prompt = nil, &block
|
58
|
+
scenes[key] = Scene::Pause.new
|
59
|
+
scenes[key].on_start do |actor, data|
|
60
|
+
data.prompt = prompt unless prompt.nil?
|
61
|
+
block.call actor, data unless block.nil?
|
62
|
+
end
|
63
|
+
scenes[key].on_finish do |actor, data|
|
64
|
+
actor.cue :active if actor.scene == key and actor.next_scene.nil?
|
65
|
+
end
|
48
66
|
end
|
49
67
|
|
50
68
|
# Create a conclusion.
|
51
|
-
# The game will end after this
|
69
|
+
# The game (or the character's participation in it) will end after this
|
70
|
+
# scene is complete.
|
52
71
|
#
|
53
72
|
# @param key [Symbol] A unique name for the scene.
|
54
73
|
# @yieldparam [Character]
|
74
|
+
# @yieldparam [Scene::Data::Base]
|
55
75
|
def conclusion key, &block
|
56
|
-
scenes[key] = Scene::Conclusion.new
|
76
|
+
scenes[key] = Scene::Conclusion.new
|
77
|
+
scenes[key].on_start &block
|
57
78
|
end
|
58
79
|
|
59
|
-
# Create a
|
60
|
-
#
|
61
|
-
#
|
80
|
+
# Create a passive scene.
|
81
|
+
# Passive scenes will cue the active scene if another scene
|
82
|
+
# has not been prepared or cued.
|
62
83
|
#
|
63
84
|
# @param [Symbol] A unique name for the scene.
|
64
85
|
# @yieldparam [Character]
|
65
|
-
|
66
|
-
|
86
|
+
# @yieldparam [Scene::Data::Base]
|
87
|
+
def passive key, &block
|
88
|
+
scenes[key] = Scene::Custom.new
|
89
|
+
scenes[key].on_start do |actor, data|
|
90
|
+
block.call actor, data
|
91
|
+
actor.cue :active if actor.scene == key and actor.next_scene.nil?
|
92
|
+
end
|
67
93
|
end
|
68
|
-
|
94
|
+
|
95
|
+
# Create a custom scene.
|
96
|
+
#
|
97
|
+
# Custom scenes should always specify the next scene to be cued or
|
98
|
+
# prepared. If not, the scene will get repeated on the next turn.
|
99
|
+
#
|
100
|
+
# This method creates a Scene::Custom by default. You can customize other
|
101
|
+
# scene types by specifying the class to create.
|
102
|
+
#
|
103
|
+
# @example Ask the user for a name
|
104
|
+
# scene :ask_for_name do |scene|
|
105
|
+
# scene.on_start do |actor, data|
|
106
|
+
# data.prompt = "What's your name?"
|
107
|
+
# end
|
108
|
+
# scene.on_finish do |actor, data|
|
109
|
+
# actor.name = data.input
|
110
|
+
# actor.tell "Hello, #{actor.name}!"
|
111
|
+
# actor.cue :active
|
112
|
+
# end
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# @example Customize the prompt for a MultipleChoice scene
|
116
|
+
# scene :ask_for_choice, Scene::MultipleChoice do |scene|
|
117
|
+
# scene.on_start do |actor, data|
|
118
|
+
# data.options.push 'red', 'green', 'blue'
|
119
|
+
# data.prompt = "Which color?"
|
120
|
+
# end
|
121
|
+
# scene.on_finish do |actor, data|
|
122
|
+
# actor.tell "You chose #{data.selection}"
|
123
|
+
# actor.cue :active
|
124
|
+
# end
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
# @param key [Symbol] A unique name for the scene.
|
128
|
+
# @param key [cls] The class of scene to be instantiated.
|
129
|
+
# @yieldparam [Scene::Custom] The instantiated scene.
|
130
|
+
def scene key, cls = Scene::Custom, &block
|
131
|
+
scenes[key] = cls.new
|
132
|
+
#block.call scenes[key]
|
133
|
+
yield scenes[key] if block_given?
|
134
|
+
end
|
135
|
+
|
69
136
|
# Choose a new scene based on a list of options.
|
70
137
|
# This is a specialized type of multiple-choice scene that determines
|
71
138
|
# which scene to cue based on a Hash of choices and scene keys.
|
@@ -85,12 +152,12 @@ module Gamefic
|
|
85
152
|
# @param key [Symbol] A unique name for the scene.
|
86
153
|
# @param map [Hash] A Hash of options and associated scene keys.
|
87
154
|
def multiple_scene key, map
|
88
|
-
scenes[key] = Scene::MultipleChoice.new
|
89
|
-
|
90
|
-
|
91
|
-
|
155
|
+
scenes[key] = Scene::MultipleChoice.new
|
156
|
+
scenes[key].on_start do |actor, data|
|
157
|
+
map.each { |k, v|
|
158
|
+
data.map k, v
|
92
159
|
}
|
93
|
-
|
160
|
+
end
|
94
161
|
end
|
95
162
|
|
96
163
|
end
|