gamefic 1.3.2 → 1.4.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 -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
|