Files
IWaniHugThatGator-Demo-Public/game/src/draw.rpy
2023-05-27 05:11:06 -03:00

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