diff -ruN a/tangomon.py b/tangomon.py --- a/tangomon.py +++ b/tangomon.py @@ -17,6 +17,8 @@ __version__ = "2.0" +import pygame +import asyncio import argparse import datetime import gettext @@ -27,7 +29,6 @@ import shutil import time import warnings -import webbrowser import sge import xsge_gui @@ -35,6 +36,9 @@ DATA = os.path.join(os.path.dirname(__file__), "data") CONFIG = os.path.join(os.path.expanduser("~"), ".config", "tangomon") +if os.path.isdir("/saves"): + # On the web the page keeps /saves in the browser (IndexedDB). + CONFIG = "/saves/tangomon" gettext.install("tangomon", os.path.abspath(os.path.join(DATA, "locale"))) @@ -67,7 +71,7 @@ "-r", "--results", help=_("Use alongside the \"--offline\" option to submit your results for offline play."), action="store_true") -args = parser.parse_args() +args = parser.parse_args([]) NOSAVE = args.nosave DELTA = not args.nodelta @@ -102,7 +106,9 @@ SAVE_SLOTS_BACKUP_PATH = os.path.join(CONFIG, "save_slots.json~") KEY_REPEAT_INTERVAL = 20 -KEY_REPEAT_DELAY = 400 +# Long on the web: when a key press comes with a slow frame (loading a room), a +# shorter delay makes the browser repeat it even if it was already released. +KEY_REPEAT_DELAY = 1500 SAVE_NSLOTS = 5 @@ -202,19 +208,11 @@ color=sge.gfx.Color("yellow"), halign="right", valign="bottom") - def event_mouse_button_press(self, button): - if button == "middle": - self.event_close() - def event_close(self): - if isinstance(self.current_room, Arena): - self.current_room.terminate_game() - else: - save_game() - self.end() + pass def event_paused_close(self): - self.event_close() + pass class Room(sge.dsp.Room): @@ -247,7 +245,6 @@ super(TitleScreen, self).event_room_start() if first_run: - FontChooser(gui_handler).show() first_run = False write_to_disk() @@ -406,7 +403,7 @@ padding = 8 w = sge.game.width - h = 2 * padding + xsge_gui.textbox_sprite.height + h = 2 * padding + xsge_gui.TextBox.get_height() self.window = xsge_gui.Window( gui_handler, 0, sge.game.height - h, w, h, border=False) @@ -642,23 +639,6 @@ load_map() - def terminate_game(self): - if not self.player_ran: - assert len(player_tangomon) > self.player - text = _("WARNING: If you leave this battle, you will lose your current tangomon! Are you sure?") - buttons = [_("No"), _("Yes")] - if xsge_gui.show_message(gui_handler, message=text, buttons=buttons): - self.reset_state() - self.player_ran = True - del player_tangomon[self.player] - self.end_battle() - save_game() - sge.game.end() - else: - self.end_battle() - save_game() - sge.game.end() - def event_key_press(self, key, char): if key in {sge.s.enter, sge.s.kp_enter}: self.evaluate_tangoji() @@ -666,10 +646,15 @@ assert len(player_tangomon) > self.player text = _("WARNING: If you leave this battle, you will lose your current tangomon! Are you sure?") buttons = [_("No"), _("Yes")] - if xsge_gui.show_message(gui_handler, message=text, buttons=buttons): - self.reset_state() - self.alarms = {} - self.event_alarm("player_lose") + + def confirmed(choice): + if choice: + self.reset_state() + self.alarms = {} + self.event_alarm("player_lose") + + xsge_gui.show_message(gui_handler, message=text, buttons=buttons, + on_close=confirmed) def event_alarm(self, alarm_id): if alarm_id == "init_tangoject": @@ -770,60 +755,6 @@ sge.game.start_room.start() -class FontChooser(xsge_gui.Dialog): - - """Screen shown at the start of the game to choose the font.""" - - def __init__(self, parent): - super(FontChooser, self).__init__( - parent, 0, 0, SCREEN_SIZE[0], SCREEN_SIZE[1], border=False) - - padding = 8 - - text = _("The quick brown fox jumps over the lazy dog.") - test_textbox = xsge_gui.TextBox(self, padding, padding, 0, - width=(self.width - 2 * padding), - text=text, text_limit=200) - - h = xsge_gui.button_sprite.height - text = _("Done") - done_button = xsge_gui.Button(self, padding, - self.height - h - padding, 4, text, - width=(self.width - 2 * padding)) - done_button.event_press = self.destroy - - h = xsge_gui.button_sprite.height - y = done_button.y - h - padding * 2 - text = _("Change Font") - change_font_button = xsge_gui.Button(self, padding, y, 3, text, - width=(self.width - 2 * padding)) - - h = xsge_gui.textbox_sprite.height - y = change_font_button.y - h - padding - font_textbox = xsge_gui.TextBox( - self, padding, y, 2, - width=(self.width - 2 * padding), - text_limit=50) - - def press_change_font(self=self, font_textbox=font_textbox): - global font_name - font_name = font_textbox.text - create_fonts() - for widget in self.widgets: - widget.font = font - widget.redraw() - self.redraw() - - change_font_button.event_press = press_change_font - - h = y - xsge_gui.textbox_sprite.height - 3 * padding - text = _('If you will be using non-ASCII characters, please ensure that they display correctly by typing them into the test textbox above. If they do not, please specify a different font to use by entering its name in the textbox below and then clicking "Change Font". When you are finished, press "Done".\n\nFor English, some good font choices are Roboto and Arial.') - label = xsge_gui.Label( - self, self.width / 2, 2 * padding + xsge_gui.textbox_sprite.height, - 1, text, width=(self.width - 2 * padding), height=h, - halign=sge.s.center) - - class Menu(xsge_gui.MenuWindow): items = [] @@ -851,8 +782,7 @@ class MainMenu(Menu): - items = [_("New Game"), _("Load Game"), _("Options"), _("Credits"), - _("Quit")] + items = [_("New Game"), _("Load Game"), _("Options"), _("Credits")] def event_choose(self): if self.choice == 0: @@ -868,8 +798,6 @@ play_sound(confirm_sound) credits_room = CreditsScreen() credits_room.start() - else: - sge.game.end() class NewGameMenu(Menu): @@ -895,8 +823,7 @@ play_sound(confirm_sound) current_save_slot = self.choice if save_slots[current_save_slot] is None: - new_game() - load_map() + new_game(load_map) else: OverwriteConfirmMenu.create(default=1) else: @@ -911,8 +838,7 @@ def event_choose(self): if self.choice == 0: play_sound(confirm_sound) - new_game() - load_map() + new_game(load_map) else: play_sound(cancel_sound) NewGameMenu.create(default=current_save_slot) @@ -927,8 +853,9 @@ play_sound(confirm_sound) current_save_slot = self.choice if not load_game(): - new_game() - load_map() + new_game(load_map) + else: + load_map() else: play_sound(cancel_sound) MainMenu.create(default=1) @@ -940,13 +867,14 @@ def create_page(cls, default=0): smt = scale_method if scale_method else "fastest" cls.items = [ - _("Fullscreen: {}").format(_("On") if fullscreen else _("Off")), _("Scale Method: {}").format(smt), _("Sound: {}").format(_("On") if sound_enabled else _("Off")), _("Music: {}").format(_("On") if music_enabled else _("Off")), _("Show FPS: {}").format(_("On") if fps_enabled else _("Off")), - _("Select Font"), _("Back")] - return cls.create(default) + _("Back")] + # The list lost its first entry (fullscreen), but event_choose still + # counts it, so the default shifts by one. + return cls.create(max(default - 1, 0)) def event_choose(self): global fullscreen @@ -957,12 +885,9 @@ global fps_enabled global joystick_threshold - if self.choice == 0: - play_sound(select_sound) - fullscreen = not fullscreen - sge.game.fullscreen = fullscreen - OptionsMenu.create_page(default=self.choice) - elif self.choice == 1: + if self.choice is not None: + self.choice += 1 # see create_page + if self.choice == 1: choices = [None, "noblur", "smooth"] + sge.SCALE_METHODS if scale_method in choices: i = choices.index(scale_method) @@ -987,9 +912,6 @@ play_sound(select_sound) fps_enabled = not fps_enabled OptionsMenu.create_page(default=self.choice) - elif self.choice == 5: - FontChooser(gui_handler).show() - OptionsMenu.create_page(default=self.choice) else: play_sound(cancel_sound) write_to_disk() @@ -1048,8 +970,9 @@ TangojiMenu.create_page() elif self.choice == 4: play_sound(confirm_sound) - add_player_tangoji() - WorldmapMenu.create(default=self.choice) + add_player_tangoji( + lambda ok, default=self.choice: WorldmapMenu.create( + default=default)) elif self.choice == 5: play_sound(confirm_sound) ChangeTangojiMenu.create_page() @@ -1065,10 +988,15 @@ elif self.choice == 7: text = _("This will only reset your location and tangomon. Are you sure?") buttons = [_("No"), _("Yes")] - if xsge_gui.show_message(gui_handler, message=text, buttons=buttons): - reset_game() - else: - WorldmapMenu.create(default=self.choice) + + def confirmed(choice, default=self.choice): + if choice: + reset_game() + else: + WorldmapMenu.create(default=default) + + xsge_gui.show_message(gui_handler, message=text, buttons=buttons, + on_close=confirmed) elif self.choice == 8: save_game() sge.game.start_room.start() @@ -1138,27 +1066,33 @@ i = self.current_tangoji[self.choice] text = _("Enter your desired changes to this tangoji.") word = player_tangojis[i].get("word") or "" - tangoji_word = xsge_gui.get_text_entry(gui_handler, message=text, - text=word) - if tangoji_word: - player_tangojis[i]["word"] = tangoji_word - - text = _("Enter your desired changes to this tangoji's clue.") - clue = player_tangojis[i].get("clue") or "" - tangoji_clue = xsge_gui.get_text_entry(gui_handler, message=text, - text=clue) - - text = _("Enter your desired changes to this tangoji's extra information.") - info = player_tangojis[i].get("info") or "" - tangoji_info = xsge_gui.get_text_entry(gui_handler, message=text, - text=info) - - if tangoji_info is not None: - player_tangojis[i]["info"] = tangoji_info - if tangoji_clue: - player_tangojis[i]["clue"] = tangoji_clue - WorldmapMenu.create(default=5) + def got_word(tangoji_word): + if tangoji_word: + player_tangojis[i]["word"] = tangoji_word + + text = _("Enter your desired changes to this tangoji's clue.") + clue = player_tangojis[i].get("clue") or "" + xsge_gui.get_text_entry(gui_handler, message=text, text=clue, + on_close=got_clue) + + def got_clue(tangoji_clue): + text = _("Enter your desired changes to this tangoji's extra information.") + info = player_tangojis[i].get("info") or "" + + def got_info(tangoji_info): + if tangoji_info is not None: + player_tangojis[i]["info"] = tangoji_info + if tangoji_clue: + player_tangojis[i]["clue"] = tangoji_clue + + WorldmapMenu.create(default=5) + + xsge_gui.get_text_entry(gui_handler, message=text, text=info, + on_close=got_info) + + xsge_gui.get_text_entry(gui_handler, message=text, text=word, + on_close=got_word) else: play_sound(cancel_sound) WorldmapMenu.create(default=5) @@ -1422,22 +1356,39 @@ return power -def add_player_tangoji(): - global player_tangojis +def add_player_tangoji(callback=None): + # Ask for a new tangoji, one dialog after the other (they no longer + # block); callback(True) is called when it was added, callback(False) + # when a step was cancelled. + def done(ok): + if callback is not None: + callback(ok) + + def got_word(tangoji_word): + if not tangoji_word: + done(False) + return + + def got_clue(tangoji_clue): + if not tangoji_clue: + done(False) + return + + def got_info(tangoji_info): + player_tangojis.append({"word": tangoji_word, + "clue": tangoji_clue, + "info": tangoji_info}) + done(True) - text = _("Enter your new tangoji.") - tangoji_word = xsge_gui.get_text_entry(gui_handler, message=text) - if tangoji_word: - text = _("Enter the clue for your new tangoji.") - tangoji_clue = xsge_gui.get_text_entry(gui_handler, message=text) - if tangoji_clue: text = _("Enter any extra information for your new tangoji (optional).") - tangoji_info = xsge_gui.get_text_entry(gui_handler, message=text) - player_tangojis.append({"word": tangoji_word, "clue": tangoji_clue, - "info": tangoji_info}) - return True + xsge_gui.get_text_entry(gui_handler, message=text, + on_close=got_info) - return False + text = _("Enter the clue for your new tangoji.") + xsge_gui.get_text_entry(gui_handler, message=text, on_close=got_clue) + + text = _("Enter your new tangoji.") + xsge_gui.get_text_entry(gui_handler, message=text, on_close=got_word) def play_sound(sound, x=None, y=None, force=True): @@ -1516,7 +1467,8 @@ load_map() -def new_game(): +def new_game(callback): + # Ask for the player's name and then start the new game (callback). global player_name global player_zone global player_tangojis @@ -1525,17 +1477,33 @@ global player_tangojections global tangomon_encountered text = _("What is your name?") - player_name = None - while not player_name: - player_name = xsge_gui.get_text_entry(gui_handler, message=text) - player_zone = 0 - player_tangojis = [] - player_tangokans = [] - player_tangomon = [] - player_tangojections = [] - tangomon_encountered = {} - for i in ZONES: - tangomon_encountered[i] = [] + + def got_name(name): + global player_name + global player_zone + global player_tangojis + global player_tangokans + global player_tangomon + global player_tangojections + global tangomon_encountered + + if not name: + new_game(callback) + return + + player_name = name + player_zone = 0 + player_tangojis = [] + player_tangokans = [] + player_tangomon = [] + player_tangojections = [] + tangomon_encountered = {} + for i in ZONES: + tangomon_encountered[i] = [] + + callback() + + xsge_gui.get_text_entry(gui_handler, message=text, on_close=got_name) def save_game(): @@ -1610,14 +1578,21 @@ else: player_tangomon.append(random.choice(list(tangomon_sets[zone]))) - while len(player_tangojis) < TANGOJI_MIN: - r = add_player_tangoji() - if not r: - text = _("You must add a tangoji to continue.") - DialogBox(gui_handler, text).show() + def check(): + if len(player_tangojis) < TANGOJI_MIN: + def added(ok): + if ok: + check() + else: + text = _("You must add a tangoji to continue.") + DialogBox(gui_handler, text).show(on_close=check) + + add_player_tangoji(added) + else: + room = Worldmap(music="overworld.ogg") + room.start() - room = Worldmap(music="overworld.ogg") - room.start() + check() def write_to_disk(): @@ -1925,13 +1900,14 @@ sge.game.start_room = TitleScreen() # Settings - sge.game.fullscreen = fullscreen sge.game.scale_method = scale_method - if __name__ == "__main__": + async def main(): print(_("Starting game...")) try: - sge.game.start() + await sge.game.start() finally: save_game() + asyncio.run(main()) +