forked from Cavemanon/IWaniHugThatGator-Demo-Public
696 lines
20 KiB
Plaintext
696 lines
20 KiB
Plaintext
|
|
init -10 python in draw_logic:
|
|
|
|
import os
|
|
import io
|
|
import math
|
|
import hashlib
|
|
import store
|
|
import enum
|
|
import pygame_sdl2 as pygame
|
|
from os import path
|
|
|
|
SAVE_FOLDER = path.abspath(renpy.config.basedir)
|
|
if renpy.android and not renpy.config.developer:
|
|
SAVE_FOLDER = os.environ["ANDROID_PUBLIC"]
|
|
GALLERY_REL_FOLDER = "_draw"
|
|
COLOR_CIRCLE = "colors.png"
|
|
DRAW_SAVE_NAME = "Drawing"
|
|
DRAW_EXT = ".png"
|
|
|
|
ERASER_STATE = False
|
|
PENCOLOR = "#0f0f0d"
|
|
PAPER = "#ede6e6"
|
|
SHOWING_UI = True
|
|
|
|
VERSION = (1, 1, 3)
|
|
|
|
class _DrawGallery(store.Gallery):
|
|
|
|
BACKGROUND = PAPER
|
|
|
|
def __init__(self):
|
|
|
|
super(_DrawGallery, self).__init__()
|
|
self.update_pictures()
|
|
|
|
def update_pictures(self):
|
|
|
|
for pic in self._get_pictures():
|
|
|
|
name = pic.replace('/', '_')
|
|
name = name.replace(' ', '_')
|
|
name = name.replace('.', '_')
|
|
|
|
if name in self.buttons:
|
|
continue
|
|
|
|
images = []
|
|
if self.BACKGROUND:
|
|
images.append(self.BACKGROUND)
|
|
images.append(pic)
|
|
|
|
self.button(name)
|
|
self.image(*images)
|
|
|
|
@staticmethod
|
|
def _get_pictures(update=True):
|
|
|
|
if update:
|
|
renpy.loader.cleardirfiles()
|
|
|
|
_fold = path.normpath(GALLERY_REL_FOLDER)
|
|
|
|
for renpy_fn in renpy.list_files():
|
|
|
|
fn = path.normpath(renpy_fn)
|
|
_fn, ext = path.splitext(fn)
|
|
ext = ext.strip().lower()
|
|
|
|
if ext not in (".jpg", ".jpeg", ".png", ".webp"):
|
|
continue
|
|
|
|
_dir = path.dirname(fn)
|
|
while True:
|
|
if _dir == _fold:
|
|
yield renpy_fn
|
|
break
|
|
if not _dir:
|
|
break
|
|
_dir = path.dirname(_dir)
|
|
|
|
def get_buttons(self):
|
|
|
|
zoom_size = float(renpy.config.screen_width) * .15
|
|
for name, button in self.buttons.items():
|
|
|
|
disp = button.images[0].displayables[-1]
|
|
|
|
w, _h = map(float, Draw._get_size(disp))
|
|
zoom = zoom_size / w
|
|
|
|
disp = store.Transform(disp, zoom=zoom)
|
|
|
|
yield self.make_button(name, disp)
|
|
|
|
draw_gallery = _DrawGallery()
|
|
|
|
class Point(object):
|
|
|
|
def __init__(self, x, y, st, color, width):
|
|
|
|
self.__x, self.__y = map(int, (x, y))
|
|
self.__st = abs(float(st))
|
|
|
|
self.__color = renpy.color.Color(color)
|
|
self.__width = abs(int(width))
|
|
|
|
@property
|
|
def x(self):
|
|
return self.__x
|
|
|
|
@property
|
|
def y(self):
|
|
return self.__y
|
|
|
|
@property
|
|
def st(self):
|
|
return self.__st
|
|
|
|
@property
|
|
def color(self):
|
|
return self.__color
|
|
|
|
@property
|
|
def width(self):
|
|
return self.__width
|
|
|
|
class ActionRequest(enum.Flag):
|
|
|
|
NOTIFY = enum.auto()
|
|
SAVE = enum.auto()
|
|
ADD_TO_GALLERY = enum.auto()
|
|
SKIP = enum.auto()
|
|
|
|
|
|
class Draw(renpy.Displayable):
|
|
|
|
DRAW_BUTTON = 1
|
|
|
|
|
|
def __init__(self, background, reference=None, **properties):
|
|
|
|
super(Draw, self).__init__(**properties)
|
|
|
|
self.__background = self._get_displayable(background)
|
|
self.__is_pressed = False
|
|
|
|
self.__curves = []
|
|
self.__active_curve = None
|
|
self.__delete_log = []
|
|
|
|
self.__color = renpy.color.Color("#0f0f0d")
|
|
self.__width = 10
|
|
|
|
if reference is not None:
|
|
self.__reference = self._get_displayable(reference)
|
|
else:
|
|
self.__reference = None
|
|
self.__show_reference = False
|
|
|
|
self.__size = None
|
|
self.__action_request = False
|
|
self.__end_interact_request = False
|
|
|
|
@classmethod
|
|
def main(
|
|
cls,
|
|
background=None,
|
|
reference=None,
|
|
start_width=None,
|
|
start_color=None,
|
|
**transform_prop
|
|
):
|
|
|
|
#renpy.notify("Use Mouse left click to draw.")
|
|
if background is None:
|
|
background = "#ede6e6"
|
|
|
|
|
|
if transform_prop:
|
|
background = store.Transform(background, **transform_prop)
|
|
|
|
draw_object = cls(background, reference)
|
|
if start_width:
|
|
draw_object.set_width(start_width)
|
|
if start_color:
|
|
draw_object.set_color(start_color)
|
|
|
|
_screen_name = "_draw_screen"
|
|
|
|
renpy.mode("screen")
|
|
renpy.show_screen(_screen_name, draw_object, _transient=True)
|
|
roll_forward = renpy.roll_forward_info()
|
|
|
|
try:
|
|
rv = renpy.ui.interact(
|
|
mouse="screen",
|
|
type="screen",
|
|
suppress_overlay=True,
|
|
suppress_window=True,
|
|
roll_forward=roll_forward
|
|
)
|
|
except (renpy.game.JumpException, renpy.game.CallException) as ex:
|
|
rv = ex
|
|
|
|
renpy.checkpoint(rv)
|
|
|
|
_special_ex = (renpy.game.JumpException, renpy.game.CallException)
|
|
if isinstance(rv, _special_ex):
|
|
raise rv
|
|
|
|
return rv
|
|
|
|
def _disable(self):
|
|
self.__end_interact_request = True
|
|
renpy.redraw(self, .0)
|
|
|
|
@staticmethod
|
|
def _get_displayable(data):
|
|
result = renpy.displayable(data)
|
|
if not isinstance(result, renpy.display.core.Displayable):
|
|
raise ValueError("{0} isn't a displayable.".format(data))
|
|
return result
|
|
|
|
@classmethod
|
|
def _get_size(cls, data):
|
|
disp = cls._get_displayable(data)
|
|
rend = renpy.display.render.render_for_size(
|
|
disp,
|
|
renpy.config.screen_width,
|
|
renpy.config.screen_height,
|
|
.0,
|
|
.0
|
|
)
|
|
return tuple(map(int, rend.get_size()))
|
|
|
|
@staticmethod
|
|
def _save_canvas(canvas, folder=None):
|
|
|
|
if folder is None:
|
|
folder = SAVE_FOLDER
|
|
|
|
w, h = tuple(map(int, canvas.get_surface().get_size()))
|
|
canvas_surf = pygame.Surface((w,h), pygame.SRCALPHA, 32)
|
|
|
|
canvas_surf.fill(pygame.Color(237, 230, 230, 255))
|
|
|
|
canvas_drawing = canvas.get_surface()
|
|
|
|
canvas_surf.blit(canvas_drawing, (0, 0))
|
|
|
|
_counter = 1
|
|
|
|
fn = path.abspath(
|
|
path.join(
|
|
folder,
|
|
"{0}{1}{2}".format(DRAW_SAVE_NAME, _counter, DRAW_EXT)
|
|
))
|
|
|
|
if path.isfile(fn) == True:
|
|
os.remove(fn)
|
|
|
|
_directory = path.dirname(fn)
|
|
if not path.isdir(_directory):
|
|
os.makedirs(_directory)
|
|
|
|
pygame.image.save(canvas_surf, renpy.fsencode(fn))
|
|
return fn
|
|
|
|
@classmethod
|
|
def add_canvas_in_gallery(cls, canvas):
|
|
fold = path.abspath(
|
|
path.join(renpy.config.gamedir, GALLERY_REL_FOLDER)
|
|
)
|
|
fn = cls._save_canvas(canvas, fold)
|
|
draw_gallery.update_pictures()
|
|
return fn
|
|
|
|
@classmethod
|
|
def get_canvas_as_disp(cls, canvas):
|
|
|
|
fn = cls._save_canvas(canvas)
|
|
with open(fn, "rb") as _file:
|
|
_data = _file.read()
|
|
# os.remove(fn)
|
|
|
|
_hash = hashlib.sha512(_data)
|
|
return renpy.display.im.Data(
|
|
_data,
|
|
"{0}{1}".format(_hash.hexdigest(), DRAW_EXT)
|
|
)
|
|
|
|
@property
|
|
def reference(self):
|
|
return self.__reference
|
|
|
|
@property
|
|
def reference_switcher(self):
|
|
return self.__show_reference
|
|
|
|
@reference_switcher.setter
|
|
def reference_switcher(self, new_value):
|
|
self.__show_reference = bool(new_value)
|
|
renpy.redraw(self, .0)
|
|
|
|
@property
|
|
def width(self):
|
|
return self.__width
|
|
|
|
@property
|
|
def eraser_state(self):
|
|
return self.__eraser_state
|
|
|
|
@width.setter
|
|
def width(self, new_width):
|
|
self.__width = int(new_width)
|
|
|
|
def draw_all(self, canvas):
|
|
|
|
for curve in self.__curves:
|
|
|
|
if not curve:
|
|
continue
|
|
|
|
elif len(curve) == 1:
|
|
point = curve[0]
|
|
canvas.circle(
|
|
point.color,
|
|
(point.x, point.y),
|
|
(point.width // 2)
|
|
)
|
|
|
|
else:
|
|
prev = None
|
|
for point in curve:
|
|
if prev:
|
|
canvas.line(
|
|
prev.color,
|
|
(prev.x, prev.y),
|
|
(point.x, point.y),
|
|
prev.width
|
|
)
|
|
prev = point
|
|
|
|
def add_point(self, x, y, st):
|
|
point = Point(x, y, st, self.__color, self.__width)
|
|
if self.__active_curve is None:
|
|
self.__active_curve = []
|
|
self.__curves.append(self.__active_curve)
|
|
self.__delete_log.clear()
|
|
self.__active_curve.append(point)
|
|
renpy.redraw(self, .0)
|
|
|
|
def add_in_gallery(self, notify=False):
|
|
|
|
rq = ActionRequest.ADD_TO_GALLERY
|
|
if notify:
|
|
rq |= ActionRequest.NOTIFY
|
|
|
|
self.__action_request = rq
|
|
renpy.redraw(self, .0)
|
|
|
|
def skip(self):
|
|
rq = ActionRequest.SKIP
|
|
self.__action_request = rq
|
|
renpy.redraw(self, .0)
|
|
|
|
def save(self, notify=False):
|
|
|
|
rq = ActionRequest.SAVE
|
|
if notify:
|
|
rq |= ActionRequest.NOTIFY
|
|
|
|
renpy.store.persistent.drawn = 1
|
|
self.__action_request = rq
|
|
renpy.redraw(self, .0)
|
|
|
|
def back(self):
|
|
self.__is_pressed = False
|
|
self.__active_curve = None
|
|
if self.__curves:
|
|
curve = self.__curves.pop(-1)
|
|
self.__delete_log.append(curve)
|
|
renpy.redraw(self, .0)
|
|
|
|
def forward(self):
|
|
self.__is_pressed = False
|
|
self.__active_curve = None
|
|
if self.__delete_log:
|
|
curve = self.__delete_log.pop(-1)
|
|
self.__curves.append(curve)
|
|
renpy.redraw(self, .0)
|
|
|
|
def clear_all(self):
|
|
self.__is_pressed = False
|
|
self.__active_curve = None
|
|
self.__curves.clear()
|
|
self.__delete_log.clear()
|
|
renpy.redraw(self, .0)
|
|
|
|
def set_color(self, color):
|
|
self.__color = renpy.color.Color(color)
|
|
|
|
def set_width(self, width):
|
|
self.width = width
|
|
|
|
def set_eraser_state(self, eraser_state):
|
|
self.__eraser_state = eraser_state
|
|
|
|
def visit(self):
|
|
return [self.__background]
|
|
|
|
def event(self, ev, x, y, st):
|
|
|
|
if not self.__size:
|
|
return
|
|
|
|
w, h = self.__size
|
|
in_area = False
|
|
if (0 <= x < w) and (0 <= y < h):
|
|
in_area = True
|
|
|
|
if in_area and (ev.type == pygame.MOUSEMOTION):
|
|
if self.__is_pressed:
|
|
self.add_point(x, y, st)
|
|
|
|
elif in_area and (ev.type == pygame.MOUSEBUTTONDOWN):
|
|
if ev.button == self.DRAW_BUTTON:
|
|
self.__is_pressed = True
|
|
if ERASER_STATE == False:
|
|
self.set_color(PENCOLOR)
|
|
self.set_width(10)
|
|
else:
|
|
self.set_color(PAPER)
|
|
self.set_width(40)
|
|
self.add_point(x, y, st)
|
|
raise renpy.IgnoreEvent()
|
|
|
|
elif ev.type == pygame.MOUSEBUTTONUP:
|
|
if ev.button == self.DRAW_BUTTON:
|
|
self.__is_pressed = False
|
|
self.__active_curve = None
|
|
|
|
elif ev.type == pygame.KEYDOWN and ev.key == pygame.K_z and pygame.key.get_mods() & pygame.KMOD_CTRL:
|
|
self.back()
|
|
|
|
|
|
def per_interact(self):
|
|
self.__is_pressed = False
|
|
self.__active_curve = None
|
|
renpy.redraw(self, .0)
|
|
|
|
def render(self, *rend_args):
|
|
|
|
back = renpy.render(self.__background, *rend_args)
|
|
w, h = self.__size = tuple(map(int, back.get_size()))
|
|
|
|
result = renpy.Render(w, h)
|
|
result.blit(back, (0, 0))
|
|
|
|
if self.reference and self.reference_switcher:
|
|
rend = renpy.render(self.reference, *rend_args)
|
|
x = (float(result.width) * .5) - (float(rend.width) * .5)
|
|
y = (float(result.height) * .5) - (float(rend.height) * .5)
|
|
x, y = map(int, (x, y))
|
|
result.blit(rend, (x, y))
|
|
|
|
canvas = result.canvas()
|
|
self.draw_all(canvas)
|
|
|
|
if self.__end_interact_request:
|
|
self.__end_interact_request = False
|
|
disp = self.get_canvas_as_disp(canvas)
|
|
renpy.end_interaction(disp)
|
|
|
|
if self.__action_request:
|
|
|
|
if self.__action_request & ActionRequest.SKIP:
|
|
self._disable()
|
|
if self.__action_request & ActionRequest.SAVE:
|
|
fn = self._save_canvas(canvas)
|
|
if self.__action_request & ActionRequest.NOTIFY:
|
|
renpy.notify(_("Saved draw as \"{0}\"").format(fn))
|
|
self._disable()
|
|
|
|
if self.__action_request & ActionRequest.ADD_TO_GALLERY:
|
|
self.add_canvas_in_gallery(canvas)
|
|
if self.__action_request & ActionRequest.NOTIFY:
|
|
renpy.notify(_("A draw added in gallery."))
|
|
|
|
self.__action_request = None
|
|
|
|
return result
|
|
|
|
renpy.config.allow_underfull_grids = True
|
|
|
|
init 150 python in draw_logic:
|
|
try:
|
|
_circle = Draw._get_displayable(COLOR_CIRCLE)
|
|
_circle.load()
|
|
except Exception:
|
|
COLOR_CIRCLE = "_placeholder/girl.png"
|
|
else:
|
|
del(_circle)
|
|
|
|
|
|
transform tf_draw_moveindown(p=0.0, scale=0.8):
|
|
subpixel True alpha 0.0
|
|
zoom scale
|
|
pause p
|
|
ypos -50
|
|
parallel:
|
|
easein_back 0.5 ypos 0
|
|
parallel:
|
|
linear 0.25 alpha 1
|
|
|
|
transform tf_draw_moveoutup(p=0.0, scale=0.8):
|
|
subpixel True alpha 1
|
|
zoom scale
|
|
pause p
|
|
ypos 0
|
|
parallel:
|
|
easeout_back 0.5 ypos -50
|
|
parallel:
|
|
linear 0.5 alpha 0.0
|
|
|
|
transform tf_draw_moveinright(p=0.0, scale=0.8):
|
|
subpixel True alpha 0.0
|
|
zoom scale
|
|
pause p
|
|
xpos -50
|
|
parallel:
|
|
easein_back 0.5 xpos 0
|
|
parallel:
|
|
linear 0.25 alpha 1
|
|
|
|
transform tf_draw_moveoutleft(p=0.0, scale=0.8):
|
|
subpixel True alpha 1.0
|
|
zoom scale
|
|
pause p
|
|
xpos 0
|
|
parallel:
|
|
easeout_back 0.5 xpos -50
|
|
parallel:
|
|
linear 0.5 alpha 0
|
|
|
|
transform tf_draw_fadetext(p=0):
|
|
xalign 0.5 yalign 0.1
|
|
alpha 0.0
|
|
|
|
pause 0.5+p
|
|
linear 0.5 alpha 1.0
|
|
pause 3
|
|
linear 0.5 alpha 0.0
|
|
|
|
screen _draw_screen(draw_object):
|
|
modal True
|
|
|
|
key "h" action SetVariable("draw_logic.SHOWING_UI", not draw_logic.SHOWING_UI)
|
|
|
|
fixed:
|
|
xfill True
|
|
yfill True
|
|
add draw_object:
|
|
align (.5, .5)
|
|
fixed:
|
|
style_prefix "draw_menu"
|
|
# style "draw_tools_frame"
|
|
|
|
hbox:
|
|
ypos 10
|
|
xpos 10
|
|
spacing 15
|
|
|
|
use draw_tool_button("gui/button/drawing/pencil.png",False,draw_logic.SHOWING_UI,0.0)
|
|
use draw_tool_button("gui/button/drawing/eraser.png",True,draw_logic.SHOWING_UI,0.2)
|
|
|
|
# frame:
|
|
# style "tooltip_frame"
|
|
# label _("Mouse left click \n to draw.\n")
|
|
vbox:
|
|
yalign 1.0
|
|
xalign 0.0
|
|
|
|
|
|
use draw_menu_buttons("gui/button/menubuttons/menu_button.png", draw_logic.SHOWING_UI,
|
|
[
|
|
[ _("Skip"), Function(draw_object.skip), 0.0, persistent.drawn == 1],
|
|
[ _("Undo"), Function(draw_object.back), 0.1, True],
|
|
[ _("Clear All"), Function(draw_object.clear_all), 0.2, True],
|
|
[ _("Done"), Function(draw_object.save, False), 0.3, True]
|
|
] )
|
|
|
|
if renpy.android:
|
|
use draw_menu_button("gui/button/menubuttons/menu_button.png",_("Toggle UI"), SetVariable("draw_logic.SHOWING_UI", not draw_logic.SHOWING_UI), 0.4, True)
|
|
|
|
|
|
#if draw_object.reference:
|
|
# textbutton (_("Hide reference") if draw_object.reference_switcher else _("Show reference")):
|
|
# action ToggleField(draw_object, "reference_switcher", True, False)
|
|
if persistent.drawn == 0:
|
|
if renpy.android:
|
|
text _("Use your finger to draw.") at tf_draw_fadetext:
|
|
color "#000000"
|
|
else:
|
|
text _("Use left click to draw.") at tf_draw_fadetext:
|
|
color "#000000"
|
|
text _("Press 'H' to hide the drawing UI.") at tf_draw_fadetext(4):
|
|
color "#000000"
|
|
|
|
|
|
screen draw_tool_button(icon,is_eraser,showing, delay):
|
|
|
|
if renpy.android:
|
|
$ scale = 1.0
|
|
else:
|
|
$ scale = 0.8
|
|
|
|
if showing:
|
|
$ animation = tf_draw_moveindown(delay,scale)
|
|
else:
|
|
$ animation = tf_draw_moveoutup(delay,scale)
|
|
|
|
# I can't believe there's not a less hacky way to stack images in renpy
|
|
vbox:
|
|
spacing -1000
|
|
imagebutton at animation:
|
|
sensitive showing
|
|
idle "gui/button/drawing/drawbutton_idle.png"
|
|
hover "gui/button/drawing/drawbutton_idle.png"
|
|
selected_idle "gui/button/drawing/drawbutton_pressed.png"
|
|
selected_hover "gui/button/drawing/drawbutton_pressed.png"
|
|
activate_sound "audio/ui/snd_ui_click.wav"
|
|
action SetVariable("draw_logic.ERASER_STATE", is_eraser)
|
|
add icon at animation
|
|
|
|
|
|
screen draw_menu_buttons(filename, showing, label_functions):
|
|
for l_f in label_functions:
|
|
if l_f[3]:
|
|
use draw_menu_button(filename, l_f[0], l_f[1], l_f[2], showing)
|
|
|
|
|
|
screen draw_menu_button(filename, label, function, delay, showing):
|
|
|
|
if renpy.android:
|
|
$ scale = 1.0
|
|
else:
|
|
$ scale = 0.8
|
|
|
|
if showing:
|
|
$ animation = tf_draw_moveinright(delay,scale)
|
|
else:
|
|
$ animation = tf_draw_moveoutleft(delay,scale)
|
|
|
|
button at animation:
|
|
sensitive showing
|
|
xmaximum 250
|
|
ymaximum 100
|
|
action function
|
|
if 'Done' in label:
|
|
activate_sound "audio/ui/snd_ui_click.wav"
|
|
else:
|
|
activate_sound "audio/ui/snd_ui_back.wav"
|
|
fixed:
|
|
add filename xalign 0.5 yalign 0.5 zoom 0.8 xanchor 0 xcenter 0.5 ycenter 0.5
|
|
text label xalign 0.5 yalign 0.5 xanchor 0.5 size 30
|
|
|
|
|
|
#change this for better bg, for some reason renpy cannot take a defined image here, only a file path wtf
|
|
#style draw_menu_frame is default:
|
|
# background "gui/main_menu.png"
|
|
|
|
|
|
style draw_menu_text is gui_text
|
|
style draw_menu_title is draw_menu_text
|
|
style draw_menu_version is draw_menu_text
|
|
|
|
|
|
style draw_menu_text:
|
|
properties gui.text_properties("main_menu") #, accent=True)
|
|
color gui.main_menu_color
|
|
hover_color gui.hover_color
|
|
size gui.main_menu_text_size
|
|
top_padding 170
|
|
|
|
style draw_menu_title:
|
|
properties gui.text_properties("title")
|
|
|
|
|
|
style draw_menu_ex is draw_menu
|
|
style draw_menu_ex_vbox is draw_menu_vbox
|
|
style draw_menu_ex_text is draw_menu_text
|
|
style draw_menu_ex_frame is draw_menu_frame
|
|
|