diff --git a/README.md b/README.md index 50b6caa..706bf14 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# AO2XP 2.9.3 +# AO2XP 2.9.5 This is an alternative Attorney Online 2 client, developed in **Python 2.7**, specifically made for older computers running Windows XP that cannot run the diff --git a/demo.py b/demo.py index f7d10a1..6e83253 100644 --- a/demo.py +++ b/demo.py @@ -5,15 +5,18 @@ from bisect import bisect_left class DemoPlayer(QtCore.QObject): MS_Chat = QtCore.pyqtSignal(list) - newChar = QtCore.pyqtSignal(str) + newChar = QtCore.pyqtSignal(int) newBackground = QtCore.pyqtSignal(str, bool) IC_Log = QtCore.pyqtSignal(str) OOC_Log = QtCore.pyqtSignal(str) - charSlots = QtCore.pyqtSignal() + charSlots = QtCore.pyqtSignal(list) loadAllEvidence = QtCore.pyqtSignal(list) rainbowColor = QtCore.pyqtSignal(str) updatePlayerList = QtCore.pyqtSignal(str, int, int, str) timerUpdate = QtCore.pyqtSignal(int, int, int) + authStatusChanged = QtCore.pyqtSignal(int) + updateAreaList = QtCore.pyqtSignal(list) + setCharList = QtCore.pyqtSignal(list) def __init__(self, parent): super(DemoPlayer, self).__init__(parent) diff --git a/gameview.py b/gameview.py index 285de49..7593625 100644 --- a/gameview.py +++ b/gameview.py @@ -1,28 +1,22 @@ -import thread, time, os, urllib, random, re, platform, subprocess -import charselect, ini, AOsocket, images, packets, demo, buttons +# This is the main file where everything happens. Honestly, it sucks and the +# thing is so complex and unsafe that I'm surprised it even works. I wish for +# a simpler client. ~ cidoku +import thread, time, os, urllib, random, platform, subprocess from os.path import exists, basename -from ConfigParserEdit import ConfigParser -from constants import * from collections import OrderedDict -from pybass_constants import * -from PyQt4 import QtGui, QtCore from functools import partial from ctypes import create_string_buffer from urllib2 import Request, urlopen +from PyQt4 import QtGui, QtCore +from ConfigParserEdit import ConfigParser +from constants import * +from pybass_constants import BASS_ATTRIB_VOL, BASS_ACTIVE_PAUSED, BASS_STREAM_AUTOFREE, BASS_SAMPLE_LOOP +import charselect, ini, images, packets, demo, buttons, audio DOWNLOAD_BLACKLIST = [] - -URL_REGEX = r"https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)" - bucket = "" -def delay(msec): - dieTime = QtCore.QTime.currentTime().addMSecs(msec) - - while QtCore.QTime.currentTime() < dieTime: - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 100) - def getCharIni(char, section, value, default=""): tempini = ConfigParser() return ini.read_ini(BASE_PATH + 'characters/' + char.lower() + '/char.ini', section, value, default) @@ -242,7 +236,6 @@ class ChatLogs(QtGui.QTextEdit): class AOCharMovie(QtGui.QLabel): done = QtCore.pyqtSignal() - usePillow = 0 pillowFrames = [] pillowFrame = 0 pillowSpeed = 0 @@ -289,7 +282,7 @@ class AOCharMovie(QtGui.QLabel): def setFlipped(self, flip): self.mFlipped = flip - + def setAlignPos(self, pos): if pos == "def": self.setAlignment(QtCore.Qt.AlignLeft) @@ -298,6 +291,21 @@ class AOCharMovie(QtGui.QLabel): else: self.setAlignment(QtCore.Qt.AlignCenter) + def findSprite(self, char, emote, prefix): + bases = [ + "{char}/{prefix}{emote}", + "{char}/{emote}", + "{char}/{prefix}/{emote}" + ] + exts = [".apng", ".png", ".gif", ".webp"] + + for base in bases: + for ext in exts: + path = BASE_PATH + "characters/" + base.format(char=char, prefix=prefix, emote=emote) + ext + if exists(path): + return path, ext + return None + def play(self, pChar, pEmote, emotePrefix, scaling = SCALING_AUTO, singleFrameDuration = -1): if not len(pEmote): return @@ -319,118 +327,51 @@ class AOCharMovie(QtGui.QLabel): pChar = pChar.lower() pEmote = pEmote.lower() - apngPath = testPath( - BASE_PATH + "characters/" + pChar + "/" + emotePrefix + pEmote + ".apng", - BASE_PATH + "characters/" + pChar + "/" + emotePrefix + "/" + pEmote + ".apng", - BASE_PATH + "characters/" + pChar + "/" + pEmote + ".apng", - BASE_PATH + "characters/" + pChar + "/(a)" + pEmote + ".apng", - BASE_PATH + "characters/" + pChar + "/(b)" + pEmote + ".apng", - BASE_PATH + "characters/" + pChar + "/(a)/" + pEmote + ".apng", - BASE_PATH + "characters/" + pChar + "/(b)/" + pEmote + ".apng" - ) - placeholderPath = AO2XPpath + "themes/default/oldplaceholder.gif" - imgPath = "" + imgPath, ext = self.findSprite(pChar, pEmote, emotePrefix) - if apngPath: - imgPath = apngPath - self.usePillow = 1 - else: - pngPath = testPath( - BASE_PATH + "characters/" + pChar + "/" + emotePrefix + pEmote + ".png", - BASE_PATH + "characters/" + pChar + "/" + emotePrefix + "/" + pEmote + ".png", - BASE_PATH + "characters/" + pChar + "/" + pEmote + ".png", - BASE_PATH + "characters/" + pChar + "/(a)" + pEmote + ".png", - BASE_PATH + "characters/" + pChar + "/(b)" + pEmote + ".png", - BASE_PATH + "characters/" + pChar + "/(a)/" + pEmote + ".png", - BASE_PATH + "characters/" + pChar + "/(b)/" + pEmote + ".png" - ) - - if pngPath: - imgPath = pngPath - self.usePillow = 0 + if not imgPath: + if self.prevGifPath: + imgPath = self.prevGifPath else: - webpPath = testPath( - BASE_PATH + "characters/" + pChar + "/" + emotePrefix + pEmote + ".webp", - BASE_PATH + "characters/" + pChar + "/" + emotePrefix + "/" + pEmote + ".webp", - BASE_PATH + "characters/" + pChar + "/" + pEmote + ".webp", - BASE_PATH + "characters/" + pChar + "/(a)" + pEmote + ".webp", - BASE_PATH + "characters/" + pChar + "/(b)" + pEmote + ".webp", - BASE_PATH + "characters/" + pChar + "/(a)/" + pEmote + ".webp", - BASE_PATH + "characters/" + pChar + "/(b)/" + pEmote + ".webp" - ) - - if webpPath: - imgPath = webpPath - self.usePillow = 2 - else: - gifPath = testPath( - BASE_PATH + "characters/" + pChar + "/" + emotePrefix + pEmote + ".gif", - BASE_PATH + "characters/" + pChar + "/" + emotePrefix + "/" + pEmote + ".gif", - BASE_PATH + "characters/" + pChar + "/" + pEmote + ".gif", - BASE_PATH + "characters/" + pChar + "/(a)" + pEmote + ".gif", - BASE_PATH + "characters/" + pChar + "/(b)" + pEmote + ".gif", - BASE_PATH + "characters/" + pChar + "/(a)/" + pEmote + ".gif", - BASE_PATH + "characters/" + pChar + "/(b)/" + pEmote + ".gif" - ) - - if gifPath: - imgPath = gifPath - self.usePillow = 0 - else: - if ini.read_ini_bool("AO2XP.ini", "General", "download characters"): - pass - # url = "base/characters/" + pChar.lower() + "/" + emotePrefix + pEmote.lower() + ".gif" - # url = url.replace(" ", "%20") - # thread.start_new_thread(downloadThread, (url, gifPath)) - - if exists(placeholderPath): - imgPath = placeholderPath - print "[debug] Sprite not found:", pChar, pEmote, emotePrefix - else: - imgPath = "" - self.usePillow = 0 - - if imgPath == "": - imgPath = self.prevGifPath - # Second check just in case - if imgPath == "": if exists(placeholderPath): imgPath = placeholderPath - print "[debug] Sprite not found: ", pChar, pEmote, emotePrefix + print "[debug] Sprite not found:", pChar, pEmote, emotePrefix else: imgPath = "placeholder.png" + # if ini.read_ini_bool("AO2XP.ini", "General", "download characters"): + # url = "base/characters/" + pChar.lower() + "/" + emotePrefix + pEmote.lower() + ".gif" + # url = url.replace(" ", "%20") + # thread.start_new_thread(downloadThread, (url, gifPath)) else: self.prevGifPath = imgPath - #print "[debug]", pChar, emotePrefix, pEmote, "(!) path is null!" if imgPath == "" else imgPath - - if not self.usePillow: - self.mMovie.setFileName(imgPath) - self.mMovie.start() - elif self.usePillow == 1: # apng - self.pillowFrames = images.load_apng(apngPath) - if len(self.pillowFrames) > 1: - self.pillowTimer.start(int(self.pillowFrames[0][1] * self.pillowSpeed)) - else: - self.pillowTimer.start(int(singleFrameDuration * self.pillowSpeed)) - - self.setPillowFrame() - elif self.usePillow == 2: # webp - try: - self.pillowFrames = images.load_webp(webpPath) + if ext == ".apng": + self.pillowFrames = images.load_apng(imgPath) if len(self.pillowFrames) > 1: self.pillowTimer.start(int(self.pillowFrames[0][1] * self.pillowSpeed)) else: self.pillowTimer.start(int(singleFrameDuration * self.pillowSpeed)) self.setPillowFrame() - except: - if exists(placeholderPath): - imgPath = placeholderPath - print "[debug] Couldn't load webp sprite!" - else: - imgPath = "placeholder.png" + elif ext == ".webp": + try: + self.pillowFrames = images.load_webp(imgPath) + if len(self.pillowFrames) > 1: + self.pillowTimer.start(int(self.pillowFrames[0][1] * self.pillowSpeed)) + else: + self.pillowTimer.start(int(singleFrameDuration * self.pillowSpeed)) + + self.setPillowFrame() + except: + if exists(placeholderPath): + imgPath = placeholderPath + print "[debug] Couldn't load webp sprite!" + else: + imgPath = "placeholder.png" + self.mMovie.setFileName(imgPath) + self.mMovie.start() + else: self.mMovie.setFileName(imgPath) self.mMovie.start() @@ -668,13 +609,18 @@ class AOMovie(QtGui.QLabel): self.pillowLabel.clear() self.pillowLabel.hide() self.hide() + + def finish(self): + self.stop() + self.done.emit() @QtCore.pyqtSlot(int) def frameChange(self, nFrame): if nFrame == self.mMovie.frameCount() - 1 and self.playOnce: - delay(self.mMovie.nextFrameDelay()) - self.stop() - self.done.emit() + QtCore.QTimer.singleShot( + self.mMovie.nextFrameDelay(), + self.finish + ) @QtCore.pyqtSlot() def pillowFrameChange(self): @@ -682,7 +628,7 @@ class AOMovie(QtGui.QLabel): if len(self.pillowFrames) - 1 == self.pillowFrame: if self.playOnce or (self.usePillow == 2 and self.pillowLoops+1 == self.webpLoops): - delay(int(self.pillowFrames[self.pillowFrame][1] * self.pillowSpeed)) + self.msleep(int(self.pillowFrames[self.pillowFrame][1] * self.pillowSpeed)) self.stop() self.done.emit() elif len(self.pillowFrames) > 1: # loop @@ -746,7 +692,7 @@ class WTCEView(QtGui.QLabel): super(WTCEView, self).move(self.parent.viewport.x(), self.parent.viewport.y()) super(WTCEView, self).resize(self.parent.viewport.size()) - def frameChange(self, frame): + def frameChange(self): if self.movie.state() != QtGui.QMovie.Running: return img = self.movie.currentImage() @@ -763,17 +709,30 @@ class WTCEView(QtGui.QLabel): def showWTCE(self, wtce, variant=0): self.finished() + + wtcefile = BASE_PATH + "sounds/general/sfx-testimony2" + if self.parent.wtceSfx: + audio.freeHandle(self.parent.wtceSfx) + if wtce == 'testimony1': self.movie.setFileName(AO2XPpath + 'themes/default/witnesstestimony.gif') elif wtce == 'testimony2': self.movie.setFileName(AO2XPpath + 'themes/default/crossexamination.gif') elif wtce == "judgeruling": if variant == 0: + wtcefile = BASE_PATH + "sounds/general/sfx-notguilty" self.movie.setFileName(AO2XPpath + 'themes/default/notguilty.gif') elif variant == 1: + wtcefile = BASE_PATH + "sounds/general/sfx-guilty" self.movie.setFileName(AO2XPpath + 'themes/default/guilty.gif') else: return + + self.parent.wtceSfx = audio.loadHandle(False, wtcefile+".opus" if exists(wtcefile+".opus") else wtcefile+".wav", 0, 0, 0) + audio.setHandleAttr(self.parent.wtceSfx, BASS_ATTRIB_VOL, self.parent.sliSoundVolume.value() / 100.0) + + if self.parent.wtceSfx: + audio.playHandle(self.parent.wtceSfx, True) self.show() self.movie.start() @@ -888,7 +847,7 @@ class EditEvidenceDialog(QtGui.QDialog): def onBrowseClicked(self): path = str(QtGui.QFileDialog.getOpenFileName(self, "Select an image", BASE_PATH + 'evidence', "Images (*.png)")) if path: - if not "/evidence/" in path.lower(): + if "/evidence/" not in path.lower(): QtGui.QMessageBox.warning(self, 'Wrong directory', 'Please select a file from the "evidence" directory.') self.onBrowseClicked() return @@ -921,16 +880,19 @@ class EditEvidenceDialog(QtGui.QDialog): class TCPThread(QtCore.QThread): connectionError = QtCore.pyqtSignal(str, str, str) MS_Chat = QtCore.pyqtSignal(list) - newChar = QtCore.pyqtSignal(str) + newChar = QtCore.pyqtSignal(int) newBackground = QtCore.pyqtSignal(str, bool) IC_Log = QtCore.pyqtSignal(str) OOC_Log = QtCore.pyqtSignal(str) - charSlots = QtCore.pyqtSignal() + charSlots = QtCore.pyqtSignal(list) showCharSelect = QtCore.pyqtSignal() loadAllEvidence = QtCore.pyqtSignal(list) rainbowColor = QtCore.pyqtSignal(str) updatePlayerList = QtCore.pyqtSignal(str, int, int, str) timerUpdate = QtCore.pyqtSignal(int, int, int) + authStatusChanged = QtCore.pyqtSignal(int) + updateAreaList = QtCore.pyqtSignal(list) + setCharList = QtCore.pyqtSignal(list) send_attempts = 0 max_attempts = 5 @@ -944,7 +906,6 @@ class TCPThread(QtCore.QThread): pingtimer = 150 rainbow = 0 sendtick = 0 - tempdata = "" color = QtGui.QColor() color.setHsv(rainbow, 255, 255) while True: @@ -961,7 +922,7 @@ class TCPThread(QtCore.QThread): pingtimer -= 1 if pingtimer == 0: - pingbefore = time.time() + # pingbefore = time.time() self.parent.tcp.send('CH#%') pingtimer = 150 @@ -1027,8 +988,7 @@ class Chatbox(QtGui.QLabel): class GUI(QtGui.QWidget): mainWindow = None - # In theory 3 sounds may play at the same time: character, evidence sweep, - # effect + # In theory 3 sounds may play at the same time: character, evidence sweep, effect soundChannels = 3 soundChannel = 0 sound = [] @@ -1767,7 +1727,7 @@ class GUI(QtGui.QWidget): self.tcp = None self.demoPlayer = None self.gotPV = False - + # TTS fun self.speaker = None if getOption("General", "tts", False) == 'True': @@ -1897,7 +1857,7 @@ class GUI(QtGui.QWidget): move[e].move(*d[0:2]) else: move[e].move(-500, -500) - + if "courtroom" in design: self.width = int(design["courtroom"][2]) self.height = int(design["courtroom"][3]) @@ -1914,7 +1874,7 @@ class GUI(QtGui.QWidget): if "found_song_color" in design: self.foundSongItemColor = QtGui.QColor(*[int(x) for x in design["found_song_color"]]) - + self.cbSlide.setVisible("slide_enable" in design) self.cbBench.setVisible("ao2xp_desk" in design) self.OOCLogin.setVisible("ao2xp_login" in design) @@ -1950,7 +1910,7 @@ class GUI(QtGui.QWidget): self.viewportScale = self.viewport.height() / float(192) self.viewportRatio = self.viewport.width() / float(self.viewport.height()) - + # If the viewport is wide, align def/pro sprites properly self.alignChars = (self.viewportRatio > 1.5) @@ -2124,7 +2084,7 @@ class GUI(QtGui.QWidget): def _themeCommon(self): # IC input and demo slider - viewportRight = max(self.viewport.x() + self.viewport.width(), 512) + # viewportRight = max(self.viewport.x() + self.viewport.width(), 512) # IC options gameTabsHeight = self.gameTabs.height() - self.gameTabs.findChild(QtGui.QTabBar).height() @@ -2572,8 +2532,11 @@ class GUI(QtGui.QWidget): self.tcp.send("RT#judgeruling#" +str(variant)+ "#%") self.ICChatFocus() - def onPVPacket(self, charName=""): + def onPVPacket(self, char=0): QtGui.QApplication.restoreOverrideCursor() + self.myChar = char + self.charSelect.hide() + charName = self.charList[char][0] self.setDisabled(False) self.gotPV = True if not self.swapping and charName: @@ -3037,7 +3000,7 @@ class GUI(QtGui.QWidget): if self.willDisconnect: self.disconnectCommon() self.mainWindow.returnToMenu() - + def exitCommon(self): self.disconnectCommon() if self.speaker: @@ -3045,6 +3008,7 @@ class GUI(QtGui.QWidget): self.speaker.wait() def disconnectCommon(self): + QtGui.QApplication.restoreOverrideCursor() self.gotPV = False self.charName = "" self.onSwitchInventory(True) @@ -3089,40 +3053,27 @@ class GUI(QtGui.QWidget): def onOOCReturn(self): text = encodeAOString(self.OOCInput.text()).replace('\\n', '\n') - if text.startsWith('//'): - code = str(self.OOCInput.text()).replace('//', '', 1).replace('\\NEWLINE', '\n') - try: - exec code - except Exception as e: - msg = 'code error\n' - for arg in e.args: - msg += str(arg) + '\n' - - msg = msg.rstrip() - self.OOCLog.append(msg) - return - return - elif text.startsWith("/pos "): # why....... + if text.startsWith("/pos "): # why....... ind = self.boxPositions.findText(str(text.split(" ")[1])) if ind >= 0: self.boxPositions.setCurrentIndex(ind) self.OOCInput.clear() return - if self.cbMockText.isChecked(): - text = mockString(text) - if self.cbAutoCaps.isChecked(): - l = QtCore.QStringList(list(text)) - l[0] = l[0].toUpper() + # if self.cbMockText.isChecked(): + # text = mockString(text) + # if self.cbAutoCaps.isChecked(): + # l = QtCore.QStringList(list(text)) + # l[0] = l[0].toUpper() - last = [".", "?", "!", ")", "]"] - if not l[-1] in last: - l.append(".") - text = l.join("").replace(" i ", " I ").replace("i'm", "I'm").replace("it's", "It's") - if self.cbSpacing.isChecked(): - l = QtCore.QStringList(list(text)) - for i in range(1, len(l)+len(l)-1, 2): - l.insert(i, " ") - text = l.join("") + # last = [".", "?", "!", ")", "]"] + # if not l[-1] in last: + # l.append(".") + # text = l.join("").replace(" i ", " I ").replace("i'm", "I'm").replace("it's", "It's") + # if self.cbSpacing.isChecked(): + # l = QtCore.QStringList(list(text)) + # for i in range(1, len(l)+len(l)-1, 2): + # l.insert(i, " ") + # text = l.join("") self.sendOOCchat(self.OOCNameInput.text().toUtf8(), text) self.OOCInput.clear() @@ -3614,7 +3565,7 @@ class GUI(QtGui.QWidget): emoteMod = int(self.mChatMessage[EMOTE_MOD]) if emoteMod == 0: self.mChatMessage[EMOTE_MOD] = 1 - + # TTS fun if self.speaker: self.speaker.interrupt.emit() @@ -4024,7 +3975,7 @@ class GUI(QtGui.QWidget): else: self.char.playIdle(fChar, fEmote, self.scaling[0]) self.animState = 3 - + # TTS fun if self.speaker: self.speaker.say.emit(self.mChatMessage[CHATMSG], self.blip) @@ -4555,7 +4506,7 @@ class GUI(QtGui.QWidget): _dataInt, ok = data.toInt() _data = self.charList[int(_dataInt)][0] if ok else decodeAOString(data) self.playerList[pid][utype] = _data - + item = self.playerItems.findItems("[%s]" % pid, QtCore.Qt.MatchStartsWith) if item: name = self.playerList[pid][0] @@ -4607,39 +4558,11 @@ class GUI(QtGui.QWidget): if self.onscreenTimerTimes == [0, 0, 0, 0, 0]: self.onscreenTimer.stop() - - def onDemoClicked(self, item): - fname = demo.getDemoFilename(self.demoItems, item) - - if not fname: - return - - self.playerItems.clear() - self.stopMusic() - self.ICLog.clear() - self.OOCLog.clear() - - if not self.demoPlaying: - self.enableWidgets(True) - self.tcpThread.stop() - self.demoPlaying = True - - self.demoPlayer = demo.DemoPlayer(self) - self.demoPlayer.MS_Chat.connect(self.netmsgMS) - 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.loadAllEvidence.connect(self.loadAllEvidence) - self.demoPlayer.updatePlayerList.connect(self.updatePlayerList) - self.demoPlayer.rainbowColor.connect(self.text.setStyleSheet) - self.demoPlayer.timerUpdate.connect(self.startPauseOnscreenTimers) - - self.demoPlayer.start(fname) - - def demoSeek(self, time): - self.demoPlayer.seek(time) + + def setCharSlots(self, data): + for i, value in enumerate(data): + self.charList[i][1] = int(value) + self.charSelect.setCharList(self.charList) def startGame(self, tcp, playerList, charList, musicList, background, evidence, areas, features=[], joinOOC=[], hpList=[], webAO_bucket=""): self.willDisconnect = False @@ -4739,7 +4662,7 @@ class GUI(QtGui.QWidget): self.boxEvidence.clear() for evi in evidence: self.boxEvidence.addItem(evi[0].strip()) - + if tcp.isWS and tcp.isSecure: self.OOCLog.append("--- Using a secure connection ---") @@ -4786,18 +4709,34 @@ class GUI(QtGui.QWidget): self.tcpThread.newBackground.connect(self.setBackground) self.tcpThread.OOC_Log.connect(self.OOCLog.append) self.tcpThread.IC_Log.connect(self.ICLog.append) - self.tcpThread.charSlots.connect(partial(self.charSelect.setCharList, self.charList)) + self.tcpThread.charSlots.connect(self.setCharSlots) self.tcpThread.showCharSelect.connect(self.charSelect.showCharSelect) self.tcpThread.loadAllEvidence.connect(self.loadAllEvidence) self.tcpThread.updatePlayerList.connect(self.updatePlayerList) + self.tcpThread.authStatusChanged.connect(self.authStatusChanged) self.tcpThread.rainbowColor.connect(self.text.setStyleSheet) self.tcpThread.timerUpdate.connect(self.startPauseOnscreenTimers) + self.tcpThread.updateAreaList.connect(self.updateAreaList) self.tcpThread.start() self.demoPlaying = False self.startDemoRecorder(background) self.ICChatInput.setFocus() + + def newDemoPlayer(self): + self.demoPlayer = demo.DemoPlayer(self) + self.demoPlayer.MS_Chat.connect(self.netmsgMS) + 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(self.setCharSlots) + self.demoPlayer.loadAllEvidence.connect(self.loadAllEvidence) + self.demoPlayer.updatePlayerList.connect(self.updatePlayerList) + self.demoPlayer.rainbowColor.connect(self.text.setStyleSheet) + self.demoPlayer.timerUpdate.connect(self.startPauseOnscreenTimers) + self.demoPlayer.setCharList.connect(self.onSCPacket) def startDemo(self, fname): self.playerList = [] @@ -4819,18 +4758,8 @@ class GUI(QtGui.QWidget): self.sliBlipsVolume.setValue(ini.read_ini_int("AO2XP.ini", "Audio", "Blip volume", 100)) self.onImportEvidence(True) - - self.demoPlayer = demo.DemoPlayer(self) - self.demoPlayer.MS_Chat.connect(self.netmsgMS) - 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.loadAllEvidence.connect(self.loadAllEvidence) - self.demoPlayer.updatePlayerList.connect(self.updatePlayerList) - self.demoPlayer.rainbowColor.connect(self.text.setStyleSheet) - self.demoPlayer.timerUpdate.connect(self.startPauseOnscreenTimers) + + self.newDemoPlayer() self.playerItems.clear() self.stopMusic() @@ -4850,6 +4779,28 @@ class GUI(QtGui.QWidget): if bg: self.demoRecorder.record([["BN", bg, ""]], encode=True) + def onDemoClicked(self, item): + fname = demo.getDemoFilename(self.demoItems, item) + + if not fname: + return + + self.playerItems.clear() + self.stopMusic() + self.ICLog.clear() + self.OOCLog.clear() + + if not self.demoPlaying: + self.enableWidgets(True) + self.tcpThread.stop() + self.demoPlaying = True + self.newDemoPlayer() + + self.demoPlayer.start(fname) + + def demoSeek(self, time): + self.demoPlayer.seek(time) + def enableWidgets(self, demo = False): for widget in [ self.OOCInput, self.btnCallMod, @@ -4859,10 +4810,11 @@ class GUI(QtGui.QWidget): self.tabMusic, self.boxEmotes, self.boxPositions, self.cbFlip, self.cbPreanim, self.cbNoInterrupt, self.boxEffects, self.cbSlide, self.cbBench, - self.cbAdditive, self.areaItems, - self.boxColors, self.btnDefenseBar.btnMinus, self.btnProsecutionBar.btnMinus, - self.btnDefenseBar.btnPlus, self.btnProsecutionBar.btnPlus, self.btnWitnessTestimony, - self.btnCrossExamination, self.btnNotGuilty, self.btnGuilty, + self.cbAdditive, self.areaItems, self.boxSounds, self.boxColors, + self.btnDefenseBar.btnMinus, self.btnProsecutionBar.btnMinus, + self.btnDefenseBar.btnPlus, self.btnProsecutionBar.btnPlus, + self.btnWitnessTestimony, self.btnCrossExamination, + self.btnNotGuilty, self.btnGuilty, self.btnRealization, self.btnShake, ]: widget.setEnabled(not demo) @@ -4875,7 +4827,35 @@ class GUI(QtGui.QWidget): else: self.btnChangeChar.setText('Switch &character') - def updateAreaList(self): + def updateAreaList(self, data=None): + if data: + kind = int(data[0]) + self.areas[kind] = [data[i] for i in range(1, len(data))] + + # This is much harder than doing it during the handshake because of + # the way the music list is implemented + if self.noARUP: + self.noARUP = False + self.areasLen = len(self.areas[kind]) + print '[client]', 'The server has %d areas' % self.areasLen + + if self.areasLen: + for i in range(self.areasLen): + area_key, area_val = self.musicList.items()[0] + self.areas[4].append(area_val) + + areaitem = QtGui.QListWidgetItem() + self.areaItems.addItem(areaitem) + + for j in range(4): + if j != kind: + self.areas[j].append("") + + # Remove the area from the music list + self.musicList.popitem(False) + + self.loadAllMusic() + try: for i in range(self.areasLen): areaPlayers = self.areas[0][i] @@ -4895,3 +4875,20 @@ class GUI(QtGui.QWidget): except Exception as e: print "[debug] Couldn't update areas. Details:" print "[debug]", e + + def authStatusChanged(self, status): + statusStrings = ["You have logged out", "Wrong password", "Logged in"] + if status == 1: + self.login = True + self.btnPlayerKick.setDisabled(False) + self.btnPlayerBan.setDisabled(False) + self.OOCLogin.setText("Lo&g out") + elif status == -1: + self.login = False + self.btnPlayerKick.setDisabled(True) + self.btnPlayerBan.setDisabled(True) + self.OOCLogin.setText("Lo&gin") + self.OOCLog.append("%s" % (statusStrings[status+1])) + + def onSCPacket(self, data): + self.charList = data \ No newline at end of file diff --git a/mainmenu.py b/mainmenu.py index 6ca52e5..e8d3714 100644 --- a/mainmenu.py +++ b/mainmenu.py @@ -345,10 +345,8 @@ class MasterServer(QtCore.QThread): def run(self): try: - tempdata = "" self.msHttp = requests.get("http://servers.aceattorneyonline.com/servers") self.msMotd = requests.get("http://servers.aceattorneyonline.com/motd") - if self.msHttp.ok: self.gotServers.emit(json.loads(self.msHttp.content)) if self.msMotd.ok: self.gotOOCMsg.emit(self.msMotd.content) except Exception as e: diff --git a/packets.py b/packets.py index fe9cddf..4b9722f 100644 --- a/packets.py +++ b/packets.py @@ -50,15 +50,14 @@ def handlePackets(caller, total, record=True): caller.OOC_Log.emit("%s: %s" % (name, chatmsg.replace("<", "<").replace("<br />","
") if len(network) > 3 and network[3] == "0" else chatmsg)) elif header == 'PV': - caller.parent.myChar = int(network[3]) - caller.parent.charSelect.hide() - caller.newChar.emit(caller.parent.charList[caller.parent.myChar][0]) + caller.newChar.emit(int(network[3])) elif header == 'LE': del network[0] caller.loadAllEvidence.emit([evi.split('&') for evi in network]) elif header == 'ZZ': + # TODO: Remove from tcp thread if caller.parent.modcall: audio.freeHandle(caller.parent.modcall) caller.parent.modcall = audio.loadHandle(0, "mod_call.wav", 0, 0, 0) @@ -69,31 +68,14 @@ def handlePackets(caller, total, record=True): caller.OOC_Log.emit('[MOD CALL] ' + network[1].replace("\n", "
") + '
') else: caller.OOC_Log.emit('[MOD CALL] But there was no extra information. (old server?)') + elif header == 'CharsCheck': del network[0] - for i in range(len(network)): - caller.parent.charList[i][1] = int(network[i]) - - caller.charSlots.emit() + caller.charSlots.emit(network) elif header == 'RT': testimony = network[1] - wtcefile = BASE_PATH + "sounds/general/sfx-testimony2" - if caller.parent.wtceSfx: - audio.freeHandle(caller.parent.wtceSfx) - - if testimony == 'judgeruling': - variant = int(network[2]) - if variant == 0: - wtcefile = BASE_PATH + "sounds/general/sfx-notguilty" - elif variant == 1: - wtcefile = BASE_PATH + "sounds/general/sfx-guilty" - else: - variant = 0 - caller.parent.wtceSfx = audio.loadHandle(False, wtcefile+".opus" if exists(wtcefile+".opus") else wtcefile+".wav", 0, 0, 0) - audio.setHandleAttr(caller.parent.wtceSfx, BASS_ATTRIB_VOL, caller.parent.sliSoundVolume.value() / 100.0) - if caller.parent.wtceSfx: - audio.playHandle(caller.parent.wtceSfx, True) + variant = int(network[2]) if testimony == 'judgeruling' else 0 caller.parent.wtceSignal.emit(testimony, variant) elif header == 'HP': @@ -102,31 +84,22 @@ def handlePackets(caller, total, record=True): caller.parent.healthbars.emit(kind, health) elif header == 'KK': + # TODO: Show message from GUI thread reason = network[1] caller.parent.emit(QtCore.SIGNAL('showMessage(QString, QString, QString)'), 'critical', 'Connection lost', 'You were kicked from the server. (%s)' % reason) elif header == 'KB': + # TODO: Show message from GUI thread reason = network[1] caller.parent.emit(QtCore.SIGNAL('showMessage(QString, QString, QString)'), 'critical', 'Connection lost', 'You have been banned from the server. (%s)' % reason) elif header == 'BB': # message popup (AO 2.9) + # TODO: Show message from GUI thread message = network[1] caller.parent.emit(QtCore.SIGNAL('showMessage(QString, QString, QString)'), 'information', 'Message from server', message) elif header == 'AUTH': # login status (AO 2.9) - status = int(network[1]) - statusStrings = ["You have logged out", "Wrong password", "Logged in"] - if status == 1: - caller.parent.login = True - caller.parent.btnPlayerKick.setDisabled(False) - caller.parent.btnPlayerBan.setDisabled(False) - caller.parent.OOCLogin.setText("Lo&g out") - elif status == -1: - caller.parent.login = False - caller.parent.btnPlayerKick.setDisabled(True) - caller.parent.btnPlayerBan.setDisabled(True) - caller.parent.OOCLogin.setText("Lo&gin") - caller.OOC_Log.emit("%s" % (statusStrings[status+1])) + caller.authStatusChanged.emit(int(network[1])) # elif header == "CHECK": #ping # pingafter = time.time() @@ -145,33 +118,7 @@ def handlePackets(caller, total, record=True): elif header == 'ARUP': del network[0] - kind = int(network[0]) - caller.parent.areas[kind] = [network[i] for i in range(1, len(network))] - - # This is much harder than doing it during the handshake because of the way the music list is implemented - if caller.parent.noARUP: - caller.parent.noARUP = False - caller.parent.areasLen = len(caller.parent.areas[kind]) - print '[client]', 'The server has %d areas' % caller.parent.areasLen - - if caller.parent.areasLen: - for i in range(caller.parent.areasLen): - area_key, area_val = caller.parent.musicList.items()[0] - caller.parent.areas[4].append(area_val) - - areaitem = QtGui.QListWidgetItem() - caller.parent.areaItems.addItem(areaitem) - - for j in range(4): - if j != kind: - caller.parent.areas[j].append("") - - # Remove the area from the music list - caller.parent.musicList.popitem(False) - - caller.parent.loadAllMusic() - - caller.parent.updateAreaList() + caller.updateAreaList.emit(network) elif header == 'TI': del network[0] @@ -186,4 +133,4 @@ def handlePackets(caller, total, record=True): # For demos elif header == 'SC': del network[0] - caller.parent.charList = [ [char.split('&')[0].decode('utf-8'), 0, "male", True ] for char in network ] \ No newline at end of file + caller.setCharList.emit([ [char.split('&')[0].decode('utf-8'), 0, "male", True ] for char in network ]) \ No newline at end of file