CampBuddy/Camp.Buddy v2.2.1/Camp_Buddy-2.2.1-pc/renpy/common/00director.rpy
2025-03-03 23:00:33 +01:00

1857 lines
48 KiB
Text

# Copyright 2017 Tom Rothamel <pytom@bishoujo.us>
# Permission to use, copy, modify, and/or distribute this software for
# non-commerical purposes is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# For the purpose of this license, when using this software to develop a
# another software program, this program is being used commerically if
# payment is required to distribute that program, to use that program, or
# to access any feature in that program, or if the program presents
# advertising to its user.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# TODO:
#
# - Allow the creator to specify a list of tags they are interested in,
# disabling the auto-detection.
init offset = -1101
default persistent._director_bottom = False
init python in director:
from store import Action, config
import store
# A set of tags that will not be show to the user.
tag_blacklist = { "black", "text", "vtext", "side" }
# A set of tags that should only be used with the scene statement.
scene_tags = { "bg" }
# The set of tags that should only be used with the show statement.
show_tags = set()
# Should we try to filter out tags that have other tags as their
# prefix (as are used alot in layerimages)?
blacklist_prefixed_tags = True
# A list of transforms to use.
transforms = [ "left", "center", "right" ]
# A list of transitions to use.
transitions = [ "dissolve", "pixellate" ]
# A list of audio channels we know about.
audio_channels = [ "music", "sound", "audio" ]
# The name of the voice channel.
voice_channel = "voice"
# A list of audio patterns to use for channels that do not have a
# more specific list of patterns already defined.
audio_patterns = [ "*.opus", "*.ogg", "*.mp3" ]
# A map from channel name to the audio patterns to use for that channel.
audio_channel_patterns = { }
# A map from channel name to the audio files available on that channel.
audio_files = { }
# The spacing between a non-display line and a display line, or vice
# versa.
spacing = 1
# The spacing between two display lines.
director_spacing = 0
# The spacing between two non-display lines.
other_spacing = 0
# The maximum height of viewports containing scrolling information.
viewport_height = 280
# Is the director enabled? Used by the tutorial to protect itself.
enable = True
state = renpy.session.get("director", None)
# A list of statements we find too uninteresting to present to the
# creator.
UNINTERESTING_NODES = (
renpy.ast.Translate,
renpy.ast.EndTranslate,
)
# Nodes that are only interesting when on a line by itself.
ALONE_NODES = (
renpy.ast.Label,
renpy.ast.Pass,
)
def audio_code_to_filename(channel, fn):
return fn
def audio_filename_to_code(channel, fn):
return fn
def audio_filename_to_display(channel, fn):
return fn
def is_play(n):
return isinstance(n, renpy.ast.UserStatement) and n.get_name().startswith("play ")
def is_queue(n):
return isinstance(n, renpy.ast.UserStatement) and n.get_name().startswith("queue ")
def is_stop(n):
return isinstance(n, renpy.ast.UserStatement) and n.get_name().startswith("stop ")
def is_voice(n):
return isinstance(n, renpy.ast.UserStatement) and (n.get_name() == "voice")
def is_interesting(n):
if isinstance(n, (
renpy.ast.Show,
renpy.ast.Hide,
renpy.ast.Scene,
renpy.ast.With,
)):
return True
if is_play(n) or is_queue(n) or is_stop(n) or is_voice(n):
return True
return False
# Initialize the state object if it doesn't exist.
if state is None:
state = store.NoRollback()
# Is the directory currently active.
state.active = False
# Should the director screen be shown.
state.show_director = False
# The list of lines we've seen recently.
state.lines = [ ]
# The mode we're in.
state.mode = "lines"
# The filename and linenumber of the line we're editing,
state.filename = ""
state.linenumber = 0
# What tags are currently showing?
state.showing = set()
# What kind of statement is this?
state.kind = None
state.original_kind = None
# The tag we're updating.
state.tag = ""
state.original_tag = ""
# The attributes of the image we're updating.
state.attributes = [ ]
state.original_attributes = [ ]
# The transforms applied to the image.
state.transforms = [ ]
state.original_transforms = [ ]
# A list of image tags the current image is behind.
state.behind = [ ]
state.original_behind = [ ]
# The transition in a with statement.
state.transition = None
state.original_transition = None
# The audio channel.
state.channel = None
state.original_channel = None
# The audio file.
state.audio = None
state.original_audio = None
# NOTE: Remember to add new states to the Cancel function.
# Has the new line been added to ast.
state.added_statement = None
# Are we changing the script? (Does the node needed to be removed?)
state.change = False
# The position of the first line.
state.old_pos = None
renpy.session["director"] = state
def interact_base():
"""
This is called by interact to update our data structures.
"""
if renpy.game.interface.trans_pause:
return False
show_director = False
# Update the line log.
lines = renpy.get_line_log()
renpy.clear_line_log()
if lines:
pos = (lines[0].filename, lines[0].line)
if pos != state.old_pos:
state.old_pos = pos
renpy.hide_screen("director")
# Update state.line to the current line.
state.line = renpy.get_filename_line()
# State.lines is the list of lines we've just seen, along with
# the actions used to edit those lines.
for lle in lines[-30:]:
if isinstance(lle.node, renpy.ast.Say):
state.lines = [ ]
break
for lle in lines[-30:]:
filename = lle.filename
line = lle.line
node = lle.node
if isinstance(node, UNINTERESTING_NODES):
continue
if isinstance(node, ALONE_NODES):
if [ i for i in renpy.scriptedit.nodes_on_line(filename, line) if not isinstance(i, ALONE_NODES) ]:
continue
if filename.startswith("renpy/"):
show_director = False
continue
else:
show_director = True
text = renpy.scriptedit.get_line_text(filename, line)
if text is None:
text = ""
text = text.strip()
short_fn = filename.rpartition('/')[2]
pos = "{}:{:04d}".format(short_fn, line)
if lle.abnormal:
add_action = None
else:
add_action = AddStatement(lle)
if is_interesting(node):
change_action = ChangeStatement(lle, node)
else:
change_action = None
state.lines.append((
pos,
text,
add_action,
change_action,
))
return show_director
def interact():
"""
This is called once per interaction, to update the list of lines
being displayed, and also to show or hide the director as appropriate.
"""
show_director = interact_base()
# Show the director screen.
if show_director and state.show_director and (renpy.context_nesting_level() == 0):
if not renpy.get_screen("director"):
renpy.show_screen("director")
else:
if renpy.get_screen("director"):
renpy.hide_screen("director")
def line_log_callback(lle):
"""
This annotates a line log entry with more information.
"""
lle.showing = set(renpy.get_showing_tags())
def init():
"""
This is called once at game start, to reconfigured Ren'Py to
support the ID.
"""
config.clear_lines = False
config.line_log = True
config.line_log_callbacks = [ line_log_callback ]
config.start_interact_callbacks.append(interact)
if state.active:
init()
def command():
"""
This can be used to invoke the ID from the command line.
"""
if not state.active:
state.active = True
init()
return True
renpy.arguments.register_command("director", command)
def get_scene_show_hide_statement():
if state.kind == "scene" and state.tag is None:
return "scene"
if state.tag is None:
return None
if state.kind == "hide":
attributes = state.attributes
else:
if state.change and (state.attributes == state.original_attributes):
attributes = state.attributes
else:
attributes = get_image_attributes()
if attributes is None:
return None
rv = [ state.kind ]
rv.append(state.tag)
rv.extend(attributes)
rv = " ".join(rv)
if state.transforms:
rv += " at " + ", ".join(state.transforms)
if state.behind:
rv += " behind " + ",".join(state.behind)
return rv
def quote_audio():
"""
Returns the quoted audio filename.
"""
if state.audio is None:
return None
if state.channel is not None:
audio = audio_filename_to_code(state.channel, state.audio)
else:
audio = state.audio
return "'" + audio.replace("\\", "\\\\").replace("'", "\\'") + "'"
def get_play_queue_statement():
if state.channel is None:
return None
if state.audio is None:
return None
return "{} {} {}".format(state.kind, state.channel, quote_audio())
def get_stop_statement():
if state.channel is None:
return None
return "{} {}".format(state.kind, state.channel)
def get_voice_statement():
if state.audio is None:
return None
return "voice {}".format(quote_audio())
def get_statement():
"""
If a statement is defined enough to implement, returns the text
that can be added to the AST. Otherwise, returns None.
"""
if state.kind is None:
rv = None
elif state.kind == "with":
rv = "with {}".format(state.transition)
elif state.kind in ("play", "queue"):
rv = get_play_queue_statement()
elif state.kind == "stop":
rv = get_stop_statement()
elif state.kind == "voice":
rv = get_voice_statement()
else:
rv = get_scene_show_hide_statement()
return rv
def update_ast(force=False):
"""
Updates the abstract syntax tree to match the current state, forcing
a rollback if something significant has changed. This always forces
an interaction restart.
"""
statement = get_statement()
if (state.added_statement == statement) and (not force):
renpy.restart_interaction()
return
linenumber = state.linenumber
if statement:
renpy.scriptedit.add_to_ast_before(statement, state.filename, linenumber)
linenumber += 1
if state.added_statement is not None:
renpy.scriptedit.remove_from_ast(state.filename, linenumber)
state.added_statement = statement
renpy.rollback(checkpoints=0, force=True, greedy=True)
def dump_script():
for i in range(0, 100):
key = ('game/script.rpy', i)
if key in renpy.scriptedit.lines:
print(key, renpy.scriptedit.lines[key].text)
def pick_tag():
"""
If there is only one valid tag, choose it and move to attribute mode.
"""
tags = get_tags()
if state.mode != "tag":
return
if len(tags) != 1:
return
state.tag = tags[0]
if state.kind == "hide":
return
state.mode = "attributes"
def find_statement(filename, line, delta, limit=10):
"""
Tries to find a statement near `line`. If it can't find it on `line`
itself, it searches forward (if `delta`) is positive or back (if `delta`)
is negative.
Returns the line number and nodes for the statement, or (None, None) if the
statement is not found after searching `limit` lines.
"""
for _i in range(limit):
nodes = renpy.scriptedit.nodes_on_line(filename, line)
if nodes:
return line, nodes
line += delta
return None, None
def needs_space(filename, line):
"""
Returns True if there should be a space between (filename, line-1) and
(filename, line).
"""
previous, previous_nodes = find_statement(filename, line-1, -1)
if previous is None:
return None
next, next_nodes = find_statement(filename, line, 1)
if next is None:
return None
def display(nodes):
for n in nodes:
if is_interesting(n):
return True
return False
previous_display = display(previous_nodes)
next_display = display(next_nodes)
if previous_display ^ next_display:
return spacing
elif previous_display:
return director_spacing
else:
return other_spacing
def is_spacing(filename, line):
line = renpy.scriptedit.get_full_text(filename, line)
if line is None:
return False
return not line.strip()
def adjust_spacing_before(filename, line):
"""
Adjusts the spacing between (filename, line) and the line before it.
"""
line, _ = find_statement(filename, line, 1)
if line is None:
return
space = needs_space(filename, line)
if space is None:
return
previous, _ = find_statement(filename, line - 1, -1)
blanks = [ ]
for i in range(previous + 1, line):
if not is_spacing(filename, i):
break
blanks.append(i)
delta_space = space - len(blanks)
if delta_space > 0:
for _ in range(delta_space):
renpy.scriptedit.adjust_ast_linenumbers(filename, previous + 1, 1)
renpy.scriptedit.insert_line_before('', filename, previous + 1)
elif delta_space < 0:
blanks.reverse()
blanks = blanks[space:]
for i in blanks:
renpy.scriptedit.adjust_ast_linenumbers(filename, i, -1)
renpy.scriptedit.remove_line(filename, i)
def add_spacing(filename, line):
adjust_spacing_before(filename, line + 1)
adjust_spacing_before(filename, line)
def remove_spacing(filename, line):
adjust_spacing_before(filename, line)
# Screen support functions #################################################
def get_tags():
"""
Returns a list of tags that are valid for the current statement kind.
"""
if state.kind == "scene":
rv = [ i for i in renpy.get_available_image_tags() if not i.startswith("_") if i not in tag_blacklist if i in scene_tags ]
elif state.kind == "show":
if show_tags:
rv = [ i for i in renpy.get_available_image_tags() if not i.startswith("_") if i not in tag_blacklist if i in show_tags ]
else:
rv = [ i for i in renpy.get_available_image_tags() if not i.startswith("_") if i not in tag_blacklist if i not in scene_tags ]
elif state.kind == "hide":
rv = [ i for i in renpy.get_available_image_tags() if i in state.showing if not i.startswith("_") if i not in tag_blacklist if i not in scene_tags ]
else:
rv = [ ]
rv.sort(key=component_key)
return rv
def get_attributes():
"""
Returns a list of attributes that are valid for the current tag.
"""
if not state.tag:
return [ ]
return renpy.get_ordered_image_attributes(state.tag, [ ], component_key)
def get_transforms():
"""
Returns a list of transforms that are valid for the current tag.
"""
return transforms
def get_image_attributes():
"""
Returns the list of attributes in the current image, or None if
no image is known.
"""
if state.tag is None:
return None
return renpy.check_image_attributes(state.tag, state.attributes)
def get_ordered_attributes():
"""
Returns the list of attributes in the order they appear in the
current image (if known), or in the order they were selected
otherwise.
"""
attrs = get_image_attributes()
if attrs is not None:
return attrs
return state.attributes
def get_behind_tags(exclude=None):
"""
Get a list of tags the current tag can be placed behind.
"""
rv = [ ]
for t in state.showing:
if t in scene_tags:
continue
if t == exclude:
continue
rv.append(t)
return rv
# Actions ##################################################################
class Start(Action):
"""
This action starts the director and displays the lines screen.
"""
def __call__(self):
if not state.show_director:
if store._menu:
return None
if not config.developer:
return None
if not enable:
renpy.notify(_("The interactive director is not enabled here."))
return None
if state.show_director:
return None
state.show_director = True
state.mode = "lines"
if state.active:
renpy.show_screen("director")
renpy.restart_interaction()
return
# renpy.session["compile"] = True
renpy.session["_greedy_rollback"] = True
state.active = True
store._reload_game()
def get_sensitive(self):
return (not state.active) or (not state.show_director)
class Stop(Action):
"""
This hides the director interface.
"""
def __call__(self):
state.show_director = False
if renpy.get_screen("director"):
renpy.hide_screen("director")
renpy.restart_interaction()
class AddStatement(Action):
"""
An action that adds a new statement before `filename`:`linenumber`.
"""
def __init__(self, lle):
self.lle = lle
self.sensitive = renpy.scriptedit.can_add_before(self.lle.filename, self.lle.line)
def get_sensitive(self):
return self.sensitive
def __call__(self):
state.filename = self.lle.filename
state.linenumber = self.lle.line
state.showing = self.lle.showing
state.kind = None
state.mode = "kind"
state.tag = None
state.original_tag = None
state.attributes = [ ]
state.original_attributes = [ ]
state.transforms = [ ]
state.original_transforms = [ ]
state.transition = None
state.original_transition = None
state.behind = [ ]
state.original_behind = [ ]
state.channel = None
state.original_channel = None
state.audio = None
state.original_audio = None
state.added_statement = None
state.change = False
update_ast()
def is_scene_show_hide_editable(node):
"""
Return true if a Scene, Show, or Hide ATL node is editable, or
False otherwise.
"""
if not node.imspec:
return True
if node.imspec[1]: # expression
return False
if node.imspec[2]: # tag
return False
if node.imspec[4]: # layer
return False
if node.imspec[5]: # zorder
return False
if getattr(node, "atl", None):
return False
return True
class ChangeStatement(Action):
"""
An action that changes the statement at `filename`:`linenumber`.
`node`
Should be the AST node corresponding to that statement.
"""
def __init__(self, lle, node):
self.lle = lle
self.tag = None
self.attributes = [ ]
self.transforms = [ ]
self.transition = None
self.behind = [ ]
self.channel = None
self.audio = None
self.sensitive = True
def audio(n):
self.sensitive = False
name = n.parsed[0]
p = n.parsed[1]
self.kind = name[0]
self.channel = p["channel"] or name[1]
if "file" in p:
try:
self.audio = eval(p["file"])
except:
return
self.audio = audio_code_to_filename(self.channel, self.audio)
else:
self.audio = None
if p.get("loop", None):
return
fadeout = p.get("fadeout", None)
if (fadeout is not None) and (fadeout != "None"):
return
if p.get("if_changed", False):
return
if p.get("fadein", "0") != "0":
return
self.sensitive = True
if isinstance(node, renpy.ast.Show):
self.kind = "show"
self.tag = node.imspec[0][0]
self.attributes = list(node.imspec[0][1:])
self.transforms = list(node.imspec[3])
self.behind = list(node.imspec[6])
self.sensitive = is_scene_show_hide_editable(node)
elif isinstance(node, renpy.ast.Scene):
self.kind = "scene"
# The scene statement does not need to show an image.
if node.imspec is not None:
self.tag = node.imspec[0][0]
self.attributes = list(node.imspec[0][1:])
self.transforms = list(node.imspec[3])
self.behind = list(node.imspec[6])
else:
self.tag = None
self.attributes = [ ]
self.transforms = [ ]
self.behind = [ ]
self.sensitive = is_scene_show_hide_editable(node)
elif isinstance(node, renpy.ast.Hide):
self.kind = "hide"
self.tag = node.imspec[0][0]
self.attributes = list(node.imspec[0][1:])
self.transforms = list(node.imspec[3])
self.sensitive = is_scene_show_hide_editable(node)
elif isinstance(node, renpy.ast.With):
self.kind = "with"
self.transition = node.expr
elif is_play(node) or is_queue(node) or is_stop(node):
audio(node)
elif is_voice(node):
p = node.parsed[1]
self.kind = "voice"
self.sensitive = False
self.channel = voice_channel
try:
self.audio = eval(p)
except:
return
self.audio = audio_code_to_filename(self.channel, self.audio)
self.sensitive = True
def get_sensitive(self):
return self.sensitive
def __call__(self):
state.filename = self.lle.filename
state.linenumber = self.lle.line
state.showing = self.lle.showing
state.kind = self.kind
state.original_kind = self.kind
if self.kind == "with":
state.mode = "with"
elif self.kind == "hide":
state.mode = "tag"
elif self.kind == "play" or self.kind == "queue":
state.mode = "audio"
elif self.kind == "stop":
state.mode = "channel"
elif self.kind == "voice":
state.mode = "audio"
else:
if self.tag is None:
state.mode = "tag"
else:
state.mode = "attributes"
state.tag = self.tag
state.original_tag = self.tag
state.attributes = self.attributes
state.original_attributes = list(self.attributes)
state.transforms = list(self.transforms)
state.original_transforms = list(self.transforms)
state.transition = self.transition
state.original_transition = self.transition
state.behind = self.behind
state.original_behind = list(self.behind)
state.channel = self.channel
state.original_channel = self.channel
state.audio = self.audio
state.original_audio = self.audio
state.added_statement = True
state.change = True
update_ast()
class SetKind(Action):
def __init__(self, kind):
self.kind = kind
def __call__(self):
if self.kind != state.kind:
state.kind = self.kind
state.tag = None
state.attributes = [ ]
if self.kind in ("scene", "show", "hide"):
state.mode = "tag"
pick_tag()
if self.kind == "with":
state.mode = "with"
if self.kind in ("play", "queue", "stop"):
state.mode = "channel"
if self.kind == "voice":
state.channel = "voice"
state.mode = "audio"
update_ast()
class SetTag(Action):
"""
An action that sets the image tag.
"""
def __init__(self, tag):
self.tag = tag
def __call__(self):
if state.tag != self.tag:
state.tag = self.tag
state.attributes = [ ]
if state.kind != "hide":
state.mode = "attributes"
update_ast()
def get_selected(self):
return self.tag == state.tag
class ToggleAttribute(Action):
"""
This action toggles on and off an attribute. If an attribute being
toggled on conflicts with other attributes, those attributes are
removed.
Then the AST is updated.
"""
def __init__(self, attribute):
self.attribute = attribute
def __call__(self):
if self.attribute in state.attributes:
state.attributes.remove(self.attribute)
else:
state.attributes.append(self.attribute)
compatible = set()
for i in renpy.get_ordered_image_attributes(state.tag, [ self.attribute ]):
compatible.add(i)
state.attributes = [ i for i in state.attributes if i in compatible ]
update_ast()
def get_selected(self):
return self.attribute in state.attributes
class SetList(Action):
"""
When clicked once, sets l to [ v ]. When clicked again, sets l to [ ]
"""
def __init__(self, l, v):
self.l = l
self.v = v
def __call__(self):
if self.v in self.l:
self.l.remove(self.v)
else:
self.l[:] = [ self.v ]
update_ast()
def get_selected(self):
return self.v in self.l
class ToggleList(Action):
"""
Toggles the presence or absence of v in l, appending it to the end
of the list when necessary.
"""
def __init__(self, l, v):
self.l = l
self.v = v
def __call__(self):
if self.v in self.l:
self.l.remove(self.v)
else:
self.l.append(self.v)
update_ast()
def get_selected(self):
return self.v in self.l
class SetTransition(Action):
"""
This sets the transition used by a with statement.
"""
def __init__(self, transition):
self.transition = transition
def __call__(self):
state.transition = self.transition
update_ast()
def get_selected(self):
return self.transition == state.transition
class SetChannel(Action):
"""
This sets the channel used by an audio statement.
"""
def __init__(self, channel):
self.channel = channel
def __call__(self):
if state.kind != "stop":
state.mode = "audio"
state.channel = self.channel
update_ast()
def get_selected(self):
return self.channel == state.channel
class SetAudio(Action):
"""
This sets the audio file played by a statement.
"""
def __init__(self, filename):
self.filename = filename
def __call__(self):
state.audio = self.filename
update_ast()
def get_selected(self):
return state.audio == self.filename
class Commit(Action):
"""
Commits the current statement to the .rpy files.
"""
def __call__(self):
statement = get_statement()
if statement:
renpy.scriptedit.insert_line_before(statement, state.filename, state.linenumber)
if state.change:
if statement:
renpy.scriptedit.remove_line(state.filename, state.linenumber + 1)
else:
renpy.scriptedit.remove_line(state.filename, state.linenumber)
if not state.change and statement:
add_spacing(state.filename, state.linenumber)
if state.change and not statement:
remove_spacing(state.filename, state.linenumber)
state.mode = "lines"
renpy.clear_line_log()
renpy.rollback(checkpoints=0, force=True, greedy=True)
def get_sensitive(self):
return get_statement()
class Reset(Action):
"""
This action resets the AST to what it was when we started adjusting the
statement.
"""
def __call__(self):
state.kind = state.original_kind
state.tag = state.original_tag
state.attributes = state.original_attributes
state.transforms = state.original_transforms
if state.kind is None:
state.mode = "kind"
elif state.tag is None:
state.mode = "tag"
update_ast()
class Cancel(Action):
"""
This action cancels the operation, resetting the AST and returning to
the lines screen.
"""
def __call__(self):
state.kind = state.original_kind
state.tag = state.original_tag
state.attributes = state.original_attributes
state.transforms = state.original_transforms
state.transition = state.original_transition
state.channel = state.original_channel
state.audio = state.original_audio
state.mode = "lines"
update_ast()
class Remove(Action):
"""
This action removes the current line from the AST and the script.
"""
def __call__(self):
state.kind = None
state.tag = None
state.mode = "lines"
try:
update_ast(force=True)
finally:
if state.change:
renpy.scriptedit.remove_line(state.filename, state.linenumber)
# Displayables #############################################################
class SemiModal(renpy.Displayable):
"""
This wraps a displayable, and ignores
"""
def __init__(self, child):
renpy.Displayable.__init__(self)
self.child = child
self.w = 0
self.h = 0
def per_interact(self):
renpy.display.render.redraw(self, 0)
def render(self, width, height, st, at):
if renpy.get_screen("confirm"):
return renpy.Render(0, 0)
surf = renpy.render(self.child, width, height, st, at)
w, h = surf.get_size()
self.w = w
self.h = h
rv = renpy.Render(w, h)
rv.blit(surf, (0, 0))
return rv
def event(self, ev, x, y, st):
if renpy.get_screen("confirm"):
return
rv = self.child.event(ev, x, y, st)
if rv is not None:
return rv
if ev.type == renpy.display.core.TIMEEVENT:
return None
if state.mode != "lines":
if renpy.map_event(ev, "rollback") or renpy.map_event(ev, "rollforward"):
raise renpy.IgnoreEvent()
raise renpy.display.layout.IgnoreLayers()
if (0 <= x < self.w) and (0 <= y < self.h):
raise renpy.display.layout.IgnoreLayers()
return None
def get_placement(self):
return self.child.get_placement()
def visit(self):
return [ self.child ]
# Sort #####################################################################
import re
def component_key(s):
"""
Sorts l in a way that groups numbers together and treats them as
numbers (so c10 comes after c9, not c1.)
"""
rv = [ ]
for i, v in enumerate(re.split(r'(\d+)', s.lower())):
if not v:
continue
if i & 1:
v = int(v)
rv.append(v)
return tuple(rv)
init 2202 python hide in director:
if state.active:
for name, file, _line in renpy.dump.transforms:
if file.startswith("renpy/common/"):
continue
if file == "game/screens.rpy":
continue
transforms.append(name)
transforms.sort(key=component_key)
import fnmatch
for c in audio_channels + [ voice_channel ]:
patterns = audio_channel_patterns.get(c, audio_patterns)
if c not in audio_files:
audio_files[c] = [ ]
for fn in renpy.list_files():
for p in patterns:
if fnmatch.fnmatch(fn, p):
break
else:
continue
audio_files[c].append(fn)
audio_files[c].sort()
if blacklist_prefixed_tags:
available = set()
for i in sorted(renpy.get_available_image_tags()):
blacklist = False
prefix = i
while prefix:
prefix, _, _ = prefix.rpartition("_")
if prefix in available:
blacklist = True
if i in scene_tags:
blacklist = False
if i in show_tags:
blacklist = False
if blacklist:
tag_blacklist.add(i)
else:
available.add(i)
# Styles and screens ###########################################################
style director_frame is _frame:
xfill True
yfill False
ypadding 0
style director_top_frame is director_frame:
background "#d0d0d0d0"
yalign 0.0
style director_bottom_frame is director_frame:
background "#d0d0d0f0"
yalign 1.0
style director_text is _text:
size 18
style director_label
style director_label_text is director_text:
bold True
style director_button is empty
style director_button_text is director_text:
color "#405060"
hover_color "#048"
insensitive_color "#00000020"
selected_color "#0099cc"
style director_edit_button is director_button:
xsize 18
style director_edit_button_text is director_button_text:
font "DejaVuSans.ttf"
xalign 0.5
style director_action_button is director_button
style director_action_button_text is director_button_text:
size 26
style director_icon_action_button is director_action_button:
xpadding 10
style director_icon_action_button_text is director_action_button_text:
font "DejaVuSans.ttf"
style director_statement_text is director_text:
size 20
style director_statement_button is director_button
style director_statement_button_text is director_button_text:
size 20
style director_vscrollbar is _vscrollbar
screen director_move_button():
if persistent._director_bottom:
textbutton _("⬆"):
style "director_icon_action_button"
action SetField(persistent, "_director_bottom", False)
xalign 1.0
else:
textbutton _("⬇"):
style "director_icon_action_button"
action SetField(persistent, "_director_bottom", True)
xalign 1.0
screen director_lines(state):
frame:
style "empty"
background Solid("#fff8", xsize=20, xpos=gui._scale(300))
has vbox:
xfill True
viewport:
scrollbars "vertical"
ymaximum director.viewport_height
mousewheel True
yinitial 1.0
viewport_yfill False
has vbox:
xfill True
for line_pos, line_text, add_action, change_action in state.lines:
fixed:
yfit True
textbutton "+":
xpos gui._scale(300)
action add_action
style "director_edit_button"
alt ("add before " + line_text)
fixed:
yfit True
text "[line_pos]":
xpos (gui._scale(300) - 10)
xalign 1.0
text_align 1.0
style "director_text"
if change_action:
textbutton "✎":
action change_action
xpos gui._scale(300)
style "director_edit_button"
alt ("change " + line_text)
frame:
style "empty"
left_padding (gui._scale(300) + 30)
text "[line_text!q]" style "director_text"
null height 14
fixed:
yfit True
hbox:
xpos (gui._scale(300) + 30)
textbutton _("Done"):
action director.Stop()
style "director_action_button"
use director_move_button()
screen director_statement(state):
$ kind = state.kind or __("(statement)")
$ tag = state.tag or __("(tag)")
$ attributes = " ".join(director.get_ordered_attributes()) or __("(attributes)")
$ transforms = ", ".join(state.transforms) or __("(transform)")
$ behind = ", ".join(state.behind) or __("(tag)")
$ behind_tags = director.get_behind_tags(state.tag)
hbox:
style_prefix "director_statement"
textbutton "[kind] " action SetField(state, "mode", "kind")
textbutton "[tag] " action SetField(state, "mode", "tag")
if state.attributes or state.kind in { "scene", "show"}:
textbutton "[attributes] " action SetField(state, "mode", "attributes")
if state.transforms or state.kind in { "scene", "show"}:
text "at "
textbutton "[transforms] " action SetField(state, "mode", "transform")
if behind_tags and (state.kind in { "show" }):
text "behind "
textbutton "[behind]" action SetField(state, "mode", "behind")
null height 14
screen director_with_statement(state):
$ transition = state.transition or __("(transition)")
hbox:
style_prefix "director_statement"
text "with "
textbutton "[transition]" action SetField(state, "mode", "with")
null height 14
screen director_audio_statement(state):
$ channel = state.channel or __("(channel)")
$ audio = director.quote_audio() or __("(filename)")
hbox:
style_prefix "director_statement"
textbutton "[state.kind] " action SetField(state, "mode", "kind")
if state.kind != "voice":
textbutton "[channel] " action SetField(state, "mode", "channel")
if state.kind != "stop":
textbutton "[audio!q]" action SetField(state, "mode", "audio")
null height 14
screen director_footer(state):
null height 14
fixed:
yfit True
hbox:
style_prefix "director_action"
spacing 26
if state.change:
textbutton _("Change") action director.Commit()
else:
textbutton _("Add") action director.Commit()
textbutton _("Cancel") action director.Cancel()
if state.change:
textbutton _("Remove") action director.Remove()
use director_move_button()
# Formats the choices.
screen director_choices(title):
text title size 20
frame:
style "empty"
left_margin 10
viewport:
scrollbars "vertical"
ymaximum director.viewport_height
mousewheel True
yinitial 0.0
viewport_yfill False
hbox:
box_wrap True
spacing 20
transclude
screen director_kind(state):
vbox:
xfill True
use director_statement(state)
use director_choices(_("Statement:")):
textbutton "scene" action director.SetKind("scene")
textbutton "show" action director.SetKind("show")
textbutton "hide" action director.SetKind("hide")
textbutton "with" action director.SetKind("with")
textbutton "play" action director.SetKind("play")
textbutton "queue" action director.SetKind("queue")
textbutton "stop" action director.SetKind("stop")
textbutton "voice" action director.SetKind("voice")
use director_footer(state)
screen director_tag(state):
vbox:
xfill True
use director_statement(state)
use director_choices(_("Tag:")):
for t in director.get_tags():
textbutton "[t]":
action director.SetTag(t)
use director_footer(state)
screen director_attributes(state):
vbox:
xfill True
use director_statement(state)
use director_choices(_("Attributes:")):
for t in director.get_attributes():
textbutton "[t]":
action director.ToggleAttribute(t)
style "director_button"
ypadding 0
use director_footer(state)
screen director_transform(state):
vbox:
xfill True
use director_statement(state)
use director_choices(_("Transforms:")):
for t in director.get_transforms():
textbutton "[t]":
action director.SetList(state.transforms, t)
alternate director.ToggleList(state.transforms, t)
style "director_button"
ypadding 0
use director_footer(state)
screen director_behind(state):
vbox:
xfill True
use director_statement(state)
use director_choices(_("Behind:")):
for t in director.get_behind_tags(state.tag):
textbutton "[t]":
action director.SetList(state.behind, t)
alternate director.ToggleList(state.behind, t)
style "director_button"
ypadding 0
use director_footer(state)
screen director_with(state):
vbox:
xfill True
use director_with_statement(state)
use director_choices(_("Transition:")):
for t in director.transitions:
textbutton "[t]":
action director.SetTransition(t)
style "director_button"
ypadding 0
use director_footer(state)
screen director_channel(state):
vbox:
xfill True
use director_audio_statement(state)
use director_choices(_("Channel:")):
for c in director.audio_channels:
textbutton "[c]":
action director.SetChannel(c)
style "director_button"
ypadding 0
use director_footer(state)
screen director_audio(state):
vbox:
xfill True
use director_audio_statement(state)
use director_choices(_("Audio Filename:")):
for fn in director.audio_files.get(state.channel, [ ]):
$ elided_fn = director.audio_filename_to_display(state.channel, fn)
textbutton "[elided_fn]":
action director.SetAudio(fn)
style "director_button"
ypadding 0
use director_footer(state)
screen director():
zorder 1400
$ state = director.state
if renpy.loadable("id/" + state.mode + ".png"):
add ("id/" + state.mode + ".png")
frame:
style_prefix "director"
style ("director_bottom_frame" if persistent._director_bottom else "director_top_frame")
xpadding ( 0 if state.mode == "lines" else gui._scale(20) )
at director.SemiModal
has fixed:
fit_first True
if state.mode == "lines":
use director_lines(state)
elif state.mode == "kind":
use director_kind(state)
elif state.mode == "tag":
use director_tag(state)
elif state.mode == "attributes":
use director_attributes(state)
elif state.mode == "transform":
use director_transform(state)
elif state.mode == "behind":
use director_behind(state)
elif state.mode == "with":
use director_with(state)
elif state.mode == "channel":
use director_channel(state)
elif state.mode == "audio":
use director_audio(state)
if state.mode == "lines":
key "director" action director.Stop()