671 lines
18 KiB
Text
671 lines
18 KiB
Text
# Copyright 2004-2019 Tom Rothamel <pytom@bishoujo.us>
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person
|
|
# obtaining a copy of this software and associated documentation files
|
|
# (the "Software"), to deal in the Software without restriction,
|
|
# including without limitation the rights to use, copy, modify, merge,
|
|
# publish, distribute, sublicense, and/or sell copies of the Software,
|
|
# and to permit persons to whom the Software is furnished to do so,
|
|
# subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be
|
|
# included in all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
# This file contains code that helps support the development of Ren'Py
|
|
# games.
|
|
|
|
# List styles.
|
|
|
|
screen _developer:
|
|
|
|
zorder 1001
|
|
modal True
|
|
|
|
frame:
|
|
style_prefix ""
|
|
|
|
has side "t c b":
|
|
spacing gui._scale(10)
|
|
|
|
label _(u"Developer Menu")
|
|
|
|
fixed:
|
|
vbox:
|
|
|
|
textbutton _("Interactive Director (D)"):
|
|
action [ director.Start(), Hide("_developer") ]
|
|
textbutton _("Reload Game (Shift+R)"):
|
|
action ui.callsinnewcontext("_save_reload_game")
|
|
textbutton _("Console (Shift+O)"):
|
|
action [ Hide("_developer"), _console.enter ]
|
|
textbutton _("Variable Viewer"):
|
|
action ui.callsinnewcontext("_debugger_screen")
|
|
textbutton _("Image Location Picker"):
|
|
action ui.callsinnewcontext("_image_location_picker")
|
|
textbutton _("Filename List"):
|
|
action ui.callsinnewcontext("_filename_list")
|
|
|
|
if not renpy.get_screen("_image_load_log"):
|
|
textbutton _("Show Image Load Log (F4)"):
|
|
action Show("_image_load_log")
|
|
else:
|
|
textbutton _("Hide Image Load Log (F4)"):
|
|
action Hide("_image_load_log")
|
|
|
|
textbutton _("Image Attributes"):
|
|
action ui.callsinnewcontext("_image_attributes")
|
|
|
|
hbox:
|
|
spacing gui._scale(25)
|
|
|
|
textbutton _(u"Return"):
|
|
action Hide("_developer")
|
|
size_group "developer"
|
|
|
|
|
|
screen _image_attributes():
|
|
|
|
frame:
|
|
style_prefix ""
|
|
right_padding 0
|
|
|
|
has side "c b":
|
|
spacing gui._scale(10)
|
|
|
|
|
|
vbox:
|
|
label _("Image Attributes")
|
|
|
|
|
|
for name in renpy.get_hidden_tags():
|
|
$ attributes = " ".join(renpy.get_attributes(name))
|
|
text _("[name] [attributes] (hidden)")
|
|
|
|
for name in renpy.get_showing_tags():
|
|
$ attributes = " ".join(renpy.get_attributes(name))
|
|
text _("[name] [attributes]")
|
|
|
|
hbox:
|
|
spacing gui._scale(25)
|
|
|
|
textbutton _(u"Return"):
|
|
action Return(True)
|
|
|
|
|
|
pass
|
|
|
|
label _image_attributes:
|
|
|
|
call _enter_game_menu
|
|
|
|
call screen _image_attributes
|
|
|
|
return
|
|
|
|
screen _variable_viewer(all_entries, deleted_entries):
|
|
|
|
zorder 1010
|
|
modal True
|
|
default show_deleted = True
|
|
python:
|
|
if show_deleted:
|
|
entries = all_entries
|
|
else:
|
|
entries = [(n, v) for n, v in all_entries if n not in deleted_entries]
|
|
|
|
frame:
|
|
style_prefix ""
|
|
right_padding 0
|
|
|
|
has side "t c b":
|
|
spacing gui._scale(10)
|
|
|
|
label _("Variable Viewer")
|
|
|
|
viewport:
|
|
scrollbars "both"
|
|
child_size (4000, 0)
|
|
yfill True
|
|
xfill True
|
|
mousewheel True
|
|
|
|
vbox:
|
|
|
|
if not entries:
|
|
text _("Nothing to inspect.")
|
|
|
|
for vn, value in entries:
|
|
text "[vn] = [value!q]"
|
|
|
|
hbox:
|
|
spacing gui._scale(25)
|
|
|
|
textbutton _(u"Return"):
|
|
action Return(True)
|
|
if all_entries and deleted_entries:
|
|
textbutton (_(u"Hide deleted") if show_deleted else _(u"Show deleted")):
|
|
action ToggleScreenVariable("show_deleted")
|
|
|
|
|
|
label _debugger_screen:
|
|
|
|
call _enter_game_menu
|
|
|
|
python hide:
|
|
|
|
import repr
|
|
aRepr = repr.Repr()
|
|
aRepr.maxstring = 120
|
|
|
|
entries = [ ]
|
|
deleted_entries = set()
|
|
|
|
for sn, d in renpy.python.store_dicts.items():
|
|
|
|
if sn.startswith("store._"):
|
|
continue
|
|
|
|
for vn in d.ever_been_changed:
|
|
|
|
if vn.startswith("__00"):
|
|
continue
|
|
|
|
if vn.startswith("_") and not vn.startswith("__"):
|
|
continue
|
|
|
|
if vn not in d:
|
|
value = "deleted"
|
|
else:
|
|
value = aRepr.repr(d[vn])
|
|
|
|
if vn == "nvl_list":
|
|
continue
|
|
|
|
name = (sn + "." + vn)[6:]
|
|
|
|
if vn not in d:
|
|
deleted_entries.add(name)
|
|
|
|
entries.append((name, value))
|
|
|
|
entries.sort(key=lambda e : e[0])
|
|
|
|
renpy.call_screen("_variable_viewer", all_entries=entries, deleted_entries=deleted_entries)
|
|
|
|
return
|
|
|
|
label _theme_test:
|
|
|
|
call _enter_game_menu
|
|
|
|
python hide:
|
|
|
|
# Never gets pickled
|
|
def role(b):
|
|
if b:
|
|
return "selected_"
|
|
else:
|
|
return ""
|
|
|
|
toggle_var = True
|
|
adj = ui.adjustment(100, 25, page=25, adjustable=True)
|
|
|
|
while True:
|
|
|
|
ui.window(style=style.gm_root)
|
|
ui.null()
|
|
|
|
# Buttons
|
|
ui.hbox(box_spacing=10, xpos=10, ypos=10)
|
|
|
|
ui.vbox(box_spacing=10)
|
|
|
|
sg = "theme_test"
|
|
|
|
ui.frame(style='menu_frame')
|
|
ui.vbox()
|
|
layout.label("Button", None)
|
|
ui.textbutton("Button", size_group=sg, clicked=ui.returns("gndn"))
|
|
ui.textbutton("Button (Selected)", size_group=sg, clicked=ui.returns("gndn"), role=role(True))
|
|
ui.textbutton("Small", clicked=ui.returns("gndn"), style='small_button')
|
|
ui.close()
|
|
|
|
ui.frame(style='menu_frame')
|
|
ui.vbox()
|
|
layout.label("Radio Button", None)
|
|
ui.textbutton("True", size_group=sg, clicked=ui.returns("set"), role=role(toggle_var), style='radio_button')
|
|
ui.textbutton("False", size_group=sg, clicked=ui.returns("unset"), role=role(not toggle_var), style='radio_button')
|
|
ui.close()
|
|
|
|
ui.frame(style='menu_frame')
|
|
ui.vbox()
|
|
layout.label("Check Button", None)
|
|
ui.textbutton("Check Button", size_group=sg, clicked=ui.returns("toggle"), role=role(toggle_var), style='check_button')
|
|
ui.close()
|
|
|
|
ui.frame(style='menu_frame')
|
|
ui.vbox(box_spacing=2)
|
|
ui.bar(adjustment=adj, style='bar', xmaximum=200)
|
|
ui.bar(adjustment=adj, style='slider', xmaximum=200)
|
|
ui.bar(adjustment=adj, style='scrollbar', xmaximum=200)
|
|
ui.close()
|
|
|
|
ui.close() # vbox
|
|
|
|
ui.frame(style='menu_frame')
|
|
ui.hbox(box_spacing=2)
|
|
ui.bar(adjustment=adj, style='vbar', ymaximum=200)
|
|
ui.bar(adjustment=adj, style='vslider', ymaximum=200)
|
|
ui.bar(adjustment=adj, style='vscrollbar', ymaximum=200)
|
|
ui.close()
|
|
|
|
ui.frame(style='menu_frame', xmaximum=0.95)
|
|
ui.vbox(box_spacing=20)
|
|
layout.prompt("This is a prompt. We've made this text long enough to wrap around so it fills multiple lines.", None)
|
|
ui.close()
|
|
|
|
ui.close() # hbox
|
|
|
|
ui.frame(style='menu_frame', xalign=.01, yalign=.99)
|
|
ui.textbutton(_("Return to the developer menu"), clicked=ui.returns("return"))
|
|
|
|
rv = ui.interact()
|
|
if rv == "return":
|
|
break
|
|
|
|
elif rv == "set":
|
|
toggle_var = True
|
|
elif rv == "unset":
|
|
toggle_var = False
|
|
elif rv == "toggle":
|
|
toggle_var = not toggle_var
|
|
|
|
return
|
|
|
|
|
|
# Not used any more, but may be in save files.
|
|
init -1050 python:
|
|
config.missing_background = "black"
|
|
|
|
|
|
# Not used any more, but may be in save files.
|
|
screen _missing_images:
|
|
pass
|
|
|
|
init 1050 python:
|
|
|
|
if config.developer:
|
|
|
|
def __missing_show_callback(name, what, layer):
|
|
if layer != 'master':
|
|
return False
|
|
|
|
if not renpy.count_displayables_in_layer(layer):
|
|
p = Placeholder("bg")
|
|
else:
|
|
p = Placeholder()
|
|
|
|
return p._duplicate(p._args.copy(args=what))
|
|
|
|
def __missing_hide_callback(name, layer):
|
|
if layer != 'master':
|
|
return False
|
|
|
|
return True
|
|
|
|
def __missing_scene_callback(layer):
|
|
if layer != 'master':
|
|
return False
|
|
|
|
return True
|
|
|
|
if config.missing_scene is None:
|
|
config.missing_scene = __missing_scene_callback
|
|
|
|
if config.missing_show is None:
|
|
config.missing_show = __missing_show_callback
|
|
|
|
if config.missing_hide is None:
|
|
config.missing_hide = __missing_hide_callback
|
|
|
|
|
|
init -1050 python:
|
|
|
|
class __FPSMeter(object):
|
|
|
|
def __init__(self):
|
|
self.last_frames = None
|
|
self.last_time = None
|
|
|
|
def __call__(self, st, at):
|
|
|
|
if self.last_time is not None:
|
|
frames = config.frames - self.last_frames
|
|
time = st - self.last_time
|
|
|
|
text = "FPS: %.1f" % (frames / time)
|
|
|
|
else:
|
|
text = "FPS: --.-"
|
|
|
|
self.last_frames = config.frames
|
|
self.last_time = st
|
|
|
|
return Text(text, xalign=1.0), .5
|
|
|
|
label _fps_meter:
|
|
|
|
python hide:
|
|
def fps_overlay():
|
|
ui.add(DynamicDisplayable(__FPSMeter()))
|
|
|
|
# We normally don't want to change this at runtime... but here
|
|
# it's okay, because we don't want to save the FPS meter anyway.
|
|
#
|
|
# Do as I say, not as I do.
|
|
config.overlay_functions.append(fps_overlay)
|
|
|
|
return
|
|
|
|
|
|
init python:
|
|
|
|
# This is a displayable that can keep track of the mouse coordinates,
|
|
# and show them to the user.
|
|
class __ImageLocationPicker(renpy.Displayable):
|
|
|
|
def __init__(self, fn, **kwargs):
|
|
super(__ImageLocationPicker, self).__init__(**kwargs)
|
|
|
|
self.child = Image(fn)
|
|
|
|
self.mouse = None
|
|
self.point1 = None
|
|
self.point2 = None
|
|
|
|
self.size = (0, 0)
|
|
|
|
self.clipboard = None
|
|
|
|
def rectangle(self):
|
|
x1, y1 = self.point1
|
|
x2, y2 = self.point2
|
|
|
|
width = self.size[0]
|
|
height = self.size[1]
|
|
|
|
x1 = min(x1, width)
|
|
x2 = min(x2, width)
|
|
y1 = min(y1, height)
|
|
y2 = min(y2, height)
|
|
|
|
minx = min(x1, x2)
|
|
miny = min(y1, y2)
|
|
maxx = max(x1, x2)
|
|
maxy = max(y1, y2)
|
|
|
|
w = int(maxx - minx)
|
|
h = int(maxy - miny)
|
|
|
|
minx = int(minx)
|
|
miny = int(miny)
|
|
|
|
|
|
return (minx, miny, w, h)
|
|
|
|
def render(self, width, height, st, at):
|
|
rv = renpy.Render(width, height)
|
|
|
|
cr = renpy.render(self.child, width, height, st, at)
|
|
rv.blit(cr, (0, 0))
|
|
|
|
text = [ ]
|
|
|
|
self.size = (cr.width, cr.height)
|
|
|
|
if self.point1 and self.point2 and not self.point1 == self.point2:
|
|
x, y, w, h = self.rectangle()
|
|
|
|
if w and h:
|
|
|
|
sr = renpy.render(Solid("#0ff4"), w, h, st, at)
|
|
rv.blit(sr, (x, y))
|
|
|
|
# text.append("Imagemap rectangle: %r" % ((minx, miny, maxx, maxy),))
|
|
text.append(__("Rectangle: %r") % ((x, y, w, h),))
|
|
|
|
if self.mouse:
|
|
mx, my = self.mouse
|
|
if mx < cr.width and my < cr.height:
|
|
text.append(__("Mouse position: %r") % (self.mouse,))
|
|
|
|
if self.clipboard:
|
|
text.append(self.clipboard)
|
|
|
|
text.append(__("Right-click or escape to quit."))
|
|
|
|
td = Text("\n".join(text), size=14, color="#fff", outlines=[ (1, "#000", 0, 0 ) ])
|
|
tr = renpy.render(td, width, height, st, at)
|
|
|
|
rv.blit(tr, (0, height - tr.height))
|
|
|
|
return rv
|
|
|
|
def event(self, ev, x, y, st):
|
|
import pygame_sdl2 as pygame
|
|
|
|
if (x < 0 or y < 0):
|
|
return None
|
|
|
|
self.mouse = (x, y)
|
|
renpy.redraw(self, 0)
|
|
|
|
if ev.type == pygame.MOUSEBUTTONDOWN and ev.button == 1:
|
|
self.point1 = (x, y)
|
|
self.point2 = (x, y)
|
|
|
|
elif ev.type == pygame.MOUSEMOTION and ev.buttons[0]:
|
|
self.point2 = (x, y)
|
|
|
|
elif ev.type == pygame.MOUSEBUTTONUP and ev.button == 1:
|
|
self.point2 = (x, y)
|
|
|
|
x, y, w, h = self.rectangle()
|
|
|
|
if w or h:
|
|
text = repr((x, y, w, h))
|
|
self.clipboard = __("Rectangle copied to clipboard.")
|
|
else:
|
|
text = repr((x, y))
|
|
self.clipboard = __("Position copied to clipboard.")
|
|
|
|
import pygame.scrap
|
|
pygame.scrap.put(pygame.scrap.SCRAP_TEXT, text)
|
|
|
|
screen _image_location_picker(image_files):
|
|
|
|
default filter = ""
|
|
|
|
frame:
|
|
style_prefix ""
|
|
|
|
has side "t c b":
|
|
spacing gui._scale(10)
|
|
|
|
vbox:
|
|
label _(u"Image Location Picker")
|
|
|
|
hbox:
|
|
text _("Type to filter: ")
|
|
input value ScreenVariableInputValue("filter")
|
|
|
|
viewport:
|
|
xfill True
|
|
yfill True
|
|
scrollbars "both"
|
|
mousewheel True
|
|
|
|
has vbox
|
|
|
|
for fn in [ i for i in image_files if filter.lower() in i.lower() ]:
|
|
|
|
textbutton "[fn!q]":
|
|
action Return(fn)
|
|
style_suffix "small_button"
|
|
|
|
hbox:
|
|
spacing gui._scale(25)
|
|
|
|
textbutton _(u"Return"):
|
|
action Return(False)
|
|
|
|
|
|
label _image_location_picker:
|
|
|
|
call _enter_game_menu
|
|
|
|
scene black
|
|
|
|
python hide:
|
|
|
|
extensions = [ ".jpg", ".jpeg", ".png", ".webp" ]
|
|
|
|
image_files = [ ]
|
|
|
|
for fn in renpy.list_files():
|
|
if fn[0] == "_":
|
|
continue
|
|
|
|
lfn = fn.lower()
|
|
|
|
for i in extensions:
|
|
if fn.endswith(i):
|
|
image_files.append(fn)
|
|
|
|
image_files.sort()
|
|
|
|
xadjustment = ui.adjustment()
|
|
yadjustment = ui.adjustment()
|
|
|
|
while True:
|
|
|
|
rv = renpy.call_screen("_image_location_picker", image_files=image_files)
|
|
|
|
if rv is False:
|
|
renpy.jump("_image_location_picker_done")
|
|
|
|
# Now, allow the user to pick the image.
|
|
|
|
ui.keymap(game_menu=ui.returns(True))
|
|
ui.add(__ImageLocationPicker(rv))
|
|
ui.interact()
|
|
|
|
renpy.jump("_image_location_picker")
|
|
|
|
label _image_location_picker_done:
|
|
return
|
|
|
|
|
|
label _filename_list:
|
|
|
|
python hide:
|
|
import os
|
|
|
|
FILES_TXT = os.path.join(renpy.config.basedir, "files.txt")
|
|
|
|
f = file(FILES_TXT, "w")
|
|
|
|
for dirname, dirs, files in os.walk(config.gamedir):
|
|
|
|
dirs.sort()
|
|
files.sort()
|
|
|
|
for fn in files:
|
|
fn = os.path.join(dirname, fn)
|
|
fn = fn[len(config.gamedir) + 1:]
|
|
print(fn.encode("utf-8", "replace"), file=f)
|
|
print(fn.encode("utf-8", "replace"))
|
|
|
|
f.close()
|
|
|
|
renpy.launch_editor([ FILES_TXT ], transient=1)
|
|
|
|
return
|
|
|
|
|
|
screen _image_load_log():
|
|
zorder 1500
|
|
|
|
default show_help = True
|
|
|
|
style_prefix ""
|
|
|
|
python:
|
|
load_log = list(renpy.get_image_load_log(5))
|
|
tex_size, tex_count = renpy.get_texture_size()
|
|
tex_size_mb = tex_size / 1024.0 / 1024.0
|
|
|
|
cache_size = renpy.display.im.cache.get_current_size(2)
|
|
cache_size_mb = cache_size * 4.0 / 1024 / 1024
|
|
cache_pct = 100.0 * cache_size / renpy.display.im.cache.cache_limit
|
|
|
|
drag:
|
|
draggable True
|
|
focus_mask None
|
|
xpos 0
|
|
ypos 0
|
|
|
|
frame:
|
|
style "empty"
|
|
background "#0004"
|
|
xpadding 5
|
|
ypadding 5
|
|
xminimum 200
|
|
|
|
has vbox
|
|
|
|
text _("Textures: [tex_count] ([tex_size_mb:.1f] MB)"):
|
|
size 14
|
|
color "#fff"
|
|
|
|
text _("Image cache: [cache_pct:.1f]% ([cache_size_mb:.1f] MB)"):
|
|
size 14
|
|
color "#fff"
|
|
|
|
if load_log:
|
|
text "\n" size 14
|
|
|
|
for when, filename, preload in load_log:
|
|
if preload:
|
|
$ color="#ccffcc"
|
|
$ prefix=__("✔ ")
|
|
else:
|
|
$ color="#ffcccc"
|
|
$ prefix=__("✘ ")
|
|
|
|
text "[prefix][filename!q]" color color size 14
|
|
|
|
if show_help:
|
|
text _("\n{color=#cfc}✔ predicted image (good){/color}\n{color=#fcc}✘ unpredicted image (bad){/color}\n{color=#fff}Drag to move.{/color}"):
|
|
size 14
|
|
|
|
timer 10.0 action SetScreenVariable("show_help", False)
|
|
|
|
init python:
|
|
if config.transparent_tile:
|
|
config.underlay.append(Frame("_transparent_tile.png", tile=True))
|
|
renpy.start_predict("_transparent_tile.png")
|
|
|
|
config.per_frame_screens.append("_image_load_log")
|
|
|
|
config.underlay.append(renpy.Keymap(
|
|
image_load_log = ToggleScreen("_image_load_log")
|
|
))
|