Adding SMS system for internet messaging dialog and moving some files to other folders
|
@ -1,5 +1,5 @@
|
||||||
game/cache
|
./cache/
|
||||||
game/saves
|
./saves/
|
||||||
.vscode/
|
.vscode/
|
||||||
*.rpyc
|
*.rpyc
|
||||||
*.txt
|
*.txt
|
|
@ -4,5 +4,126 @@
|
||||||
"**/*.rpa": true,
|
"**/*.rpa": true,
|
||||||
"**/*.rpymc": true,
|
"**/*.rpymc": true,
|
||||||
"**/cache/": true
|
"**/cache/": true
|
||||||
|
},
|
||||||
|
"editor.tokenColorCustomizations": {
|
||||||
|
"textMateRules": [
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.plain",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.i",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "italic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.b",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "bold"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"renpy.meta.u",
|
||||||
|
"renpy.meta.a"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "underline"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.s",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "strikethrough"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.i renpy.meta.b",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "italic bold"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.i renpy.meta.u",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "italic underline"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.i renpy.meta.s",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "italic strikethrough"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.b renpy.meta.u",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "bold underline"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.b renpy.meta.s",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "bold strikethrough"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.u renpy.meta.s",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "underline strikethrough"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.i renpy.meta.b renpy.meta.u",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "italic bold underline"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.i renpy.meta.b renpy.meta.s",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "italic bold strikethrough"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.i renpy.meta.u renpy.meta.s",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "italic underline strikethrough"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.b renpy.meta.u renpy.meta.s",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "bold underline strikethrough"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.i renpy.meta.b renpy.meta.u renpy.meta.s",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "italic bold underline strikethrough"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.color.text",
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#ffffff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.color.#0f0",
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#0f0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "renpy.meta.color.#f00",
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#f00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,950 @@
|
||||||
|
# чтобы str() не вылетала на линуксе
|
||||||
|
define mystr = eval("lambda i: '%s' % i")
|
||||||
|
|
||||||
|
init -2000 python:
|
||||||
|
# деление с переводом в целочисленное для нового ренпая
|
||||||
|
def d2(x, d=2):
|
||||||
|
return int(x / d)
|
||||||
|
|
||||||
|
# префиксы спрайтов, теги в которых нужно делить не пробелами, а "_"
|
||||||
|
layered_prefixes = []
|
||||||
|
|
||||||
|
init:
|
||||||
|
# масштабирование
|
||||||
|
transform zoom(zoom=1):
|
||||||
|
zoom zoom
|
||||||
|
|
||||||
|
transform xzoom(zoom=1):
|
||||||
|
xzoom zoom
|
||||||
|
|
||||||
|
transform yzoom(zoom=1):
|
||||||
|
yzoom zoom
|
||||||
|
|
||||||
|
# прозрачность
|
||||||
|
transform alpha(alpha=1.):
|
||||||
|
alpha alpha
|
||||||
|
|
||||||
|
# размытость
|
||||||
|
transform blur(blur=4):
|
||||||
|
blur blur
|
||||||
|
|
||||||
|
# яркость
|
||||||
|
transform brightness(brightness=.25):
|
||||||
|
matrixcolor BrightnessMatrix(brightness)
|
||||||
|
|
||||||
|
# контраст
|
||||||
|
transform contrast(contrast=1.25):
|
||||||
|
matrixcolor ContrastMatrix(contrast)
|
||||||
|
|
||||||
|
# насыщенность
|
||||||
|
transform saturation(saturation=1.):
|
||||||
|
matrixcolor SaturationMatrix(saturation)
|
||||||
|
|
||||||
|
# подкрашивание картинок
|
||||||
|
transform color(color="#000"):
|
||||||
|
matrixcolor TintMatrix(color)
|
||||||
|
|
||||||
|
# спрайт цвета color1 переливается в color2
|
||||||
|
transform color2(color1="#fff", color2="#def", t=2):
|
||||||
|
matrixcolor TintMatrix(color1)
|
||||||
|
ease_quad t*.5 matrixcolor TintMatrix(color2)
|
||||||
|
ease_quad t*.5 matrixcolor TintMatrix(color1)
|
||||||
|
repeat
|
||||||
|
|
||||||
|
# силуэт цвета color
|
||||||
|
transform paint(color="#fff"):
|
||||||
|
matrixcolor TintMatrix(color) * InvertMatrix(1.) * TintMatrix("#000")
|
||||||
|
|
||||||
|
# силуэт цвета color1 переливается в color2
|
||||||
|
transform paint2(color1="#fff", color2="#def", t=2):
|
||||||
|
matrixcolor TintMatrix(color1) * InvertMatrix(1.) * TintMatrix("#000")
|
||||||
|
ease_quad t*.5 matrixcolor TintMatrix(color2) * InvertMatrix(1.) * TintMatrix("#000")
|
||||||
|
ease_quad t*.5 matrixcolor TintMatrix(color1) * InvertMatrix(1.) * TintMatrix("#000")
|
||||||
|
repeat
|
||||||
|
|
||||||
|
# относительное положение
|
||||||
|
transform xalign(xalign=.5):
|
||||||
|
xalign xalign
|
||||||
|
transform yalign(yalign=1.):
|
||||||
|
yalign yalign
|
||||||
|
transform align(xalign=.5, yalign=1.):
|
||||||
|
align (xalign, yalign)
|
||||||
|
|
||||||
|
# положение
|
||||||
|
transform xpos(xpos=.5):
|
||||||
|
xpos xpos
|
||||||
|
transform ypos(ypos=.0):
|
||||||
|
ypos ypos
|
||||||
|
transform pos(xpos=.5, ypos=.0):
|
||||||
|
pos (xpos, ypos)
|
||||||
|
|
||||||
|
# якорь
|
||||||
|
transform xanchor(xanchor=.5):
|
||||||
|
xanchor xanchor
|
||||||
|
transform yanchor(yanchor=1.):
|
||||||
|
yanchor yanchor
|
||||||
|
transform anchor(xanchor=.5, yanchor=1.):
|
||||||
|
anchor (xanchor, yanchor)
|
||||||
|
|
||||||
|
# отзеркаливание
|
||||||
|
transform hflip(xz=-1.):
|
||||||
|
xzoom xz
|
||||||
|
transform vflip(yz=-1.):
|
||||||
|
yzoom yz
|
||||||
|
|
||||||
|
# поворот против часовой
|
||||||
|
transform rotate(a=45, rotate_pad=True):
|
||||||
|
rotate_pad rotate_pad
|
||||||
|
rotate a
|
||||||
|
|
||||||
|
# повороты с перспективой
|
||||||
|
## вверх-вниз
|
||||||
|
transform turnx(x=45):
|
||||||
|
perspective True
|
||||||
|
matrixtransform RotateMatrix(x, 0, 0)
|
||||||
|
|
||||||
|
## влево-вправо
|
||||||
|
transform turny(y=45):
|
||||||
|
perspective True
|
||||||
|
matrixtransform RotateMatrix(0, y, 0)
|
||||||
|
|
||||||
|
## против часовой-по часовой
|
||||||
|
transform turnz(z=45):
|
||||||
|
perspective True
|
||||||
|
matrixtransform RotateMatrix(0, 0, z)
|
||||||
|
|
||||||
|
## по всем направлениям
|
||||||
|
transform turn(x=0, y=45, z=0):
|
||||||
|
perspective True
|
||||||
|
matrixtransform RotateMatrix(x, y, z)
|
||||||
|
|
||||||
|
# вырезание
|
||||||
|
transform crop(x=0, y=0, w=1., h=1.):
|
||||||
|
crop(x, y, w, h)
|
||||||
|
|
||||||
|
# подпрыгивание персонажа
|
||||||
|
transform leap(dt=.4, dyz=0.01, dxz=0.005):
|
||||||
|
yzoom 1.
|
||||||
|
easein dt*0.35 yzoom 1.+dyz xzoom 1.-dxz
|
||||||
|
easeout dt*0.35 yzoom 1. xzoom 1.
|
||||||
|
easein dt*0.15 yzoom 1.-dyz xzoom 1.+dxz
|
||||||
|
easeout dt*0.15 yzoom 1. xzoom 1.
|
||||||
|
|
||||||
|
# слева, но не у самого края
|
||||||
|
transform left2(xa=.35):
|
||||||
|
anchor (.5, 1.)
|
||||||
|
align(xa, 1.)
|
||||||
|
|
||||||
|
# справа, но не у самого края
|
||||||
|
transform right2(xa=.65):
|
||||||
|
anchor (.5, 1.)
|
||||||
|
align(xa, 1.)
|
||||||
|
|
||||||
|
# слева, за краем
|
||||||
|
transform left0():
|
||||||
|
anchor (1., 1.)
|
||||||
|
pos (.0, 1.)
|
||||||
|
|
||||||
|
# справа, за краем
|
||||||
|
transform right0():
|
||||||
|
anchor (1., 1.)
|
||||||
|
pos (1., 1.)
|
||||||
|
|
||||||
|
# сиськотряс
|
||||||
|
transform boobs(t=2):
|
||||||
|
yanchor 0 yzoom 1
|
||||||
|
easeout (t*.075) yzoom 1.05
|
||||||
|
easein (t*.1) yzoom .95
|
||||||
|
easeout (t*.125) yzoom 1.025
|
||||||
|
easein (t*.125) yzoom .975
|
||||||
|
easeout (t*.125) yzoom 1.01
|
||||||
|
easein (t*.15) yzoom .99
|
||||||
|
easeout (t*.15) yzoom 1.005
|
||||||
|
easein (t*.15) yzoom 1.
|
||||||
|
|
||||||
|
init -2 python:
|
||||||
|
# единоразовый dismiss
|
||||||
|
def skip_once():
|
||||||
|
renpy.end_interaction(True)
|
||||||
|
SkipOnce = renpy.curry(skip_once)
|
||||||
|
|
||||||
|
# остановить перемотку
|
||||||
|
def skip_stop():
|
||||||
|
renpy.config.skipping = None
|
||||||
|
SkipStop = renpy.curry(skip_stop)
|
||||||
|
|
||||||
|
# нужно, чтобы включить/отключить dismiss
|
||||||
|
can_dismiss = True
|
||||||
|
|
||||||
|
def dismiss_block():
|
||||||
|
global can_dismiss
|
||||||
|
return can_dismiss
|
||||||
|
|
||||||
|
# включить dismiss
|
||||||
|
def dismiss_on():
|
||||||
|
store.can_dismiss = True
|
||||||
|
DismissOn = renpy.curry(dismiss_on)
|
||||||
|
|
||||||
|
# отключить dismiss
|
||||||
|
def dismiss_off():
|
||||||
|
if config.say_allow_dismiss is None:
|
||||||
|
config.say_allow_dismiss = dismiss_block
|
||||||
|
store.can_dismiss = False
|
||||||
|
DismissOff = renpy.curry(dismiss_off)
|
||||||
|
|
||||||
|
# для отладки
|
||||||
|
def log(*args):
|
||||||
|
for i in args:
|
||||||
|
print(str(i))
|
||||||
|
Log = renpy.curry(log)
|
||||||
|
|
||||||
|
# эффект вспышки нужного цвета для смены фонов
|
||||||
|
def flash(color="#fff"):
|
||||||
|
return Fade(.25, 0, .75, color=color)
|
||||||
|
|
||||||
|
# показан ли экран (или один из экранов)
|
||||||
|
def has_screen(*args):
|
||||||
|
args = make_list(args)
|
||||||
|
for i in args:
|
||||||
|
if renpy.get_screen(i):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
# директории для хранение звуков и музыки
|
||||||
|
audio_dir = config.play_channel
|
||||||
|
music_dir = "music"
|
||||||
|
|
||||||
|
# жёсткая пауза
|
||||||
|
def pause(t=1, hard=True):
|
||||||
|
renpy.pause(t, hard=hard)
|
||||||
|
|
||||||
|
# получить скриншот заданного размера, по умолчанию весь экран
|
||||||
|
def shot(w=config.screen_width, h=config.screen_height):
|
||||||
|
renpy.take_screenshot((w, h))
|
||||||
|
return FileCurrentScreenshot()
|
||||||
|
|
||||||
|
# сканируем папку музыки, на выходе - список мелодий без указанного расширения и папки
|
||||||
|
def get_music_list(folder=music_dir, ext="ogg"):
|
||||||
|
res = []
|
||||||
|
list = renpy.list_files()
|
||||||
|
for i in list:
|
||||||
|
if i.startswith(folder):
|
||||||
|
s = i[(len(folder) + 1):]
|
||||||
|
if s.endswith("." + ext):
|
||||||
|
res.append(s[:(-len(ext) - 1)])
|
||||||
|
return res
|
||||||
|
|
||||||
|
# сканируем папку, на выходе - список файлов нужного расширения
|
||||||
|
# по умолчанию расширения убираются
|
||||||
|
def get_file_list(folder="", ext="", hideext=True):
|
||||||
|
res = []
|
||||||
|
list = renpy.list_files()
|
||||||
|
for i in list:
|
||||||
|
if i.startswith(folder) or (not folder):
|
||||||
|
if folder:
|
||||||
|
s = i[(len(folder) + 1):]
|
||||||
|
else:
|
||||||
|
s = i
|
||||||
|
if ext:
|
||||||
|
if s.endswith("." + ext):
|
||||||
|
if hideext:
|
||||||
|
s = s[:(-len(ext) - 1)]
|
||||||
|
res.append(s)
|
||||||
|
else:
|
||||||
|
res.append(s)
|
||||||
|
if len(res) > 1:
|
||||||
|
# сортировка без учета регистра
|
||||||
|
res = sorted(res, key=lambda s: s.lower())
|
||||||
|
return res
|
||||||
|
|
||||||
|
# окно игры в центре экрана (вызывается из init)
|
||||||
|
def window_center():
|
||||||
|
import os
|
||||||
|
os.environ['SDL_VIDEO_CENTERED'] = '1'
|
||||||
|
|
||||||
|
# автоматическое объявление изображений (вызывается из init)
|
||||||
|
def images_auto(folders=["images"]):
|
||||||
|
config.automatic_images_minimum_components = 1
|
||||||
|
config.automatic_images = [' ', '_', '/']
|
||||||
|
config.automatic_images_strip = folders
|
||||||
|
|
||||||
|
# остановить перемотку
|
||||||
|
def stop_skip():
|
||||||
|
renpy.config.skipping = None
|
||||||
|
|
||||||
|
# строку в displayable
|
||||||
|
def img2disp(displayable):
|
||||||
|
if isinstance(displayable, (str, unicode)):
|
||||||
|
return renpy.displayable(displayable)
|
||||||
|
return displayable
|
||||||
|
|
||||||
|
# узнать текущие размеры изображения типа displayable
|
||||||
|
# например, после масштабирования и других операций
|
||||||
|
# не работает в разделе init
|
||||||
|
def get_size(displayable, w=config.screen_width, h=config.screen_height, st=0, at=0):
|
||||||
|
w, h = renpy.render(img2disp(displayable), w, h, st, at).get_size()
|
||||||
|
return int(w), int(h)
|
||||||
|
def get_width(displayable):
|
||||||
|
return get_size(displayable)[0]
|
||||||
|
def get_height(displayable):
|
||||||
|
return get_size(displayable)[1]
|
||||||
|
|
||||||
|
# если это не список, то сделать единственным элементом списка
|
||||||
|
def make_list(param):
|
||||||
|
if param is None:
|
||||||
|
return None
|
||||||
|
if not isinstance(param, list):
|
||||||
|
param = [param]
|
||||||
|
return param
|
||||||
|
|
||||||
|
# автоматическое объявление анимации
|
||||||
|
# описание функции Ani:
|
||||||
|
# автоматическое объявление картинки с анимацией,
|
||||||
|
# например есть кадры "images/neko%s.png",
|
||||||
|
# где %s - числа от 1 до 5, тогда объявляем анимацию так:
|
||||||
|
# image neko = Ani("neko", 5, 0.5, reverse = False)
|
||||||
|
# где:
|
||||||
|
# img_name - имя файла без номера (например, "neko")
|
||||||
|
# frames - количество кадров
|
||||||
|
# delay - пауза между кадрами в секундах
|
||||||
|
# если delay это кортеж, например (1, 2), то скорость будет меняться от 1 до 2 секунд
|
||||||
|
# loop - зациклить анимацию (по умолчанию включено)
|
||||||
|
# reverse - нужно ли проигрывание анимации в обратную сторону
|
||||||
|
# effect - эффект для смены кадров
|
||||||
|
# start - с какой цифры начинать отсчет кадров
|
||||||
|
# ext - расширение, если оно отлично от Null, то работаем с файлами,
|
||||||
|
# при ext=Null - с displayable (уже объявленными или даже измененными изображениями)
|
||||||
|
# так же можно добавлять любые стандартные для изображений параметры, типа масштабирования или прозрачности:
|
||||||
|
# image neko = Ani("neko", 5, 0.5, zoom=2.0, alpha=0.75)
|
||||||
|
def Ani(img_name, frames, delay=.1, loop=True, reverse=False, effect=Dissolve(.1, alpha=True), start=1, ext=None, **properties):
|
||||||
|
args = []
|
||||||
|
# если пауза переменная, то вычисляем ее шаг
|
||||||
|
if isinstance(delay, tuple):
|
||||||
|
d0 = delay[0]
|
||||||
|
d1 = delay[1]
|
||||||
|
f = (frames - 1)
|
||||||
|
if f <= 0:
|
||||||
|
dp = 0
|
||||||
|
else:
|
||||||
|
dp = (d1 - d0) * 1. / f
|
||||||
|
delay = d0
|
||||||
|
else:
|
||||||
|
dp = 0
|
||||||
|
# перебираем все кадры анимации
|
||||||
|
for i in range(start, start + frames):
|
||||||
|
if ext:
|
||||||
|
img = renpy.display.im.image(img_name + str(i) + "." + ext)
|
||||||
|
else:
|
||||||
|
img = img2disp(img_name + str(i))
|
||||||
|
img = Transform(img, **properties)
|
||||||
|
args.append(img)
|
||||||
|
if reverse or loop or (i < start + frames - 1):
|
||||||
|
args.append(delay)
|
||||||
|
delay += dp
|
||||||
|
# добавляем эффект для смены кадров
|
||||||
|
args.append(effect)
|
||||||
|
if reverse: # обратная анимация, если нужна
|
||||||
|
dp = -dp
|
||||||
|
delay += dp
|
||||||
|
for i in range(start + frames - 2, start, -1):
|
||||||
|
if ext:
|
||||||
|
img = renpy.display.im.image(img_name + str(i) + "." + ext)
|
||||||
|
else:
|
||||||
|
img = img2disp(img_name + str(i))
|
||||||
|
img = Transform(img, **properties)
|
||||||
|
args.append(img)
|
||||||
|
if loop or (i > start + 1):
|
||||||
|
args.append(delay)
|
||||||
|
delay += dp
|
||||||
|
args.append(effect)
|
||||||
|
return anim.TransitionAnimation(*args)
|
||||||
|
|
||||||
|
# показать фон с именем bg указанного цвета color
|
||||||
|
def bg(color="#000", bg="bg"):
|
||||||
|
renpy.scene()
|
||||||
|
renpy.show(bg, what=img2disp(color))
|
||||||
|
|
||||||
|
# меняем стандартное время всех или некоторых эффектов для появления/исчезновения спрайтов
|
||||||
|
def move_time(delay=.5, effects=["move", "ease"]):
|
||||||
|
effects = make_list(effects)
|
||||||
|
for i in effects:
|
||||||
|
define.move_transitions(i, delay)
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
# для цифровых часиков (не менять, не вызывать)
|
||||||
|
clock_properties = None
|
||||||
|
def show_digital_clock(st, at):
|
||||||
|
cur_time = datetime.datetime.now().strftime("%H:%M:%S")
|
||||||
|
img = Text(cur_time, **clock_properties)
|
||||||
|
return img, .25
|
||||||
|
|
||||||
|
# создать цифровые часики с любым именем картинки (только в init!):
|
||||||
|
# $ create_clock("digital_clock", size=48, color="#fff8", outlines=[(2, "#0008", 0, 0)], align=(.05, .05))
|
||||||
|
# применение - на любом экране screen:
|
||||||
|
# add "digital_clock"
|
||||||
|
# или в виде спрайта:
|
||||||
|
# show digital_clock
|
||||||
|
def create_clock(name, **properties):
|
||||||
|
global clock_properties
|
||||||
|
clock_properties = properties
|
||||||
|
renpy.image(name, DynamicDisplayable(show_digital_clock))
|
||||||
|
|
||||||
|
# показать экран на слое "мастер",
|
||||||
|
# чтобы он не исчезал, когда прячем интерфейс
|
||||||
|
def show_s(screen, *arg, **kwarg):
|
||||||
|
renpy.show_screen(screen, _layer="master", *arg, **kwarg)
|
||||||
|
|
||||||
|
# убрать экран со слоя "мастер"
|
||||||
|
def hide_s(screen, **kwarg):
|
||||||
|
renpy.hide_screen(screen, layer="master", **kwarg)
|
||||||
|
|
||||||
|
# добавляем неубирающийся по hide_interface слой
|
||||||
|
if not "forever" in config.layers:
|
||||||
|
config.layers.insert(config.layers.index("screens"), "forever")
|
||||||
|
|
||||||
|
# показать неубирающийся по нажатию "h" экран
|
||||||
|
def show_forever(screen):
|
||||||
|
renpy.show_screen(screen, _layer="forever")
|
||||||
|
|
||||||
|
# спрятать неубирающийся по нажатию "h" экран
|
||||||
|
def hide_forever(screen):
|
||||||
|
renpy.hide_screen(screen, layer="forever")
|
||||||
|
|
||||||
|
# получить английское название времени суток
|
||||||
|
# если не указывать время в часах,
|
||||||
|
# то будет взято системное время
|
||||||
|
# можно задать начало утра, дня, вечера и ночи в часах от 0 до 23
|
||||||
|
def time_of_day(hours=None, morning=7, day=11, evening=18, night=23):
|
||||||
|
if hours is None:
|
||||||
|
hours = int(datetime.datetime.now().strftime("%H"))
|
||||||
|
res = "night" # по умолчанию ночь
|
||||||
|
# границы любого времени суток можно поменять
|
||||||
|
if (hours >= morning) and (hours <= day):
|
||||||
|
res = "morning"
|
||||||
|
if (hours > day) and (hours <= evening):
|
||||||
|
res = "day"
|
||||||
|
if (hours > evening) and (hours < night):
|
||||||
|
res = "evening"
|
||||||
|
return res
|
||||||
|
|
||||||
|
# словарь цветов для времен суток
|
||||||
|
color_filters = {"morning": "#8404", "day": "#0000", "evening": "#0484", "night": "#000b"}
|
||||||
|
|
||||||
|
# получить цвет фильтра, соответствующий времени суток
|
||||||
|
def color_of_day(hours=None):
|
||||||
|
return color_filters[time_of_day(hours)]
|
||||||
|
|
||||||
|
# удалить все сохранения
|
||||||
|
def delete_saves_now():
|
||||||
|
all = renpy.list_saved_games(fast=True)
|
||||||
|
for i in all:
|
||||||
|
renpy.unlink_save(i)
|
||||||
|
renpy.restart_interaction()
|
||||||
|
DeleteSavesNow = renpy.curry(delete_saves_now)
|
||||||
|
|
||||||
|
# удаление всех сохранений с запросом подтверждения
|
||||||
|
def delete_saves(confirm=True):
|
||||||
|
if confirm:
|
||||||
|
layout.yesno_screen(message=_("Удалить все сохранения?"), yes=DeleteSavesNow(), no=NullAction())
|
||||||
|
else:
|
||||||
|
delete_saves_now()
|
||||||
|
DeleteSaves = renpy.curry(delete_saves)
|
||||||
|
|
||||||
|
# очистить постоянные данные и сохранения
|
||||||
|
def delete_data_now():
|
||||||
|
# удаление сохранений
|
||||||
|
delete_saves(False)
|
||||||
|
# удаление ачивок
|
||||||
|
# achievement.clear_all()
|
||||||
|
# achievement.sync()
|
||||||
|
# удаление постоянных данных
|
||||||
|
persistent._clear(progress=True)
|
||||||
|
# выход из игры
|
||||||
|
Quit(confirm=False)()
|
||||||
|
DeleteDataNow = renpy.curry(delete_data_now)
|
||||||
|
|
||||||
|
# удаление всех данных и сохранений с запросом подтверждения
|
||||||
|
def delete_data(confirm=True):
|
||||||
|
if confirm:
|
||||||
|
layout.yesno_screen(message="Удалить все данные?\nИгра будет закрыта.", yes=DeleteDataNow(), no=NullAction())
|
||||||
|
DeleteData = renpy.curry(delete_data)
|
||||||
|
|
||||||
|
# действие - продолжить игру оттуда, где закончили
|
||||||
|
# если загружать пока нечего, то кнопка неактивна
|
||||||
|
# textbutton _("Продолжить игру") action Continue()
|
||||||
|
class Continue(Action, DictEquality):
|
||||||
|
def __call__(self):
|
||||||
|
FileLoad(1, confirm=False, page="auto", newest=True)()
|
||||||
|
# кликабельность кнопки
|
||||||
|
def get_sensitive(self):
|
||||||
|
return FileLoadable(1, page="auto")
|
||||||
|
|
||||||
|
# объявлена ли картинка с именем name
|
||||||
|
def has_image(name):
|
||||||
|
for i in renpy.display.image.images:
|
||||||
|
# такая конструкция позволяет исключить пустые теги
|
||||||
|
if name == " ".join(" ".join(i).split()):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
# проверить существование нескольких изображений через запятую
|
||||||
|
def has_images(*args):
|
||||||
|
res = True
|
||||||
|
for i in args:
|
||||||
|
# вместо какой-то картинки может быть список или кортеж
|
||||||
|
if isinstance(i, (list, dict)):
|
||||||
|
res = res & has_images(i)
|
||||||
|
else:
|
||||||
|
res = res & has_image(i)
|
||||||
|
return res
|
||||||
|
|
||||||
|
# задан ли курсор с таким именем
|
||||||
|
def has_mouse(mouse):
|
||||||
|
if config.mouse:
|
||||||
|
if mouse in config.mouse.keys():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
# рандомный элемент из параметров на входе
|
||||||
|
# просто сокращаем писанину
|
||||||
|
def rnds(*args):
|
||||||
|
return renpy.random.choice(args)
|
||||||
|
|
||||||
|
# рандомное целое число в заданных пределах
|
||||||
|
# второй предел НЕ включительно, как в питоне
|
||||||
|
# (i_to можно не указывать, тогда максимум берется из i_from)
|
||||||
|
def rnd(i_from=0, i_to=None):
|
||||||
|
if i_to is None:
|
||||||
|
i_to = i_from
|
||||||
|
i_from = 0
|
||||||
|
return renpy.random.randint(int(i_from), int(i_to - 1))
|
||||||
|
|
||||||
|
# рандомное дробное число в заданных пределах
|
||||||
|
# (f_to можно не указывать, тогда максимум берется из f_from)
|
||||||
|
def rndf(f_from=0, f_to=None):
|
||||||
|
if f_to is None:
|
||||||
|
f_to = f_from
|
||||||
|
f_from = .0
|
||||||
|
return f_from + renpy.random.random() * (f_to - f_from)
|
||||||
|
|
||||||
|
# канал для зацикленного эффекта
|
||||||
|
renpy.music.register_channel("effect", "sfx", loop=True)
|
||||||
|
|
||||||
|
# зацикленный звуковой эффект, не музыка
|
||||||
|
def sfxplay(name, channel="effect", loop=True, fadein=1, fadeout=1, ext="ogg"):
|
||||||
|
if name:
|
||||||
|
renpy.music.play(audio_dir + "/" + name + "." + ext, channel=channel, loop=loop, fadein=fadein, fadeout=fadeout)
|
||||||
|
|
||||||
|
# костыли для звуков и музыки - сокращают писанину
|
||||||
|
# можно запускать музыку или звуки, не указывая папки и расширения
|
||||||
|
# по умолчанию для музыки music/*.ogg
|
||||||
|
# по умолчанию для звуков audio/*.ogg
|
||||||
|
|
||||||
|
# запустить музыку или плейлист
|
||||||
|
def mplay(mname, fadein=1, fadeout=1, loop=True, channel="music", ext="ogg"):
|
||||||
|
list = []
|
||||||
|
mname = make_list(mname)
|
||||||
|
for i in mname:
|
||||||
|
list.append(music_dir + "/" + i + "." + ext)
|
||||||
|
renpy.music.play(list, channel=channel, loop=loop, fadein=fadein, fadeout=fadeout)
|
||||||
|
|
||||||
|
# запустить музыку или случайно перемешанный плейлист
|
||||||
|
def rndplay(mname, fadein=1, fadeout=1, loop=True, channel="music", ext="ogg"):
|
||||||
|
list = make_list(mname)
|
||||||
|
if len(list) > 1:
|
||||||
|
renpy.random.shuffle(list)
|
||||||
|
mplay(list, fadein, fadeout, loop, channel, ext)
|
||||||
|
|
||||||
|
# перезапустить музыку, даже если уже играет она же
|
||||||
|
def mreplay(mname, fadein=1, fadeout=1, loop=True, channel="music", ext="ogg"):
|
||||||
|
new_fn = music_dir + "/" + mname + "." + ext
|
||||||
|
renpy.music.play(new_fn, channel=channel, loop=loop, fadein=fadein, fadeout=fadeout)
|
||||||
|
|
||||||
|
# запустить музыку для имени файла и пути
|
||||||
|
def fnplay(new_fn, fadein=1, fadeout=1, channel="music"):
|
||||||
|
old_fn = renpy.music.get_playing()
|
||||||
|
if old_fn != new_fn and new_fn:
|
||||||
|
renpy.music.play(new_fn, channel=channel, loop=True, fadein=fadein, fadeout=fadeout)
|
||||||
|
|
||||||
|
# последняя сохраненная мелодия
|
||||||
|
last_music_fn = ""
|
||||||
|
|
||||||
|
# сохранить в памяти играющую мелодию
|
||||||
|
def msave():
|
||||||
|
global last_music_fn
|
||||||
|
last_music_fn = renpy.music.get_playing()
|
||||||
|
|
||||||
|
# восстановить игравшую при сохранении мелодию
|
||||||
|
def mrestore(fadein=1, fadeout=1, channel="music"):
|
||||||
|
if last_music_fn:
|
||||||
|
fnplay(last_music_fn, fadein=fadein, fadeout=fadeout, channel=channel)
|
||||||
|
|
||||||
|
# воспроизвести звук для канала audio, который поддерживает многопоточность
|
||||||
|
def splay(mname, fadein=0, fadeout=0, channel=config.play_channel, ext="ogg"):
|
||||||
|
if mname:
|
||||||
|
mname = make_list(mname)
|
||||||
|
lst = []
|
||||||
|
for i in mname:
|
||||||
|
lst.append(audio_dir + "/" + i + "." + ext)
|
||||||
|
renpy.play(lst, channel=channel, fadein=fadein, fadeout=fadeout)
|
||||||
|
|
||||||
|
# воспроизвести звук
|
||||||
|
def sndplay(mname, fadein=0, fadeout=0, channel="sound", ext="ogg"):
|
||||||
|
if mname:
|
||||||
|
mname = make_list(mname)
|
||||||
|
lst = []
|
||||||
|
for i in mname:
|
||||||
|
lst.append(audio_dir + "/" + i + "." + ext)
|
||||||
|
renpy.play(lst, channel=channel, fadein=fadein, fadeout=fadeout)
|
||||||
|
|
||||||
|
# голос
|
||||||
|
def vplay(mname, fadein=0, fadeout=0, channel="voice", ext="ogg"):
|
||||||
|
if mname:
|
||||||
|
renpy.play("voices/" + mname + "." + ext, channel=channel, fadein=fadein, fadeout=fadeout)
|
||||||
|
|
||||||
|
# остановить звук
|
||||||
|
def sstop(fadeout=None, channel='audio'):
|
||||||
|
renpy.music.stop(channel=channel, fadeout=fadeout)
|
||||||
|
|
||||||
|
# остановить звук
|
||||||
|
def sndstop(fadeout=0, channel='sound'):
|
||||||
|
renpy.music.stop(channel=channel, fadeout=fadeout)
|
||||||
|
|
||||||
|
# остановить музыку
|
||||||
|
def mstop(fadeout=1, channel='music'):
|
||||||
|
renpy.music.stop(channel=channel, fadeout=fadeout)
|
||||||
|
|
||||||
|
# остановить зацикленный эффект
|
||||||
|
def sfxstop(fadeout=1, channel='effect'):
|
||||||
|
renpy.music.stop(channel=channel, fadeout=fadeout)
|
||||||
|
|
||||||
|
# превращаем функции в action для экранов screen
|
||||||
|
SPlay = renpy.curry(splay)
|
||||||
|
SFXPlay = renpy.curry(sfxplay)
|
||||||
|
SFXStop = renpy.curry(sfxstop)
|
||||||
|
MPlay = renpy.curry(mplay)
|
||||||
|
FNPlay = renpy.curry(fnplay)
|
||||||
|
VPlay = renpy.curry(vplay)
|
||||||
|
SStop = renpy.curry(sstop)
|
||||||
|
MStop = renpy.curry(mstop)
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
# получить словарь с найденными в строке тегами и их значением
|
||||||
|
def get_tags(text, prefix='#'):
|
||||||
|
res = {}
|
||||||
|
# выуживаем все теги ремарок
|
||||||
|
tags = re.findall('{' + prefix + '([^}]+)}', text)
|
||||||
|
# перебираем полученные теги
|
||||||
|
for i in tags:
|
||||||
|
parts = i.split('=')
|
||||||
|
if len(parts) > 0:
|
||||||
|
key = parts[0].strip()
|
||||||
|
val = None
|
||||||
|
if len(parts) > 1:
|
||||||
|
val = parts[1]
|
||||||
|
# добавляем тэг и его значение в словарь
|
||||||
|
res[key] = val
|
||||||
|
# возвращаем значения тэгов в виде словаря
|
||||||
|
return res
|
||||||
|
|
||||||
|
# убрать все тэги из строки
|
||||||
|
def del_tags(txt, prefix='#'):
|
||||||
|
if txt:
|
||||||
|
return re.sub(r'{' + prefix + '([^}]+)}', '', txt)
|
||||||
|
else:
|
||||||
|
return txt
|
||||||
|
|
||||||
|
# получить тэги и вернуть их в виде списка строк
|
||||||
|
def get_tags_str(text, prefix='#'):
|
||||||
|
# выуживаем все теги ремарок
|
||||||
|
return re.findall('{' + prefix + '([^}]+)}', text)
|
||||||
|
|
||||||
|
# разделить строку на две части - до и после знака равно
|
||||||
|
# (или другого разделителя) и убрать пробелы вокруг этих частей
|
||||||
|
def get_key_val(text, sep='='):
|
||||||
|
txt = text.split(sep, 1)
|
||||||
|
val, key = None, None
|
||||||
|
if len(txt) > 0:
|
||||||
|
key = txt[0].strip()
|
||||||
|
if len(txt) > 1:
|
||||||
|
val = txt[1].strip()
|
||||||
|
return key, val
|
||||||
|
|
||||||
|
# поиск в строке значения невидимого читателю тега
|
||||||
|
# пример использования:
|
||||||
|
# $ text = "Текст текст {#image=logo.png} текст."
|
||||||
|
# $ img = get_tag(text, 'image')
|
||||||
|
# на выходе в img будет logo.png
|
||||||
|
def get_tag(text, tag, default=None, prefix='#'):
|
||||||
|
tag = tag.strip()
|
||||||
|
tags = get_tags(text, prefix)
|
||||||
|
if tag in tags.keys():
|
||||||
|
return tags[tag]
|
||||||
|
return None
|
||||||
|
|
||||||
|
# есть ли тэг в строке
|
||||||
|
def have_tag(text, tag, prefix='#'):
|
||||||
|
return tag in get_tags(text, prefix).keys()
|
||||||
|
|
||||||
|
# нужно включить автоматические сохранения, чтобы работала Continue
|
||||||
|
config.has_autosave = True
|
||||||
|
|
||||||
|
# список спрайтов на экране (не только тегов)
|
||||||
|
def get_showing_sprites(layer='master'):
|
||||||
|
images = []
|
||||||
|
tags = renpy.get_showing_tags(layer)
|
||||||
|
for i in tags:
|
||||||
|
tag = i
|
||||||
|
atrs = renpy.get_attributes(i, layer)
|
||||||
|
for a in atrs:
|
||||||
|
tag += " " + a
|
||||||
|
images.append(tag)
|
||||||
|
return images
|
||||||
|
|
||||||
|
# найти на экране спрайт, содержащий тег
|
||||||
|
def get_sprite_by_tag(tag, layer='master'):
|
||||||
|
if tag:
|
||||||
|
# если среди всех спрайтов на экране
|
||||||
|
images = get_showing_sprites(layer)
|
||||||
|
for i in images:
|
||||||
|
# есть тот, что содержит нужный тэг
|
||||||
|
if str(tag) in str(i):
|
||||||
|
return str(i)
|
||||||
|
# если на экране нет говорящего персонажа
|
||||||
|
return None
|
||||||
|
|
||||||
|
# получить параметры спрайта
|
||||||
|
def get_sprite_bounds(tag, layer='master'):
|
||||||
|
# если не нашли спрайт
|
||||||
|
x, y, w, h = -1, -1, -1, -1
|
||||||
|
# ищем спрайт на экране
|
||||||
|
i = get_sprite_by_tag(tag, layer)
|
||||||
|
# если спрайт на экране
|
||||||
|
if i:
|
||||||
|
x, y, w, h = renpy.get_image_bounds(i, layer=layer)
|
||||||
|
return int(x), int(y), int(w), int(h)
|
||||||
|
|
||||||
|
# полное копирование
|
||||||
|
import copy as dcopy
|
||||||
|
def copy(*args):
|
||||||
|
return dcopy.deepcopy(*args)
|
||||||
|
|
||||||
|
# БЛОК ДЛЯ РАБОТЫ С ВРЕМЕНАМИ СУТОК
|
||||||
|
# как пользоваться:
|
||||||
|
|
||||||
|
# 1) если есть нужные рисунки, то назвать файлы по временам суток:
|
||||||
|
# bg_street_night, bg_street_morning и т.д. по списку
|
||||||
|
# в противном случае будет перекрашиваться основная картинка
|
||||||
|
# например, "bg street day" или скопированная в неё "bg street"
|
||||||
|
# первый суффикс в списке времён суток можно и не указывать: bg street
|
||||||
|
|
||||||
|
# 2) воспользоваться автоматическим объявлением спрайтов в блоке init:
|
||||||
|
# $ images_auto()
|
||||||
|
|
||||||
|
# 3) указать перфиксы спрайтов, которые будут зависеть от времени суток
|
||||||
|
# например, чтобы все фоны и все спрайты eileen и pytom зависели от времени суток:
|
||||||
|
# daytime_prefix = ["bg", "eileen", "pytom"]
|
||||||
|
|
||||||
|
# 4) при необходимости задать список времён суток, по умолчанию он:
|
||||||
|
# alldaytime = ["day", "night"]
|
||||||
|
|
||||||
|
# 5) в скрипте просто задавать время суток $ setdaytime("night")
|
||||||
|
# или переключение на следующее по списку (зациклено): $ setdaytime()
|
||||||
|
# спрайты на экране сразу поменяются
|
||||||
|
|
||||||
|
init:
|
||||||
|
# настройки нового освещения, на входе сразу матрица
|
||||||
|
transform daytime_light(matrix):
|
||||||
|
matrixcolor matrix
|
||||||
|
|
||||||
|
transform daytime_empty():
|
||||||
|
pass
|
||||||
|
|
||||||
|
init -99 python:
|
||||||
|
# список допустимых времён суток
|
||||||
|
# "night" - обязательно!
|
||||||
|
alldaytime = ["day", "night"]
|
||||||
|
|
||||||
|
# список префиксов-меток для динамических спрайтов,
|
||||||
|
# освещение которых будет зависеть от времени суток в curdaytime
|
||||||
|
# например, все фоны и пара героев:
|
||||||
|
# daytime_prefix = ["bg", "eileen", "pytom"]
|
||||||
|
# к таким именам будет добавлен суффикс из daytime_suffix
|
||||||
|
# а потом они будут превращены в динамические уже со своими именами без суффикса
|
||||||
|
daytime_prefix = []
|
||||||
|
|
||||||
|
# настройки каждого времени суток для перекрашивания фонов и спрайтов
|
||||||
|
# (color, brightness, saturation, contrast)
|
||||||
|
day_bg_attrs = ("#000", 0, 1, 1)
|
||||||
|
day_attrs = ("#000", 0, 1, 1)
|
||||||
|
evening_bg_attrs = ("#9af", -.2, .8, 1)
|
||||||
|
evening_attrs = ("#9af", 0, .9, 1)
|
||||||
|
night_bg_attrs = ("#9af", -.475, .375, .575)
|
||||||
|
night_attrs = ("#9af", -.1, .375, 1)
|
||||||
|
morning_bg_attrs = ("#fca", .1, 1, 1)
|
||||||
|
morning_attrs = ("#fca", .1, 1, 1)
|
||||||
|
|
||||||
|
# по умолчанию первое в списке время суток
|
||||||
|
curdaytime = alldaytime[0]
|
||||||
|
|
||||||
|
# суффикс для динамических спрайтов - первое в списке время суток
|
||||||
|
daytime_suffix = alldaytime[0]
|
||||||
|
|
||||||
|
# здесь будут храниться имена изменённых спрайтов, зависимых от curdaytime
|
||||||
|
daytime_suffixed = []
|
||||||
|
|
||||||
|
# префикс-метка для фонов
|
||||||
|
bg_prefix = "bg"
|
||||||
|
|
||||||
|
# переключение времени суток на новое или на следующее, если не указывать, на какое
|
||||||
|
def setdaytime(newdaytime=None, effect=dissolve):
|
||||||
|
if newdaytime is None:
|
||||||
|
# если новое время суток == None,
|
||||||
|
# то переключаемся на следующее в списке (по кругу)
|
||||||
|
i = alldaytime.index(curdaytime) + 1
|
||||||
|
if i >= len(alldaytime):
|
||||||
|
i = 0
|
||||||
|
newdaytime = alldaytime[i]
|
||||||
|
if effect:
|
||||||
|
renpy.show("black", tag="daytimeblack")
|
||||||
|
renpy.with_statement(effect)
|
||||||
|
store.curdaytime = newdaytime
|
||||||
|
if effect:
|
||||||
|
renpy.hide("daytimeblack")
|
||||||
|
renpy.with_statement(effect)
|
||||||
|
|
||||||
|
# получить трансформ с матрицей для ночного освещения
|
||||||
|
# по умолчанию для спрайта, True - для фона
|
||||||
|
def atdaytime(bg=False):
|
||||||
|
if bg:
|
||||||
|
key = "_" + bg_prefix + "_"
|
||||||
|
else:
|
||||||
|
key = "_"
|
||||||
|
key = curdaytime + key + "attrs"
|
||||||
|
attrs = ("#000", 0, 1, 0)
|
||||||
|
if key in globals().keys():
|
||||||
|
color, brightness, saturation, contrast = globals()[key]
|
||||||
|
matrix = BrightnessMatrix(brightness) * ContrastMatrix(contrast) * TintMatrix(color) * SaturationMatrix(saturation)
|
||||||
|
return daytime_light(matrix)
|
||||||
|
return daytime_empty()
|
||||||
|
|
||||||
|
# БЛОК ДЛЯ ДОПОЛНЕННОГО АВТООБЪЯВЛЕНИЯ СПРАЙТОВ
|
||||||
|
# стандартное автообъявление картинок, но с webp
|
||||||
|
# плюс не разбиваются на теги имена, предназначенные для LayerdImage
|
||||||
|
# вместо этого объединяются через нижний минус "_"
|
||||||
|
# префиксы, по которым определяются такие имена берутся из layered_prefixes
|
||||||
|
# выполняется после всего остального
|
||||||
|
init 1900 python hide:
|
||||||
|
def create_automatic_images():
|
||||||
|
|
||||||
|
seps = config.automatic_images
|
||||||
|
|
||||||
|
if seps is True:
|
||||||
|
seps = [ ' ', '/', '_' ]
|
||||||
|
|
||||||
|
for dir, fn in renpy.loader.listdirfiles():
|
||||||
|
|
||||||
|
if fn.startswith("_"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# только .png и .jpg и .jpeg и .webp
|
||||||
|
if not (fn.lower().endswith(".png") or fn.lower().endswith(".jpg") or fn.lower().endswith(".jpeg") or fn.lower().endswith(".webp")):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# убираем расширения и заменяем слеши.
|
||||||
|
if fn.lower().endswith(".jpeg") or fn.lower().endswith(".webp"):
|
||||||
|
shortfn = fn[:-5].replace("\\", "/")
|
||||||
|
else:
|
||||||
|
shortfn = fn[:-4].replace("\\", "/")
|
||||||
|
|
||||||
|
# делим строку на части
|
||||||
|
name = ( shortfn, )
|
||||||
|
for sep in seps:
|
||||||
|
name = tuple(j for i in name for j in i.split(sep))
|
||||||
|
|
||||||
|
# выбрасываем имя папок из тегов
|
||||||
|
while name:
|
||||||
|
for i in config.automatic_images_strip:
|
||||||
|
if name[0] == i:
|
||||||
|
name = name[1:]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
# для проверки префиксов и суффиксов после
|
||||||
|
# возможной склейки тегов
|
||||||
|
prefix = name[0]
|
||||||
|
suffix = name[len(name) - 1]
|
||||||
|
|
||||||
|
# убираем суффикс, если это первое время суток
|
||||||
|
if suffix == daytime_suffix:
|
||||||
|
name = name[:-1]
|
||||||
|
|
||||||
|
# имя без суффикса
|
||||||
|
name0 = name
|
||||||
|
|
||||||
|
# теги одной строкой
|
||||||
|
sname = " ".join(name)
|
||||||
|
|
||||||
|
# не делим на части имена, которые начинаются с префиксов для LayeredImage
|
||||||
|
layered = False
|
||||||
|
if prefix in layered_prefixes:
|
||||||
|
name = "_".join(name)
|
||||||
|
sname = name
|
||||||
|
layered = True
|
||||||
|
|
||||||
|
# добавляем суффикс по умолчанию, если начинается
|
||||||
|
# с префикса из списка меток для динамических спрайтов,
|
||||||
|
# которые будут менять освещение в зависимости от времени суток
|
||||||
|
if prefix in daytime_prefix:
|
||||||
|
# нельзя автоматизировать файлы с суффиксами времён суток,
|
||||||
|
# потому что они станут частью автоматизированного спрайта
|
||||||
|
if not suffix in alldaytime[1:]:
|
||||||
|
if not sname in daytime_suffixed:
|
||||||
|
# запоминаем имя изменённого спрайта
|
||||||
|
store.daytime_suffixed.append(sname)
|
||||||
|
# добавляем суффикс
|
||||||
|
if layered:
|
||||||
|
name = name + " " + daytime_suffix
|
||||||
|
else:
|
||||||
|
name = (*name, daytime_suffix)
|
||||||
|
|
||||||
|
# игнорируем, если уже создана одноимённая копия с суффиксом
|
||||||
|
if name0 in daytime_suffixed:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# игнорируем, если не набирается указанное в переменной количество тегов
|
||||||
|
if len(name) < config.automatic_images_minimum_components:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# игнорируем, если такой спрайт уже есть
|
||||||
|
if name in renpy.display.image.images:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# объявляем спрайт
|
||||||
|
renpy.image(name, fn)
|
||||||
|
|
||||||
|
# если заданы параметры, то объявляем спрайты автоматом
|
||||||
|
if config.automatic_images:
|
||||||
|
create_automatic_images()
|
||||||
|
|
||||||
|
# функция для динамического освещения спрайтов и фонов
|
||||||
|
def def_daytime(st, at, img):
|
||||||
|
# ищем картинку для текущего времени суток
|
||||||
|
new = img + " " + curdaytime
|
||||||
|
if has_image(new):
|
||||||
|
return new, None
|
||||||
|
# если не нашли, то берем основную
|
||||||
|
new = img + " " + daytime_suffix
|
||||||
|
# и перекрашиваем в соответствии с настройками
|
||||||
|
if img.startswith(bg_prefix):
|
||||||
|
return At(new, atdaytime(True)), None
|
||||||
|
return At(new, atdaytime()), None
|
||||||
|
|
||||||
|
# если заданы параметры, то создаём динамически освещённые спрайты
|
||||||
|
if len(daytime_suffixed) > 0:
|
||||||
|
# перебираем все спрайты, для которых нужна реакция на освещение
|
||||||
|
for i in daytime_suffixed:
|
||||||
|
# создаём такие спрайты
|
||||||
|
renpy.image(i, DynamicDisplayable(def_daytime, i))
|
|
@ -0,0 +1,26 @@
|
||||||
|
label fast_end1:
|
||||||
|
play music "Anemoia - System #1.mp3" loop volume 0.5
|
||||||
|
|
||||||
|
nvl clear
|
||||||
|
|
||||||
|
root_mind "На удивление, я заснул в этот раз быстро и крепко."
|
||||||
|
sysmsg "System will be shutdown..."
|
||||||
|
sysmsg "...3"
|
||||||
|
sysmsg "...2"
|
||||||
|
sysmsg "...1"
|
||||||
|
sysmsg "Это была самая быстра концовка на Диком Западе!"
|
||||||
|
|
||||||
|
return
|
||||||
|
label fast_end2:
|
||||||
|
play music "Anemoia - System #1.mp3" loop volume 0.5
|
||||||
|
|
||||||
|
nvl clear
|
||||||
|
|
||||||
|
root_mind "Ну и не очень то и хотелось. Сами за копейки вкалывайте. Я лучше попробую спать лечь."
|
||||||
|
sysmsg "System will be shutdown..."
|
||||||
|
sysmsg "...3"
|
||||||
|
sysmsg "...2"
|
||||||
|
sysmsg "...1"
|
||||||
|
sysmsg "Суслик пошел домой и... никого не встретил!"
|
||||||
|
|
||||||
|
return
|
|
@ -59,13 +59,13 @@ define gui.interface_text_color = '#ffffff'
|
||||||
## Шрифты и их размеры #########################################################
|
## Шрифты и их размеры #########################################################
|
||||||
|
|
||||||
## Шрифт, используемый внутриигровым текстом.
|
## Шрифт, используемый внутриигровым текстом.
|
||||||
define gui.text_font = "terminus.ttf"
|
define gui.text_font = "gui/fonts/terminus.ttf"
|
||||||
|
|
||||||
## Шрифт, используемый именами персонажей.
|
## Шрифт, используемый именами персонажей.
|
||||||
define gui.name_text_font = "terminus.ttf"
|
define gui.name_text_font = "gui/fonts/terminus.ttf"
|
||||||
|
|
||||||
## Шрифт, используемый текстом вне игры.
|
## Шрифт, используемый текстом вне игры.
|
||||||
define gui.interface_text_font = "terminus.ttf"
|
define gui.interface_text_font = "gui/fonts/terminus.ttf"
|
||||||
|
|
||||||
## Размер нормального текста диалога.
|
## Размер нормального текста диалога.
|
||||||
define gui.text_size = 33
|
define gui.text_size = 33
|
||||||
|
|
BIN
game/gui.rpyc
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 397 B |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 397 B |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,23 @@
|
||||||
|
#Инициализирующие параметры. Переменные используемые в игре и т.п.
|
||||||
|
|
||||||
|
# Определение персонажей игры.
|
||||||
|
define sysmsg = Character('sysmsg', color="#c83333")
|
||||||
|
define root = Character("[root]", color="#2ad3fd")
|
||||||
|
define root_mind = Character("{i}[root]{/i}", kind=nvl, color="#00ffea")
|
||||||
|
define unknown_char = Character("Unknown Contact", color="#09ff00")
|
||||||
|
define mystic_char = Character("╨п╨╕╨╖╨▒╤Г╨┤╤Г╤Й╨╡╨│╨╛", color="#ff8800")
|
||||||
|
|
||||||
|
#Определение сцен
|
||||||
|
image root_room = "Scenes/root_room.jpg"
|
||||||
|
|
||||||
|
#define menu = nvl_menu
|
||||||
|
# Вместо использования оператора image можете просто
|
||||||
|
# складывать все ваши файлы изображений в папку images.
|
||||||
|
# Например, сцену bg room можно вызвать файлом "bg room.png",
|
||||||
|
# а eileen happy — "eileen happy.webp", и тогда они появятся в игре.
|
||||||
|
|
||||||
|
|
||||||
|
$ sms_trans = [align(.15, .5), rotate(0, False)]
|
||||||
|
#Переменные
|
||||||
|
#Прочитал ли персонаж сообщение с предупреждением не запускать скрипт
|
||||||
|
define mystic_read = 0
|
|
@ -95,6 +95,8 @@ style frame:
|
||||||
## https://www.renpy.org/doc/html/screen_special.html#say
|
## https://www.renpy.org/doc/html/screen_special.html#say
|
||||||
|
|
||||||
screen say(who, what):
|
screen say(who, what):
|
||||||
|
on "show" action SMSAdd(what)
|
||||||
|
|
||||||
style_prefix "say"
|
style_prefix "say"
|
||||||
|
|
||||||
window:
|
window:
|
||||||
|
|
122
game/script.rpy
|
@ -1,19 +1,4 @@
|
||||||
# Вы можете расположить сценарий своей игры в этом файле.
|
# Игра начинается здесь:
|
||||||
|
|
||||||
# Определение персонажей игры.
|
|
||||||
define sysmsg = Character('sysmsg', color="#c83333")
|
|
||||||
define root = Character("[root]", color="#2ad3fd")
|
|
||||||
define root_mind = Character("{i}[root]{/i}", kind=nvl, color="#00ffea")
|
|
||||||
define unknown_char = Character("???", color="#09ff00")
|
|
||||||
define mystic_char = Character("╨п╨╕╨╖╨▒╤Г╨┤╤Г╤Й╨╡╨│╨╛", color="#ff8800")
|
|
||||||
image root_room = "Scenes/root_room.jpg"
|
|
||||||
#define menu = nvl_menu
|
|
||||||
# Вместо использования оператора image можете просто
|
|
||||||
# складывать все ваши файлы изображений в папку images.
|
|
||||||
# Например, сцену bg room можно вызвать файлом "bg room.png",
|
|
||||||
# а eileen happy — "eileen happy.webp", и тогда они появятся в игре.
|
|
||||||
|
|
||||||
# Игра начинается здесь:
|
|
||||||
label start:
|
label start:
|
||||||
play music "UNIVERSFIELD - Orion Nebula 2.mp3" loop volume 0.5
|
play music "UNIVERSFIELD - Orion Nebula 2.mp3" loop volume 0.5
|
||||||
scene console
|
scene console
|
||||||
|
@ -34,21 +19,6 @@ label start:
|
||||||
scene root_room
|
scene root_room
|
||||||
show monitor
|
show monitor
|
||||||
|
|
||||||
# root_mind "Рабочий вечер после трудного рабочего дня. Что может быть лучше? Только рабочий вечер с кружкой горячего кофе."
|
|
||||||
# root_mind "Я заварил свой любимый, хоть и дешевый кофе в кофеварке. Некоторые бы подумали, что при моей зарплате, я бы мог позволить себе и подороже."
|
|
||||||
# root_mind "Однако я пристрастился к этому ужасному вкусу довольно давно, и все остальные сорта кофе казались блеклыми. Ну и ладно. Главное что работу стимулятора мой напиток выполнял исправно."
|
|
||||||
# root_mind "Я привычно запустил подключение к серверу компании на которую работал уже больше полугода. Пробежавшись быстро по задачам, запустил деплой, открыл редактор и начал писать скрипт."
|
|
||||||
# root_mind "Не сказать, чтобы мне уж так нужна была эта подработка, однако глупо отказываться от денег, когда есть силы работать, а вечера занять абсолютно нечем"
|
|
||||||
# root_mind "Друзей у меня было не много, да и с теми что были я встречался довольно редко. Девушки хоть и бывали, но как-то всегда не серьезно и непостоянно. Игры давно приелись, хобби тоже."
|
|
||||||
# root_mind "Скучные, долгие вечера... и скучные долгие ночи..."
|
|
||||||
# root_mind "Бессоница пришла ко мне пару месяцев назад. Сначала это были задержки за подработкой до часу ночи. Потом до двух. Незаметно для меня это превратилось в засиживание до 5 утра."
|
|
||||||
# root_mind "И подъем в семь на основную работу. Я с некоторым внутренним удивлением наблюдал за изменениями в моем режиме сна, но ничего не предпринимал для того чтобы что-то изменить."
|
|
||||||
# root_mind "Дни становились тягучее и тусклее. Час забытия после основной работы и затем вечер за подработкой."
|
|
||||||
# root_mind "А затем, даже ощущая бесконечную усталось, я закрывал рабочие задачи и открывал интернет. Смотрел на ютубе ролики о науке, новости о политике, читал айтишные форумы и блоги."
|
|
||||||
# root_mind "В пять утра я начинал зевать и благодаря всех богов за эту милость падал в кровать и проваливался в черное забытье"
|
|
||||||
# root_mind "Утро я помнил плохо и снова блеклый день, и жизнь безостановочно проходящая мимо..."
|
|
||||||
# root_mind "Возможно так бы дальше все и продолжалось. Но все изменилось когда в мессенджер пришло сообщение с неизвестного аккаунта."
|
|
||||||
|
|
||||||
root_mind """
|
root_mind """
|
||||||
Рабочий вечер после трудного рабочего дня. Что может быть лучше? Только рабочий вечер с кружкой горячего кофе.
|
Рабочий вечер после трудного рабочего дня. Что может быть лучше? Только рабочий вечер с кружкой горячего кофе.
|
||||||
|
|
||||||
|
@ -116,18 +86,46 @@ label start:
|
||||||
label game:
|
label game:
|
||||||
play music "UNIVERSFIELD - Space Journey Through Nebulae and Galaxy.mp3" loop volume 0.5
|
play music "UNIVERSFIELD - Space Journey Through Nebulae and Galaxy.mp3" loop volume 0.5
|
||||||
|
|
||||||
unknown_char "Привет. Дали твои контакты, сказали ты админишь сервера по удаленке. Есть пара задач, по оплате - какая ставка у тебя за час работы?"
|
# Переписка с неизвестным от Палармо
|
||||||
|
$ sms_unknown = SMSL(_("Неизвестный контакт"))
|
||||||
|
|
||||||
|
# это слова получателя
|
||||||
|
$ sms_r = SMSR(_("Я"))
|
||||||
|
|
||||||
|
# сервисные сообщения
|
||||||
|
$ sms_c = SMSC(_("Системное сообщение"))
|
||||||
|
|
||||||
|
# имя абонента
|
||||||
|
$ sms_who = _("Неизвестный")
|
||||||
|
|
||||||
|
# цвет имени в шапке
|
||||||
|
$ sms_who_color = "#fff"
|
||||||
|
|
||||||
|
$ sms_show(_("Неизвестный контакт"), True)
|
||||||
|
with dissolve
|
||||||
|
|
||||||
|
$ now = time_now()
|
||||||
|
sms_c "Сегодня 03:16"
|
||||||
|
|
||||||
|
|
||||||
|
sms_unknown "[root], привет! Дали твои контакты, сказали ты админишь сервера по удаленке. Есть пара задач, по оплате - какая ставка у тебя за час работы?"
|
||||||
|
|
||||||
python:
|
python:
|
||||||
rubperhour = renpy.input("Час вашей работы стоит:", length=32)
|
rubperhour = renpy.input("Час вашей работы стоит:", length=32)
|
||||||
rubperhour = int(rubperhour.strip())
|
rubperhour = int(rubperhour.strip())
|
||||||
if not rubperhour:
|
if not rubperhour:
|
||||||
rubperhour = 3000
|
rubperhour = 3000
|
||||||
|
sms_r "[rubperhour] рублей за час. Оплата минимум за час, даже если делов на 5 минут."
|
||||||
|
|
||||||
if rubperhour > 5000:
|
if rubperhour > 5000:
|
||||||
unknown_char "Ух... Извини что побеспокоил. Нам такие суммы не подъемны."
|
sms_unknown "{image=emoji sweat} Ух... Извини что побеспокоил. Нам такие суммы не подъемны."
|
||||||
|
$ sms_hide()
|
||||||
|
with dissolve
|
||||||
jump fast_end2
|
jump fast_end2
|
||||||
else:
|
else:
|
||||||
unknown_char "Ок. Нас устраивает. Скину файл с заданием и доступы."
|
sms_unknown "Ок. Нас устраивает. Скину файл с заданием и доступы."
|
||||||
|
$ sms_hide()
|
||||||
|
with dissolve
|
||||||
jump game1
|
jump game1
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -176,11 +174,33 @@ label game1:
|
||||||
return
|
return
|
||||||
|
|
||||||
label game2:
|
label game2:
|
||||||
mystic_char "Не смей запускать скрипт тестирования нейросети!"
|
# Переписка с неизвестным от Палармо
|
||||||
root "Ты кто?"
|
$ sms_mystic = SMSL(_("╨п╨╕╨╖╨▒╤Г╨┤╤Г╤Й╨╡╨│╨╛"))
|
||||||
mystic_char "Не смей запускать скрипт тестирования нейросети!"
|
|
||||||
root "Понятно. Лети в бан."
|
# это слова получателя
|
||||||
|
$ sms_r = SMSR(_("Я"))
|
||||||
|
|
||||||
|
# сервисные сообщения
|
||||||
|
$ sms_c = SMSC(_("Системное сообщение"))
|
||||||
|
|
||||||
|
# имя абонента
|
||||||
|
$ sms_who = _("Неизвестный")
|
||||||
|
|
||||||
|
# цвет имени в шапке
|
||||||
|
$ sms_who_color = "#fc8619"
|
||||||
|
|
||||||
|
$ sms_show(_("╨п╨╕╨╖╨▒╤Г╨┤╤Г╤Й╨╡╨│╨╛"), True)
|
||||||
|
with dissolve
|
||||||
|
|
||||||
|
sms_mystic "Не смей запускать скрипт тестирования нейросети!"
|
||||||
|
sms_r "Ты кто?"
|
||||||
|
sms_mystic "Не смей запускать скрипт тестирования нейросети!"
|
||||||
|
sms_r "Понятно. Лети в бан."
|
||||||
|
$ sms_hide()
|
||||||
|
with dissolve
|
||||||
|
|
||||||
root_mind "Еще мне ботов не хватало. Интересно, по площадям бьют, или где-то в какой-то группе мессенджера охотник сидит? Ладно, не имеет значения."
|
root_mind "Еще мне ботов не хватало. Интересно, по площадям бьют, или где-то в какой-то группе мессенджера охотник сидит? Ладно, не имеет значения."
|
||||||
|
|
||||||
jump game3
|
jump game3
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -216,31 +236,5 @@ label game3:
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
label fast_end1:
|
|
||||||
play music "Anemoia - System #1.mp3" loop volume 0.5
|
|
||||||
|
|
||||||
nvl clear
|
|
||||||
|
|
||||||
root_mind "На удивление, я заснул в этот раз быстро и крепко."
|
|
||||||
sysmsg "System will be shutdown..."
|
|
||||||
sysmsg "...3"
|
|
||||||
sysmsg "...2"
|
|
||||||
sysmsg "...1"
|
|
||||||
sysmsg "Это была самая быстра концовка на Диком Западе!"
|
|
||||||
|
|
||||||
return
|
|
||||||
label fast_end2:
|
|
||||||
play music "Anemoia - System #1.mp3" loop volume 0.5
|
|
||||||
|
|
||||||
nvl clear
|
|
||||||
|
|
||||||
root_mind "Ну и не очень то и хотелось. Сами за копейки вкалывайте. Я лучше попробую спать лечь."
|
|
||||||
sysmsg "System will be shutdown..."
|
|
||||||
sysmsg "...3"
|
|
||||||
sysmsg "...2"
|
|
||||||
sysmsg "...1"
|
|
||||||
sysmsg "Суслик пошел домой и... никого не встретил!"
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
game/script.rpyc
|
@ -0,0 +1,293 @@
|
||||||
|
## модуль sms работает только в паре с модулем 7dots.rpy 2022 года
|
||||||
|
## для работы модуля нужно на экран "say" добавить строку:
|
||||||
|
## on "show" action SMSAdd(what)
|
||||||
|
|
||||||
|
# показать телефон на экране
|
||||||
|
# who - имя собеседника в шапке
|
||||||
|
# clear - очистить сообщения
|
||||||
|
# $ sms_show(who=None, clear=False)
|
||||||
|
|
||||||
|
# убрать телефон с экрана
|
||||||
|
# $ sms_hide()
|
||||||
|
|
||||||
|
# очистить экран телефона
|
||||||
|
# $ sms_clear()
|
||||||
|
|
||||||
|
# персонажи для переписки, собеседник
|
||||||
|
# $ sms_oksana = SMSL(_("Оксана Ш."))
|
||||||
|
|
||||||
|
# это слова получателя
|
||||||
|
# $ sms_r = SMSR(_("Я"))
|
||||||
|
|
||||||
|
# сервисные сообщения
|
||||||
|
# $ sms_c = SMSC(_("Системное сообщение"))
|
||||||
|
|
||||||
|
# имена лучше задавать, хоть их и не видно, для сохранения в истории
|
||||||
|
|
||||||
|
# чтобы переместить телефон, нужно поменять переменную:
|
||||||
|
# sms_trans = [align(.15, .5), rotate(-2.5, False)]
|
||||||
|
|
||||||
|
## НАСТРАИВАЕМЫЕ ПАРАМЕТРЫ
|
||||||
|
init python:
|
||||||
|
|
||||||
|
# размеры всего телефона
|
||||||
|
phone_w, phone_h = 500, 960
|
||||||
|
|
||||||
|
# размеры экрана телефона
|
||||||
|
scr_w, scr_h = 480, 860
|
||||||
|
|
||||||
|
# высота статичных участков (шапка и меню)
|
||||||
|
scr_top_h = 80
|
||||||
|
scr_bottom_h = 80
|
||||||
|
|
||||||
|
# цвет фона для шапки
|
||||||
|
scr_top_color = "#004472"
|
||||||
|
|
||||||
|
# фон экрана телефона
|
||||||
|
scr_bg = "#fff"
|
||||||
|
|
||||||
|
# максимальная ширина пузыря с сообщением
|
||||||
|
sms_w = 380
|
||||||
|
|
||||||
|
# если ширина/высота единственной в сообщении картинки больше этих,
|
||||||
|
# то картинка преобразуется в прикреплённое фото
|
||||||
|
sms_min_w, sms_min_h = 200, 200
|
||||||
|
|
||||||
|
# цвета фона
|
||||||
|
sms_left_bg_color = "#09f"
|
||||||
|
sms_right_bg_color = "#e5e5ea"
|
||||||
|
sms_center_bg_color = "#0000"
|
||||||
|
|
||||||
|
# цвета текста
|
||||||
|
sms_left_text_color = "#fff"
|
||||||
|
sms_right_text_color = "#000"
|
||||||
|
sms_center_text_color = "#aaa"
|
||||||
|
|
||||||
|
# шрифт
|
||||||
|
sms_text_size = 28
|
||||||
|
sms_text_font = "gui/fonts/Roboto-Regular.ttf"
|
||||||
|
|
||||||
|
# отступы текста sms от краёв пузырей
|
||||||
|
sms_xpadding, sms_ypadding = 24, 12
|
||||||
|
|
||||||
|
# отступы пузырей sms от краёв экрана
|
||||||
|
sms_xmargin, sms_ymargin = 24, 4
|
||||||
|
|
||||||
|
# фиксированные углы для пузырей с сообщениями (для Frame)
|
||||||
|
sms_corner_w, sms_corner_h = 16, 16
|
||||||
|
|
||||||
|
# трансформ для окна с телефоном
|
||||||
|
# например, можно задать положение
|
||||||
|
sms_trans = [align(.15, .5), rotate(-2.5, False)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## остальное лучше не менять
|
||||||
|
|
||||||
|
# показать телефон на экране
|
||||||
|
def sms_show(who=None, clear=False):
|
||||||
|
if not sms_current_style:
|
||||||
|
store.sms_current_style = "sms_center"
|
||||||
|
if who is not None:
|
||||||
|
store.sms_who = who
|
||||||
|
# если нужно, очистить сообщения
|
||||||
|
if clear:
|
||||||
|
sms_clear()
|
||||||
|
renpy.show_screen("sms", _layer="master")
|
||||||
|
|
||||||
|
# убрать телефон с экрана
|
||||||
|
def sms_hide():
|
||||||
|
renpy.hide_screen("sms", layer="master")
|
||||||
|
|
||||||
|
# очистить экран телефона
|
||||||
|
def sms_clear():
|
||||||
|
store.sms_all = []
|
||||||
|
renpy.restart_interaction()
|
||||||
|
|
||||||
|
# текущее время читателя
|
||||||
|
def time_now():
|
||||||
|
return datetime.datetime.now().strftime("%H:%M")
|
||||||
|
|
||||||
|
init:
|
||||||
|
# стиль для фрейма с телефоном
|
||||||
|
style phone_window is empty:
|
||||||
|
xysize(phone_w, phone_h)
|
||||||
|
background None
|
||||||
|
foreground Frame("phone skin", 0, 0)
|
||||||
|
|
||||||
|
# стиль конкретно для экрана телефона
|
||||||
|
style scr_frame is empty:
|
||||||
|
xysize(scr_w, scr_h)
|
||||||
|
background Frame(scr_bg, 0, 0)
|
||||||
|
align(.5, .5)
|
||||||
|
|
||||||
|
# стиль для шапки с именем абонента
|
||||||
|
style top_frame is empty:
|
||||||
|
xysize(scr_w, scr_top_h)
|
||||||
|
background scr_top_color
|
||||||
|
foreground Frame("phone top", 0, 0)
|
||||||
|
align(.5, .0)
|
||||||
|
|
||||||
|
# стиль для подвала с псевдо-полем ввода
|
||||||
|
style bottom_frame is empty:
|
||||||
|
xysize(scr_w, scr_bottom_h)
|
||||||
|
background Frame("phone bottom", 0, 0)
|
||||||
|
align(.5, 1.)
|
||||||
|
|
||||||
|
# стиль для sms от собеседника
|
||||||
|
style sms_left is frame:
|
||||||
|
background Frame(At("phone mask", color(sms_left_bg_color)), sms_corner_w, sms_corner_h)
|
||||||
|
xmaximum sms_w
|
||||||
|
xpadding sms_xpadding
|
||||||
|
ypadding sms_ypadding
|
||||||
|
xmargin sms_xmargin
|
||||||
|
ymargin sms_ymargin
|
||||||
|
xalign .0
|
||||||
|
|
||||||
|
# стиль для sms от получателя
|
||||||
|
style sms_right is sms_left:
|
||||||
|
background Frame(At("phone mask", color(sms_right_bg_color)), sms_corner_w, sms_corner_h)
|
||||||
|
xalign 1.
|
||||||
|
|
||||||
|
# стиль для сервисных сообщений
|
||||||
|
style sms_center is sms_left:
|
||||||
|
background None
|
||||||
|
xmaximum scr_w
|
||||||
|
xalign .5
|
||||||
|
|
||||||
|
# стили для текста сообщений
|
||||||
|
style sms_left_text is text:
|
||||||
|
font sms_text_font
|
||||||
|
size sms_text_size
|
||||||
|
color sms_left_text_color
|
||||||
|
text_align .0
|
||||||
|
|
||||||
|
style sms_right_text is sms_left_text:
|
||||||
|
color sms_right_text_color
|
||||||
|
|
||||||
|
style sms_center_text is sms_left_text:
|
||||||
|
color sms_center_text_color
|
||||||
|
|
||||||
|
init -1 python:
|
||||||
|
# листать вниз при добавлении сообщений
|
||||||
|
yadjValue = float("inf")
|
||||||
|
yadj = ui.adjustment()
|
||||||
|
|
||||||
|
# все sms в формате кортежей (стиль, текст)
|
||||||
|
sms_all = []
|
||||||
|
|
||||||
|
# здесь будет храниться стиль текущего сообщения
|
||||||
|
sms_current_style = ""
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
# определяем стиль текущего персонажа
|
||||||
|
def call_style(smsstyle, event_name, *args, **kwarg):
|
||||||
|
if event_name == "begin":
|
||||||
|
store.sms_current_style = smsstyle
|
||||||
|
# если это sms, то пикаем
|
||||||
|
if smsstyle in sms_beep_styles:
|
||||||
|
splay("new_message", ext="mp3")
|
||||||
|
|
||||||
|
# функция с параметрами в качестве одного параметра
|
||||||
|
def sms_style(*args, **kwarg):
|
||||||
|
return partial(call_style, *args, **kwarg)
|
||||||
|
|
||||||
|
# заготовки для невидимых на экране персонажей
|
||||||
|
def SMS(narrator=None, smsstyle="sms_center", *args, **kwarg):
|
||||||
|
return Character(narrator=narrator, window_style="empty", window_yoffset=config.screen_height, statement_name="say-centered", callback=sms_style(smsstyle), *args, **kwarg)
|
||||||
|
def SMSL(narrator=None, *args, **kwarg):
|
||||||
|
return SMS(narrator, "sms_left", *args, **kwarg)
|
||||||
|
def SMSR(narrator=None, *args, **kwarg):
|
||||||
|
return SMS(narrator, "sms_right", *args, **kwarg)
|
||||||
|
def SMSC(narrator=None, *args, **kwarg):
|
||||||
|
return SMS(narrator, *args, **kwarg)
|
||||||
|
|
||||||
|
sms_beep_styles = ["sms_left", "sms_right"]
|
||||||
|
|
||||||
|
# читаем сообщение, чтобы вывести на экран в общей куче
|
||||||
|
def sms_add(what=None):
|
||||||
|
# добавляем сообщение в список сообщений на экране
|
||||||
|
if what is not None and sms_current_style is not None:
|
||||||
|
store.sms_all.append((sms_current_style, what))
|
||||||
|
# если это не системное сообщение, то пикаем
|
||||||
|
if style in sms_beep_styles:
|
||||||
|
splay("new_message")
|
||||||
|
SMSAdd = renpy.curry(sms_add)
|
||||||
|
|
||||||
|
# не будет работать с пробелами, типа такого: "{ image = smile }"
|
||||||
|
# если вместо текста только картинка, то показать её с закруглёнными краями, а не пузырь
|
||||||
|
def sms_image(i_style, i_text):
|
||||||
|
if "{image=" in i_text.replace(" ", "") and not del_tags(i_text, ""):
|
||||||
|
imgs = get_tags(i_text, "image")
|
||||||
|
if imgs:
|
||||||
|
key = list(imgs.keys())
|
||||||
|
if len(key) == 1:
|
||||||
|
img = imgs[key[0]]
|
||||||
|
w, h = get_size(img)
|
||||||
|
# если картинка больше указанных в настройках размеров
|
||||||
|
if w >= sms_min_w or h >= sms_min_h:
|
||||||
|
# вписываем картинку в сообщение
|
||||||
|
if w > h:
|
||||||
|
z = sms_w / w
|
||||||
|
else:
|
||||||
|
z = sms_h / h
|
||||||
|
img = Transform(img, zoom=z)
|
||||||
|
w2, h2 = int(w * z), int(h * z)
|
||||||
|
# скругляем края
|
||||||
|
mask = Frame("phone mask", sms_corner_w, sms_corner_h, xysize=(w2, h2))
|
||||||
|
return AlphaMask(img, mask)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# экран телефона
|
||||||
|
screen sms():
|
||||||
|
$ yadj.value = yadjValue
|
||||||
|
# окно с телефоном
|
||||||
|
frame:
|
||||||
|
style "phone_window"
|
||||||
|
at sms_trans
|
||||||
|
# фрейм с экраном телефона
|
||||||
|
frame:
|
||||||
|
style "scr_frame"
|
||||||
|
# стопочкой шапка, смски и подвал
|
||||||
|
vbox:
|
||||||
|
xfill True
|
||||||
|
yfill True
|
||||||
|
# шапка с абонентом
|
||||||
|
frame:
|
||||||
|
style "top_frame"
|
||||||
|
# имя абонента
|
||||||
|
text sms_who style "sms_center_text" align(.5, .7) color sms_who_color
|
||||||
|
# смски с прокруткой
|
||||||
|
viewport:
|
||||||
|
id "sms_vp"
|
||||||
|
# размеры окна прокрутки с учетом шапки и подвала
|
||||||
|
xysize(scr_w, scr_h - scr_top_h - scr_bottom_h)
|
||||||
|
xinitial 1.
|
||||||
|
yfill False
|
||||||
|
mousewheel True
|
||||||
|
draggable True
|
||||||
|
side_xfill True
|
||||||
|
transclude
|
||||||
|
# перематываем в конец
|
||||||
|
yadjustment yadj
|
||||||
|
# стопочкой все сообщения
|
||||||
|
vbox:
|
||||||
|
xfill True
|
||||||
|
for i_style, i_text in sms_all:
|
||||||
|
# если это единственная картинка больше пузыря
|
||||||
|
$ img = sms_image(i_style, i_text)
|
||||||
|
if img:
|
||||||
|
# вписываем её в пузырь
|
||||||
|
frame:
|
||||||
|
style i_style
|
||||||
|
xpadding 0
|
||||||
|
ypadding 0
|
||||||
|
background None
|
||||||
|
add img
|
||||||
|
# иначе просто выводим текст в пузыре
|
||||||
|
else:
|
||||||
|
textbutton i_text style i_style
|
||||||
|
# подвал с псевдо-полем ввода
|
||||||
|
frame:
|
||||||
|
style "bottom_frame"
|