# чтобы 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))