diff --git a/AO2XP.py b/AO2XP.py index a3459ff..833acbd 100644 --- a/AO2XP.py +++ b/AO2XP.py @@ -10,6 +10,8 @@ import audio as AUDIO import ini from constants import * +import gameview, mainmenu, options, demo + __builtin__.audio = AUDIO del AUDIO @@ -48,6 +50,7 @@ class gamewindow(QtGui.QMainWindow): self.setWindowFlags(QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowCloseButtonHint) self.settingsgui = options.Settings(self) + self.demopickergui = demo.DemoPicker(self) self.aboutgui = self.aboutBox() def center(self): @@ -72,6 +75,9 @@ class gamewindow(QtGui.QMainWindow): def showSettings(self): self.settingsgui.showSettings() + def show_demo_picker(self): + self.demopickergui.show() + def aboutBox(self): box = QtGui.QMessageBox() box.setText("AO2XP %s\nRunning on %s %s %s (Python %s)\n\n2019-2025 headshot / cidoku" % (GAME_VERSION, platform.system(), platform.release(), platform.machine(), platform.python_version())) @@ -107,7 +113,6 @@ if not debugmode: os.chdir(path.resourcePath()) # return to Resources folder -import gameview, mainmenu, options # This hides stupid useless QT warnings def handler(msg_type, msg_string): diff --git a/demo.py b/demo.py index 53f6cff..d6f33cb 100644 --- a/demo.py +++ b/demo.py @@ -33,6 +33,7 @@ class DemoPlayer(QtCore.QObject): self.last_bg = "" def start(self, file): + self.paused = True self.wait_timer.stop() self.time = 0 @@ -71,6 +72,7 @@ class DemoPlayer(QtCore.QObject): self.parent.demoslider.setValue(self.time) self.parent.demoslider.blockSignals(False) self.time += 1 + self.paused = False if packet[0] == "wait": if skip_wait: @@ -132,6 +134,10 @@ class DemoPlayer(QtCore.QObject): if self.paused: return self.step() + + def stop(self): + self.paused = True + self.wait_timer.stop() class DemoRecorder(): def __init__(self): @@ -161,6 +167,46 @@ class DemoRecorder(): if encode: line = line.encode('utf-8') demofile.write(line) + +class DemoPicker(QtGui.QDialog): + def __init__(self, parent): + super(DemoPicker, self).__init__() + self.setModal(True) + self.gamewindow = parent + self.parent = parent + + self.setWindowTitle("Select a demo file...") + self.setFixedSize(480, 512) + self.setWindowIcon(QtGui.QIcon("AO2XP.ico")) + self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint) + self.hide() + + self.treeview = get_demo_treeview() + self.treeview.doubleClicked.connect(self.on_demo_click) + + self.cancelbtn = QtGui.QPushButton() + self.cancelbtn.setText("Cancel") + self.cancelbtn.clicked.connect(self.hide) + + main_layout = QtGui.QVBoxLayout(self) + btns_layout = QtGui.QHBoxLayout() + + btns_layout.addWidget(self.cancelbtn, 0, QtCore.Qt.AlignRight) + + main_layout.addWidget(self.treeview) + main_layout.addLayout(btns_layout) + + self.cancelbtn.setFocus() + + def on_demo_click(self, item): + fname = get_demo_fname(self.treeview, item) + + if not fname: + return + + self.gamewindow.gamewidget.start_demo(fname) + self.gamewindow.stackwidget.setCurrentWidget(self.gamewindow.gamewidget) + self.hide() def get_demo_treeview(): demo_model = QtGui.QFileSystemModel(); @@ -175,4 +221,20 @@ def get_demo_treeview(): for i in range(1, demo_model.columnCount()): demo_treeview.hideColumn(i) - return demo_treeview \ No newline at end of file + return demo_treeview + +def get_demo_fname(treeview, item): + model = treeview.model() + if model.isDir(item): + return None + + path = QtCore.QStringList() + + while item.isValid(): + fname = model.fileName(item) + if fname == "logs": + break + path.insert(0, fname) + item = item.parent() + + return path.join("/") \ No newline at end of file diff --git a/gameview.py b/gameview.py index c79f46f..de76a60 100644 --- a/gameview.py +++ b/gameview.py @@ -912,6 +912,7 @@ class TCPThread(QtCore.QThread): while True: if self.stop_now: self.parent.tcp.close() + self.parent.tcp = None self.quit() return @@ -1419,7 +1420,6 @@ class GUI(QtGui.QWidget): self.settingsbtn.clicked.connect(self.gamewindow.showSettings) self.changechar = QtGui.QPushButton(self) - self.changechar.setText('Switch &character') self.changechar.clicked.connect(self.onClick_changeChar) spacing = 1 @@ -1580,6 +1580,7 @@ class GUI(QtGui.QWidget): self.stream = 0 self.specialstream = 0 self.download_thread = None + self.tcp = None # Finally, load the theme self.width = 820 @@ -2147,8 +2148,13 @@ class GUI(QtGui.QWidget): self.tcp.send("ZZ#%") def onClick_changeChar(self): - #self.tcp.send('RD#%') - self.charselect.showCharSelect() + if self.demo_playing: + self.inbox_timer.stop() + self.chat_tick_timer.stop() + self.disconnectCommon() + self.gamewindow.returnToMenu() + else: + self.charselect.showCharSelect() def changeFlipCheck(self, on): if on == 2: @@ -2285,11 +2291,16 @@ class GUI(QtGui.QWidget): self.player_list.clear() self.player_kick.setDisabled(True) self.player_ban.setDisabled(True) - self.demo_recorder = None self.ooclogin.setText("Lo&gin") self.login = False self.privateinv = False - self.tcp.close() + if self.tcp: + self.tcp.close() + if self.demo_player: + self.demo_player.stop() + self.demo_player = None + self.demo_recorder = None + self.demo_playing = False self.stopMusic() def onMusicClick(self, item): @@ -3691,37 +3702,37 @@ class GUI(QtGui.QWidget): self.onscreen_timer.stop() def on_demo_click(self, item): - model = self.demoitems.model() - if model.isDir(item): + fname = demo.get_demo_fname(self.demoitems, item) + + if not fname: return - - path = QtCore.QStringList() - - while item.isValid(): - fname = model.fileName(item) - if fname == "logs": - break - path.insert(0, fname) - item = item.parent() - - fname = path.join("/") self.player_list.clear() self.stopMusic() self.iclog.clear() self.ooclog.clear() + if not self.demo_playing: - for widget in [self.oocinput, self.callmodbtn, self.changechar, self.oocnameinput, self.ooclogin, self.gametab_evidence, self.gametab_msgqueue, self.gametab_iniswap, self.gametab_mute, self.gametab_pair, self.gametab_misc, self.gametab_players, self.gametab_music, self.emotedropdown, self.posdropdown, self.flipbutton, self.sfxbutton, self.nointerruptbtn, self.effectdropdown, self.slidebutton, self.deskbtn, self.additivebtn, self.areaitems, self.shownameedit, self.colordropdown]: - widget.setEnabled(False) - self.demoslider.setVisible(True) - self.icchatinput.setVisible(False) + self.enable_widgets(True) self.tcpthread.stop() self.demo_playing = True + + self.demo_player = demo.DemoPlayer(self) + self.demo_player.MS_Chat.connect(self.netmsg_ms) + self.demo_player.newChar.connect(self.onPVPacket) + self.demo_player.newBackground.connect(self.setBackground) + self.demo_player.OOC_Log.connect(self.ooclog.append) + self.demo_player.IC_Log.connect(self.iclog.append) + self.demo_player.charSlots.connect(partial(self.charselect.setCharList, self.charlist)) + self.demo_player.allEvidence.connect(self.allEvidence) + self.demo_player.updatePlayerList.connect(self.updatePlayerList) + self.demo_player.rainbowColor.connect(self.text.setStyleSheet) + self.demo_player.timerUpdate.connect(self.start_pause_timers) - self.demoplayer.start(fname) + self.demo_player.start(fname) def demo_seek(self, time): - self.demoplayer.seek(time) + self.demo_player.seek(time) def start_game(self, tcp, playerlist, charlist, musiclist, background, evidence, areas, features=[], oocjoin=[], hplist=[], webAO_bucket=""): self.willDisconnect = False @@ -3863,17 +3874,8 @@ class GUI(QtGui.QWidget): self.tcpthread.timerUpdate.connect(self.start_pause_timers) self.tcpthread.start() - self.demoplayer = demo.DemoPlayer(self) - self.demoplayer.MS_Chat.connect(self.netmsg_ms) - self.demoplayer.newChar.connect(self.onPVPacket) - self.demoplayer.newBackground.connect(self.setBackground) - self.demoplayer.OOC_Log.connect(self.ooclog.append) - self.demoplayer.IC_Log.connect(self.iclog.append) - self.demoplayer.charSlots.connect(partial(self.charselect.setCharList, self.charlist)) - self.demoplayer.allEvidence.connect(self.allEvidence) - self.demoplayer.updatePlayerList.connect(self.updatePlayerList) - self.demoplayer.rainbowColor.connect(self.text.setStyleSheet) - self.demoplayer.timerUpdate.connect(self.start_pause_timers) + self.demo_playing = False + self.enable_widgets() self.start_demo_recorder(background) self.icchatinput.setFocus() @@ -3899,30 +3901,27 @@ class GUI(QtGui.QWidget): self.onImportEvidence(True) - self.demoplayer = demo.DemoPlayer(self) - self.demoplayer.MS_Chat.connect(self.netmsg_ms) - self.demoplayer.newChar.connect(self.onPVPacket) - self.demoplayer.newBackground.connect(self.setBackground) - self.demoplayer.OOC_Log.connect(self.ooclog.append) - self.demoplayer.IC_Log.connect(self.iclog.append) - self.demoplayer.charSlots.connect(partial(self.charselect.setCharList, self.charlist)) - self.demoplayer.allEvidence.connect(self.allEvidence) - self.demoplayer.updatePlayerList.connect(self.updatePlayerList) - self.demoplayer.rainbowColor.connect(self.text.setStyleSheet) - self.demoplayer.timerUpdate.connect(self.start_pause_timers) + self.demo_player = demo.DemoPlayer(self) + self.demo_player.MS_Chat.connect(self.netmsg_ms) + self.demo_player.newChar.connect(self.onPVPacket) + self.demo_player.newBackground.connect(self.setBackground) + self.demo_player.OOC_Log.connect(self.ooclog.append) + self.demo_player.IC_Log.connect(self.iclog.append) + self.demo_player.charSlots.connect(partial(self.charselect.setCharList, self.charlist)) + self.demo_player.allEvidence.connect(self.allEvidence) + self.demo_player.updatePlayerList.connect(self.updatePlayerList) + self.demo_player.rainbowColor.connect(self.text.setStyleSheet) + self.demo_player.timerUpdate.connect(self.start_pause_timers) self.player_list.clear() self.stopMusic() self.iclog.clear() self.ooclog.clear() - for widget in [self.oocinput, self.callmodbtn, self.changechar, self.oocnameinput, self.ooclogin, self.gametab_evidence, self.gametab_msgqueue, self.gametab_iniswap, self.gametab_mute, self.gametab_pair, self.gametab_misc, self.gametab_players, self.gametab_music, self.emotedropdown, self.posdropdown, self.flipbutton, self.sfxbutton, self.nointerruptbtn, self.effectdropdown, self.slidebutton, self.deskbtn, self.additivebtn, self.areaitems, self.shownameedit, self.colordropdown]: - widget.setEnabled(False) - self.demoslider.setVisible(True) - self.icchatinput.setVisible(False) + self.changechar.setText('Disconnect') + self.enable_widgets(True) self.demo_playing = True - - self.demoplayer.start(fname) + self.demo_player.start(fname) def start_demo_recorder(self, bg): if ini.read_ini_bool("AO2XP.ini", "General", "record demos", False): @@ -3930,4 +3929,27 @@ class GUI(QtGui.QWidget): self.demo_recorder.start() self.demo_recorder.record([["SC"] + [char[0] for char in self.charlist]], encode=True) self.demo_recorder.record([["BN", bg, ""]], encode=True) - \ No newline at end of file + + def enable_widgets(self, demo = False): + for widget in [ + self.oocinput, self.callmodbtn, + self.oocnameinput, self.ooclogin, self.gametab_evidence, + self.gametab_msgqueue, self.gametab_iniswap, self.gametab_mute, + self.gametab_pair, self.gametab_misc, self.gametab_players, + self.gametab_music, self.emotedropdown, self.posdropdown, + self.flipbutton, self.sfxbutton, self.nointerruptbtn, + self.effectdropdown, self.slidebutton, self.deskbtn, + self.additivebtn, self.areaitems, self.shownameedit, + self.colordropdown, self.defensebar.minusbtn, self.prosecutionbar.minusbtn, + self.defensebar.plusbtn, self.prosecutionbar.plusbtn, self.wtcebtn_1, + self.wtcebtn_2, self.notguiltybtn, self.guiltybtn, + self.realizationbtn, self.shakebtn, + ]: + widget.setEnabled(not demo) + self.demoslider.setVisible(demo) + self.icchatinput.setVisible(not demo) + + if demo: + self.changechar.setText('Disconnect') + else: + self.changechar.setText('Switch &character') \ No newline at end of file diff --git a/mainmenu.py b/mainmenu.py index b1b1329..d61331e 100644 --- a/mainmenu.py +++ b/mainmenu.py @@ -76,6 +76,12 @@ class lobby(QtGui.QWidget): self.settingsbtn.move(self.pix_lobby.size().width() - self.settingsbtn.size().width(), 0) self.settingsbtn.clicked.connect(self.onSettingsClicked) + self.demobtn = QtGui.QPushButton(self) + self.demobtn.setText("Play a demo") + self.demobtn.resize(self.demobtn.sizeHint()) + self.demobtn.move(self.settingsbtn.x() - self.demobtn.size().width(), 0) + self.demobtn.clicked.connect(self.on_demo_clicked) + self.btn_public = PicButton(self.pix_btn_public, self) self.btn_public.resize(self.btn_public.sizeHint()) self.btn_public.move(46, 88) @@ -223,6 +229,9 @@ class lobby(QtGui.QWidget): def onSettingsClicked(self): self.gamewindow.showSettings() + + def on_demo_clicked(self): + self.gamewindow.show_demo_picker() def showMessageBox(self, type, title, message): if type == 0: #critical @@ -298,14 +307,13 @@ class lobby(QtGui.QWidget): self.onlineplayers.hide() self.serverinfo.hide() self.settingsbtn.hide() + self.demobtn.hide() self.connectprogress.setText('Connecting...') self.connectingimg.show() self.connectcancel.show() self.connectprogress.show() self.aoserverinfo.tcp.send('askchaa#%') - # self.gamewindow.gamewidget.start_demo("test.demo") - # self.gamewindow.stackwidget.setCurrentWidget(self.gamewindow.gamewidget) def onClicked_cancelconnect(self): self.connectingimg.hide() @@ -320,6 +328,7 @@ class lobby(QtGui.QWidget): self.onlineplayers.show() self.serverinfo.show() self.settingsbtn.show() + self.demobtn.show() def onClicked_serverlist(self, item): self.svclicked = item diff --git a/options.py b/options.py index 5ea251d..0b35256 100644 --- a/options.py +++ b/options.py @@ -344,15 +344,16 @@ class Settings(QtGui.QDialog): self.gamewindow.gamewidget.text_wait_time = self.textstaytime.value() self.gamewindow.gamewidget.slide_enabled = self.enableslide.isChecked() - if not (self.savelogs_state == self.savetolog.isChecked() and self.combinelogs_state == self.savetolog_combine.isChecked()): - self.gamewindow.gamewidget.ooclog.set_logfiles() - self.gamewindow.gamewidget.iclog.set_logfiles(self.gamewindow.gamewidget.ooclog.logfile) - - if not self.savedemos_state == self.savedemos.isChecked(): - if self.savedemos.isChecked(): - self.gamewindow.gamewidget.start_demo_recorder() - else: - self.gamewindow.gamewidget.demo_recorder = None + if not self.gamewindow.gamewidget.demo_playing: + if not (self.savelogs_state == self.savetolog.isChecked() and self.combinelogs_state == self.savetolog_combine.isChecked()): + self.gamewindow.gamewidget.ooclog.set_logfiles() + self.gamewindow.gamewidget.iclog.set_logfiles(self.gamewindow.gamewidget.ooclog.logfile) + + if not self.savedemos_state == self.savedemos.isChecked(): + if self.savedemos.isChecked(): + self.gamewindow.gamewidget.start_demo_recorder() + else: + self.gamewindow.gamewidget.demo_recorder = None self.hide()