SnootGame/game/src/cg_gallery.rpy

221 lines
7.5 KiB
Plaintext

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<typeof galleryItems>
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