init python early: import zipfile import json import os import ctypes import shutil def is_admin(): try: return bool(ctypes.windll.shell32.IsUserAnAdmin()) except: return False def ask_admin(): """We only need admin privileges if we fail deleting a file.""" try: ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(" ".join(sys.argv)), None, 1) return True except: return False def remove_file(f): f = os.path.join(config.basedir, f) if not os.path.isfile(f): return 0 try: os.remove(f) except OSError as e: print "Cannot delete '{}'".format(f) print "Error: {}".format(e) return 2 else: print "'{}' deleted".format(f) return 1 def copytree(src, dst, symlinks=False, ignore=None): for item in os.listdir(src): s = os.path.join(src, item) d = os.path.join(dst, item) if os.path.isdir(s): shutil.copytree(s, d, symlinks, ignore) else: shutil.copy2(s, d) def version_float(): control, major, minor = config.version.split(" ")[0].split(".") return float("{}.{}{}".format(control, major, minor)) # This function is no longer being used to check for potential updates. # def version_check(): # if config.developer: # return False # import urllib2 # request = urllib2.Request(binascii.unhexlify("68747470733a2f2f706173746562696e2e636f6d2f7261772f424e354261647639")) # try: # response = urllib2.urlopen(request) # except URLError as e: # if hasattr(e, "reason"): # print "Cannot check for updates. Server cannot be reached." # print "Reason: {}".format(e.reason) # elif hasattr(e, "code"): # print "Cannot check for updates. Server cannot fulfill the request." # print "Error: {}".format(e.code) # return False # current = version_float() # latest = response.read() # return current >= float(latest) def version_patch(): if renpy.is_init_phase(): # Don't update save files from when game recovers from a crash. return latest = version_float() if not hasattr(renpy.store, "version"): if hasattr(renpy.store, "save_internal_version"): raise Exception("Loaded save file is incompatible. (Save Version: {}, Game Version: {})".format(getattr(renpy.store, "save_internal_version")), latest) raise Exception("Loaded save file is incompatible. (Save Version: Unknown, Game Version: {})".format(latest)) current = version if current > latest: raise Exception("Loaded save file is incompatible. (Save Version: {}, Game Version: {})".format(current, latest)) if current < latest: setattr(renpy.store, "version", latest) message = "Have fun!" achievements.attempt_repair() if current < 1.401: global inventory_mode, time_turner_ITEM, imagination, bdsm_imagination, cheat_reading, birthday_happened inventory_mode=0 inventory.remove(time_turner_ITEM) del time_turner_ITEM del imagination del bdsm_imagination del cheat_reading del birthday_happened if renpy.get_screen("blkfade"): renpy.hide_screen("blkfade") if not ball_quest.E4_complete: her_outfit_ball.price = 0 if current < 1.402: global mr_ev_ADR, hg_pr_sex_skip mirror.remove(mr_ev_ADR) del mr_ev_ADR ton_hat_classy.zorder=3 ton_hat_classy.set_layers() tonks.rebuild() del hg_pr_sex_skip if renpy.music.get_playing(channel="bg_sounds") == "sounds/fire02.ogg": renpy.music.stop(channel="bg_sounds") if current < 1.41: reset_variables("tutorial_dict") cho_outfit_cheerleader.name = "Ravenclaw Cheerleader Uniform" cho_outfit_party.name = "Clubslut Outfit" cho_outfit_bikini.name = "Micro Bikini Set" her_outfit_bikini2.name = "Leathered Bikini Set" her_outfit_bikini3.name = "Sling Bikini Set" her_outfit_witch.name = "16th Century Witch Costume" her_outfit_slutty_schoolgirl.name = "Slutty Schoolgirl Outfit" her_outfit_fishnet.name = "Fishnet Set" her_outfit_cheerleader_1.name = "Gryffindor Cheerleader Uniform" her_outfit_cheerleader_2.name = "Gryffindor Cheerleader Plus Uniform" lun_outfit_bikini3.name = "Rave Bikini Set" ton_outfit_bikini_1.name = "Simple Bikini set" ton_outfit_bikini_2.name = "Striped Bikini set" quidditchguide_ITEM.price = 100 for i in inventory.items: if hasattr(i, "infinite"): continue setattr(i, "infinite", False) i.infinite = False ### Start Update 'Admire Panties' event # Store states ev_tier = hg_pf_admire_panties.tier ev_in_progress = hg_pf_admire_panties.inProgress ev_counter = hg_pf_admire_panties.counter # Remove old pointer from favour list hg_favor_list.remove(hg_pf_admire_panties) # Re-run default statement reset_variables("hg_pf_admire_panties") # Restore state hg_pf_admire_panties.tier = ev_tier hg_pf_admire_panties.inProgress = ev_in_progress hg_pf_admire_panties.counter = ev_counter # Mark events from tier below as completed if ev_tier > 1: for i in xrange(ev_tier): for ev in hg_pf_admire_panties.events[i]: ev[1] = True # Add new pointer to favour list hg_favor_list.append(hg_pf_admire_panties) ### End Update 'Admire Panties' event leg2_scroll_ITEM.label = "leg2_scroll" global sna_support, ton_support del sna_support del ton_support preferences.renderer = "angle2" if renpy.windows else "gl2" # Update tooltip scope renpy.hide_screen("tooltip") renpy.show_screen("tooltip") # Reset house points (Required to ensure balance isn't affected) global gryffindor, slytherin, ravenclaw, hufflepuff gryffindor, slytherin, ravenclaw, hufflepuff = 400, 500, 500, 500 # Update Outfits Addons her_outfit_pajama.group = [her_hair_base.clone(), her_top_pajama.clone(), her_bottom_pajama.clone()] her_outfit_pajama.addons = [her_bottom_pajama2] her_outfit_maid.group = [her_hair_base.clone(), her_top_maid1.clone(), her_stockings_maid1.clone(), her_hat_maid1.clone(), her_neckwear_maid1.clone(), her_gloves_maid1.clone(), her_panties_base1.clone(), her_bra_base1.clone()] her_outfit_maid.addons = [her_neckwear_maid2] her_outfit_xmas.group = [her_hair_base.clone(), her_hat_antlers.clone(), her_neckwear_bell1.clone(), her_top_xmas.clone(), her_bottom_xmas.clone(), her_gloves_xmas.clone(), her_stockings_xmas.clone(), her_panties_base1.clone()] her_outfit_xmas.addons = [her_hat_elf] ton_outfit_succubus.group = [ton_hair_base_new.clone(), ton_hat_succubus.clone(), ton_neckwear_succubus.clone(), ton_gloves_succubus.clone(), ton_top_succubus.clone(), ton_panties_succubus.clone(), ton_accessory0_succubus.clone(), ton_accessory1_succubus.clone()] ton_outfit_succubus.addons = [ton_top_succubus2] ton_outfit_xmas.group = [ton_hair_base_new.clone(), ton_hat_antlers.clone(), ton_earring_bells.clone(), ton_neckwear_bell1.clone(), ton_bra_pasties2.clone(), ton_bottom_xmas.clone(), ton_gloves_xmas.clone(), ton_stockings_xmas.clone()] ton_outfit_xmas.addons = [ton_piercing1_nipple_bells, ton_bra_pasties2] # Update tonks' bikini ton_panties_base.color_default = [[228, 250, 255, 255], [228, 55, 20, 255]] ton_bra_base.color_default = [[228, 250, 255, 255], [228, 55, 20, 255]] ton_panties_base.set_layers() ton_bra_base.set_layers() for i in CHARACTERS: char = getattr(renpy.store, i) for outfit in char.outfits: if hasattr(outfit, "addons"): continue setattr(outfit, "addons", []) char.rebuild() # Add new backside layer for Hermione, and update zorders hermione.body.body.update({"backside": [None, 1],}) hermione.body.set_zorder(armright=-1, base=0) # Update CG camera object camera.min_zoom = 0.1 # Didn't we remove that in last patch? if hasattr(renpy.store, "hg_pr_sex_skip"): delattr(renpy.store, "hg_pr_sex_skip") # Update room objects fireplace_OBJ.tooltip = "Light/Extinguish" trophy_OBJ.pos = (650, 120) if current < 1.411: ton_gloves_leather.armfix = True ton_gloves_leather.set_layers() for i in CHARACTERS: char = getattr(renpy.store, i) char.rebuild() setattr(renpy.store, "inventory_mode", 0) if ll_pf_inspect.is_event_complete(2, 3): hair_luna_ITEM.owned = 1 if current < 1.412: message = "Happy halloween!" # Fix luna levels setattr(cho_hat_catears, "level", 10) setattr(lun_top_crop, "level", 7) setattr(lun_panties_lace2, "level", 7) setattr(lun_stockings_muggle_knee_socks, "categories", ("legwear", "stockings")) for i in inventory.items: # Fix general items if not hasattr(i, "give_label"): setattr(i, "give_label", None) if not hasattr(i, "usable_on"): setattr(i, "usable_on", []) # Fix decorations if i.type == "decoration": if not hasattr(i, "replaces"): setattr(i, "replaces", False) if not hasattr(i, "use_action"): setattr(i, "use_action", None) if not hasattr(i, "replace_action"): setattr(i, "replace_action", None) if not hasattr(i, "room_image_hover"): setattr(i, "room_image_hover", i.room_image) elif i.type == "gift": setattr(i, "usable_on", list(CHARACTERS)) setattr(collar_ITEM, "name", "Magic Collar") setattr(collar_ITEM, "price", 500) setattr(collar_ITEM, "givable", True) setattr(collar_ITEM, "give_label", "collar_scene") setattr(collar_ITEM, "caption", "Give") setattr(collar_ITEM, "usable_on", ["hermione"]) setattr(collar_ITEM, "unlocked", True) setattr(collar_ITEM, "label", None) setattr(collar_ITEM, "usable", False) setattr(collar_ITEM, "desc", "{size=-2}A collar made out of metal. It has an inscription on the back.\n\n{/size}{size=-2}{i}\"Transforms to show the wearers true self.\n WARNING: May cause harm to adjacent clothing during transformation.\"{/i}{/size}") setattr(collar_ITEM, "limit", 100) setattr(halloween_phoenix_ITEM, "name", "Phoenix Halloween Set") setattr(halloween_phoenix_ITEM, "desc", "A Halloween themed set for your favourite bird!") setattr(halloween_fireplace_ITEM, "desc", "Adds a spooky pumpkin near your fireplace!") setattr(halloween_cupboard, "name", "Cupboard Pumpkin") setattr(halloween_cupboard, "desc", "Get in the Halloween spirit with this pumpkin, nobody's eating them so might as well decorate with them!") # Fix room objects setattr(candleL_OBJ, "focus_mask", True) setattr(candleR_OBJ, "focus_mask", True) setattr(candleL_OBJ, "zorder", 3) setattr(candleR_OBJ, "zorder", 3) # Rebuild models for i in CHARACTERS: char = getattr(renpy.store, i) char.rebuild() if current < 1.413: message = "Happy halloween!" if getattr(renpy.store, "room_menu_active"): renpy.hide_screen("with_snape") renpy.hide_screen("with_tonks_animated") renpy.hide_screen("tonks_sit_ani") renpy.hide_screen("tonks_sit_ani") renpy.hide_screen("snape_chibi") if current < 1.414: message = "Happy Holidays!" for i in inventory.items: if i.type == "decoration": if not hasattr(i, "replace_anchor"): setattr(i, "replace_anchor", None) setattr(i, "replace_pos", None) # To fix hover images we need to dive into children of the hover transform displayable, # and see if the child is None, in which case it should be replaced with the base room image. # Animations (blink) are added on show events so it's irrevelant. if i.room_image_hover and i.room_image_hover.child is None: i.room_image_hover = i.room_image ton_panties_succubus.armfix = True tonks.rebuild() # Power saving results in flickering on Intel Xe graphics units and low performance on some android devices. (probably a symptom rather than a cause) preferences.gl_powersave = False if current < 1.42: message = "Happy April 1st! \n{size=-4}Disclaimer: This update is no joke, it's totaly real!{/size}" # Update Cho clothes choq_cloth_robequidditch1.armfix = False choq_cloth_robequidditch1.color_default = [[60, 78, 131, 255], [186, 141, 11, 255]] choq_cloth_robequidditch1.reset_color() choq_cloth_topsweater1.armfix = False choq_cloth_topsweater1.color_default = [[60, 78, 131, 255], [186, 141, 11, 255]] choq_cloth_topsweater1.reset_color() choq_cloth_pantslong2.armfix = False choq_cloth_pantsshort4.armfix = False cho_outfit_quidditch.group = [choq_cloth_topsweater1, choq_cloth_pantslong2, choq_cloth_robequidditch1, choq_bra_sports1, choq_panties_sport1] cho_outfit_quidditch_hufflepuff.group = [choq_cloth_topsweater1, choq_cloth_schoolskirt2, choq_cloth_robequidditch1, choq_accessory_protectors, choq_bra_sports1, choq_panties_sport1] cho_outfit_quidditch_slytherin.group = [choq_cloth_topsweater1, choq_cloth_pantslong2, choq_accessory_protectors2, choq_bra_sports1, choq_panties_sport1] cho_outfit_quidditch_gryffindor.group = [choq_cloth_topsweater1, choq_cloth_schoolskirt3, choq_accessory_protectors, choq_bra_sports1, choq_panties_sport1] # Update chitchat states and delete old vars from memory. setattr(renpy.store, "snape_chatted", chitchated_with_snape) setattr(renpy.store, "tonks_chatted", chitchated_with_tonks) setattr(renpy.store, "hermione_chatted", chitchated_with_hermione) setattr(renpy.store, "luna_chatted", chitchated_with_luna) setattr(renpy.store, "cho_chatted", chitchated_with_cho) setattr(renpy.store, "astoria_chatted", chitchated_with_astoria) setattr(renpy.store, "susan_chatted", chitchated_with_susan) delattr(renpy.store, "chitchated_with_snape") delattr(renpy.store, "chitchated_with_tonks") delattr(renpy.store, "chitchated_with_hermione") delattr(renpy.store, "chitchated_with_luna") delattr(renpy.store, "chitchated_with_cho") delattr(renpy.store, "chitchated_with_astoria") delattr(renpy.store, "chitchated_with_susan") # Quidditch setattr(cho_quid, "E10_complete", False) setattr(cho_quid, "E11_complete", False) setattr(cho_quid, "E12_complete", False) setattr(cho_quid, "E13_complete", False) setattr(cho_quid, "gryffindor_failed", False) setattr(cho_quid, "gryffindor_prepared", False) setattr(cho_quid, "gryffindor_training", False) setattr(cho_quid, "gryffindor_complete", False) # Tonks setattr(renpy.store, "tonks_wardrobe_unlocked", True) ### Start Update 'Talk To Me' events # Store states ev_tier = cc_pf_talk.tier ev_in_progress = cc_pf_talk.inProgress ev_counter = cc_pf_talk.counter # Remove old pointer from favour list cc_favor_list.remove(cc_pf_talk) # Re-run default statement reset_variables("cc_pf_talk") # Restore state cc_pf_talk.tier = ev_tier cc_pf_talk.inProgress = ev_in_progress cc_pf_talk.counter = ev_counter # Mark events from tier below as completed # TODO: This might cause some events from current tier to be skipped. # It would be better to use deepcopy on the list instead. if ev_tier > 1: for i in xrange(ev_tier): for ev in cc_pf_talk.events[i]: ev[1] = True # Add new pointer to favour list cc_favor_list.append(cc_pf_talk) cho.rebuild() # Reverse whoring cheat, otherwise the player would be locked out of some paths. if getattr(renpy.store, "cho_whoring") >= 12: setattr(renpy.store, "cho_whoring", 12) # Update camera camera.max_zoom = 5.0 ### End Update 'Talk To Me' event if current < 1.421: message = "Happy April 1st!" hooch.equip(hoo_outfit_default) cho_outfit_quidditch.group = [choq_cloth_topsweater1.clone(), choq_cloth_pantslong2.clone(), choq_cloth_robequidditch1.clone(), choq_bra_sports1.clone(), choq_panties_sport1.clone()] cho_outfit_quidditch_hufflepuff.group = [choq_cloth_topsweater1.clone(), choq_cloth_schoolskirt2.clone(), choq_cloth_robequidditch1.clone(), choq_accessory_protectors.clone(), choq_bra_sports1.clone(), choq_panties_sport1.clone()] cho_outfit_quidditch_slytherin.group = [choq_cloth_topsweater1.clone(), choq_cloth_pantslong2.clone(), choq_accessory_protectors2.clone(), choq_bra_sports1.clone(), choq_panties_sport1.clone()] cho_outfit_quidditch_gryffindor.group = [choq_cloth_topsweater1.clone(), choq_cloth_schoolskirt3.clone(), choq_accessory_protectors.clone(), choq_bra_sports1.clone(), choq_panties_sport1.clone()] if current < 1.422: message = "" hooch.equip(hoo_outfit_default) cho_outfit_quidditch.group = [cho_hair_ponytail1.clone(), choq_cloth_topsweater1.clone(), choq_cloth_pantslong2.clone(), choq_cloth_robequidditch1.clone(), choq_bra_sports1.clone(), choq_panties_sport1.clone()] cho_outfit_quidditch_hufflepuff.group = [cho_hair_ponytail1.clone(), choq_cloth_topsweater1.clone(), choq_cloth_schoolskirt2.clone(), choq_cloth_robequidditch1.clone(), choq_accessory_protectors.clone(), choq_bra_sports1.clone(), choq_panties_sport1.clone()] cho_outfit_quidditch_slytherin.group = [cho_hair_ponytail1.clone(), choq_cloth_topsweater1.clone(), choq_cloth_pantslong2.clone(), choq_accessory_protectors2.clone(), choq_bra_sports1.clone(), choq_panties_sport1.clone()] cho_outfit_quidditch_gryffindor.group = [cho_hair_ponytail1.clone(), choq_cloth_topsweater1.clone(), choq_cloth_schoolskirt3.clone(), choq_accessory_protectors.clone(), choq_bra_sports1.clone(), choq_panties_sport1.clone()] cho.rebuild() renpy.call_in_new_context("modal_popup", "Update Successful", "\nYour save file has been successfully updated to version {{b}}{}{{/b}}.\n\n{}".format(config.version, message), None, "Hurray!") renpy.block_rollback() return def version_upgrade(): if config.developer: return if renpy.mobile: return def get_patch(files, deep=False): prefix = config.gamedir if deep else config.basedir archives = [os.path.join(prefix, x) for x in files if x == "patch.zip"] if not archives: return None file = max(archives, key=os.path.getmtime) return file def get_progress(files): for i, _ in enumerate(files): print i print "Searching for an archive..." # Base search files = [x for x in os.listdir(config.basedir) if os.path.isfile(x)] patch = get_patch(files) # Deep search if not patch: files = renpy.list_files() patch = get_patch(files, deep=True) if not patch: print "No update packages detected. Skipping." return print "Archive found.\n{}".format(patch) with zipfile.ZipFile(patch, "r") as zip: print "Testing archive..." corrupted = zip.testzip() if corrupted: print "Cannot perform an upgrade. File is corrupted." return print "Checking manifest..." contents = zip.namelist() if not "manifest.json" in contents: print "Cannot perform an upgrade. Manifest not found." return with zip.open("manifest.json") as manifest: data = json.load(manifest) target = data.get("Target", None) if not target: print "Cannot perform an upgrade. Manifest is missing a target." return current = version_float() if current >= float(target): print "Cannot perform an upgrade. Equal or higher version already installed." return delete = data.get("Delete", []) for f in delete: status = remove_file(f) if status == 2: # Lacking permissions if renpy.windows and not is_admin(): ask_admin() renpy.quit() print "Backing up files..." src = os.path.join(config.basedir, "game/scripts/") dest = os.path.join(config.basedir, "old-game/scripts/") if os.path.isdir(dest): shutil.rmtree(dest) copytree(src, dest) print "Unpacking..." zip.extractall(config.basedir, members=get_progress(contents)) remove_file("instructions.txt") remove_file("DO NOT EXTRACT!") remove_file("hotfix.zip") src = patch dest = os.path.join(config.basedir, "old_patch.zip") if os.path.isfile(dest): remove_file(dest) os.rename(src, dest) print "Done." print "Restarting..." renpy.quit(relaunch=True) version_upgrade() init python: config.after_load_callbacks.append(version_patch) label before_main_menu(): # Add screen return