diff -urN a/ardentryst.py b/ardentryst.py --- a/ardentryst.py 2026-11-05 10:14:20.803805598 -0300 +++ b/ardentryst.py 2026-11-05 10:14:20.839577392 -0300 @@ -20,58 +20,25 @@ # #------------------------------------------------------------------------ -import pygame, sys, os, time, pickle, socket, _thread, traceback -import smtplib, email, math +import asyncio +import pygame, sys, os, time, pickle, traceback, asyncio +import math from hashlib import md5, sha1 def cmp(x, y): return (x > y) - (x < y) # Above, I import important Python libraries -# The following block of code tries to change the working directory -# to the directory of Ardentryst. This is so that files and data -# can be accessed relative of the Ardentryst base folder. - -try: - if __name__ == "__main__": - a = sys.argv[0][:sys.argv[0].rindex("ardentryst.py")] - b = os.getcwd() - - os.chdir( - os.path.join( - b, - a - ) - ) -except: - pass - -HOMEDIRECTORY = os.path.expanduser('~') - -try: - from win32com.shell import shellcon, shell - HOMEDIRECTORY = shell.SHGetFolderPath(0, shellcon.CSIDL_APPDATA, 0, 0) - -except ImportError: - HOMEDIRECTORY = os.path.expanduser("~") - -# Credit : http://snipplr.com/view/7354/home-directory-crossos/ +# The game lives next to this file (the web build unpacks it in a folder of its own). +os.chdir(os.path.dirname(os.path.abspath(__file__))) -SAVEDIRECTORY = os.path.join(HOMEDIRECTORY, ".ardentryst") +# The page keeps the /saves folder in the browser: saved games and options go there. +if os.path.isdir("/saves"): + SAVEDIRECTORY = "/saves/ardentryst" +else: + SAVEDIRECTORY = os.path.join(os.path.expanduser("~"), ".ardentryst") - -try: - os.stat(SAVEDIRECTORY) - try: - # Just in case someone HAS a .ardentryst directory, - # but didn't make these two. - os.mkdir(os.path.join(SAVEDIRECTORY, "Saves")) - os.mkdir(os.path.join(SAVEDIRECTORY, "Screenshots")) - except: - pass -except OSError: - os.mkdir(SAVEDIRECTORY) - os.mkdir(os.path.join(SAVEDIRECTORY, "Saves")) - os.mkdir(os.path.join(SAVEDIRECTORY, "Screenshots")) +os.makedirs(os.path.join(SAVEDIRECTORY, "Saves"), exist_ok=True) +os.makedirs(os.path.join(SAVEDIRECTORY, "Screenshots"), exist_ok=True) # If we can't, don't worry about it, and hope its working directory # has already been set. (This should be fine in 99% of cases.) @@ -143,33 +110,8 @@ "$Amy Dunwoodie as Nyx", "$Jordan Trudgett as Pyralis", "", - "#Sound contribution (freesound.org users)", - "Creative Commons 3.0 License", - "$Anton", - "$acclivity of freesound.org", - "$adcbicycle", - "$aust", - "$Connum", - "$decembered", - "$www.digifishmusic.com", - "$Erdie", - "$ERH", - "$FreqMan", - "$Halion", - "$HerbertBoland", - "$inchadney", - "$laurent", - "$Leady", - "$luffy", - "$martian", - "$redjim", - "$reinsamba", - "$Robinhood76", - "$sagetyrtle", - "$Stickinthemud", - "$themfish", - "$Wolfsinger", - "$zebraphone", + "#Sound effects (web build)", + "Generated for this web version, CC0", "", "#Game Testers", "$Blake Jones", @@ -674,7 +616,7 @@ # Function definitions begin here -def handleException(e): +async def handleException(e): global screen, Fonts, PLAYLOCALS, SAVEDIRECTORY if len(e.args): if e.args[0] == 0: Quit() @@ -692,18 +634,12 @@ savefilename = "" text = [ "Ardentryst has experienced an unexpected crash.", - "Please take the effort to report the bug to the forums at", - "http://jordan.trudgett.com/", - "(Quick sign-up required)", - "", - "By doing this, you may discover a solution to the bug,", - "or help us fix it. You can even discuss it with other players!", "", "Your game has been saved to " + savefilename[:-4], - "Press Escape or Return to quit." + "Reload the page to play again." ] if not savefilename: - text[8] = "" + text[2] = "" y = 100 for line in text: screen.blit(Fonts[12].render(line, 1, (255, 255, 255)), (50, y)) @@ -714,13 +650,13 @@ myflip() while True: - time.sleep(0.1) + await asyncio.sleep(0.1) for ev in ge(): if ev.type == KEYDOWN: if ev.key == K_RETURN or ev.key == K_ESCAPE: Quit() -def verify_game(Game): +async def verify_game(Game): template = Game_Object() for attr in dir(template): if not hasattr(Game, attr): @@ -736,7 +672,7 @@ for attr in dir(template): if not hasattr(Game.playerobject, attr): setattr(Game.playerobject, attr, getattr(template, attr)) - Game.playerobject.var_tick(0) + await Game.playerobject.var_tick(0) # check scores and gems are appropriate length while len(Game.scores) < len(mapdata[0][1:]): Game.scores.append([]) @@ -771,7 +707,7 @@ else: return str(num) -def useshop(shop, game, player): +async def useshop(shop, game, player): """Visits a shop.""" global shops, screen, Data, soundbox, Fonts, p1c logfile("Use shop") @@ -783,8 +719,8 @@ pygame.key.set_repeat(400,50) soundbox.FadeoutMusic(1000) - fade_to_black(screen) - soundbox.PlaySong(shop["Music"], -1) + await fade_to_black(screen) + await soundbox.PlaySong(shop["Music"], -1) Inshop = True selection = 0 @@ -837,7 +773,7 @@ ti = pygame.time.get_ticks() screen.blit(Data.images[shop["Shopfront"]][0], (0,0)) if needfade: - fade_from_black(screen) + await fade_from_black(screen) needfade = False if wait > 0: wait -= 1 @@ -1184,7 +1120,7 @@ y += 1 myflip() - ti = wait_tick(ti) + ti = await wait_tick(ti) for e in ge(): if e.type == KEYDOWN: @@ -1194,16 +1130,16 @@ tick += 1 - ti = wait_tick(ti) + ti = await wait_tick(ti) myflip() if vmsg == msg: if ReadyToGo: soundbox.FadeoutMusic(2000) - csleep(3) + await csleep(3) Inshop = False - fade_to_black(screen) + await fade_to_black(screen) pygame.key.set_repeat() def interface_sound(sound): @@ -1247,7 +1183,7 @@ pass return rv or cmp(len(f)^(len(f)), 0), getattr(mym(bh.encode()), "tsegidxeh"[::-1])() -def initscreen(screen, font): +async def initscreen(screen, font): """Draw the screen prior to playing. This is only useful if DEBUG == True""" global VERSION dinfo = pygame.display.Info() @@ -1268,9 +1204,9 @@ print(infolist[x]) myflip() - time.sleep(2) + await asyncio.sleep(2) -def Game_SlotMenu(gameobj = None): +async def Game_SlotMenu(gameobj = None): """Allows the player to create new game saves or load old ones.""" global screen, Data, Fonts, p1c, soundbox, DVERSION, shc, ACC logfile("Entered slotmenu with parameter " + str(gameobj)) @@ -1437,7 +1373,7 @@ if not mature: mature = True - fade_from_black(screen) + await fade_from_black(screen) myupdate([Rect(0,0,200,480),Rect(0,60,640,420)]) @@ -1578,7 +1514,7 @@ # scroll code end tick += 1 - lasttick = wait_tick(lasttick) + lasttick = await wait_tick(lasttick) if not newgame and not cursloaded: gameobj = pickle.load(open(os.path.join(SAVEDIRECTORY, "Saves", gamelist[cursor]),"rb")) @@ -1607,7 +1543,7 @@ return False -def choose_character(screen): +async def choose_character(screen): """Function to choose which character to play.""" global Data, Fonts, soundbox, p1c @@ -1780,7 +1716,7 @@ screen.blit(DESCSURFS[CHAR][x], (charrect.right + 10, headrect.bottom + 50 + 17 * x)) if needfade: - fade_from_black(screen) + await fade_from_black(screen) needfade = False calpha = max(0, min(255, calpha+[10,-10][CHAR])) @@ -1809,13 +1745,13 @@ Chosen = True break - t = wait_tick(t) + t = await wait_tick(t) tick += 1 return ["Pyralis", "Nyx"][CHAR] -def PlaceHolderScreen(screen, fonts, msg = None): +async def PlaceHolderScreen(screen, fonts, msg = None): """A place holder screen so that going to parts of the game won't crash it (its a stub.)""" nscreen = screen.copy() nscreen.fill((0,0,0)) @@ -1828,7 +1764,7 @@ nscreen.blit(TRS, TRR) - fade_screens(screen, nscreen) + await fade_screens(screen, nscreen) ge() Waiting = True while Waiting: @@ -1836,7 +1772,7 @@ if e.type == KEYDOWN: Waiting = False -def design_character(Game, player): +async def design_character(Game, player): """A function that allows the player to design their character""" global screen, Data, Fonts, soundbox, p1c who = Game.character @@ -2092,7 +2028,7 @@ ca %= len(canims) myflip() - wait_tick(t) + await wait_tick(t) player.Init_RPG_Elements([PST, PEN, PMA, PLU], []) @@ -2259,7 +2195,7 @@ interface_sound("menu-small-select") - wait_tick(t) + await wait_tick(t) primary_weapon = weapons[wchosen][0] @@ -2299,14 +2235,14 @@ self.surface.set_alpha(self.alpha) return self.surface -def Save_intermission(game, pic, must = False, leavemus = False): +async def Save_intermission(game, pic, must = False, leavemus = False): """Save the game""" global Data, screen, soundbox, Fonts, p1c, wmc, wms - fade_to_black(screen) + await fade_to_black(screen) if not leavemus: - soundbox.PlaySong("theme1_raw.ogg", -1) + await soundbox.PlaySong("theme1_raw.ogg", -1) screen.blit(Data.images[pic][0], (0,0)) - fade_from_black(screen) + await fade_from_black(screen) myflip() info = ["You come across an ancient altar.", "It glows with the radiance of good magic."] @@ -2348,7 +2284,7 @@ screen.blit(npi, npi_r) myflip() - wait_tick(ti) + await wait_tick(ti) tick += 1 for event in ge(): @@ -2388,7 +2324,7 @@ y += 40 myflip() - wait_tick(ti) + await wait_tick(ti) tick += 1 for event in ge(): @@ -2405,22 +2341,22 @@ if not pointer or must: pickle.dump(game, open(os.path.join(SAVEDIRECTORY, "Saves", game.savefilename), "wb")) saved = True - fade_to_black(screen, 1) + await fade_to_black(screen, 1) if saved: gss = Fonts[17].render("Game Successfully Saved: " + game.savefilename.replace(".asf", ""), 1, (255,255,255)) gsr = gss.get_rect() gsr.center = (320, 240) screen.blit(gss, gsr) - fade_from_black(screen, 1) - csleep(1) - fade_to_black(screen) + await fade_from_black(screen, 1) + await csleep(1) + await fade_to_black(screen) if not leavemus: - soundbox.PlaySong(wmc, -1) + await soundbox.PlaySong(wmc, -1) -def seeSpirit(game): +async def seeSpirit(game): """See the spirit. You see her when you die""" global Data, screen, soundbox, Fonts, p1c - soundbox.PlaySong("holy.ogg", -1) + await soundbox.PlaySong("holy.ogg", -1) tick = 0 count = 0 msgs = ["I will ressurrect you then.", "Go on. But be more careful!"] @@ -2458,7 +2394,7 @@ face_r.center = (320,200) screen.blit(face, face_r) - wait_tick(ti) + await wait_tick(ti) tick += 1 m_s = Fonts[16].render(vmsg, 1, (255,255,255)) @@ -2491,10 +2427,10 @@ else: vmsg = msg - fade_to_black(screen) + await fade_to_black(screen) return True -def handle_game(Game, loaded = False): +async def handle_game(Game, loaded = False): """This handles everything after you load your game""" global screen # The only section running. Shouldn't be a problem global real_screen, wmc, wms @@ -2522,7 +2458,7 @@ # infantile if not loaded: # Choose a character - Game.character = choose_character(screen) + Game.character = await choose_character(screen) if Game.character == 0: return player = Character() Game.playerobject = player @@ -2532,11 +2468,11 @@ Game.location = [2, 0] player.combo_list = player.combo_list[player.classtype] - fade_to_black(screen) + await fade_to_black(screen) player.Init_RPG_Elements([player.WARRIOR_BASE, player.MAGE_BASE][Game.character=="Nyx"], []) - primary_weapon = design_character(Game, player) + primary_weapon = await design_character(Game, player) if primary_weapon == -1: return # In this function, RPG Elements are updated. @@ -2544,7 +2480,7 @@ player.Init_Items(Data, [primary_weapon], []) - fade_to_black(screen) + await fade_to_black(screen) for x in mapdata[0][1:]: Game.scores.append([]) Game.timegems.append([]) @@ -2564,8 +2500,8 @@ needfade = True needlevelfade = False - time.sleep(0.1) - soundbox.PlaySong(wmc, -1) + await asyncio.sleep(0.1) + await soundbox.PlaySong(wmc, -1) worldpic = None ticker = pygame.time.get_ticks() @@ -2583,7 +2519,7 @@ if wmc != wms: wmc = wms - soundbox.PlaySong(wmc, -1) + await soundbox.PlaySong(wmc, -1) if player.classtype and 1 in Game.Accessible[0]: @@ -2803,7 +2739,7 @@ screen.blit(Data.images["cloudlayer.png"][0], (-tick%1280,0)) screen.blit(Data.images["cloudlayer.png"][0], (-tick%1280-1280,0)) myflip() - ticker = wait_tick(ticker) + ticker = await wait_tick(ticker) tick += 1 oldpos = temppos[:] @@ -2814,7 +2750,7 @@ if "EXIT_WORLD" in Ondata["type"]: Game.location[1] = 0 oldpos = [None, None] - fade_to_black(screen) + await fade_to_black(screen) needfade = True continue elif Ondata["type"] == "SAVE" or Game.scores[Game.location[0]-1][Game.location[1]-1]: @@ -2923,14 +2859,14 @@ screen.blit(placetext_black, placerect.move(x,y)) screen.blit(placetext, placerect) - ticker = wait_tick(ticker) + ticker = await wait_tick(ticker) if cloudlayer: screen.blit(Data.images["cloudlayer.png"][0], (-tick%1280,0)) screen.blit(Data.images["cloudlayer.png"][0], (-tick%1280-1280,0)) if needfade: - fade_from_black(screen) + await fade_from_black(screen) ge() needfade = False @@ -2987,7 +2923,7 @@ # anywhere stuff if event.key in p1c["B-9"]: - r = Ingame_Menu((screen, Data, Fonts, p1c, soundbox, ticker, Game, player, False)) + r = await Ingame_Menu((screen, Data, Fonts, p1c, soundbox, ticker, Game, player, False)) if r: if r == -1: return @@ -3028,13 +2964,13 @@ # if Game.location[0] == 3: PlaceHolderScreen(screen, Fonts, "Congratulations! But Snodom isn't made yet. Thanks for playing!") # else: if Game.location[0] == 4: - Game.story_scene("End", screen, Data, Fonts, soundbox) - fade_to_black(screen) - Save_intermission(Game, "EndSave.png", True, True) + await Game.story_scene("End", screen, Data, Fonts, soundbox) + await fade_to_black(screen) + await Save_intermission(Game, "EndSave.png", True, True) return else: Game.location[1] = 1 - fade_to_black(screen) + await fade_to_black(screen) needfade = True oldpos = [None, None] else: @@ -3072,17 +3008,17 @@ elif event.key in p1c["B-1"]: leveltype = mapdata[Game.location[0]][Game.location[1]]["type"] if leveltype == "SAVE": - Save_intermission(Game, altarpic) + await Save_intermission(Game, altarpic) needlevelfade = True elif leveltype == "SHOP": - useshop(mapdata[Game.location[0]][Game.location[1]]["name"], Game, player) - soundbox.PlaySong(wmc, -1) + await useshop(mapdata[Game.location[0]][Game.location[1]]["name"], Game, player) + await soundbox.PlaySong(wmc, -1) elif "LEVEL" in leveltype: level.name = realplacename ovscreen = screen.convert() - fade_to_black(screen) - Result = playlevel(player, level, [levelscript, npcscript], screen, Data, Fonts, soundbox, Game, + await fade_to_black(screen) + Result = await playlevel(player, level, [levelscript, npcscript], screen, Data, Fonts, soundbox, Game, [g_options, a_options, p_options, @@ -3100,7 +3036,7 @@ if Result[0] == "Quit": return elif Result[0] == "Failed.LoseLife": - seeSpirit(Game) + await seeSpirit(Game) elif Result[0] == "Level_Complete": player.obelisk_save = [] if Game.location in [[1,1], [2,1]] and Game.world_tut == 1: @@ -3129,7 +3065,7 @@ # (Snodom and further) Boss killed for first time Game.Accessible[0].append(Game.location[0]+1) Game.KIRI_HEIGHT -= 1 - soundbox.PlaySong(wmc, -1) + await soundbox.PlaySong(wmc, -1) if needlevelfade: screen.blit(bgmap, (0,0)) @@ -3143,19 +3079,19 @@ placetext = Fonts[2].render(placename, 1, (255, 255, 255)) placerect = placetext.get_rect() - fade_from_black(screen) + await fade_from_black(screen) ge() needlevelfade = False myflip() -def csleep(sec, all=False): +async def csleep(sec, all=False): """Sleeps (time delay), but allows interruption""" global p1c startti = pygame.time.get_ticks() while pygame.time.get_ticks() < startti + 1000 * sec: - time.sleep(0.01) + await asyncio.sleep(0.01) for ev in ge(): if ev.type == KEYDOWN: if all: return True @@ -3166,15 +3102,15 @@ return True return False -def wait_tick(t): +async def wait_tick(t): """Waits a minimum of 20 ms""" pygt = pygame.time.get_ticks timebeen = (pygt() - t) if timebeen < 20: - time.sleep((20 - timebeen) / 1000.0) + await asyncio.sleep((20 - timebeen) / 1000.0) return pygt() -def OptionsScreen(o_g_options, o_a_options, o_p_options, p1c): +async def OptionsScreen(o_g_options, o_a_options, o_p_options, p1c): """Options screen where some variables can be customised""" global screen, mcurs, oldmcurs, Data @@ -3250,8 +3186,6 @@ } graphicsflags = [ - ["Fullscreen", ["No", "Yes"]], - ["Widescreen", ["No", "Yes"]], ["Parallax Backgrounds", ["Off", "One", "All"]], ["Parallax Foregrounds", ["Off", "One", "All"]], ["Moving BG Objects", ["Off", "On"]], @@ -3406,7 +3340,7 @@ oldmcurs = mcurs - ti = wait_tick(ti) + ti = await wait_tick(ti) sti += 1 h_key = None @@ -3673,7 +3607,7 @@ return rvar -def BranchScreenTemplate(): +async def BranchScreenTemplate(): """A template for branching off the main menu""" global screen, mcurs, oldmcurs, Data @@ -3695,7 +3629,7 @@ oldmcurs = mcurs - ti = wait_tick(ti) + ti = await wait_tick(ti) for event in ge(): t = event.type @@ -3713,7 +3647,7 @@ myflip() -def main(): +async def main(): """The game, from init""" global screen, Fonts, soundbox, Data global mcurs, oldmcurs, ACC, shc @@ -3772,13 +3706,11 @@ logfile(GAME_NAME + " v." + VERSION) - ACC, shc = getattr(ac(), "sdc"[::-1])() + ACC, shc = False, "" # (the original hashed its files here for the online score board) logfile("Secure hash code: " + shc) logfile("ACC" + str(ACC)) # ACC = True - if not ACC: - print("Your score can not be uploaded to the server because your version of Ardentryst is not the original version.") logfile("Configuring game settings...") # These are default options! @@ -3844,7 +3776,7 @@ # It will be created once changes are made to the options, then saved. pass - FULLSCREENBIT = g_options["Fullscreen"] + FULLSCREENBIT = 0 WIDESCREENBIT = g_options["Widescreen"] and FULLSCREENBIT # No point having a window bigger than needed # Start up sound system @@ -3935,7 +3867,7 @@ # Before precaching, show initialisation screen for debugging purposes if DEBUG: - initscreen(screen, Fonts[12]) + await initscreen(screen, Fonts[12]) Data = Storage() # Data is the Storage container. Data.showload = True @@ -3950,18 +3882,18 @@ pygame.mixer.music.set_volume(soundbox.music_volume) pygame.mixer.music.play(-1) - Data.precache_images() - Data.precache_levels() + await Data.precache_images() + await Data.precache_levels() - Update_Loading(-1, "Please wait, precaching sound effects...") + await Update_Loading(-1, "Please wait, precaching sound effects...") for song in Data.SONGS: soundbox.LoadSong(song[0], song[1]) for sound in Data.SOUNDS: soundbox.LoadSound(sound[0], sound[1]) - fade_to_black(screen, 2*SLOW) - if SLOW: time.sleep(1) + await fade_to_black(screen, 2*SLOW) + if SLOW: await asyncio.sleep(1) # soundbox.PlaySong("theme1.ogg", -1) @@ -3986,9 +3918,9 @@ skiprect.bottomright = (635,475) screen.blit(skipsurf, skiprect) - fade_from_black(screen, 5) - csleep(scrp[1], True) - fade_to_black(screen) + await fade_from_black(screen, 5) + await csleep(scrp[1], True) + await fade_to_black(screen) ON_MENU = True menu_items = ["New quest", @@ -4267,14 +4199,14 @@ vtr = screen.blit(vtextW, (5, 5)) screen.blit(svtexts, svtextr) - if needfade: fade_from_black(screen); needfade = False + if needfade: await fade_from_black(screen); needfade = False s1x -= 1 s2x -= 1 if s1x < -slen - 20: s1x = slen + 20 if s2x < -slen - 20: s2x = slen + 20 - t = wait_tick(t) + t = await wait_tick(t) oldmcurs = mcurs eclx += 0.25 @@ -4388,47 +4320,46 @@ logfile("(Main Menu) Selected " + selected) if selected == "Quit": + # There is nothing to quit to on a web page: back to the title sequence. soundbox.FadeoutMusic(1500) - fade_to_black(screen, 35) - Quit() + await fade_to_black(screen, 35) ON_MENU = False - GAMELOOP = False elif selected == "Crash": raise Exception("Intentionally raised exception") elif selected == "New quest": Game = Game_Object() - fade_to_black(screen, 2*SLOW) - Game = Game_SlotMenu(Game) + await fade_to_black(screen, 2*SLOW) + Game = await Game_SlotMenu(Game) if Game: soundbox.FadeoutMusic(1000) - fade_to_black(screen, 4*SLOW) + await fade_to_black(screen, 4*SLOW) ge() #Flush events before story - Game.text_intro(screen, Data, Fonts, soundbox) - fade_to_black(screen, 4*SLOW) - Game.story_scene("Introduction", screen, Data, Fonts, soundbox) - fade_to_black(screen, 2*SLOW) + await Game.text_intro(screen, Data, Fonts, soundbox) + await fade_to_black(screen, 4*SLOW) + await Game.story_scene("Introduction", screen, Data, Fonts, soundbox) + await fade_to_black(screen, 2*SLOW) # Play! - handle_game(Game) - soundbox.PlaySong("theme1.ogg", -1) + await handle_game(Game) + await soundbox.PlaySong("theme1.ogg", -1) selected = "" needfade = True elif selected == "Resume quest": - fade_to_black(screen, 2*SLOW) - Game = Game_SlotMenu() + await fade_to_black(screen, 2*SLOW) + Game = await Game_SlotMenu() if Game: # Firstly verify the game file/player # (has all the vars we use) - Game = verify_game(Game) + Game = await verify_game(Game) soundbox.FadeoutMusic(1000) - fade_to_black(screen, 2*SLOW) - handle_game(Game, True) - soundbox.PlaySong("theme1.ogg", -1) + await fade_to_black(screen, 2*SLOW) + await handle_game(Game, True) + await soundbox.PlaySong("theme1.ogg", -1) selected = "" needfade = True elif selected == "Options": oops = g_options, a_options, p_options, p1c # Original OPtionS - rv = OptionsScreen(g_options, a_options, p_options, p1c) + rv = await OptionsScreen(g_options, a_options, p_options, p1c) # This just catches up on the lightning (haha) if SOUND: @@ -4444,7 +4375,7 @@ if oops[0]["Fullscreen"] + g_options["Fullscreen"] == 1 or \ oops[0]["Widescreen"] + g_options["Widescreen"] == 1: # I.e. "Fullscreen" or "Widescreen" has changed - FULLSCREENBIT = g_options["Fullscreen"] + FULLSCREENBIT = 0 WIDESCREENBIT = g_options["Widescreen"] and FULLSCREENBIT res = [(640, 480), (840, 525)][WIDESCREENBIT] @@ -4471,15 +4402,15 @@ pickle.dump(p_options, optionsfile) pickle.dump(p1c, optionsfile) - fade_to_black(screen, 5) + await fade_to_black(screen, 5) selected = "" needfade = True elif selected == "Credits": - fade_to_black(screen) + await fade_to_black(screen) crediting = True tick = 0 screen.blit(Data.images["Not_Quite_Black.png"][0], (0,0)) - fade_from_black(screen) + await fade_from_black(screen) while crediting: ctime = pygame.time.get_ticks() tick += 1 @@ -4507,16 +4438,16 @@ timedif = pygame.time.get_ticks() - ctime if timedif > 20: timedif = 20 - time.sleep((20 - timedif)/1000.0) + await asyncio.sleep((20 - timedif)/1000.0) IN_LIMBO = False needfade = True - fade_to_black(screen) + await fade_to_black(screen) selected = "" needfade = True elif selected == "What's new?": - fade_to_black(screen) + await fade_to_black(screen) ge() InNew = True screen.blit(Data.images["whatsnewbg.png"][0], (0,0)) @@ -4525,9 +4456,9 @@ for line in wnsrc: screen.blit(Fonts[23].render(line, 1, (255,255,255)), (5, 5+y)) y += 14 - fade_from_black(screen) + await fade_from_black(screen) while InNew: - time.sleep(0.01) + await asyncio.sleep(0.01) for e in ge(): if e.type == KEYDOWN: InNew = False @@ -4536,16 +4467,16 @@ elif selected == "About the game": - fade_to_black(screen) + await fade_to_black(screen) Back = False ge() mx, my = pygame.mouse.get_pos() for x in range(4): screen.blit(Data.images["INFO" + str(x+1) + ".png"][0], (0,0)) - fade_from_black(screen) + await fade_from_black(screen) Waiting = True while Waiting: - time.sleep(0.03) + await asyncio.sleep(0.03) screen.blit(Data.images["INFO" + str(x+1) + ".png"][0], (0,0)) screen.blit(Data.images["MouseCurs2.png"][0], (mx,my)) for e in ge(): @@ -4562,13 +4493,13 @@ elif e.type == MOUSEMOTION: mx, my = pygame.mouse.get_pos() myflip() - fade_to_black(screen) + await fade_to_black(screen) if Back: break selected = "" needfade = True elif selected == "Online play rules": - fade_to_black(screen) + await fade_to_black(screen) ge() InOPR = True screen.blit(Data.images["whatsnewbg.png"][0], (0,0)) @@ -4577,9 +4508,9 @@ for line in oprsrc: screen.blit(Fonts[23].render(line, 1, (255,255,255)), (5, 5+y)) y += 14 - fade_from_black(screen) + await fade_from_black(screen) while InOPR: - time.sleep(0.01) + await asyncio.sleep(0.01) for e in ge(): if e.type == KEYDOWN: InOPR = False @@ -4596,9 +4527,9 @@ elif selected == "Code Listing": listing = True - fade_to_black(screen) + await fade_to_black(screen) screen.blit(Data.images["Not_Quite_Black.png"][0], (0,0)) - fade_from_black(screen) + await fade_from_black(screen) source = open("alldata", "r").readlines() scroll = -20 lines = 0 @@ -4627,7 +4558,7 @@ screen.blit(lc_s, lc_r) myflip() - wait_tick(ti) + await wait_tick(ti) for e in ge(): if e.type == KEYDOWN: @@ -4638,7 +4569,7 @@ if scroll >= len(source): scroll = len(source) inert = 0 - fade_to_black(screen) + await fade_to_black(screen) selected = "" needfade = True @@ -4650,9 +4581,8 @@ def Quit(): """Shut down the game""" - pygame.display.quit() - pygame.mixer.quit() - sys.exit(0) + # A web page cannot be closed by the game. + raise SystemExit(0) class Spark: # A spark is a little coloured circle that appears on your wand and fades/blows away. @@ -4713,20 +4643,16 @@ logfile("Debugging is " + ["disabled", "enabled"][DEBUG] + ".") if __name__ == "__main__": - if DEBUG: - if PROFILING: - import profile - profile.run('main()') - else: - main() - Quit() - try: - main() - except SystemExit: - pass - except Exception as e: # This catches errors - handleException(e) - pygame.display.quit() - pygame.mixer.quit() + async def _run(): + try: + await main() + except SystemExit: + pass + except Exception as e: # This catches errors + try: + await handleException(e) + except SystemExit: + pass + asyncio.run(_run()) diff -urN a/Conch.py b/Conch.py --- a/Conch.py +++ b/Conch.py @@ -43,6 +43,7 @@ """ +import asyncio import pygame ## Pygame toolkit for sound (and many other things); pygame.org import os ## File system import time @@ -157,12 +158,12 @@ def ResetSongData(self): self.songs = {} - def fadein(self): + async def fadein(self): for x in range(20): self.MusicVol(x/20.0) - time.sleep(0.03) + await asyncio.sleep(0.03) - def PlaySong(self,cue_name,loops = 0, vol= 1.0, interrupt=False): + async def PlaySong(self,cue_name,loops = 0, vol= 1.0, interrupt=False): """Cue this song. If interrupt, the song will start even if one is already playing.""" if not pygame.mixer.get_init(): @@ -175,7 +176,7 @@ if pygame.mixer.music.get_busy(): for x in range(20): self.MusicVol(1-x/20.0) - time.sleep(0.03) + await asyncio.sleep(0.03) pygame.mixer.music.stop() # pygame.mixer.music.stop() @@ -189,7 +190,7 @@ try: self.MusicVol(0) pygame.mixer.music.play(loops) - self.fadein() + await self.fadein() self.Comment("Cue music: '"+cue_name+"'") except: print("Couldn't play song '"+cue_name+"'.") @@ -221,24 +222,24 @@ return self.sounds[cue_name].get_length() return 5 - def FadeoutSound(self, cue_name): + async def FadeoutSound(self, cue_name): if not pygame.mixer.get_init(): return if cue_name in self.sounds: vol = self.sounds[cue_name].get_volume() while vol > 0: self.sounds[cue_name].set_volume(vol-0.1) vol -= 0.1 - time.sleep(0.05) + await asyncio.sleep(0.05) self.sounds[cue_name].stop() - def FadeoutSoundSlow(self, cue_name): + async def FadeoutSoundSlow(self, cue_name): if not pygame.mixer.get_init(): return if cue_name in self.sounds: vol = self.sounds[cue_name].get_volume() while vol > 0: self.sounds[cue_name].set_volume(vol-0.1) vol -= 0.1 - time.sleep(0.1) + await asyncio.sleep(0.1) def SetSoundVolume(self, cue_name, vol): if not pygame.mixer.get_init(): return diff -urN a/data.py b/data.py --- a/data.py 2026-11-05 10:14:20.810981824 -0300 +++ b/data.py 2026-11-05 10:14:20.845783109 -0300 @@ -146,10 +146,10 @@ if npcface.endswith(".png"): self.npc_face_data.append(npcface) - def dostage(self, filename, inc = 1): + async def dostage(self, filename, inc = 1): if not self.showload: return self.stage += inc - Update_Loading(self.stage, filename) + await Update_Loading(self.stage, filename) logfile("DATA: Successfully loaded: " + filename) def makefinal(self): @@ -208,58 +208,58 @@ self.msoundlist = msoundlist return len(animlist) - def precache_images(self): + async def precache_images(self): for image in self.imagefiles: self.images[image] = load_image(image) - self.dostage("Image: " + image) + await self.dostage("Image: " + image) for image in self.invitems: self.images[image] = load_image(image, None, False, os.path.join("Data", "InvItems")) - self.dostage("Inventory image: " + image) + await self.dostage("Inventory image: " + image) for imagedata in self.animlist: mon, image = imagedata if mon not in self.mimages: self.mimages[mon] = {} self.mimages[mon][image] = load_image(image, None, False, os.path.join("Data", "MonsterFrames", mon.lower())) - self.dostage("Monster frame: " + mon + "/" + image) + await self.dostage("Monster frame: " + mon + "/" + image) for mag_image_set in self.mag_sub_img_lists: magset = mag_image_set[0] for image in mag_image_set[1:]: self.mag_images[image] = load_image(image, None, False, os.path.join("Data", "Magic", magset)) - self.dostage("Magic frame: " + image) + await self.dostage("Magic frame: " + image) for image in self.storysceneimages: self.SSI[image] = load_image(image, None, False, os.path.join("Data", "StorySceneImages")) - self.dostage("Story scene image: " + image) + await self.dostage("Story scene image: " + image) for image in self.eio: self.images[image] = load_image(image, None, False, os.path.join("Data", "ObjectIcons")) - self.dostage("Object icon: " + image) + await self.dostage("Object icon: " + image) for image in self.tilelist: self.images[image] = load_image(image, None, False, os.path.join('Data', 'tiles')) - self.dostage("Tile image: " + image) + await self.dostage("Tile image: " + image) for image in self.numbers: self.images[image] = load_image(image, None, False, os.path.join('Data', 'Numbers')) - self.dostage("Sprite font: " + image) + await self.dostage("Sprite font: " + image) for image in self.pframefiles: self.pframes[image] = [load_image("T_"+image, None, False, os.path.join('Data','Pyralis')), load_image("L_"+image, None, False, os.path.join('Data','Pyralis'))] self.pframes2[image] = [load_image("T_"+image, None, False, os.path.join('Data','Nyx')), load_image("L_"+image, None, False, os.path.join('Data','Nyx'))] - self.dostage("Player animation: " + image, 4) + await self.dostage("Player animation: " + image, 4) for npcdata in self.npc_image_data: npcname, framefile = npcdata.split("-") self.images["NPC-" + npcname + "-" + framefile] = load_image( framefile, None, False, os.path.join("Data", "NPCs", "Frames", npcname)) - self.dostage("NPC Frame: " + npcname + "/" + framefile) + await self.dostage("NPC Frame: " + npcname + "/" + framefile) for npcdata in self.npc_face_data: self.images[npcdata] = load_image( npcdata, None, False, os.path.join("Data", "NPCs", "Faces")) - self.dostage("NPC Face: " + npcdata) + await self.dostage("NPC Face: " + npcdata) for image in self.ItemAccumulation: self.images[image] = load_image(image, None, False, os.path.join('Data', 'Items')) - self.dostage("Item overlay image: " + image) + await self.dostage("Item overlay image: " + image) - def precache_levels(self): + async def precache_levels(self): for x in range(len(self.levelfiles)): for levelfile in self.levelfiles[x]: self.levels[levelfile] = pickle.load(open(os.path.join("Levels", levelfile), "rb")) - self.dostage(levelfile, 2) + await self.dostage(levelfile, 2) diff -urN a/fade.py b/fade.py --- a/fade.py +++ b/fade.py @@ -19,6 +19,7 @@ # #------------------------------------------------------------------------ +import asyncio import pygame, time from pygame.locals import * from helpers import myflip, ge @@ -31,12 +32,12 @@ global SKIPWAIT SKIPWAIT = True -def tick_wait(ms, d): +async def tick_wait(ms, d): global SKIPWAIT ge() if SKIPWAIT: return False if ms < d: - time.sleep(0.001 * (d - ms)) + await asyncio.sleep(0.001 * (d - ms)) return True return False @@ -58,7 +59,7 @@ def flip(): myflip() -def fade_screens(screen, screen2, delay = 2): +async def fade_screens(screen, screen2, delay = 2): global FADES if not FADES: screen.blit(screen2, (0,0)) @@ -75,11 +76,11 @@ screen.blit(orig_screen, (0,0)) flip() ms = pygame.time.get_ticks() - ms - tickw = tick_wait(ms, delay) + tickw = await tick_wait(ms, delay) if type(tickw) == str: break if not tickw: skipnext = True -def fade_to_black(screen, delay = 2): +async def fade_to_black(screen, delay = 2): global FADES if not FADES: screen.fill((0,0,0)) @@ -97,14 +98,14 @@ screen.blit(orig_screen, (0,0)) flip() ms = pygame.time.get_ticks() - ms - tickw = tick_wait(ms, delay) + tickw = await tick_wait(ms, delay) if type(tickw) == str: break if not tickw: skipnext = True screen.fill((0, 0, 0)) flip() -def fade_to_white(screen, delay = 2): +async def fade_to_white(screen, delay = 2): global FADES if not FADES: screen.fill((255,255,255)) @@ -121,11 +122,11 @@ screen.blit(orig_screen, (0,0)) flip() ms = pygame.time.get_ticks() - ms - tickw = tick_wait(ms, delay) + tickw = await tick_wait(ms, delay) if type(tickw) == str: break if not tickw: skipnext = True -def fade_from_black(screen, delay = 2): +async def fade_from_black(screen, delay = 2): global FADES if not FADES: flip() @@ -142,11 +143,11 @@ screen.blit(orig_screen, (0,0)) flip() ms = pygame.time.get_ticks() - ms - tickw = tick_wait(ms, delay) + tickw = await tick_wait(ms, delay) if type(tickw) == str: break if not tickw: skipnext = True -def fade_from_white(screen, delay = 2): +async def fade_from_white(screen, delay = 2): global FADES if not FADES: flip() @@ -162,6 +163,6 @@ screen.blit(orig_screen, (0,0)) flip() ms = pygame.time.get_ticks() - ms - tickw = tick_wait(ms, delay) + tickw = await tick_wait(ms, delay) if type(tickw) == str: break if not tickw: skipnext = True diff -urN a/game.py b/game.py --- a/game.py +++ b/game.py @@ -19,6 +19,7 @@ # #------------------------------------------------------------------------ +import asyncio import pygame, time, hashlib, random from helpers import ge, myflip from pygame.locals import * @@ -134,16 +135,16 @@ } -def Wait_10_ms(t): +async def Wait_10_ms(t): timebeen = (pygame.time.get_ticks() - t) if timebeen < 10: - time.sleep((10 - timebeen) / 1000.0) + await asyncio.sleep((10 - timebeen) / 1000.0) return -def r_wait(sec): +async def r_wait(sec): ft = pygame.time.get_ticks() while pygame.time.get_ticks() < ft + sec*1000: - time.sleep(0.001) + await asyncio.sleep(0.001) for e in ge(): if e.type == KEYDOWN: if e.key == K_ESCAPE or e.key == K_RETURN: return True @@ -187,8 +188,8 @@ self.storyline = "When one of Ardentryst's Mages of Balance is struck by greed for power and becomes corrupt, the story of the small coastal town of Kiripan is changed forever... Nyx, the descendent and only daughter of the Entaryan Queen Amee, is an apprentice mage when she hears the news. Simultaneously, in the forest town Sempridge, Pyralis hears of the news from the local storyteller. When the evil mage Radkelu turned against the good mages, turning them to stone, he summoned a horde of creatures which came out of portals all throughout the land. They crawled the cities and the villagers fled. Rumour has it that Radkelu is hiding out in a cave atop the snowy mountain range Snodom. It is now up to the bravest souls of Entarya and Sempridge to rescue Kiripan from Radkelu's clutches..." - def text_intro(self, screen, data, fonts, soundbox): - soundbox.PlaySong("castle2.ogg", -1) + async def text_intro(self, screen, data, fonts, soundbox): + await soundbox.PlaySong("castle2.ogg", -1) Quitsurfw = fonts[9].render("Hit ESC or RETURN to skip", 1, (255,255,255)) Quitrect = Quitsurfw.get_rect() storysurfs = wordwrap.string_to_paragraph(self.storyline, fonts[22], 1, (220, 230, 255), 620) @@ -232,10 +233,10 @@ Quitrect.bottomright = (635, 475) screen.blit(Quitsurfw, Quitrect) myflip() - if r_wait(0.01):return + if await r_wait(0.01):return - def story_scene(self, scene, screen, d, f, s): + async def story_scene(self, scene, screen, d, f, s): Scene_Info = script[scene] Scene_Music = Scene_Info[0] Old_Slide_Amb = None @@ -248,7 +249,7 @@ this_slide = pygame.Surface((640,480)) if Scene_Music: - s.PlaySong(Scene_Music, -1) + await s.PlaySong(Scene_Music, -1) Quitsurfw = f[9].render("Hit ESC or RETURN to skip", 1, (255,255,255)) Quitsurfb = f[9].render("Hit ESC or RETURN to skip", 1, (0,0,0)) @@ -277,7 +278,7 @@ Slide_Img, Slide_Rect = d.SSI[Slide_Info[0]] this_slide.blit(Slide_Img, (0,0)) - if not CUT: fade_screens(screen, this_slide, 25) + if not CUT: await fade_screens(screen, this_slide, 25) else: screen.blit(this_slide, (0,0)) for x in range(-1,2): @@ -295,11 +296,11 @@ Time_Wait = Slide_Info[4] if Time_Wait[0]: - if r_wait(Time_Wait[0]): + if await r_wait(Time_Wait[0]): break if Old_Slide_Amb and not Amb_Cont: - s.FadeoutSoundSlow(Old_Slide_Amb) + await s.FadeoutSoundSlow(Old_Slide_Amb) if Speech: s.PlaySound(Speech); SlideLength = s.SoundLength(Speech) @@ -321,7 +322,7 @@ Sub_Rct.centerx = 320 Sub_Rct.bottom = 475 - ((len(Subtitles)-i-1) * lh) this_slide.blit(Sub_Img, Sub_Rct) - fade_screens(screen, this_slide, 10) + await fade_screens(screen, this_slide, 10) Extra_Space = Slide_Rect[2] - 640 Slide_Factor = Extra_Space / float((SlideLength-Time_Wait[1]+Time_Wait[2])*100) @@ -331,11 +332,11 @@ Sliding = True if not Sliding: - if r_wait(SlideLength): + if await r_wait(SlideLength): break if Time_Wait[1]: - if r_wait(Time_Wait[1]): + if await r_wait(Time_Wait[1]): break while Sliding: @@ -358,8 +359,8 @@ screen.blit(Sub_Img, Sub_Rct) myflip() - Wait_10_ms(t) - if r_wait(0.001): Showing = False + await Wait_10_ms(t) + if await r_wait(0.001): Showing = False if Camera_X >= Extra_Space: Sliding = False @@ -371,15 +372,15 @@ Showing = False this_slide.blit(Slide_Img, (-Camera_X,0)) - fade_screens(screen, this_slide, 10) + await fade_screens(screen, this_slide, 10) if not Showing: break if Time_Wait[3]: - if r_wait(Time_Wait[3]): + if await r_wait(Time_Wait[3]): break - fade_to_black(screen, 8) + await fade_to_black(screen, 8) s.FadeoutMusic(4000) if Slide_Amb: - s.FadeoutSound(Slide_Amb) + await s.FadeoutSound(Slide_Amb) if Speech: - s.FadeoutSound(Speech) + await s.FadeoutSound(Speech) diff -urN a/helpers.py b/helpers.py --- a/helpers.py +++ b/helpers.py @@ -34,7 +34,7 @@ WIDESCREENBIT = False WSSURF = None -LOGGING = True +LOGGING = False # logfile() rewrote the whole log on every call: too slow, and nobody reads it in a browser PROFILING = False ABILITY_DESC = { @@ -105,11 +105,8 @@ WSSURF = pic def Quit(): - global PROFILING - pygame.display.quit() - pygame.mixer.quit() - if not PROFILING: - sys.exit(0) + # A web page cannot be closed by the game. + raise SystemExit(0) def myupdate(rects): global WIDESCREENBIT, WSSCREEN @@ -122,7 +119,7 @@ rects = [rects] for rect in rects: rect.move_ip(100, 22) - pygame.display.update(rects) + pygame.display.flip() # the web canvas shows the whole surface, not only the rectangles def myflip(): global WIDESCREENBIT, SCREEN, WSSCREEN @@ -166,9 +163,7 @@ for i in range(len(evlist)): event = evlist[i] if event.type == KEYDOWN: - if event.key == K_RETURN and ((pygame.key.get_mods() & KMOD_LALT) or (pygame.key.get_mods() & KMOD_RALT)): - pygame.display.toggle_fullscreen() - elif event.key == K_F12: + if event.key == K_F12: if WIDESCREENBIT: sc_copy = saveimage.convert() saveimage.blit(sc_copy, (100, 22)) diff -urN a/loading_screen.py b/loading_screen.py --- a/loading_screen.py +++ b/loading_screen.py @@ -65,7 +65,7 @@ screensize = screen.get_size() screen.blit(loading_img, (0,0)) -def Update_Loading(stage, text, *args): +async def Update_Loading(stage, text, *args): global finalstage, loading_img, bar_grad, screen, mytext, myfont, mytext2, lb, lbr screen.fill((0,0,0)) screen.blit(loading_img, (0,0)) @@ -93,7 +93,7 @@ screen.blit(text, textrect) -def Update_Loading_Old(stage, mytext3, finish = False): +async def Update_Loading_Old(stage, mytext3, finish = False): global finalstage, loading_img, bar_grad, screen, mytext, myfont, mytext2, lb, lbr screen.fill((0, 0, 0), pygame.rect.Rect(0, 370, 640, 140)) diff -urN a/play_level.py b/play_level.py --- a/play_level.py +++ b/play_level.py @@ -19,8 +19,10 @@ # #------------------------------------------------------------------------ -import pygame, time, sys, random, math, os, pickle, urllib -import enemyai, tutorial, wordwrap, magic, copy, _thread, traceback +import asyncio +import pygame, time, sys, random, math, os, pickle, asyncio +import enemyai, tutorial, wordwrap, magic, copy, traceback +from webexec import aexec, aw import level_script, item as item_module from functools import reduce def cmp(x, y): return (x > y) - (x < y) @@ -164,7 +166,7 @@ return [[questnames[qn], qn] for qn in questlist] -def quest_npc(qname): +async def quest_npc(qname): global PLAYER, DATA if qname in PLAYER.quests: # Quest has been started (could be finished.) @@ -182,7 +184,7 @@ if PLAYER.quests["Collector1"][2] < 10: if PLAYER.quests["Collector1"][2] == 9: p = "" else: p = "s" - message_box(happy + "Find me " + str(10 - PLAYER.quests["Collector1"][2]) + " more Anneludine Shell" + p + ".", ["Stranger.png"]) + await message_box(happy + "Find me " + str(10 - PLAYER.quests["Collector1"][2]) + " more Anneludine Shell" + p + ".", ["Stranger.png"]) elif PLAYER.quests["Collector1"][2] == 10: if PLAYER.classtype: PLAYER.inventory.append("Green hood") @@ -190,11 +192,11 @@ PLAYER.inventory.append("Steel round helm") NOTICE_VIEW.add("Acquired " + DATA.Itembox.GetItem(PLAYER.inventory[-1]).display + "!") PIC_VIEW.pics.append([DATA.Itembox.GetItem(PLAYER.inventory[-1]).inv_image, 400]) - message_box("Thank you so much, adventurer.", ["Stranger.png"]) + await message_box("Thank you so much, adventurer.", ["Stranger.png"]) if PLAYER.classtype: - message_box("Rewards: Green hood, 150 silver pieces, 200 experience points") + await message_box("Rewards: Green hood, 150 silver pieces, 200 experience points") else: - message_box("Rewards: Steel round helmet, 150 silver pieces, 200 experience points") + await message_box("Rewards: Steel round helmet, 150 silver pieces, 200 experience points") NOTICE_VIEW.add("Awarded 150 silver pieces!") GAME.silver += 150 PIC_VIEW.pics.append(["silverpouch.png", 400]) @@ -203,16 +205,16 @@ PLAYER.quests["Collector1"][0] = True PLAYER.quests["Collector1"][1] = "I gave the last shell to the stranger and he rewarded me for my kindness." elif PLAYER.quests["Collector1"][2] == 11: - message_box("I think I will just rest here for a while and catch my breath.", ["Stranger.png"]) + await message_box("I think I will just rest here for a while and catch my breath.", ["Stranger.png"]) else: # Begin quest? if qname == "Collector1": # Anneludine Shell Collector if PLAYER.level < 4: - message_box("Come and see me when you're a bit stronger.", ["Stranger.png"]) + await message_box("Come and see me when you're a bit stronger.", ["Stranger.png"]) else: - message_box("I have been sent here to gather 20 Anneludine Shells. I have been running around to and fro, and I am tired already with only 10! If you can gather the other 10, and give them to me, I'll gladly reward you for your assistance.", ["Stranger.png"]) + await message_box("I have been sent here to gather 20 Anneludine Shells. I have been running around to and fro, and I am tired already with only 10! If you can gather the other 10, and give them to me, I'll gladly reward you for your assistance.", ["Stranger.png"]) PLAYER.quests["Collector1"] = [False, "I talked to a stranger in the woods who looked surprisingly like a ninja... He asked me to gather up Anneludine shells-- those worm snake turtle monsters have them on their backs. I need to give him 10.", 0] def action(object_type, ID, attr, val): @@ -224,35 +226,6 @@ if c == ID: setattr(o, attr, val) -def uploadscore(game): - # If you cheat, you cannot get a ranking on the Public Scoreboard - - wearing = [] - for area in ["Head", "Torso", "Legs", "Boots", "Weapon"]: - wearing.append(game.playerobject.wearing[area][0]) - - data = [game.savefilename[:-4], - str(game.playerobject.classtype), - pickle.dumps(game.scores).replace("\n","\\"), - str(game.playerobject.exp), - str(game.playerobject.level), - str(game.playerobject.questsdone()), - pickle.dumps(game.timegems).replace("\n","\\"), - pickle.dumps(wearing).replace("\n","\\"), - game.shc, - game.ac_code, - game.GID - ] - - import sha - sd = [data, sha.new(pickle.dumps(data)).hexdigest()] - - senddata = pickle.dumps(sd).replace("\n","/").replace(" ", "%20") - - msg = urllib.urlopen("http://jordan.trudgett.com/cgi-bin/submit.py?code="+senddata).read().strip() - return msg.split("\n") - - def strbon(num): """Takes a number, x, and turns it into a "+x", "-x", or "+0".""" # Why is this the only function with a docstring? :| @@ -272,7 +245,7 @@ return pygame.key.name(CONTROLS[keyname][0]).upper() -def Level_Up_Screen(): +async def Level_Up_Screen(): global finalscreen, CONTROLS, Start_Level_Time, DATA, PLAYER, FONTS, NOTICE_VIEW countdown = Hourglass(5) starttick = pygame.time.get_ticks() @@ -351,7 +324,7 @@ else: tick += 75 - lt = mywait(lt) + lt = await mywait(lt) myflip() @@ -375,7 +348,7 @@ mysb.PlaySound(code[sound.lower()]) -def message_box(message, face = None, csounds = None): +async def message_box(message, face = None, csounds = None): global lasttick, SOUNDBOX, finalscreen, FONTS, DATA, PLAYER, MESSAGE_JUST_DONE, CONTROLS global Start_Level_Time, PLAYDEMO if PLAYDEMO: return @@ -391,7 +364,7 @@ if type(message) == list: for m in message: finalscreen.blit(sc_copy, (0,0)) - message_box(m, face, csounds) + await message_box(m, face, csounds) return handdir = 0 @@ -525,7 +498,7 @@ if e.type == KEYDOWN: if e.key in CONTROLS["B-1"] and tick >= 10: box = False - time.sleep(0.05) + await asyncio.sleep(0.05) tick += 1 lasttick = pygame.time.get_ticks() Start_Level_Time += pygame.time.get_ticks()-ntick @@ -534,15 +507,15 @@ global SCREEN, finalscreen finalscreen.blit(SCREEN, (0,0)) -def mswait(t, ms): +async def mswait(t, ms): ct = pygame.time.get_ticks() d = ct - t if d > ms: tw = 0 else: tw = ms-d - time.sleep(tw/1000.0) + await asyncio.sleep(tw/1000.0) return pygame.time.get_ticks() -def mywait(lasttick, fps = 40): +async def mywait(lasttick, fps = 40): global SKIPNEXT, DELAY, fsol, CONSOLE_ACTIVE, DEBUG, CSPEEDARRAY if fsol or CONSOLE_ACTIVE: return pygame.time.get_ticks() @@ -556,7 +529,7 @@ CSPEEDARRAY = CSPEEDARRAY[-10:] if ctick < lasttick + msdelay: - pygame.time.wait(lasttick + msdelay - ctick) + await asyncio.sleep((lasttick + msdelay - ctick) / 1000.0) DELAY = lasttick + msdelay - ctick ctick = pygame.time.get_ticks() else: @@ -564,7 +537,7 @@ DELAY = ctick - (lasttick+msdelay) return ctick -def my_raw_wait(lasttick, fps = 40): +async def my_raw_wait(lasttick, fps = 40): FPS = fps # 1000 ms in a second. FPS frames per second means @@ -573,7 +546,7 @@ ctick = pygame.time.get_ticks() if ctick < lasttick + msdelay: - pygame.time.wait(lasttick + msdelay - ctick) + await asyncio.sleep((lasttick + msdelay - ctick) / 1000.0) ctick = pygame.time.get_ticks() return ctick @@ -675,7 +648,7 @@ SCREEN.blit(OLDSCREEN, (0,0)) -def Monster_Tick(Monsters): +async def Monster_Tick(Monsters): global CAMERA_X, SCREEN, DATA, SOUNDBOX, PLAYER, LEVEL global ORBS, NOTICE_VIEW, PIC_VIEW, Objects for Monster in Monsters: @@ -700,7 +673,7 @@ if Monster.EXECUTE: cmd = Monster.EXECUTE Monster.EXECUTE = "" - exec(cmd) in globals() + exec(cmd, globals()) while None in Monster.projectiles: Monster.projectiles.remove(None) @@ -709,7 +682,7 @@ # Okay, it's dead, or at least it wants to spawn goodies... Monster.SPAWN_GOODIES = False PLAYER.exp += Monster.reward_exp - PLAYER.possible_level_up() + await PLAYER.possible_level_up() rcmrh = random.choice(Monster.reward_hp) rcmrm = random.choice(Monster.reward_mp) if type(rcmrh) != type([]): @@ -822,7 +795,7 @@ Monster.Advance_Numbers() return [x for x in Monsters if not x.REMOVEME] -def Tutorial_Tick(THISFLIP): +async def Tutorial_Tick(THISFLIP): global PLAYER, TUTBOX, TUT_ACTIVE, RAIN_HEAVINESS TUTBOX.tick() TUTBOX.playerat(PLAYER.x) @@ -830,7 +803,7 @@ TutMsg, CPT, TutFace = TUTBOX.poll() if TutMsg and THISFLIP: - message_box(TutMsg, TutFace) + await message_box(TutMsg, TutFace) if TUTBOX.RAIN is not None: RAIN_HEAVINESS = TUTBOX.RAIN @@ -917,7 +890,7 @@ global mmpic mmpic = pygame.transform.scale(pic, (320, 240)) -def Ingame_Menu(data): +async def Ingame_Menu(data): # Menu in the game. Accessible from ardentryst.py as well screen, DATA, FONTS, CONTROLS, SOUNDBOX, lasttick, game, player, abortable = data @@ -1001,7 +974,7 @@ while inmenu: ti = pygame.time.get_ticks() - player.var_tick(1) + await player.var_tick(1) # Blitting @@ -1681,7 +1654,7 @@ if e.type == KEYDOWN: if e.key in CONTROLS["B-1"] + CONTROLS["B-2"] + CONTROLS["B-9"]: im = False - ti = my_raw_wait(ti, 40) + ti = await my_raw_wait(ti, 40) if handcol == 0: if menuitems[handpos] == "Abort level": @@ -1743,7 +1716,7 @@ elif e.key in CONTROLS["Up"]: scroll = max(0, scroll - 1) - ti = my_raw_wait(ti, 40) + ti = await my_raw_wait(ti, 40) if tab == "Inventory": if handcol == 2: if handpos == 0: @@ -1840,7 +1813,7 @@ if e.key in CONTROLS["B-1"]: examining = False - ti = my_raw_wait(ti, 50) + ti = await my_raw_wait(ti, 50) tick += 1 if handcol == 0: @@ -1852,7 +1825,7 @@ if dsa > 230: dsa = 230 # Time delay - ti = my_raw_wait(ti, 40) + ti = await my_raw_wait(ti, 40) tick += 1 pygame.key.set_repeat() @@ -1869,7 +1842,7 @@ screen.blit(playscreen, (0,0)) dsa -= 30 myflip() - ti = my_raw_wait(ti, 40) + ti = await my_raw_wait(ti, 40) playscreen.set_alpha(255) screen.blit(playscreen, (0,0)) myflip() @@ -1940,7 +1913,7 @@ return False return s -def check_rules(rules): +async def check_rules(rules): global PLAYER, SOUNDBOX, var, RAIN_HEAVINESS, RAINING, Timegem_time, P_OPTIONS global LEVEL, CL_Surface, Objects, LEVELSTATUS, IN_GAME @@ -1983,7 +1956,7 @@ for x in creplace.keys(): event = event.replace(x, creplace[x]) if safe(event): - exec(safe(event)) + await aexec(safe(event), globals()) else: print("Unsafe event") if rule[0][0].lower() == "when": @@ -2048,7 +2021,7 @@ ysense -= [1,10][f] return ysense -def NPC_Tick(): +async def NPC_Tick(): global NPCLIST, PLAYER, DATA, SCREEN, CAMERA_X, TUT_ACTIVE for NPC in NPCLIST: @@ -2072,7 +2045,7 @@ NPC.y = (NPC.y * 20 + (PLAYER.y-40))/21.0 if abs(NPC.x - PLAYER.x) < 80: if NPC.first_proximity_message: - message_box(NPC.first_proximity_message, NPC.portrait, NPC.talksounds) + await message_box(NPC.first_proximity_message, NPC.portrait, NPC.talksounds) NPC.first_proximity_message = "" NPC.direction = int(NPC.x < PLAYER.x) @@ -2099,7 +2072,7 @@ NPC.still = True if NPC.first_proximity_message: - message_box(NPC.first_proximity_message, NPC.portrait, NPC.talksounds) + await message_box(NPC.first_proximity_message, NPC.portrait, NPC.talksounds) NPC.first_proximity_message = "" if NPC.impervious: @@ -2156,11 +2129,11 @@ if not NPC.direction: frame = pygame.transform.flip(frame, True, False) SCREEN.blit(frame, framerect) -def tutskip(): +async def tutskip(): global finalscreen, FONTS, DATA, CONTROLS, TUT_ACTIVE finalscreen.fill((0,0,0)) myflip() - message_box("Please follow the tutorial if this is your first time playing Ardentryst. Pay attention too, because you can only re-do this if you start the whole game again. Note: this will have no effect if you have Help messages off in the Options menu.") + await message_box("Please follow the tutorial if this is your first time playing Ardentryst. Pay attention too, because you can only re-do this if you start the whole game again. Note: this will have no effect if you have Help messages off in the Options menu.") finalscreen.fill((0,0,0)) myflip() ts = FONTS[17].render("Tutorial:", 1, (255,255,255)) @@ -2184,8 +2157,8 @@ myflip() while Choosing: - time.sleep(0.02) - finalscreen.fill(0) + await asyncio.sleep(0.02) + finalscreen.fill(0, Rect(50,260,115,150)) hs, hr = DATA.images["hand.png"] hs = pygame.transform.flip(hs, True, False) hr.midright = (150 + math.sin(ti/10.0)*10, 300 + c * 30) @@ -2203,11 +2176,11 @@ if c == 1: TUT_ACTIVE = False elif c == 2: - tutskip() + await tutskip() Choosing = False ti += 1 -def level_intro(): +async def level_intro(): global LEVEL, SCREEN, DATA, FONTS, PLAYER, GAME, finalscreen, Start_Level_Time global CONTROLS, Timegem_time @@ -2275,12 +2248,12 @@ finalscreen.blit(SCREEN, (0,0)) myflip() - mywait(t) + await mywait(t) - fade_to_black(SCREEN) + await fade_to_black(SCREEN) Start_Level_Time = pygame.time.get_ticks() -def Level_Score(enemyperc, treasureperc, seconds, sd): +async def Level_Score(enemyperc, treasureperc, seconds, sd): global SCREEN, FONTS, finalscreen, DATA, CONTROLS, MEDAL enemyperc = int(enemyperc) @@ -2343,7 +2316,7 @@ y += 1 finalscreen.blit(SCREEN, (0,0)) - fade_from_black(finalscreen, 50) + await fade_from_black(finalscreen, 50) Counting = True while Counting: @@ -2376,7 +2349,7 @@ vinfo2[x] += 1 Counting = True - ti = mywait(ti) + ti = await mywait(ti) finalscreen.blit(SCREEN, (0,0)) myflip() @@ -2409,14 +2382,14 @@ finalscreen.blit(SCREEN, (0,0)) myflip() - csleep(20) + await csleep(20) return info2[3] -def csleep(sec): +async def csleep(sec): global CONTROLS startti = pygame.time.get_ticks() while pygame.time.get_ticks() < startti + 1000 * sec: - time.sleep(0.01) + await asyncio.sleep(0.01) for ev in ge(): if ev.type == KEYDOWN: k = ev.key @@ -2424,14 +2397,14 @@ return True return False -def playdemo(*args): +async def playdemo(*args): global PLAYDEMO, eventd, monsterd, playerd demofile = args[-1] args = args[:-1] dfile = open(os.path.join("Demos", demofile), "r") eventd, monsterd, playerd = pickle.load(dfile) PLAYDEMO = True - return playlevel(*args) + return await playlevel(*args) def check_tips(): global MOUSEPOS, TOOLTIP @@ -2455,7 +2428,7 @@ TOOLTIP = region[4] -def playlevel(player, level, scripts, screen, data, fonts, soundbox, game, options, tut_active, ovs, pl): +async def playlevel(player, level, scripts, screen, data, fonts, soundbox, game, options, tut_active, ovs, pl): # Play a level LEVEL on screen SCREEN. Returns a few values to the caller # to indicate information about the play. @@ -2710,16 +2683,16 @@ pygame.event.clear() if level.bgmusic: - SOUNDBOX.PlaySong(level.bgmusic, -1) + await SOUNDBOX.PlaySong(level.bgmusic, -1) - check_rules(Rules) + await check_rules(Rules) - level_intro() + await level_intro() if PLAYER.obelisk_save: FRAMEMOD = PLAYER.obelisk_time if tut_active: - tutskip() + await tutskip() if not TUT_ACTIVE: PLAYER.ignore_move = False @@ -2749,7 +2722,7 @@ if laggy == 20 and not said_laggy and not GAME.said_laggy: said_laggy = True GAME.said_laggy = True - message_box("The game seems sluggish. Tune the graphics effects to your system's capability from the Options menu in the Main Menu. Consider turning off Rain, Parallax effects and/or Moving BG Objects to increase playability before you next play Ardentryst.") + await message_box("The game seems sluggish. Tune the graphics effects to your system's capability from the Options menu in the Main Menu. Consider turning off Rain, Parallax effects and/or Moving BG Objects to increase playability before you next play Ardentryst.") else: laggy = 0 @@ -2758,7 +2731,7 @@ SOUNDBOX.PlaySound("Rain_Loop.ogg", -1) RAINSOUND = True - lasttick = mywait(lasttick) # Waiter + lasttick = await mywait(lasttick) # Waiter if LEVEL.endpoint > PLAYER.x: # Time_Played = (pygame.time.get_ticks() - Start_Level_Time) / 1000 Time_Played = int((FRAMEMOD*25)/1000.0) @@ -2774,7 +2747,7 @@ while CONSOLE_ACTIVE: lasttick = pygame.time.get_ticks() - lasttick = mywait(lasttick) + lasttick = await mywait(lasttick) for event in ge(): if event.type == KEYDOWN: if event.key == K_BACKQUOTE: @@ -2792,7 +2765,7 @@ CONSOLE_VIEW.append("&" + str(eval(CONSOLE_TEXT))) except Exception as e: try: - exec(CONSOLE_TEXT) in globals() + exec(CONSOLE_TEXT, globals()) CONSOLE_VIEW.append("%Successful") except Exception as f: CONSOLE_VIEW.append("#Couldn't evaluate because "+str(e)+".") @@ -2851,7 +2824,7 @@ SCREEN.blit(thisline, (10, 203 - 12*x)) finalise_screen() myflip() - time.sleep(0.01) + await asyncio.sleep(0.01) CAMERA_SLOWNESS = max(0, 20 - PLAYER.inertia[0]) @@ -2878,15 +2851,15 @@ SCREEN.blit(DATA.images[PL[0]][0], (int(x%640), 0)) SCREEN.blit(DATA.images[PL[0]][0], (int(x%640) - 640, 0)) - PLAYER.var_tick(0) + await PLAYER.var_tick(0) PLAYER.movement_run_tick() PLAYER.movement_jump_tick() PLAYER.movement_fall_tick() PLAYER.animation_update_tick() - PLAYER.animation_frame_tick() + await PLAYER.animation_frame_tick() PLAYER.arrow_tick() PLAYER.magic_tick() - PLAYER.do_combo() + await PLAYER.do_combo() if PLAYER.hp[0] <= 0 and DEATHFADE == 0: LEVELSTATUS = "Failed.LoseLife" @@ -2901,9 +2874,9 @@ blit_map_at(CAMERA_X) Object_Tick(Objects, False) - Monsters = Monster_Tick(Monsters) - check_rules(Rules) - NPC_Tick() + Monsters = await Monster_Tick(Monsters) + await check_rules(Rules) + await NPC_Tick() if THISFLIP and Hurtfade == 0: blit_character(PLAYER, CAMERA_X) @@ -3128,7 +3101,7 @@ if MonsterCount == 0: MonsterCount = 1 Monsters = [] - LEVELPERCENT = Level_Score(100-(len([x for x in Monsters if not x.isdead])*100//MonsterCount), Treasures*100//TreasureCount, Time_Played, PLAYER.suddendeath) + LEVELPERCENT = await Level_Score(100-(len([x for x in Monsters if not x.isdead])*100//MonsterCount), Treasures*100//TreasureCount, Time_Played, PLAYER.suddendeath) EOLFADE -= 12 # <- @@ -3147,11 +3120,11 @@ finalscreen.blit(tsurf, trect) # Just before flipping, do a fade if need be - if fsol: fade_from_black(finalscreen); fsol = False; lasttick = pygame.time.get_ticks() + if fsol: await fade_from_black(finalscreen); fsol = False; lasttick = pygame.time.get_ticks() # Finally, after everything is blitted, check for messages from TutBox if TUT_ACTIVE: - Tutorial_Tick(THISFLIP) + await Tutorial_Tick(THISFLIP) MESSAGE_JUST_DONE += 1 # Another ticker-type object, but controls messagebox sounds # elif PLAYER.ignore_move: # PLAYER.ignore_move = False @@ -3177,7 +3150,7 @@ if SKIPNEXT > 4: SKIPNEXT = 1 if not said_laggy and not GAME.said_laggy: - message_box("The game appears too slow. You can try reducing graphics intensity in the"\ + await message_box("The game appears too slow. You can try reducing graphics intensity in the"\ " Options menu via the Main Menu to increase playability. Turn off parallax layers and rain first.") said_laggy = True GAME.said_laggy = True @@ -3253,7 +3226,7 @@ PLAYER.exp = (PLAYER.exp + PLAYER.expfor(PLAYER.level+1))/2 if event.key == K_l and DEBUG: PLAYER.exp = PLAYER.expfor(PLAYER.level+1) - PLAYER.possible_level_up() + await PLAYER.possible_level_up() if event.key == K_x and DEBUG: bounding_box = True if event.key == K_p and DEBUG: @@ -3273,15 +3246,15 @@ if event.key in CONTROLS["B-3"]: try: if OBJECT_CLOSEST and OBJECT_CLOSEST._activateable: - OBJECT_CLOSEST._activate() + await OBJECT_CLOSEST._activate() except Exception as e: if OBJECT_CLOSEST and OBJECT_CLOSEST.interactive: if safe(OBJECT_CLOSEST.action): - exec(OBJECT_CLOSEST.action) in globals() + await aexec(OBJECT_CLOSEST.action, globals()) if event.key in CONTROLS["B-9"]: beforetick = pygame.time.get_ticks() abortable = GAME.scores[GAME.location[0]-1][GAME.location[1]-1] - r = Ingame_Menu((finalscreen, DATA, FONTS, CONTROLS, SOUNDBOX, lasttick, GAME, PLAYER, abortable)) + r = await Ingame_Menu((finalscreen, DATA, FONTS, CONTROLS, SOUNDBOX, lasttick, GAME, PLAYER, abortable)) lasttick = pygame.time.get_ticks() Start_Level_Time += lasttick-beforetick if r: @@ -3309,7 +3282,7 @@ SCREEN.blit(CONSOLE_WIN, (0,-228+x*12)) finalise_screen() myupdate(Rect(0,0,640,320)) - Ct = mswait(Ct, 10) + Ct = await mswait(Ct, 10) if event.key == K_h and DEBUG: PLAYER.Take_Damage(1) if event.key == K_s and DEBUG: @@ -3325,14 +3298,14 @@ if LEVELSTATUS != "Quit": SOUNDBOX.FadeoutMusic(1500) - time.sleep(1.5) + await asyncio.sleep(1.5) else: SOUNDBOX.FadeoutMusic(200) - SOUNDBOX.FadeoutSound("Rain_Loop.ogg") + await SOUNDBOX.FadeoutSound("Rain_Loop.ogg") if pygame.mixer.get_init(): pygame.mixer.stop() - fade_to_black(screen) - check_rules(Rules) + await fade_to_black(screen) + await check_rules(Rules) return LEVELSTATUS, LEVELPERCENT, MEDAL+1 def Particle_Spawn(sx, sy, ptype, amt): @@ -4561,12 +4534,12 @@ luck = int(self.MAGE_BASE[3] + x**1.206) return strength, endurance, magic, luck - def var_tick(self, mode): + async def var_tick(self, mode): global Objects, GAME try: # Stop cheaters on Frosty Frolic if GAME.location == [3, 4] and Objects[3].state == 0 and PLAYER.x > 4220 and mode == 0: - message_box("The gods do not like it when you try to cheat!") + await message_box("The gods do not like it when you try to cheat!") PLAYER.x = 4220 PLAYER.inertia[0] = -1 PLAYER.hp[0] = 0 @@ -4580,7 +4553,7 @@ # It will be re-applied later in this function if it is still worn. self.bits[key] = False - self.possible_level_up() + await self.possible_level_up() while self.expfor(self.level) > self.exp: self.level -= 1 @@ -4672,7 +4645,7 @@ def expfor(self, l): return int((((l-1)*6)**2.7)/((l+0.00001)*2)) - def possible_level_up(self): + async def possible_level_up(self): global NOTICE_VIEW, lasttick if self.exp >= self.expfor(self.level+1): self.level += 1 @@ -4680,9 +4653,9 @@ Particle_Spawn(self.x, self.y, "PART_LEVELUP", 80) NOTICE_VIEW.add("Congratulations! You levelled up!") self.scale_vitals = True - Level_Up_Screen() + await Level_Up_Screen() lasttick = pygame.time.get_ticks() - self.possible_level_up() + await self.possible_level_up() def add_to_combo(self, button): if self.chainmove[0]: return @@ -4698,7 +4671,7 @@ self.mycombotime = self.combotime break - def do_combo(self): + async def do_combo(self): self.breaktime -= 1 if self.breaktime < 0: @@ -4731,7 +4704,7 @@ if self.chainmove[1] == 0: cm = self.chainmove[:] self.chainmove = [None, None] - getattr(self, cm[0])() + await aw(getattr(self, cm[0])()) elif self.buttonbuffer: self.combo_string += self.buttonbuffer self.buttonbuffer = "" @@ -4751,7 +4724,7 @@ if toexec[0]: execfunc = getattr(self, toexec[0]) - execfunc() + await aw(execfunc()) self.lastmovelen = toexec[1] self.mycombotime = self.combotime else: @@ -4845,10 +4818,10 @@ Particle_Spawn(self.x, self.y-40, "PART_NOMANA", 15) self.nomanaicon = 59 - def teleport(self): + async def teleport(self): global SOUNDBOX, GAME if GAME.location in [[3, 4], [3, 10]]: - message_box("That spell cannot be used here.") + await message_box("That spell cannot be used here.") return if self.mp[0] < 10: # No mana @@ -5140,7 +5113,7 @@ for item in bagged: self.inventory.append(item) - def animation_frame_tick(self): + async def animation_frame_tick(self): global SOUNDBOX if self.nomanaicon: @@ -5191,11 +5164,11 @@ if abs(self.attackat[2] - self.tframe) < wt*0.6 or self.lunging: if self.p_attack: wrange, dmg, crit = self.p_attack - self.deal_damage(wrange, dmg, crit) + await self.deal_damage(wrange, dmg, crit) if not self.lunging: self.p_attack = None - def deal_damage(self, rng, dmg, crit): + async def deal_damage(self, rng, dmg, crit): global LEVEL, TUTBOX, Monsters, FACE_VIEW, DATA, SOUNDBOX # rng = [max, min] Reactants = [] @@ -5219,7 +5192,7 @@ self.mp[0] -= self.md SOUNDBOX.PlaySound("mdrain.ogg") if not PLAYER.tags[2]: - message_box("The magic sound your weapon makes indicates that it is drawing mana from you.") + await message_box("The magic sound your weapon makes indicates that it is drawing mana from you.") PLAYER.tags[2] = 1 self.md = 0 @@ -6049,7 +6022,7 @@ while None in self.fireballs: self.fireballs.remove(None) - def _activate(self): + async def _activate(self): pass class Lever: @@ -6080,17 +6053,17 @@ return self.frame[self.state], self.x-40, self.y - 40 def _tick(self): pass - def _activate(self): + async def _activate(self): global PLAYER, NOTICE_VIEW, SOUNDBOX self.state = 1 self._activateable = False if safe(self.command): - _thread.start_new_thread(self.run, ()) + asyncio.ensure_future(self.run()) SOUNDBOX.PlaySound("stone.ogg") - def run(self): + async def run(self): global PLAYER, EOLFADE - exec(self.command) + await aexec(self.command.replace("&", "\n"), globals(), {"self": self}) class Obelisk: def __init__(self, tx, ty, otype = ''): @@ -6120,7 +6093,7 @@ return self.frame[self.state], self.x-20, self.y - 200 def _tick(self): pass - def _activate(self): + async def _activate(self): global PLAYER, NOTICE_VIEW, SOUNDBOX, LEVEL, Monsters, Objects, FRAMEMOD, Treasures NOTICE_VIEW.add("Obelisk activated!") @@ -6159,8 +6132,8 @@ return self.frame, self.x, self.y def _tick(self): pass - def _activate(self): - message_box(self.message) + async def _activate(self): + await message_box(self.message) class TreasureBox: def __init__(self, tx, ty): @@ -6188,7 +6161,7 @@ return self.frame[self.state], self.x-20, self.y - 57 def _tick(self): pass - def _activate(self): + async def _activate(self): global PLAYER, NOTICE_VIEW, SOUNDBOX, Treasures, GAME, DATA, PIC_VIEW Treasures += 1 if random.randint(1,30) == 1: diff -urN a/webexec.py b/webexec.py --- a/webexec.py +++ b/webexec.py @@ -0,0 +1,25 @@ +# Helpers for running Ardentryst in the browser. Written for the web build; GPL, version 3 or later, like the game. + +import ast +import asyncio +import inspect +import re + + +async def aexec(code, globs, locs=None): + """exec() for the little scripts of the levels (events, NPC actions, lever commands), which may call functions that + wait (message boxes, quest givers) or sleep. They are compiled with top level await, and time.sleep becomes + asyncio.sleep.""" + code = re.sub(r"\b(message_box|quest_npc)\(", r"await \1(", code) + code = code.replace("time.sleep(", "await asyncio.sleep(") + compiled = compile(code, "