init python early: # Import commonly used python modules import time import datetime import math import random import pygame import fnmatch import posixpath import re import functools import timeit as timeit_module import hashlib from operator import itemgetter from collections import OrderedDict get_volume_preference = renpy.game.preferences.get_volume def hashstring(input_string): return int(hashlib.md5(input_string.encode("utf-8")).hexdigest(), 16) def num_to_word(n, readable=True): """Transcript numbers (integers) into readable words.""" n = int(n) units = ("","one","two","three","four","five","six","seven","eight","nine") teens = ("","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen") tens = ("","ten","twenty","thirty","forty","fifty","sixty","seventy","eighty","ninety") thousands = ("","thousand","million","billion","trillion","quadrillion","quintillion","sextillion","septillion","octillion","nonillion","decillion","undecillion","duodecillion","tredecillion","quattuordecillion","sexdecillion","septendecillion","octodecillion","novemdecillion","vigintillion") output = [] if n == 0: output.append("zero") else: s = str(n) groups = (len(s)+2)//3 s = s.zfill(groups*3) for i in range(0, groups*3, 3): h,t,u = int(s[i]), int(s[i+1]), int(s[i+2]) g = groups-(i//3+1) if h > 0: output.append(units[h]+" hundred") if t > 1: if u > 0: output.append(tens[t]+"-"+units[u]) else: output.append(tens[t]) elif t == 1: if u > 0: output.append(teens[u]) else: output.append(tens[t]) else: if u > 0: output.append(units[u]) if g > 0 and (h+t+u) > 0: if i == (groups*3)-6: output.append(thousands[g]+" and") else: output.append(thousands[g]+",") if readable: output = " ".join(output) return output def clamp(n, smallest, largest): return max(smallest, min(n, largest)) def white_tint(image): return Transform( image, matrixcolor=TintMatrix((1.1, 1.1, 1.1)) ) def gray_tint(image): return Transform( image, matrixcolor=SaturationMatrix(0.0) ) def yellow_tint(image): return Transform( image, matrixcolor=TintMatrix((1.2, 1.1, 0.7)) ) def image_hover(image, brightness=0.12): """Returns slightly brighter image used during hover events""" return Transform( image, matrixcolor=BrightnessMatrix(brightness) ) def image_alpha(image, alpha=0.5): """Returns an image with changed alpha 0 - fully transparent 1 - fully visible""" return Transform( image, matrixcolor=OpacityMatrix(alpha) ) def set_clipboard(txt): txt = str(txt) pygame.scrap.put(pygame.scrap.SCRAP_TEXT, txt.encode()) def get_clipboard(): clipboard = pygame.scrap.get(pygame.scrap.SCRAP_TEXT) if clipboard: return clipboard.decode() return None def evaluate(txt): try: return __import__('ast').literal_eval(txt) except Exception as e: print("Error evaluating data:") print(e) def reset_variables(*args): """ Resets the given variables to their default values. As of Ren'py version 8.2.1: After reevaluating this function and the underlying Ren'py code, this function is deemed safe to use because Ren'Py employs a similar implementation internally, and it is also utilized for user-defined statements. Essentially, it calls execute_default on a node marked as ever_been_changed, followed by running py_eval_bytecode to add it to the global store. The potential side effects to consider is when config.after_default_callbacks is provided, or direct object references are in use. However, this can be easily patched if necessary. """ # Refer to renpy.ast.Default.set_default for implementation details defaults_set = renpy.store._defaults_set changed_set = renpy.store.__dict__.ever_been_changed for arg in args: if arg in defaults_set: if arg in changed_set: defaults_set.remove(arg) changed_set.remove(arg) elif config.developer: raise Exception(f"The variable {arg!r} was not previously set with a default value.") renpy.execute_default_statement(False) def disable_game_menu(): global _game_menu_screen _game_menu_screen = None def enable_game_menu(): global _game_menu_screen _game_menu_screen = "navigation" def is_game_menu(): global _game_menu_screen return _game_menu_screen is not None def make_revertable(obj): if isinstance(obj, _list): return [make_revertable(x) for x in obj] elif isinstance(obj, _dict): return dict((make_revertable(k), make_revertable(v)) for (k,v) in obj.items()) else: return obj def is_integer(s): s = str(s) if not s: return False if s[0] in ("-", "+"): # calling lstrip("0+-") would be faster but not exactly identical s = s[1:] if s.lstrip("0").isdigit(): return True return False def timeit(func, loops=10000, args=(), kwargs={}): rv = timeit_module.timeit("func(*args, **kwargs)", number=loops, globals=dict(func=func, args=args, kwargs=kwargs)) print(f"The task has taken {rv} seconds to finish") def autorange(func, args=(), kwargs={}): loops, time = timeit_module.Timer("func(*args, **kwargs)", globals=dict(func=func, args=args, kwargs=kwargs)).autorange() print(f"The task has taken {time/loops} seconds to finish ({loops} iterations in {time} seconds)") def list_swap_values(l, val1, val2): """Mutates the original list.""" l[val1], l[val2] = l[val2], l[val1] def natsort_key(s, pattern=re.compile("([0-9]+)")): return [int(t) if t.isdigit() else t.lower() for t in pattern.split(str(s))] def tts(s): renpy.display.tts.tts(str(s)) def is_in_lead(house): if isinstance(house, str): house = getattr(states.env, house) return (house == max(states.env.gryffindor, states.env.slytherin, states.env.ravenclaw, states.env.hufflepuff)) def play_potion_return(who): if states.env.daytime: return for i in inventory.get_instances_of_type("potion"): if not who in i.usable_on: continue if not i.in_progress[who]: continue i.ret(who) def strip(s): # We need a custom strip implementation because we cannot tell # if the raw argument isn't encapsulated in double, or triple quotes if s.startswith(('"', "'")) and s.endswith(('"', "'")): return s[1:-1] return s def matches(s1, s2, filter=" "): return s1.replace(filter, "") == s2.replace(filter, "") def istype(inst, clss): if isinstance(clss, (list, tuple, set)): return type(inst) in clss return type(inst) is clss class IntLike(python_object): # Does not support rollback def __init__(self, callable): self._callable = callable def __call__(self): return self._callable() def __repr__(self): return repr(self()) def __add__(self, value): raise Exception("IntLike does not support add") def __eq__(self, value): return self._callable() == value def __gt__(self, value): return self._callable() > value def __lt__(self, value): return self._callable() < value def __ge__(self, value): return self.__gt__(value) or self.__eq__(value) def __le__(self, value): return self.__lt__(value) or self.__eq__(value) def __len__(self): return len(self._callable()) def execute_callbacks(callbacks): for callback in callbacks: callback() def extract_number(key): match = re.match(r'^(\d+)', key) if match: return int(match.group(1)) return float('inf') # Return a large number for non-numeric keys