From 2e589f1b36417c983a9e123d6510758fb866c6f3 Mon Sep 17 00:00:00 2001 From: cidoku Date: Wed, 12 Feb 2025 19:57:36 -0300 Subject: [PATCH] add tooltips to character selection --- charselect.py | 244 ++-- freeglut.dll | Bin 218624 -> 0 bytes gameview_scaled.py | 2882 -------------------------------------------- 3 files changed, 122 insertions(+), 3004 deletions(-) delete mode 100644 freeglut.dll delete mode 100644 gameview_scaled.py diff --git a/charselect.py b/charselect.py index 0409261..cf9ff84 100644 --- a/charselect.py +++ b/charselect.py @@ -6,132 +6,132 @@ from buttons import PixmapButton AOpath = "base/" AO2XPpath = "AO2XPbase/" -#AOpath = "I:/aovanilla1.7.5/client/base/" def get_option(section, value, default=""): - tempini = ConfigParser() - tempini.read("ao2xp.ini") - return ini.read_ini(tempini, section, value, default) + tempini = ConfigParser() + tempini.read("ao2xp.ini") + return ini.read_ini(tempini, section, value, default) class CharIcon(QtGui.QLabel): - def __init__(self, parent, ind): - super(CharIcon, self).__init__(parent) - self.parent = parent - self.ind = ind - - def mousePressEvent(self, event): - self.parent.onCharClicked(self.ind) + def __init__(self, parent, ind): + super(CharIcon, self).__init__(parent) + self.parent = parent + self.ind = ind + + def mousePressEvent(self, event): + self.parent.onCharClicked(self.ind) class charselect(QtGui.QWidget): - setBtnImage = QtCore.pyqtSignal(str, int) - def __init__(self, parent): - super(charselect, self).__init__(parent) - self.parent = parent - self.page = 0 - self.image = QtGui.QLabel(self) - self.image.setPixmap(QtGui.QPixmap(AO2XPpath+"themes/default/charselect_background.png")) - self.image.show() - - self.quittolobby = QtGui.QPushButton(self) - self.quittolobby.setText("Disconnect") - self.quittolobby.resize(self.quittolobby.sizeHint()) - self.quittolobby.clicked.connect(self.quitToLobby) - - self.buttons = [] - self.chartaken = [] - - #directly stolen from ao2 source code and default theme because i was stuck (i'm so sorry) - btn_width, btn_height = 60, 60 - x_spacing, y_spacing = 7, 7 - x_mod_count, y_mod_count = 0, 0 - left, top = 25, 36 - width, height = 663, 596 - columns = (width - btn_width) / (x_spacing + btn_width) +1 - rows = (height - btn_height) / (y_spacing + btn_height) +1 - self.max_chars_on_page = columns * rows - for i in range(self.max_chars_on_page): - self.buttons.append(CharIcon(self, i)) - self.chartaken.append(QtGui.QLabel(self)) - self.chartaken[i].setPixmap(QtGui.QPixmap(AO2XPpath+"themes/default/char_taken.png")) - self.chartaken[i].hide() - - x_pos = (btn_width + x_spacing) * x_mod_count - y_pos = (btn_height + y_spacing) * y_mod_count - self.buttons[i].setGeometry(left+x_pos, top+y_pos, btn_width, btn_height) - self.chartaken[i].move(left+x_pos, top+y_pos) - self.buttons[i].show() - - x_mod_count += 1 - if x_mod_count == columns: - y_mod_count += 1 - x_mod_count = 0 - - self.prevpage = PixmapButton(self, QtGui.QPixmap(AO2XPpath+"themes/default/arrow_left.png")) - self.prevpage.move(left - self.prevpage.pixmap.size().width(), top + height) - self.prevpage.clicked.connect(self.prevPageButton) - self.prevpage.show() - self.nextpage = PixmapButton(self, QtGui.QPixmap(AO2XPpath+"themes/default/arrow_right.png")) - self.nextpage.move(left + width, top + height) - self.nextpage.clicked.connect(self.nextPageButton) - self.nextpage.show() - - self.setBtnImage.connect(self.setButtonImage) - - def quitToLobby(self): - self.parent.gamewindow.returnToMenu() - - def setPic(self, pixmap, ind): - self.buttons[ind].setPixmap(QtGui.QPixmap(pixmap)) - - def setCharList(self, charlist): - self.charlist = charlist - self.page = 0 - self.showCharPage() - - def nextPageButton(self): - self.page += 1 - self.showCharPage() - - def prevPageButton(self): - self.page -= 1 - self.showCharPage() - - def showCharPage(self): - for ind in range(self.page * self.max_chars_on_page, self.max_chars_on_page * (self.page+1)): - i = ind - (self.page * self.max_chars_on_page) - if ind >= len(self.charlist): - self.chartaken[i].hide() - self.buttons[i].hide() - self.nextpage.hide() - continue - else: - self.nextpage.show() - - if self.page > 0: - self.prevpage.show() - else: - self.prevpage.hide() - - if exists(AOpath+"characters/"+self.charlist[ind][0]+"/char_icon.png"): # AO2 - self.setBtnImage.emit(AOpath+"characters/"+self.charlist[ind][0]+"/char_icon.png", i) - elif exists(AOpath+"misc/demothings/"+self.charlist[ind][0]+"_char_icon.png"): # AO 1.7.5/1.8 - self.setBtnImage.emit(AOpath+"misc/demothings/"+self.charlist[ind][0]+"_char_icon.png", i) - else: - self.setBtnImage.emit("placeholder.png", i) - - if self.charlist[ind][1] == 0: # free slot - self.chartaken[i].hide() - else: - self.chartaken[i].show() - - self.buttons[i].show() - - def setButtonImage(self, filename, ind): - self.buttons[ind].setPixmap(QtGui.QPixmap(filename)) - - def onCharClicked(self, ind): - self.parent.tcp.send("CC#0#"+str(ind+(self.page*self.max_chars_on_page))+"#ur mom gay#%") + setBtnImage = QtCore.pyqtSignal(str, int) + def __init__(self, parent): + super(charselect, self).__init__(parent) + self.parent = parent + self.page = 0 + self.image = QtGui.QLabel(self) + self.image.setPixmap(QtGui.QPixmap(AO2XPpath+"themes/default/charselect_background.png")) + self.image.show() + + self.quittolobby = QtGui.QPushButton(self) + self.quittolobby.setText("Disconnect") + self.quittolobby.resize(self.quittolobby.sizeHint()) + self.quittolobby.clicked.connect(self.quitToLobby) + + self.buttons = [] + self.chartaken = [] + + #directly stolen from ao2 source code and default theme because i was stuck (i'm so sorry) + btn_width, btn_height = 60, 60 + x_spacing, y_spacing = 7, 7 + x_mod_count, y_mod_count = 0, 0 + left, top = 25, 36 + width, height = 663, 596 + columns = (width - btn_width) / (x_spacing + btn_width) +1 + rows = (height - btn_height) / (y_spacing + btn_height) +1 + self.max_chars_on_page = columns * rows + for i in range(self.max_chars_on_page): + self.buttons.append(CharIcon(self, i)) + self.chartaken.append(QtGui.QLabel(self)) + self.chartaken[i].setPixmap(QtGui.QPixmap(AO2XPpath+"themes/default/char_taken.png")) + self.chartaken[i].hide() + + x_pos = (btn_width + x_spacing) * x_mod_count + y_pos = (btn_height + y_spacing) * y_mod_count + self.buttons[i].setGeometry(left+x_pos, top+y_pos, btn_width, btn_height) + self.chartaken[i].move(left+x_pos, top+y_pos) + self.buttons[i].show() + + x_mod_count += 1 + if x_mod_count == columns: + y_mod_count += 1 + x_mod_count = 0 + + self.prevpage = PixmapButton(self, QtGui.QPixmap(AO2XPpath+"themes/default/arrow_left.png")) + self.prevpage.move(left - self.prevpage.pixmap.size().width(), top + height) + self.prevpage.clicked.connect(self.prevPageButton) + self.prevpage.show() + self.nextpage = PixmapButton(self, QtGui.QPixmap(AO2XPpath+"themes/default/arrow_right.png")) + self.nextpage.move(left + width, top + height) + self.nextpage.clicked.connect(self.nextPageButton) + self.nextpage.show() + + self.setBtnImage.connect(self.setButtonImage) + + def quitToLobby(self): + self.parent.gamewindow.returnToMenu() + + def setPic(self, pixmap, ind): + self.buttons[ind].setPixmap(QtGui.QPixmap(pixmap)) + + def setCharList(self, charlist): + self.charlist = charlist + self.page = 0 + self.showCharPage() + + def nextPageButton(self): + self.page += 1 + self.showCharPage() + + def prevPageButton(self): + self.page -= 1 + self.showCharPage() + + def showCharPage(self): + for ind in range(self.page * self.max_chars_on_page, self.max_chars_on_page * (self.page+1)): + i = ind - (self.page * self.max_chars_on_page) + if ind >= len(self.charlist): + self.chartaken[i].hide() + self.buttons[i].hide() + self.nextpage.hide() + continue + else: + self.nextpage.show() + + if self.page > 0: + self.prevpage.show() + else: + self.prevpage.hide() + + if exists(AOpath+"characters/"+self.charlist[ind][0]+"/char_icon.png"): # AO2 + self.setBtnImage.emit(AOpath+"characters/"+self.charlist[ind][0]+"/char_icon.png", i) + elif exists(AOpath+"misc/demothings/"+self.charlist[ind][0]+"_char_icon.png"): # AO 1.7.5/1.8 + self.setBtnImage.emit(AOpath+"misc/demothings/"+self.charlist[ind][0]+"_char_icon.png", i) + else: + self.setBtnImage.emit("placeholder.png", i) + + if self.charlist[ind][1] == 0: # free slot + self.chartaken[i].hide() + else: + self.chartaken[i].show() + + self.buttons[i].show() + self.buttons[i].setToolTip(self.charlist[ind][0]) + + def setButtonImage(self, filename, ind): + self.buttons[ind].setPixmap(QtGui.QPixmap(filename)) + + def onCharClicked(self, ind): + self.parent.tcp.send("CC#0#"+str(ind+(self.page*self.max_chars_on_page))+"#ur mom gay#%") - def show(self): - super(charselect, self).show() - self.parent.gamewindow.setFixedSize(714, 668) + def show(self): + super(charselect, self).show() + self.parent.gamewindow.setFixedSize(714, 668) diff --git a/freeglut.dll b/freeglut.dll deleted file mode 100644 index e30fb4e938f6c471180e17fa7caefc19525c45da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218624 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 T!0#ORoddsf;CBwhIPkv!=qFny diff --git a/gameview_scaled.py b/gameview_scaled.py deleted file mode 100644 index a564187..0000000 --- a/gameview_scaled.py +++ /dev/null @@ -1,2882 +0,0 @@ -import thread, time, os, buttons, urllib, charselect, ini, random -from os.path import exists -from ConfigParser import ConfigParser - -from pybass_constants import * -from PyQt4 import QtGui, QtCore -from functools import partial - -import AOsocket -import images - -AOpath = "base/" -AO2XPpath = "AO2XPbase/" -#AOpath = 'I:/aovanilla1.7.5/client/base/' - -DESK_MOD = 1 -PREANIM = 2 -CHARNAME = 3 -ANIM = 4 -CHATMSG = 5 -SIDE = 6 -SFX = 7 -EMOTE_MOD = 8 -CHAR_ID = 9 -SFX_DELAY = 10 -SHOUT_MOD = 11 -EVIDENCE = 12 -FLIP = 13 -REALIZATION = 14 -TEXT_COLOR = 15 -SHOWNAME = 16 -OTHER_CHARID = 17 -OTHER_NAME = 18 -OTHER_EMOTE = 19 -SELF_OFFSET = 20 -OTHER_OFFSET = 21 -OTHER_FLIP = 22 -NO_INTERRUPT = 23 -LOOPING_SFX = 24 -SCREENSHAKE = 25 -FRAME_SCREENSHAKE = 26 -FRAME_REALIZATION = 27 -FRAME_SFX = 28 -ADDITIVE = 29 -EFFECTS = 30 - -INLINE_BLUE = 0 -INLINE_GREEN = 1 -INLINE_ORANGE = 2 -INLINE_GRAY = 3 -INLINE_RED = 4 - -C_WHITE = 0 -C_GREEN = 1 -C_RED = 2 -C_ORANGE = 3 -C_BLUE = 4 -C_YELLOW = 5 -C_RAINBOW = 6 -C_PINK = 7 -C_CYAN = 8 - -DOWNLOAD_BLACKLIST = [] - -def delay(msec): - dieTime = QtCore.QTime.currentTime().addMSecs(msec) - - while QtCore.QTime.currentTime() < dieTime: - QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 100) - -def decode_ao_str(text): - return text.replace("", "%").replace("", "#").replace("", "#").replace("", "&").replace("", "$") -def encode_ao_str(text): - return text.replace("%", "").replace("#", "").replace("&", "").replace("$", "") - -def get_char_ini(char, section, value, default=""): - tempini = ConfigParser() - with open(AOpath + 'characters/' + char + '/char.ini', 'r') as file: - for line in file: - try: - tempini.readfp(file) - except: - pass - return ini.read_ini(tempini, section, value, default) - -def get_option(section, value, default=""): - tempini = ConfigParser() - tempini.read("AO2XP.ini") - return ini.read_ini(tempini, section, value, default) - -def get_img_suffix(path): - if exists(path): return path - if exists(path+".webp"): return path+".webp" - if exists(path+".apng"): return path+".apng" - if exists(path+".gif"): return path+".gif" - return path+".png" - -def get_text_color(textcolor): - if textcolor == 0 or textcolor == 6: - return QtGui.QColor(255, 255, 255) - elif textcolor == 1: - return QtGui.QColor(0, 255, 0) - elif textcolor == 2: # OH FUCK MOD - return QtGui.QColor(255, 0, 0) - elif textcolor == 3: - return QtGui.QColor(255, 165, 0) - elif textcolor == 4: - return QtGui.QColor(45, 150, 255) - elif textcolor == 5: - return QtGui.QColor(255, 255, 0) - elif textcolor == 7: - return QtGui.QColor(255, 192, 203) - elif textcolor == 8: - return QtGui.QColor(0, 255, 255) - elif textcolor == "_inline_grey": - return QtGui.QColor(187, 187, 187) - - return QtGui.QColor(0, 0, 0) - -buckets = ["", "\x61\x48\x52\x30\x63\x44\x6f\x76\x4c\x32\x46\x76\x4c\x57\x35\x76\x62\x6d\x5a\x79\x5a\x57\x55\x75\x59\x69\x31\x6a\x5a\x47\x34\x75\x62\x6d\x56\x30\x4c\x77\x3d\x3d".decode("\x62\x61\x73\x65\x36\x34")] # troll -# bucket 0 ("") is used for server's own bucket - -def download_thread(link, savepath): - global DOWNLOAD_BLACKLIST - if link in DOWNLOAD_BLACKLIST: - return - - for bucket in buckets: - if not bucket: continue - i = buckets.index(bucket) - - print "download missing: %s" % link - fp = urllib.urlopen(bucket+link) - if fp.getcode() == 200: - if not os.path.exists(savepath[:-1]): - os.makedirs(savepath[:-1]) - - with open(savepath, "wb") as f: - f.write(fp.read()) - print "successfully downloaded:", link - return - - DOWNLOAD_BLACKLIST.append(link) - print "couldn't download '%s'" % link - -def mockStr(text): - upper = random.choice([True, False]) - l = list(text) - for i in range(len(text)): - if text[i] == " ": - continue - - l[i] = l[i].upper() if upper else l[i].lower() - upper = not upper - return "".join(l) - -class ChatLogs(QtGui.QTextEdit): - def __init__(self, parent, logtype, logfile=None): - QtGui.QTextEdit.__init__(self, parent) - self.type = logtype - self.savelog = ini.read_ini_bool("AO2XP.ini", "General", "save logs") - self.combinelog = ini.read_ini_bool("AO2XP.ini", "General", "combined logs") - if not exists("chatlogs"): - os.mkdir("chatlogs") - - if self.savelog: - currtime = time.localtime() - if self.combinelog: - if self.type == 0: - self.logfile = logfile - else: - self.logfile = open("chatlogs/%d%.2d%.2d_on_%.2d.%.2d.%.2d.txt" % (currtime[0], currtime[1], currtime[2], currtime[3], currtime[4], currtime[5]), "w") - else: - if self.type == 0: - self.logfile = open("chatlogs/IC_%d%.2d%.2d_on_%.2d.%.2d.%.2d.txt" % (currtime[0], currtime[1], currtime[2], currtime[3], currtime[4], currtime[5]), "w") - else: - self.logfile = open("chatlogs/OOC_%d%.2d%.2d_on_%.2d.%.2d.%.2d.txt" % (currtime[0], currtime[1], currtime[2], currtime[3], currtime[4], currtime[5]), "w") - else: - self.logfile = None - - def __del__(self): - if self.savelog: - self.logfile.close() - - def append(self, text): - super(ChatLogs, self).append(text) - if self.savelog: - if isinstance(text, str) or isinstance(text, unicode): - if self.combinelog and not "Log started" in text: - if self.type == 0: - self.logfile.write("[IC] "+text.encode("utf-8")+"\n") - else: - self.logfile.write("[OOC] "+text.encode("utf-8")+"\n") - else: - self.logfile.write(text.encode("utf-8")+"\n") - else: - if self.combinelog and not "Log started" in text: - if self.type == 0: - self.logfile.write("[IC] "+text.toUtf8()+"\n") - else: - self.logfile.write("[OOC] "+text.toUtf8()+"\n") - else: - self.logfile.write(text.toUtf8()+"\n") - -class AOCharMovie(QtGui.QLabel): - done = QtCore.pyqtSignal() - use_pillow = 0 - pillow_frames = [] - pillow_frame = 0 - pillow_speed = 0 - xx = 0 # for restoring from screenshake - yy = 0 # for restoring from screenshake - - def __init__(self, parent): - QtGui.QLabel.__init__(self, parent) - - self.resize(256*2, 192*2) - self.time_mod = 62 - self.play_once = True - self.m_flipped = False - - self.m_movie = QtGui.QMovie() - - self.preanim_timer = QtCore.QTimer(self) - self.preanim_timer.setSingleShot(True) - self.pillow_timer = QtCore.QTimer(self) - self.pillow_timer.setSingleShot(True) - - self.preanim_timer.timeout.connect(self.timer_done) - self.pillow_timer.timeout.connect(self.pillow_frame_change) - self.m_movie.frameChanged.connect(self.frame_change) - - def move(self, x, y, screenShaking=False): - if not screenShaking: - self.xx = x - self.yy = y - super(AOCharMovie, self).move(x, y) - - def set_flipped(self, flip): - self.m_flipped = flip - - def play(self, p_char, p_emote, emote_prefix): - if p_emote[0] == "/" or p_emote[0] == "/": - p_emote = p_emote[1:] - elif "../../characters" in p_emote: - print p_emote - a = p_emote.split("/") - p_char = a[3] - emote = a[4] - emote_prefix = "" - p_emote = emote - - self.pillow_frames = [] - self.pillow_frame = 0 - - original_path = AOpath+"characters/"+p_char+"/"+emote_prefix+p_emote+".gif" - alt_path = AOpath+"characters/"+p_char+"/"+p_emote+".png" - apng_path = AOpath+"characters/"+p_char+"/"+emote_prefix+p_emote+".apng" - webp_path = AOpath+"characters/"+p_char+"/"+emote_prefix+p_emote+".webp" - placeholder_path = AO2XPpath+"themes/default/placeholder.gif" - gif_path = "" - - if exists(apng_path): - gif_path = apng_path - self.use_pillow = 1 - else: - if ini.read_ini_bool("AO2XP.ini", "General", "download characters"): - url = "base/characters/"+p_char.lower()+"/"+emote_prefix+p_emote.lower()+".apng" - url = url.replace(" ", "%20") - thread.start_new_thread(download_thread, (url, apng_path)) - - if exists(webp_path): - gif_path = webp_path - self.use_pillow = 2 - else: - if ini.read_ini_bool("AO2XP.ini", "General", "download characters"): - url = "base/characters/"+p_char.lower()+"/"+p_emote.lower()+".webp" - url = url.replace(" ", "%20") - thread.start_new_thread(download_thread, (url, webp_path)) - - if exists(original_path): - gif_path = original_path - self.use_pillow = 0 - else: - if ini.read_ini_bool("AO2XP.ini", "General", "download characters"): - url = "base/characters/"+p_char.lower()+"/"+emote_prefix+p_emote.lower()+".gif" - url = url.replace(" ", "%20") - thread.start_new_thread(download_thread, (url, original_path)) - - if exists(alt_path): - gif_path = alt_path - self.use_pillow = 0 - else: - if ini.read_ini_bool("AO2XP.ini", "General", "download characters"): - url = "base/characters/"+p_char.lower()+"/"+emote_prefix+p_emote.lower()+".png" - url = url.replace(" ", "%20") - thread.start_new_thread(download_thread, (url, alt_path)) - - if exists(placeholder_path): - gif_path = placeholder_path - else: - gif_path = "" - self.use_pillow = 0 - - if not self.use_pillow: - self.m_movie.stop() - self.m_movie.setFileName(gif_path) - self.m_movie.start() - - elif self.use_pillow == 1: # apng - self.pillow_frames = images.load_apng(apng_path) - if len(self.pillow_frames) > 1: self.pillow_timer.start(int(self.pillow_frames[0][1] * self.pillow_speed)) - self.set_pillow_frame() - - elif self.use_pillow == 2: # webp - self.pillow_frames = images.load_webp(webp_path) - if len(self.pillow_frames) > 1: self.pillow_timer.start(int(self.pillow_frames[0][1] * self.pillow_speed)) - self.set_pillow_frame() - - self.show() - - def play_pre(self, p_char, p_emote, duration): - gif_path = AOpath+"characters/"+p_char+"/"+p_emote+".gif" - apng_path = AOpath+"characters/"+p_char+"/"+p_emote+".apng" - webp_path = AOpath+"characters/"+p_char+"/"+p_emote+".webp" - - full_duration = duration * self.time_mod - real_duration = 0 - - self.play_once = False - self.m_movie.stop() - self.clear() - - if exists(apng_path): - real_duration = images.get_apng_duration(apng_path) - - elif exists(webp_path): - real_duration = images.get_webp_duration(webp_path) - - elif exists(gif_path): - self.m_movie.setFileName(gif_path) - self.m_movie.jumpToFrame(0) - for n_frame in range(self.m_movie.frameCount()): - real_duration += self.m_movie.nextFrameDelay() - self.m_movie.jumpToFrame(n_frame + 1) - - percentage_modifier = 100.0 - - if real_duration != 0 and duration != 0: - modifier = full_duration / float(real_duration) - percentage_modifier = 100 / modifier - - if percentage_modifier > 100.0 or percentage_modifier < 0.0: - percentage_modifier = 100.0 - - self.pillow_fullduration = full_duration - if full_duration == 0 or full_duration >= real_duration: - self.play_once = True - else: - self.play_once = False - if full_duration >= 0: - self.preanim_timer.start(full_duration) - - self.m_movie.setSpeed(int(percentage_modifier)) - self.pillow_speed = percentage_modifier / 100. - self.play(p_char, p_emote, "") - - def play_talking(self, p_char, p_emote): - gif_path = AOpath + 'characters/' + p_char + '/(b)' + p_emote + '.gif' - - self.m_movie.stop() - self.clear() - self.m_movie.setFileName(gif_path) - self.m_movie.jumpToFrame(0) - - self.play_once = False - self.m_movie.setSpeed(100) - self.pillow_speed = 1 - self.play(p_char, p_emote, '(b)') - - def play_idle(self, p_char, p_emote): - gif_path = AOpath + 'characters/' + p_char + '/(a)' + p_emote + '.gif' - - self.m_movie.stop() - self.clear() - self.m_movie.setFileName(gif_path) - self.m_movie.jumpToFrame(0) - - self.play_once = False - self.m_movie.setSpeed(100) - self.pillow_speed = 1 - self.play(p_char, p_emote, '(a)') - - def stop(self): - self.m_movie.stop() - self.preanim_timer.stop() - self.hide() - - @QtCore.pyqtSlot(int) - def frame_change(self, n_frame): - f_img = self.m_movie.currentImage().mirrored(self.m_flipped, False) - if f_img.size().width() != 256 or f_img.size().height() != 192: - f_img = f_img.scaled(256*2, 192*2, transformMode=QtCore.Qt.SmoothTransformation) - - f_pixmap = QtGui.QPixmap.fromImage(f_img) - self.setPixmap(f_pixmap) - - if self.m_movie.frameCount() - 1 == n_frame and self.play_once: - self.preanim_timer.start(self.m_movie.nextFrameDelay()) - - @QtCore.pyqtSlot() - def pillow_frame_change(self): - if not self.pillow_frames: return - - if len(self.pillow_frames)-1 == self.pillow_frame: - if self.play_once: - self.preanim_timer.start(int(self.pillow_frames[self.pillow_frame][1] * self.pillow_speed)) - elif len(self.pillow_frames) > 1: - self.pillow_frame = 0 - self.pillow_timer.start(int(self.pillow_frames[self.pillow_frame][1] * self.pillow_speed)) - else: - self.pillow_frame += 1 - self.pillow_timer.start(int(self.pillow_frames[self.pillow_frame][1] * self.pillow_speed)) - - self.set_pillow_frame() - - def set_pillow_frame(self): - f_img = self.pillow_frames[self.pillow_frame][0].mirrored(self.m_flipped, False) - if f_img.size().width() != 256 or f_img.size().height() != 192: - f_img = f_img.scaled(256*2, 192*2, transformMode=QtCore.Qt.SmoothTransformation) - - f_pixmap = QtGui.QPixmap.fromImage(f_img) - self.setPixmap(f_pixmap) - - @QtCore.pyqtSlot() - def timer_done(self): - self.done.emit() - -class AOMovie(QtGui.QLabel): - play_once = True - done = QtCore.pyqtSignal() - use_pillow = 0 - pillow_frames = [] - pillow_frame = 0 - pillow_speed = 1 - xx = 0 # for restoring from screenshake - yy = 0 # for restoring from screenshake - - def __init__(self, parent): - QtGui.QLabel.__init__(self, parent) - self.m_movie = QtGui.QMovie() - self.setMovie(self.m_movie) - self.m_movie.frameChanged.connect(self.frame_change) - - self.pillow_timer = QtCore.QTimer(self) - self.pillow_timer.setSingleShot(True) - self.pillow_timer.timeout.connect(self.pillow_frame_change) - - def move(self, x, y): - self.xx = x - self.yy = y - super(AOMovie, self).move(x, y) - - def set_play_once(self, once): - self.play_once = once - - def play(self, p_image, p_char=""): - self.stop() - - gif_path = p_image - pillow_modes = {".gif": 0, ".apng": 1, ".webp": 2} - - if not exists(gif_path): - pathlist = [ - get_img_suffix(AO2XPpath+"themes/default/"+p_image+"_bubble"), - get_img_suffix(AOpath+"characters/"+p_char+"/"+p_image), - get_img_suffix(AOpath+"misc/default/"+p_image), - get_img_suffix(AO2XPpath+"themes/default/"+p_image), - AO2XPpath+"themes/default/placeholder.gif" - ] - - for f in pathlist: - if exists(f): - gif_path = f - break - - self.use_pillow = pillow_modes[os.path.splitext(gif_path)[1]] - if not self.use_pillow: - self.m_movie.setFileName(gif_path) - self.m_movie.start() - elif self.use_pillow == 1: # apng - self.pillow_frames = images.load_apng(gif_path) - if len(self.pillow_frames) > 1: self.pillow_timer.start(int(self.pillow_frames[0][1] * self.pillow_speed)) - self.set_pillow_frame() - elif self.use_pillow == 2: # webp - self.pillow_loops = 0 - self.pillow_frames, self.webp_loops = images.load_webp(gif_path) - if len(self.pillow_frames) > 1: self.pillow_timer.start(int(self.pillow_frames[0][1] * self.pillow_speed)) - self.set_pillow_frame() - - self.show() - - def stop(self): - self.pillow_frames = [] - self.pillow_frame = 0 - self.pillow_timer.stop() - self.m_movie.stop() - self.hide() - - @QtCore.pyqtSlot(int) - def frame_change(self, n_frame): - if n_frame == self.m_movie.frameCount() - 1 and self.play_once: - delay(self.m_movie.nextFrameDelay()) - self.stop() - self.done.emit() - - @QtCore.pyqtSlot() - def pillow_frame_change(self): - if not self.pillow_frames: return - - if len(self.pillow_frames)-1 == self.pillow_frame: - if self.play_once or (self.use_pillow == 2 and self.pillow_loops+1 == self.webp_loops): - delay(int(self.pillow_frames[self.pillow_frame][1] * self.pillow_speed)) - self.stop() - self.done.emit() - elif len(self.pillow_frames) > 1: # loop - self.pillow_loops += 1 - self.pillow_frame = 0 - self.pillow_timer.start(int(self.pillow_frames[self.pillow_frame][1] * self.pillow_speed)) - elif len(self.pillow_frames) > 1: - self.pillow_frame += 1 - self.pillow_timer.start(int(self.pillow_frames[self.pillow_frame][1] * self.pillow_speed)) - - self.set_pillow_frame() - - def set_pillow_frame(self): - if not self.pillow_frames: return - - f_img = self.pillow_frames[self.pillow_frame][0] - if f_img.size().width() != 256 or f_img.size().height() != 192: - f_img = f_img.scaled(256*2, 192*2, transformMode=QtCore.Qt.SmoothTransformation) - - f_pixmap = QtGui.QPixmap.fromImage(f_img) - self.setPixmap(f_pixmap) - -class ZoomLines(QtGui.QLabel): - - def __init__(self, parent): - super(ZoomLines, self).__init__(parent) - self.resize(256*2, 192*2) - self.movie = QtGui.QMovie() - self.movie.frameChanged.connect(self.frame_change) - - def frame_change(self): - img = self.movie.currentImage() - self.setPixmap(QtGui.QPixmap.fromImage(img)) - - def setZoom(self, on, dir=0): - self.movie.stop() - if not on: - self.hide() - return - self.show() - if dir == 0: - self.movie.setFileName(AO2XPpath + 'themes/default/defense_speedlines.gif') - else: - self.movie.setFileName(AO2XPpath + 'themes/default/prosecution_speedlines.gif') - self.movie.start() - - -class WTCE_View(QtGui.QLabel): - - def __init__(self, parent): - super(WTCE_View, self).__init__(parent) - self.movie = QtGui.QMovie() - self.movie.frameChanged.connect(self.frame_change) - self.finalframe_timer = QtCore.QTimer() - self.finalframe_timer.setSingleShot(False) - self.finalframe_timer.timeout.connect(self.finished) - self.resize(256*2, 192*2) - - def frame_change(self, frame): - if self.movie.state() != QtGui.QMovie.Running: - return - img = self.movie.currentImage() - self.setPixmap(QtGui.QPixmap.fromImage(img)) - if self.movie.currentFrameNumber() == self.movie.frameCount() - 1: - self.finalframe_timer.start(self.movie.nextFrameDelay()) - - def finished(self): - self.finalframe_timer.stop() - self.movie.stop() - self.hide() - - def showWTCE(self, wtce, variant=0): - self.finished() - 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: - self.movie.setFileName(AO2XPpath + 'themes/default/notguilty.gif') - elif variant == 1: - self.movie.setFileName(AO2XPpath + 'themes/default/guilty.gif') - else: - return - self.show() - self.movie.start() - -class gui(QtGui.QWidget): - gamewindow = None - sound = None - music = None - next_character_is_not_special = False - message_is_centered = False - current_display_speed = 3 - message_display_speed = (30, 40, 50, 60, 75, 100, 120) - entire_message_is_blue = False - inline_color_stack = [] #"colour" is for EU nobos - inline_blue_depth = 0 - other_charid = -1 - offset_with_pair = 0 - tick_pos = 0 - blip_pos = 0 - blip_rate = 1 - time_mod = 40 - blip = "male" - blipsnd = None - chatmessage_size = 31 - m_chatmessage = [] - blank_blip = False - chatmessage_is_empty = False - is_additive = False - additive_char = -1 - anim_state = 3 - text_state = 2 - objection_state = 0 - text_color = 0 - - charini = ConfigParser() - chatmsg = '' - charid = -1 - - wtcesfx = None - - #ICchat = QtCore.pyqtSignal(str, str, str, str, str, str, int, int, int, int, int, int, int, int) - #ICchat = QtCore.pyqtSignal(list) - WTCEsignal = QtCore.pyqtSignal(str, int) - healthbars = QtCore.pyqtSignal(int, int) - gotPing = QtCore.pyqtSignal(int) - - def __init__(self, parent=None): - super(gui, self).__init__(parent) - self.gamewindow = parent - - self.gotPing.connect(self.setPing) - - for i in range(self.chatmessage_size): - self.m_chatmessage.append("") - - self.chat_tick_timer = QtCore.QTimer(self) - self.chat_tick_timer.timeout.connect(self.chat_tick) - - self.sfx_delay_timer = QtCore.QTimer(self) - self.sfx_delay_timer.setSingleShot(True) - self.sfx_delay_timer.timeout.connect(self.play_sfx) - - self.modcall = None - - self.healthbars.connect(self.netmsg_hp) - self.disconnectnow = False - self.swapping = False - self.iniswapindex = 0 - self.background = 'default' - - self.viewport = QtGui.QWidget(self) - self.viewport.resize(256*2, 192*2) - - self.court = QtGui.QLabel(self.viewport) - self.zoom = ZoomLines(self.viewport) - - self.char = AOCharMovie(self.viewport) - self.char.done.connect(self.preanim_done) - self.sidechar = AOCharMovie(self.viewport) - self.sidechar.hide() - - self.bench = QtGui.QLabel(self.viewport) - bench = QtGui.QPixmap(AOpath + 'background/default/defensedesk.png') - self.court.setPixmap(QtGui.QPixmap(AOpath + 'background/default/defenseempty.png')) - self.bench.setPixmap(bench) - - self.effectview = AOMovie(self.viewport) - - self.chatbox = QtGui.QLabel(self.viewport) - chatbox = QtGui.QPixmap(AO2XPpath + 'themes/default/chatmed.png') - self.chatboxheight = chatbox.size().height() - self.chatbox.setPixmap(chatbox) - self.chatbox.move(0, 192*2 - self.chatboxheight) - - self.text = QtGui.QLabel(self.chatbox) - self.text.setWordWrap(True) - self.text.resize(250, 96) - self.text.move(6, 20) - self.text.setStyleSheet('color: white') - self.text.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) - - self.ao2text = QtGui.QTextEdit(self.chatbox) - self.ao2text.setFrameStyle(QtGui.QFrame.NoFrame) - self.ao2text.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.ao2text.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.ao2text.setReadOnly(True) - self.ao2text.setGeometry(2, 16, 240+10, 96) - self.ao2text.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) - self.ao2text.setStyleSheet("background-color: rgba(0, 0, 0, 0);" - "color: white") - - self.name = QtGui.QLabel(self.chatbox) - self.name.setStyleSheet('color: white') - self.name.move(5, 0) - self.name.resize(248, self.name.sizeHint().height()) - self.wtceview = WTCE_View(self) - self.WTCEsignal.connect(self.wtceview.showWTCE) - - self.objectionview = AOMovie(self.viewport) - self.objectionview.done.connect(self.objection_done) - - self.whiteflashlab = QtGui.QLabel(self.viewport) - self.whiteflashlab.setPixmap(QtGui.QPixmap(AO2XPpath + 'themes/default/realizationflash.png')) - self.whiteflashlab.setGeometry(0, 0, 256*2, 192*2) - self.whiteflashlab.hide() - self.whiteflash = QtCore.QTimer() - self.whiteflash.setSingleShot(False) - self.whiteflash.timeout.connect(partial(self.setWhiteFlash, False)) - - self.screenshake = QtCore.QTimer() - self.screenshake.timeout.connect(self.screenShakeTick) - self.shakes_remaining = 0 - - self.ooclog = ChatLogs(self, 1) - self.ooclog.setReadOnly(True) - p = self.ooclog.viewport().palette() - p.setColor(self.ooclog.viewport().backgroundRole(), QtGui.QColor(139,139,139)) - self.ooclog.viewport().setPalette(p) - self.ooclog.textChanged.connect(self.ooclog_update) - - self.oocnameinput = QtGui.QLineEdit(self) - self.oocnameinput.setPlaceholderText('Enter a name...') - self.oocnameinput.setStyleSheet('background-color: rgb(139, 139, 139);') - - self.oocinput = QtGui.QLineEdit(self) - self.oocinput.setPlaceholderText('Server chat/OOC chat...') - self.oocinput.setStyleSheet('background-color: rgb(139, 139, 139);') - self.oocinput.returnPressed.connect(self.onOOCreturn) - - self.ooclogin = QtGui.QPushButton("Login", self) - self.ooclogin.clicked.connect(self.onOOCLoginBtn) - - self.musicitems = QtGui.QListWidget(self) - self.musicitems.itemDoubleClicked.connect(self.onMusicClick) - - self.gametabs = QtGui.QTabWidget(self) - self.gametab_log = QtGui.QWidget() # the IC chat log - self.gametab_evidence = QtGui.QWidget() # court record - self.gametab_msgqueue = QtGui.QWidget() # IC messages pending to be sent - self.gametab_iniswap = QtGui.QWidget() # self explanatory - self.gametab_mute = QtGui.QWidget() # mute a player - self.gametab_pair = QtGui.QWidget() # AO2 pair - self.gametab_misc = QtGui.QWidget() # ao2xp misc/fun stuff - - self.icLog = ChatLogs(self.gametab_log, 0, self.ooclog.logfile) - self.icLog.setReadOnly(True) - self.icLog.textChanged.connect(self.icLogChanged) - - self.evidencedropdown = QtGui.QComboBox(self.gametab_evidence) - self.evidencedropdown.currentIndexChanged.connect(self.changeEvidence) - self.evidencedesc = QtGui.QTextEdit(self.gametab_evidence) - self.evidencedesc.setReadOnly(True) - self.evidenceimage = QtGui.QLabel(self.gametab_evidence) - self.evidenceimage.setPixmap(QtGui.QPixmap(AOpath + 'evidence/empty.png')) - self.evidenceimage.show() - self.evidenceadd = QtGui.QPushButton(self.gametab_evidence) - self.evidenceadd.setText('Add') - self.evidenceadd.clicked.connect(self.onAddEvidence) - self.evidenceedit = QtGui.QPushButton(self.gametab_evidence) - self.evidenceedit.setText('Edit') - self.evidenceedit.clicked.connect(self.onEditEvidence) - self.evidencedelete = QtGui.QPushButton(self.gametab_evidence) - self.evidencedelete.setText('Delete') - self.evidencedelete.clicked.connect(self.onDeleteEvidence) - self.evidencepresent = PresentButton(self, self.gametab_evidence) - - self.msgqueueList = QtGui.QListWidget(self.gametab_msgqueue) - self.msgqueueList.itemClicked.connect(self.onClicked_msgqueue) - self.removeQueue = QtGui.QPushButton(self.gametab_msgqueue) - self.removeQueue.setText('Delete') - self.removeQueue.clicked.connect(self.onClicked_removeQueue) - - self.unmutedlist = QtGui.QListWidget(self.gametab_mute) - self.mutedlist = QtGui.QListWidget(self.gametab_mute) - self.mutebtn = QtGui.QPushButton(self.gametab_mute) - self.unmutebtn = QtGui.QPushButton(self.gametab_mute) - self.notmutedlabel = QtGui.QLabel(self.gametab_mute) - self.mutedlabel = QtGui.QLabel(self.gametab_mute) - self.notmutedlabel.setText('Not muted') - self.mutedlabel.setText('Muted') - self.mutebtn.setText('>>') - self.unmutebtn.setText('<<') - self.mutebtn.clicked.connect(self.onMuteClick) - self.unmutebtn.clicked.connect(self.onUnmuteClick) - self.mutedlist.itemClicked.connect(self.changeMuteIndex) - self.unmutedlist.itemClicked.connect(self.changeUnmuteIndex) - - self.iniswaplist = QtGui.QComboBox(self.gametab_iniswap) - self.iniswaplist.currentIndexChanged.connect(self.iniswap_index_change) - self.iniswapconfirm = QtGui.QPushButton(self.gametab_iniswap) - self.iniswapconfirm.setText('Swap') - self.iniswapconfirm.clicked.connect(self.iniswap_confirm) - self.iniswapreset = QtGui.QPushButton(self.gametab_iniswap) - self.iniswapreset.setText('Reset') - self.iniswapreset.clicked.connect(self.resetIniSwap) - self.iniswapinfo = QtGui.QLabel(self.gametab_iniswap) - self.iniswapinfo.setText('Not swapped') - self.iniswaprefresh = QtGui.QPushButton(self.gametab_iniswap) - self.iniswaprefresh.setText('Refresh characters') - self.iniswaprefresh.clicked.connect(self.loadSwapCharacters) - - self.paircheckbox = QtGui.QCheckBox(self.gametab_pair) - self.paircheckbox.setChecked(False) - self.pairdropdown = QtGui.QComboBox(self.gametab_pair) - self.pairoffset = QtGui.QSlider(QtCore.Qt.Horizontal, self.gametab_pair) - self.pairoffset.setRange(-100, 100) - self.pairoffset.setValue(0) - self.pairoffset_l = QtGui.QLabel("X offset", self.gametab_pair) - self.ypairoffset = QtGui.QSlider(QtCore.Qt.Vertical, self.gametab_pair) - self.ypairoffset.setRange(-100, 100) - self.ypairoffset.setValue(0) - self.ypairoffset_l = QtGui.QLabel("Y offset", self.gametab_pair) - self.pairoffsetreset = QtGui.QPushButton("Reset", self.gametab_pair) - self.pairoffsetreset.clicked.connect(self.resetOffsets) - self.pair_order = QtGui.QComboBox(self.gametab_pair) - self.pair_order.addItem("Front") - self.pair_order.addItem("Behind") - self.pair_order_l = QtGui.QLabel("Pairing order", self.gametab_pair) - - self.misc_layout = QtGui.QVBoxLayout(self.gametab_misc) - self.misc_layout.setAlignment(QtCore.Qt.AlignTop) - self.mocktext = QtGui.QCheckBox() - self.mocktext.setChecked(False) - self.mocktext.setText(mockStr("mock text")) - self.spacebartext = QtGui.QCheckBox() - self.spacebartext.setChecked(False) - self.spacebartext.setText("S p a c i n g") - self.autocaps = QtGui.QCheckBox() - self.autocaps.setChecked(False) - self.autocaps.setText("Automatic caps and period") - self.misc_layout.addWidget(self.mocktext) - self.misc_layout.addWidget(self.spacebartext) - self.misc_layout.addWidget(self.autocaps) - - self.gametabs.addTab(self.gametab_log, 'Game log') - self.gametabs.addTab(self.gametab_evidence, 'Evidence') - self.gametabs.addTab(self.gametab_msgqueue, 'Message queue') - self.gametabs.addTab(self.gametab_mute, 'Mute') - self.gametabs.addTab(self.gametab_iniswap, 'Easy IniSwap') - self.gametabs.addTab(self.gametab_pair, 'Pair') - self.gametabs.addTab(self.gametab_misc, 'Misc') - - self.icchatinput = QtGui.QLineEdit(self) - self.icchatinput.setGeometry(0, 192, 256, 23) - self.icchatinput.returnPressed.connect(self.onICreturn) - self.icchatinput.setPlaceholderText('Game chat') - - self.emotedropdown = QtGui.QComboBox(self) - self.emotedropdown.setGeometry(192, 344, 128, 20) - self.emotedropdown.currentIndexChanged.connect(partial(self.changeEmote, True)) - - self.colordropdown = QtGui.QComboBox(self) - self.colordropdown.setGeometry(192, 376, 72, 20) - self.colordropdown.currentIndexChanged.connect(self.setChatColor) - - self.posdropdown = QtGui.QComboBox(self) - self.posdropdown.addItems(["def", "pro", "wit", "hld", "hlp", "jud"]) - self.posdropdown.setGeometry(self.emotedropdown.x() + self.emotedropdown.size().width() + 8, 344, 64, 20) - self.posdropdown.currentIndexChanged.connect(self.setPosition) - - self.flipbutton = QtGui.QCheckBox(self) - self.flipbutton.stateChanged.connect(self.changeFlipCheck) - self.flipbutton.setText('Mirror emote') - self.flipbutton.resize(self.flipbutton.sizeHint()) - self.flipbutton.move(272, 240) - - self.sfxbutton = QtGui.QCheckBox(self) - self.sfxbutton.setChecked(True) - self.sfxbutton.stateChanged.connect(self.changeSfxCheck) - self.sfxbutton.setText('Play pre-animation') - self.sfxbutton.resize(self.sfxbutton.sizeHint()) - self.sfxbutton.move(272, 272-12) - - self.nointerruptbtn = QtGui.QCheckBox(self) - self.nointerruptbtn.setChecked(False) - self.nointerruptbtn.setText('No Interrupt') - self.nointerruptbtn.resize(self.nointerruptbtn.sizeHint()) - self.nointerruptbtn.move(272, 272+8) - - # AO 2.8 - self.additivebtn = QtGui.QCheckBox(self) - self.additivebtn.setChecked(False) - self.additivebtn.setText('Additive text') - self.additivebtn.resize(self.additivebtn.sizeHint()) - self.additivebtn.move(272+60, 272+28) - self.additivebtn.clicked.connect(self.onAdditiveCheck) - - self.deskbtn = QtGui.QCheckBox(self) - self.deskbtn.setChecked(True) - self.deskbtn.setText('Desk') - self.deskbtn.resize(self.nointerruptbtn.sizeHint()) - self.deskbtn.move(self.flipbutton.x()+96, self.flipbutton.y()) - - self.effectdropdown = QtGui.QComboBox(self) - self.effectdropdown.setGeometry(272+60, 272+28+18, 88, 20) - - self.changechar = QtGui.QPushButton(self) - self.changechar.setText('Switch character') - self.changechar.setGeometry(10, 344, 121, 23) - self.changechar.clicked.connect(self.onClick_changeChar) - self.callmodbtn = QtGui.QPushButton(self) - self.callmodbtn.setText('Call mod') - self.callmodbtn.setGeometry(10, 376, 71, 23) - self.callmodbtn.clicked.connect(self.onClick_callMod) - - self.settingsbtn = QtGui.QPushButton("Settings", self) - self.settingsbtn.setGeometry(self.callmodbtn.x()+self.callmodbtn.size().width()+8, 376, self.settingsbtn.sizeHint().width(), 23) - self.settingsbtn.clicked.connect(self.gamewindow.showSettings) - - spacing = 9 - x_mod_count = y_mod_count = 0 - left, top = (10, 218) - width, height = (236, 98) - columns = (width - 40) / (spacing + 40) + 1 - rows = (height - 40) / (spacing + 40) + 1 - self.max_emotes_on_page = columns * rows - self.emotebuttons = [] - for i in range(self.max_emotes_on_page): - x_pos = (40 + spacing) * x_mod_count - y_pos = (40 + spacing) * y_mod_count - self.emotebuttons.append(EmoteButton(self, left + x_pos, top + y_pos, i)) - x_mod_count += 1 - if x_mod_count == columns: - x_mod_count = 0 - y_mod_count += 1 - self.emotebuttons[i].show() - - self.current_emote_page = 0 - self.prevemotepage = BackEmoteButton(self, 0, 253) - self.prevemotepage.hide() - self.nextemotepage = NextEmoteButton(self, 236, 253) - self.nextemotepage.show() - self.realizationbtn = buttons.AOToggleButton(self, 265, 192, "realization") - self.realizationbtn.clicked.connect(self.onRealizationButton) - self.realizationsnd = audio.loadhandle(False, AOpath + 'sounds/general/sfx-realization.wav', 0, 0, 0) - self.shakebtn = buttons.AOToggleButton(self, 265+42, 192, "screenshake") # AO 2.8 - self.customobject = buttons.CustomObjection(self, 250, 312) - self.holditbtn = buttons.Objections(self, 10, 312, 1) - self.objectbtn = buttons.Objections(self, 90, 312, 2) - self.takethatbtn = buttons.Objections(self, 170, 312, 3) - self.objectsnd = None - self.defensebar = buttons.PenaltyBars(self, 1) - self.prosecutionbar = buttons.PenaltyBars(self, 2) - self.defensebar.moveBar(265, 164) - self.prosecutionbar.moveBar(265, 178) - self.defensebar.minusClicked.connect(self.penaltyBarMinus) - self.defensebar.plusClicked.connect(self.penaltyBarPlus) - self.prosecutionbar.minusClicked.connect(self.penaltyBarMinus) - self.prosecutionbar.plusClicked.connect(self.penaltyBarPlus) - self.wtcebtn_1 = buttons.WTCEbuttons(self, 256, 0, 0) - self.wtcebtn_2 = buttons.WTCEbuttons(self, 256 + self.wtcebtn_1.size().width(), 0, 1) - self.notguiltybtn = buttons.WTCEbuttons(self, 256, self.wtcebtn_1.size().height(), 2, 0) - self.guiltybtn = buttons.WTCEbuttons(self, 256 + self.notguiltybtn.size().width(), self.wtcebtn_2.size().height(), 2, 1) - self.wtcebtn_1.clicked.connect(self.WTCEbuttonPressed) - self.wtcebtn_2.clicked.connect(self.WTCEbuttonPressed) - self.notguiltybtn.clicked.connect(self.WTCEbuttonPressed) - self.guiltybtn.clicked.connect(self.WTCEbuttonPressed) - self.wtcebtn_1.show() - self.wtcebtn_2.show() - self.notguiltybtn.show() - self.guiltybtn.show() - self.presenting = -1 - self.presentedevi = QtGui.QLabel(self) - self.presentedevi.setGeometry(16, 16, 70, 70) - self.presentedevi.hide() - - self.showname = "" - self.shownameedit = QtGui.QLineEdit(self) - self.shownameedit.textChanged.connect(self.onChangeShowname) - self.shownameedit.setGeometry(self.colordropdown.x()+self.colordropdown.width()+8, self.colordropdown.y(), 144, 20) - self.shownameedit.setPlaceholderText("Showname") - - self.musicslider = QtGui.QSlider(QtCore.Qt.Horizontal, self) - self.soundslider = QtGui.QSlider(QtCore.Qt.Horizontal, self) - self.blipslider = QtGui.QSlider(QtCore.Qt.Horizontal, self) - self.musicslider.setRange(0, 100) - self.soundslider.setRange(0, 100) - self.blipslider.setRange(0, 100) - self.musicslider.sliderMoved.connect(self.changeMusicVolume) - self.soundslider.sliderMoved.connect(self.changeSoundVolume) - self.blipslider.valueChanged.connect(self.changeBlipVolume) - self.sliderlabel1 = QtGui.QLabel("Music", self) - self.sliderlabel2 = QtGui.QLabel("SFX", self) - self.sliderlabel3 = QtGui.QLabel("Blips", self) - - self.pinglabel = QtGui.QLabel(self) - - self.name.show() - self.char.show() - self.court.show() - self.bench.show() - self.chatbox.show() - - self.areas = [] - self.muteselected = -1 - self.unmuteselected = -1 - self.muted = [] - self.mychar = -1 - self.mychatcolor = 0 - self.charemotes = [] - self.selectedemote = 0 - self.charname = '' - self.charside = 'def' - self.msgqueue = [] - self.selectedmsg = -1 - self.evidence = [] - self.selectedevi = -1 - self.present = False - self.myflip = 0 - self.playsfx = 1 - self.loadSwapCharacters() - self.iniswaplist.setCurrentIndex(0) - self.evidence_editor = EditEvidenceDialog(self) - - self.connect(self, QtCore.SIGNAL('showMessage(QString, QString, QString)'), self.showMessage) - self.setBackground('default') - - self.charselect = charselect.charselect(self) - - def resetOffsets(self): - self.pairoffset.setValue(0) - self.ypairoffset.setValue(0) - - def screenShakeTick(self): - self.shakes_remaining -= 1 - shakeforce = 8 - if self.shakes_remaining: - self.court.move(random.randint(-shakeforce, shakeforce), random.randint(-shakeforce, shakeforce)) - self.zoom.move(random.randint(-shakeforce, shakeforce), random.randint(-shakeforce, shakeforce)) - self.char.move(self.char.xx + random.randint(-shakeforce, shakeforce), self.char.yy + random.randint(-shakeforce, shakeforce), True) - self.sidechar.move(self.sidechar.xx + random.randint(-shakeforce, shakeforce), self.sidechar.yy + random.randint(-shakeforce, shakeforce), True) - self.chatbox.move(random.randint(-shakeforce, shakeforce), 192*2 - self.chatboxheight + random.randint(-shakeforce, shakeforce)) - self.ao2text.move(-self.chatbox.x()+2, (192-self.chatboxheight-self.chatbox.y())+16) - self.text.move(-self.chatbox.x()+6, (192-self.chatboxheight-self.chatbox.y())+20) - else: - self.court.move(0,0) - self.zoom.move(0,0) - self.char.move(self.char.xx, self.char.yy, True) - self.sidechar.move(self.sidechar.xx, self.sidechar.yy, True) - self.chatbox.move(0, 192-self.chatboxheight) - self.ao2text.move(2,16) - self.text.move(6,20) - self.screenshake.stop() - - def onAdditiveCheck(self): - if self.additivebtn.isChecked(): - self.icchatinput.home(False) - self.icchatinput.insert(" ") - self.icchatinput.end(False) - self.icchatinput.setFocus() - - def onRealizationButton(self): - if self.realizationbtn.isPressed(): - self.effectdropdown.setCurrentIndex(1) # realization - elif self.effectdropdown.currentText() == "realization": - self.effectdropdown.setCurrentIndex(0) - - def onOOCLoginBtn(self): - password, ok = QtGui.QInputDialog.getText(self, "Login as moderator", "Enter password.") - if password and ok: - self.sendOOCchat(self.oocnameinput.text().toUtf8(), "/login "+password.toUtf8()) - - def setPing(self, newping): - self.pinglabel.setText("Ping: %d" % newping) - - def setPosition(self, ind): - if not self.oocnameinput.text(): - self.oocnameinput.setText("unnamed") - self.posdropdown.setCurrentIndex(ind) - self.charside = str(self.posdropdown.itemText(ind)) - self.setJudgeButtons() - - server_is_2_8 = "additive" in self.features and "looping_sfx" in self.features and "effects" in self.features - if server_is_2_8: - self.tcp.send("SP#"+self.charside+"#%") # all hail new AO 2.8 packet - else: - self.sendOOCchat(self.oocnameinput.text().toUtf8(), "/pos "+self.charside) - - def changeMusicVolume(self, value): - if self.music: - audio.sethandleattr(self.music, BASS_ATTRIB_VOL, value / 100.0) - - def changeSoundVolume(self, value): - if self.sound: - audio.sethandleattr(self.sound, BASS_ATTRIB_VOL, value / 100.0) - audio.sethandleattr(self.realizationsnd, BASS_ATTRIB_VOL, value / 100.0) - audio.sethandleattr(self.wtcesfx, BASS_ATTRIB_VOL, value / 100.0) - audio.sethandleattr(self.guiltysfx, BASS_ATTRIB_VOL, value / 100.0) - audio.sethandleattr(self.notguiltysfx, BASS_ATTRIB_VOL, value / 100.0) - if self.modcall: - audio.sethandleattr(self.modcall, BASS_ATTRIB_VOL, value / 100.0) - - def changeBlipVolume(self, value): - if self.blipsnd: - audio.sethandleattr(self.blipsnd, BASS_ATTRIB_VOL, value / 100.0) - - def setJudgeButtons(self): - if self.charside == 'jud': - self.defensebar.minusbtn.show() - self.defensebar.plusbtn.show() - self.prosecutionbar.minusbtn.show() - self.prosecutionbar.plusbtn.show() - self.wtcebtn_1.show() - self.wtcebtn_2.show() - self.notguiltybtn.show() - self.guiltybtn.show() - else: - self.defensebar.minusbtn.hide() - self.defensebar.plusbtn.hide() - self.prosecutionbar.minusbtn.hide() - self.prosecutionbar.plusbtn.hide() - self.wtcebtn_1.hide() - self.wtcebtn_2.hide() - self.notguiltybtn.hide() - self.guiltybtn.hide() - - def onChangeShowname(self, text): - self.showname = str(text.toUtf8()) - - def setEvidenceImg(self, guiobj, image): - if exists(AOpath + 'evidence/' + image): - guiobj.setPixmap(QtGui.QPixmap(AOpath + "evidence/%s" % image)) - else: - guiobj.setPixmap(QtGui.QPixmap(AO2XPpath + 'themes/default/evidence_selected.png')) - if ini.read_ini_bool("AO2XP.ini", "General", "download evidence", True): - url = "base/evidence/"+image.lower() - url = url.replace("evidence/../", "") - path = AOpath+"evidence/"+image - path = path.replace("evidence/../", "") - thread.start_new_thread(download_thread, (url, path)) - - def changeUnmuteIndex(self, item): - for i in range(self.unmutedlist.count()): - if self.unmutedlist.item(i) == item: - self.muteselected = i - - def changeMuteIndex(self, item): - for i in range(self.mutedlist.count()): - if self.mutedlist.item(i) == item: - self.unmuteselected = i - - def onMuteClick(self): - if self.unmutedlist.count() == 0: - return QtGui.QMessageBox.warning(self, 'smh', 'you muted everyone\nhow does it feel?') - if self.muteselected == -1: - return QtGui.QMessageBox.warning(self, 'hey genius', 'who exactly are you muting?\nclick on their name then on the >> button') - for i in range(len(self.charlist)): - if self.charlist[i][0] == self.unmutedlist.item(self.muteselected).text(): - self.muted.append(i) - self.muted.sort() - self.muteselected = -1 - break - - self.unmutedlist.clear() - self.mutedlist.clear() - for i in range(len(self.charlist)): - if i in self.muted: - self.mutedlist.addItem(self.charlist[i][0]) - else: - self.unmutedlist.addItem(self.charlist[i][0]) - - def onUnmuteClick(self): - if self.mutedlist.count() == 0: - return QtGui.QMessageBox.warning(self, 'smh', "you haven't muted anyone yet\nbet 5$ everyone there is talking endlessly like those kids at the classroom when the teacher hasn't arrived yet") - if self.unmuteselected == -1: - return QtGui.QMessageBox.warning(self, 'hey genius', 'who exactly are you unmuting?\nclick on their name then on the >> button') - for char in self.charlist: - if char[0] == self.mutedlist.item(self.unmuteselected).text(): - del self.muted[self.unmuteselected] - self.unmuteselected = -1 - break - - self.unmutedlist.clear() - self.mutedlist.clear() - for i in range(len(self.charlist)): - if i in self.muted: - self.mutedlist.addItem(self.charlist[i][0]) - else: - self.unmutedlist.addItem(self.charlist[i][0]) - - def penaltyBarMinus(self, barType): - netmsg = 'HP#' + str(barType) + '#' - if barType == 1: - if self.defensebar.getHealth() <= 0: - return - netmsg += str(self.defensebar.getHealth() - 1) + '#' - elif barType == 2: - if self.prosecutionbar.getHealth() <= 0: - return - netmsg += str(self.prosecutionbar.getHealth() - 1) + '#' - netmsg += '%' - self.tcp.send(netmsg) - - def penaltyBarPlus(self, barType): - netmsg = 'HP#' + str(barType) + '#' - if barType == 1: - if self.defensebar.getHealth() >= 10: - return - netmsg += str(self.defensebar.getHealth() + 1) + '#' - elif barType == 2: - if self.prosecutionbar.getHealth() >= 10: - return - netmsg += str(self.prosecutionbar.getHealth() + 1) + '#' - netmsg += '%' - self.tcp.send(netmsg) - - def setWhiteFlash(self, on, realizationtype=0, msec=0): - self.whiteflashlab.setVisible(on) - if realizationtype == 1: - self.playRealization() - if msec: - self.whiteflash.start(msec) - - def setScreenShake(self, on, amount=20): - self.shakes_remaining = amount if on else 1 - self.screenshake.start(25) - - def WTCEbuttonPressed(self, type, variant): - if type != 2: - self.tcp.send('RT#testimony' + str(type + 1) + '#%') - else: - self.tcp.send("RT#judgeruling#" +str(variant)+ "#%") - - def onPVPacket(self, charname): - exec open(AO2XPpath+"ao2xp_themes/"+get_option("General", "theme", "default")+"/theme.py") - if not self.swapping: - self.loadCharacter(charname) - - def loadCharacter(self, charname): - self.msgqueueList.clear() - self.msgqueue = [] - self.effectdropdown.clear() - self.emotedropdown.clear() - self.charemotes = [] - self.selectedemote = 0 - self.current_emote_page = 0 - - effectslist = ini.get_effects(charname) - self.effectdropdown.setVisible(bool(effectslist)) - if effectslist: - effectslist.insert(0, "No effect") - self.effectdropdown.addItems(effectslist) - - self.charname = ini.read_ini(AOpath + 'characters/' + charname + '/char.ini', "options", "name", charname) - self.charside = ini.read_ini(AOpath + 'characters/' + charname + '/char.ini', "options", "side", "def") - self.posdropdown.setCurrentIndex(self.posdropdown.findText(self.charside)) - self.setJudgeButtons() - for emoteind in range(1, ini.read_ini_int(AOpath+"characters/"+self.charname+"/char.ini", "emotions", "number") + 1): - if emoteind == 1: - suffix = 'on' - else: - suffix = 'off' - - emote = ini.read_ini(AOpath + 'characters/' + charname + '/char.ini', "emotions", str(emoteind), 'normal#(a)normal#normal#0#') - sound = ini.read_ini(AOpath + 'characters/' + charname + '/char.ini', "soundn", str(emoteind), '1') - soundt = ini.read_ini(AOpath + 'characters/' + charname + '/char.ini', "soundt", str(emoteind), '0') - soundl = ini.read_ini(AOpath + 'characters/' + charname + '/char.ini', "soundl", str(emoteind), '0') # AO 2.8 - emotelist = emote.split('#') - del emotelist[len(emotelist) - 1] - emotelist.append(sound) - emotelist.append(soundt) - emotelist.append(soundl) # AO 2.8 - self.charemotes.append(emotelist) - if emotelist[0]: - self.emotedropdown.addItem(emotelist[0]) - else: - self.emotedropdown.addItem(emotelist[1] + ' ' + emotelist[2]) - - self.emotedropdown.setCurrentIndex(0) - self.set_emote_page() - - def set_emote_page(self): - if self.mychar < 0: - return - self.prevemotepage.hide() - self.nextemotepage.hide() - - total_emotes = ini.read_ini_int(AOpath+"characters/"+self.charname+"/char.ini", "emotions", "number", 1) - for button in self.emotebuttons: - button.hide() - - total_pages = total_emotes / self.max_emotes_on_page - emotes_on_page = 0 - if total_emotes % self.max_emotes_on_page != 0: - total_pages += 1 - if total_pages > self.current_emote_page + 1: - emotes_on_page = self.max_emotes_on_page - else: - emotes_on_page = total_emotes % self.max_emotes_on_page - else: - emotes_on_page = self.max_emotes_on_page - if total_pages > self.current_emote_page + 1: - self.nextemotepage.show() - if self.current_emote_page > 0: - self.prevemotepage.show() - for n_emote in range(emotes_on_page): - n_real_emote = n_emote + self.current_emote_page * self.max_emotes_on_page - if n_real_emote == self.selectedemote: - self.emotebuttons[n_emote].setPixmap(QtGui.QPixmap(AOpath + 'characters/' + self.charname + '/emotions/button' + str(n_real_emote + 1) + '_on.png')) - else: - self.emotebuttons[n_emote].setPixmap(QtGui.QPixmap(AOpath + 'characters/' + self.charname + '/emotions/button' + str(n_real_emote + 1) + '_off.png')) - self.emotebuttons[n_emote].show() - - def iniswap_index_change(self, ind): - self.iniswapindex = ind - - def loadSwapCharacters(self): - self.charsfolder = [] - self.iniswaplist.clear() - for folder in os.listdir(AOpath + 'characters'): - if exists(AOpath + 'characters/' + folder + '/char.ini'): - self.charsfolder.append(folder) - self.iniswaplist.addItem(folder) - - def iniswap_confirm(self): - if self.charlist[self.mychar][0].lower() == self.charsfolder[self.iniswapindex].lower(): - self.resetIniSwap() - else: - self.swapping = True - self.iniswapinfo.setText('Swapped to ' + self.charsfolder[self.iniswapindex]) - self.loadCharacter(self.charsfolder[self.iniswapindex]) - - def resetIniSwap(self): - self.swapping = False - self.iniswapinfo.setText('Not swapped') - self.loadCharacter(self.charlist[self.mychar][0]) - - def onAddEvidence(self): - self.evidence_editor.show() - - def onEditEvidence(self): - if not self.evidence: - return QtGui.QMessageBox.information(self, 'edit what?', "there's no evidence on the court record...") - self.evidence_editor.EditEvidence(self.selectedevi) - - def onDeleteEvidence(self): - if self.evidence: - self.tcp.send('DE#' + str(self.selectedevi) + '#%') - else: - self.tcp.send('DE#0#%') - - def onClick_callMod(self): - if "modcall_reason" in self.features: - reason, ok = QtGui.QInputDialog.getText(self, "Call a moderator", "Enter your reason.") - if ok and reason: - self.tcp.send("ZZ#"+reason.toUtf8()+"#%") - else: - self.tcp.send("ZZ#%") - - def onClick_changeChar(self): - #self.tcp.send('RD#%') - self.charselect.show() - - def changeFlipCheck(self, on): - if on == 2: - on = 1 - self.myflip = on - - def changeSfxCheck(self, on): - if on == 2: - on = 1 - self.playsfx = on - self.nointerruptbtn.setDisabled(not on) - if on == 0: - self.nointerruptbtn.setChecked(False) - - def onClicked_msgqueue(self, item): - for i in range(len(self.msgqueueList)): - if self.msgqueueList.item(i) == item: - self.selectedmsg = i - - def onClicked_removeQueue(self): - if self.selectedmsg == -1: - return QtGui.QMessageBox.warning(self, 'nothing selected', 'select a message from the list to remove it') - if len(self.msgqueueList) == 0: - return QtGui.QMessageBox.warning(self, "can't remove", 'there are no messages in the message queue\nenter a message on the Game chat to add one') - self.msgqueueList.takeItem(self.selectedmsg) - del self.msgqueue[self.selectedmsg] - - def changeEvidence(self, ind): - if ind < 0: - return - self.selectedevi = ind - self.evidencedesc.setText(self.evidence[ind][1]) - self.setEvidenceImg(self.evidenceimage, self.evidence[ind][2]) - - def changeEmote(self, dropdown, ind): - if ind == -1: - return - if not dropdown: - self.selectedemote = ind + self.current_emote_page * self.max_emotes_on_page - else: - self.selectedemote = ind - for button in self.emotebuttons: - if button.emoteid == ind: - button.setPixmap(QtGui.QPixmap(AOpath + 'characters/' + self.charname + '/emotions/button' + str(button.emoteid + self.current_emote_page * self.max_emotes_on_page + 1) + '_on.png')) - else: - button.setPixmap(QtGui.QPixmap(AOpath + 'characters/' + self.charname + '/emotions/button' + str(button.emoteid + self.current_emote_page * self.max_emotes_on_page + 1) + '_off.png')) - - def setChatColor(self, ind): - self.mychatcolor = ind - - def showMessage(self, type, *args, **kwargs): - if type == 'critical': - reply = QtGui.QMessageBox.critical(self, *args, **kwargs) - elif type == 'information': - reply = QtGui.QMessageBox.information(self, *args, **kwargs) - elif type == 'question': - reply = QtGui.QMessageBox.question(self, *args, **kwargs) - elif type == 'warning': - reply = QtGui.QMessageBox.warning(self, *args, **kwargs) - if self.willDisconnect: - self.stopMusic() - self.gamewindow.returnToMenu() - - def onMusicClick(self, item): - if "cccc_ic_support" in self.features and self.showname: - self.tcp.send('MC#' + item.text().toUtf8() + '#' + str(self.mychar) + '#' + self.showname + '#%') - else: - self.tcp.send('MC#' + item.text().toUtf8() + '#' + str(self.mychar) + '#%') - - def icLogChanged(self): - if self.icLog.verticalScrollBar().value() == self.icLog.verticalScrollBar().maximum(): self.icLog.verticalScrollBar().setValue(self.icLog.verticalScrollBar().maximum()) - - def ooclog_update(self): - if self.ooclog.verticalScrollBar().value() == self.ooclog.verticalScrollBar().maximum(): self.ooclog.verticalScrollBar().setValue(self.ooclog.verticalScrollBar().maximum()) - - def sendOOCchat(self, name, text): - self.tcp.send('CT#' + name + '#' + text + '#%') - - def onOOCreturn(self): - text = self.oocinput.text().toUtf8().replace('#', '').replace('%', '').replace('&', '').replace('$', '').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....... - ind = self.posdropdown.findText(str(text.split(" ")[1])) - if ind >= 0: self.posdropdown.setCurrentIndex(ind) - self.oocinput.clear() - return - - if self.mocktext.isChecked(): - text = mockStr(text) - if self.autocaps.isChecked(): - l = list(text) - l[0] = l[0].upper() - if l[-1] != ".": - l.append(".") - text = "".join(l).replace(" i ", " I ").replace("i'm", "I'm").replace("it's", "It's") - - self.sendOOCchat(self.oocnameinput.text().toUtf8(), text) - self.oocinput.clear() - - def onICreturn(self): - text = str(self.icchatinput.text().toUtf8()).replace('#', '').replace('%', '').replace('&', '').replace('$', '').replace('/n', '\n') - if not text: - return - - if self.mocktext.isChecked(): - text = mockStr(text) - if self.autocaps.isChecked(): - l = list(text) - l[0] = l[0].upper() - if l[-1] != ".": - l.append(".") - text = "".join(l).replace(" i ", " I ").replace("i'm", "I'm").replace("it's", "It's") - if self.spacebartext.isChecked(): - l = list(text) - for i in range(1, len(l)+len(l)-1, 2): - l.insert(i, " ") - text = "".join(l) - - emote = self.charemotes[self.selectedemote] - if self.nointerruptbtn.isChecked(): - modifier = 0 - else: - modifier = self.playsfx - objection = 0 - if self.customobject.isPressed(): - objection = 4 - self.customobject.setPressed(False) - elif self.holditbtn.isPressed(): - objection = 1 - self.holditbtn.setPressed(False) - elif self.objectbtn.isPressed(): - objection = 2 - self.objectbtn.setPressed(False) - elif self.takethatbtn.isPressed(): - objection = 3 - self.takethatbtn.setPressed(False) - if emote[3] == '5': #zoom - if self.nointerruptbtn.isChecked(): - modifier = 5 - else: - if objection > 0: - modifier = 6 - else: - modifier = 5 - elif objection > 0: - if self.nointerruptbtn.isChecked(): - modifier = 0 - else: - modifier = 2 - - msg = "MS#" - - if "deskmod" in self.features: # visible desk modifier - msg += "%d#" % self.deskbtn.isChecked() - else: - msg += "chat#" - - msg += emote[1]+"#" #pre-anim - msg += self.charname+"#" - msg += emote[2]+"#" #anim - msg += text+"#" - msg += self.charside+"#" - msg += emote[4]+"#" #sfx - msg += str(modifier)+"#" #emote modifier - msg += str(self.mychar)+"#" #character ID - msg += emote[5]+"#" #sfx delay - msg += str(objection)+"#" - msg += str((self.selectedevi + 1) * int(self.present))+"#" #selected evidence - - if self.present: - self.present = False - self.evidencepresent.setPixmap(self.evidencepresent.button_off) - - if "flipping" in self.features: - msg += str(self.myflip)+"#" - else: - msg += str(self.mychar)+"#" # old AO servers send a second charID in the message because drunk fanat - - msg += str(int(self.realizationbtn.isPressed()))+"#" - msg += str(self.mychatcolor)+"#" - - if "cccc_ic_support" in self.features: - msg += self.showname+"#" # custom showname - if self.paircheckbox.isChecked(): - msg += str(self.pairdropdown.currentIndex()) # pair charID - if "effects" in self.features: - msg += "^%d#" % self.pair_order.currentIndex() # pair ordering - else: - msg += "#" - else: - msg += "-1#" - - # AO 2.8: always send offset - if "y_offset" in self.features: # AO 2.9 - msg += str(self.pairoffset.value()) + "&" + str(-self.ypairoffset.value()) + "#" - else: - msg += str(self.pairoffset.value())+"#" - - msg += str(int(self.nointerruptbtn.isChecked()))+"#" # NoInterrupt(TM) - - if "looping_sfx" in self.features: # AO 2.8 - msg += emote[6]+"#" # loop sound? - msg += "%d#" % self.shakebtn.isPressed() # screen shake - emotes_to_check = [emote[1], "(b)"+emote[2], "(a)"+emote[2]] - effects_to_check = ["_FrameScreenshake", "_FrameRealization", "_FrameSFX"] - - for f_effect in effects_to_check: - packet = "" - for f_emote in emotes_to_check: - packet += f_emote - if ini.read_ini_bool("AO2XP.ini", "General", "network frame effects"): - sfx_frames = "|".join(ini.read_ini_tags(AOpath+"characters/"+self.charname+"/char.ini", f_emote + f_effect)) - if sfx_frames: - packet += "|" + sfx_frames - packet += "^" - msg += packet+"#" - - if "additive" in self.features: - msg += "%d#" % self.additivebtn.isChecked() - - if "effects" in self.features: - fx = self.effectdropdown.currentText() if self.effectdropdown.currentIndex() > 0 else "" - fx_sound = ini.get_effect_sound(fx, self.charname) - p_effect = ini.read_ini(AOpath+"characters/"+self.charname+"/char.ini", "options", "effects") - msg += fx + "|" + p_effect + "|" + fx_sound + "#" - self.effectdropdown.setCurrentIndex(0) - - msg += "%" - self.msgqueueList.addItem(self.icchatinput.text()) - self.msgqueue.append(msg) - - self.icchatinput.clear() - self.realizationbtn.setPressed(False) - self.shakebtn.setPressed(False) - - def setBackground(self, bg): - if not exists(AOpath + 'background/' + bg): - bg = 'default' - - for bgfile in [["side_def", "defenseempty"], - ["bench_def", "defensedesk"], - ["side_pro", "prosecutorempty"], - ["bench_pro", "prosecutiondesk"], - ["side_wit", "witnessempty"], - ["bench_wit", "stand"], - ["side_hld", "helperstand"], - ["bench_hld", "helperdesk"], - ["side_hlp", "prohelperstand"], - ["bench_hlp", "prohelperdesk"], - ["side_jud", "judgestand"], - ["bench_jud", "judgedesk"], - ["side_jur", "jurystand"], - ["bench_jur", "jurydesk"], - ["side_sea", "seancestand"], - ["bench_sea", "seancedesk"]]: - - bgimg = QtGui.QImage(AOpath + 'background/' + bg + '/' + bgfile[1] + '.png') - if not bgimg.isNull(): - if bgimg.size().width() != 256 or bgimg.size().height() != 192: - setattr(self, bgfile[0], QtGui.QPixmap.fromImage(bgimg.scaled(256*2, 192*2, transformMode=QtCore.Qt.SmoothTransformation))) - else: - setattr(self, bgfile[0], QtGui.QPixmap.fromImage(bgimg)) - else: - setattr(self, bgfile[0], QtGui.QPixmap.fromImage(bgimg)) - - - def netmsg_hp(self, type, health): - if type == 1: - self.defensebar.setHealth(health) - elif type == 2: - self.prosecutionbar.setHealth(health) - - def netmsg_ms(self, p_contents): - if len(p_contents) < 15: #this is already done on the TCP thread but i'll do it here anyway as well - return - - AO2chat = "cccc_ic_support" in self.features - - if int(p_contents[CHAR_ID]) in self.muted: # skip the self.chatmessage copy line below - return - - for n_string in range(self.chatmessage_size): - if n_string < len(p_contents) and (n_string < 16 or AO2chat): - self.m_chatmessage[n_string] = p_contents[n_string] - else: - self.m_chatmessage[n_string] = "" - - f_char_id = int(self.m_chatmessage[CHAR_ID]) - - if f_char_id < 0 or f_char_id >= len(self.charlist): - return - - f_showname = "" - if not self.m_chatmessage[SHOWNAME]: - f_showname = self.m_chatmessage[CHARNAME] - else: - f_showname = self.m_chatmessage[SHOWNAME] - - self.text_state = 0 - self.anim_state = 0 - self.objectionview.stop() - self.char.stop() - self.chat_tick_timer.stop() - self.presentedevi.hide() - - self.chatmessage_is_empty = self.m_chatmessage[CHATMSG] == " " or self.m_chatmessage[CHATMSG] == "" - - if self.msgqueue: - chatmsgcomp = str(self.msgqueue[0].split('#')[5]).decode('utf-8').replace('', '$').replace('', '%').replace('', '&').replace('', '#') - if f_char_id == self.mychar and self.m_chatmessage[CHATMSG] == chatmsgcomp: # our message showed up - del self.msgqueue[0] - self.msgqueueList.takeItem(0) - if self.additivebtn.isChecked(): - self.icchatinput.insert(" ") - - f_char = self.m_chatmessage[CHARNAME] - evidence = int(self.m_chatmessage[EVIDENCE])-1 - - t = time.localtime() - logcharname = f_char - if f_char.lower() != self.charlist[f_char_id][0].lower(): - logcharname = self.charlist[f_char_id][0] + ' (' + f_char.decode("utf-8") + ')' - - if self.m_chatmessage[SHOWNAME]: - try: - logcharname += " ("+self.m_chatmessage[SHOWNAME].decode("utf-8")+")" - except: - logcharname += " (???)" - - if evidence == -1: - self.icLog.append('[%d:%.2d] %s: %s' % (t[3], t[4], logcharname, self.m_chatmessage[CHATMSG])) - else: - eviname = '(NULL) %d' % evidence - try: - eviname = self.evidence[evidence][0] - except: - pass - - self.icLog.append('[%d:%.2d] %s: %s\n%s presented an evidence: %s' % (t[3], t[4], logcharname, self.m_chatmessage[CHATMSG], f_char, eviname)) - - self.is_additive = (self.m_chatmessage[ADDITIVE] == "1") - - custom_objection = "custom" - try: objection_mod = int(self.m_chatmessage[SHOUT_MOD]) - except: - if "4&" in self.m_chatmessage[SHOUT_MOD]: # custom objection name - objection_mod = 4 - custom_objection = self.m_chatmessage[SHOUT_MOD].split("4&")[1] # get the name - else: # just in case of mindfuckery - objection_mod = 0 - - if objection_mod <= 4 and objection_mod >= 1: - objections = ["holdit", "objection", "takethat", "custom_objections/"+custom_objection if custom_objection != "custom" else "custom"] - self.objectionview.play(objections[objection_mod-1], f_char) - self.playObjectionSnd(f_char, objection_mod) - - emote_mod = int(self.m_chatmessage[EMOTE_MOD]) - if emote_mod == 0: - self.m_chatmessage[EMOTE_MOD] = 1 - else: - self.handle_chatmessage_2() - - def set_text_color(self): - textcolor = int(self.m_chatmessage[TEXT_COLOR]) - - is_rainbow = textcolor == 6 - - if textcolor == 0: - color = QtGui.QColor(255, 255, 255) - elif textcolor == 1: - color = QtGui.QColor(0, 255, 0) - elif textcolor == 2: #OH FUCK MOD - color = QtGui.QColor(255, 0, 0) - elif textcolor == 3: - color = QtGui.QColor(255, 165, 0) - elif textcolor == 4: - color = QtGui.QColor(45, 150, 255) - elif textcolor == 5: - color = QtGui.QColor(255, 255, 0) - elif textcolor == 6: - color = QtGui.QColor(255, 255, 255) - elif textcolor == 7: - color = QtGui.QColor(255, 192, 203) - elif textcolor == 8: - color = QtGui.QColor(0, 255, 255) - - if is_rainbow: - self.text.show() - self.ao2text.hide() - else: - self.text.hide() - self.ao2text.show() - - style = "background-color: rgba(0, 0, 0, 0);\n" - style += "color: rgb("+str(color.red())+", "+str(color.green())+", "+str(color.blue())+")" - self.ao2text.setStyleSheet(style) - - def set_scene(self): - side = self.m_chatmessage[SIDE] - - if side == 'def': - self.court.setPixmap(self.side_def) - self.bench.setPixmap(self.bench_def) - self.bench.move(0, 192*2 - self.bench_def.size().height()) - self.presentedevi.move(170, 16) - elif side == 'pro': - self.court.setPixmap(self.side_pro) - self.bench.setPixmap(self.bench_pro) - self.bench.move(256*2 - self.bench_pro.size().width(), 192*2 - self.bench_pro.size().height()) - self.presentedevi.move(16, 16) - elif side == 'wit': - self.court.setPixmap(self.side_wit) - self.bench.setPixmap(self.bench_wit) - self.bench.move(0, 0) - self.presentedevi.move(16, 16) - elif side == 'hld': - self.court.setPixmap(self.side_hld) - self.bench.setPixmap(self.bench_hld) - self.presentedevi.move(16, 16) - elif side == 'hlp': - self.court.setPixmap(self.side_hlp) - self.bench.setPixmap(self.bench_hlp) - self.presentedevi.move(170, 16) - elif side == 'jud': - self.court.setPixmap(self.side_jud) - self.bench.setPixmap(self.bench_jud) - self.presentedevi.move(16, 16) - elif side == 'sea': - self.court.setPixmap(self.side_jud if self.side_sea.isNull() else self.side_sea) - self.bench.setPixmap(self.bench_jud if self.bench_sea.isNull() else self.bench_sea) - self.presentedevi.move(16, 16) - elif side == 'jur': - self.court.setPixmap(self.side_jud if self.side_jur.isNull() else self.side_jur) - self.bench.setPixmap(self.bench_jud if self.bench_jur.isNull() else self.bench_jur) - self.presentedevi.move(16, 16) - - deskmod = self.m_chatmessage[DESK_MOD] - if deskmod == "0" or (deskmod != "1" and (side in ("jud", "hld", "hlp"))): - self.bench.hide() - else: - self.bench.show() - - def objection_done(self): - self.handle_chatmessage_2() - - def handle_chatmessage_2(self): - self.zoom.setZoom(False) - self.char.stop() - self.effectview.stop() - - if not self.m_chatmessage[SHOWNAME]: - self.name.setText(self.m_chatmessage[CHARNAME]) - else: - self.name.setText(self.m_chatmessage[SHOWNAME].decode("utf-8")) - - self.chatbox.hide() - - self.set_scene() - self.set_text_color() - - f_message = self.m_chatmessage[CHATMSG] - if len(f_message) >= 2: - self.message_is_centered = f_message.startswith("~~") - else: - self.ao2text.setAlignment(QtCore.Qt.AlignLeft) - self.text.setAlignment(QtCore.Qt.AlignLeft) - - if self.m_chatmessage[FLIP] == "1": - self.char.set_flipped(True) - else: - self.char.set_flipped(False) - - side = self.m_chatmessage[SIDE] - emote_mod = int(self.m_chatmessage[EMOTE_MOD]) - - # AO 2.8: always offset player - hor_offset = vert_offset = 0 - - if "y_offset" in self.features: # AO 2.9 - keyword = "" if "" in self.m_chatmessage[SELF_OFFSET] else "&" # i don't think it's hdf's fault but this is still ridiculous - hor_offset = int(self.m_chatmessage[SELF_OFFSET].split(keyword)[0]) - vert_offset = int(self.m_chatmessage[SELF_OFFSET].split(keyword)[1]) if len(self.m_chatmessage[SELF_OFFSET].split(keyword)) > 1 else 0 - else: - hor_offset = int(self.m_chatmessage[SELF_OFFSET]) - - - if side == "def": - if hor_offset > 0 and vert_offset == 0: - vert_offset = hor_offset / 10 - elif side == "pro": - if hor_offset < 0 and vert_offset == 0: - vert_offset = -1 * hor_offset / 10 - - self.char.move(256 * hor_offset / 100, 192 * vert_offset / 100) - - # check if paired - if not self.m_chatmessage[OTHER_CHARID]: - self.sidechar.hide() - self.sidechar.move(0,0) - else: - if "effects" in self.features: - got_other_charid = int(self.m_chatmessage[OTHER_CHARID].split("^")[0]) - else: - got_other_charid = int(self.m_chatmessage[OTHER_CHARID]) - - if got_other_charid > -1: # user is paired - self.sidechar.show() - - if "effects" in self.features: - pair_order = int(self.m_chatmessage[OTHER_CHARID].split("^")[1]) - else: - pair_order = -1 - - hor2_offset = vert2_offset = 0 - if "y_offset" in self.features: # AO 2.9 - keyword = "" if "" in self.m_chatmessage[OTHER_OFFSET] else "&" # i don't think it's hdf's fault but this is still ridiculous - hor2_offset = int(self.m_chatmessage[OTHER_OFFSET].split(keyword)[0]) - vert2_offset = int(self.m_chatmessage[OTHER_OFFSET].split(keyword)[1]) if len(self.m_chatmessage[OTHER_OFFSET].split(keyword)) > 1 else 0 - else: - hor2_offset = int(self.m_chatmessage[OTHER_OFFSET]) - - if side == "def": - if hor2_offset > 0: - vert2_offset = hor2_offset / 10 - - elif side == "pro": - if hor2_offset < 0: - vert2_offset = -1 * hor2_offset / 10 - - if pair_order == -1: # pair ordering not supported - if hor2_offset >= hor_offset: - self.sidechar.raise_() - self.char.raise_() - else: - self.char.raise_() - self.sidechar.raise_() - elif pair_order == 0: # front - self.char.raise_() - self.sidechar.raise_() - elif pair_order == 1: # behind - self.sidechar.raise_() - self.char.raise_() - - self.sidechar.move(256 * hor2_offset / 100, 192 * vert2_offset / 100) - - self.bench.raise_() - self.chatbox.raise_() - self.effectview.raise_() - self.objectionview.raise_() - self.whiteflashlab.raise_() - - self.sidechar.set_flipped(self.m_chatmessage[OTHER_FLIP] == "1") - self.sidechar.play_idle(self.m_chatmessage[OTHER_NAME], self.m_chatmessage[OTHER_EMOTE]) - - else: - self.sidechar.hide() - self.sidechar.move(0, 0) - - if (emote_mod == 1 or emote_mod == 6) and self.m_chatmessage[PREANIM] != "-": - self.play_preanim(False) - elif emote_mod == 0 or emote_mod == 5 or self.m_chatmessage[PREANIM] == "-": - if self.m_chatmessage[NO_INTERRUPT] == "0" or self.m_chatmessage[PREANIM] == "-": - self.handle_chatmessage_3() - else: - self.play_preanim(True) - - def play_preanim(self, noninterrupting): - f_char = self.m_chatmessage[CHARNAME] - f_preanim = self.m_chatmessage[PREANIM] - - ao2_duration = ini.read_ini_int(AOpath+"characters/"+f_char+"/char.ini", "time", "%"+f_preanim, -1) - text_delay = ini.read_ini_int(AOpath+"characters/"+f_char+"/char.ini", "textdelay", f_preanim, -1) - sfx_delay = int(self.m_chatmessage[SFX_DELAY]) * 60 - - preanim_duration = 0 - if ao2_duration < 0: - preanim_duration = ini.read_ini_int(AOpath+"characters/"+f_char+"/char.ini", "time", f_preanim, -1) - else: - preanim_duration = ao2_duration - - anim_to_find = AOpath+"characters/"+f_char+"/"+f_preanim+".gif" - apng_to_find = AOpath+"characters/"+f_char+"/"+f_preanim+".apng" - webp_to_find = AOpath+"characters/"+f_char+"/"+f_preanim+".webp" - if (not exists(anim_to_find) and not exists(apng_to_find) and not exists(webp_to_find)) or preanim_duration < 0: - if noninterrupting: - self.anim_state = 4 - else: - self.anim_state = 1 - self.preanim_done() - - self.char.play_pre(f_char, f_preanim, preanim_duration) - if noninterrupting: - self.anim_state = 4 - else: - self.anim_state = 1 - - if sfx_delay > 0: - self.sfx_delay_timer.start(sfx_delay) - else: - self.play_sfx() - - if text_delay >= 0: - pass #text delay timer, but not now. - - if noninterrupting: - self.handle_chatmessage_3() - - def preanim_done(self): - self.anim_state = 1 - self.handle_chatmessage_3() - - def handle_chatmessage_3(self): - self.start_chat_ticking() - - f_evi_id = int(self.m_chatmessage[EVIDENCE]) - f_side = self.m_chatmessage[SIDE] - - emote_mod = int(self.m_chatmessage[EMOTE_MOD]) - - if f_evi_id > 0 and f_evi_id <= len(self.evidence): - f_image = self.evidence[f_evi_id-1][2] - is_left_side = not (f_side == "def" or f_side == "hlp" or f_side == "jud" or f_side == "jur") - - self.setEvidenceImg(self.presentedevi, f_image) - - if not is_left_side: - self.presentedevi.move(170, 16) - else: - self.presentedevi.move(16, 16) - self.presentedevi.show() - else: - self.presentedevi.hide() - - side = self.m_chatmessage[SIDE] - if emote_mod == 5 or emote_mod == 6: - self.bench.hide() - self.sidechar.hide() - self.char.move(0,0) - - if side == "pro" or side == "hlp" or side == "wit": - self.zoom.setZoom(True, 1) - else: - self.zoom.setZoom(True, 0) - - f_anim_state = 0 - text_is_blue = int(self.m_chatmessage[TEXT_COLOR]) == 4 - - if not text_is_blue and self.text_state == 1: - f_anim_state = 2 - self.entire_message_is_blue = False - else: - f_anim_state = 3 - self.entire_message_is_blue = True - - if f_anim_state <= self.anim_state: - return - - self.char.stop() - f_char = self.m_chatmessage[CHARNAME] - f_emote = self.m_chatmessage[ANIM] - - if f_anim_state == 2: - self.char.play_talking(f_char, f_emote) - self.anim_state = 2 - else: - self.char.play_idle(f_char, f_emote) - self.anim_state = 3 - - if exists(AOpath+"callwords.ini"): - callwords = [line.rstrip() for line in open(AOpath+"callwords.ini")] - for callword in callwords: - if callword.lower() in self.m_chatmessage[CHATMSG].lower(): - self.ooclog.append("%s called you." % f_char) - snd = audio.loadhandle(False, "word_call.wav", 0, 0, BASS_STREAM_AUTOFREE) - if snd: - audio.playhandle(snd, True) - break - - def do_effect(self, fx_name, fx_sound, p_char, p_folder): - effect = ini.get_effect(fx_name, p_char, p_folder) - if not effect: return - - if fx_sound: - self.playSound(fx_sound) - - if "effects" not in self.features: return - - self.effectview.set_play_once(False) - try: self.effectview.play(effect) - except: pass # Windows XP webp error with Pillow - print "effect", repr(effect) - - def start_chat_ticking(self): - if self.text_state != 0: - return - - if self.m_chatmessage[EFFECTS]: - fx_list = self.m_chatmessage[EFFECTS].split("|") - fx = fx_list[0] - fx_sound = "" - fx_folder = "" - - if len(fx_list) > 1: - fx_sound = fx_list[1] - if len(fx_list) > 2: - fx_folder = fx_list[1] - fx_sound = fx_list[2] - - if fx and fx not in ("-", "None"): - self.do_effect(fx, fx_sound, self.m_chatmessage[CHARNAME], fx_folder) - - elif self.m_chatmessage[REALIZATION] == "1": - self.setWhiteFlash(True, 1, 125) - - self.set_text_color() - - charid = int(self.m_chatmessage[CHAR_ID]) - if not self.is_additive or self.additive_char != charid: - self.ao2text.clear() - self.text.setText("") - self.additive_char = charid - - if self.chatmessage_is_empty: - self.text_state = 2 - return - - self.inline_color_stack = [] - - self.chatbox.show() - - self.tick_pos = 0 - self.blip_pos = 0 - self.inline_blue_depth = 0 - - self.current_display_speed = 3 - self.chat_tick_timer.start(self.message_display_speed[self.current_display_speed]) - - self.blip = self.charlist[charid][2] - - if exists(AOpath+"sounds/general/sfx-blip"+self.blip+".wav"): - self.blipsnd = audio.loadhandle(False, AOpath+"sounds/general/sfx-blip"+self.blip+".wav", 0, 0, 0) - elif exists(AOpath+"sounds/general/sfx-blip"+self.blip+".opus"): - self.blipsnd = audio.loadhandle(False, AOpath+"sounds/general/sfx-blip"+self.blip+".opus", 0, 0, 0) - elif exists(AOpath+"sounds/blips/"+self.blip+".wav"): - self.blipsnd = audio.loadhandle(False, AOpath+"sounds/blips/"+self.blip+".wav", 0, 0, 0) - elif exists(AOpath+"sounds/blips/"+self.blip+".opus"): - self.blipsnd = audio.loadhandle(False, AOpath+"sounds/blips/"+self.blip+".opus", 0, 0, 0) - else: - self.blipsnd = 0 - - audio.sethandleattr(self.blipsnd, BASS_ATTRIB_VOL, self.blipslider.value() / 100.0) - - emote_mod = int(self.m_chatmessage[EMOTE_MOD]) - if emote_mod in (0, 5) and self.m_chatmessage[SCREENSHAKE] == "1": - self.setScreenShake(True) - - self.text_state = 1 - - def chat_tick(self): - f_message = self.m_chatmessage[CHATMSG] - - self.chat_tick_timer.stop() - formatting_char = False - - if self.message_is_centered: - f_message = f_message.strip("~~") - - if self.tick_pos >= len(f_message): - self.text_state = 2 - if self.anim_state != 4: - self.anim_state = 3 - self.char.play_idle(self.m_chatmessage[CHARNAME], self.m_chatmessage[ANIM]) - else: - f_character2 = f_message[self.tick_pos] - f_character = QtCore.QString(f_character2) - - if f_character == " ": - self.text.setText(self.text.text() + " ") - self.ao2text.insertPlainText(" ") - - elif f_character == "\n" or f_character == "\r": - self.text.setText(self.text.text() + "\n") - self.ao2text.insertPlainText("\n") - - elif f_character == "\\" and not self.next_character_is_not_special: - self.next_character_is_not_special = True - formatting_char = True - - elif f_character == "{" and not self.next_character_is_not_special: - self.current_display_speed += 1 - formatting_char = True - - elif f_character == "}" and not self.next_character_is_not_special: - self.current_display_speed -= 1 - formatting_char = True - - elif f_character == "|" and not self.next_character_is_not_special: #orange. - if self.inline_color_stack: - if self.inline_color_stack[-1] == INLINE_ORANGE: - del self.inline_color_stack[-1] - else: - self.inline_color_stack.append(INLINE_ORANGE) - else: - self.inline_color_stack.append(INLINE_ORANGE) - formatting_char = True - - elif f_character == "(" and not self.next_character_is_not_special: #blue. - self.inline_color_stack.append(INLINE_BLUE) - self.ao2text.insertHtml("" + f_character + "") - - self.inline_blue_depth += 1 - if not self.entire_message_is_blue and self.anim_state != 4: - f_char = self.m_chatmessage[CHARNAME] - f_emote = self.m_chatmessage[ANIM] - self.char.play_idle(f_char, f_emote) - - elif f_character == ")" and not self.next_character_is_not_special and self.inline_color_stack: - if self.inline_color_stack[-1] == INLINE_BLUE: - del self.inline_color_stack[-1] - self.ao2text.insertHtml("" + f_character + "") - - if self.inline_blue_depth > 0: - self.inline_blue_depth -= 1 - - if not self.entire_message_is_blue: - if self.inline_blue_depth == 0 and self.anim_state != 4 and not (self.tick_pos+1 >= len(f_message)): - f_char = self.m_chatmessage[CHARNAME] - f_emote = self.m_chatmessage[ANIM] - self.char.play_talking(f_char, f_emote) - else: - self.next_character_is_not_special = True - self.tick_pos -= 1 - - elif f_character == "[" and not self.next_character_is_not_special: #gray. - self.inline_color_stack.append(INLINE_GRAY) - self.ao2text.insertHtml("" + f_character + "") - - elif f_character == "]" and not self.next_character_is_not_special and self.inline_color_stack: - if self.inline_color_stack[-1] == INLINE_GRAY: - del self.inline_color_stack[-1] - self.ao2text.insertHtml("" + f_character + "") - else: - self.next_character_is_not_special = True - self.tick_pos -= 1 - - elif f_character == "`" and not self.next_character_is_not_special: #green. - if self.inline_color_stack: - if self.inline_color_stack[-1] == INLINE_GREEN: - del self.inline_color_stack[-1] - else: - self.inline_color_stack.append(INLINE_GREEN) - else: - self.inline_color_stack.append(INLINE_GREEN) - formatting_char = True - - elif f_character == "~" and not self.next_character_is_not_special: #green. - if self.inline_color_stack: - if self.inline_color_stack[-1] == INLINE_RED: - del self.inline_color_stack[-1] - else: - self.inline_color_stack.append(INLINE_RED) - else: - self.inline_color_stack.append(INLINE_RED) - formatting_char = True - - elif f_character == "s" and self.next_character_is_not_special: # shake - self.setScreenShake(True) - self.next_character_is_not_special = False - - elif f_character == "f" and self.next_character_is_not_special: # flash - self.setWhiteFlash(True, 0, 75) - self.next_character_is_not_special = False - - elif f_character == "n" and self.next_character_is_not_special: # newline - self.text.setText(self.text.text() + "\n") - self.ao2text.insertPlainText("\n") - self.next_character_is_not_special = False - - else: - self.next_character_is_not_special = False - if self.inline_color_stack: - top_color = self.inline_color_stack[-1] - if top_color == INLINE_ORANGE: - self.ao2text.insertHtml("" + f_character + "") - elif top_color == INLINE_BLUE: - self.ao2text.insertHtml("" + f_character + "") - elif top_color == INLINE_GREEN: - self.ao2text.insertHtml("" + f_character + "") - elif top_color == INLINE_GRAY: - self.ao2text.insertHtml("" + f_character + "") - elif top_color == INLINE_RED: - self.ao2text.insertHtml("" + f_character + "") - else: - self.ao2text.insertHtml(f_character) - else: - if int(self.m_chatmessage[TEXT_COLOR]) == C_RAINBOW: - self.text.setText(self.text.text() + f_character) - else: - self.ao2text.insertHtml(f_character) - - if self.message_is_centered: - self.ao2text.setAlignment(QtCore.Qt.AlignCenter) - self.text.setAlignment(QtCore.Qt.AlignCenter) - else: - self.ao2text.setAlignment(QtCore.Qt.AlignLeft) - self.text.setAlignment(QtCore.Qt.AlignLeft) - - if f_message[self.tick_pos] != " " or self.blank_blip: - if self.blip_pos % self.blip_rate == 0 and not formatting_char: - self.blip_pos = 0 - audio.playhandle(self.blipsnd, True) - - self.blip_pos += 1 - - self.tick_pos += 1 - - if self.current_display_speed < 0: - self.current_display_speed = 0 - elif self.current_display_speed > 6: - self.current_display_speed = 6 - - if formatting_char: - self.chat_tick_timer.start(1) - else: - self.chat_tick_timer.start(self.message_display_speed[self.current_display_speed]) - - def playRealization(self): - audio.playhandle(self.realizationsnd, True) - - def playObjectionSnd(self, charname, objection): - if self.objectsnd: - if audio.handleisactive(self.objectsnd): - audio.stophandle(self.objectsnd) - audio.freehandle(self.objectsnd) - - objecting = ["holdit", "objection", "takethat", "custom"][objection-1] - - if objecting: - if exists(AOpath + 'characters/' + charname + '/' + objecting + '.wav'): - self.objectsnd = audio.loadhandle(False, AOpath + 'characters/' + charname + '/' + objecting + '.wav', 0, 0, 0) - elif exists(AOpath + 'characters/' + charname + '/' + objecting + '.opus'): - self.objectsnd = audio.loadhandle(False, AOpath + 'characters/' + charname + '/' + objecting + '.opus', 0, 0, 0) - else: - self.objectsnd = None - if ini.read_ini_bool("AO2XP.ini", "General", "download sounds", True): - thread.start_new_thread(download_thread, ("base/characters/"+charname.lower()+"/"+objecting.lower()+".wav", AOpath+"characters/"+charname.lower()+"/"+objecting.lower()+".wav")) - thread.start_new_thread(download_thread, ("base/characters/"+charname.lower()+"/"+objecting.lower()+".opus", AOpath+"characters/"+charname.lower()+"/"+objecting.lower()+".wav")) - - if exists(AOpath + 'sounds/general/sfx-objection.opus'): - self.objectsnd = audio.loadhandle(False, AOpath + 'sounds/general/sfx-objection.opus', 0, 0, 0) - else: - self.objectsnd = audio.loadhandle(False, AOpath + 'sounds/general/sfx-objection.wav', 0, 0, 0) - audio.sethandleattr(self.objectsnd, BASS_ATTRIB_VOL, self.soundslider.value() / 100.0) - audio.playhandle(self.objectsnd, True) - - def play_sfx(self): - sfx_name = self.m_chatmessage[SFX] - if sfx_name == "1": - return - - self.playSound(sfx_name) - - def playSound(self, sfx): - if self.sound: - if audio.handleisactive(self.sound): - audio.stophandle(self.sound) - audio.freehandle(self.sound) - - if exists(AOpath + 'sounds/general/' + sfx + '.wav'): - self.sound = audio.loadhandle(False, AOpath + 'sounds/general/' + sfx + '.wav', 0, 0, 0) - audio.sethandleattr(self.sound, BASS_ATTRIB_VOL, self.soundslider.value() / 100.0) - audio.playhandle(self.sound, True) - - elif exists(AOpath + 'sounds/general/' + sfx + '.opus'): - self.sound = audio.loadhandle(False, AOpath + 'sounds/general/' + sfx + '.opus', 0, 0, 0) - audio.sethandleattr(self.sound, BASS_ATTRIB_VOL, self.soundslider.value() / 100.0) - audio.playhandle(self.sound, True) - - def playMusic(self, mus): - if not mus.endswith(".mp3") and "===MUSIC START===.mp3" in self.musiclist: #vidya workaround - mus += ".mp3" - - if self.music: - if audio.handleisactive(self.music): - audio.stophandle(self.music) - audio.freehandle(self.music) - - if exists(AOpath + 'sounds/music/' + mus): - self.music = audio.loadhandle(False, AOpath + 'sounds/music/' + mus, 0, 0, BASS_SAMPLE_LOOP) - audio.sethandleattr(self.music, BASS_ATTRIB_VOL, self.musicslider.value() / 100.0) - audio.playhandle(self.music, True) - - elif ini.read_ini_bool("AO2XP.ini", "General", "download music", True): - if mus.lower().startswith("http"): - self.music = audio.loadURLhandle(mus, 0, BASS_STREAM_BLOCK | BASS_SAMPLE_LOOP) - print "Trying to play", mus.lower() - else: - for bucket in buckets: - if not bucket: continue - print "music stream:", bucket+'base/sounds/music/' + mus.lower() - self.music = audio.loadURLhandle(bucket+'base/sounds/music/' + mus.lower(), 0, BASS_STREAM_BLOCK) - if self.music: break - - if self.music: - audio.sethandleattr(self.music, BASS_ATTRIB_VOL, self.musicslider.value() / 100.0) - audio.playhandle(self.music, True) - else: - print "Couldn't play music. Error", audio.getbasserror() - - def stopMusic(self): - if self.music: - if audio.handleisactive(self.music): - audio.stophandle(self.music) - audio.freehandle(self.music) - - def startGame(self, tcp, charlist, musiclist, background, evidence, areas, features=[], oocjoin=[], hplist=[], webAO_bucket=""): - self.willDisconnect = False - self.mychar = -1 - self.mychatcolor = 0 - self.tcp = tcp - self.charlist = charlist - self.musiclist = musiclist - self.evidence = evidence - self.areas = areas - self.features = features - - if "base/" in webAO_bucket: - webAO_bucket = webAO_bucket.replace("base/", "") - buckets[0] = webAO_bucket - - self.charselect.setCharList(charlist) - self.charselect.show() - - self.oocnameinput.setText(ini.read_ini("AO2XP.ini", "General", "OOC name")) - self.shownameedit.setText(ini.read_ini("AO2XP.ini", "General", "Showname")) - - self.pairdropdown.clear() - self.paircheckbox.setChecked(False) - if "cccc_ic_support" in features: - self.shownameedit.show() - self.nointerruptbtn.show() - self.paircheckbox.setDisabled(False) - self.paircheckbox.setText("Enable pairing") - for char in charlist: - self.pairdropdown.addItem(char[0]) - else: - self.shownameedit.hide() - self.nointerruptbtn.hide() - self.paircheckbox.setDisabled(True) - self.paircheckbox.setText("This server does not support pairing.") - - self.deskbtn.setDisabled("deskmod" not in features) - self.flipbutton.setVisible("flipping" in features) - self.customobject.setVisible("customobjections" in features) - self.ypairoffset.setVisible("y_offset" in features) - self.ypairoffset_l.setVisible("y_offset" in features) - - self.colordropdown.clear() - self.colordropdown.addItems(['white', 'green', 'red', 'orange', 'blue']) - if "yellowtext" in features: - self.colordropdown.addItems(['yellow', 'gay pride', 'pink', 'cyan']) - self.colordropdown.setCurrentIndex(self.mychatcolor) - - for hp in hplist: - self.healthbars.emit(hp[0], hp[1]) - - for char in self.charlist: - if not exists(AOpath + 'characters/' + char[0] + '/char.ini'): - continue - char[2] = get_char_ini(char[0], "options", "gender", "male") - - self.realizationbtn.setPressed(False) - self.customobject.setPressed(False) - self.mutedlist.clear() - self.unmutedlist.clear() - for char in self.charlist: - self.unmutedlist.addItem(char[0]) - - self.musicitems.clear() - self.evidencedropdown.clear() - for evi in evidence: - self.evidencedropdown.addItem(evi[0]) - - logstart = '--- Log started on ' + time.ctime() + ' ---' - if self.ooclog.toPlainText(): - self.ooclog.append("\n"+logstart) - else: - self.ooclog.append(logstart) - if self.icLog.toPlainText(): - self.icLog.append("\n"+logstart) - else: - self.icLog.append(logstart) - - self.setBackground(background) - for msg in oocjoin: - self.ooclog.append(msg) - - for song in musiclist: - songitem = QtGui.QListWidgetItem() - songitem.setText(song) - if exists(AOpath + 'sounds/music/' + song): - songitem.setBackgroundColor(QtGui.QColor(128, 255, 128)) - else: - songitem.setBackgroundColor(QtGui.QColor(255, 128, 128)) - self.musicitems.addItem(songitem) - - self.musicslider.setValue(ini.read_ini_int("AO2XP.ini", "Audio", "Music volume", 100)) - self.soundslider.setValue(ini.read_ini_int("AO2XP.ini", "Audio", "Sound volume", 100)) - self.blipslider.setValue(ini.read_ini_int("AO2XP.ini", "Audio", "Blip volume", 100)) - - #thread.start_new_thread(self.tcp_thread, ()) - self.tcpthread = TCP_Thread(self) - self.tcpthread.MS_Chat.connect(self.netmsg_ms) - self.tcpthread.newChar.connect(self.onPVPacket) - 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.showCharSelect.connect(self.charselect.show) - self.tcpthread.allEvidence.connect(self.allEvidence) - self.tcpthread.rainbowColor.connect(self.text.setStyleSheet) - self.tcpthread.start() - - def allEvidence(self, evi): - self.evidence = evi - if self.evidencedropdown.count() > 0: - self.evidencedropdown.clear() - for evi in self.evidence: - while len(evi) < 3: # new AO 2.9 bug where they never correctly escaped evidence name/desc/image on FantaProtocol - evi += [""] - evi[0] = decode_ao_str(evi[0].decode('utf-8')) - evi[1] = decode_ao_str(evi[1].decode('utf-8')) - evi[2] = decode_ao_str(evi[2].decode('utf-8')) - self.evidencedropdown.addItem(evi[0]) - - if not self.evidence: - self.evidencedropdown.setCurrentIndex(0) - self.evidencedesc.setText('.') - else: - self.evidencedropdown.setCurrentIndex(self.selectedevi) - -class PresentButton(QtGui.QLabel): - - def __init__(self, gamegui, parent): - super(PresentButton, self).__init__(parent) - self.gamegui = gamegui - self.button_off = QtGui.QPixmap(AO2XPpath + 'themes/default/present_disabled.png') - self.button_on = QtGui.QPixmap(AO2XPpath + 'themes/default/present.png') - self.setPixmap(self.button_off) - self.show() - - def mousePressEvent(self, event): - self.gamegui.present = not self.gamegui.present - if self.gamegui.present: - self.setPixmap(self.button_on) - else: - self.setPixmap(self.button_off) - - -class EditEvidenceDialog(QtGui.QDialog): - def __init__(self, gamegui): - super(EditEvidenceDialog, self).__init__() - self.gamegui = gamegui - self.setWindowTitle('Add evidence') - self.resize(512, 384) - self.setModal(True) - - self.eviname = QtGui.QLineEdit(self) - self.eviname.setGeometry(8, 8, 384, 24) - self.evidesc = QtGui.QTextEdit(self) - self.evidesc.setGeometry(8, 192, 496, 160) - self.evipicture = QtGui.QLabel(self) - self.filename = 'empty.png' - evipic = QtGui.QPixmap(AOpath + 'evidence/empty.png') - self.evipicture.setPixmap(evipic) - self.evipicture.move(434, 8) - self.evipicture.show() - self.save = QtGui.QPushButton(self) - self.save.setText('Save') - self.save.clicked.connect(self.onSave) - self.save.move(256*2 - self.save.size().width() - 8, 384 - self.save.size().height()) - self.cancel = QtGui.QPushButton(self) - self.cancel.setText('Cancel') - self.cancel.clicked.connect(self.onCancel) - self.cancel.move(264, 384 - self.cancel.size().height()) - self.choosepic = QtGui.QComboBox(self) - self.filenames = [] - self.choosepic.setGeometry(376, 78, 128, 24) - files = os.listdir(AOpath + 'evidence') - fileslength = len(files) - i = 0 - while i < fileslength: - if not files[i].endswith('.png'): - del files[i] - fileslength = len(files) - i -= 1 - i += 1 - - for i in range(len(files)): - if files[i].endswith('.png'): - self.choosepic.addItem(files[i].strip('.png')) - self.filenames.append(files[i]) - if files[i].lower() == 'empty.png': - self.emptyfile = i - - self.editing = False - self.choosepic.currentIndexChanged.connect(self.choosePicChange) - self.choosepic.setCurrentIndex(i) - - def choosePicChange(self, ind): - self.filename = self.filenames[ind] - if exists(AOpath + 'evidence/' + self.filename): - self.evipicture.setPixmap(QtGui.QPixmap(AOpath + 'evidence/' + self.filename)) - else: - self.evipicture.setPixmap(QtGui.QPixmap(AO2XPpath + 'themes/default/evidence_selected.png')) - - def onSave(self): - name = encode_ao_str(self.eviname.text().toUtf8()) - desc = encode_ao_str(self.evidesc.toPlainText().toUtf8()) - - if self.editing: - self.gamegui.tcp.send('EE#' + str(self.edit_ind) + '#' + name + '#' + desc + '#' + self.filename + '#%') - else: - self.gamegui.tcp.send('PE#' + name + '#' + desc + '#' + self.filename + '#%') - - self.eviname.setText('') - self.evidesc.setText('') - evipic = QtGui.QPixmap(AOpath + 'evidence/empty.png') - self.evipicture.setPixmap(evipic) - self.filename = 'empty.png' - self.editing = False - self.setWindowTitle('Add evidence') - self.choosepic.setCurrentIndex(self.emptyfile) - self.hide() - - def onCancel(self): - self.eviname.setText('') - self.evidesc.setText('') - evipic = QtGui.QPixmap(AOpath + 'evidence/empty.png') - self.evipicture.setPixmap(evipic) - self.filename = 'empty.png' - self.editing = False - self.setWindowTitle('Add evidence') - self.choosepic.setCurrentIndex(self.emptyfile) - self.hide() - - def EditEvidence(self, ind): - self.editing = True - self.edit_ind = ind - if self.gamegui.evidence[ind][2] not in self.filenames: - self.filenames.append(self.gamegui.evidence[ind][2]) - self.choosepic.addItem(self.gamegui.evidence[ind][2].split('.')[0]) - self.choosepic.setCurrentIndex(self.filenames.index(self.gamegui.evidence[ind][2])) - self.eviname.setText(self.gamegui.evidence[ind][0]) - self.evidesc.setText(self.gamegui.evidence[ind][1]) - self.setWindowTitle('Edit evidence') - self.show() - - -class EmoteButton(QtGui.QLabel): - - def __init__(self, gamewindow, x, y, id): - super(EmoteButton, self).__init__(gamewindow) - self.gamewindow = gamewindow - self.resize(40, 40) - self.move(x, y) - self.emoteid = id - - def paintEvent(self, event): - if self.gamewindow.mychar == -1: - return - - painter = QtGui.QPainter(self) - painter.setRenderHint(QtGui.QPainter.TextAntialiasing, False) - painter.setPen(QtGui.QColor(255, 255, 255)) - font = QtGui.QFont("Tahoma", 8) - font.setStyle(QtGui.QFont.StyleNormal) - font.setWeight(QtGui.QFont.Normal) - painter.setFont(font) - - if self.pixmap(): - if self.pixmap().isNull(): - painter.fillRect(0, 0, 39, 39, QtGui.QColor(0, 0, 0)) - painter.drawText(0, 0, str(self.emoteid)) - else: - painter.drawPixmap(0, 0, self.pixmap()) - else: - painter.fillRect(0, 0, 39, 39, QtGui.QColor(0, 0, 0)) - painter.drawText(1, 1, str(self.emoteid)) - - def mousePressEvent(self, event): - self.gamewindow.changeEmote(False, self.emoteid) - - -class BackEmoteButton(QtGui.QLabel): - - def __init__(self, gamewindow, x, y): - super(BackEmoteButton, self).__init__(gamewindow) - self.gamewindow = gamewindow - self.move(x, y) - self.setPixmap(QtGui.QPixmap(AO2XPpath + 'themes/default/arrow_left.png')) - self.show() - - def mousePressEvent(self, event): - self.gamewindow.current_emote_page -= 1 - self.gamewindow.set_emote_page() - - -class NextEmoteButton(QtGui.QLabel): - - def __init__(self, gamewindow, x, y): - super(NextEmoteButton, self).__init__(gamewindow) - self.gamewindow = gamewindow - self.move(x, y) - self.setPixmap(QtGui.QPixmap(AO2XPpath + 'themes/default/arrow_right.png')) - self.show() - - def mousePressEvent(self, event): - self.gamewindow.current_emote_page += 1 - self.gamewindow.set_emote_page() - - -class TCP_Thread(QtCore.QThread): - connectionError = QtCore.pyqtSignal(str, str, str) - MS_Chat = QtCore.pyqtSignal(list) - newChar = QtCore.pyqtSignal(str) - newBackground = QtCore.pyqtSignal(str) - IC_Log = QtCore.pyqtSignal(str) - OOC_Log = QtCore.pyqtSignal(str) - charSlots = QtCore.pyqtSignal() - showCharSelect = QtCore.pyqtSignal() - allEvidence = QtCore.pyqtSignal(list) - rainbowColor = QtCore.pyqtSignal(str) - - def __init__(self, parent): - super(TCP_Thread, self).__init__(parent) - self.parent = parent - - def run(self): - pingtimer = 150 - rainbow = 0 - sendtick = 0 - tempdata = "" - color = QtGui.QColor() - color.setHsv(rainbow, 255, 255) - while True: - if self.parent.disconnectnow: - self.parent.stopMusic() - self.parent.tcp.close() - self.quit() - return - pingtimer -= 1 - if pingtimer == 0: - pingbefore = time.time() - self.parent.tcp.send('CH#%') - pingtimer = 150 - - if self.parent.m_chatmessage[TEXT_COLOR] == "6": - color.setHsv(rainbow, 255, 255) - rainbow += 5 - if rainbow > 255: - rainbow = 0 - #self.parent.text.setStyleSheet('color: rgb(' + str(color.red()) + ', ' + str(color.green()) + ', ' + str(color.blue()) + ')') - self.rainbowColor.emit('color: rgb(' + str(color.red()) + ', ' + str(color.green()) + ', ' + str(color.blue()) + ')') - - if sendtick: - sendtick -= 1 - if self.parent.msgqueue and not sendtick: - self.parent.tcp.send(self.parent.msgqueue[0]) - sendtick = 4 - - error, total = self.parent.tcp.recv() - if error == -2: - continue - elif error == -1: - self.parent.emit(QtCore.SIGNAL('showMessage(QString, QString, QString)'), 'critical', 'Connection lost', "%s connection to server lost." % ("WebSocket" if self.parent.tcp.isWS else "TCP")) - self.parent.willDisconnect = True - self.quit() - return - - for network in total: - header = network[0] - #del network[-1] - if header == 'MS': - if len(network) < 15: - print '[warning]', 'malformed/incomplete MS#chat (IC chat) network message was received' - continue - - network[CHATMSG] = decode_ao_str(network[CHATMSG].decode('utf-8')) - self.MS_Chat.emit(network) - - elif header == 'MC': - music = decode_ao_str(network[1]) - charid = int(network[2]) - t = time.localtime() - if charid != -1: - try: - name = self.parent.charlist[charid][0] - except: - name = 'char id %d' % charid - - if len(network) > 3 and network[3]: - name += " ("+network[3].decode("utf-8")+")" - #self.parent.icLog.append('[%d:%.2d] %s changed the music to %s' % (t[3], t[4], name, music)) - self.IC_Log.emit('[%d:%.2d] %s changed the music to %s' % (t[3], t[4], name, music)) - else: - self.IC_Log.emit('[%d:%.2d] the music was changed to %s' % (t[3], t[4], music)) - self.parent.playMusic(music) - - elif header == 'BN': - self.newBackground.emit(network[1]) - - elif header == 'CT': - name = decode_ao_str(network[1].decode('utf-8')) - chatmsg = decode_ao_str(network[2].decode('utf-8').replace("\n", "
")) - #self.parent.ooclog.append('%s: %s' % (name, chatmsg)) - self.OOC_Log.emit("%s: %s" % (name, chatmsg)) - - elif header == 'PV': - self.parent.mychar = int(network[3]) - self.parent.charselect.hide() - self.newChar.emit(self.parent.charlist[self.parent.mychar][0]) - - elif header == 'LE': - del network[0] - self.allEvidence.emit([evi.split('&') for evi in network]) - - elif header == 'ZZ': - if self.parent.modcall: - audio.freehandle(self.parent.modcall) - self.parent.modcall = audio.loadhandle(0, "mod_call.wav", 0, 0, 0) - audio.sethandleattr(self.parent.modcall, BASS_ATTRIB_VOL, self.parent.soundslider.value() / 100.0) - audio.playhandle(self.parent.modcall, False) - - if len(network) > 1: - self.OOC_Log.emit('[MOD CALL] ' + network[1].replace("\n", "
") + '
') - else: - self.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)): - self.parent.charlist[i][1] = int(network[i]) - - self.charSlots.emit() - - elif header == 'RT': - testimony = network[1] - wtcefile = AOpath+"sounds/general/sfx-testimony2" - if self.parent.wtcesfx: - audio.freehandle(self.parent.wtcesfx) - - if testimony == 'judgeruling': - variant = int(network[2]) - if variant == 0: - wtcefile = AOpath+"sounds/general/sfx-notguilty" - elif variant == 1: - wtcefile = AOpath+"sounds/general/sfx-guilty" - else: - variant = 0 - 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.soundslider.value() / 100.0) - audio.playhandle(self.parent.wtcesfx, True) - self.parent.WTCEsignal.emit(testimony, variant) - - elif header == 'HP': - type = int(network[1]) - health = int(network[2]) - self.parent.healthbars.emit(type, health) - - elif header == 'KK': - reason = network[1] - self.parent.emit(QtCore.SIGNAL('showMessage(QString, QString, QString)'), 'critical', 'Connection lost', 'You were kicked off the server. (%s)' % reason) - - elif header == 'KB': - reason = network[1] - self.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) - message = network[1] - self.parent.emit(QtCore.SIGNAL('showMessage(QString, QString, QString)'), 'warning', '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"] - self.OOC_Log.emit("%s" % (statusStrings[status+1])) - - elif header == "CHECK": #ping - pingafter = time.time() - self.parent.gotPing.emit(int((pingafter - pingbefore)*1000)) - - elif header == 'DONE': - self.showCharSelect.emit()