default persistent.cggallery = [] init python: # GALLERY OBJECT # Handles unlockables via ren'py g = Gallery() g.transition = dissolve # CONST PARAMS GALLERY_COLS = 3 GALLERY_CGS_PER_PAGE = 6 PREFERRED_WIDTH = 432 #px (1920 * 0.225) PREFERRED_HEIGHT = 243 #px (1080 * 0.225) DEFAULT_WIDTH_SCALE_RATIO = round(float(PREFERRED_WIDTH) / float(1920), 4) DEFAULT_HEIGHT_SCALE_RATIO = round(float(PREFERRED_HEIGHT) / float(1080), 4) NOT_UNLOCKED_COVER = im.FactorScale("gui/gallery/unlocked_cg_button_cover.png", DEFAULT_WIDTH_SCALE_RATIO, DEFAULT_HEIGHT_SCALE_RATIO) ACCEPTED_EXTENSIONS = ["jpg", "png"] # GALLERY ITEMS # Data structure that holds the data for each cg and button # item is the key in the Gallery # ext is the file extension # { item: string; cg: Displayable; ext: string }[] galleryItems = [] # Which page of the gallery is shown galleryPage = 1 # Global function to unlock a cg # fname should be a key used in galleryItems # (fname: string) -> None def unlockCg(fname): unlocked = fname in persistent.cggallery if unlocked: return # Save to renpy persistent data tmp = list(persistent.cggallery).copy() tmp.append(fname) persistent.cggallery = tmp renpy.persistent.save() # TODO: fix unlocking without game reset # Rebuild the gallery for i in range(0, len(galleryItems) - 1): if fname == str(galleryItems[i]["item"]): loadGallery() return # Make a scaled cg button # (cg: string; ext: string; w: float; h: float; unlocked?: boolean): Displayable def cg(fname, ext, w, h, unlocked = False): if not unlocked: return NOT_UNLOCKED_COVER scaleFactor = getBoxNormalizerRatio(w, h) return im.FactorScale("images/cgs/" + fname + "." + ext, scaleFactor["x"], scaleFactor["y"]) # Create an object in g:Gallery, add to galleryItems # (imageName: string; ext: string; w: float; h: float; unlocked?: boolean) -> None def addGalleryItem(imageName, ext, w, h, unlocked = False): g.button(imageName) g.image(imageName) horizontalPan = Pan((w - 1920, h - 1080), (0, h - 1080), 30.0) verticalPan = Pan((w - 1920, h - 1080), (w - 1920, 0), 30.0) g.transform(horizontalPan if w > h else verticalPan) #TODO: niceify if unlocked: g.unlock(imageName) g.condition("True") else: g.condition("False") galleryItems.append({ "item": imageName, "cg": cg(imageName, ext, w, h, unlocked), "ext": ext }) # Reads /images/cgs dir for all image files # Populates g:Gallery and galleryItems # Appends extra spaces at the end # () -> None def loadGallery(): from os import listdir, getcwd from os.path import isfile, join cgPath = "images/cgs/" workingDirPath = getcwd().replace("\\", "/") cgDirPath = workingDirPath + "/game/" + cgPath # Reset gallery galleryItems = [] g = Gallery() g.transition = dissolve # Add each image to the gallery for cgFile in listdir(cgDirPath): filePath = join(cgDirPath, cgFile) if isfile(filePath): ext = cgFile[-3:].lower() if (ext in ACCEPTED_EXTENSIONS): attr = getImageFileAttributes(filePath) fname = str(cgFile[0:-4]) unlocked = fname in persistent.cggallery addGalleryItem(fname, ext, attr["w"], attr["h"], unlocked) # Add empty items to fill grid after last cg button extraSpaces = GALLERY_COLS - (len(galleryItems) % GALLERY_COLS) for i in range(0, extraSpaces): galleryItems.append({ "item": None, "cg": None, "ext": None }) # Get width/height of image by absolute path # based on https://stackoverflow.com/questions/8032642/how-to-obtain-image-size-using-standard-python-class-without-using-external-lib # (filePath: string) -> { w: int; h: int } | None def getImageFileAttributes(filePath): try: from struct import unpack from imghdr import what w, h = 0, 0 with open(filePath, "rb") as file: ext = what(filePath) header = file.read(24) if (len(header) != 24): raise Exception("File header invalid for " + filePath) if (ext == 'png'): checksum = unpack('>i', header[4:8])[0] if (checksum != 0x0d0a1a0a): raise Exception("File checksum invalid for " + filePath) w, h = unpack('>ii', head[16:24]) if (ext == 'jpeg' or ext == 'jpg'): file.seek(0) size = 2 ftype = 0 while not 0xc0 <= ftype <= 0xcf: file.seek(size, 1) byte = file.read(1) while ord(byte) == 0xff: byte = file.read(1) ftype = ord(byte) size = unpack('>H', file.read(2))[0] - 2 file.seek(1, 1) h, w = unpack('>HH', file.read(4)) return { "w": w, "h": h } except Exception: #TODO: log error return None # Returns what params to call im.FactorScale with for cg button size # Basically the delta diff dimensions # (w: int; h: int) -> { x: float; y: float } def getBoxNormalizerRatio(w, h): x = round(float(PREFERRED_WIDTH) / float(w), 4) y = round(float(PREFERRED_HEIGHT) / float(h), 4) return { "x": x, "y": y } # Returns the pageth slice from galleryItems # (page: int): Partial def getGalleryPage(page): start = (page - 1) * GALLERY_CGS_PER_PAGE end = start + GALLERY_CGS_PER_PAGE return galleryItems[start:end] # Call to loading the gallery loadGallery() ## CG Gallery screen ######################################################## ## A screen that shows the image gallery screen cg_gallery(): tag menu use game_menu(_("Gallery"), scroll="viewport"): fixed: $ pageItems = getGalleryPage(galleryPage) $ galleryRows = len(pageItems) / GALLERY_COLS $ maxPage = len(galleryItems) / GALLERY_CGS_PER_PAGE vbox: hbox: grid GALLERY_COLS galleryRows: spacing gui.slot_spacing # Iterate through galleryItems and add cgs buttons for item in pageItems: if item["item"] == None: hbox else: add g.make_button(item["item"], item["cg"], xalign = 0.5, yalign = 0.5) grid 3 1: if galleryPage > 1: textbutton "<" action SetVariable("galleryPage", galleryPage - 1) else: hbox label str(galleryPage) if galleryPage < maxPage: textbutton ">" action SetVariable("galleryPage", galleryPage + 1) else: hbox