allow demo playback from main menu + quitting the demo player

This commit is contained in:
cidoku 2025-03-11 19:30:06 -03:00
parent 24e034b34e
commit 51b248cf76
5 changed files with 165 additions and 66 deletions

View File

@ -10,6 +10,8 @@ import audio as AUDIO
import ini import ini
from constants import * from constants import *
import gameview, mainmenu, options, demo
__builtin__.audio = AUDIO __builtin__.audio = AUDIO
del 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.setWindowFlags(QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowCloseButtonHint)
self.settingsgui = options.Settings(self) self.settingsgui = options.Settings(self)
self.demopickergui = demo.DemoPicker(self)
self.aboutgui = self.aboutBox() self.aboutgui = self.aboutBox()
def center(self): def center(self):
@ -72,6 +75,9 @@ class gamewindow(QtGui.QMainWindow):
def showSettings(self): def showSettings(self):
self.settingsgui.showSettings() self.settingsgui.showSettings()
def show_demo_picker(self):
self.demopickergui.show()
def aboutBox(self): def aboutBox(self):
box = QtGui.QMessageBox() 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())) 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 os.chdir(path.resourcePath()) # return to Resources folder
import gameview, mainmenu, options
# This hides stupid useless QT warnings # This hides stupid useless QT warnings
def handler(msg_type, msg_string): def handler(msg_type, msg_string):

62
demo.py
View File

@ -33,6 +33,7 @@ class DemoPlayer(QtCore.QObject):
self.last_bg = "" self.last_bg = ""
def start(self, file): def start(self, file):
self.paused = True
self.wait_timer.stop() self.wait_timer.stop()
self.time = 0 self.time = 0
@ -71,6 +72,7 @@ class DemoPlayer(QtCore.QObject):
self.parent.demoslider.setValue(self.time) self.parent.demoslider.setValue(self.time)
self.parent.demoslider.blockSignals(False) self.parent.demoslider.blockSignals(False)
self.time += 1 self.time += 1
self.paused = False
if packet[0] == "wait": if packet[0] == "wait":
if skip_wait: if skip_wait:
@ -133,6 +135,10 @@ class DemoPlayer(QtCore.QObject):
return return
self.step() self.step()
def stop(self):
self.paused = True
self.wait_timer.stop()
class DemoRecorder(): class DemoRecorder():
def __init__(self): def __init__(self):
self.demofile = None self.demofile = None
@ -162,6 +168,46 @@ class DemoRecorder():
line = line.encode('utf-8') line = line.encode('utf-8')
demofile.write(line) 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(): def get_demo_treeview():
demo_model = QtGui.QFileSystemModel(); demo_model = QtGui.QFileSystemModel();
demo_model.setRootPath("logs"); demo_model.setRootPath("logs");
@ -176,3 +222,19 @@ def get_demo_treeview():
demo_treeview.hideColumn(i) demo_treeview.hideColumn(i)
return demo_treeview 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("/")

View File

@ -912,6 +912,7 @@ class TCPThread(QtCore.QThread):
while True: while True:
if self.stop_now: if self.stop_now:
self.parent.tcp.close() self.parent.tcp.close()
self.parent.tcp = None
self.quit() self.quit()
return return
@ -1419,7 +1420,6 @@ class GUI(QtGui.QWidget):
self.settingsbtn.clicked.connect(self.gamewindow.showSettings) self.settingsbtn.clicked.connect(self.gamewindow.showSettings)
self.changechar = QtGui.QPushButton(self) self.changechar = QtGui.QPushButton(self)
self.changechar.setText('Switch &character')
self.changechar.clicked.connect(self.onClick_changeChar) self.changechar.clicked.connect(self.onClick_changeChar)
spacing = 1 spacing = 1
@ -1580,6 +1580,7 @@ class GUI(QtGui.QWidget):
self.stream = 0 self.stream = 0
self.specialstream = 0 self.specialstream = 0
self.download_thread = None self.download_thread = None
self.tcp = None
# Finally, load the theme # Finally, load the theme
self.width = 820 self.width = 820
@ -2147,8 +2148,13 @@ class GUI(QtGui.QWidget):
self.tcp.send("ZZ#%") self.tcp.send("ZZ#%")
def onClick_changeChar(self): def onClick_changeChar(self):
#self.tcp.send('RD#%') if self.demo_playing:
self.charselect.showCharSelect() self.inbox_timer.stop()
self.chat_tick_timer.stop()
self.disconnectCommon()
self.gamewindow.returnToMenu()
else:
self.charselect.showCharSelect()
def changeFlipCheck(self, on): def changeFlipCheck(self, on):
if on == 2: if on == 2:
@ -2285,11 +2291,16 @@ class GUI(QtGui.QWidget):
self.player_list.clear() self.player_list.clear()
self.player_kick.setDisabled(True) self.player_kick.setDisabled(True)
self.player_ban.setDisabled(True) self.player_ban.setDisabled(True)
self.demo_recorder = None
self.ooclogin.setText("Lo&gin") self.ooclogin.setText("Lo&gin")
self.login = False self.login = False
self.privateinv = 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() self.stopMusic()
def onMusicClick(self, item): def onMusicClick(self, item):
@ -3691,37 +3702,37 @@ class GUI(QtGui.QWidget):
self.onscreen_timer.stop() self.onscreen_timer.stop()
def on_demo_click(self, item): def on_demo_click(self, item):
model = self.demoitems.model() fname = demo.get_demo_fname(self.demoitems, item)
if model.isDir(item):
if not fname:
return 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.player_list.clear()
self.stopMusic() self.stopMusic()
self.iclog.clear() self.iclog.clear()
self.ooclog.clear() self.ooclog.clear()
if not self.demo_playing: 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]: self.enable_widgets(True)
widget.setEnabled(False)
self.demoslider.setVisible(True)
self.icchatinput.setVisible(False)
self.tcpthread.stop() self.tcpthread.stop()
self.demo_playing = True self.demo_playing = True
self.demoplayer.start(fname) 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.demo_player.start(fname)
def demo_seek(self, time): 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=""): def start_game(self, tcp, playerlist, charlist, musiclist, background, evidence, areas, features=[], oocjoin=[], hplist=[], webAO_bucket=""):
self.willDisconnect = False self.willDisconnect = False
@ -3863,17 +3874,8 @@ class GUI(QtGui.QWidget):
self.tcpthread.timerUpdate.connect(self.start_pause_timers) self.tcpthread.timerUpdate.connect(self.start_pause_timers)
self.tcpthread.start() self.tcpthread.start()
self.demoplayer = demo.DemoPlayer(self) self.demo_playing = False
self.demoplayer.MS_Chat.connect(self.netmsg_ms) self.enable_widgets()
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.start_demo_recorder(background) self.start_demo_recorder(background)
self.icchatinput.setFocus() self.icchatinput.setFocus()
@ -3899,30 +3901,27 @@ class GUI(QtGui.QWidget):
self.onImportEvidence(True) self.onImportEvidence(True)
self.demoplayer = demo.DemoPlayer(self) self.demo_player = demo.DemoPlayer(self)
self.demoplayer.MS_Chat.connect(self.netmsg_ms) self.demo_player.MS_Chat.connect(self.netmsg_ms)
self.demoplayer.newChar.connect(self.onPVPacket) self.demo_player.newChar.connect(self.onPVPacket)
self.demoplayer.newBackground.connect(self.setBackground) self.demo_player.newBackground.connect(self.setBackground)
self.demoplayer.OOC_Log.connect(self.ooclog.append) self.demo_player.OOC_Log.connect(self.ooclog.append)
self.demoplayer.IC_Log.connect(self.iclog.append) self.demo_player.IC_Log.connect(self.iclog.append)
self.demoplayer.charSlots.connect(partial(self.charselect.setCharList, self.charlist)) self.demo_player.charSlots.connect(partial(self.charselect.setCharList, self.charlist))
self.demoplayer.allEvidence.connect(self.allEvidence) self.demo_player.allEvidence.connect(self.allEvidence)
self.demoplayer.updatePlayerList.connect(self.updatePlayerList) self.demo_player.updatePlayerList.connect(self.updatePlayerList)
self.demoplayer.rainbowColor.connect(self.text.setStyleSheet) self.demo_player.rainbowColor.connect(self.text.setStyleSheet)
self.demoplayer.timerUpdate.connect(self.start_pause_timers) self.demo_player.timerUpdate.connect(self.start_pause_timers)
self.player_list.clear() self.player_list.clear()
self.stopMusic() self.stopMusic()
self.iclog.clear() self.iclog.clear()
self.ooclog.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]: self.changechar.setText('Disconnect')
widget.setEnabled(False) self.enable_widgets(True)
self.demoslider.setVisible(True)
self.icchatinput.setVisible(False)
self.demo_playing = True self.demo_playing = True
self.demo_player.start(fname)
self.demoplayer.start(fname)
def start_demo_recorder(self, bg): def start_demo_recorder(self, bg):
if ini.read_ini_bool("AO2XP.ini", "General", "record demos", False): if ini.read_ini_bool("AO2XP.ini", "General", "record demos", False):
@ -3931,3 +3930,26 @@ class GUI(QtGui.QWidget):
self.demo_recorder.record([["SC"] + [char[0] for char in self.charlist]], encode=True) self.demo_recorder.record([["SC"] + [char[0] for char in self.charlist]], encode=True)
self.demo_recorder.record([["BN", bg, ""]], encode=True) self.demo_recorder.record([["BN", bg, ""]], encode=True)
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')

View File

@ -76,6 +76,12 @@ class lobby(QtGui.QWidget):
self.settingsbtn.move(self.pix_lobby.size().width() - self.settingsbtn.size().width(), 0) self.settingsbtn.move(self.pix_lobby.size().width() - self.settingsbtn.size().width(), 0)
self.settingsbtn.clicked.connect(self.onSettingsClicked) 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 = PicButton(self.pix_btn_public, self)
self.btn_public.resize(self.btn_public.sizeHint()) self.btn_public.resize(self.btn_public.sizeHint())
self.btn_public.move(46, 88) self.btn_public.move(46, 88)
@ -224,6 +230,9 @@ class lobby(QtGui.QWidget):
def onSettingsClicked(self): def onSettingsClicked(self):
self.gamewindow.showSettings() self.gamewindow.showSettings()
def on_demo_clicked(self):
self.gamewindow.show_demo_picker()
def showMessageBox(self, type, title, message): def showMessageBox(self, type, title, message):
if type == 0: #critical if type == 0: #critical
return QtGui.QMessageBox.critical(self, title, message) return QtGui.QMessageBox.critical(self, title, message)
@ -298,14 +307,13 @@ class lobby(QtGui.QWidget):
self.onlineplayers.hide() self.onlineplayers.hide()
self.serverinfo.hide() self.serverinfo.hide()
self.settingsbtn.hide() self.settingsbtn.hide()
self.demobtn.hide()
self.connectprogress.setText('Connecting...') self.connectprogress.setText('Connecting...')
self.connectingimg.show() self.connectingimg.show()
self.connectcancel.show() self.connectcancel.show()
self.connectprogress.show() self.connectprogress.show()
self.aoserverinfo.tcp.send('askchaa#%') self.aoserverinfo.tcp.send('askchaa#%')
# self.gamewindow.gamewidget.start_demo("test.demo")
# self.gamewindow.stackwidget.setCurrentWidget(self.gamewindow.gamewidget)
def onClicked_cancelconnect(self): def onClicked_cancelconnect(self):
self.connectingimg.hide() self.connectingimg.hide()
@ -320,6 +328,7 @@ class lobby(QtGui.QWidget):
self.onlineplayers.show() self.onlineplayers.show()
self.serverinfo.show() self.serverinfo.show()
self.settingsbtn.show() self.settingsbtn.show()
self.demobtn.show()
def onClicked_serverlist(self, item): def onClicked_serverlist(self, item):
self.svclicked = item self.svclicked = item

View File

@ -344,15 +344,16 @@ class Settings(QtGui.QDialog):
self.gamewindow.gamewidget.text_wait_time = self.textstaytime.value() self.gamewindow.gamewidget.text_wait_time = self.textstaytime.value()
self.gamewindow.gamewidget.slide_enabled = self.enableslide.isChecked() self.gamewindow.gamewidget.slide_enabled = self.enableslide.isChecked()
if not (self.savelogs_state == self.savetolog.isChecked() and self.combinelogs_state == self.savetolog_combine.isChecked()): if not self.gamewindow.gamewidget.demo_playing:
self.gamewindow.gamewidget.ooclog.set_logfiles() if not (self.savelogs_state == self.savetolog.isChecked() and self.combinelogs_state == self.savetolog_combine.isChecked()):
self.gamewindow.gamewidget.iclog.set_logfiles(self.gamewindow.gamewidget.ooclog.logfile) 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 not self.savedemos_state == self.savedemos.isChecked():
if self.savedemos.isChecked(): if self.savedemos.isChecked():
self.gamewindow.gamewidget.start_demo_recorder() self.gamewindow.gamewidget.start_demo_recorder()
else: else:
self.gamewindow.gamewidget.demo_recorder = None self.gamewindow.gamewidget.demo_recorder = None
self.hide() self.hide()