diff options
author | Jesusaves <cpntb1@ymail.com> | 2020-12-16 19:41:56 -0300 |
---|---|---|
committer | Jesusaves <cpntb1@ymail.com> | 2020-12-16 19:41:56 -0300 |
commit | 4492fb781ac9a3f5707a3d83f033d7807cd5b8cd (patch) | |
tree | d0bd72a92a4e2bb32462c1a93b23a616e71cd42e | |
parent | e4a937a0a27e7024e730a778b13e92a9f4dd0d9a (diff) | |
download | client-4492fb781ac9a3f5707a3d83f033d7807cd5b8cd.tar.gz client-4492fb781ac9a3f5707a3d83f033d7807cd5b8cd.tar.bz2 client-4492fb781ac9a3f5707a3d83f033d7807cd5b8cd.tar.xz client-4492fb781ac9a3f5707a3d83f033d7807cd5b8cd.zip |
Split defs.rpy in five files
-rw-r--r-- | game/01_init.rpy | 325 | ||||
-rw-r--r-- | game/02_init.rpy | 247 | ||||
-rw-r--r-- | game/03_init.rpy | 313 | ||||
-rw-r--r-- | game/04_init.rpy | 75 | ||||
-rw-r--r-- | game/05_init.rpy | 30 | ||||
-rw-r--r-- | game/defs.rpy | 820 | ||||
-rw-r--r-- | game/misc.rpy | 7 | ||||
-rw-r--r-- | game/script.rpy | 77 |
8 files changed, 1008 insertions, 886 deletions
diff --git a/game/01_init.rpy b/game/01_init.rpy new file mode 100644 index 0000000..ddc00f4 --- /dev/null +++ b/game/01_init.rpy @@ -0,0 +1,325 @@ +######################################################################################## +# This file is part of Spheres. +# Copyright (C) 2019 Jesusalva + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +######################################################################################## +# Definitions + +init -3 python: + renpy.add_python_directory("python-extra") + import requests, zlib, base64, sys, copy, uuid, time, json + import websock as wsock + + # set PYTHON_VERSION variable (should be 2713, 3605 could fail) + PYTHON_VERSION="%d%d%02d" % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro) + PYTHON_VERSION=int(PYTHON_VERSION) + + # Ren'Py should come with Python 2.7.10 (2710), but just in case + # After all, I only tested with 2.7.10, 2.7.13 and 2.7.15 + if (PYTHON_VERSION < 2700 or PYTHON_VERSION > 3000): + raise Exception("WARNING: Python version is not 2.7\nStrange bugs may happen on your client.\n\nClick on \"Ignore\" to continue.\nClick on \"Quit\" to exit.") + + # Why setting this...? + ApTimer="" + + # Configuration + config.autoreload = False + config.save_on_mobile_background = False + persistent.release_name = "Christmas" + if persistent.host is None: + persistent.host="spheres.tmw2.org" + persistent.serverlist=[["TMW2", "spheres.tmw2.org", 61000]] + # FIXME: Set good defaults (=bad) for Android + if renpy.android: + persistent.nothreading=False + persistent.ssl_enabled=False + else: + persistent.serverlist.append(["Localhost", "localhost", 61000]) + if (persistent.allfiles is None): + persistent.allfiles=[] + allfiles=[] + HOST=str(persistent.host) + PORT=str(persistent.port) + UPDP=10302 + FAILUREMSG="Request failed, Please try again" + OFFLINEMSG="401 Unauthorized" + OKMSG="200 OK" + TIMEOUT_INTERVAL=3.0 + MAX_IRC_BUFFER=50 + SSL_IS_BROKEN=False + CERT_NONE=0 + debug=copy.copy(config.developer) + + # Error Codes + ERR_JSONDECODER=101 + ERR_LOGIN_DEFAULT=102 + ERR_INVALID=103 + ERR_TIMEOUT=104 + ERR_NOGEMS=105 + ERR_INVFULL=106 + ERR_OK=200 + + # All error code library + ERRNO=[FAILUREMSG, OFFLINEMSG, OKMSG, ERR_LOGIN_DEFAULT, ERR_INVALID, ERR_TIMEOUT, ERR_NOGEMS, ERR_INVFULL, ERR_OK] + + # Core musics/sfx + MUSIC_OPENING="sfx/bgm01.mp3" + MUSIC_TOWN="sfx/bgm02.mp3" + + # Spheres actions + AP_NONE =False + AP_SPHERE =1 + AP_SKILL =2 + + # Status + ST_TOWN =0 + ST_QUEST =1 + + # Unit flags + UF_NOLVL =1 + UF_NOPART =2 + UF_EXPUP =4 + UF_EVOMAT =8 + UF_SUPEREVO =64 + + # Jobs + Job_Swordsman =1 + Job_Assassin =2 + Job_Mage =3 + Job_Archer =4 + Job_Gunner =5 + + # IRC flags + IRC_AUTH_NONE =0 + IRC_AUTH_USER =1 + IRC_AUTH_NICK =2 + IRC_AUTH_CHAN =3 + + # Smart Print command + def stdout(message): + if debug: + if renpy.android: + if not renpy.is_init_phase(): + renpy.notify(message) + else: + print(message) + renpy.write_log("[DEBUG] %s" % message) + else: + renpy.write_log("[GAME] %s" % message) + return + + # Smart wait + def sdelay(delta=0.02): + try: + renpy.pause(delta, hard=True) + except: + time.sleep(delta) + return + + + # Global classes + # We need to override standard list method. Original by Triptych (stackoverflow) + class dlist(list): + + def __setitem__(self, index, value): + size = len(self) + if index >= size: + self.extend(None for _ in range(size, index + 1)) + + list.__setitem__(self, index, value) + + class ExecuteOnCall(): + def __init__(self, callable, *args, **kwargs): + self.callable = callable + self.args = args + self.kwargs = kwargs + + def __call__(self): + rv = self.callable(*self.args, **self.kwargs) + return rv + + def id(self): + return self.__call__() + + class RetString(): + def __init__(self, string): + self.string = string + + def __call__(self): + return self.string + + def id(self): + return self.__call__() + + # Screen Functions/class + # Override + class SpheresMainMenu(MainMenu): + def __call__(self): + + if not self.get_sensitive(): + return + + if self.confirm: + if config.autosave_on_quit: + renpy.force_autosave() + + layout.yesno_screen(layout.MAIN_MENU, SpheresMainMenu(False)) + else: + # Flush labels/sockets/timers as needed + renpy.call_in_new_context("quit") + # Restart + renpy.full_restart(config.game_main_transition) + + class ExtraImage(renpy.display.im.Image): + """ + Custom image manipulator, based on bink's code, topic 11732 + """ + + def __init__(self, loc, **properties): + """ + @param loc: Where the image really is (get_path already applied) + """ + + super(ExtraImage, self).__init__(loc, **properties) + self.loc = loc + + def load(self, unscaled=False): # W0221 + try: + #page = open(self.loc, "rb") + #pd = page.read() + #picdata = os.tmpfile() + #picdata.write(pd) + #picdata.seek(0) # reset seek position + stdout("Requested to open: %s" % repr(self.loc)) + picdata = open(self.loc, "rb") + stdout("Picdata is open (%s)" % self.loc) + + if unscaled: + surf = renpy.display.pgrender.load_image_unscaled(picdata, self.loc) + else: + surf = renpy.display.pgrender.load_image(picdata, self.loc) + + stdout("Picdata was closed") + picdata.close() + #page.close() + + return surf + except Exception, e: + if renpy.config.missing_image_callback: + im = renpy.config.missing_image_callback(self.loc) + if im is None: + raise e + return im.load() + raise + + def virtpos(posix): + if isinstance(posix, float): + return posix*1024 + else: + return posix/1024.0 + + # File Managment Functions + def get_path(path): + if True or renpy.android: + path=path.replace("/", "_") + #return renpy.loader.get_path(path) + return renpy.config.savedir + "/" + path + else: + return renpy.loader.get_path(path) + + def get_path_if_exists(path): + if True or renpy.android: + path=path.replace("/", "_") + #return renpy.loader.get_path(path) + return renpy.config.savedir + "/" + path + else: + return renpy.loader.transfn(path) + + # URL3 Function + def GAME_UPDATER(): + global tr_load + tr_load=False + + # If no version is provided, we are using default files + # Default files version is "1" (Should never happen) + if (persistent.version is None): + persistent.version=1 + + # Download upstream version + x=requests.get("http://"+HOST+'/version.txt') + try: + ver=int(x.text) + except: + stdout("IMPOSSIBLE TO DETERMINE VERSION") + raise Exception("Could not estabilish a connection to update server:\n%s is not valid." % x.text) # TODO: Show this beautifully? + # TODO: Should we set a "ver"? + + if (int(persistent.version) < ver): + # TODO: Check if the server have SSL support + stdout("Downloading updated game data...") + + # Download quests.json + f=open(get_path("quests.json"), "w") + stdout("Downloading quests.json") + x=requests.get("http://"+HOST+'/quests.json') + f.write(x.text) + f.close() + + # Download units.json + f=open(get_path("units.json"), "w") + stdout("Downloading units.json") + x=requests.get("http://"+HOST+'/units.json') + f.write(x.text) + f.close() + + # Download story.json + f=open(get_path("story.json"), "w") + stdout("Downloading story.json") + x=requests.get("http://"+HOST+'/story.json') + f.write(x.text) + f.close() + + # Download world.json + f=open(get_path("world.json"), "w") + stdout("Downloading world.json") + x=requests.get("http://"+HOST+'/world.json') + f.write(x.text) + f.close() + + # Download bar.json + f=open(get_path("bar.json"), "w") + stdout("Downloading bar.json") + x=requests.get("http://"+HOST+'/bar.json') + f.write(x.text) + f.close() + + # Download summons.json + f=open(get_path("summons.json"), "w") + stdout("Downloading summons.json") + x=requests.get("http://"+HOST+'/summons.json') + f.write(x.text) + f.close() + + persistent.version=ver + stdout("Update complete") + + # Download server news + # Handled by GAME_LOADER + + tr_uptodate=True + tr_load=True + return tr_load + diff --git a/game/02_init.rpy b/game/02_init.rpy new file mode 100644 index 0000000..6186e32 --- /dev/null +++ b/game/02_init.rpy @@ -0,0 +1,247 @@ +######################################################################################## +# This file is part of Spheres. +# Copyright (C) 2019 Jesusalva + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +######################################################################################## +# Definitions +############################################################################ +init -1 python: + # Android might have special SSL problems + if renpy.android: + try: + import ssl + except ImportError: + SSL_IS_BROKEN=True + stdout("Android SSL misbehavior detected") + import _openssl as ssl + else: + import ssl + print("Using system-wide SSL implementation...") + + # wsock already imported + + stdout("======================= %s %s %s" % (config.name, config.version, persistent.release_name)) + print "[STDBY] Loading Basic functions......." + + # Search for array[?][key]==search in an array of dicts + # Returns the dictionary, or returns ERR_INVALID + def dl_search(array, key, search): + try: + r=(item for item in array if item[key] == search).next() + except: + r=ERR_INVALID + if r is None: + r=ERR_INVALID + stdout("dlsearch: r is None") + return r + + def check_fail(raw): + global debug, FAILUREMSG + + if (debug): + stdout(str(raw)) + if (raw == FAILUREMSG): + return True + return False + + def json_decode(raw): + global ERRNO + + if (check_fail(raw)): + return ERR_LOGIN_DEFAULT + + # TODO Move this to check_fail and rewrite check_fail + # ERR_OFF should be handled top-level no? With full_restart() + if raw in ERRNO: + return raw + + try: + return int(raw) + except: + pass + + # Maybe base 64 + try: + rw=base64.b64decode(raw) + raw=rw + if (debug): + print "base64 decoded" + except: + pass + + # Maybe zlib compressed + try: + rw=zlib.decompress(raw) + raw=rw + if (debug): + print str(raw) + except: + pass + + # Decode JSON + try: + return json.loads(raw) + except: + return ERR_JSONDECODER + + def get_token(): + try: + t=Player['token'] + except: + t="0" #"f528764d624db129b32c21fbca0cb8d6" + return t + + def login(): + global Player + stdout("Login requested for %s" % logindata()) + raw=send_packet("login", logindata()) + + Player=json_decode(raw) + if (Player == ERR_JSONDECODER): + return ERR_JSONDECODER + if (Player == ERR_LOGIN_DEFAULT): + return ERR_LOGIN_DEFAULT + + try: + Player["inv"]=dlist() + except: + pass + return Player["code"] + + def send_packet_now(packet, args="", legacy=False): + global tr_load, tr_val, tr_busy + global ws + + stdout("Sending: %s" % packet) + try: + ws.send(get_token() + ";" + packet + ";" + args) + except: + stdout("Failed to send message!!") + # FIXME set tr_val/tr_load/tr_busy to good values + return False + # TODO: Wait for onmsg + # TODO: ping packets + return True + + def send_packet(packet, args=""): + global tr_load, tr_val, tr_busy + # TODO: if tr_busy already true, wait until it is made false + while tr_busy: + sdelay() + + # Book processing space for ourselves + tr_busy=True + tr_load=False + tr_val=None + + # This is a secret variable which disables threading and queue + # This may cause hangs and other issues. + if persistent.nothreading: + send_packet_now(packet, args) + tr_busy=False + val=tr_val + return val + + # FIXME + timeout=0.0 + renpy.show("spinner", at_list=[truecenter]) + r = send_packet_now(packet, args) + if not r: + # Something went wrong + return ERR_INVALID + + while not tr_load: + sdelay() # FIXME: This can cause errors in mobile? + timeout+=0.02 + + if timeout >= TIMEOUT_INTERVAL: + # FIXME: What if a screen is already being displayed? BUG + try: + renpy.call_screen("msgbox", "Error Code: %d\n\nApplication timeout, click to try again" % (ERR_TIMEOUT)) + timeout=0.0 + except: + if not "ping" in packet.lower(): + stdout("WARNING, ILLEGAL PACKET ON SCREEN TIME: %s" % packet) + pass + + renpy.hide("spinner") + val=tr_val + tr_busy=False + del tr_val + + print "value obtained" + if (val is None): + return ERR_INVALID + return val + + def GAME_LOADER(): + global allunitsbase, allunits, allquests, allstory, allworld, alltaverns + global allnews, tr_load + tr_load=False + + # Wait until everything is up to date + while not tr_uptodate: + sdelay() + + # Load unit data + allunitsbase=json.loads(requests.get("http://"+HOST+'/units.json').text) + # f=open(get_path_if_exists("units.json"), "r") + # allunitsbase=json.load(f) + # f.close() + + # Reorder unit data + allunits={} + for j in allunitsbase: + allunits[j["unit_id"]]=j + + # Load summons data + f=open(get_path_if_exists("summons.json"), "r") + allworld=json.load(f) + f.close() + + # Load quest data + f=open(get_path_if_exists("quests.json"), "r") + allquests=json.load(f) + f.close() + + # Load story data + f=open(get_path_if_exists("story.json"), "r") + allstory=json.load(f) + f.close() + + # Load world data + f=open(get_path_if_exists("world.json"), "r") + allworld=json.load(f) + f.close() + + # Load tavern data + f=open(get_path_if_exists("bar.json"), "r") + alltaverns=json.load(f) + f.close() + + # Load server news + try: + allnews=json.loads(requests.get("http://"+HOST+'/news.json', timeout=5.0).text) + except: + allnews=[] + pass + + stdout("[OK] Game data loaded to memory") + tr_load=True + tr_memcheck=True + return tr_load + + diff --git a/game/03_init.rpy b/game/03_init.rpy new file mode 100644 index 0000000..1125462 --- /dev/null +++ b/game/03_init.rpy @@ -0,0 +1,313 @@ +######################################################################################## +# This file is part of Spheres. +# Copyright (C) 2019 Jesusalva + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +######################################################################################## +# Definitions +############################################################################ +# This is the JSON data formatter +init python: + def logindata(): + global password + # TODO: Obtain user id based on device + return """{ + "passwd": "%s", + "version": "%s" + }""" % (password, config.version) + + + def recruitdata(t, a): + return """{ + "tavern": %d, + "amount": %d + }""" % (t, a) + + + def questdata(q, p): + return """{ + "quest_id": %d, + "party_id": %d + }""" % (q, p) + + + def battledata(u, s): + return """{ + "unit": %s, + "sphere": %s + }""" % (json.dumps(u), json.dumps(s)) + + def card_composite(cid, path): + # We need to try to get rarity + try: + r=allunits[int(cid)]["rare"] + except: + r=4 # FIXME + + # We need to try to get the element + try: + e=allunits[int(cid)]["attribute"] + print str(e) + except: + e=0 + + return Composite( + (640, 960), + (0, 0), "gfx/cards/bg.png", + (0, 0), path, + (0, 0), "gfx/cards/"+str(r)+".png", + (0, 0), "gfx/cards/ele/"+str(e)+".png") + # size,) + + # TODO: square_ and gfx/square/units + # Converts regular image filepaths to displayables names and vice-versa + def img_regex(name, reverse=False): + if not reverse: + # Extension is ommited, add them yourself! + if name.startswith("unit_"): + return "gfx/units/%s" % name.replace("unit_") + elif name.startswith("mob_"): + return "gfx/mobs/%s" % name.replace("mob_") + elif name.startswith("dialog_"): + return "gfx/dialog/%s" % name.replace("dialog_") + elif name.startswith("bg "): + return "gfx/bg/%s" % name.replace("bg ") + elif name.startswith("summon_"): + return "gfx/summons/%s" % name.replace("summon_") + else: + print("ERROR: Not a regular filename") + return name + else: + if name.startswith("gfx/units/"): + return "unit_%s" % name + elif name.startswith("gfx/mobs/"): + return "mob_%s" % name + elif name.startswith("gfx/dialog/"): + return "dialog_%s" % name + elif name.startswith("gfx/bg/"): + return "bg %s" % name + elif name.startswith("gfx/summons/"): + return "summon_%s" % name + else: + print("ERROR: Not a regular display name") + return name + + # Loads a sound called VAL + def get_sfx(val, ext): + # Search for the sound + show_img(val, False, ext=ext) + + valb=dl_search(persistent.allfiles, 0, val)[1] + if valb == ERR_INVALID: + print ("Invalid Sound: %s (%s)" % (path, val)) + return "sfx/regnum.mp3" # FIXME + return valb + + # Load sprite images: "unit" + for file in renpy.list_files(): + fn=file.replace('gfx/units/','').replace('/', ' ').replace('.png','') + if file.startswith('gfx/units/'): + if file.endswith('.png') or file.endswith('.webp'): + name = "unit_"+fn + #renpy.image(name, Image(file, yanchor=1.0)) + renpy.image(name, card_composite(fn, file)) + + dl=dl_search(persistent.allfiles, 0, name) + if dl is not ERR_INVALID: + persistent.allfiles.append((name, file)) + allfiles.append(name) + continue + continue + + # Load sprite images: "mob" + for file in renpy.list_files(): + fn=file.replace('gfx/mobs/','').replace('/', ' ').replace('.png','') + if file.startswith('gfx/mobs/'): + if file.endswith('.png') or file.endswith('.webp'): + name = "mob_"+fn + renpy.image(name, Image(file, yanchor=1.0)) + if not name in persistent.allfiles: + persistent.allfiles.append((name, file)) + allfiles.append(name) + continue + continue + + + # Load sprite images: "dialog" + for file in renpy.list_files(): + fn=file.replace('gfx/dialog/','').replace('/', ' ').replace('.png','').replace('.webp','') + if file.startswith('gfx/dialog/'): + if file.endswith('.png') or file.endswith('.webp'): + name = "dialog_"+fn + renpy.image(name, Image(file, yanchor=1.0)) + dl=dl_search(persistent.allfiles, 0, name) + if dl is not ERR_INVALID: + persistent.allfiles.append((name, file)) + allfiles.append(name) + continue + continue + + + # Load background images: "bg" + for file in renpy.list_files(): + fn=file.replace('gfx/bg/','').replace('/', ' ').replace('.png','').replace('.webp','') + if file.startswith('gfx/bg/'): + if file.endswith('.png') or file.endswith('.webp'): + name = "bg "+fn + renpy.image(name, Frame(file, 0, 0)) + dl=dl_search(persistent.allfiles, 0, name) + if dl is not ERR_INVALID: + persistent.allfiles.append((name, file)) + allfiles.append(name) + continue + continue + + + # Load summon images: "summon" + for file in renpy.list_files(): + fn=file.replace('gfx/summons/','').replace('/', ' ').replace('.png','').replace('.webp','') + if file.startswith('gfx/summons/'): + if file.endswith('.png') or file.endswith('.webp'): + name = "summon_"+fn + renpy.image(name, Image(file, yanchor=1.0)) + dl=dl_search(persistent.allfiles, 0, name) + if dl is not ERR_INVALID: + persistent.allfiles.append((name, file)) + allfiles.append(name) + continue + continue + + def star_write(am): + i, st = 0, "" + while i < am: + i+=1 + st+="★" + return st + + + # Overrides renpy.image() method + def new_img(name, where): + # d: Downloaded path + if not isinstance(name, tuple): + name = tuple(name.split()) + + #d = renpy.renpy.easy.displayable(where) + if renpy.android: + d=ExtraImage(where) + else: + d = renpy.renpy.easy.displayable(where) + renpy.renpy.display.image.register_image(name, d) + return + + # Retrieves Ren'Py displayable name associated to PATH + def get_img(path): + # Search for the image name + val=img_regex(path, True) + show_img(val, False) + + valb=dl_search(persistent.allfiles, 0, val)[1] + if valb == ERR_INVALID: + print ("Invalid Image: %s (%s)" % (path, val)) + return "gfx/spinner.png" + return valb + + # Overrides renpy.show() and renpy.image() methods + # Missing: transient=False, munge_name=True + def show_img(img, show=True, at_list=[ ], tag=None, zorder=None, behind=[ ], atl=None, what=None, layer=None, ext=".png"): + global tr_loading + # Image exists, display it + if img in allfiles: + if show: + renpy.show(img, at_list=at_list, tag=tag, zorder=zorder, behind=behind, atl=atl, what=what, layer=layer) + return + + # Have we downloaded this image previously? + path=dl_search(persistent.allfiles, 0, img) + print str(path) + + # Image doesn't exists, we must download it + while (path == ERR_INVALID): + tr_loading=True + # Latest version converts these formats to WebP + if ext in [".png", ".jpg", ".jpeg"]: + ext=".webp" + # Otherwise, preserve extension. + if renpy.android: + addr="extra_%s%s" % (img.replace(" ", "_"), ext) + else: + addr="extra/%s%s" % (img.replace(" ", "_"), ext) + + f=open(get_path(addr), "w") + stdout("Downloading additional file: %s" % img.replace(" ", "_")) + x=requests.get("http://%s:%d/%s?token=%s" % (HOST, UPDP, img.replace(" ", "_"), get_token())) # , timeout=8.0 → Need to handle sudden death + if x.status_code == 200: + f.write(x.content) + f.close() + # Android needs paths to be saved by full + if renpy.android: + addr=get_path(addr) + path=((img, addr)) + persistent.allfiles.append(path) + else: + try: + retry=renpy.call_screen("confirm", "Error downloading file.\nError Code: %d\n\nRetry?" % x.status_code, Return(True), Return(False)) + if not retry: + if tag is None: + tag=img + path=None + if show: + renpy.show("spinner", at_list=at_list, tag=tag, zorder=zorder, behind=behind, atl=atl, what=what, layer=layer) # TODO Show error + return + # TODO: “Retry?” + except: + print("Failed, trying again") + + # Image exists, but wasn't loaded yet + if (path != ERR_INVALID and path is not None): + print "Detected not loaded image" + # Valid Image Extensions: PNG, JPG, JPEG, GIF, WEBP + if ext in [".png", ".jpg", ".jpeg", ".gif", ".webp"]: + # Maybe it is an unit + if img.startswith("unit_"): + new_img(img, card_composite(img.replace("unit_", ""), path[1])) + else: + new_img(img, path[1]) + + stdout("registered image: "+path[1]) + allfiles.append(img) + if show: + renpy.show(img, at_list=at_list, tag=tag, zorder=zorder, behind=behind, atl=atl, what=what, layer=layer) + tr_loading=False + return + + # Something went wrong + stdout("show_img reached abnormal ending") + return + + ########################################################## + # Other Music + MUSIC_BATTLE=RetString("sfx/bgm03.mp3") + MUSIC_BOSS=RetString("sfx/bgm04.mp3") + MUSIC_PARTY=RetString("sfx/bgm02.mp3") + #MUSIC_PARTY=ExecuteOnCall(get_sfx, "sfx_bgm05", ".mp3")#"sfx/bgm05.mp3" + MUSIC_VICTORY=RetString("sfx/bgm06.mp3") + MUSIC_WORLDMAP=RetString("sfx/bgm02.mp3") + #MUSIC_WORLDMAP=ExecuteOnCall(get_sfx, "sfx_bgm07", ".mp3")#"sfx/bgm07.mp3" + MUSIC_PROLOGUE01=RetString("sfx/regnum.mp3") + MUSIC_PROLOGUE02=RetString("sfx/prologue.mp3") + MUSIC_PROLOGUE03=RetString("sfx/shining.mp3") + + ws = None + diff --git a/game/04_init.rpy b/game/04_init.rpy new file mode 100644 index 0000000..7cbf54a --- /dev/null +++ b/game/04_init.rpy @@ -0,0 +1,75 @@ +######################################################################################## +# This file is part of Spheres. +# Copyright (C) 2019 Jesusalva + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +######################################################################################## +# Definitions + +init python: + # FIXME: Drop dead if session_id mismatches (new "token") + # Same as ondata? + def onmsg(self, message): + global tr_load, tr_val, tr_busy + stdout("Server : " + message) + + # Inform whatever is waiting for us that a reply was obtained + tr_load=True + tr_val=str(received) + # We do not clean tr_busy here + return + + def ondata(self, msg, dtype, cont): + #print("data received") + """ + on_data: callback object which is called when a message received. + This is called before on_message or on_cont_message, + and then on_message or on_cont_message is called. + on_data has 4 argument. + The 1st argument is this class object. + The 2nd argument is utf-8 string which we get from the server. + The 3rd argument is data type. ABNF.OPCODE_TEXT or ABNF.OPCODE_BINARY will be came. + The 4th argument is continue flag. if 0, the data continue + """ + #renpy.notify(msg) + return + + def onopen(self): + global tr_busy, tr_load, tr_val, HOST + print("Opening connection to %s" % HOST) + tr_load=True + tr_val="" + tr_busy=False + self.send("PING") + + def onerror(self, err): + stdout("ERROR RECEIVED") + stdout("More details: %s" % repr(err)) + stdout("An error happened: %s" % str(err)) + # FIXME: If such error happen, the game never dies + # This is a huge problem o.o + while True: + renpy.call_screen("msgbox", + "An unrecoverable error happened.\nPress OK to return to main menu screen.") + #raise KeyboardException(str(err)) + #raise err + # FIXME: Not working, and the loop also does not work + # Maybe because it causes an Exception? + # I wonder if I can/should run this in a non-daemon thread? + # But I sense the problem is not here but a level above + #renpy.quit(relaunch=True, status=1) + sdelay(1.0) + return 1 + diff --git a/game/05_init.rpy b/game/05_init.rpy new file mode 100644 index 0000000..15d24b6 --- /dev/null +++ b/game/05_init.rpy @@ -0,0 +1,30 @@ +######################################################################################## +# This file is part of Spheres. +# Copyright (C) 2019 Jesusalva + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +######################################################################################## +# Definitions + +label prestart: + python: + tr_busy=True + tr_val="" + tr_load=False + tr_uptodate=False + tr_memcheck=False + session_id=uuid.uuid4().hex + return + diff --git a/game/defs.rpy b/game/defs.rpy index 54b94fc..41afa76 100644 --- a/game/defs.rpy +++ b/game/defs.rpy @@ -18,832 +18,12 @@ ######################################################################################## # Definitions -init -3 python: - renpy.add_python_directory("python-extra") - import requests, zlib, base64, sys, copy, uuid, time - import websock as wsock - # set PYTHON_VERSION variable (should be 2713, 3605 could fail) - PYTHON_VERSION="%d%d%02d" % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro) - PYTHON_VERSION=int(PYTHON_VERSION) - # Ren'Py should come with Python 2.7.10 (2710), but just in case - # After all, I only tested with 2.7.10, 2.7.13 and 2.7.15 - if (PYTHON_VERSION < 2700 or PYTHON_VERSION > 3000): - raise Exception("WARNING: Python version is not 2.7\nStrange bugs may happen on your client.\n\nClick on \"Ignore\" to continue.\nClick on \"Quit\" to exit.") - # Why setting this...? - ApTimer="" - # Configuration - config.autoreload = False - config.save_on_mobile_background = False - persistent.release_name = "Christmas" - if persistent.host is None: - persistent.host="spheres.tmw2.org" - persistent.serverlist=[["TMW2", "spheres.tmw2.org", 61000]] - # FIXME: Set good defaults (=bad) for Android - if renpy.android: - persistent.nothreading=False - persistent.ssl_enabled=False - else: - persistent.serverlist.append(["Localhost", "localhost", 61000]) - if (persistent.allfiles is None): - persistent.allfiles=[] - allfiles=[] - HOST=str(persistent.host) - PORT=str(persistent.port) - UPDP=10302 - FAILUREMSG="Request failed, Please try again" - OFFLINEMSG="401 Unauthorized" - OKMSG="200 OK" - TIMEOUT_INTERVAL=3.0 - MAX_IRC_BUFFER=50 - SSL_IS_BROKEN=False - CERT_NONE=0 - debug=copy.copy(config.developer) - # Error Codes - ERR_JSONDECODER=101 - ERR_LOGIN_DEFAULT=102 - ERR_INVALID=103 - ERR_TIMEOUT=104 - ERR_NOGEMS=105 - ERR_INVFULL=106 - ERR_OK=200 - # All error code library - ERRNO=[FAILUREMSG, OFFLINEMSG, OKMSG, ERR_LOGIN_DEFAULT, ERR_INVALID, ERR_TIMEOUT, ERR_NOGEMS, ERR_INVFULL, ERR_OK] - # Core musics/sfx - MUSIC_OPENING="sfx/bgm01.mp3" - MUSIC_TOWN="sfx/bgm02.mp3" - # Spheres actions - AP_NONE =False - AP_SPHERE =1 - AP_SKILL =2 - - # Status - ST_TOWN =0 - ST_QUEST =1 - - # Unit flags - UF_NOLVL =1 - UF_NOPART =2 - UF_EXPUP =4 - UF_EVOMAT =8 - UF_SUPEREVO =64 - - # Jobs - Job_Swordsman =1 - Job_Assassin =2 - Job_Mage =3 - Job_Archer =4 - Job_Gunner =5 - - # IRC flags - IRC_AUTH_NONE =0 - IRC_AUTH_USER =1 - IRC_AUTH_NICK =2 - IRC_AUTH_CHAN =3 - - # Smart Print command - def stdout(message): - if debug: - if renpy.android: - if not renpy.is_init_phase(): - renpy.notify(message) - else: - print(message) - renpy.write_log("[DEBUG] %s" % message) - else: - renpy.write_log("[GAME] %s" % message) - return - - # Smart wait - def sdelay(delta=0.02): - try: - renpy.pause(delta, hard=True) - except: - time.sleep(delta) - return - - - # Global classes - # We need to override standard list method. Original by Triptych (stackoverflow) - class dlist(list): - - def __setitem__(self, index, value): - size = len(self) - if index >= size: - self.extend(None for _ in range(size, index + 1)) - - list.__setitem__(self, index, value) - - class ExecuteOnCall(): - def __init__(self, callable, *args, **kwargs): - self.callable = callable - self.args = args - self.kwargs = kwargs - - def __call__(self): - rv = self.callable(*self.args, **self.kwargs) - return rv - - def id(self): - return self.__call__() - - class RetString(): - def __init__(self, string): - self.string = string - - def __call__(self): - return self.string - - def id(self): - return self.__call__() - - # Screen Functions/class - # Override - class SpheresMainMenu(MainMenu): - def __call__(self): - - if not self.get_sensitive(): - return - - if self.confirm: - if config.autosave_on_quit: - renpy.force_autosave() - - layout.yesno_screen(layout.MAIN_MENU, SpheresMainMenu(False)) - else: - # Flush labels/sockets/timers as needed - renpy.call_in_new_context("quit") - # Restart - renpy.full_restart(config.game_main_transition) - - class ExtraImage(renpy.display.im.Image): - """ - Custom image manipulator, based on bink's code, topic 11732 - """ - - def __init__(self, loc, **properties): - """ - @param loc: Where the image really is (get_path already applied) - """ - - super(ExtraImage, self).__init__(loc, **properties) - self.loc = loc - - def load(self, unscaled=False): # W0221 - try: - #page = open(self.loc, "rb") - #pd = page.read() - #picdata = os.tmpfile() - #picdata.write(pd) - #picdata.seek(0) # reset seek position - stdout("Requested to open: %s" % repr(self.loc)) - picdata = open(self.loc, "rb") - stdout("Picdata is open (%s)" % self.loc) - - if unscaled: - surf = renpy.display.pgrender.load_image_unscaled(picdata, self.loc) - else: - surf = renpy.display.pgrender.load_image(picdata, self.loc) - - stdout("Picdata was closed") - picdata.close() - #page.close() - - return surf - except Exception, e: - if renpy.config.missing_image_callback: - im = renpy.config.missing_image_callback(self.loc) - if im is None: - raise e - return im.load() - raise - - def virtpos(posix): - if isinstance(posix, float): - return posix*1024 - else: - return posix/1024.0 - - # File Managment Functions - def get_path(path): - if True or renpy.android: - path=path.replace("/", "_") - #return renpy.loader.get_path(path) - return renpy.config.savedir + "/" + path - else: - return renpy.loader.get_path(path) - - def get_path_if_exists(path): - if True or renpy.android: - path=path.replace("/", "_") - #return renpy.loader.get_path(path) - return renpy.config.savedir + "/" + path - else: - return renpy.loader.transfn(path) - - # URL3 Function - def GAME_UPDATER(): - global tr_load - tr_load=False - - # If no version is provided, we are using default files - # Default files version is "1" (Should never happen) - if (persistent.version is None): - persistent.version=1 - - # Download upstream version - x=requests.get("http://"+HOST+'/version.txt') - try: - ver=int(x.text) - except: - stdout("IMPOSSIBLE TO DETERMINE VERSION") - raise Exception("Could not estabilish a connection to update server:\n%s is not valid." % x.text) # TODO: Show this beautifully? - # TODO: Should we set a "ver"? - - if (int(persistent.version) < ver): - # TODO: Check if the server have SSL support - stdout("Downloading updated game data...") - - # Download quests.json - f=open(get_path("quests.json"), "w") - stdout("Downloading quests.json") - x=requests.get("http://"+HOST+'/quests.json') - f.write(x.text) - f.close() - - # Download units.json - f=open(get_path("units.json"), "w") - stdout("Downloading units.json") - x=requests.get("http://"+HOST+'/units.json') - f.write(x.text) - f.close() - - # Download story.json - f=open(get_path("story.json"), "w") - stdout("Downloading story.json") - x=requests.get("http://"+HOST+'/story.json') - f.write(x.text) - f.close() - - # Download world.json - f=open(get_path("world.json"), "w") - stdout("Downloading world.json") - x=requests.get("http://"+HOST+'/world.json') - f.write(x.text) - f.close() - - # Download bar.json - f=open(get_path("bar.json"), "w") - stdout("Downloading bar.json") - x=requests.get("http://"+HOST+'/bar.json') - f.write(x.text) - f.close() - - # Download summons.json - f=open(get_path("summons.json"), "w") - stdout("Downloading summons.json") - x=requests.get("http://"+HOST+'/summons.json') - f.write(x.text) - f.close() - - persistent.version=ver - stdout("Update complete") - - # Download server news - # Handled by GAME_LOADER - - tr_uptodate=True - tr_load=True - return tr_load - - - - - -############################################################################ -init -1 python: - import socket, time, json - # Android might have special SSL problems - if renpy.android: - try: - import ssl - except ImportError: - SSL_IS_BROKEN=True - stdout("Android SSL misbehavior detected") - import _openssl as ssl - else: - import ssl - print("Using system-wide SSL implementation...") - - # wsock already imported - - stdout("======================= %s %s %s" % (config.name, config.version, persistent.release_name)) - print "[STDBY] Loading Basic functions......." - - # Search for array[?][key]==search in an array of dicts - # Returns the dictionary, or returns ERR_INVALID - def dl_search(array, key, search): - try: - r=(item for item in array if item[key] == search).next() - except: - r=ERR_INVALID - if r is None: - r=ERR_INVALID - stdout("dlsearch: r is None") - return r - - def check_fail(raw): - global debug, FAILUREMSG - - if (debug): - stdout(str(raw)) - if (raw == FAILUREMSG): - return True - return False - - def json_decode(raw): - global ERRNO - - if (check_fail(raw)): - return ERR_LOGIN_DEFAULT - - # TODO Move this to check_fail and rewrite check_fail - # ERR_OFF should be handled top-level no? With full_restart() - if raw in ERRNO: - return raw - - try: - return int(raw) - except: - pass - - # Maybe base 64 - try: - rw=base64.b64decode(raw) - raw=rw - if (debug): - print "base64 decoded" - except: - pass - - # Maybe zlib compressed - try: - rw=zlib.decompress(raw) - raw=rw - if (debug): - print str(raw) - except: - pass - - # Decode JSON - try: - return json.loads(raw) - except: - return ERR_JSONDECODER - - def get_token(): - try: - t=Player['token'] - except: - t="0" #"f528764d624db129b32c21fbca0cb8d6" - return t - - def login(): - global Player - stdout("Login requested for %s" % logindata()) - raw=send_packet("login", logindata()) - - Player=json_decode(raw) - if (Player == ERR_JSONDECODER): - return ERR_JSONDECODER - if (Player == ERR_LOGIN_DEFAULT): - return ERR_LOGIN_DEFAULT - - try: - Player["inv"]=dlist() - except: - pass - return Player["code"] - - def send_packet_now(packet, args="", legacy=False): - global tr_load, tr_val, tr_busy - global ws - - stdout("Sending: %s" % packet) - try: - ws.send(get_token() + ";" + packet + ";" + args) - except: - stdout("Failed to send message!!") - # FIXME set tr_val/tr_load/tr_busy to good values - return False - # TODO: Wait for onmsg - # TODO: ping packets - return True - - def send_packet(packet, args=""): - global tr_load, tr_val, tr_busy - # TODO: if tr_busy already true, wait until it is made false - while tr_busy: - sdelay() - - # Book processing space for ourselves - tr_busy=True - tr_load=False - tr_val=None - - # This is a secret variable which disables threading and queue - # This may cause hangs and other issues. - if persistent.nothreading: - send_packet_now(packet, args) - tr_busy=False - val=tr_val - return val - - timeout=0.0 - renpy.show("spinner", at_list=[truecenter]) - renpy.invoke_in_thread(send_packet_now, packet, args) - while not tr_load: - sdelay() # FIXME: This can cause errors in mobile? - timeout+=0.02 - - if timeout >= TIMEOUT_INTERVAL: - # FIXME: What if a screen is already being displayed? BUG - try: - renpy.call_screen("msgbox", "Error Code: %d\n\nApplication timeout, click to try again" % (ERR_TIMEOUT)) - timeout=0.0 - except: - if not "ping" in packet.lower(): - stdout("WARNING, ILLEGAL PACKET ON SCREEN TIME: %s" % packet) - pass - - renpy.hide("spinner") - val=tr_val - tr_busy=False - del tr_val - - print "value obtained" - if (val is None): - return ERR_INVALID - return val - - def GAME_LOADER(): - global allunitsbase, allunits, allquests, allstory, allworld, alltaverns - global allnews, tr_load - tr_load=False - - # Wait until everything is up to date - while not tr_uptodate: - sdelay() - - # Load unit data - allunitsbase=json.loads(requests.get("http://"+HOST+'/units.json').text) - # f=open(get_path_if_exists("units.json"), "r") - # allunitsbase=json.load(f) - # f.close() - - # Reorder unit data - allunits={} - for j in allunitsbase: - allunits[j["unit_id"]]=j - - # Load summons data - f=open(get_path_if_exists("summons.json"), "r") - allworld=json.load(f) - f.close() - - # Load quest data - f=open(get_path_if_exists("quests.json"), "r") - allquests=json.load(f) - f.close() - - # Load story data - f=open(get_path_if_exists("story.json"), "r") - allstory=json.load(f) - f.close() - - # Load world data - f=open(get_path_if_exists("world.json"), "r") - allworld=json.load(f) - f.close() - - # Load tavern data - f=open(get_path_if_exists("bar.json"), "r") - alltaverns=json.load(f) - f.close() - - # Load server news - try: - allnews=json.loads(requests.get("http://"+HOST+'/news.json', timeout=5.0).text) - except: - allnews=[] - pass - - stdout("[OK] Game data loaded to memory") - tr_load=True - tr_memcheck=True - return tr_load - - - - - -############################################################################ -# This is the JSON data formatter -init python: - import json - def logindata(): - global password - # TODO: Obtain user id based on device - return """{ - "passwd": "%s", - "version": "%s" - }""" % (password, config.version) - - - def recruitdata(t, a): - return """{ - "tavern": %d, - "amount": %d - }""" % (t, a) - - - def questdata(q, p): - return """{ - "quest_id": %d, - "party_id": %d - }""" % (q, p) - - - def battledata(u, s): - return """{ - "unit": %s, - "sphere": %s - }""" % (json.dumps(u), json.dumps(s)) - - def card_composite(cid, path): - # We need to try to get rarity - try: - r=allunits[int(cid)]["rare"] - except: - r=4 # FIXME - - # We need to try to get the element - try: - e=allunits[int(cid)]["attribute"] - print str(e) - except: - e=0 - - return Composite( - (640, 960), - (0, 0), "gfx/cards/bg.png", - (0, 0), path, - (0, 0), "gfx/cards/"+str(r)+".png", - (0, 0), "gfx/cards/ele/"+str(e)+".png") - # size,) - - # TODO: square_ and gfx/square/units - # Converts regular image filepaths to displayables names and vice-versa - def img_regex(name, reverse=False): - if not reverse: - # Extension is ommited, add them yourself! - if name.startswith("unit_"): - return "gfx/units/%s" % name.replace("unit_") - elif name.startswith("mob_"): - return "gfx/mobs/%s" % name.replace("mob_") - elif name.startswith("dialog_"): - return "gfx/dialog/%s" % name.replace("dialog_") - elif name.startswith("bg "): - return "gfx/bg/%s" % name.replace("bg ") - elif name.startswith("summon_"): - return "gfx/summons/%s" % name.replace("summon_") - else: - print("ERROR: Not a regular filename") - return name - else: - if name.startswith("gfx/units/"): - return "unit_%s" % name - elif name.startswith("gfx/mobs/"): - return "mob_%s" % name - elif name.startswith("gfx/dialog/"): - return "dialog_%s" % name - elif name.startswith("gfx/bg/"): - return "bg %s" % name - elif name.startswith("gfx/summons/"): - return "summon_%s" % name - else: - print("ERROR: Not a regular display name") - return name - - # Loads a sound called VAL - def get_sfx(val, ext): - # Search for the sound - show_img(val, False, ext=ext) - - valb=dl_search(persistent.allfiles, 0, val)[1] - if valb == ERR_INVALID: - print ("Invalid Sound: %s (%s)" % (path, val)) - return "sfx/regnum.mp3" # FIXME - return valb - - # Load sprite images: "unit" - for file in renpy.list_files(): - fn=file.replace('gfx/units/','').replace('/', ' ').replace('.png','') - if file.startswith('gfx/units/'): - if file.endswith('.png') or file.endswith('.webp'): - name = "unit_"+fn - #renpy.image(name, Image(file, yanchor=1.0)) - renpy.image(name, card_composite(fn, file)) - - dl=dl_search(persistent.allfiles, 0, name) - if dl is not ERR_INVALID: - persistent.allfiles.append((name, file)) - allfiles.append(name) - continue - continue - - # Load sprite images: "mob" - for file in renpy.list_files(): - fn=file.replace('gfx/mobs/','').replace('/', ' ').replace('.png','') - if file.startswith('gfx/mobs/'): - if file.endswith('.png') or file.endswith('.webp'): - name = "mob_"+fn - renpy.image(name, Image(file, yanchor=1.0)) - if not name in persistent.allfiles: - persistent.allfiles.append((name, file)) - allfiles.append(name) - continue - continue - - - # Load sprite images: "dialog" - for file in renpy.list_files(): - fn=file.replace('gfx/dialog/','').replace('/', ' ').replace('.png','').replace('.webp','') - if file.startswith('gfx/dialog/'): - if file.endswith('.png') or file.endswith('.webp'): - name = "dialog_"+fn - renpy.image(name, Image(file, yanchor=1.0)) - dl=dl_search(persistent.allfiles, 0, name) - if dl is not ERR_INVALID: - persistent.allfiles.append((name, file)) - allfiles.append(name) - continue - continue - - - # Load background images: "bg" - for file in renpy.list_files(): - fn=file.replace('gfx/bg/','').replace('/', ' ').replace('.png','').replace('.webp','') - if file.startswith('gfx/bg/'): - if file.endswith('.png') or file.endswith('.webp'): - name = "bg "+fn - renpy.image(name, Frame(file, 0, 0)) - dl=dl_search(persistent.allfiles, 0, name) - if dl is not ERR_INVALID: - persistent.allfiles.append((name, file)) - allfiles.append(name) - continue - continue - - - # Load summon images: "summon" - for file in renpy.list_files(): - fn=file.replace('gfx/summons/','').replace('/', ' ').replace('.png','').replace('.webp','') - if file.startswith('gfx/summons/'): - if file.endswith('.png') or file.endswith('.webp'): - name = "summon_"+fn - renpy.image(name, Image(file, yanchor=1.0)) - dl=dl_search(persistent.allfiles, 0, name) - if dl is not ERR_INVALID: - persistent.allfiles.append((name, file)) - allfiles.append(name) - continue - continue - - def star_write(am): - i, st = 0, "" - while i < am: - i+=1 - st+="★" - return st - - - # Overrides renpy.image() method - def new_img(name, where): - # d: Downloaded path - if not isinstance(name, tuple): - name = tuple(name.split()) - - #d = renpy.renpy.easy.displayable(where) - if renpy.android: - d=ExtraImage(where) - else: - d = renpy.renpy.easy.displayable(where) - renpy.renpy.display.image.register_image(name, d) - return - - # Retrieves Ren'Py displayable name associated to PATH - def get_img(path): - # Search for the image name - val=img_regex(path, True) - show_img(val, False) - - valb=dl_search(persistent.allfiles, 0, val)[1] - if valb == ERR_INVALID: - print ("Invalid Image: %s (%s)" % (path, val)) - return "gfx/spinner.png" - return valb - - # Overrides renpy.show() and renpy.image() methods - # Missing: transient=False, munge_name=True - def show_img(img, show=True, at_list=[ ], tag=None, zorder=None, behind=[ ], atl=None, what=None, layer=None, ext=".png"): - global tr_loading - # Image exists, display it - if img in allfiles: - if show: - renpy.show(img, at_list=at_list, tag=tag, zorder=zorder, behind=behind, atl=atl, what=what, layer=layer) - return - - # Have we downloaded this image previously? - path=dl_search(persistent.allfiles, 0, img) - print str(path) - - # Image doesn't exists, we must download it - while (path == ERR_INVALID): - tr_loading=True - # Latest version converts these formats to WebP - if ext in [".png", ".jpg", ".jpeg"]: - ext=".webp" - # Otherwise, preserve extension. - if renpy.android: - addr="extra_%s%s" % (img.replace(" ", "_"), ext) - else: - addr="extra/%s%s" % (img.replace(" ", "_"), ext) - - f=open(get_path(addr), "w") - stdout("Downloading additional file: %s" % img.replace(" ", "_")) - x=requests.get("http://%s:%d/%s?token=%s" % (HOST, UPDP, img.replace(" ", "_"), get_token())) # , timeout=8.0 → Need to handle sudden death - if x.status_code == 200: - f.write(x.content) - f.close() - # Android needs paths to be saved by full - if renpy.android: - addr=get_path(addr) - path=((img, addr)) - persistent.allfiles.append(path) - else: - try: - retry=renpy.call_screen("confirm", "Error downloading file.\nError Code: %d\n\nRetry?" % x.status_code, Return(True), Return(False)) - if not retry: - if tag is None: - tag=img - path=None - if show: - renpy.show("spinner", at_list=at_list, tag=tag, zorder=zorder, behind=behind, atl=atl, what=what, layer=layer) # TODO Show error - return - # TODO: “Retry?” - except: - print("Failed, trying again") - - # Image exists, but wasn't loaded yet - if (path != ERR_INVALID and path is not None): - print "Detected not loaded image" - # Valid Image Extensions: PNG, JPG, JPEG, GIF, WEBP - if ext in [".png", ".jpg", ".jpeg", ".gif", ".webp"]: - # Maybe it is an unit - if img.startswith("unit_"): - new_img(img, card_composite(img.replace("unit_", ""), path[1])) - else: - new_img(img, path[1]) - - stdout("registered image: "+path[1]) - allfiles.append(img) - if show: - renpy.show(img, at_list=at_list, tag=tag, zorder=zorder, behind=behind, atl=atl, what=what, layer=layer) - tr_loading=False - return - - # Something went wrong - stdout("show_img reached abnormal ending") - return - - ########################################################## - # Other Music - MUSIC_BATTLE=RetString("sfx/bgm03.mp3") - MUSIC_BOSS=RetString("sfx/bgm04.mp3") - MUSIC_PARTY=RetString("sfx/bgm02.mp3") - #MUSIC_PARTY=ExecuteOnCall(get_sfx, "sfx_bgm05", ".mp3")#"sfx/bgm05.mp3" - MUSIC_VICTORY=RetString("sfx/bgm06.mp3") - MUSIC_WORLDMAP=RetString("sfx/bgm02.mp3") - #MUSIC_WORLDMAP=ExecuteOnCall(get_sfx, "sfx_bgm07", ".mp3")#"sfx/bgm07.mp3" - MUSIC_PROLOGUE01=RetString("sfx/regnum.mp3") - MUSIC_PROLOGUE02=RetString("sfx/prologue.mp3") - MUSIC_PROLOGUE03=RetString("sfx/shining.mp3") diff --git a/game/misc.rpy b/game/misc.rpy index 0313cc5..13e6756 100644 --- a/game/misc.rpy +++ b/game/misc.rpy @@ -227,7 +227,10 @@ label clear_cache: $renpy.full_restart() jump start -image spinner: +image spinner = "gfx/spinner.png" + +init 5 python: + """ "gfx/spinner.png" rotate 30.0 pause 0.05 @@ -265,4 +268,6 @@ image spinner: #rotate 360.0 pause 0.05 repeat + """ + pass diff --git a/game/script.rpy b/game/script.rpy index 6009b2d..b224474 100644 --- a/game/script.rpy +++ b/game/script.rpy @@ -21,59 +21,6 @@ by sending an email to {a=mailto:dmca@tmw2.org}dmca@tmw2.org{/a}.{/color}\n\ No warranties.{/b}{fast}" return -init python: - # FIXME: Drop dead if session_id mismatches (new "token") - # Same as ondata? - def onmsg(self, message): - global tr_load, tr_val, tr_busy - stdout("Server : " + message) - - # Inform whatever is waiting for us that a reply was obtained - tr_load=True - tr_val=str(received) - # We do not clean tr_busy here - return - - def ondata(self, msg, dtype, cont): - #print("data received") - """ - on_data: callback object which is called when a message received. - This is called before on_message or on_cont_message, - and then on_message or on_cont_message is called. - on_data has 4 argument. - The 1st argument is this class object. - The 2nd argument is utf-8 string which we get from the server. - The 3rd argument is data type. ABNF.OPCODE_TEXT or ABNF.OPCODE_BINARY will be came. - The 4th argument is continue flag. if 0, the data continue - """ - #renpy.notify(msg) - return - - def onopen(self): - global tr_busy, tr_load, tr_val, HOST - print("Opening connection to %s" % HOST) - tr_load=True - tr_val="" - tr_busy=False - self.send("PING") - - def onerror(self, err): - stdout("ERROR RECEIVED") - stdout("More details: %s" % repr(err)) - stdout("An error happened: %s" % str(err)) - # FIXME: If such error happen, the game never dies - # This is a huge problem o.o - while True: - renpy.call_screen("msgbox", - "An unrecoverable error happened.\nPress OK to return to main menu screen.") - #raise KeyboardException(str(err)) - #raise err - # FIXME: Not working, and the loop also does not work - # Maybe because it causes an Exception? - # I wonder if I can/should run this in a non-daemon thread? - #renpy.quit(relaunch=True, status=1) - sdelay(1.0) - return 1 # Inform you are now ingame label start: @@ -83,18 +30,11 @@ label start: show spinner at truecenter # Initial configuration - python: - tr_busy=True - tr_val="" - tr_load=False - tr_uptodate=False - tr_memcheck=False - session_id=uuid.uuid4().hex + call prestart #"We shall now begin testing a websocket" python: # FIXME: Broken code - ws=persistent.ws if (ws is not None): stdout("Socket exists, trying to reuse connection...") try: @@ -128,8 +68,8 @@ label start: renpy.invoke_in_thread(ws.run_forever) except: # Enter in infinite loop, this will never resolve - tr_load=False stdout("Unrecoverable error, the program is dead.") + tr_load=False while not tr_load: sdelay() @@ -143,8 +83,6 @@ label start: while not tr_load: sdelay() - hide spinner - # Open game scene bg town with Dissolve(0.3) pause 0.1 @@ -152,6 +90,16 @@ label start: $ password=persistent.password $ email="" + if config.developer: + menu: + "tr_load is [tr_load]\ntr_busy is [tr_busy]" + "Send ping": + $ send_packet_now('PING') + "Send formatted packet": + $ send_packet_now('TOKEN;PACKET;') + "Send invalid packet": + $ send_packet_now('INVALID PACKET') + # Check if we need to register if persistent.password is None: call screen welcome @@ -219,7 +167,6 @@ label login: python: try: message=int(message) - import copy dlcode=copy.copy(message) except: print "AN ERROR HAPPENED AT LOGIN TIME" |