From 794f528b8bbaae664f55ed8c0b75d0bd65ba353c Mon Sep 17 00:00:00 2001 From: LoafyLemon Date: Sun, 29 May 2022 22:27:18 +0100 Subject: [PATCH] Web Updater * Implemented web updater (simulation only) * Added progress support to GUI * Version bump * Added updates directory * Fixed slider missing image variants --- game/gui/bar/bottom.png | 3 + game/gui/bar/left.png | 3 + game/gui/bar/right.png | 3 + game/gui/bar/top.png | 3 + game/scripts/gui/_images_.rpy | 2 + game/scripts/gui/main_menu.rpy | 7 +- game/scripts/options.rpy | 6 +- game/scripts/utility/updatechecker.rpy | 591 ------------------------- game/scripts/utility/updater.rpy | 242 ++++++++++ updates/.gitignore | 4 + 10 files changed, 269 insertions(+), 595 deletions(-) create mode 100644 game/gui/bar/bottom.png create mode 100644 game/gui/bar/left.png create mode 100644 game/gui/bar/right.png create mode 100644 game/gui/bar/top.png delete mode 100644 game/scripts/utility/updatechecker.rpy create mode 100644 game/scripts/utility/updater.rpy create mode 100644 updates/.gitignore diff --git a/game/gui/bar/bottom.png b/game/gui/bar/bottom.png new file mode 100644 index 00000000..a5ca50c9 --- /dev/null +++ b/game/gui/bar/bottom.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f685721571c5bd86eceb5e375cc2016a9736f5716478c7ce6b753f90d15fbbdd +size 317 diff --git a/game/gui/bar/left.png b/game/gui/bar/left.png new file mode 100644 index 00000000..16a1098f --- /dev/null +++ b/game/gui/bar/left.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de65b1cb39ea4679cdf1cd1705befd9a0b21622038dafcf7f9f2df271bce68bd +size 261 diff --git a/game/gui/bar/right.png b/game/gui/bar/right.png new file mode 100644 index 00000000..68ba6d71 --- /dev/null +++ b/game/gui/bar/right.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd98fb35a91407b45a696c01c60d33d7a39f2801601f16a8cf85ed65348c6cbd +size 262 diff --git a/game/gui/bar/top.png b/game/gui/bar/top.png new file mode 100644 index 00000000..2cef8e17 --- /dev/null +++ b/game/gui/bar/top.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9bab473ba582c893927e22766316ef1013539bc5b7a6dc01438c5d61a51a52d9 +size 317 diff --git a/game/scripts/gui/_images_.rpy b/game/scripts/gui/_images_.rpy index f39e36cd..caf38132 100644 --- a/game/scripts/gui/_images_.rpy +++ b/game/scripts/gui/_images_.rpy @@ -189,6 +189,7 @@ image slider_horizontal_idle_bar = Solid(gui.muted_color) image slider_horizontal_selected_idle_bar = "slider_horizontal_idle_bar" image slider_horizontal_hover_bar = Solid(gui.hover_muted_color) image slider_horizontal_selected_hover_bar = "slider_horizontal_hover_bar" +image slider_horizontal_insensitive_thumb = "slider_horizontal_idle_thumb" image slider_vertical_idle_thumb = Solid(gui.idle_color, ysize=gui.thumb_size) image slider_vertical_selected_idle_thumb = "slider_vertical_idle_thumb" @@ -198,6 +199,7 @@ image slider_vertical_idle_bar = Solid(gui.muted_color) image slider_vertical_selected_idle_bar = "slider_vertical_idle_bar" image slider_vertical_hover_bar = Solid(gui.hover_muted_color) image slider_vertical_selected_hover_bar = "slider_vertical_hover_bar" +image slider_vertical_insensitive_thumb = "slider_vertical_idle_thumb" image dark_slider_empty = "gui/slider/dark_empty.png" image light_slider_empty = "gui/slider/light_empty.png" diff --git a/game/scripts/gui/main_menu.rpy b/game/scripts/gui/main_menu.rpy index 228f49ec..1a7fb7c9 100644 --- a/game/scripts/gui/main_menu.rpy +++ b/game/scripts/gui/main_menu.rpy @@ -242,8 +242,13 @@ screen navigation(title=None): null height 14 # Half button height if main_menu: - if not title: + if (updater.can_update() or config.developer): + if new_version: + textbutton "Update available" action updater.Update(UPDATE_URL, simulate="available", patch=True) style_prefix "update_available" + else: + textbutton "Check for updates" action Function(check_for_updates, 300) + if show_quick_start: textbutton _("Quick Start") action Start("start_quick") elif show_dev_start: diff --git a/game/scripts/options.rpy b/game/scripts/options.rpy index fbb946e8..f58aa1ad 100644 --- a/game/scripts/options.rpy +++ b/game/scripts/options.rpy @@ -33,8 +33,8 @@ define config.developer = "auto" define config.console = True # Game version and naming -define config.version = "1.42.2" -define compatible_version = 1.40 +define config.version = "1.43.0" +define compatible_version = 1.43 define config.name = "WT Silver" # Application window settings @@ -136,7 +136,7 @@ define config.gc_print_unreachable = False init python: build.name = "WTS" - build.include_update = False + build.include_update = True build.include_old_themes = False build.exclude_empty_directories = True diff --git a/game/scripts/utility/updatechecker.rpy b/game/scripts/utility/updatechecker.rpy deleted file mode 100644 index a7bda691..00000000 --- a/game/scripts/utility/updatechecker.rpy +++ /dev/null @@ -1,591 +0,0 @@ - -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 diff --git a/game/scripts/utility/updater.rpy b/game/scripts/utility/updater.rpy new file mode 100644 index 00000000..9d93a511 --- /dev/null +++ b/game/scripts/utility/updater.rpy @@ -0,0 +1,242 @@ +define update_message_list = [ + "Spinning up disks", + "Lubricating Hermione", + "Filling up Tonks' glass", + "Rendering Cho's marvellous abs", + "Teaching Astoria some manners", + "Gazing into the crystal ball", + "Waxing Cho's broom", + "Sanitizing Dumbledore's desk", + "Confiscating Hermione's panties", + "Adjusting the dress code", + "Deleting Hermiobrine", + "Initializing Snape walk physics", + "Loading Genie's name mispronunciation engine", + "Generating {s}dad{/s} bad jokes", + "Perverting common nouns", + "Inserting obscure pop culture references", + "Loading wardrobe feature fondling", + "Corrupting Teachers", + "Ignoring plot holes", + "Inflating Susan's tits", + "Hiding Room of Requirement", + "Predicting images", + "Initializing sex-drive", + "Loading loading screen", + "Bribing ministry of magic employees", + "Maximizing student gullibility levels", + "Tuning out Luna's rambles", + "Filling cupboard with trash", + "Shredding rule violation reports", + "Adding tissue box of holding to desk", + "Ignoring established lore", + "Neglecting minor characters", + "Hiding the marauders map", + "Injecting British slang", + "Stiffening nipples", + "Nickering at knickers", + "Greasing Snape's hair", + "Increasing Genie's imagination levels", + "Adding illusion of choice dialogue options", + "Redirecting blood flow", + "Slutifying Slytherins", + "Dampening Genie's powers", + "Sleeving wizard cards", + "Breaking old save files", + "Loading pointless bird petting mechanics", + "Applying desk chair cushioning charm", + "Calculating semen trajectory", + "Opening spank-bank account at Gringotts", + "Scratching Genie's leg", + "Polishing Genie's wand", + "Defiling Cho's panties", + "Eliminating common sense", + "Applying lube", + "Restocking chocolate frog supply", + "Charming students", + "Breaking the fourth wall", + "Shortening skirts", + "Undressing house-elves", + "Lewdifying spells", + "Inserting sexual innuendoes", + "Searching for the g-spot", + "Adjusting refractory period levels", + "Indexing breast sizes", + "Prolonging the inevitable heat death of the universe", + "Redefining the big bang", + "Insert disc 2", + ] + +init python: + import requests + + UPDATE_URL = "http://update.silverstudiogames.org/updates.json" + new_version = None + + def check_for_updates(interval=3600*6): + global new_version + new_version = updater.UpdateVersion(UPDATE_URL, simulate="not_available", check_interval=interval) + + def fetch_update_logo(url): + if not (updater.can_update() or config.developer) or new_version is None: + return Null() + + filename = "logo_{}.png".format(new_version) + path = os.path.join(config.basedir, "updates/{}".format(filename)) + + # Read file if exists + if os.path.isfile(path): + with open(path, "rb") as f: + data = f.read() + return im.Data(data, path) + + # Fetch file if doesn't exist + response = requests.get(url, timeout=5) + data = response.content + + if not data: + return Null() + + with open(path, "wb") as f: + f.write(data) + + return im.Data(data, path) + + # check_for_updates() + +screen updater: + + tag menu + + default msg = renpy.random.choice(update_message_list) + default logo = fetch_update_logo("https://cdn.discordapp.com/attachments/535709194876354571/957762605735354378/discord.png") # http://update.silverstudiogames.org/logo.webp + + use game_menu(_("Updater"), scroll="viewport"): + + style_prefix "updater" + + vbox: + spacing gui.pref_spacing + xfill True + + button: + xalign 0.5 + action OpenURL("https://www.silverstudiogames.org/") + add logo xysize(570, 324) + + if u.state == u.ERROR: + text _("An error has occured:") + elif u.state == u.CHECKING: + text _("Fetching for updates.") + elif u.state == u.UPDATE_NOT_AVAILABLE: + text _("This program is up to date.") + elif u.state == u.UPDATE_AVAILABLE: + text _("[u.version] is available. Do you want to install it?") + hbox: + xalign 0.5 + + textbutton "Changelog" action OpenURL("https://www.silverstudiogames.org/p/changelog.html") + textbutton "Patreon" action OpenURL("https://patreon.com/silverstudiogames") + textbutton "Discord" action OpenURL("https://discord.gg/UbQeTCJ5RW") + elif u.state == u.PREPARING: + text _("Preparing to download the updates.") + elif u.state == u.DOWNLOADING: + text _("Downloading the updates.") + elif u.state == u.UNPACKING: + text _("Unpacking the updates.") + elif u.state == u.FINISHING: + text _("Finishing up.") + elif u.state == u.DONE: + text _("The updates have been installed. The program will restart.") + elif u.state == u.DONE_NO_RESTART: + text _("The updates have been installed.") + elif u.state == u.CANCELLED: + text _("The updates were cancelled.") + + if u.message is not None: + text "[u.message!q]" + + if u.progress is not None: + fixed: + bar value (u.progress or 0.0) range 1.0 style "updater_bar" + text "[msg]" color "#fff" size 10 yoffset 5 + + hbox: + xalign 0.5 + + if u.can_proceed: + textbutton _("Proceed") action u.proceed keysym "K_RETURN" + + if u.can_cancel: + textbutton _("Cancel") action u.cancel keysym "K_ESCAPE" + + timer 2.0 action SetScreenVariable("msg", renpy.random.choice(update_message_list)) repeat True + +style updater_text: + xalign 0.5 + +style updater_button + +style updater_button_text is gui_button_text +style updater_label is gui_label: + xsize 209 + right_padding 17 + +style updater_label_text is gui_label_text: + xalign 1.0 + text_align 1.0 + outlines [(2, "#000", 0, 0)] + +style updater_bar is empty: + ysize gui.bar_size + left_bar Frame("gui/bar/left.png", Borders(8, 8, 8, 8), tile=False) + right_bar Frame("gui/bar/right.png", Borders(8, 8, 8, 8), tile=False) + +style update_available_button: + xalign 0.5 + +style update_available_button_text: + color "#F9A001" + hover_color "#fff" + xalign 0.5 + + +init python early: + + def version_float(): + control, major, minor = config.version.split(" ")[0].split(".") + return float("{}.{}{}".format(control, major, minor)) + + 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() + + 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 + +init python: + config.after_load_callbacks.append(version_patch) + +label before_main_menu(): + # Add screen + return diff --git a/updates/.gitignore b/updates/.gitignore new file mode 100644 index 00000000..5e7d2734 --- /dev/null +++ b/updates/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore