|
|
#!/usr/bin/env python
|
|
|
"""
|
|
|
Generate HTML from a subset of rpy
|
|
|
"""
|
|
|
import fileinput
|
|
|
import sys
|
|
|
|
|
|
|
|
|
quick_help_text = """
|
|
|
This is a 'kinetic visual novel'. To advance in the<br>
|
|
|
story, just click the screen. There are no decisions<br>
|
|
|
to make, items to collect, or minigames to play.
|
|
|
"""
|
|
|
|
|
|
container_text = """
|
|
|
<!doctype html>
|
|
|
<html>
|
|
|
<head>
|
|
|
<title>{title}</title>
|
|
|
<link rel="stylesheet" href="fullscreen.css">
|
|
|
<meta name="viewport" content="width=device-width, user-scalable=no">
|
|
|
</head>
|
|
|
<body>
|
|
|
<div class="viewport-container">
|
|
|
<iframe src="{filename}#frame_0" seamless="true"></iframe>
|
|
|
</div>
|
|
|
</body>
|
|
|
</html>
|
|
|
"""
|
|
|
|
|
|
header = """
|
|
|
<!doctype html>
|
|
|
<html>
|
|
|
<head>
|
|
|
<link rel="stylesheet" href="vn.css">
|
|
|
<link rel="stylesheet" href="scenes.css">
|
|
|
<meta name="viewport" content="width=device-width, user-scalable=no">
|
|
|
</head>
|
|
|
<body>
|
|
|
<div class="viewport">
|
|
|
"""
|
|
|
|
|
|
character_names = ['pirogoeth']
|
|
|
|
|
|
images_used = [
|
|
|
"assets/character/pirogoeth/pirogoeth_pfront_crouching_m0.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pfront_crouching_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pfront_lungingclose_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pfront_lunging_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pfront_nonplussed_m0.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pfront_nonplussed_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headdown_lookingaside_m0.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headdown_lookingaside_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headdown_lookingfront_m0.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headdown_lookingfront_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headstraight_lookingaside_m0.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headstraight_lookingaside_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headstraight_lookingfront_m0.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headstraight_lookingfront_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headstraight_lookingstraightangry_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headstraight_lookingstraight_m0.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headstraight_lookingstraight_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headstraight_lookingstraightsadface_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headstraight_lookingstraightwideeyed_m0.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pleft_headstraight_lookingstraightwideeyed_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pright_lookingoff_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pright_lookingup_m0.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pright_lookingup_m1.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pright_turning_m0.png",
|
|
|
"assets/character/pirogoeth/pirogoeth_pright_turning_m1.png",
|
|
|
"assets/background/market_empty.jpg",
|
|
|
"assets/background/market_busy.jpg",
|
|
|
"assets/background/flashback/flashbackCamping.jpg",
|
|
|
"assets/background/flashback/flashbackDancing.jpg",
|
|
|
"assets/background/flashback/flashbackDarkBG.png",
|
|
|
"assets/background/flashback/flashbackDomina.jpg",
|
|
|
"assets/background/flashback/flashbackEscaping.jpg",
|
|
|
"assets/background/flashback/flashbackFalling.jpg",
|
|
|
"assets/background/flashback/flashbackHunting.jpg",
|
|
|
]
|
|
|
next_classes = None
|
|
|
scene = "black"
|
|
|
frame_count = 0
|
|
|
characters = {}
|
|
|
expressions = []
|
|
|
last_content = ""
|
|
|
last_scene = ""
|
|
|
last_expression = ""
|
|
|
audio_background = None
|
|
|
|
|
|
output_buffer = ''
|
|
|
current_file_num = 0
|
|
|
|
|
|
def format_filename(file_num):
|
|
|
t = fileinput.filename().replace('.webvn','')
|
|
|
if file_num != -1:
|
|
|
t += '_' + str(file_num)
|
|
|
t += '.htm'
|
|
|
return t
|
|
|
|
|
|
def output_append(string):
|
|
|
global output_buffer
|
|
|
output_buffer += string
|
|
|
|
|
|
def output_buffer_write(name):
|
|
|
global output_buffer
|
|
|
output_buffer = output_buffer.replace('_XXX_','')
|
|
|
with open(name, 'w') as out:
|
|
|
out.write(output_buffer)
|
|
|
output_buffer = ''
|
|
|
|
|
|
def output_replace_last_link(new_target):
|
|
|
global output_buffer
|
|
|
parts = output_buffer.split('_XXX_')
|
|
|
x = parts[:-1]
|
|
|
output_buffer = "_XXX_".join(x) + new_target + parts[-1]
|
|
|
|
|
|
def next_file():
|
|
|
global current_file_num
|
|
|
print_preload()
|
|
|
print_footer()
|
|
|
output_buffer_write(format_filename(current_file_num))
|
|
|
current_file_num += 1
|
|
|
output_append(header)
|
|
|
|
|
|
def wordbox(text):
|
|
|
return """
|
|
|
<div class="wordbox">
|
|
|
<div class="wordbox-content">
|
|
|
{0}
|
|
|
</div>
|
|
|
</div>
|
|
|
""".format(text)
|
|
|
|
|
|
def wordbubble(text):
|
|
|
return """
|
|
|
<div class="wordbubble">
|
|
|
<div class="wordbubble-content">
|
|
|
{0}
|
|
|
</div>
|
|
|
</div>
|
|
|
""".format(text)
|
|
|
|
|
|
def whitetext(text):
|
|
|
global scene
|
|
|
if scene != "black": return wordbox(text)
|
|
|
line_map = { 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five' }
|
|
|
line_count = len(text.splitlines())
|
|
|
return """
|
|
|
<div class="centered-white-text vertical-{0}-line">
|
|
|
{1}
|
|
|
</div>
|
|
|
""".format(line_map[line_count] if line_count in line_map else "one", text)
|
|
|
|
|
|
def switch_mouth(character, newmouth):
|
|
|
if '_m1' in characters[character]['pose']:
|
|
|
characters[character]['pose'] = characters[character]['pose'].replace('_m1','_'+newmouth)
|
|
|
elif '_m0' in characters[character]['pose']:
|
|
|
characters[character]['pose'] = characters[character]['pose'].replace('_m0','_'+newmouth)
|
|
|
|
|
|
def switch_pose(character, newpose):
|
|
|
if '_m1' in characters[character]['pose']:
|
|
|
characters[character]['pose'] = newpose + '_m1'
|
|
|
elif '_m0' in characters[character]['pose']:
|
|
|
characters[character]['pose'] = newpose + '_m0'
|
|
|
|
|
|
def characterbubble(character, text):
|
|
|
if text.startswith('"'): text = "m0 " + text
|
|
|
mouth, _, text = text.partition(" ")
|
|
|
if not (mouth == 'm1' or mouth == 'm0'):
|
|
|
switch_pose(character, mouth)
|
|
|
mouth, _, text = text.partition(" ")
|
|
|
text = eval(text)
|
|
|
switch_mouth(character, mouth)
|
|
|
return """
|
|
|
<div class="wordbubble-character">
|
|
|
<div class="wordbubble-content">
|
|
|
{0}
|
|
|
</div>
|
|
|
</div>
|
|
|
""".format(text)
|
|
|
|
|
|
def character(name, extra=None):
|
|
|
attributes = characters[name]
|
|
|
return """
|
|
|
<div class="{2}-character">
|
|
|
<img class="{0}" src="assets/character/{0}/{0}_{1}.png">
|
|
|
</div>
|
|
|
""".format(name, attributes['pose'], attributes['position'])
|
|
|
|
|
|
def make_expression(expression):
|
|
|
return """<div class="overlay {0}"></div>""".format(expression)
|
|
|
|
|
|
def stage(contents):
|
|
|
global scene, next_classes, characters, expressions, last_content, last_scene, last_expression
|
|
|
chars = ""
|
|
|
for k,v in characters.iteritems():
|
|
|
chars += character(k)
|
|
|
extra_classes = "scene-{0}".format(scene if scene else "black")
|
|
|
my_expressions = "\n".join([make_expression(x) for x in expressions])
|
|
|
out = """
|
|
|
<div class="stage {3}">
|
|
|
{4}
|
|
|
{7}
|
|
|
</div>
|
|
|
<div class="stage {0} {5}">
|
|
|
{1}
|
|
|
{6}
|
|
|
{2}
|
|
|
</div>
|
|
|
""".format(extra_classes, chars, "\n".join(contents), last_scene, last_content, next_classes if next_classes else "fade-in", my_expressions, last_expression)
|
|
|
last_content = chars + "\n".join(contents)
|
|
|
last_scene = extra_classes
|
|
|
last_expression = my_expressions
|
|
|
return out
|
|
|
|
|
|
def print_frame(content):
|
|
|
global frame_count
|
|
|
frame_count += 1
|
|
|
output_append("""
|
|
|
<div class="frame" id="frame_{1}">
|
|
|
{0}
|
|
|
<a target="_self" class="framelink" href="_XXX_#frame_{2}"></a>
|
|
|
</div>
|
|
|
""".format(content.strip(), frame_count - 1, frame_count))
|
|
|
|
|
|
def process(line):
|
|
|
global scene, next_classes, characters, expressions, audio_background
|
|
|
c, _, rest = line.strip().partition(" ")
|
|
|
if c == "#WEB:":
|
|
|
c, _, rest = rest.strip().partition(" ")
|
|
|
if line.strip().startswith('"'):
|
|
|
print_frame(stage([whitetext(eval(line.strip()))]))
|
|
|
elif c == "class":
|
|
|
next_classes = rest
|
|
|
elif c == "expression":
|
|
|
expressions = rest.split(" ")
|
|
|
elif c == "moh":
|
|
|
print_frame(stage([wordbubble(eval(rest))]))
|
|
|
elif c == "pirogoeth":
|
|
|
print_frame(stage([characterbubble("pirogoeth", rest)]))
|
|
|
elif c == "scene":
|
|
|
scene = rest.split()[-1]
|
|
|
characters.clear()
|
|
|
del expressions[:]
|
|
|
elif c == "pause":
|
|
|
# Can't pause, can insert an interstitial
|
|
|
print_frame(stage(["<!-- blank interstitial -->"]))
|
|
|
elif c == "show":
|
|
|
character = rest.split()[0].replace(":","")
|
|
|
if character not in character_names:
|
|
|
print >>sys.stderr, "Unknown character:", character
|
|
|
return
|
|
|
if character not in characters:
|
|
|
characters[character] = {'pose': None, 'position': 'center'}
|
|
|
|
|
|
attributes = rest.split()[1:]
|
|
|
if len(attributes) == 1 and attributes[0] == 'm1':
|
|
|
switch_mouth(character, 'm1')
|
|
|
return
|
|
|
if len(attributes) == 1 and attributes[0] == 'm0':
|
|
|
switch_mouth(character, 'm0')
|
|
|
return
|
|
|
if len(attributes) == 1 and not (attributes[0].endswith('_m1') or attributes[0].endswith('_m0')):
|
|
|
switch_pose(character, attributes[0])
|
|
|
return
|
|
|
if len(attributes) > 0:
|
|
|
characters[character]['pose'] = attributes[0]
|
|
|
if len(attributes) > 1:
|
|
|
characters[character]['position'] = attributes[1]
|
|
|
elif c == "hide":
|
|
|
character = rest.split()[0]
|
|
|
if character in characters:
|
|
|
del charactersc[character]
|
|
|
elif c == "music":
|
|
|
output_replace_last_link(format_filename(current_file_num + 1))
|
|
|
next_file()
|
|
|
if rest.split()[0] == 'stop':
|
|
|
audio_background = None
|
|
|
else:
|
|
|
audio_background = eval(rest)
|
|
|
|
|
|
|
|
|
def print_footer():
|
|
|
output_append("\n </div>\n")
|
|
|
if audio_background:
|
|
|
output_append("""
|
|
|
<audio autoplay="autoplay" loop="loop" src="{audio}" />
|
|
|
""".format(audio=audio_background))
|
|
|
output_append("""
|
|
|
</body>
|
|
|
</html>
|
|
|
""")
|
|
|
|
|
|
def print_preload():
|
|
|
output_append("""
|
|
|
</div>
|
|
|
<div id="preload">
|
|
|
{0}
|
|
|
""".format("\n".join(['<img src="{0}" width="1" height="1">'.format(x) for x in images_used])))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
output_append(header)
|
|
|
|
|
|
scene = 'black'
|
|
|
text = whitetext(quick_help_text)
|
|
|
scene = 'splash'
|
|
|
expressions = ['screen-color-80']
|
|
|
print_frame(stage([text]))
|
|
|
|
|
|
for line in fileinput.input():
|
|
|
process(line)
|
|
|
|
|
|
next_file()
|
|
|
|
|
|
with open(format_filename(-1), 'w') as f:
|
|
|
f.write(container_text.format(title="Endgames: Persistance", filename=format_filename(0)))
|
|
|
|
|
|
|