4327 lines
181 KiB
Python
4327 lines
181 KiB
Python
import thread, time, os, urllib, random, re , platform
|
|
import charselect, ini, AOsocket, images, packets, demo, buttons
|
|
|
|
from os.path import exists, basename
|
|
from ConfigParserEdit import ConfigParser
|
|
from constants import *
|
|
from collections import OrderedDict
|
|
from pybass_constants import *
|
|
from PyQt4 import QtGui, QtCore
|
|
from functools import partial
|
|
from ctypes import create_string_buffer
|
|
from urllib2 import Request, urlopen
|
|
|
|
DOWNLOAD_BLACKLIST = []
|
|
|
|
URL_REGEX = r"https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)"
|
|
|
|
bucket = ""
|
|
|
|
def delay(msec):
|
|
dieTime = QtCore.QTime.currentTime().addMSecs(msec)
|
|
|
|
while QtCore.QTime.currentTime() < dieTime:
|
|
QtCore.QCoreApplication.processEvents(QtCore.QEventLoop.AllEvents, 100)
|
|
|
|
def getCharIni(char, section, value, default=""):
|
|
tempini = ConfigParser()
|
|
return ini.read_ini(AOpath + 'characters/' + char.lower() + '/char.ini', section, value, default)
|
|
|
|
def getOption(section, value, default=""):
|
|
tempini = ConfigParser()
|
|
tempini.read("AO2XP.ini")
|
|
return ini.read_ini(tempini, section, value, default)
|
|
|
|
def getImageSuffix(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 getTextColor(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:
|
|
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)
|
|
|
|
def testPath(*args):
|
|
for path in args:
|
|
if exists(path):
|
|
return path
|
|
return False
|
|
|
|
def downloadThread(link, savepath):
|
|
global DOWNLOAD_BLACKLIST
|
|
if link in DOWNLOAD_BLACKLIST:
|
|
return
|
|
|
|
print "[client] Download missing: %s" % link
|
|
fp = urllib.urlopen(bucket + link)
|
|
if fp.getcode() == 200:
|
|
print savepath[:-1]
|
|
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 mockString(text):
|
|
upper = random.choice([True, False])
|
|
if isinstance(text, QtCore.QString):
|
|
l = QtCore.QStringList(list(text))
|
|
for i in range(len(text)):
|
|
if text[i] == " ":
|
|
continue
|
|
|
|
l[i] = l[i].toUpper() if upper else l[i].toLower()
|
|
upper = not upper
|
|
return l.join("")
|
|
else:
|
|
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 MusicDownloadThread(QtCore.QThread):
|
|
# Part of the evil HTTPS music download hack for XP systems
|
|
finished_signal = QtCore.pyqtSignal(int)
|
|
|
|
def __init__(self, caller, url):
|
|
super(MusicDownloadThread, self).__init__()
|
|
self.caller = caller
|
|
self.url = url
|
|
self.exiting = False
|
|
|
|
def run(self):
|
|
self.exiting = False
|
|
self.download(self.url)
|
|
|
|
def download(self, url):
|
|
if self.exiting:
|
|
return
|
|
|
|
headers = {
|
|
'User-Agent': "AO2XP %s" % (GAME_VERSION),
|
|
'Accept': '*/*',
|
|
'Accept-Encoding': 'gzip, deflate',
|
|
'Accept-Language': 'en-US,en;q=0.9',
|
|
'Connection': 'keep-alive',
|
|
'Upgrade-Insecure-Requests': '1'
|
|
}
|
|
|
|
request = Request(url, headers=headers)
|
|
request.get_method = lambda: 'HEAD'
|
|
try:
|
|
response = urlopen(request, timeout=5)
|
|
except Exception as e:
|
|
print "[audio] There's no response, aborting. Details below:"
|
|
print "[audio]", e, url
|
|
self.quit()
|
|
return
|
|
|
|
file_length = int(response.headers.get('Content-Length', 0))
|
|
|
|
if file_length > 0:
|
|
request = Request(url, headers=headers)
|
|
response = urlopen(request)
|
|
stream = ""
|
|
|
|
bytes_downloaded = 0
|
|
buffer_size = 8192
|
|
|
|
while bytes_downloaded < file_length:
|
|
if self.exiting:
|
|
self.quit()
|
|
break
|
|
|
|
chunk = response.read(buffer_size)
|
|
if not chunk:
|
|
break
|
|
|
|
stream += chunk
|
|
bytes_downloaded += len(chunk)
|
|
|
|
if not self.exiting:
|
|
self.caller.stream = create_string_buffer(stream)
|
|
self.finished_signal.emit(file_length)
|
|
else:
|
|
print "[audio] There's no response, aborting..."
|
|
self.quit()
|
|
return
|
|
|
|
def stop(self):
|
|
self.exiting = True
|
|
|
|
class ChatLogs(QtGui.QTextEdit):
|
|
def __init__(self, parent, logtype, logfile=None):
|
|
QtGui.QTextEdit.__init__(self, parent)
|
|
self.type = logtype
|
|
#self.setMouseTracking(True)
|
|
self.logfile = None
|
|
self.anchor = None
|
|
self.savelog = False
|
|
self.combinelog = False
|
|
self.setLogFiles(logfile)
|
|
|
|
def setLogFiles(self, logfile=None):
|
|
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("logs"):
|
|
os.mkdir("logs")
|
|
|
|
if not self.logfile:
|
|
currtime = time.localtime()
|
|
if self.combinelog:
|
|
if self.type == 0:
|
|
self.logfile = logfile
|
|
else:
|
|
self.logfile = "logs/%d-%02d-%02d %02d.%02d.%02d.log" % (currtime[0], currtime[1], currtime[2], currtime[3], currtime[4], currtime[5])
|
|
else:
|
|
if self.type == 0:
|
|
self.logfile = "logs/%d-%02d-%02d %02d.%02d.%02d [IC].log" % (currtime[0], currtime[1], currtime[2], currtime[3], currtime[4], currtime[5])
|
|
else:
|
|
self.logfile = "logs/%d-%02d-%02d %02d.%02d.%02d [OOC].log" % (currtime[0], currtime[1], currtime[2], currtime[3], currtime[4], currtime[5])
|
|
else:
|
|
self.logfile = None
|
|
|
|
# def mouseMoveEvent(self, e):
|
|
# super(ChatLogs, self).mouseMoveEvent(e)
|
|
# self.anchor = self.anchorAt(e.pos())
|
|
# if self.anchor:
|
|
# QtGui.QApplication.setOverrideCursor(QtCore.Qt.PointingHandCursor)
|
|
# else:
|
|
# QtGui.QApplication.setOverrideCursor(QtCore.Qt.ArrowCursor)
|
|
|
|
# def mouseReleaseEvent(self, e):
|
|
# if self.anchor:
|
|
# QtGui.QDesktopServices.openUrl(QtCore.QUrl(self.anchor))
|
|
# self.anchor = None
|
|
# QtGui.QApplication.setOverrideCursor(QtCore.Qt.ArrowCursor)
|
|
|
|
def __del__(self):
|
|
if self.savelog:
|
|
self.logfile.close()
|
|
|
|
def append(self, text):
|
|
if self.savelog and not "Log started" in text:
|
|
with open(self.logfile, "a") as logfile:
|
|
if isinstance(text, str) or isinstance(text, unicode):
|
|
text_ = text.encode("utf-8")
|
|
if self.combinelog:
|
|
if self.type == 0:
|
|
logfile.write("[IC] " + text_.replace("<b>", "").replace("</b>", "") +"\n")
|
|
else:
|
|
logfile.write("[OOC] " + text_.replace("<b>", "").replace("</b>", "") +"\n")
|
|
else:
|
|
logfile.write(text_.replace("<b>", "").replace("</b>", "") + "\n")
|
|
else:
|
|
text_ = text.toUtf8()
|
|
if self.combinelog:
|
|
if self.type == 0:
|
|
logfile.write("[IC] " + text_.replace("<b>", "").replace("</b>", "") +"\n")
|
|
else:
|
|
logfile.write("[OOC] " + text_.replace("<b>", "").replace("</b>", "") +"\n")
|
|
else:
|
|
logfile.write(text_.replace("<b>", "").replace("</b>", "") +"\n")
|
|
|
|
# if "http" in text:
|
|
# text = unicode(text) # Get rid of QStrings
|
|
# text = re.sub(URL_REGEX, r'<a href="\g<0>">\g<0></a>', text)
|
|
|
|
super(ChatLogs, self).append(text)
|
|
|
|
class AOCharMovie(QtGui.QLabel):
|
|
done = QtCore.pyqtSignal()
|
|
usePillow = 0
|
|
pillowFrames = []
|
|
pillowFrame = 0
|
|
pillowSpeed = 0
|
|
xx = 0 # for restoring from screenshake
|
|
yy = 0 # for restoring from screenshake
|
|
|
|
def __init__(self, parent):
|
|
QtGui.QLabel.__init__(self, parent)
|
|
|
|
self.parent = parent
|
|
self.setAlignment(QtCore.Qt.AlignCenter)
|
|
self.timeMod = 60
|
|
self.playOnce = True
|
|
self.mFlipped = False
|
|
self.scaling = SCALING_AUTO
|
|
self.showOnPlay = True
|
|
|
|
self.mMovie = QtGui.QMovie()
|
|
|
|
self.preanimTimer = QtCore.QTimer(self)
|
|
self.preanimTimer.setSingleShot(True)
|
|
self.pillowTimer = QtCore.QTimer(self)
|
|
self.pillowTimer.setSingleShot(True)
|
|
|
|
self.preanimTimer.timeout.connect(self.timer_done)
|
|
self.pillowTimer.timeout.connect(self.pillowFrameChange)
|
|
self.mMovie.frameChanged.connect(self.frameChange)
|
|
|
|
self.prevGifPath = ""
|
|
|
|
def resize(self):
|
|
super(AOCharMovie, self).resize(self.parent.size())
|
|
|
|
def move(self, x, y, screenShaking=False):
|
|
if not screenShaking:
|
|
self.xx = x
|
|
self.yy = y
|
|
super(AOCharMovie, self).move(x, y)
|
|
|
|
def moveSlide(self, x):
|
|
super(AOCharMovie, self).move(x, self.y())
|
|
|
|
def setFlipped(self, flip):
|
|
self.mFlipped = flip
|
|
|
|
def play(self, pChar, pEmote, emotePrefix, scaling = SCALING_AUTO, singleFrameDuration = -1):
|
|
if not len(pEmote):
|
|
return
|
|
|
|
if pEmote[0] == "/" or pEmote[0] == "/":
|
|
pEmote = pEmote[1:]
|
|
elif "../../characters" in pEmote:
|
|
a = pEmote.split("/")
|
|
pChar = a[3]
|
|
emote = a[4]
|
|
emotePrefix = ""
|
|
pEmote = emote
|
|
|
|
self.pillowFrames = []
|
|
self.pillowFrame = 0
|
|
|
|
self.scaling = scaling
|
|
|
|
pChar = pChar.lower()
|
|
pEmote = pEmote.lower()
|
|
|
|
apngPath = testPath(
|
|
AOpath + "characters/" + pChar + "/" + emotePrefix + pEmote + ".apng",
|
|
AOpath + "characters/" + pChar + "/" + emotePrefix + "/" + pEmote + ".apng",
|
|
AOpath + "characters/" + pChar + "/" + pEmote + ".apng",
|
|
AOpath + "characters/" + pChar + "/(a)" + pEmote + ".apng",
|
|
AOpath + "characters/" + pChar + "/(b)" + pEmote + ".apng",
|
|
AOpath + "characters/" + pChar + "/(a)/" + pEmote + ".apng",
|
|
AOpath + "characters/" + pChar + "/(b)/" + pEmote + ".apng"
|
|
)
|
|
|
|
placeholderPath = AO2XPpath + "themes/default/oldplaceholder.gif"
|
|
|
|
imgPath = ""
|
|
downloadCharacters = ini.read_ini_bool("AO2XP.ini", "General", "download characters")
|
|
|
|
if apngPath:
|
|
imgPath = apngPath
|
|
self.usePillow = 1
|
|
else:
|
|
if downloadCharacters:
|
|
url = "base/characters/" + pChar.lower() + "/" + emotePrefix + pEmote.lower() + ".apng"
|
|
url = url.replace(" ", "%20")
|
|
thread.start_new_thread(downloadThread, (url, apngPath))
|
|
|
|
pngPath = testPath(
|
|
AOpath + "characters/" + pChar + "/" + emotePrefix + pEmote + ".png",
|
|
AOpath + "characters/" + pChar + "/" + emotePrefix + "/" + pEmote + ".png",
|
|
AOpath + "characters/" + pChar + "/" + pEmote + ".png",
|
|
AOpath + "characters/" + pChar + "/(a)" + pEmote + ".png",
|
|
AOpath + "characters/" + pChar + "/(b)" + pEmote + ".png",
|
|
AOpath + "characters/" + pChar + "/(a)/" + pEmote + ".png",
|
|
AOpath + "characters/" + pChar + "/(b)/" + pEmote + ".png"
|
|
)
|
|
|
|
if pngPath:
|
|
imgPath = pngPath
|
|
self.usePillow = 0
|
|
else:
|
|
if downloadCharacters:
|
|
url = "base/characters/" + pChar.lower() + "/" + emotePrefix + pEmote.lower() + ".png"
|
|
url = url.replace(" ", "%20")
|
|
thread.start_new_thread(downloadThread, (url, pngPath))
|
|
|
|
webpPath = testPath(
|
|
AOpath + "characters/" + pChar + "/" + emotePrefix + pEmote + ".webp",
|
|
AOpath + "characters/" + pChar + "/" + emotePrefix + "/" + pEmote + ".webp",
|
|
AOpath + "characters/" + pChar + "/" + pEmote + ".webp",
|
|
AOpath + "characters/" + pChar + "/(a)" + pEmote + ".webp",
|
|
AOpath + "characters/" + pChar + "/(b)" + pEmote + ".webp",
|
|
AOpath + "characters/" + pChar + "/(a)/" + pEmote + ".webp",
|
|
AOpath + "characters/" + pChar + "/(b)/" + pEmote + ".webp"
|
|
)
|
|
|
|
if webpPath:
|
|
imgPath = webpPath
|
|
self.usePillow = 2
|
|
else:
|
|
if downloadCharacters:
|
|
url = "base/characters/" + pChar.lower() + "/"+pEmote.lower() + ".webp"
|
|
url = url.replace(" ", "%20")
|
|
thread.start_new_thread(downloadThread, (url, webpPath))
|
|
|
|
gifPath = testPath(
|
|
AOpath + "characters/" + pChar + "/" + emotePrefix + pEmote + ".gif",
|
|
AOpath + "characters/" + pChar + "/" + emotePrefix + "/" + pEmote + ".gif",
|
|
AOpath + "characters/" + pChar + "/" + pEmote + ".gif",
|
|
AOpath + "characters/" + pChar + "/(a)" + pEmote + ".gif",
|
|
AOpath + "characters/" + pChar + "/(b)" + pEmote + ".gif",
|
|
AOpath + "characters/" + pChar + "/(a)/" + pEmote + ".gif",
|
|
AOpath + "characters/" + pChar + "/(b)/" + pEmote + ".gif"
|
|
)
|
|
|
|
if gifPath:
|
|
imgPath = gifPath
|
|
self.usePillow = 0
|
|
else:
|
|
if downloadCharacters:
|
|
url = "base/characters/" + pChar.lower() + "/" + emotePrefix + pEmote.lower() + ".gif"
|
|
url = url.replace(" ", "%20")
|
|
thread.start_new_thread(downloadThread, (url, gifPath))
|
|
|
|
if exists(placeholderPath):
|
|
imgPath = placeholderPath
|
|
print "[debug] Sprite not found: ", pChar, pEmote, emotePrefix
|
|
else:
|
|
imgPath = ""
|
|
self.usePillow = 0
|
|
|
|
if imgPath == "":
|
|
imgPath = self.prevGifPath
|
|
# Second check just in case
|
|
if imgPath == "":
|
|
if exists(placeholderPath):
|
|
imgPath = placeholderPath
|
|
print "[debug] Sprite not found: ", pChar, pEmote, emotePrefix
|
|
else:
|
|
imgPath = "placeholder.png"
|
|
else:
|
|
self.prevGifPath = imgPath
|
|
|
|
#print "[debug]", pChar, emotePrefix, pEmote, "(!) path is null!" if imgPath == "" else imgPath
|
|
|
|
if not self.usePillow:
|
|
self.mMovie.setFileName(imgPath)
|
|
self.mMovie.start()
|
|
elif self.usePillow == 1: # apng
|
|
self.pillowFrames = images.load_apng(apngPath)
|
|
if len(self.pillowFrames) > 1:
|
|
self.pillowTimer.start(int(self.pillowFrames[0][1] * self.pillowSpeed))
|
|
else:
|
|
self.pillowTimer.start(int(singleFrameDuration * self.pillowSpeed))
|
|
|
|
self.setPillowFrame()
|
|
elif self.usePillow == 2: # webp
|
|
try:
|
|
self.pillowFrames = images.load_webp(webpPath)
|
|
if len(self.pillowFrames) > 1:
|
|
self.pillowTimer.start(int(self.pillowFrames[0][1] * self.pillowSpeed))
|
|
else:
|
|
self.pillowTimer.start(int(singleFrameDuration * self.pillowSpeed))
|
|
|
|
self.setPillowFrame()
|
|
except:
|
|
if exists(placeholderPath):
|
|
imgPath = placeholderPath
|
|
print "[debug] Couldn't load webp sprite!"
|
|
else:
|
|
imgPath = "placeholder.png"
|
|
self.mMovie.setFileName(imgPath)
|
|
self.mMovie.start()
|
|
|
|
if self.showOnPlay:
|
|
self.show()
|
|
|
|
def playPre(self, pChar, pEmote, duration, scaling = SCALING_AUTO):
|
|
pChar = pChar.lower()
|
|
|
|
gifPath = AOpath + "characters/" + pChar + "/" + pEmote + ".gif"
|
|
apngPath = AOpath + "characters/" + pChar + "/" + pEmote + ".apng"
|
|
webpPath = AOpath + "characters/" + pChar + "/" + pEmote + ".webp"
|
|
|
|
fullDuration = duration * self.timeMod
|
|
realDuration = 0
|
|
|
|
self.playOnce = False
|
|
self.mMovie.stop()
|
|
self.clear()
|
|
|
|
if exists(apngPath):
|
|
realDuration = images.get_apng_duration(apngPath)
|
|
elif exists(webpPath):
|
|
realDuration = images.get_webp_duration(webpPath)
|
|
elif exists(gifPath):
|
|
self.mMovie.setFileName(gifPath)
|
|
self.mMovie.jumpToFrame(0)
|
|
for nFrame in range(self.mMovie.frameCount()):
|
|
realDuration += self.mMovie.nextFrameDelay()
|
|
self.mMovie.jumpToFrame(nFrame + 1)
|
|
|
|
percentageModifier = 100.0
|
|
|
|
if realDuration != 0 and duration != 0:
|
|
modifier = fullDuration / float(realDuration)
|
|
percentageModifier = 100 / modifier
|
|
|
|
if percentageModifier > 100.0 or percentageModifier < 0.0:
|
|
percentageModifier = 100.0
|
|
|
|
self.pillow_fullduration = fullDuration
|
|
if fullDuration == 0 or fullDuration >= realDuration:
|
|
self.playOnce = True
|
|
else:
|
|
self.playOnce = False
|
|
if fullDuration >= 0:
|
|
self.preanimTimer.start(fullDuration)
|
|
|
|
self.mMovie.setSpeed(int(percentageModifier))
|
|
|
|
self.pillowSpeed = percentageModifier / 100.
|
|
if realDuration > 0:
|
|
self.play(pChar, pEmote, "", scaling)
|
|
else:
|
|
self.play(pChar, pEmote, "", scaling, fullDuration)
|
|
|
|
def playTalking(self, pChar, pEmote, scaling = SCALING_AUTO):
|
|
pChar = pChar.lower()
|
|
gifPath = AOpath + 'characters/' + pChar + '/(b)' + pEmote + '.gif'
|
|
|
|
self.mMovie.stop()
|
|
self.clear()
|
|
self.mMovie.setFileName(gifPath)
|
|
self.mMovie.jumpToFrame(0)
|
|
|
|
self.playOnce = False
|
|
self.mMovie.setSpeed(100)
|
|
self.pillowSpeed = 1
|
|
self.play(pChar, pEmote, '(b)', scaling)
|
|
|
|
def playIdle(self, pChar, pEmote, scaling = SCALING_AUTO):
|
|
pChar = pChar.lower()
|
|
gifPath = AOpath + 'characters/' + pChar + '/(a)' + pEmote + '.gif'
|
|
|
|
self.mMovie.stop()
|
|
self.clear()
|
|
self.mMovie.setFileName(gifPath)
|
|
self.mMovie.jumpToFrame(0)
|
|
|
|
self.playOnce = False
|
|
self.mMovie.setSpeed(100)
|
|
self.pillowSpeed = 1
|
|
self.play(pChar, pEmote, '(a)', scaling)
|
|
|
|
def stop(self):
|
|
self.mMovie.stop()
|
|
self.preanimTimer.stop()
|
|
self.hide()
|
|
|
|
def getTransform(self, smooth_condition=True):
|
|
if self.scaling == SCALING_PIXEL:
|
|
return QtCore.Qt.FastTransformation
|
|
elif self.scaling == SCALING_SMOOTH:
|
|
return QtCore.Qt.SmoothTransformation
|
|
elif smooth_condition:
|
|
return QtCore.Qt.SmoothTransformation
|
|
else:
|
|
return QtCore.Qt.FastTransformation
|
|
|
|
def getAspect(self, taller):
|
|
if taller:
|
|
return QtCore.Qt.KeepAspectRatio
|
|
else:
|
|
return QtCore.Qt.KeepAspectRatioByExpanding
|
|
|
|
def getScaledImage(self, f_img):
|
|
if not f_img.isNull():
|
|
transform = self.getTransform(f_img.size().height() > self.size().height())
|
|
aspect = self.getAspect(f_img.size().height() > f_img.size().width())
|
|
return f_img.scaled(self.size(), aspect, transform)
|
|
return f_img
|
|
|
|
@QtCore.pyqtSlot(int)
|
|
def frameChange(self, nFrame):
|
|
f_img = self.getScaledImage(self.mMovie.currentImage().mirrored(self.mFlipped, False))
|
|
|
|
fPixmap = QtGui.QPixmap.fromImage(f_img)
|
|
self.setPixmap(fPixmap)
|
|
|
|
if self.mMovie.frameCount() - 1 == nFrame and self.playOnce:
|
|
self.preanimTimer.start(self.mMovie.nextFrameDelay())
|
|
|
|
@QtCore.pyqtSlot()
|
|
def pillowFrameChange(self):
|
|
if not self.pillowFrames: return
|
|
|
|
if len(self.pillowFrames) - 1 == self.pillowFrame:
|
|
if self.playOnce:
|
|
self.preanimTimer.start(int(self.pillowFrames[self.pillowFrame][1] * self.pillowSpeed))
|
|
elif len(self.pillowFrames) > 1:
|
|
self.pillowFrame = 0
|
|
self.pillowTimer.start(int(self.pillowFrames[self.pillowFrame][1] * self.pillowSpeed))
|
|
else:
|
|
self.pillowFrame += 1
|
|
self.pillowTimer.start(int(self.pillowFrames[self.pillowFrame][1] * self.pillowSpeed))
|
|
|
|
self.setPillowFrame()
|
|
|
|
def setPillowFrame(self):
|
|
f_img = self.getScaledImage(self.pillowFrames[self.pillowFrame][0].mirrored(self.mFlipped, False))
|
|
|
|
fPixmap = QtGui.QPixmap.fromImage(f_img)
|
|
self.setPixmap(fPixmap)
|
|
|
|
@QtCore.pyqtSlot()
|
|
def timer_done(self):
|
|
self.done.emit()
|
|
|
|
class AOMovie(QtGui.QLabel):
|
|
playOnce = True
|
|
done = QtCore.pyqtSignal()
|
|
usePillow = 0
|
|
pillowFrames = []
|
|
pillowFrame = 0
|
|
pillowSpeed = 1
|
|
pillowLoops = 0
|
|
xx = 0 # for restoring from screenshake
|
|
yy = 0 # for restoring from screenshake
|
|
|
|
def __init__(self, parent):
|
|
QtGui.QLabel.__init__(self, parent)
|
|
self.parent = parent
|
|
self.mMovie = QtGui.QMovie()
|
|
self.setMovie(self.mMovie)
|
|
self.mMovie.frameChanged.connect(self.frameChange)
|
|
|
|
self.pillowTimer = QtCore.QTimer(self)
|
|
self.pillowTimer.setSingleShot(True)
|
|
self.pillowTimer.timeout.connect(self.pillowFrameChange)
|
|
|
|
self.pillowLabel = QtGui.QLabel(self)
|
|
self.pillowLabel.hide()
|
|
|
|
def resize(self):
|
|
super(AOMovie, self).resize(self.parent.size())
|
|
self.pillowLabel.setGeometry(0, 0, self.parent.size().width(), self.parent.size().height())
|
|
|
|
def move(self, x, y):
|
|
self.xx = x
|
|
self.yy = y
|
|
super(AOMovie, self).move(x, y)
|
|
|
|
def setPlayOnce(self, once):
|
|
self.playOnce = once
|
|
|
|
def play(self, pImage, pChar=""):
|
|
gifPath = pImage
|
|
pillowModes = {".gif": 0, ".apng": 1, ".webp": 2, ".png": 1}
|
|
|
|
pImage = unicode(pImage)
|
|
|
|
if not exists(gifPath):
|
|
pathlist = [
|
|
getImageSuffix(AO2XPpath + "themes/default/" + pImage + "_bubble"),
|
|
getImageSuffix(AOpath + "characters/" + pChar + "/" + pImage),
|
|
getImageSuffix(AOpath + "misc/default/" + pImage),
|
|
getImageSuffix(AO2XPpath + "themes/default/" + pImage)
|
|
]
|
|
|
|
for f in pathlist:
|
|
if exists(f):
|
|
gifPath = f
|
|
break
|
|
|
|
if not exists(gifPath):
|
|
self.done.emit()
|
|
return
|
|
|
|
self.usePillow = pillowModes[os.path.splitext(gifPath)[1]]
|
|
|
|
if not self.usePillow:
|
|
self.mMovie.setFileName(gifPath)
|
|
self.mMovie.start()
|
|
elif self.usePillow == 1: # apng
|
|
self.pillowLabel.show()
|
|
self.pillowFrames = images.load_apng(gifPath)
|
|
#if len(self.pillowFrames) > 1:
|
|
self.pillowTimer.start(int(self.pillowFrames[0][1] * self.pillowSpeed))
|
|
self.setPillowFrame()
|
|
elif self.usePillow == 2: # webp
|
|
self.pillowLabel.show()
|
|
self.pillowLoops = 0
|
|
self.pillowFrames, self.webpLoops = images.load_webp(gifPath)
|
|
#if len(self.pillowFrames) > 1:
|
|
self.pillowTimer.start(int(self.pillowFrames[0][1] * self.pillowSpeed))
|
|
self.setPillowFrame()
|
|
|
|
self.show()
|
|
|
|
def stop(self):
|
|
self.pillowFrames = []
|
|
self.pillowFrame = 0
|
|
self.pillowTimer.stop()
|
|
self.mMovie.stop()
|
|
self.pillowLabel.clear()
|
|
self.pillowLabel.hide()
|
|
self.hide()
|
|
|
|
@QtCore.pyqtSlot(int)
|
|
def frameChange(self, nFrame):
|
|
if nFrame == self.mMovie.frameCount() - 1 and self.playOnce:
|
|
delay(self.mMovie.nextFrameDelay())
|
|
self.stop()
|
|
self.done.emit()
|
|
|
|
@QtCore.pyqtSlot()
|
|
def pillowFrameChange(self):
|
|
if not self.pillowFrames: return
|
|
|
|
if len(self.pillowFrames) - 1 == self.pillowFrame:
|
|
if self.playOnce or (self.usePillow == 2 and self.pillowLoops+1 == self.webpLoops):
|
|
delay(int(self.pillowFrames[self.pillowFrame][1] * self.pillowSpeed))
|
|
self.stop()
|
|
self.done.emit()
|
|
elif len(self.pillowFrames) > 1: # loop
|
|
self.pillowLoops += 1
|
|
self.pillowFrame = 0
|
|
self.pillowTimer.start(int(self.pillowFrames[self.pillowFrame][1] * self.pillowSpeed))
|
|
elif len(self.pillowFrames) > 1:
|
|
self.pillowFrame += 1
|
|
self.pillowTimer.start(int(self.pillowFrames[self.pillowFrame][1] * self.pillowSpeed))
|
|
|
|
self.setPillowFrame()
|
|
|
|
def setPillowFrame(self):
|
|
if not self.pillowFrames: return
|
|
|
|
f_img = self.pillowFrames[self.pillowFrame][0]
|
|
if not f_img.isNull() and (f_img.size().width() != self.size().width() or f_img.size().height() != self.size().height()):
|
|
f_img = f_img.scaled(self.size().width(), self.size().height(), QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.FastTransformation)
|
|
|
|
fPixmap = QtGui.QPixmap.fromImage(f_img)
|
|
self.pillowLabel.setPixmap(fPixmap)
|
|
|
|
class ZoomLines(QtGui.QLabel):
|
|
def __init__(self, parent):
|
|
super(ZoomLines, self).__init__(parent)
|
|
self.parent = parent
|
|
self.setScaledContents(True)
|
|
self.movie = QtGui.QMovie()
|
|
self.movie.frameChanged.connect(self.frameChange)
|
|
|
|
def resize(self):
|
|
super(ZoomLines, self).resize(self.parent.size())
|
|
|
|
def frameChange(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 WTCEView(QtGui.QLabel):
|
|
def __init__(self, parent):
|
|
super(WTCEView, self).__init__(parent)
|
|
self.parent = parent
|
|
self.movie = QtGui.QMovie()
|
|
self.movie.frameChanged.connect(self.frameChange)
|
|
self.finalframeTimer = QtCore.QTimer()
|
|
self.finalframeTimer.setSingleShot(False)
|
|
self.finalframeTimer.timeout.connect(self.finished)
|
|
|
|
def resize(self):
|
|
super(WTCEView, self).move(self.parent.viewport.x(), self.parent.viewport.y())
|
|
super(WTCEView, self).resize(self.parent.viewport.size())
|
|
|
|
def frameChange(self, frame):
|
|
if self.movie.state() != QtGui.QMovie.Running:
|
|
return
|
|
img = self.movie.currentImage()
|
|
pixmap = QtGui.QPixmap.fromImage(img)
|
|
if not pixmap.isNull():
|
|
self.setPixmap(pixmap.scaled(self.size().width(), self.size().height(), QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.FastTransformation))
|
|
if self.movie.currentFrameNumber() == self.movie.frameCount() - 1:
|
|
self.finalframeTimer.start(self.movie.nextFrameDelay())
|
|
|
|
def finished(self):
|
|
self.finalframeTimer.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 EditEvidenceDialog(QtGui.QDialog):
|
|
def __init__(self, gamegui):
|
|
super(EditEvidenceDialog, self).__init__()
|
|
self.gamegui = gamegui
|
|
self.setTitle()
|
|
self.resize(512, 384)
|
|
self.setModal(True)
|
|
|
|
self.eviname = QtGui.QLineEdit(self)
|
|
self.eviname.setGeometry(8, 8, 410, 24)
|
|
self.evidesc = QtGui.QTextEdit(self)
|
|
self.evidesc.setGeometry(8, 192 - 105, 496, 255)
|
|
self.evidesc.setAcceptRichText(False)
|
|
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.onSaveClicked)
|
|
self.save.move(256 - self.save.size().width() - 8, 384 - self.save.size().height() - 2)
|
|
self.cancel = QtGui.QPushButton(self)
|
|
self.cancel.setText('Cancel')
|
|
self.cancel.clicked.connect(self.onCancelClicked)
|
|
self.cancel.move(264 + 16, 384 - self.cancel.size().height() - 2)
|
|
self.choosepic = QtGui.QComboBox(self)
|
|
self.choosepic.setGeometry(self.eviname.x() + self.eviname.size().width() - 128 - 84, self.eviname.y() + 70 - 32, 128, 24)
|
|
self.filenames = []
|
|
self.browse = QtGui.QPushButton(self)
|
|
self.browse.setText('Browse')
|
|
self.browse.clicked.connect(self.onBrowseClicked)
|
|
self.browse.move(self.choosepic.x() + self.choosepic.width() + 8, self.choosepic.y())
|
|
|
|
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 onSaveClicked(self):
|
|
name = encodeAOString(self.eviname.text())
|
|
desc = encodeAOString(self.evidesc.toPlainText())
|
|
|
|
if not self.gamegui.privateInventorySelected:
|
|
if self.editing:
|
|
self.gamegui.tcp.send('EE#' + str(self.editInd) + '#' + name + '#' + desc + '#' + self.filename + '#%')
|
|
else:
|
|
self.gamegui.tcp.send('PE#' + name + '#' + desc + '#' + self.filename + '#%')
|
|
else:
|
|
if self.editing:
|
|
self.gamegui.privateEvidence[self.gamegui.selectedEvidence] = [unicode(name), unicode(desc), unicode(self.filename)]
|
|
self.gamegui.boxPrivateEvidence.setItemText(self.gamegui.selectedEvidence, name)
|
|
|
|
evi = self.gamegui.privateEvidence[self.gamegui.selectedEvidence]
|
|
self.gamegui.evidenceDescription.setText(evi[1])
|
|
self.gamegui.setEvidenceImage(self.gamegui.evidenceImage, evi[2])
|
|
else:
|
|
self.gamegui.privateEvidence.append([name, desc, self.filename])
|
|
self.gamegui.boxPrivateEvidence.addItem(name)
|
|
self.gamegui.onExportEvidence(True)
|
|
|
|
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.setTitle()
|
|
self.choosepic.setCurrentIndex(self.emptyfile)
|
|
self.hide()
|
|
|
|
def onCancelClicked(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.setTitle()
|
|
self.choosepic.setCurrentIndex(self.emptyfile)
|
|
self.hide()
|
|
|
|
def onBrowseClicked(self):
|
|
path = str(QtGui.QFileDialog.getOpenFileName(self, "Select an image", AOpath + 'evidence', "Images (*.png)"))
|
|
if path:
|
|
if not "/evidence/" in path.lower():
|
|
QtGui.QMessageBox.warning(self, 'Wrong directory', 'Please select a file from the "evidence" directory.')
|
|
self.onBrowseClicked()
|
|
return
|
|
|
|
file = basename(path)
|
|
if file.lower().endswith('.png'):
|
|
ind = os.listdir(AOpath + 'evidence').index(file)
|
|
self.choosepic.setCurrentIndex(ind)
|
|
else:
|
|
QtGui.QMessageBox.warning(self, 'Not a valid file', 'Please select a PNG image.')
|
|
self.onBrowseClicked()
|
|
|
|
def editEvidence(self, ind):
|
|
self.editing = True
|
|
self.editInd = ind
|
|
evidence = self.gamegui.privateEvidence if self.gamegui.privateInventorySelected else self.gamegui.evidence
|
|
|
|
if evidence[ind][2] not in self.filenames:
|
|
self.filenames.append(evidence[ind][2])
|
|
self.choosepic.addItem(evidence[ind][2].split('.')[0])
|
|
self.choosepic.setCurrentIndex(self.filenames.index(evidence[ind][2]))
|
|
self.eviname.setText(evidence[ind][0])
|
|
self.evidesc.setText(evidence[ind][1])
|
|
self.setWindowTitle("Edit evidence" if not self.gamegui.privateInventorySelected else "Edit evidence in private inventory")
|
|
self.show()
|
|
|
|
def setTitle(self):
|
|
self.setWindowTitle('Add evidence' if not self.gamegui.privateInventorySelected else "Add evidence to private inventory")
|
|
|
|
class TCPThread(QtCore.QThread):
|
|
connectionError = QtCore.pyqtSignal(str, str, str)
|
|
MS_Chat = QtCore.pyqtSignal(list)
|
|
newChar = QtCore.pyqtSignal(str)
|
|
newBackground = QtCore.pyqtSignal(str, bool)
|
|
IC_Log = QtCore.pyqtSignal(str)
|
|
OOC_Log = QtCore.pyqtSignal(str)
|
|
charSlots = QtCore.pyqtSignal()
|
|
showCharSelect = QtCore.pyqtSignal()
|
|
loadAllEvidence = QtCore.pyqtSignal(list)
|
|
rainbowColor = QtCore.pyqtSignal(str)
|
|
updatePlayerList = QtCore.pyqtSignal(str, int, int, str)
|
|
timerUpdate = QtCore.pyqtSignal(int, int, int)
|
|
|
|
send_attempts = 0
|
|
max_attempts = 5
|
|
stop_now = False
|
|
|
|
def __init__(self, parent):
|
|
super(TCPThread, 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.stop_now:
|
|
self.parent.tcp.close()
|
|
self.parent.tcp = None
|
|
self.quit()
|
|
return
|
|
|
|
if self.parent.disconnectNow:
|
|
self.parent.disconnectCommon()
|
|
self.quit()
|
|
return
|
|
pingtimer -= 1
|
|
|
|
if pingtimer == 0:
|
|
pingbefore = time.time()
|
|
self.parent.tcp.send('CH#%')
|
|
pingtimer = 150
|
|
|
|
if self.parent.mChatMessage[TEXT_COLOR] == str(C_RAINBOW):
|
|
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('background-color: rgba(0, 0, 0, 0); color: rgb(' + str(color.red()) + ', ' + str(color.green()) + ', ' + str(color.blue()) + ')')
|
|
|
|
if sendtick:
|
|
sendtick -= 1
|
|
if self.parent.messageQueue and not sendtick:
|
|
self.parent.tcp.send(self.parent.messageQueue[0])
|
|
sendtick = 4
|
|
|
|
error, total = self.parent.tcp.recv()
|
|
if error == -2:
|
|
# if the message can't be sent, discard it
|
|
if sendtick == 4:
|
|
self.send_attempts += 1
|
|
if self.send_attempts >= self.max_attempts:
|
|
self.send_attempts = 0
|
|
#print "[warning] message discarded"
|
|
del self.parent.messageQueue[0]
|
|
self.parent.queueItems.takeItem(0)
|
|
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
|
|
elif error == -3:
|
|
self.parent.emit(QtCore.SIGNAL('showMessage(QString, QString, QString)'), 'critical', 'Connection lost', "There was a critical connection failure. Please check your internet connection.\n\nDetails: %s." % total)
|
|
self.parent.willDisconnect = True
|
|
self.quit()
|
|
return
|
|
else:
|
|
self.send_attempts = 0
|
|
|
|
packets.handlePackets(self, total)
|
|
|
|
def stop(self):
|
|
self.stop_now = True
|
|
|
|
class GUI(QtGui.QWidget):
|
|
gamewindow = None
|
|
sound = None
|
|
music = None
|
|
nextCharacterIsNotSpecial = False
|
|
messageIsCentered = False
|
|
currentDisplaySpeed = 3
|
|
messageDisplaySpeed = (30, 40, 50, 60, 75, 100, 120)
|
|
entireMessageIsBlue = False
|
|
inlineColorStack = []
|
|
inlineBlueDepth = 0
|
|
otherCharId = -1
|
|
offsetWithPair = 0
|
|
tickPos = 0
|
|
blipPos = 0
|
|
blipRate = 1
|
|
timeMod = 40
|
|
blip = "male"
|
|
blipSound = None
|
|
chatMessageSize = 33
|
|
mChatMessage = []
|
|
blankBlip = False
|
|
chatMessageIsEmpty = False
|
|
animIsEmpty = False
|
|
isAdditive = False
|
|
additiveChar = -1
|
|
animState = 3
|
|
textState = 2
|
|
objectionState = 0
|
|
textColor = 0
|
|
|
|
charini = ConfigParser()
|
|
chatmsg = ''
|
|
charid = -1
|
|
login = False
|
|
privateMusicSelected = False
|
|
privateInventorySelected = False
|
|
scaling = [SCALING_AUTO, SCALING_AUTO]
|
|
|
|
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.chatMessageSize):
|
|
self.mChatMessage.append("")
|
|
|
|
self.chatTickTimer = QtCore.QTimer(self)
|
|
self.chatTickTimer.timeout.connect(self.chatTick)
|
|
|
|
self.sfxDelayTimer = QtCore.QTimer(self)
|
|
self.sfxDelayTimer.setSingleShot(True)
|
|
self.sfxDelayTimer.timeout.connect(self.playSfx)
|
|
|
|
self.inboxTimer = QtCore.QTimer(self)
|
|
self.inboxTimer.setSingleShot(True)
|
|
self.inboxTimer.timeout.connect(self.inboxTimerTimeout)
|
|
|
|
self.modcall = None
|
|
|
|
self.healthbars.connect(self.netmsgHP)
|
|
self.disconnectNow = False
|
|
self.swapping = False
|
|
self.iniSwapIndex = 0
|
|
self.background = 'default'
|
|
|
|
self.viewport = QtGui.QWidget(self)
|
|
self.court = QtGui.QLabel(self.viewport)
|
|
|
|
self.slideBg = QtGui.QLabel(self.viewport)
|
|
self.slideBgAnimation = QtCore.QPropertyAnimation(self.slideBg, "geometry")
|
|
self.slideBgAnimation.finished.connect(self.slideDone)
|
|
self.slideBgAnimation.valueChanged.connect(self.slideChanged)
|
|
self.slideBg.hide()
|
|
|
|
self.zoom = ZoomLines(self.viewport)
|
|
|
|
self.char = AOCharMovie(self.viewport)
|
|
self.char.done.connect(self.preanimDone)
|
|
self.sideChar = AOCharMovie(self.viewport)
|
|
self.sideChar.hide()
|
|
|
|
self.slideLastWit = []
|
|
self.slideLastPos = None
|
|
self.slideWitness = AOCharMovie(self.viewport)
|
|
self.slideWitness.showOnPlay = False
|
|
self.slideWitness.hide()
|
|
self.slideSpeaker = AOCharMovie(self.viewport)
|
|
self.slideSpeaker.hide()
|
|
self.slideSpeaker.showOnPlay = False
|
|
|
|
self.slideOverlay = QtGui.QLabel(self.viewport)
|
|
self.slideOverlay.hide()
|
|
|
|
self.bench = QtGui.QLabel(self.viewport)
|
|
|
|
self.effectView = AOMovie(self.viewport)
|
|
self.effectView.setScaledContents(True)
|
|
|
|
self.chatbox = QtGui.QLabel(self.viewport)
|
|
self.chatbox.setScaledContents(True)
|
|
|
|
self.text = QtGui.QTextEdit(self.chatbox)
|
|
self.text.setFrameStyle(QtGui.QFrame.NoFrame)
|
|
self.text.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
|
self.text.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
|
self.text.setReadOnly(True)
|
|
self.text.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
|
|
|
|
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.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
|
|
|
|
self.name = QtGui.QLabel(self.chatbox)
|
|
|
|
self.presentedEvidence = QtGui.QLabel(self)
|
|
self.presentedEvidence.setScaledContents(True)
|
|
self.presentedEvidence.hide()
|
|
|
|
self.wtceView = WTCEView(self)
|
|
self.wtceSignal.connect(self.wtceView.showWTCE)
|
|
|
|
self.objectionView = AOMovie(self.viewport)
|
|
self.objectionView.setScaledContents(True)
|
|
self.objectionView.done.connect(self.objectionDone)
|
|
|
|
self.whiteFlashLabel = QtGui.QLabel(self.viewport)
|
|
self.whiteFlashLabel.setPixmap(QtGui.QPixmap(AO2XPpath + 'themes/default/realizationflash.png'))
|
|
self.whiteFlashLabel.setScaledContents(True)
|
|
self.whiteFlashLabel.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.shakesRemaining = 0
|
|
|
|
self.onscreenTimerLabels = []
|
|
self.onscreenTimerTimes = [0, 0, 0, 0, 0]
|
|
self.onscreenTimerPaused = [True, True, True, True, True]
|
|
self.onscreenTimer = QtCore.QTimer(self)
|
|
self.onscreenTimer.timeout.connect(self.updateOnscreenTimers)
|
|
self.onscreenTimerTick = 1000
|
|
|
|
for i in range(len(self.onscreenTimerTimes)):
|
|
label = QtGui.QLabel(self)
|
|
label.hide()
|
|
label.setText("00:00:00")
|
|
label.setStyleSheet('color: white;')
|
|
self.onscreenTimerLabels.append(label)
|
|
|
|
self.onscreenTimerLabels[0].setAlignment(QtCore.Qt.AlignCenter)
|
|
self.onscreenTimerLabels[3].setAlignment(QtCore.Qt.AlignRight)
|
|
self.onscreenTimerLabels[4].setAlignment(QtCore.Qt.AlignRight)
|
|
self.onscreenTimerLabels[2].move(self.onscreenTimerLabels[1].x(), self.onscreenTimerLabels[1].y() + self.onscreenTimerLabels[1].size().height() + 4)
|
|
self.onscreenTimerLabels[4].move(self.onscreenTimerLabels[3].x(), self.onscreenTimerLabels[3].y() + self.onscreenTimerLabels[3].size().height() + 4)
|
|
|
|
# GUI start
|
|
self.gameTabs = QtGui.QTabWidget(self)
|
|
self.serverTabs = QtGui.QTabWidget(self)
|
|
self.tabLog = QtGui.QWidget() # the IC chat log
|
|
self.tabEvidence = QtGui.QWidget() # court record
|
|
self.tabQueue = QtGui.QWidget() # IC messages pending to be sent
|
|
self.tabIniSwap = QtGui.QWidget() # self explanatory
|
|
self.tabMute = QtGui.QWidget() # mute a player
|
|
self.tabPair = QtGui.QWidget() # AO2 pair
|
|
self.tabMisc = QtGui.QWidget() # ao2xp misc/fun stuff
|
|
self.tabPlayers = QtGui.QWidget() # player list
|
|
self.tabMusic = QtGui.QWidget() # music list
|
|
self.tabBackgrounds = QtGui.QWidget() # backgrounds list
|
|
|
|
# OOC chat log
|
|
self.OOCLog = ChatLogs(self, 1)
|
|
self.OOCLog.setReadOnly(True)
|
|
self.OOCLog.textChanged.connect(self.onOOCLogChanged)
|
|
self.OOCNameInput = QtGui.QLineEdit(self)
|
|
self.OOCNameInput.setPlaceholderText('Enter a name...')
|
|
self.OOCInput = QtGui.QLineEdit(self)
|
|
self.OOCInput.setPlaceholderText('Server chat/OOC chat...')
|
|
self.OOCInput.returnPressed.connect(self.onOOCReturn)
|
|
self.OOCLogin = QtGui.QPushButton("Lo&gin", self)
|
|
self.OOCLogin.clicked.connect(self.onOOCLoginClicked)
|
|
|
|
# IC chat log
|
|
self.ICLog = ChatLogs(self.tabLog, 0, self.OOCLog.logfile)
|
|
self.ICLog.setReadOnly(True)
|
|
self.ICLog.textChanged.connect(self.onICLogChanged)
|
|
|
|
# Evidence
|
|
self.boxEvidence = QtGui.QComboBox(self.tabEvidence)
|
|
self.boxEvidence.currentIndexChanged.connect(self.changeGlobalEvidence)
|
|
self.boxEvidence.setEditable(1)
|
|
self.boxPrivateEvidence = QtGui.QComboBox(self.tabEvidence)
|
|
self.boxPrivateEvidence.currentIndexChanged.connect(self.changePrivateEvidence)
|
|
self.evidenceDescription = QtGui.QTextEdit(self.tabEvidence)
|
|
self.evidenceDescription.setReadOnly(True)
|
|
self.evidenceImage = QtGui.QLabel(self.tabEvidence)
|
|
self.evidenceImage.setPixmap(QtGui.QPixmap(AOpath + 'evidence/empty.png'))
|
|
self.evidenceImage.show()
|
|
self.btnEvidenceAdd = QtGui.QPushButton(QtGui.QIcon(AO2XPpath + "icons/" + "add.png"), "", self.tabEvidence)
|
|
self.btnEvidenceAdd.setToolTip('Add new evidence')
|
|
self.btnEvidenceAdd.clicked.connect(self.onAddEvidence)
|
|
self.btnEvidenceEdit = QtGui.QPushButton(QtGui.QIcon(AO2XPpath + "icons/" + "edit.png"), "", self.tabEvidence)
|
|
self.btnEvidenceEdit.setToolTip('Edit selected evidence')
|
|
self.btnEvidenceEdit.clicked.connect(self.onEditEvidence)
|
|
self.btnEvidenceDelete = QtGui.QPushButton(QtGui.QIcon(AO2XPpath + "icons/" + "delete.png"), "", self.tabEvidence)
|
|
self.btnEvidenceDelete.setToolTip('Delete selected evidence')
|
|
self.btnEvidenceDelete.clicked.connect(self.onDeleteEvidence)
|
|
self.btnEvidenceLoad = QtGui.QPushButton(QtGui.QIcon(AO2XPpath + "icons/" + "folder.png"), "", self.tabEvidence)
|
|
self.btnEvidenceLoad.setToolTip('Import all evidence from a file')
|
|
self.btnEvidenceLoad.clicked.connect(self.onImportEvidence)
|
|
self.btnEvidenceSave = QtGui.QPushButton(QtGui.QIcon(AO2XPpath + "icons/" + "disk.png"), "", self.tabEvidence)
|
|
self.btnEvidenceSave.setToolTip('Export all evidence to a file')
|
|
self.btnEvidenceSave.clicked.connect(self.onExportEvidence)
|
|
self.btnEvidenceMoveToPrivate = QtGui.QPushButton(QtGui.QIcon(AO2XPpath + "icons/" + "arrow_right.png"), "", self.tabEvidence)
|
|
self.btnEvidenceMoveToPrivate.setToolTip('Transfer selected evidence to the private inventory')
|
|
self.btnEvidenceMoveToPrivate.clicked.connect(self.onTransferEvidence)
|
|
self.btnEvidenceMoveAllToPrivate = QtGui.QPushButton(QtGui.QIcon(AO2XPpath + "icons/" + "arrow_right2.png"), "", self.tabEvidence)
|
|
self.btnEvidenceMoveAllToPrivate.setToolTip('Transfer all evidence to the private inventory')
|
|
self.btnEvidenceMoveAllToPrivate.clicked.connect(self.onTransferAllEvidence)
|
|
self.btnEvidenceMoveToGlobal = QtGui.QPushButton(QtGui.QIcon(AO2XPpath + "icons/" + "arrow_left.png"), "", self.tabEvidence)
|
|
self.btnEvidenceMoveToGlobal.setToolTip('Transfer selected evidence to the global inventory')
|
|
self.btnEvidenceMoveToGlobal.clicked.connect(self.onTransferEvidence)
|
|
self.btnEvidenceMoveAllToGlobal = QtGui.QPushButton(QtGui.QIcon(AO2XPpath + "icons/" + "arrow_left2.png"), "", self.tabEvidence)
|
|
self.btnEvidenceMoveAllToGlobal.setToolTip('Transfer all evidence to the global inventory')
|
|
self.btnEvidenceMoveAllToGlobal.clicked.connect(self.onTransferAllEvidence)
|
|
self.btnEvidenceSwitchToPrivate = QtGui.QPushButton(QtGui.QIcon(AO2XPpath + "icons/" + "world.png"), "", self.tabEvidence)
|
|
self.btnEvidenceSwitchToPrivate.setToolTip('Switch to the private inventory')
|
|
self.btnEvidenceSwitchToPrivate.clicked.connect(self.onSwitchInventory)
|
|
self.btnEvidenceSwitchToGlobal = QtGui.QPushButton(QtGui.QIcon(AO2XPpath + "icons/" + "briefcase.png"), "", self.tabEvidence)
|
|
self.btnEvidenceSwitchToGlobal.setToolTip('Switch to the global inventory')
|
|
self.btnEvidenceSwitchToGlobal.clicked.connect(self.onSwitchInventory)
|
|
self.btnEvidencePresent = buttons.PresentButton(self, self.tabEvidence)
|
|
self.boxPrivateEvidence.hide()
|
|
self.btnEvidenceMoveToGlobal.hide()
|
|
self.btnEvidenceMoveAllToGlobal.hide()
|
|
self.btnEvidenceSwitchToGlobal.hide()
|
|
|
|
# Muting
|
|
self.unmutedList = QtGui.QListWidget(self.tabMute)
|
|
self.unmutedList.itemClicked.connect(self.changeUnmuteIndex)
|
|
self.mutedList = QtGui.QListWidget(self.tabMute)
|
|
self.mutedList.itemClicked.connect(self.changeMuteIndex)
|
|
self.btnMute = QtGui.QPushButton(self.tabMute)
|
|
self.btnMute.setText('>>')
|
|
self.btnMute.clicked.connect(self.onMuteClicked)
|
|
self.btnUnmute = QtGui.QPushButton(self.tabMute)
|
|
self.btnUnmute.setText('<<')
|
|
self.btnUnmute.clicked.connect(self.onUnmuteClicked)
|
|
self.lblNotMuted = QtGui.QLabel(self.tabMute)
|
|
self.lblNotMuted.setText('Not muted')
|
|
self.lblMuted = QtGui.QLabel(self.tabMute)
|
|
self.lblMuted.setText('Muted')
|
|
|
|
# Ini swaps
|
|
self.iniSwapList = QtGui.QComboBox(self.tabIniSwap)
|
|
self.iniSwapList.currentIndexChanged.connect(self.iniSwapIndexChange)
|
|
self.iniSwapList.setEditable(1)
|
|
self.btnIniSwapConfirm = QtGui.QPushButton(self.tabIniSwap)
|
|
self.btnIniSwapConfirm.setText('Swap')
|
|
self.btnIniSwapConfirm.clicked.connect(self.onIniSwapConfirmClicked)
|
|
self.btnIniSwapReset = QtGui.QPushButton(self.tabIniSwap)
|
|
self.btnIniSwapReset.setText('Reset')
|
|
self.btnIniSwapReset.clicked.connect(self.resetIniSwap)
|
|
self.btnIniSwapRefresh = QtGui.QPushButton(self.tabIniSwap)
|
|
self.btnIniSwapRefresh.setText('Refresh characters')
|
|
self.btnIniSwapRefresh.clicked.connect(self.loadSwapCharacters)
|
|
self.lblIniSwapInfo = QtGui.QLabel(self.tabIniSwap)
|
|
self.lblIniSwapInfo.setText('Not swapped')
|
|
|
|
# Pairing
|
|
self.cbPair = QtGui.QCheckBox(self.tabPair)
|
|
self.cbPair.setChecked(False)
|
|
self.boxPair = QtGui.QComboBox(self.tabPair)
|
|
self.sliPairOffset = QtGui.QSlider(QtCore.Qt.Horizontal, self.tabPair)
|
|
self.sliPairOffset.setRange(-100, 100)
|
|
self.sliPairOffset.setValue(0)
|
|
self.lblPairOffset = QtGui.QLabel("X offset", self.tabPair)
|
|
self.sliPairOffsetY = QtGui.QSlider(QtCore.Qt.Vertical, self.tabPair)
|
|
self.sliPairOffsetY.setRange(-100, 100)
|
|
self.sliPairOffsetY.setValue(0)
|
|
self.lblPairOffsetY = QtGui.QLabel("Y offset", self.tabPair)
|
|
self.btnPairOffsetReset = QtGui.QPushButton("Reset", self.tabPair)
|
|
self.btnPairOffsetReset.clicked.connect(self.resetPairOffsets)
|
|
self.boxPairOrder = QtGui.QComboBox(self.tabPair)
|
|
self.boxPairOrder.addItem("Front")
|
|
self.boxPairOrder.addItem("Behind")
|
|
self.lblPairOrder = QtGui.QLabel("Pairing order", self.tabPair)
|
|
|
|
# Misc
|
|
self.layMisc = QtGui.QVBoxLayout(self.tabMisc)
|
|
self.layMisc.setAlignment(QtCore.Qt.AlignTop)
|
|
self.cbMockText = QtGui.QCheckBox()
|
|
self.cbMockText.setChecked(False)
|
|
self.cbMockText.setText(mockString("mock text"))
|
|
self.cbSpacing = QtGui.QCheckBox()
|
|
self.cbSpacing.setChecked(False)
|
|
self.cbSpacing.setText("S p a c i n g")
|
|
self.cbAutoCaps = QtGui.QCheckBox()
|
|
self.cbAutoCaps.setChecked(False)
|
|
self.cbAutoCaps.setText("Automatic caps and period")
|
|
self.layMisc.addWidget(self.cbMockText)
|
|
self.layMisc.addWidget(self.cbSpacing)
|
|
self.layMisc.addWidget(self.cbAutoCaps)
|
|
|
|
# Message queue
|
|
self.queueItems = QtGui.QListWidget(self.tabQueue)
|
|
self.queueItems.itemClicked.connect(self.onMessageQueueItemClicked)
|
|
self.btnQueueRemove = QtGui.QPushButton(self.tabQueue)
|
|
self.btnQueueRemove.setText('Delete')
|
|
self.btnQueueRemove.clicked.connect(self.onRemoveQueueClicked)
|
|
self.btnQueueClear = QtGui.QPushButton(self.tabQueue)
|
|
self.btnQueueClear.setText('Clear')
|
|
self.btnQueueClear.clicked.connect(self.onClearQueueClicked)
|
|
|
|
self.gameTabs.addTab(self.tabLog, '&Log')
|
|
self.gameTabs.addTab(self.tabEvidence, '&Evidence')
|
|
self.gameTabs.addTab(self.tabMute, 'Mu&te')
|
|
self.gameTabs.addTab(self.tabIniSwap, '&INI swap')
|
|
self.gameTabs.addTab(self.tabPair, 'Pai&r')
|
|
self.gameTabs.addTab(self.tabMisc, 'E&xtras')
|
|
self.gameTabs.addTab(self.tabQueue, '&Queue')
|
|
|
|
# Music list
|
|
self.musicItems = QtGui.QListWidget(self.tabMusic)
|
|
self.musicItems.itemClicked.connect(self.onMusicItemClicked)
|
|
self.musicItems.itemDoubleClicked.connect(self.onMusicItemDoubleClicked)
|
|
self.musicSearch = QtGui.QLineEdit(self.tabMusic)
|
|
self.musicSearch.setPlaceholderText("Search...")
|
|
self.musicSearch.textChanged.connect(self.onMusicSearch)
|
|
self.foundSongItemColor = "background: #80FF80;"
|
|
self.btnMusicOptions = QtGui.QPushButton(QtGui.QIcon(AO2XPpath + "icons/" + "cog.png"), "", self.tabMusic)
|
|
self.btnMusicOptions.setToolTip('Music options')
|
|
self.btnMusicOptions.clicked.connect(self.onMusicOptionsClicked)
|
|
self.actMusicRandom = QtGui.QAction(QtGui.QIcon(AO2XPpath + "icons/" + "control_play_blue.png"), "&Play random", self.tabMusic)
|
|
self.actMusicRandom.triggered.connect(self.onActMusicRandomTriggered)
|
|
self.actMusicCopy = QtGui.QAction(QtGui.QIcon(AO2XPpath + "icons/" + "page_copy.png"), "&Copy selected song name", self.tabMusic)
|
|
self.actMusicCopy.triggered.connect(self.onActMusicCopyTriggered)
|
|
self.actMusicCopy.setDisabled(True)
|
|
self.actMusicSeparator = QtGui.QAction(self.tabMusic)
|
|
self.actMusicSeparator.setSeparator(True)
|
|
self.actMusicAdd = QtGui.QAction(QtGui.QIcon(AO2XPpath + "icons/" + "add.png"), "&Add new entry", self.tabMusic)
|
|
self.actMusicAdd.triggered.connect(self.onActMusicAddTriggered)
|
|
self.actMusicAdd.setVisible(False)
|
|
self.actMusicAdd.setDisabled(True)
|
|
self.actMusicEdit = QtGui.QAction(QtGui.QIcon(AO2XPpath + "icons/" + "edit.png"), "&Edit selected entry", self.tabMusic)
|
|
self.actMusicEdit.triggered.connect(self.onActMusicEditTriggered)
|
|
self.actMusicEdit.setVisible(False)
|
|
self.actMusicEdit.setDisabled(True)
|
|
self.actMusicDelete = QtGui.QAction(QtGui.QIcon(AO2XPpath + "icons/" + "delete.png"), "&Delete selected entry", self.tabMusic)
|
|
self.actMusicDelete.triggered.connect(self.onActMusicDeleteTriggered)
|
|
self.actMusicDelete.setVisible(False)
|
|
self.actMusicDelete.setDisabled(True)
|
|
self.actMusicRefresh = QtGui.QAction(QtGui.QIcon(AO2XPpath + "icons/" + "arrow_refresh.png"), "&Refresh list", self.tabMusic)
|
|
self.actMusicRefresh.triggered.connect(self.onActMusicRefreshTriggered)
|
|
self.actMusicRefresh.setVisible(False)
|
|
self.actMusicSwitchToGlobal = QtGui.QAction(QtGui.QIcon(AO2XPpath + "icons/" + "world.png"), "&View global music list", self.tabMusic)
|
|
self.actMusicSwitchToGlobal.triggered.connect(self.onActMusicSwitchTriggered)
|
|
self.actMusicSwitchToGlobal.setVisible(False)
|
|
self.actMusicSwitchToPrivate = QtGui.QAction(QtGui.QIcon(AO2XPpath + "icons/" + "briefcase.png"), "&View private music list", self.tabMusic)
|
|
self.actMusicSwitchToPrivate.triggered.connect(self.onActMusicSwitchTriggered)
|
|
self.mnuMusicOptions = QtGui.QMenu(self.tabMusic)
|
|
self.mnuMusicOptions.addAction(self.actMusicRandom)
|
|
self.mnuMusicOptions.addAction(self.actMusicCopy)
|
|
self.mnuMusicOptions.addAction(self.actMusicSeparator)
|
|
self.mnuMusicOptions.addAction(self.actMusicAdd)
|
|
self.mnuMusicOptions.addAction(self.actMusicEdit)
|
|
self.mnuMusicOptions.addAction(self.actMusicDelete)
|
|
self.mnuMusicOptions.addAction(self.actMusicRefresh)
|
|
self.mnuMusicOptions.addSeparator()
|
|
self.mnuMusicOptions.addAction(self.actMusicSwitchToPrivate)
|
|
self.mnuMusicOptions.addAction(self.actMusicSwitchToGlobal)
|
|
|
|
self.musicListPrivate = OrderedDict([])
|
|
self.onActMusicRefreshTriggered(True)
|
|
|
|
# Areas list
|
|
self.areaItems = QtGui.QListWidget()
|
|
self.areaItems.itemDoubleClicked.connect(self.onAreaItemClicked)
|
|
|
|
# Player list
|
|
self.playerItems = QtGui.QListWidget(self.tabPlayers)
|
|
self.playerItems.itemClicked.connect(self.onPlayerItemClicked)
|
|
self.btnPlayerPair = QtGui.QPushButton(self.tabPlayers)
|
|
self.btnPlayerPair.setText('Pair')
|
|
self.btnPlayerPair.clicked.connect(self.onPlayerPairClicked)
|
|
self.btnPlayerKick = QtGui.QPushButton(self.tabPlayers)
|
|
self.btnPlayerKick.setText('Kick')
|
|
self.btnPlayerKick.clicked.connect(self.onPlayerKickClicked)
|
|
self.btnPlayerKick.setDisabled(True)
|
|
self.btnPlayerBan = QtGui.QPushButton(self.tabPlayers)
|
|
self.btnPlayerBan.setText('Ban')
|
|
self.btnPlayerBan.clicked.connect(self.onPlayerBanClicked)
|
|
self.btnPlayerBan.setDisabled(True)
|
|
|
|
# Background list
|
|
self.backgrounds = []
|
|
self.backgroundItems = QtGui.QListWidget(self.tabBackgrounds)
|
|
self.backgroundItems.itemDoubleClicked.connect(self.onBackgroundItemClicked)
|
|
self.backgroundSearch = QtGui.QLineEdit(self.tabBackgrounds)
|
|
self.backgroundSearch.setPlaceholderText("Search...")
|
|
self.backgroundSearch.textChanged.connect(self.onBackgroundSearch)
|
|
self.btnBackgroundsRefresh = QtGui.QPushButton(self.tabBackgrounds)
|
|
self.btnBackgroundsRefresh.setText('Refresh')
|
|
self.btnBackgroundsRefresh.clicked.connect(self.loadBackgrounds)
|
|
self.loadBackgrounds()
|
|
|
|
# Demo list
|
|
self.demoItems = demo.get_demo_treeview()
|
|
self.demoItems.doubleClicked.connect(self.onDemoClicked)
|
|
|
|
self.serverTabs.addTab(self.tabMusic, "&Music")
|
|
self.serverTabs.addTab(self.areaItems, "&Areas")
|
|
self.serverTabs.addTab(self.tabPlayers, 'Pla&yers')
|
|
self.serverTabs.addTab(self.tabBackgrounds, "&Backs")
|
|
self.serverTabs.addTab(self.demoItems, "Demos")
|
|
|
|
# IC options
|
|
self.ICChatInput = QtGui.QLineEdit(self)
|
|
self.ICChatInput.returnPressed.connect(self.onICReturn)
|
|
self.ICChatInput.setPlaceholderText('Game chat')
|
|
|
|
self.btnChangeChar = QtGui.QPushButton(self)
|
|
self.btnChangeChar.clicked.connect(self.onChangeCharClicked)
|
|
|
|
self.btnCallMod = QtGui.QPushButton(self)
|
|
self.btnCallMod.setText('Call mod')
|
|
self.btnCallMod.clicked.connect(self.onCallModClicked)
|
|
|
|
self.btnSettings = QtGui.QPushButton("Settings", self)
|
|
self.btnSettings.clicked.connect(self.gamewindow.showSettings)
|
|
|
|
self.boxEmotes = QtGui.QComboBox(self)
|
|
self.boxEmotes.currentIndexChanged.connect(partial(self.changeEmote, True))
|
|
self.boxEmotes.setToolTip('Select an emotion for your character')
|
|
|
|
self.boxColors = QtGui.QComboBox(self)
|
|
self.boxColors.currentIndexChanged.connect(self.setChatColor)
|
|
self.boxColors.setToolTip('Change the color of your message')
|
|
|
|
self.defaultPositions = ["def", "pro", "wit", "hld", "hlp", "jud", "jur", "sea"]
|
|
self.boxPositions = QtGui.QComboBox(self)
|
|
self.boxPositions.addItems(self.defaultPositions)
|
|
self.boxPositions.currentIndexChanged.connect(self.setPosition)
|
|
self.boxPositions.setToolTip('Select your position in the courtroom')
|
|
|
|
self.boxEffects = QtGui.QComboBox(self)
|
|
self.boxEffects.currentIndexChanged.connect(self.ICChatFocus)
|
|
self.boxEffects.setToolTip('Show this effect on your next message')
|
|
|
|
self.showname = ""
|
|
self.shownameEdit = QtGui.QLineEdit(self)
|
|
self.shownameEdit.textChanged.connect(self.onShownameChanged)
|
|
self.shownameEdit.setPlaceholderText("Showname")
|
|
self.shownameEdit.setToolTip('Set a custom name for your character')
|
|
|
|
self.cbFlip = QtGui.QCheckBox(self)
|
|
self.cbFlip.stateChanged.connect(self.changeFlipCheck)
|
|
self.cbFlip.setText('&Flip')
|
|
self.cbFlip.resize(self.cbFlip.sizeHint())
|
|
self.cbFlip.setToolTip("Mirror your character horizontally")
|
|
|
|
self.cbPreanim = QtGui.QCheckBox(self)
|
|
self.cbPreanim.setChecked(True)
|
|
self.cbPreanim.stateChanged.connect(self.changePreanimCheck)
|
|
self.cbPreanim.setText('&Pre-anim')
|
|
self.cbPreanim.setToolTip("Play a character-specific animation before the next message")
|
|
|
|
self.cbBench = QtGui.QCheckBox(self)
|
|
self.cbBench.setChecked(True)
|
|
self.cbBench.setText('&Desk')
|
|
self.cbBench.stateChanged.connect(self.ICChatFocus)
|
|
self.cbBench.setToolTip('Show or hide the desk in front of your character')
|
|
|
|
self.cbSlide = QtGui.QCheckBox(self)
|
|
self.cbSlide.stateChanged.connect(self.ICChatFocus)
|
|
self.cbSlide.setText('&Slide')
|
|
self.cbSlide.resize(self.cbSlide.sizeHint())
|
|
self.cbSlide.setToolTip("Tell clients to play courtroom slide animations for your message")
|
|
|
|
self.cbNoInterrupt = QtGui.QCheckBox(self)
|
|
self.cbNoInterrupt.setChecked(False)
|
|
self.cbNoInterrupt.stateChanged.connect(self.ICChatFocus)
|
|
self.cbNoInterrupt.setText('&No interrupt')
|
|
self.cbNoInterrupt.setToolTip("Show the next message immediately, ignoring animations")
|
|
|
|
self.cbAdditive = QtGui.QCheckBox(self)
|
|
self.cbAdditive.setChecked(False)
|
|
self.cbAdditive.setText('Additi&ve')
|
|
self.cbAdditive.resize(self.cbAdditive.sizeHint())
|
|
self.cbAdditive.clicked.connect(self.ICChatFocus)
|
|
self.cbAdditive.setToolTip('Append the next message to the previous one, without a new textbox')
|
|
|
|
# Emotions
|
|
self.btnPrevEmotePage = buttons.BackEmoteButton(self)
|
|
self.btnPrevEmotePage.hide()
|
|
self.btnNextEmotePage = buttons.NextEmoteButton(self)
|
|
self.btnNextEmotePage.show()
|
|
self.currentEmotePage = 0
|
|
self.maxEmotesOnPage = 0
|
|
self.emoteButtons = []
|
|
|
|
# Special effects
|
|
self.btnRealization = buttons.AOToggleButton(self, "realization")
|
|
self.btnRealization.clicked.connect(self.onRealizationClicked)
|
|
self.btnRealization.setToolTip('Show the next message with a realization effect')
|
|
self.sndRealization = audio.loadHandle(False, AOpath + 'sounds/general/sfx-realization.wav', 0, 0, 0)
|
|
self.btnShake = buttons.AOToggleButton(self, "screenshake") # AO 2.8
|
|
self.btnShake.clicked.connect(self.onShakeClicked)
|
|
self.btnShake.setToolTip('Show the next message with a shaking effect')
|
|
#self.sndShake = audio.loadHandle(False, AOpath + 'sounds/general/sfx-damage.wav', 0, 0, 0)
|
|
|
|
# Objections
|
|
self.btnCustomObjection = buttons.CustomObjection(self)
|
|
self.btnCustomObjection.clicked.connect(self.ICChatFocus)
|
|
self.btnTakeThat = buttons.Objections(self, 3)
|
|
self.btnTakeThat.clicked.connect(self.ICChatFocus)
|
|
self.btnObjection = buttons.Objections(self, 2)
|
|
self.btnObjection.clicked.connect(self.ICChatFocus)
|
|
self.btnHoldIt = buttons.Objections(self, 1)
|
|
self.btnHoldIt.clicked.connect(self.ICChatFocus)
|
|
self.sndObjection = 0
|
|
|
|
# Judge buttons
|
|
self.btnDefenseBar = buttons.PenaltyBars(self, 1)
|
|
self.btnDefenseBar.minusClicked.connect(self.onPenaltyBarMinusClicked)
|
|
self.btnDefenseBar.plusClicked.connect(self.onPenaltyBarPlusClicked)
|
|
self.btnProsecutionBar = buttons.PenaltyBars(self, 2)
|
|
self.btnProsecutionBar.minusClicked.connect(self.onPenaltyBarMinusClicked)
|
|
self.btnProsecutionBar.plusClicked.connect(self.onPenaltyBarPlusClicked)
|
|
self.btnWitnessTestimony = buttons.WTCEbuttons(self, 0)
|
|
self.btnWitnessTestimony.clicked.connect(self.OnWTCEButtonClicked)
|
|
self.btnCrossExamination = buttons.WTCEbuttons(self, 1)
|
|
self.btnCrossExamination.clicked.connect(self.OnWTCEButtonClicked)
|
|
self.btnNotGuilty = buttons.WTCEbuttons(self, 2, 0)
|
|
self.btnNotGuilty.clicked.connect(self.OnWTCEButtonClicked)
|
|
self.btnGuilty = buttons.WTCEbuttons(self, 2, 1)
|
|
self.btnGuilty.clicked.connect(self.OnWTCEButtonClicked)
|
|
self.btnWitnessTestimony.show()
|
|
self.btnCrossExamination.show()
|
|
self.btnNotGuilty.show()
|
|
self.btnGuilty.show()
|
|
self.presenting = -1
|
|
|
|
# Volume sliders, etc.
|
|
self.sliMusicVolume = QtGui.QSlider(QtCore.Qt.Horizontal, self)
|
|
self.sliSoundVolume = QtGui.QSlider(QtCore.Qt.Horizontal, self)
|
|
self.sliBlipsVolume = QtGui.QSlider(QtCore.Qt.Horizontal, self)
|
|
self.sliMusicVolume.setRange(0, 100)
|
|
self.sliSoundVolume.setRange(0, 100)
|
|
self.sliBlipsVolume.setRange(0, 100)
|
|
self.sliMusicVolume.valueChanged.connect(self.changeMusicVolume)
|
|
self.sliSoundVolume.valueChanged.connect(self.changeSoundVolume)
|
|
self.sliBlipsVolume.valueChanged.connect(self.changeBlipVolume)
|
|
self.lblMusicVolume = QtGui.QLabel("Music", self)
|
|
self.lblSoundVolume = QtGui.QLabel("SFX", self)
|
|
self.lblBlipsVolume = QtGui.QLabel("Blips", self)
|
|
self.lblPing = QtGui.QLabel(self)
|
|
|
|
# Demo playback seekbar
|
|
self.sliDemoSeekbar = QtGui.QSlider(QtCore.Qt.Horizontal, self)
|
|
self.sliDemoSeekbar.valueChanged.connect(self.demoSeek)
|
|
self.sliDemoSeekbar.setVisible(False)
|
|
self.sliDemoSeekbar.setMinimum(0)
|
|
|
|
# GUI end
|
|
|
|
self.name.show()
|
|
self.char.show()
|
|
self.court.show()
|
|
self.bench.show()
|
|
self.chatbox.show()
|
|
|
|
self.areas = []
|
|
self.areasLen = 0
|
|
self.noARUP = False
|
|
self.muteSelected = -1
|
|
self.unmuteSelected = -1
|
|
self.muted = []
|
|
self.myChar = -1
|
|
self.myChatColor = 0
|
|
self.charEmotes = []
|
|
self.selectedEmote = 0
|
|
self.charName = ''
|
|
self.charShowname = ''
|
|
self.charSide = 'def'
|
|
self.lastMessage = ''
|
|
self.inboxQueue = []
|
|
self.textWaitTime = int(getOption("General", "text stay time", 200))
|
|
self.messageQueue = []
|
|
self.selectedMessage = -1
|
|
self.evidence = []
|
|
self.privateEvidence = []
|
|
self.selectedEvidence = -1
|
|
self.present = False
|
|
self.playerList = {}
|
|
self.selectedPlayer = -1
|
|
self.myFlip = 0
|
|
self.playPreanim = 1
|
|
self.demoRecorder = None
|
|
self.demoPlaying = False
|
|
self.musicList = {}
|
|
self.pickedMusicItem = False
|
|
|
|
self.slideEnabled = getOption("General", "slide", False) == 'True'
|
|
self.slideAvailable = False
|
|
self.slideHasOverlay = False
|
|
self.slideKind = 0 # 0 = def-pro, 1 = def-wit, 2 = pro-wit
|
|
self.slideDirection = 0 # 0 = left to right, 1 = right to left
|
|
|
|
# slideMap[oldPos][newPos] = [kind, direction]
|
|
self.slideMap = {
|
|
"def": { "pro": [0, 0], "wit": [1, 0] },
|
|
"wit": { "def": [1, 1], "pro": [2, 0] },
|
|
"pro": { "def": [0, 1], "wit": [2, 1] },
|
|
}
|
|
|
|
self.loadSwapCharacters()
|
|
self.iniSwapList.setCurrentIndex(0)
|
|
self.evidenceEditor = EditEvidenceDialog(self)
|
|
|
|
self.connect(self, QtCore.SIGNAL('showMessage(QString, QString, QString)'), self.showMessage)
|
|
|
|
self.charSelect = charselect.charselect(self)
|
|
|
|
self.wtceSfx = 0
|
|
self.guiltySfx = 0
|
|
self.notGuiltySfx = 0
|
|
|
|
self.stream = 0
|
|
self.specialStream = False
|
|
self.downloadThread = None
|
|
self.tcp = None
|
|
self.demoPlayer = None
|
|
|
|
# Finally, load the theme
|
|
self.width = 820
|
|
self.height = 730
|
|
self.viewportScale = 1
|
|
self.nameFontPointSize = 12
|
|
self.textFontPointSize = 24
|
|
self.emotePageGeometry = (526, 384, 288, 98)
|
|
self.loadTheme()
|
|
|
|
def loadTheme(self, switching=False):
|
|
theme = getOption("General", "theme", "default")
|
|
try:
|
|
with open(AO2XPpath + "ao2xp_themes/"+theme+"/theme.py") as t:
|
|
exec t
|
|
if switching:
|
|
# This is to reset the colors on the music list. Not necessary on startup
|
|
self.onMusicSearch("")
|
|
|
|
self.viewportScale = self.viewport.height() / float(192)
|
|
|
|
self.court.resize(self.viewport.size())
|
|
self.bench.resize(self.viewport.size())
|
|
self.effectView.resize()
|
|
self.objectionView.resize()
|
|
self.zoom.resize()
|
|
self.wtceView.resize()
|
|
self.char.resize()
|
|
self.sideChar.resize()
|
|
self.slideWitness.resize()
|
|
self.slideSpeaker.resize()
|
|
|
|
self.ao2text.setGeometry(self.text.geometry())
|
|
self.ao2text.setStyleSheet(self.text.styleSheet())
|
|
|
|
chatboxPixmap = QtGui.QPixmap(AO2XPpath + 'themes/default/chatmed.png')
|
|
chatboxWidth = chatboxPixmap.size().width() * self.viewportScale
|
|
self.chatboxHeight = chatboxPixmap.size().height() * self.viewportScale
|
|
self.chatbox.setPixmap(chatboxPixmap.scaled(chatboxWidth, self.chatboxHeight, QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.FastTransformation))
|
|
self.chatbox.resize(chatboxWidth, self.chatboxHeight)
|
|
self.chatbox.move(0, self.viewport.height() - self.chatboxHeight)
|
|
|
|
self.presentedEvidence.setGeometry(self.viewport.x() + 16, self.viewport.y() + 16, 70 * self.viewportScale, 70 * self.viewportScale)
|
|
|
|
fontDb = None
|
|
nameFont = None
|
|
textFont = None
|
|
|
|
fontDb = QtGui.QFontDatabase()
|
|
fontDb.addApplicationFont(AO2XPpath + 'font/Igiari.ttf')
|
|
fontDb.addApplicationFont(AO2XPpath + 'font/Ace_Name_Regular.ttf')
|
|
nameFont = QtGui.QFont("Ace Name")
|
|
textFont = QtGui.QFont("Igiari")
|
|
nameFont.setPointSize(self.nameFontPointSize)
|
|
textFont.setPointSize(self.textFontPointSize)
|
|
self.text.setFont(textFont)
|
|
self.ao2text.setFont(textFont)
|
|
self.name.setFont(nameFont)
|
|
|
|
for label in self.onscreenTimerLabels:
|
|
label.resize(self.viewport.width(), label.sizeHint().height())
|
|
label.setFont(nameFont)
|
|
|
|
spacing = 1
|
|
xMod_count = yMod_count = 0
|
|
left, top, width, height = self.emotePageGeometry
|
|
columns = (width - 40) / (spacing + 40) + 1
|
|
rows = (height - 40) / (spacing + 40) + 1
|
|
self.maxEmotesOnPage = columns * rows
|
|
|
|
if len(self.emoteButtons) > 0:
|
|
for button in self.emoteButtons:
|
|
button.setParent(None)
|
|
button.deleteLater()
|
|
button = None
|
|
|
|
self.emoteButtons = []
|
|
|
|
for i in range(self.maxEmotesOnPage):
|
|
xPos = (40 + spacing) * xMod_count
|
|
yPos = (40 + spacing) * yMod_count
|
|
xMod_count += 1
|
|
if xMod_count == columns:
|
|
xMod_count = 0
|
|
yMod_count += 1
|
|
|
|
button = buttons.EmoteButton(self, i)
|
|
button.move(left + xPos, top + yPos)
|
|
button.clicked.connect(self.ICChatFocus)
|
|
button.show()
|
|
button.lower()
|
|
self.emoteButtons.append(button)
|
|
|
|
self.btnPrevEmotePage.raise_()
|
|
self.btnNextEmotePage.raise_()
|
|
|
|
if switching:
|
|
self.setBackground()
|
|
self.setScene()
|
|
self.boxEmotes.setCurrentIndex(0)
|
|
self.setEmotePage()
|
|
self.gamewindow.setFixedSize(self.width, self.height)
|
|
self.gamewindow.center()
|
|
except Exception as e:
|
|
QtGui.QMessageBox.critical(None, "Unable to load theme", "There was a problem loading the current theme \"%s\":\n\n%s." % (theme, e))
|
|
os._exit(-2)
|
|
|
|
def resetPairOffsets(self):
|
|
self.sliPairOffset.setValue(0)
|
|
self.sliPairOffsetY.setValue(0)
|
|
|
|
def screenShakeTick(self):
|
|
self.shakesRemaining -= 1
|
|
shakeForce = 8
|
|
if self.shakesRemaining:
|
|
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), self.viewport.height() - self.chatboxHeight + random.randint(-shakeForce, shakeForce))
|
|
self.ao2text.move(-self.chatbox.x()+16, (self.viewport.height()-self.chatboxHeight-self.chatbox.y())+32)
|
|
self.text.move(-self.chatbox.x()+16, (self.viewport.height()-self.chatboxHeight-self.chatbox.y())+-1)
|
|
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, self.viewport.height()-self.chatboxHeight)
|
|
self.ao2text.move(16, 32)
|
|
self.text.move(16,32)
|
|
self.screenshake.stop()
|
|
|
|
def ICChatFocus(self):
|
|
self.ICChatInput.setFocus()
|
|
|
|
def onRealizationClicked(self):
|
|
if self.btnRealization.isPressed():
|
|
self.boxEffects.setCurrentIndex(1) # realization
|
|
elif self.boxEffects.currentText() == "realization":
|
|
self.boxEffects.setCurrentIndex(0)
|
|
self.ICChatFocus()
|
|
|
|
def onShakeClicked(self):
|
|
self.cbPreanim.setChecked(False)
|
|
self.ICChatFocus()
|
|
|
|
def onOOCLoginClicked(self):
|
|
if not self.OOCNameInput.text():
|
|
self.OOCNameInput.setText("unnamed")
|
|
|
|
if not self.login:
|
|
password, ok = QtGui.QInputDialog.getText(self, "Login as moderator", "Enter password.")
|
|
if password and ok:
|
|
self.sendOOCchat(self.OOCNameInput.text().toUtf8(), "/login")
|
|
self.sendOOCchat(self.OOCNameInput.text().toUtf8(), password.toUtf8())
|
|
else:
|
|
self.sendOOCchat(self.OOCNameInput.text().toUtf8(), "/logout")
|
|
|
|
def setPing(self, newping):
|
|
self.lblPing.setText("Ping: %d" % newping)
|
|
|
|
def setPosition(self, ind):
|
|
if not self.OOCNameInput.text():
|
|
self.OOCNameInput.setText("unnamed")
|
|
self.boxPositions.setCurrentIndex(ind)
|
|
self.charSide = str(self.boxPositions.itemText(ind))
|
|
self.setJudgeButtons()
|
|
self.ICChatFocus()
|
|
|
|
if self.demoPlaying:
|
|
return
|
|
|
|
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)
|
|
if value == 0:
|
|
audio.pauseHandle(self.music)
|
|
elif audio.handleIsActive(self.music) == BASS_ACTIVE_PAUSED:
|
|
audio.playHandle(self.music, False)
|
|
|
|
def changeSoundVolume(self, value):
|
|
if self.sound:
|
|
audio.setHandleAttr(self.sound, BASS_ATTRIB_VOL, value / 100.0)
|
|
audio.setHandleAttr(self.sndRealization, 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.blipSound:
|
|
audio.setHandleAttr(self.blipSound, BASS_ATTRIB_VOL, value / 100.0)
|
|
|
|
def setJudgeButtons(self):
|
|
if self.charSide == 'jud':
|
|
self.btnDefenseBar.minusbtn.show()
|
|
self.btnDefenseBar.plusbtn.show()
|
|
self.btnProsecutionBar.minusbtn.show()
|
|
self.btnProsecutionBar.plusbtn.show()
|
|
self.btnWitnessTestimony.show()
|
|
self.btnCrossExamination.show()
|
|
self.btnNotGuilty.show()
|
|
self.btnGuilty.show()
|
|
else:
|
|
self.btnDefenseBar.minusbtn.hide()
|
|
self.btnDefenseBar.plusbtn.hide()
|
|
self.btnProsecutionBar.minusbtn.hide()
|
|
self.btnProsecutionBar.plusbtn.hide()
|
|
self.btnWitnessTestimony.hide()
|
|
self.btnCrossExamination.hide()
|
|
self.btnNotGuilty.hide()
|
|
self.btnGuilty.hide()
|
|
|
|
def onShownameChanged(self, text):
|
|
self.showname = str(text.toUtf8())
|
|
|
|
def onMusicItemClicked(self, item):
|
|
self.actMusicCopy.setDisabled(False)
|
|
# self.actMusicAdd.setDisabled(False)
|
|
# self.actMusicEdit.setDisabled(False)
|
|
# self.actMusicDelete.setDisabled(False)
|
|
|
|
def onMusicItemDoubleClicked(self, item):
|
|
self.pickedMusicItem = True
|
|
if self.privateMusicSelected:
|
|
self.sendOOCchat(self.OOCNameInput.text().toUtf8(), "/play " + self.musicListPrivate[item.text()])
|
|
else:
|
|
self.sendMC(self.musicList[item.text()])
|
|
|
|
def onMusicSearch(self, text):
|
|
self.musicItems.clear()
|
|
self.actMusicCopy.setDisabled(True)
|
|
# self.actMusicAdd.setDisabled(True)
|
|
# self.actMusicEdit.setDisabled(True)
|
|
# self.actMusicDelete.setDisabled(True)
|
|
|
|
_musicList = self.musicListPrivate if self.privateMusicSelected else self.musicList
|
|
|
|
if text:
|
|
for song, fname in _musicList.items():
|
|
if QtCore.QString(fname).contains(text, QtCore.Qt.CaseInsensitive):
|
|
songitem = QtGui.QListWidgetItem()
|
|
songitem.setText(song)
|
|
if not self.privateMusicSelected and exists(unicode(AOpath + 'sounds/music/' + decodeAOString(fname).lower())):
|
|
songitem.setBackgroundColor(QtGui.QColor(self.foundSongItemColor))
|
|
self.musicItems.addItem(songitem)
|
|
else:
|
|
self.loadAllMusic()
|
|
|
|
def onMusicOptionsClicked(self):
|
|
self.mnuMusicOptions.exec_(QtGui.QCursor.pos())
|
|
|
|
def onActMusicRandomTriggered(self):
|
|
_musicList = self.musicListPrivate if self.privateMusicSelected else self.musicList
|
|
choice = random.randint(0, self.musicItems.count() - 1)
|
|
_item = self.musicItems.item(choice)
|
|
self.musicItems.setCurrentItem(_item)
|
|
self.actMusicCopy.setDisabled(False)
|
|
# self.actMusicAdd.setDisabled(False)
|
|
# self.actMusicEdit.setDisabled(False)
|
|
# self.actMusicDelete.setDisabled(False)
|
|
self.onMusicItemDoubleClicked(_item)
|
|
|
|
def onActMusicCopyTriggered(self):
|
|
QtGui.QApplication.clipboard().setText(self.musicItems.currentItem().text())
|
|
|
|
def onActMusicAddTriggered(self):
|
|
pass
|
|
|
|
def onActMusicEditTriggered(self):
|
|
pass
|
|
|
|
def onActMusicDeleteTriggered(self):
|
|
pass
|
|
|
|
def onActMusicRefreshTriggered(self, init=False):
|
|
if exists(AO2XPpath + "music.ini"):
|
|
self.musicListPrivate = ini.sectionless_ini_to_dict(AO2XPpath + "music.ini", QtCore.QString)
|
|
if not init:
|
|
self.musicItems.clear()
|
|
self.loadAllMusic()
|
|
|
|
def onActMusicSwitchTriggered(self):
|
|
self.privateMusicSelected = not self.privateMusicSelected
|
|
|
|
self.actMusicSeparator.setVisible(self.privateMusicSelected)
|
|
self.actMusicAdd.setVisible(self.privateMusicSelected)
|
|
self.actMusicEdit.setVisible(self.privateMusicSelected)
|
|
self.actMusicDelete.setVisible(self.privateMusicSelected)
|
|
self.actMusicRefresh.setVisible(self.privateMusicSelected)
|
|
self.actMusicSwitchToGlobal.setVisible(self.privateMusicSelected)
|
|
self.actMusicSwitchToPrivate.setVisible(not self.privateMusicSelected)
|
|
|
|
self.musicItems.clear()
|
|
self.musicSearch.blockSignals(True)
|
|
self.musicSearch.clear()
|
|
self.musicSearch.blockSignals(False)
|
|
self.actMusicCopy.setDisabled(True)
|
|
self.loadAllMusic()
|
|
|
|
def loadBackgrounds(self):
|
|
self.backgroundItems.clear()
|
|
self.backgroundSearch.clear()
|
|
self.backgrounds = []
|
|
for folder in os.listdir(unicode(AOpath + 'background')):
|
|
self.backgrounds.append(folder)
|
|
self.backgroundItems.addItem(folder)
|
|
|
|
def onBackgroundSearch(self, text):
|
|
self.backgroundItems.clear()
|
|
|
|
if text:
|
|
for bg in self.backgrounds:
|
|
if QtCore.QString(bg).contains(text, QtCore.Qt.CaseInsensitive):
|
|
self.backgroundItems.addItem(bg)
|
|
else:
|
|
for bg in self.backgrounds:
|
|
self.backgroundItems.addItem(bg)
|
|
|
|
def setEvidenceImage(self, guiobj, image, scale=False):
|
|
if exists(AOpath + 'evidence/' + image):
|
|
img = QtGui.QPixmap(AOpath + "evidence/%s" % image)
|
|
if not img.isNull() and scale:
|
|
guiobj.setPixmap(img.scaled(140, 140, QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.FastTransformation))
|
|
else:
|
|
guiobj.setPixmap(img)
|
|
else:
|
|
img = QtGui.QPixmap(AO2XPpath + 'themes/default/evidence_selected.png')
|
|
if not img.isNull() and scale:
|
|
guiobj.setPixmap(img.scaled(140, 140, QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.FastTransformation))
|
|
else:
|
|
guiobj.setPixmap(img)
|
|
|
|
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(downloadThread, (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 onMuteClicked(self):
|
|
if self.unmutedList.count() == 0:
|
|
return QtGui.QMessageBox.information(self, 'No character selected', 'There are no characters to mute.')
|
|
if self.muteSelected == -1:
|
|
return QtGui.QMessageBox.information(self, 'No character selected', 'To mute a character, select their name from the list to the left, then click 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 onUnmuteClicked(self):
|
|
if self.mutedList.count() == 0:
|
|
return QtGui.QMessageBox.information(self, 'No character selected', "There are no characters to unmute.")
|
|
if self.unmuteSelected == -1:
|
|
return QtGui.QMessageBox.information(self, 'No character selected', 'To unmute a character, select their name from the list to the right, then click 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 onPenaltyBarMinusClicked(self, barType):
|
|
netmsg = 'HP#' + str(barType) + '#'
|
|
if barType == 1:
|
|
if self.btnDefenseBar.getHealth() <= 0:
|
|
return
|
|
netmsg += str(self.btnDefenseBar.getHealth() - 1) + '#'
|
|
elif barType == 2:
|
|
if self.btnProsecutionBar.getHealth() <= 0:
|
|
return
|
|
netmsg += str(self.btnProsecutionBar.getHealth() - 1) + '#'
|
|
netmsg += '%'
|
|
self.tcp.send(netmsg)
|
|
self.ICChatFocus()
|
|
|
|
def onPenaltyBarPlusClicked(self, barType):
|
|
netmsg = 'HP#' + str(barType) + '#'
|
|
if barType == 1:
|
|
if self.btnDefenseBar.getHealth() >= 10:
|
|
return
|
|
netmsg += str(self.btnDefenseBar.getHealth() + 1) + '#'
|
|
elif barType == 2:
|
|
if self.btnProsecutionBar.getHealth() >= 10:
|
|
return
|
|
netmsg += str(self.btnProsecutionBar.getHealth() + 1) + '#'
|
|
netmsg += '%'
|
|
self.tcp.send(netmsg)
|
|
self.ICChatFocus()
|
|
|
|
def setWhiteFlash(self, on, realizationtype=0, msec=0):
|
|
self.whiteFlashLabel.setVisible(on)
|
|
if realizationtype == 1:
|
|
self.playRealization(f)
|
|
if msec:
|
|
self.whiteFlash.start(msec)
|
|
|
|
def setScreenShake(self, on, amount=20):
|
|
self.shakesRemaining = amount if on else 1
|
|
self.screenshake.start(25)
|
|
|
|
def OnWTCEButtonClicked(self, type, variant):
|
|
if type != 2:
|
|
self.tcp.send('RT#testimony' + str(type + 1) + '#%')
|
|
else:
|
|
self.tcp.send("RT#judgeruling#" +str(variant)+ "#%")
|
|
self.ICChatFocus()
|
|
|
|
def onPVPacket(self, charName=""):
|
|
self.gamewindow.setFixedSize(self.width, self.height)
|
|
self.gamewindow.center()
|
|
if not self.swapping and charName:
|
|
self.loadCharacter(charName)
|
|
|
|
def loadCharacter(self, charName):
|
|
self.queueItems.clear()
|
|
self.messageQueue = []
|
|
self.boxEffects.clear()
|
|
self.boxEmotes.clear()
|
|
self.charEmotes = []
|
|
self.selectedEmote = 0
|
|
self.currentEmotePage = 0
|
|
|
|
self.swapping = False
|
|
self.lblIniSwapInfo.setText('Not swapped')
|
|
|
|
effectsList = ini.get_effects(charName)
|
|
self.boxEffects.setVisible(bool(effectsList))
|
|
if effectsList:
|
|
effectsList.insert(0, "No effect")
|
|
self.boxEffects.addItems(effectsList)
|
|
|
|
if isinstance(charName, str):
|
|
charName = unicode(charName.lower())
|
|
elif isinstance(charName, QtCore.QString):
|
|
charName = unicode(charName.toLower())
|
|
|
|
#self.charName = ini.read_ini(AOpath + 'characters/' + charName + '/char.ini', "options", "name", charName.decode('utf-8').lower()
|
|
self.charName = charName # Just use the folder name
|
|
|
|
self.charShowname = ini.read_ini(AOpath + 'characters/' + charName + '/char.ini', "options", "showname")
|
|
if not self.charShowname == "":
|
|
self.charShowname = self.charShowname.decode('utf-8')
|
|
self.charSide = ini.read_ini(AOpath + 'characters/' + charName + '/char.ini', "options", "side", "def")
|
|
|
|
self.boxPositions.setCurrentIndex(self.boxPositions.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('#')
|
|
deskmod = emoteList.pop(len(emoteList) - 1)
|
|
|
|
emoteList.append(sound)
|
|
emoteList.append(soundt)
|
|
emoteList.append(soundl) # AO 2.8
|
|
emoteList.append(deskmod)
|
|
self.charEmotes.append(emoteList)
|
|
if emoteList[0]:
|
|
self.boxEmotes.addItem(emoteList[0])
|
|
else:
|
|
self.boxEmotes.addItem(emoteList[1] + ' ' + emoteList[2])
|
|
|
|
self.boxEmotes.setCurrentIndex(0)
|
|
self.setEmotePage()
|
|
|
|
def setEmotePage(self):
|
|
if self.myChar < 0:
|
|
return
|
|
self.btnPrevEmotePage.hide()
|
|
self.btnNextEmotePage.hide()
|
|
|
|
totalEmotes = len(self.charEmotes)
|
|
|
|
for button in self.emoteButtons:
|
|
button.hide()
|
|
|
|
if not totalEmotes:
|
|
print "[client] The selected character appears to have no emotions defined"
|
|
return
|
|
|
|
totalPages = totalEmotes / self.maxEmotesOnPage
|
|
emotesOnPage = 0
|
|
if totalEmotes % self.maxEmotesOnPage != 0:
|
|
totalPages += 1
|
|
if totalPages > self.currentEmotePage + 1:
|
|
emotesOnPage = self.maxEmotesOnPage
|
|
else:
|
|
emotesOnPage = totalEmotes % self.maxEmotesOnPage
|
|
else:
|
|
emotesOnPage = self.maxEmotesOnPage
|
|
if totalPages > self.currentEmotePage + 1:
|
|
self.btnNextEmotePage.show()
|
|
if self.currentEmotePage > 0:
|
|
self.btnPrevEmotePage.show()
|
|
for nEmote in range(emotesOnPage):
|
|
nRealEmote = nEmote + self.currentEmotePage * self.maxEmotesOnPage
|
|
if nRealEmote == self.selectedEmote:
|
|
image = QtGui.QPixmap(AOpath + 'characters/' + self.charName + '/emotions/button' + str(nRealEmote + 1) + '_on.png')
|
|
else:
|
|
image = QtGui.QPixmap(AOpath + 'characters/' + self.charName + '/emotions/button' + str(nRealEmote + 1) + '_off.png')
|
|
|
|
if not image.isNull() and not image.width() == 40:
|
|
self.emoteButtons[nEmote].setPixmap(image.scaled(40, 40, QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation))
|
|
else:
|
|
self.emoteButtons[nEmote].setPixmap(image)
|
|
|
|
self.emoteButtons[nEmote].show()
|
|
self.emoteButtons[nEmote].setToolTip(self.charEmotes[nEmote + self.currentEmotePage * self.maxEmotesOnPage][0])
|
|
|
|
def iniSwapIndexChange(self, ind):
|
|
self.iniSwapIndex = ind
|
|
|
|
def loadSwapCharacters(self):
|
|
self.charsFolder = []
|
|
self.iniSwapList.clear()
|
|
for folder in os.listdir(unicode(AOpath + 'characters')):
|
|
if exists(AOpath + 'characters/' + folder + '/char.ini'):
|
|
self.charsFolder.append(folder)
|
|
self.iniSwapList.addItem(folder)
|
|
|
|
def onIniSwapConfirmClicked(self):
|
|
if self.charList[self.myChar][0].lower() == self.charsFolder[self.iniSwapIndex].lower():
|
|
self.resetIniSwap()
|
|
else:
|
|
self.swapping = True
|
|
self.lblIniSwapInfo.setText('Swapped to ' + self.charsFolder[self.iniSwapIndex])
|
|
self.loadCharacter(self.charsFolder[self.iniSwapIndex])
|
|
|
|
def resetIniSwap(self):
|
|
self.swapping = False
|
|
self.lblIniSwapInfo.setText('Not swapped')
|
|
self.loadCharacter(self.charList[self.myChar][0])
|
|
|
|
def onAddEvidence(self):
|
|
self.evidenceEditor.show()
|
|
|
|
def onEditEvidence(self):
|
|
if not self.evidence:
|
|
return QtGui.QMessageBox.information(self, 'No evidence', "There's no evidence on the court record.")
|
|
self.evidenceEditor.editEvidence(self.selectedEvidence)
|
|
|
|
def onDeleteEvidence(self):
|
|
if self.selectedEvidence == -1:
|
|
return
|
|
|
|
if not self.privateInventorySelected:
|
|
if self.evidence:
|
|
self.tcp.send('DE#' + str(self.selectedEvidence) + '#%')
|
|
else:
|
|
self.tcp.send('DE#0#%')
|
|
elif len(self.privateEvidence):
|
|
del self.privateEvidence[self.selectedEvidence]
|
|
self.boxPrivateEvidence.removeItem(self.selectedEvidence)
|
|
|
|
def onExportEvidence(self, isAutosave=False):
|
|
if not exists("evidence"):
|
|
os.mkdir("evidence")
|
|
|
|
path = unicode(QtGui.QFileDialog.getSaveFileName(self, "Save evidence", "evidence", "Evidence (*.ini)")) if not isAutosave else "evidence/inventory.ini"
|
|
|
|
if path:
|
|
evidence = self.evidence if not self.privateInventorySelected else self.privateEvidence
|
|
inifile = ConfigParser()
|
|
for i in range(len(evidence)):
|
|
evi = evidence[i]
|
|
id = str(i)
|
|
inifile.add_section(id)
|
|
if isinstance(evi[0], QtCore.QString):
|
|
inifile.set(id, "name", evi[0].replace('\n', '\\n').toUtf8())
|
|
else:
|
|
inifile.set(id, "name", evi[0].replace('\n', '\\n').encode('utf-8'))
|
|
|
|
if isinstance(evi[1], QtCore.QString):
|
|
inifile.set(id, "description", evi[1].replace('\n', '\\n').toUtf8())
|
|
else:
|
|
inifile.set(id, "description", evi[1].replace('\n', '\\n').encode('utf-8'))
|
|
|
|
if isinstance(evi[2], QtCore.QString):
|
|
inifile.set(id, "image", evi[2].toUtf8())
|
|
else:
|
|
inifile.set(id, "image", evi[2].encode('utf-8'))
|
|
|
|
with open(path, "wb") as f:
|
|
inifile.write(f)
|
|
|
|
def onImportEvidence(self, is_autoload=False):
|
|
if not is_autoload:
|
|
if not self.privateInventorySelected:
|
|
if QtGui.QMessageBox.warning(self, "Import evidence", 'This will OVERWRITE the global evidence server-side.\n\nContinue?', QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) == QtGui.QMessageBox.No:
|
|
return
|
|
else:
|
|
if QtGui.QMessageBox.warning(self, "Import evidence", 'This will OVERWRITE your private evidence.\n\nContinue?', QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) == QtGui.QMessageBox.No:
|
|
return
|
|
|
|
if not exists("evidence"):
|
|
os.mkdir("evidence")
|
|
|
|
path = unicode(QtGui.QFileDialog.getOpenFileName(self, "Load evidence", "evidence", "Evidence (*.ini)")) if not is_autoload else "evidence/inventory.ini"
|
|
if path and exists(path):
|
|
evidence = []
|
|
inifile = ConfigParser()
|
|
inifile.read(path)
|
|
for section in inifile.sections():
|
|
name = ini.read_ini(inifile, section, "name").replace('\\n', '\n').replace('\\"', '"').rstrip()
|
|
description = ini.read_ini(inifile, section, "description").replace('\\n', '\n').replace('\\"', '"').rstrip()
|
|
image = ini.read_ini(inifile, section, "image", "empty.png")
|
|
|
|
# Remove opening and closing quotes
|
|
if description[0] == '"' and description[-1] == '"':
|
|
description = description[1:-1]
|
|
|
|
# Not all evidence files are plain unicode
|
|
name = name.decode("unicode_escape") if "\\x" in name else name.decode('utf-8')
|
|
description = description.decode("unicode_escape") if "\\x" in description else description.decode('utf-8')
|
|
|
|
evidence.append([name, description, image])
|
|
|
|
if self.privateInventorySelected or is_autoload:
|
|
dropdown = self.boxPrivateEvidence
|
|
self.privateEvidence = evidence
|
|
if dropdown.count() > 0:
|
|
dropdown.clear()
|
|
|
|
if evidence:
|
|
for evi in evidence:
|
|
dropdown.addItem(evi[0])
|
|
dropdown.setCurrentIndex(self.selectedEvidence)
|
|
|
|
if not is_autoload:
|
|
self.onExportEvidence(True)
|
|
elif evidence:
|
|
if self.evidence:
|
|
for i in range(len(self.evidence)):
|
|
self.tcp.send('DE#' + str(self.selectedEvidence) + '#%')
|
|
for evi in evidence:
|
|
self.tcp.send('PE#' + evi[0] + '#' + evi[1] + '#' + evi[2] + '#%')
|
|
|
|
def onTransferEvidence(self):
|
|
if self.privateInventorySelected:
|
|
evi = self.privateEvidence[self.selectedEvidence]
|
|
target = self.evidence
|
|
target_str = "global"
|
|
else:
|
|
evi = self.evidence[self.selectedEvidence]
|
|
target = self.privateEvidence
|
|
target_str = "private"
|
|
|
|
if evi in target:
|
|
return QtGui.QMessageBox.information(self, "Can't transfer evidence", 'The evidence "%s" already exists in the %s inventory.' % (evi[0], target_str))
|
|
else:
|
|
if self.privateInventorySelected:
|
|
for i in range(len(evi)):
|
|
evi[i] = encodeAOString(evi[i]).replace('\\n', '\n')
|
|
self.tcp.send('PE#' + evi[0] + '#' + evi[1] + '#' + evi[2] + '#%')
|
|
else:
|
|
self.privateEvidence.append(evi)
|
|
self.boxPrivateEvidence.addItem(evi[0])
|
|
|
|
def onTransferAllEvidence(self):
|
|
fail = []
|
|
|
|
if self.privateInventorySelected:
|
|
evi = self.privateEvidence[self.selectedEvidence]
|
|
origin = self.privateEvidence
|
|
target = self.evidence
|
|
target_str = "global"
|
|
else:
|
|
evi = self.evidence[self.selectedEvidence]
|
|
origin = self.evidence
|
|
target = self.privateEvidence
|
|
target_str = "private"
|
|
|
|
for evi in origin:
|
|
if evi in target:
|
|
fail.append(evi[0])
|
|
else:
|
|
if self.privateInventorySelected:
|
|
self.tcp.send('PE#' + evi[0] + '#' + evi[1] + '#' + evi[2] + '#%')
|
|
else:
|
|
self.privateEvidence.append(evi)
|
|
self.boxPrivateEvidence.addItem(evi[0])
|
|
|
|
if fail:
|
|
return QtGui.QMessageBox.information(self, "Some evidence wasn't transferred", "The following evidence already exists in the %s inventory:\n\n%s." % (target_str, ", ".join(fail)))
|
|
|
|
def onSwitchInventory(self, reset=False):
|
|
self.privateInventorySelected = not self.privateInventorySelected
|
|
if self.privateInventorySelected and not reset:
|
|
self.present = False
|
|
self.btnEvidencePresent.setPixmap(self.btnEvidencePresent.button_off)
|
|
self.btnEvidencePresent.hide()
|
|
self.boxEvidence.hide()
|
|
self.boxPrivateEvidence.show()
|
|
self.btnEvidenceMoveToGlobal.show()
|
|
self.btnEvidenceMoveAllToGlobal.show()
|
|
self.btnEvidenceSwitchToGlobal.show()
|
|
self.btnEvidenceMoveToPrivate.hide()
|
|
self.btnEvidenceMoveAllToPrivate.hide()
|
|
self.btnEvidenceSwitchToPrivate.hide()
|
|
self.boxPrivateEvidence.setCurrentIndex(0)
|
|
self.changeEvidence(0, 1)
|
|
else:
|
|
self.btnEvidencePresent.show()
|
|
self.boxEvidence.show()
|
|
self.boxPrivateEvidence.hide()
|
|
self.btnEvidenceMoveToGlobal.hide()
|
|
self.btnEvidenceMoveAllToGlobal.hide()
|
|
self.btnEvidenceSwitchToGlobal.hide()
|
|
self.btnEvidenceMoveToPrivate.show()
|
|
self.btnEvidenceMoveAllToPrivate.show()
|
|
self.btnEvidenceSwitchToPrivate.show()
|
|
self.boxEvidence.setCurrentIndex(0)
|
|
self.changeEvidence(0, 0)
|
|
|
|
def onCallModClicked(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 onChangeCharClicked(self):
|
|
if self.demoPlaying:
|
|
self.inboxTimer.stop()
|
|
self.chatTickTimer.stop()
|
|
self.disconnectCommon()
|
|
self.gamewindow.returnToMenu()
|
|
else:
|
|
self.charSelect.showCharSelect()
|
|
|
|
def changeFlipCheck(self, on):
|
|
if on == 2:
|
|
on = 1
|
|
self.myFlip = on
|
|
self.ICChatFocus()
|
|
|
|
def changePreanimCheck(self, on):
|
|
if on == 2:
|
|
on = 1
|
|
self.playPreanim = on
|
|
self.cbNoInterrupt.setDisabled(not on)
|
|
if on == 0:
|
|
self.cbNoInterrupt.setChecked(False)
|
|
self.ICChatFocus()
|
|
|
|
def onMessageQueueItemClicked(self, item):
|
|
for i in range(len(self.queueItems)):
|
|
if self.queueItems.item(i) == item:
|
|
self.selectedMessage = i
|
|
|
|
def onRemoveQueueClicked(self):
|
|
if len(self.queueItems) == 0:
|
|
return QtGui.QMessageBox.information(self, "No messages in queue", 'Enter a message on the game chat to add one.')
|
|
if self.selectedMessage == -1:
|
|
return QtGui.QMessageBox.information(self, 'No message selected', 'Select a message from the list to remove it.')
|
|
self.queueItems.takeItem(self.selectedMessage)
|
|
del self.messageQueue[self.selectedMessage]
|
|
|
|
def onClearQueueClicked(self):
|
|
return QtGui.QMessageBox.information(self, "Clear queue", 'Not implemented.')
|
|
|
|
def onPlayerItemClicked(self, item):
|
|
for i in range(len(self.playerItems)):
|
|
sel = self.playerItems.item(i)
|
|
if sel == item:
|
|
s = sel.text()
|
|
self.selectedPlayer = s[1:s.indexOf("]")]
|
|
|
|
def onPlayerPairClicked(self):
|
|
if not self.selectedPlayer == -1:
|
|
self.gameTabs.setCurrentWidget(self.tabPair)
|
|
self.cbPair.setChecked(True)
|
|
char = self.playerList[str(self.selectedPlayer)][1]
|
|
if char == '':
|
|
return QtGui.QMessageBox.information(self, "Unable to pair", 'That player has no character selected.')
|
|
else:
|
|
self.boxPair.setCurrentIndex([c[0] for c in self.charList].index(char))
|
|
else:
|
|
return QtGui.QMessageBox.information(self, 'No player selected', 'Select a player from the list to attempt pairing.')
|
|
|
|
def onPlayerKickClicked(self):
|
|
if not self.selectedPlayer == -1:
|
|
reason, ok = QtGui.QInputDialog.getText(self, "Kick a player", "Please enter the reason.", text="Being annoying")
|
|
if reason and ok:
|
|
self.tcp.send("MA#%s#0#%s#%%" % (self.selectedPlayer, reason))
|
|
else:
|
|
return QtGui.QMessageBox.information(self, 'No player selected', 'Select a player from the list to kick.')
|
|
|
|
def onPlayerBanClicked(self):
|
|
if not self.selectedPlayer == -1:
|
|
reason, ok = QtGui.QInputDialog.getText(self, "Ban a player", "Please enter the reason.", text="Being annoying")
|
|
if reason and ok:
|
|
duration, ok = QtGui.QInputDialog.getInt(self, "Ban a player", "Please enter the ban length in minutes.", 60, 1)
|
|
if duration and ok:
|
|
self.tcp.send("MA#%s#%s#%s#%%" % (self.selectedPlayer, duration, reason))
|
|
else:
|
|
return QtGui.QMessageBox.information(self, 'No player selected', 'Select a player from the list to ban.')
|
|
|
|
def changeEvidence(self, ind, kind):
|
|
if ind < 0:
|
|
return
|
|
if self.privateInventorySelected:
|
|
if not kind == 1:
|
|
return
|
|
evi = self.privateEvidence
|
|
else:
|
|
if not kind == 0:
|
|
return
|
|
evi = self.evidence
|
|
|
|
self.selectedEvidence = ind
|
|
if len(evi) > 0:
|
|
self.evidenceDescription.setText(evi[ind][1])
|
|
self.setEvidenceImage(self.evidenceImage, evi[ind][2])
|
|
|
|
def changeGlobalEvidence(self, ind):
|
|
self.changeEvidence(ind, 0)
|
|
|
|
def changePrivateEvidence(self, ind):
|
|
self.changeEvidence(ind, 1)
|
|
|
|
def changeEmote(self, dropdown, ind):
|
|
if ind == -1:
|
|
return
|
|
|
|
self.ICChatFocus()
|
|
if not dropdown:
|
|
self.selectedEmote = ind + self.currentEmotePage * self.maxEmotesOnPage
|
|
else:
|
|
self.selectedEmote = ind
|
|
for button in self.emoteButtons:
|
|
if button.emoteid == ind:
|
|
button.path = AOpath + 'characters/' + self.charName + '/emotions/button' + str(button.emoteid + self.currentEmotePage * self.maxEmotesOnPage + 1)
|
|
image = QtGui.QPixmap(button.path + '_on.png')
|
|
else:
|
|
image = QtGui.QPixmap(AOpath + 'characters/' + self.charName + '/emotions/button' + str(button.emoteid + self.currentEmotePage * self.maxEmotesOnPage + 1) + '_off.png')
|
|
|
|
if not image.isNull() and not image.width() == 40:
|
|
button.setPixmap(image.scaled(40, 40, QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation))
|
|
else:
|
|
button.setPixmap(image)
|
|
|
|
def setChatColor(self, ind):
|
|
self.myChatColor = ind
|
|
self.ICChatFocus()
|
|
|
|
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.disconnectCommon()
|
|
self.gamewindow.returnToMenu()
|
|
|
|
def disconnectCommon(self):
|
|
self.onSwitchInventory(True)
|
|
self.selectedPlayer = -1
|
|
self.playerItems.clear()
|
|
self.btnPlayerKick.setDisabled(True)
|
|
self.btnPlayerBan.setDisabled(True)
|
|
self.OOCLogin.setText("Lo&gin")
|
|
self.login = False
|
|
self.privateInventorySelected = False
|
|
if self.tcp:
|
|
self.tcp.close()
|
|
if self.demoPlayer:
|
|
self.demoPlayer.stop()
|
|
self.demoPlayer = None
|
|
self.demoRecorder = None
|
|
self.demoPlaying = False
|
|
self.stopMusic()
|
|
|
|
def onAreaItemClicked(self, item):
|
|
area = item.text().split('\n')[0]
|
|
self.sendMC(area)
|
|
|
|
def onBackgroundItemClicked(self, item):
|
|
self.sendOOCchat(self.OOCNameInput.text().toUtf8(), "/bg " + item.text())
|
|
|
|
def sendMC(self, content):
|
|
if "cccc_ic_support" in self.features and self.showname:
|
|
self.tcp.send('MC#' + content + '#' + str(self.myChar) + '#' + self.showname.decode('utf-8') + '#%')
|
|
else:
|
|
self.tcp.send('MC#' + content + '#' + str(self.myChar) + '#%')
|
|
|
|
def onICLogChanged(self):
|
|
if self.ICLog.verticalScrollBar().value() == self.ICLog.verticalScrollBar().maximum(): self.ICLog.verticalScrollBar().setValue(self.ICLog.verticalScrollBar().maximum())
|
|
|
|
def onOOCLogChanged(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 = encodeAOString(self.OOCInput.text()).replace('\\n', '\n')
|
|
if text.startsWith('//'):
|
|
code = str(self.OOCInput.text()).replace('//', '', 1).replace('\\NEWLINE', '\n')
|
|
try:
|
|
exec code
|
|
except Exception as e:
|
|
msg = 'code error\n'
|
|
for arg in e.args:
|
|
msg += str(arg) + '\n'
|
|
|
|
msg = msg.rstrip()
|
|
self.OOCLog.append(msg)
|
|
return
|
|
return
|
|
elif text.startsWith("/pos "): # why.......
|
|
ind = self.boxPositions.findText(str(text.split(" ")[1]))
|
|
if ind >= 0: self.boxPositions.setCurrentIndex(ind)
|
|
self.OOCInput.clear()
|
|
return
|
|
|
|
if self.cbMockText.isChecked():
|
|
text = mockString(text)
|
|
if self.cbAutoCaps.isChecked():
|
|
l = QtCore.QStringList(list(text))
|
|
l[0] = l[0].toUpper()
|
|
|
|
last = [".", "?", "!", ")", "]"]
|
|
if not l[-1] in last:
|
|
l.append(".")
|
|
text = l.join("").replace(" i ", " I ").replace("i'm", "I'm").replace("it's", "It's")
|
|
if self.cbSpacing.isChecked():
|
|
l = QtCore.QStringList(list(text))
|
|
for i in range(1, len(l)+len(l)-1, 2):
|
|
l.insert(i, " ")
|
|
text = l.join("")
|
|
|
|
self.sendOOCchat(self.OOCNameInput.text().toUtf8(), text)
|
|
self.OOCInput.clear()
|
|
|
|
def onICReturn(self):
|
|
text = encodeAOString(unicode(self.ICChatInput.text())) #.replace('/n', '\n')
|
|
|
|
if text:
|
|
if self.cbMockText.isChecked():
|
|
text = mockString(text)
|
|
if self.cbAutoCaps.isChecked():
|
|
l = list(text)
|
|
if l[0] == " " and len(l) > 1:
|
|
l[1] = l[1].upper()
|
|
else:
|
|
l[0] = l[0].upper()
|
|
last = [".", "?", "!", "<", ">", ")", "]"]
|
|
if not l[-1] in last:
|
|
l.append(".")
|
|
text = "".join(l).replace(" i ", " I ").replace("i'm", "I'm").replace("it's", "It's")
|
|
if self.cbSpacing.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.cbNoInterrupt.isChecked():
|
|
modifier = 0
|
|
else:
|
|
modifier = self.playPreanim
|
|
objection = 0
|
|
if self.btnCustomObjection.isPressed():
|
|
objection = 4
|
|
self.btnCustomObjection.setPressed(False)
|
|
elif self.btnHoldIt.isPressed():
|
|
objection = 1
|
|
self.btnHoldIt.setPressed(False)
|
|
elif self.btnObjection.isPressed():
|
|
objection = 2
|
|
self.btnObjection.setPressed(False)
|
|
elif self.btnTakeThat.isPressed():
|
|
objection = 3
|
|
self.btnTakeThat.setPressed(False)
|
|
if emote[3] == '5': #zoom
|
|
if self.cbNoInterrupt.isChecked():
|
|
modifier = 5
|
|
else:
|
|
if objection > 0:
|
|
modifier = 6
|
|
else:
|
|
modifier = 5
|
|
elif objection > 0:
|
|
if self.cbNoInterrupt.isChecked():
|
|
modifier = 0
|
|
else:
|
|
modifier = 2
|
|
|
|
msg = u"MS#"
|
|
|
|
# Visible desk modifier
|
|
if "deskmod" in self.features:
|
|
if emote[3] == '5': # Zoom forcibly hides the desk
|
|
msg += "0#"
|
|
elif len(emote) > 7 and emote[7]: # Respect deskmod if found
|
|
msg += "%s#" % str(emote[7])
|
|
else:
|
|
msg += "%d#" % self.cbBench.isChecked()
|
|
else:
|
|
msg += "chat#"
|
|
|
|
msg += emote[1]+"#" #pre-anim
|
|
msg += self.charName.title() + "#"
|
|
msg += emote[2]+"#" #anim
|
|
msg += text+"#"
|
|
msg += self.charSide+"#"
|
|
msg += (ini.get_effect_sound(self.boxEffects.currentText(), self.charName) if self.boxEffects.currentIndex() > 0 else 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.selectedEvidence + 1) * int(self.present))+"#" #selected evidence
|
|
|
|
if self.present:
|
|
self.present = False
|
|
self.btnEvidencePresent.setPixmap(self.btnEvidencePresent.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.btnRealization.isPressed()))+"#"
|
|
msg += str(self.myChatColor)+"#"
|
|
|
|
if "cccc_ic_support" in self.features:
|
|
showname = self.showname.decode('utf-8')
|
|
if self.showname == "" and not self.charShowname == "":
|
|
showname = self.charShowname
|
|
msg += showname+"#" # custom showname
|
|
if self.cbPair.isChecked():
|
|
msg += str(self.boxPair.currentIndex()) # pair charID
|
|
if "effects" in self.features:
|
|
msg += "^%d#" % self.boxPairOrder.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.sliPairOffset.value()) + "&" + str(-self.sliPairOffsetY.value()) + "#"
|
|
else:
|
|
msg += str(self.sliPairOffset.value())+"#"
|
|
|
|
msg += str(int(self.cbNoInterrupt.isChecked()))+"#" # NoInterrupt(TM)
|
|
|
|
if "looping_sfx" in self.features: # AO 2.8
|
|
msg += emote[6]+"#" # loop sound?
|
|
msg += "%d#" % self.btnShake.isPressed() # screen shake
|
|
emotes_to_check = [emote[1], "(b)"+emote[2], "(b)/"+emote[2], "(a)"+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", True):
|
|
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.cbAdditive.isChecked()
|
|
|
|
if "effects" in self.features:
|
|
fx = self.boxEffects.currentText() if self.boxEffects.currentIndex() > 0 else ""
|
|
fxSound = ini.get_effect_sound(fx, self.charName)
|
|
p_effect = ini.read_ini(AOpath + "characters/"+self.charName+"/char.ini", "options", "effects")
|
|
msg += str(fx + "|" + p_effect + "|" + fxSound + "#").encode('utf-8')
|
|
self.boxEffects.setCurrentIndex(0)
|
|
|
|
# AO 2.10.2+
|
|
if "custom_blips" in self.features:
|
|
blip = ini.read_ini(AOpath + "characters/"+self.charName+"/char.ini", "options", "blips")
|
|
if not blip:
|
|
blip = ini.read_ini(AOpath + "characters/"+self.charName+"/char.ini", "options", "gender")
|
|
if blip:
|
|
msg += str(blip) + "#"
|
|
|
|
# Slides
|
|
msg += "%d#" % self.cbSlide.isChecked()
|
|
|
|
msg += "%"
|
|
|
|
self.queueItems.addItem(self.ICChatInput.text())
|
|
self.messageQueue.append(msg)
|
|
self.lastMessage = msg
|
|
|
|
self.ICChatInput.clear()
|
|
self.btnRealization.setPressed(False)
|
|
self.btnShake.setPressed(False)
|
|
|
|
def setBackground(self, bg=None, reset=False):
|
|
if bg is None:
|
|
bg = self.background
|
|
else:
|
|
self.background = 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() != self.viewport.width() or bgimg.size().height() != self.viewport.height():
|
|
setattr(self, bgfile[0], QtGui.QPixmap.fromImage(bgimg.scaled(self.viewport.width(), self.viewport.height(), QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.FastTransformation)))
|
|
else:
|
|
setattr(self, bgfile[0], QtGui.QPixmap.fromImage(bgimg))
|
|
else:
|
|
setattr(self, bgfile[0], QtGui.QPixmap.fromImage(bgimg))
|
|
|
|
court = AOpath + 'background/' + bg + '/court.png'
|
|
self.slideAvailable = exists(court)
|
|
|
|
if self.slideAvailable:
|
|
slide = QtGui.QPixmap(court)
|
|
slide_width = slide.width() * 2
|
|
|
|
self.slideBg.resize(slide_width, self.viewport.height())
|
|
self.slideBg.setPixmap(slide.scaled(slide.width() * 2, self.viewport.height(), QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.FastTransformation))
|
|
|
|
courtOverlay = AOpath + 'background/' + bg + '/courtOverlay.png'
|
|
if exists(courtOverlay):
|
|
slideOverlay = QtGui.QPixmap(courtOverlay)
|
|
self.slideOverlay.resize(slide_width, self.viewport.height())
|
|
self.slideOverlay.setPixmap(slideOverlay.scaled(slide.width() * 2, self.viewport.height(), QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.FastTransformation))
|
|
self.slideHasOverlay = True
|
|
else:
|
|
self.slideHasOverlay = False
|
|
|
|
self.bench.show()
|
|
|
|
if reset:
|
|
self.chatbox.hide()
|
|
self.char.hide()
|
|
self.setScene(True)
|
|
|
|
def startSlide(self, value = [0, 0]):
|
|
self.chatbox.hide()
|
|
self.presentedEvidence.hide()
|
|
|
|
slide_time = 500
|
|
self.bench.hide()
|
|
self.slideBg.show()
|
|
|
|
defPos = QtCore.QRect(0, 0, self.slideBg.width(), self.viewport.height())
|
|
proPos = QtCore.QRect(-(defPos.size().width() - self.viewport.width()), 0, defPos.size().width(), self.viewport.height())
|
|
witPos = QtCore.QRect(-(self.slideBg.width() / 2 - self.viewport.width() / 2), 0, self.slideBg.width(), self.viewport.height())
|
|
|
|
self.slideKind = value[0]
|
|
self.slideDirection = value[1]
|
|
|
|
# TODO: play only first frame of preanim, figure out zooms
|
|
scaling = getScaling(ini.read_ini(AOpath + 'characters/' + self.mChatMessage[CHARNAME] + '/char.ini', "options", "scaling").lower())
|
|
if self.mChatMessage[FLIP] == "1":
|
|
self.slideSpeaker.setFlipped(True)
|
|
else:
|
|
self.slideSpeaker.setFlipped(False)
|
|
|
|
self.slideSpeaker.playIdle(self.mChatMessage[CHARNAME], self.mChatMessage[ANIM], scaling)
|
|
self.slideSpeaker.show()
|
|
|
|
if self.slideKind == 0:
|
|
if self.slideLastWit:
|
|
self.slideWitness.playIdle(self.slideLastWit[0], self.slideLastWit[1], self.slideLastWit[2])
|
|
self.slideWitness.show()
|
|
if self.slideDirection == 0:
|
|
bg_start = defPos
|
|
bg_end = proPos
|
|
else:
|
|
bg_start = proPos
|
|
bg_end = defPos
|
|
elif self.slideKind == 1:
|
|
if self.slideDirection == 0:
|
|
bg_start = defPos
|
|
bg_end = witPos
|
|
else:
|
|
bg_start = witPos
|
|
bg_end = defPos
|
|
elif self.slideKind == 2:
|
|
if self.slideDirection == 0:
|
|
bg_start = witPos
|
|
bg_end = proPos
|
|
else:
|
|
bg_start = proPos
|
|
bg_end = witPos
|
|
|
|
self.slideBg.setGeometry(bg_start)
|
|
self.slideBgAnimation.setStartValue(bg_start)
|
|
self.slideBgAnimation.setEndValue(bg_end)
|
|
self.slideBgAnimation.setDuration(slide_time)
|
|
self.slideBgAnimation.setEasingCurve(QtCore.QEasingCurve.InOutQuad)
|
|
self.slideBgAnimation.start()
|
|
|
|
if self.slideHasOverlay:
|
|
self.slideOverlay.show()
|
|
self.slideOverlay.setGeometry(bg_start)
|
|
|
|
def slideChanged(self):
|
|
x = self.slideBg.x()
|
|
self.slideOverlay.move(x, 0)
|
|
|
|
# def-pro
|
|
if self.slideKind == 0:
|
|
if self.slideLastWit:
|
|
self.slideWitness.moveSlide(x + self.slideBg.width() / 2 - self.viewport.width() / 2)
|
|
if self.slideDirection == 0:
|
|
self.char.moveSlide(x)
|
|
self.slideSpeaker.moveSlide(x + self.slideBg.width() - self.viewport.width())
|
|
else:
|
|
self.char.moveSlide(x + self.slideBg.width() - self.viewport.width())
|
|
self.slideSpeaker.moveSlide(x)
|
|
# def-wit
|
|
elif self.slideKind == 1:
|
|
if self.slideDirection == 0:
|
|
self.char.moveSlide(x)
|
|
self.slideSpeaker.moveSlide(x + self.slideBg.width() / 2 - self.viewport.width() / 2)
|
|
else:
|
|
self.char.moveSlide(x + self.slideBg.width() / 2 - self.viewport.width() / 2)
|
|
self.slideSpeaker.moveSlide(x)
|
|
# pro-wit
|
|
elif self.slideKind == 2:
|
|
if self.slideDirection == 0:
|
|
self.char.moveSlide(x + self.slideBg.width() / 2 - self.viewport.width() / 2)
|
|
self.slideSpeaker.moveSlide(x + self.slideBg.width() - self.viewport.width())
|
|
else:
|
|
self.char.moveSlide(x + self.slideBg.width() - self.viewport.width())
|
|
self.slideSpeaker.moveSlide(x + self.slideBg.width() / 2 - self.viewport.width() / 2)
|
|
|
|
def slideDone(self):
|
|
self.slideBg.hide()
|
|
self.slideOverlay.hide()
|
|
self.slideWitness.hide()
|
|
self.slideSpeaker.hide()
|
|
self.handleChatMessage2()
|
|
|
|
def netmsgHP(self, type, health):
|
|
if type == 1:
|
|
self.btnDefenseBar.setHealth(health)
|
|
elif type == 2:
|
|
self.btnProsecutionBar.setHealth(health)
|
|
|
|
def netmsgMS(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
|
|
|
|
mChatMessage = {}
|
|
|
|
for n_string in range(self.chatMessageSize):
|
|
if n_string < len(p_contents) and (n_string < 16 or AO2chat):
|
|
mChatMessage[n_string] = p_contents[n_string]
|
|
else:
|
|
mChatMessage[n_string] = ""
|
|
|
|
# For debugging
|
|
# print mChatMessage
|
|
|
|
fCharId = int(mChatMessage[CHAR_ID])
|
|
|
|
if fCharId < 0 or fCharId >= len(self.charList):
|
|
return
|
|
|
|
fShowname = ""
|
|
if not mChatMessage[SHOWNAME]:
|
|
fShowname = mChatMessage[CHARNAME]
|
|
else:
|
|
fShowname = mChatMessage[SHOWNAME]
|
|
|
|
if self.messageQueue:
|
|
chatMsgComp = decodeAOString(self.messageQueue[0].split('#')[5])
|
|
examine = chatMsgComp == ">" or chatMsgComp == "<" or chatMsgComp == "="
|
|
special = not chatMsgComp or chatMsgComp.isspace()
|
|
if examine or (fCharId == self.myChar and (special or mChatMessage[CHATMSG] == chatMsgComp)): # our message showed up
|
|
del self.messageQueue[0]
|
|
self.queueItems.takeItem(0)
|
|
if self.cbAdditive.isChecked():
|
|
self.ICChatInput.insert(" ")
|
|
|
|
mChatMessage[CHARNAME] = mChatMessage[CHARNAME].decode("utf-8")
|
|
mChatMessage[OTHER_NAME] = mChatMessage[OTHER_NAME].decode("utf-8")
|
|
mChatMessage[SHOWNAME] = mChatMessage[SHOWNAME].decode('utf-8')
|
|
|
|
fChar = mChatMessage[CHARNAME]
|
|
evidence = int(mChatMessage[EVIDENCE])-1
|
|
|
|
# Some characters use " - " instead of "-" for no preanim.
|
|
mChatMessage[PREANIM] = mChatMessage[PREANIM].strip()
|
|
|
|
t = time.localtime()
|
|
logcharName = fChar
|
|
|
|
timestamp = "[%d:%.2d] " % (t[3], t[4]) if not self.demoPlaying else ""
|
|
|
|
if fChar.lower() != self.charList[fCharId][0].lower():
|
|
logcharName = self.charList[fCharId][0] + ' (' + fChar + ')'
|
|
|
|
chatmsg = mChatMessage[CHATMSG]
|
|
|
|
if mChatMessage[SHOWNAME] and mChatMessage[SHOWNAME].lower() != fChar.lower():
|
|
try:
|
|
logcharName += " (" + mChatMessage[SHOWNAME]+")"
|
|
except:
|
|
logcharName += " (???)"
|
|
|
|
if evidence == -1:
|
|
self.ICLog.append(timestamp + '%s: %s' % (logcharName, chatmsg))
|
|
else:
|
|
eviname = '(NULL) %d' % evidence
|
|
try:
|
|
eviname = self.evidence[evidence][0]
|
|
except:
|
|
pass
|
|
|
|
self.ICLog.append(timestamp + '%s: %s\n%s presented an evidence: %s' % (logcharName, chatmsg, fChar, eviname.strip()))
|
|
|
|
self.isAdditive = (mChatMessage[ADDITIVE] == "1")
|
|
|
|
customObjection = "custom"
|
|
try:
|
|
objectionMod = int(mChatMessage[SHOUT_MOD])
|
|
except:
|
|
if "4&" in mChatMessage[SHOUT_MOD]: # custom objection name
|
|
objectionMod = 4
|
|
customObjection = mChatMessage[SHOUT_MOD].split("4&")[1] # get the name
|
|
else: # just in case of mindfuckery
|
|
objectionMod = 0
|
|
|
|
if objectionMod <= 4 and objectionMod >= 1:
|
|
# Skip everything in the queue, show message immediately
|
|
self.inboxQueue = []
|
|
self.inboxQueue.append(mChatMessage)
|
|
self.inboxTimer.stop()
|
|
self.chatTickTimer.stop()
|
|
self.mChatMessage = mChatMessage
|
|
|
|
objections = ["holdit", "objection", "takethat", "custom_objections/"+customObjection if customObjection != "custom" else "custom"]
|
|
self.objectionView.stop()
|
|
self.objectionView.play(objections[objectionMod-1], fChar.lower())
|
|
self.presentedEvidence.hide()
|
|
self.playObjectionSound(fChar.lower(), objectionMod)
|
|
|
|
emoteMod = int(self.mChatMessage[EMOTE_MOD])
|
|
if emoteMod == 0:
|
|
self.mChatMessage[EMOTE_MOD] = 1
|
|
else:
|
|
# Old behavior
|
|
#self.mChatMessage = mChatMessage
|
|
#self.handleChatMessage2()
|
|
|
|
# Add message to queue and wait, unless queue empty
|
|
self.inboxQueue.append(mChatMessage)
|
|
if len(self.inboxQueue) == 1:
|
|
self.handleChatMessage1(mChatMessage)
|
|
|
|
def setTextColor(self):
|
|
textColor = int(self.mChatMessage[TEXT_COLOR])
|
|
|
|
isRainbow = textColor == C_RAINBOW
|
|
|
|
if textColor == 0:
|
|
color = QtGui.QColor(255, 255, 255)
|
|
elif textColor == 1:
|
|
color = QtGui.QColor(0, 255, 0)
|
|
elif textColor == 2:
|
|
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, 192, 203)
|
|
elif textColor == 7:
|
|
color = QtGui.QColor(0, 255, 255)
|
|
elif textColor == 8:
|
|
color = QtGui.QColor(200, 200, 200)
|
|
elif textColor == 10:
|
|
color = QtGui.QColor(0, 0, 0)
|
|
else:
|
|
color = QtGui.QColor(255, 255, 255)
|
|
|
|
if isRainbow:
|
|
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 setScene(self, init=False):
|
|
if not init:
|
|
side = self.mChatMessage[SIDE]
|
|
# TODO: support custom positions
|
|
if side not in self.defaultPositions:
|
|
side = 'wit'
|
|
else:
|
|
side = 'wit'
|
|
|
|
self.presentedEvidence.hide()
|
|
|
|
if side == 'def':
|
|
self.court.setPixmap(self.side_def)
|
|
self.bench.setPixmap(self.bench_def)
|
|
self.bench.move(0, self.viewport.height() - self.bench_def.size().height())
|
|
elif side == 'pro':
|
|
self.court.setPixmap(self.side_pro)
|
|
self.bench.setPixmap(self.bench_pro)
|
|
self.bench.move(self.viewport.width() - self.bench_pro.size().width(), self.viewport.height() - self.bench_pro.size().height())
|
|
elif side == 'wit':
|
|
self.court.setPixmap(self.side_wit)
|
|
self.bench.setPixmap(self.bench_wit)
|
|
self.bench.move(0, 0)
|
|
elif side == 'hld':
|
|
self.court.setPixmap(self.side_hld)
|
|
self.bench.setPixmap(self.bench_hld)
|
|
elif side == 'hlp':
|
|
self.court.setPixmap(self.side_hlp)
|
|
self.bench.setPixmap(self.bench_hlp)
|
|
elif side == 'jud':
|
|
self.court.setPixmap(self.side_jud)
|
|
self.bench.setPixmap(self.bench_jud)
|
|
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)
|
|
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)
|
|
|
|
def setBench(self, isPreanim = False):
|
|
if self.animIsEmpty:
|
|
return
|
|
|
|
deskmod = self.mChatMessage[DESK_MOD]
|
|
|
|
if deskmod == "0" or (deskmod == "chat" and side in ("jud", "hld", "hlp")):
|
|
self.bench.hide()
|
|
elif deskmod == "1" or (deskmod == "chat" and side in ("def", "pro", "wit")):
|
|
self.bench.show()
|
|
elif deskmod == "2" or deskmod == "4":
|
|
if isPreanim:
|
|
self.bench.hide()
|
|
else:
|
|
self.bench.show()
|
|
elif deskmod == "3" or deskmod == "5":
|
|
if isPreanim:
|
|
self.bench.show()
|
|
else:
|
|
self.bench.hide()
|
|
else:
|
|
self.bench.hide()
|
|
|
|
def objectionDone(self):
|
|
self.handleChatMessage1()
|
|
|
|
def handleChatMessage1(self, mChatMessage = None):
|
|
if not self.slideEnabled:
|
|
if mChatMessage:
|
|
self.mChatMessage = mChatMessage
|
|
self.handleChatMessage2()
|
|
return
|
|
|
|
wasZoom = self.mChatMessage[EMOTE_MOD] and int(self.mChatMessage[EMOTE_MOD]) >= 5
|
|
|
|
if mChatMessage:
|
|
self.mChatMessage = mChatMessage
|
|
|
|
newSide = self.mChatMessage[SIDE]
|
|
canSlide = self.slideAvailable and not wasZoom and int(self.mChatMessage[EMOTE_MOD]) < 5
|
|
|
|
if canSlide and self.mChatMessage[SLIDE] == "1" and self.slideLastPos and newSide != self.slideLastPos and newSide in ["def", "pro", "wit"]:
|
|
self.startSlide(self.slideMap[self.slideLastPos][newSide])
|
|
else:
|
|
self.handleChatMessage2()
|
|
|
|
def handleChatMessage2(self):
|
|
self.zoom.setZoom(False)
|
|
self.effectView.stop()
|
|
|
|
self.textState = 0
|
|
self.animState = 0
|
|
self.objectionView.stop()
|
|
self.chatTickTimer.stop()
|
|
self.presentedEvidence.hide()
|
|
|
|
self.chatMessageIsEmpty = self.mChatMessage[CHATMSG].strip() == ""
|
|
self.animIsEmpty = self.mChatMessage[ANIM].strip() == ""
|
|
|
|
if not self.animIsEmpty:
|
|
self.char.stop()
|
|
|
|
if not self.mChatMessage[SHOWNAME]:
|
|
self.name.setText(self.mChatMessage[CHARNAME])
|
|
else:
|
|
self.name.setText(self.mChatMessage[SHOWNAME])
|
|
|
|
self.chatbox.hide()
|
|
|
|
self.setScene()
|
|
self.setTextColor()
|
|
|
|
fMessage = self.mChatMessage[CHATMSG]
|
|
if len(fMessage) >= 2:
|
|
self.messageIsCentered = fMessage.startswith("~~")
|
|
else:
|
|
self.ao2text.setAlignment(QtCore.Qt.AlignLeft)
|
|
self.text.setAlignment(QtCore.Qt.AlignLeft)
|
|
|
|
if self.mChatMessage[FLIP] == "1":
|
|
self.char.setFlipped(True)
|
|
else:
|
|
self.char.setFlipped(False)
|
|
|
|
side = self.mChatMessage[SIDE]
|
|
emoteMod = int(self.mChatMessage[EMOTE_MOD])
|
|
no_preanim = not self.mChatMessage[PREANIM] or self.mChatMessage[PREANIM] == "-"
|
|
|
|
# AO 2.8: always offset player
|
|
hor_offset = vert_offset = 0
|
|
|
|
if "y_offset" in self.features: # AO 2.9
|
|
keyword = "<and>" if "<and>" in self.mChatMessage[SELF_OFFSET] else "&" # i don't think it's hdf's fault but this is still ridiculous
|
|
offset = self.mChatMessage[SELF_OFFSET].split(keyword)
|
|
hor_offset = int(offset[0]) if offset[0] else 0
|
|
vert_offset = int(offset[1]) if len(offset) > 1 else 0
|
|
else:
|
|
hor_offset = int(self.mChatMessage[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
|
|
|
|
if not self.animIsEmpty:
|
|
self.char.move(self.viewport.width() * hor_offset / 100, self.viewport.height() * vert_offset / 100)
|
|
|
|
# check if paired
|
|
if not self.mChatMessage[OTHER_CHARID] and not self.animIsEmpty:
|
|
self.sideChar.hide()
|
|
self.sideChar.move(0,0)
|
|
else:
|
|
if "effects" in self.features:
|
|
got_otherCharId = int(self.mChatMessage[OTHER_CHARID].split("^")[0])
|
|
else:
|
|
got_otherCharId = int(self.mChatMessage[OTHER_CHARID])
|
|
|
|
if got_otherCharId > -1: # user is paired
|
|
self.sideChar.show()
|
|
|
|
boxPairOrder = self.mChatMessage[OTHER_CHARID].split("^")
|
|
if "effects" in self.features and len(boxPairOrder) > 1:
|
|
boxPairOrder = int(boxPairOrder[1])
|
|
else:
|
|
boxPairOrder = -1
|
|
|
|
hor2_offset = vert2_offset = 0
|
|
if "y_offset" in self.features: # AO 2.9
|
|
keyword = "<and>" if "<and>" in self.mChatMessage[OTHER_OFFSET] else "&" # i don't think it's hdf's fault but this is still ridiculous
|
|
hor2_offset = int(self.mChatMessage[OTHER_OFFSET].split(keyword)[0])
|
|
vert2_offset = int(self.mChatMessage[OTHER_OFFSET].split(keyword)[1]) if len(self.mChatMessage[OTHER_OFFSET].split(keyword)) > 1 else 0
|
|
else:
|
|
hor2_offset = int(self.mChatMessage[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 boxPairOrder == -1: # pair ordering not supported
|
|
if hor2_offset >= hor_offset:
|
|
self.sideChar.raise_()
|
|
self.char.raise_()
|
|
else:
|
|
self.char.raise_()
|
|
self.sideChar.raise_()
|
|
elif boxPairOrder == 0: # front
|
|
self.sideChar.raise_()
|
|
self.char.raise_()
|
|
elif boxPairOrder == 1: # behind
|
|
self.char.raise_()
|
|
self.sideChar.raise_()
|
|
|
|
self.sideChar.move(self.viewport.width() * hor2_offset / 100, self.viewport.height() * vert2_offset / 100)
|
|
|
|
self.bench.raise_()
|
|
self.chatbox.raise_()
|
|
self.effectView.raise_()
|
|
self.objectionView.raise_()
|
|
self.whiteFlashLabel.raise_()
|
|
|
|
self.scaling[1] = getScaling(ini.read_ini(AOpath + 'characters/' + self.mChatMessage[OTHER_NAME] + '/char.ini', "options", "scaling").lower())
|
|
|
|
self.sideChar.setFlipped(self.mChatMessage[OTHER_FLIP] == "1")
|
|
if not self.animIsEmpty:
|
|
self.sideChar.playIdle(self.mChatMessage[OTHER_NAME], self.mChatMessage[OTHER_EMOTE], self.scaling[1])
|
|
|
|
elif not self.animIsEmpty:
|
|
self.sideChar.hide()
|
|
self.sideChar.move(0, 0)
|
|
|
|
self.scaling[0] = getScaling(ini.read_ini(AOpath + 'characters/' + self.mChatMessage[CHARNAME] + '/char.ini', "options", "scaling").lower())
|
|
|
|
if self.slideEnabled and self.slideAvailable:
|
|
if side == "wit":
|
|
if int(self.mChatMessage[EMOTE_MOD]) < 5: # Don't save anim if zoom
|
|
self.slideLastWit = [
|
|
self.mChatMessage[CHARNAME],
|
|
self.mChatMessage[ANIM],
|
|
self.scaling[0]
|
|
]
|
|
self.slideLastPos = "wit"
|
|
elif side == "def" or side == "pro":
|
|
self.slideLastPos = side
|
|
else:
|
|
self.slideLastPos = None
|
|
|
|
if (emoteMod == 1 or emoteMod == 2 or emoteMod == 6) and self.mChatMessage[PREANIM] != "-":
|
|
# sfxDelay = int(self.mChatMessage[SFX_DELAY]) * 60
|
|
# if sfxDelay > 0:
|
|
# self.sfxDelayTimer.start(sfxDelay)
|
|
# else:
|
|
# self.playSfx()
|
|
self.setBench(True)
|
|
self.playPre(False)
|
|
elif emoteMod == 0 or emoteMod == 5 or no_preanim:
|
|
if self.mChatMessage[NO_INTERRUPT] == "0" or no_preanim:
|
|
self.handleChatMessage3()
|
|
else:
|
|
self.playPre(True)
|
|
|
|
def playPre(self, nonInterrupting):
|
|
fChar = self.mChatMessage[CHARNAME].lower()
|
|
fPreanim = self.mChatMessage[PREANIM].strip()
|
|
|
|
ao2Duration = ini.read_ini_int(AOpath + "characters/"+fChar+"/char.ini", "time", fPreanim, -1)
|
|
textDelay = ini.read_ini_int(AOpath + "characters/"+fChar+"/char.ini", "textdelay", fPreanim, -1)
|
|
sfxDelay = int(self.mChatMessage[SFX_DELAY]) * 60
|
|
|
|
if sfxDelay > 0:
|
|
self.sfxDelayTimer.start(sfxDelay)
|
|
else:
|
|
self.playSfx()
|
|
|
|
preanimDuration = ao2Duration
|
|
|
|
animToFind = AOpath + "characters/"+fChar+"/"+fPreanim+".gif"
|
|
apngToFind = AOpath + "characters/"+fChar+"/"+fPreanim+".apng"
|
|
webpToFind = AOpath + "characters/"+fChar+"/"+fPreanim+".webp"
|
|
|
|
if (not animToFind and not apngToFind and not webpToFind) or preanimDuration < 0:
|
|
if nonInterrupting:
|
|
self.animState = 4
|
|
else:
|
|
self.animState = 1
|
|
self.preanimDone()
|
|
return
|
|
|
|
self.char.playPre(fChar, fPreanim, preanimDuration, self.scaling[0])
|
|
if nonInterrupting:
|
|
self.animState = 4
|
|
else:
|
|
self.animState = 1
|
|
|
|
if textDelay >= 0:
|
|
pass #text delay timer, but not now.
|
|
|
|
if nonInterrupting:
|
|
self.handleChatMessage3()
|
|
|
|
def preanimDone(self):
|
|
self.animState = 1
|
|
self.handleChatMessage3()
|
|
|
|
def handleChatMessage3(self):
|
|
self.startChatTicking()
|
|
self.setBench(False)
|
|
|
|
fEvidenceId = int(self.mChatMessage[EVIDENCE])
|
|
fSide = self.mChatMessage[SIDE]
|
|
|
|
emoteMod = int(self.mChatMessage[EMOTE_MOD])
|
|
|
|
if fEvidenceId > 0 and fEvidenceId <= len(self.evidence):
|
|
fImage = self.evidence[fEvidenceId-1][2]
|
|
isLeftSide = not (fSide == "def" or fSide == "hlp" or fSide == "jud" or fSide == "jur")
|
|
|
|
self.setEvidenceImage(self.presentedEvidence, fImage, True)
|
|
self.playSound("sfx-evidenceshoop.opus")
|
|
|
|
if not isLeftSide:
|
|
self.presentedEvidence.move(self.viewport.x() + 170 * self.viewportScale, self.viewport.y() + 16 * self.viewportScale)
|
|
else:
|
|
self.presentedEvidence.move(self.viewport.x() + 16 * self.viewportScale, self.viewport.y() + 16 * self.viewportScale)
|
|
self.presentedEvidence.show()
|
|
else:
|
|
self.presentedEvidence.hide()
|
|
|
|
side = self.mChatMessage[SIDE]
|
|
if emoteMod == 5 or emoteMod == 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_animState = 0
|
|
text_is_blue = int(self.mChatMessage[TEXT_COLOR]) == 4
|
|
|
|
if not text_is_blue and self.textState == 1:
|
|
f_animState = 2
|
|
self.entireMessageIsBlue = False
|
|
else:
|
|
f_animState = 3
|
|
self.entireMessageIsBlue = True
|
|
|
|
if f_animState <= self.animState:
|
|
return
|
|
|
|
fChar = self.mChatMessage[CHARNAME]
|
|
f_emote = self.mChatMessage[ANIM]
|
|
|
|
if not self.animIsEmpty:
|
|
self.char.stop()
|
|
|
|
if f_animState == 2:
|
|
self.char.playTalking(fChar, f_emote, self.scaling[0])
|
|
self.animState = 2
|
|
else:
|
|
self.char.playIdle(fChar, f_emote, self.scaling[0])
|
|
self.animState = 3
|
|
|
|
if exists(AO2XPpath + "callwords.ini"):
|
|
with open(AO2XPpath + "callwords.ini") as f:
|
|
callwords = [line.rstrip() for line in f]
|
|
for callword in callwords:
|
|
if callword.decode('utf-8').lower() in self.mChatMessage[CHATMSG].lower().split(" "):
|
|
self.OOCLog.append("<b>%s called you.</b>" % fChar)
|
|
QtGui.QApplication.alert(self, 1000)
|
|
snd = audio.loadHandle(False, "word_call.wav", 0, 0, BASS_STREAM_AUTOFREE)
|
|
if snd:
|
|
audio.playHandle(snd, True)
|
|
break
|
|
|
|
def playEffect(self, fxName, fxSound, pChar, pFolder):
|
|
effect = ini.get_effect(fxName, pChar, pFolder)
|
|
if not effect: return
|
|
|
|
if fxSound:
|
|
self.playSound(fxSound)
|
|
|
|
if "effects" not in self.features: return
|
|
|
|
self.effectView.setPlayOnce(True)
|
|
self.effectView.play(effect)
|
|
|
|
def startChatTicking(self):
|
|
if self.textState != 0:
|
|
return
|
|
|
|
if self.mChatMessage[EFFECTS]:
|
|
fxList = self.mChatMessage[EFFECTS].split("|")
|
|
fx = fxList[0]
|
|
fxSound = ""
|
|
fxFolder = ""
|
|
|
|
if len(fxList) > 1:
|
|
fxSound = fxList[1]
|
|
if len(fxList) > 2:
|
|
fxFolder = fxList[1]
|
|
fxSound = fxList[2]
|
|
|
|
if fx and fx not in ("-", "None"):
|
|
self.playEffect(fx, fxSound, self.mChatMessage[CHARNAME], fxFolder)
|
|
|
|
elif self.mChatMessage[REALIZATION] == "1":
|
|
self.setWhiteFlash(True, 1, 125)
|
|
|
|
self.setTextColor()
|
|
|
|
charid = int(self.mChatMessage[CHAR_ID])
|
|
if not self.isAdditive or self.additiveChar != charid:
|
|
self.ao2text.clear()
|
|
self.text.setText("")
|
|
self.additiveChar = charid
|
|
|
|
if self.chatMessageIsEmpty:
|
|
self.textState = 2
|
|
self.inboxTimer.start(self.textWaitTime)
|
|
return
|
|
|
|
self.inlineColorStack = []
|
|
|
|
self.chatbox.show()
|
|
|
|
self.tickPos = 0
|
|
self.blipPos = 0
|
|
self.inlineBlueDepth = 0
|
|
|
|
self.currentDisplaySpeed = 3
|
|
self.chatTickTimer.start(self.messageDisplaySpeed[self.currentDisplaySpeed])
|
|
|
|
self.blip = self.mChatMessage[BLIPS].lower()
|
|
|
|
if not self.blip:
|
|
self.blip = self.charList[charid][2].lower()
|
|
|
|
path = testPath(
|
|
AOpath + "sounds/blips/"+ self.blip +".wav",
|
|
AOpath + "sounds/blips/"+ self.blip +".opus",
|
|
AOpath + "sounds/general/sfx-blip"+ self.blip +".wav",
|
|
AOpath + "sounds/general/sfx-blip"+ self.blip +".opus"
|
|
)
|
|
|
|
if path:
|
|
if self.blipSound:
|
|
audio.freeHandle(self.blipSound)
|
|
self.blipSound = audio.loadHandle(False, path, 0, 0, 0)
|
|
|
|
if self.blipSound:
|
|
audio.setHandleAttr(self.blipSound, BASS_ATTRIB_VOL, self.sliBlipsVolume.value() / 100.0)
|
|
|
|
emoteMod = int(self.mChatMessage[EMOTE_MOD])
|
|
if emoteMod in (0, 5) and self.mChatMessage[SCREENSHAKE] == "1":
|
|
self.setScreenShake(True)
|
|
|
|
self.textState = 1
|
|
|
|
def chatTick(self):
|
|
fMessage = self.mChatMessage[CHATMSG]
|
|
|
|
self.chatTickTimer.stop()
|
|
formattingChar = False
|
|
|
|
if self.messageIsCentered:
|
|
fMessage = fMessage.strip("~~")
|
|
|
|
if self.tickPos >= len(fMessage):
|
|
self.textState = 2
|
|
if self.animState != 4:
|
|
self.animState = 3
|
|
if not self.animIsEmpty:
|
|
self.char.playIdle(self.mChatMessage[CHARNAME], self.mChatMessage[ANIM], self.scaling[0])
|
|
self.inboxTimer.start(self.textWaitTime)
|
|
|
|
else:
|
|
fCharacter2 = fMessage[self.tickPos]
|
|
fCharacter = QtCore.QString(fCharacter2)
|
|
|
|
if fCharacter == " ":
|
|
self.text.insertPlainText(" ")
|
|
self.ao2text.insertPlainText(" ")
|
|
|
|
elif fCharacter == "\n" or fCharacter == "\r":
|
|
self.text.insertPlainText("\n")
|
|
self.ao2text.insertPlainText("\n")
|
|
|
|
elif fCharacter == "\\" and not self.nextCharacterIsNotSpecial:
|
|
self.nextCharacterIsNotSpecial = True
|
|
formattingChar = True
|
|
|
|
elif fCharacter == "{" and not self.nextCharacterIsNotSpecial:
|
|
self.currentDisplaySpeed += 1
|
|
formattingChar = True
|
|
|
|
elif fCharacter == "}" and not self.nextCharacterIsNotSpecial:
|
|
self.currentDisplaySpeed -= 1
|
|
formattingChar = True
|
|
|
|
elif fCharacter == "|" and not self.nextCharacterIsNotSpecial: #orange.
|
|
if self.inlineColorStack:
|
|
if self.inlineColorStack[-1] == INLINE_ORANGE:
|
|
del self.inlineColorStack[-1]
|
|
else:
|
|
self.inlineColorStack.append(INLINE_ORANGE)
|
|
else:
|
|
self.inlineColorStack.append(INLINE_ORANGE)
|
|
formattingChar = True
|
|
|
|
elif fCharacter == "(" and not self.nextCharacterIsNotSpecial: #blue.
|
|
self.inlineColorStack.append(INLINE_BLUE)
|
|
html = "<font color=\"" + getTextColor(4).name() + "\">" + fCharacter + "</font>"
|
|
self.ao2text.insertHtml(html)
|
|
self.text.insertHtml(html)
|
|
|
|
self.inlineBlueDepth += 1
|
|
if not self.entireMessageIsBlue and self.animState != 4:
|
|
fChar = self.mChatMessage[CHARNAME]
|
|
f_emote = self.mChatMessage[ANIM]
|
|
if not self.animIsEmpty:
|
|
self.char.playIdle(fChar, f_emote, self.scaling[0])
|
|
|
|
elif fCharacter == ")" and not self.nextCharacterIsNotSpecial and self.inlineColorStack:
|
|
if self.inlineColorStack[-1] == INLINE_BLUE:
|
|
del self.inlineColorStack[-1]
|
|
html = "<font color=\"" + getTextColor(4).name() + "\">" + fCharacter + "</font>"
|
|
self.ao2text.insertHtml(html)
|
|
self.text.insertHtml(html)
|
|
|
|
if self.inlineBlueDepth > 0:
|
|
self.inlineBlueDepth -= 1
|
|
|
|
if not self.entireMessageIsBlue:
|
|
if self.inlineBlueDepth == 0 and self.animState != 4 and not (self.tickPos+1 >= len(fMessage)):
|
|
fChar = self.mChatMessage[CHARNAME]
|
|
f_emote = self.mChatMessage[ANIM]
|
|
self.char.playTalking(fChar, f_emote, self.scaling[0])
|
|
else:
|
|
self.nextCharacterIsNotSpecial = True
|
|
self.tickPos -= 1
|
|
|
|
elif fCharacter == "[" and not self.nextCharacterIsNotSpecial: #gray.
|
|
self.inlineColorStack.append(INLINE_GRAY)
|
|
html = "<font color=\"" + getTextColor("_inline_grey").name() + "\">" + fCharacter + "</font>"
|
|
self.ao2text.insertHtml(html)
|
|
self.text.insertHtml(html)
|
|
|
|
elif fCharacter == "]" and not self.nextCharacterIsNotSpecial and self.inlineColorStack:
|
|
if self.inlineColorStack[-1] == INLINE_GRAY:
|
|
del self.inlineColorStack[-1]
|
|
html = "<font color=\"" + getTextColor("_inline_grey").name() + "\">" + fCharacter + "</font>"
|
|
self.ao2text.insertHtml(html)
|
|
self.text.insertHtml(html)
|
|
else:
|
|
self.nextCharacterIsNotSpecial = True
|
|
self.tickPos -= 1
|
|
|
|
elif fCharacter == "`" and not self.nextCharacterIsNotSpecial: #green.
|
|
if self.inlineColorStack:
|
|
if self.inlineColorStack[-1] == INLINE_GREEN:
|
|
del self.inlineColorStack[-1]
|
|
else:
|
|
self.inlineColorStack.append(INLINE_GREEN)
|
|
else:
|
|
self.inlineColorStack.append(INLINE_GREEN)
|
|
formattingChar = True
|
|
|
|
elif fCharacter == "~" and not self.nextCharacterIsNotSpecial: #green.
|
|
if self.inlineColorStack:
|
|
if self.inlineColorStack[-1] == INLINE_RED:
|
|
del self.inlineColorStack[-1]
|
|
else:
|
|
self.inlineColorStack.append(INLINE_RED)
|
|
else:
|
|
self.inlineColorStack.append(INLINE_RED)
|
|
formattingChar = True
|
|
|
|
elif fCharacter == "s" and self.nextCharacterIsNotSpecial: # shake
|
|
self.setScreenShake(True)
|
|
self.nextCharacterIsNotSpecial = False
|
|
|
|
elif fCharacter == "f" and self.nextCharacterIsNotSpecial: # flash
|
|
self.setWhiteFlash(True, 0, 75)
|
|
self.nextCharacterIsNotSpecial = False
|
|
|
|
elif fCharacter == "n" and self.nextCharacterIsNotSpecial: # newline
|
|
self.text.insertPlainText("\n")
|
|
self.ao2text.insertPlainText("\n")
|
|
self.nextCharacterIsNotSpecial = False
|
|
|
|
else:
|
|
self.nextCharacterIsNotSpecial = False
|
|
if self.inlineColorStack:
|
|
top_color = self.inlineColorStack[-1]
|
|
if top_color == INLINE_ORANGE:
|
|
html = "<font color=\"" + getTextColor(C_ORANGE).name() + "\">" + fCharacter + "</font>"
|
|
self.ao2text.insertHtml(html)
|
|
self.text.insertHtml(html)
|
|
elif top_color == INLINE_BLUE:
|
|
html = "<font color=\"" + getTextColor(C_BLUE).name() + "\">" + fCharacter + "</font>"
|
|
self.ao2text.insertHtml(html)
|
|
self.text.insertHtml(html)
|
|
elif top_color == INLINE_GREEN:
|
|
html = "<font color=\"" + getTextColor(C_GREEN).name() + "\">" + fCharacter + "</font>"
|
|
self.ao2text.insertHtml(html)
|
|
self.text.insertHtml(html)
|
|
elif top_color == INLINE_GRAY:
|
|
html = "<font color=\"" + getTextColor("_inline_grey").name() + "\">" + fCharacter + "</font>"
|
|
self.ao2text.insertHtml(html)
|
|
self.text.insertHtml(html)
|
|
elif top_color == INLINE_RED:
|
|
html = "<font color=\"" + getTextColor(C_RED).name() + "\">" + fCharacter + "</font>"
|
|
self.ao2text.insertHtml(html)
|
|
self.text.insertHtml(html)
|
|
else:
|
|
self.text.insertHtml(fCharacter)
|
|
self.ao2text.insertHtml(fCharacter)
|
|
else:
|
|
if int(self.mChatMessage[TEXT_COLOR]) == C_RAINBOW:
|
|
self.text.insertHtml(fCharacter)
|
|
else:
|
|
self.ao2text.insertHtml(fCharacter)
|
|
|
|
if self.messageIsCentered:
|
|
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 fMessage[self.tickPos] != " " or self.blankBlip:
|
|
if self.blipPos % self.blipRate == 0 and not formattingChar:
|
|
self.blipPos = 0
|
|
if self.blipSound:
|
|
audio.playHandle(self.blipSound, True)
|
|
|
|
self.blipPos += 1
|
|
|
|
self.tickPos += 1
|
|
|
|
if self.currentDisplaySpeed < 0:
|
|
self.currentDisplaySpeed = 0
|
|
elif self.currentDisplaySpeed > 6:
|
|
self.currentDisplaySpeed = 6
|
|
|
|
if formattingChar:
|
|
self.chatTickTimer.start(1)
|
|
else:
|
|
self.chatTickTimer.start(self.messageDisplaySpeed[self.currentDisplaySpeed])
|
|
|
|
def inboxTimerTimeout(self):
|
|
if len(self.inboxQueue) > 0:
|
|
del self.inboxQueue[0]
|
|
if len(self.inboxQueue) > 0:
|
|
self.handleChatMessage1(self.inboxQueue[0])
|
|
|
|
def playRealization(self):
|
|
audio.playHandle(self.sndRealization, True)
|
|
|
|
def playObjectionSound(self, charName, objection):
|
|
try:
|
|
charName = str(charName)
|
|
except:
|
|
print "WARNING: Can't play objection sound if charName is unicode yet"
|
|
return
|
|
|
|
if self.sndObjection:
|
|
if audio.handleIsActive(self.sndObjection):
|
|
audio.stopHandle(self.sndObjection)
|
|
audio.freeHandle(self.sndObjection)
|
|
|
|
objecting = ["holdit", "objection", "takethat", "custom"][objection-1]
|
|
|
|
if objecting:
|
|
if exists(AOpath + 'characters/' + charName + '/' + objecting + '.wav'):
|
|
self.sndObjection = audio.loadHandle(False, AOpath + 'characters/' + charName + '/' + objecting + '.wav', 0, 0, 0)
|
|
elif exists(AOpath + 'characters/' + charName + '/' + objecting + '.opus'):
|
|
self.sndObjection = audio.loadHandle(False, AOpath + 'characters/' + charName + '/' + objecting + '.opus', 0, 0, 0)
|
|
else:
|
|
self.sndObjection = None
|
|
if ini.read_ini_bool("AO2XP.ini", "General", "download sounds", True):
|
|
thread.start_new_thread(downloadThread, ("base/characters/"+charName+"/"+objecting+".wav", AOpath + "characters/"+charName+"/"+objecting.lower() + ".wav"))
|
|
thread.start_new_thread(downloadThread, ("base/characters/"+charName+"/"+objecting+".opus", AOpath + "characters/"+charName+"/"+objecting.lower() + ".wav"))
|
|
|
|
if exists(AOpath + 'sounds/general/sfx-objection.opus'):
|
|
self.sndObjection = audio.loadHandle(False, AOpath + 'sounds/general/sfx-objection.opus', 0, 0, 0)
|
|
else:
|
|
self.sndObjection = audio.loadHandle(False, AOpath + 'sounds/general/sfx-objection.wav', 0, 0, 0)
|
|
audio.setHandleAttr(self.sndObjection, BASS_ATTRIB_VOL, self.sliSoundVolume.value() / 100.0)
|
|
audio.playHandle(self.sndObjection, True)
|
|
|
|
def playSfx(self):
|
|
sfxName = self.mChatMessage[SFX]
|
|
if sfxName == "1" or sfxName == "0":
|
|
return
|
|
|
|
self.playSound(sfxName)
|
|
|
|
def playSound(self, sfx):
|
|
if self.sound:
|
|
if audio.handleIsActive(self.sound):
|
|
audio.stopHandle(self.sound)
|
|
audio.freeHandle(self.sound)
|
|
|
|
path = testPath(AOpath + 'sounds/general/' + sfx, AOpath + 'sounds/general/' + sfx + '.wav', AOpath + 'sounds/general/' + sfx + '.opus')
|
|
|
|
if path:
|
|
self.sound = audio.loadHandle(False, path, 0, 0, 0)
|
|
audio.setHandleAttr(self.sound, BASS_ATTRIB_VOL, self.sliSoundVolume.value() / 100.0)
|
|
if self.sound:
|
|
audio.playHandle(self.sound, True)
|
|
|
|
def playMusic(self, mus):
|
|
self.stopMusic()
|
|
if mus == "~stop.mp3" or not mus:
|
|
return
|
|
|
|
_musicList = self.musicListPrivate if self.privateMusicSelected else self.musicList
|
|
|
|
if not mus.endswith(".mp3") and "===MUSIC START===.mp3" in _musicList: #vidya workaround
|
|
mus += ".mp3"
|
|
musl = mus.lower()
|
|
|
|
playLocal = False
|
|
|
|
if exists(AOpath + 'sounds/music/' + musl):
|
|
playLocal = True
|
|
elif self.pickedMusicItem:
|
|
_musl = _musicList[self.musicItems.currentItem().text()]
|
|
if exists(AOpath + 'sounds/music/' + _musl):
|
|
musl = _musl
|
|
playLocal = True
|
|
elif musl.startswith("http"):
|
|
_musl = urllib.unquote(basename(mus))
|
|
if exists(AOpath + 'sounds/music/' + _musl):
|
|
musl = _musl
|
|
playLocal = True
|
|
|
|
self.pickedMusicItem = False
|
|
|
|
if playLocal:
|
|
if isinstance(musl, unicode):
|
|
if platform.system() == "Windows":
|
|
musl = musl.encode('mbcs')
|
|
else:
|
|
musl = musl.encode('utf-8')
|
|
self.music = audio.loadHandle(False, AOpath + 'sounds/music/' + musl, 0, 0, BASS_SAMPLE_LOOP)
|
|
if self.music:
|
|
audio.setHandleAttr(self.music, BASS_ATTRIB_VOL, self.sliMusicVolume.value() / 100.0)
|
|
audio.playHandle(self.music, True)
|
|
if self.sliMusicVolume.value() == 0:
|
|
audio.pauseHandle(self.music)
|
|
else:
|
|
error = audio.getBassError()
|
|
print "[audio] Couldn't play local track! Error", error
|
|
|
|
elif ini.read_ini_bool("AO2XP.ini", "General", "download music", True):
|
|
if musl.startswith("http"):
|
|
self.music = audio.loadURLHandle(mus, 0, BASS_SAMPLE_LOOP)
|
|
print "[audio] Trying to play", mus
|
|
else:
|
|
global bucket
|
|
mus = musl = bucket + 'base/sounds/music/' + musl.replace(" ", "%20")
|
|
print "[audio] Music stream:", bucket + 'base/sounds/music/' + mus
|
|
self.music = audio.loadURLHandle(musl, 0, BASS_SAMPLE_LOOP)
|
|
|
|
if self.music:
|
|
audio.setHandleAttr(self.music, BASS_ATTRIB_VOL, self.sliMusicVolume.value() / 100.0)
|
|
audio.playHandle(self.music, True)
|
|
if self.sliMusicVolume.value() == 0:
|
|
audio.pauseHandle(self.music)
|
|
else:
|
|
error = audio.getBassError()
|
|
#print "[audio] Couldn't play music. Error", error
|
|
# Here comes the evil HTTPS hack for XP systems, but it also allows us to download and play modules, because, why not?
|
|
musext = os.path.splitext(basename(musl))[-1]
|
|
if musext in ['.xm', '.mod', '.mo3', '.it', '.s3m', '.mtm', '.umx']:
|
|
self.specialStream = True
|
|
if (musl.startswith("https") and error == 2) or self.specialStream:
|
|
print "[audio] Downloading music with urllib2"
|
|
self.downloadThread = MusicDownloadThread(self, mus)
|
|
self.downloadThread.finished_signal.connect(self.playDownloadedMusic)
|
|
self.downloadThread.start()
|
|
|
|
def stopMusic(self):
|
|
if self.music:
|
|
if audio.handleIsActive(self.music):
|
|
audio.stopHandle(self.music)
|
|
if self.specialStream:
|
|
audio.freeMOD(self.music)
|
|
self.specialStream = False
|
|
else:
|
|
audio.freeHandle(self.music)
|
|
if self.stream:
|
|
self.stream = None
|
|
|
|
if self.downloadThread:
|
|
self.downloadThread.stop()
|
|
#self.downloadThread.wait()
|
|
self.downloadThread = None
|
|
|
|
def playDownloadedMusic(self, file_length):
|
|
# Part of the evil HTTPS music download hack for XP systems
|
|
print "[audio] Done downloading; playing stream"
|
|
if self.specialStream:
|
|
self.music = audio.loadMOD(True, self.stream, 0, file_length, BASS_SAMPLE_LOOP)
|
|
else:
|
|
self.music = audio.loadHandle(True, self.stream, 0, file_length, BASS_SAMPLE_LOOP)
|
|
audio.setHandleAttr(self.music, BASS_ATTRIB_VOL, self.sliMusicVolume.value() / 100.0)
|
|
audio.playHandle(self.music, True)
|
|
if self.sliMusicVolume.value() == 0:
|
|
audio.pauseHandle(self.music)
|
|
|
|
def loadAllMusic(self):
|
|
_musicList = self.musicListPrivate if self.privateMusicSelected else self.musicList
|
|
|
|
self.actMusicRandom.setDisabled(not len(_musicList.items()))
|
|
|
|
for song, fname in _musicList.items():
|
|
songitem = QtGui.QListWidgetItem()
|
|
songitem.setText(song)
|
|
if not self.privateMusicSelected and exists(unicode(AOpath + 'sounds/music/' + decodeAOString(fname).lower())):
|
|
songitem.setBackgroundColor(QtGui.QColor(self.foundSongItemColor))
|
|
self.musicItems.addItem(songitem)
|
|
|
|
def loadAllEvidence(self, evi):
|
|
self.evidence = evi
|
|
|
|
if self.boxEvidence.count() > 0:
|
|
self.boxEvidence.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] = decodeAOString(evi[0].decode('utf-8'))
|
|
evi[1] = decodeAOString(evi[1].decode('utf-8'))
|
|
evi[2] = decodeAOString(evi[2].decode('utf-8'))
|
|
self.boxEvidence.addItem(evi[0].strip())
|
|
|
|
if not self.evidence:
|
|
self.boxEvidence.setCurrentIndex(0)
|
|
self.evidenceDescription.setText('.')
|
|
else:
|
|
self.boxEvidence.setCurrentIndex(self.selectedEvidence)
|
|
|
|
def updatePlayerList(self, pid, op, utype, data=""):
|
|
if not self.playerList:
|
|
return
|
|
|
|
pid = str(pid)
|
|
if op == 0: # Add or remove player
|
|
if utype == 0: # Add a player
|
|
self.playerItems.addItem("[%s]" % pid)
|
|
if not pid in self.playerList:
|
|
self.playerList[pid] = ["", "", "", ""]
|
|
if utype == 1: # Remove a player
|
|
item = self.playerItems.findItems("[%s]" % pid, QtCore.Qt.MatchStartsWith)
|
|
if item:
|
|
self.playerItems.takeItem(self.playerItems.row(item[0]))
|
|
if pid in self.playerList:
|
|
del self.playerList[pid]
|
|
else: # Update a player
|
|
if pid in self.playerList:
|
|
self.playerList[pid][utype] = data
|
|
|
|
item = self.playerItems.findItems("[%s]" % pid, QtCore.Qt.MatchStartsWith)
|
|
if item:
|
|
name = self.playerList[pid][0]
|
|
char = self.playerList[pid][1]
|
|
charName = self.playerList[pid][2]
|
|
text = "[%s]" % pid
|
|
if char:
|
|
text += " %s" % char
|
|
if charName:
|
|
text += " (%s)" % charName
|
|
if name:
|
|
text += " %s" % name
|
|
item[0].setText(text)
|
|
|
|
def startPauseOnscreenTimers(self, command, timer_id, timer_ms):
|
|
if timer_id > 4:
|
|
return
|
|
|
|
if command == 0:
|
|
if not self.onscreenTimer.isActive():
|
|
self.onscreenTimer.start(self.onscreenTimerTick)
|
|
self.onscreenTimerTimes[timer_id] = timer_ms
|
|
self.onscreenTimerPaused[timer_id] = False
|
|
self.updateOnscreenTimers()
|
|
print "[client] Timer %d was started for %d ms" % (timer_id, timer_ms)
|
|
elif command == 1:
|
|
self.onscreenTimerPaused[timer_id] = True
|
|
elif command == 2:
|
|
self.onscreenTimerLabels[timer_id].show()
|
|
elif command == 3:
|
|
self.onscreenTimerLabels[timer_id].hide()
|
|
|
|
def updateOnscreenTimers(self):
|
|
for timer_id, label in enumerate(self.onscreenTimerLabels):
|
|
time_ms = self.onscreenTimerTimes[timer_id]
|
|
if not time_ms or self.onscreenTimerPaused[timer_id]:
|
|
continue
|
|
|
|
secs = time_ms / 1000
|
|
mins = secs / 60
|
|
hours = mins / 60
|
|
label.setText("%02d:%02d:%02d" % (hours, mins % 60, secs % 60))
|
|
|
|
self.onscreenTimerTimes[timer_id] -= self.onscreenTimerTick
|
|
if self.onscreenTimerTimes[timer_id] <= 0:
|
|
label.hide()
|
|
self.onscreenTimerTimes[timer_id] = 0
|
|
self.onscreenTimerPaused[timer_id] = True
|
|
|
|
if self.onscreenTimerTimes == [0, 0, 0, 0, 0]:
|
|
self.onscreenTimer.stop()
|
|
|
|
def onDemoClicked(self, item):
|
|
fname = demo.getDemoFilename(self.demoItems, item)
|
|
|
|
if not fname:
|
|
return
|
|
|
|
self.playerItems.clear()
|
|
self.stopMusic()
|
|
self.ICLog.clear()
|
|
self.OOCLog.clear()
|
|
|
|
if not self.demoPlaying:
|
|
self.enableWidgets(True)
|
|
self.tcpThread.stop()
|
|
self.demoPlaying = True
|
|
|
|
self.demoPlayer = demo.DemoPlayer(self)
|
|
self.demoPlayer.MS_Chat.connect(self.netmsgMS)
|
|
self.demoPlayer.newChar.connect(self.onPVPacket)
|
|
self.demoPlayer.newBackground.connect(self.setBackground)
|
|
self.demoPlayer.OOC_Log.connect(self.OOCLog.append)
|
|
self.demoPlayer.IC_Log.connect(self.ICLog.append)
|
|
self.demoPlayer.charSlots.connect(partial(self.charSelect.setCharList, self.charList))
|
|
self.demoPlayer.loadAllEvidence.connect(self.loadAllEvidence)
|
|
self.demoPlayer.updatePlayerList.connect(self.updatePlayerList)
|
|
self.demoPlayer.rainbowColor.connect(self.text.setStyleSheet)
|
|
self.demoPlayer.timerUpdate.connect(self.startPauseOnscreenTimers)
|
|
|
|
self.demoPlayer.start(fname)
|
|
|
|
def demoSeek(self, time):
|
|
self.demoPlayer.seek(time)
|
|
|
|
def startGame(self, tcp, playerList, charList, musicList, background, evidence, areas, features=[], joinOOC=[], hpList=[], webAO_bucket=""):
|
|
self.willDisconnect = False
|
|
self.myChar = -1
|
|
self.myChatColor = 0
|
|
self.tcp = tcp
|
|
self.playerList = playerList
|
|
self.charList = charList
|
|
self.evidence = evidence
|
|
self.areas = areas
|
|
self.areasLen = len(areas[0])
|
|
self.features = features
|
|
self.musicList = OrderedDict([])
|
|
|
|
# We want only song names without paths or extensions in the music list
|
|
for song in musicList:
|
|
_basename = os.path.splitext(basename(song))
|
|
self.musicList[QtCore.QString(decodeAOString(_basename[0].decode('utf-8')))] = song.decode('utf-8')
|
|
|
|
if "base/" in webAO_bucket:
|
|
webAO_bucket = webAO_bucket.replace("base/", "")
|
|
global bucket
|
|
bucket = webAO_bucket
|
|
|
|
self.charSelect.setCharList(charList)
|
|
autopick = getOption("General", "auto pick").decode('utf-8').lower()
|
|
coincidence = -1
|
|
for i, char in enumerate(self.charList):
|
|
if char[0].lower() == autopick and char[1] == 0:
|
|
coincidence = i
|
|
break
|
|
|
|
if coincidence > -1:
|
|
self.charSelect.selectChar(coincidence)
|
|
else:
|
|
self.charSelect.showCharSelect(False)
|
|
|
|
# Putting it down here because some servers won't allow you to switch areas without picking a character first
|
|
autojoinarea = getOption("General", "auto join area").decode('utf-8')
|
|
if autojoinarea != "": self.sendMC(autojoinarea)
|
|
|
|
self.OOCNameInput.setText(ini.read_ini("AO2XP.ini", "General", "OOC name", "unnamed"))
|
|
self.shownameEdit.setText(ini.read_ini("AO2XP.ini", "General", "Showname"))
|
|
|
|
self.enableWidgets()
|
|
|
|
self.boxPair.clear()
|
|
self.cbPair.setChecked(False)
|
|
if "cccc_ic_support" in features:
|
|
self.shownameEdit.show()
|
|
self.cbNoInterrupt.show()
|
|
self.cbPair.setDisabled(False)
|
|
self.cbPair.setText("Enable pairing")
|
|
for char in charList:
|
|
self.boxPair.addItem(char[0])
|
|
else:
|
|
self.shownameEdit.hide()
|
|
self.cbNoInterrupt.hide()
|
|
self.cbPair.setDisabled(True)
|
|
self.cbPair.setToolTip("This server does not support pairing.")
|
|
|
|
self.cbBench.setDisabled("deskmod" not in features)
|
|
self.cbFlip.setDisabled("flipping" not in features)
|
|
self.cbSlide.setDisabled(not self.slideEnabled or "custom_blips" not in features)
|
|
self.btnCustomObjection.setVisible("customobjections" in features)
|
|
self.sliPairOffsetY.setVisible("y_offset" in features)
|
|
self.lblPairOffsetY.setVisible("y_offset" in features)
|
|
|
|
self.boxColors.clear()
|
|
self.boxColors.addItems(['White', 'Green', 'Red', 'Orange', 'Blue'])
|
|
if "yellowtext" in features:
|
|
self.boxColors.addItems(['Yellow', 'Pink', 'Cyan', 'Gray', 'Rainbow', 'Black'])
|
|
self.boxColors.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].lower() + '/char.ini'):
|
|
continue
|
|
char[2] = getCharIni(char[0], "Options", "gender").lower()
|
|
if char[2] == "":
|
|
char[2] = getCharIni(char[0], "Options", "blips").lower()
|
|
|
|
self.btnRealization.setPressed(False)
|
|
self.btnCustomObjection.setPressed(False)
|
|
self.mutedList.clear()
|
|
self.unmutedList.clear()
|
|
for char in self.charList:
|
|
self.unmutedList.addItem(char[0])
|
|
|
|
self.musicItems.clear()
|
|
self.areaItems.clear()
|
|
self.boxEvidence.clear()
|
|
for evi in evidence:
|
|
self.boxEvidence.addItem(evi[0].strip())
|
|
|
|
logstart = '<b>--- Log started on ' + time.ctime() + ' ---</b>'
|
|
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.lower())
|
|
self.setScene(True)
|
|
self.chatbox.hide()
|
|
|
|
for msg in joinOOC:
|
|
self.OOCLog.append(msg)
|
|
|
|
if self.areasLen:
|
|
for i in range(self.areasLen):
|
|
areaitem = QtGui.QListWidgetItem()
|
|
self.areaItems.addItem(areaitem)
|
|
self.loadAllMusic()
|
|
self.updateAreaList()
|
|
else:
|
|
self.noARUP = True
|
|
|
|
for pid in playerList:
|
|
self.updatePlayerList(pid, 0, 0)
|
|
for type in range(len(playerList[pid])):
|
|
self.updatePlayerList(pid, 1, type, playerList[pid][type])
|
|
|
|
self.sliMusicVolume.setValue(ini.read_ini_int("AO2XP.ini", "Audio", "Music volume", 100))
|
|
self.sliSoundVolume.setValue(ini.read_ini_int("AO2XP.ini", "Audio", "Sound volume", 100))
|
|
self.sliBlipsVolume.setValue(ini.read_ini_int("AO2XP.ini", "Audio", "Blip volume", 100))
|
|
|
|
self.onImportEvidence(True)
|
|
|
|
self.tcpThread = TCPThread(self)
|
|
self.tcpThread.MS_Chat.connect(self.netmsgMS)
|
|
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.showCharSelect)
|
|
self.tcpThread.loadAllEvidence.connect(self.loadAllEvidence)
|
|
self.tcpThread.updatePlayerList.connect(self.updatePlayerList)
|
|
self.tcpThread.rainbowColor.connect(self.text.setStyleSheet)
|
|
self.tcpThread.timerUpdate.connect(self.startPauseOnscreenTimers)
|
|
self.tcpThread.start()
|
|
|
|
self.demoPlaying = False
|
|
|
|
self.startDemoRecorder(background)
|
|
self.ICChatInput.setFocus()
|
|
|
|
def startDemo(self, fname):
|
|
self.playerList = []
|
|
self.charList = []
|
|
self.evidence = []
|
|
self.areas = []
|
|
self.areasLen = 0
|
|
self.features = ['noencryption', 'yellowtext', 'prezoom', 'flipping', 'customobjections', 'fastloading', 'deskmod', 'evidence', 'cccc_ic_support', 'arup', 'casing_alerts', 'modcall_reason', 'looping_sfx', 'additive', 'effects', 'y_offset', 'expanded_desk_mods', 'auth_packet', 'custom_blips']
|
|
self.musicList = OrderedDict([])
|
|
self.charSelect.hide()
|
|
self.onPVPacket()
|
|
|
|
self.setBackground("default")
|
|
self.setScene(True)
|
|
self.chatbox.hide()
|
|
|
|
self.sliMusicVolume.setValue(ini.read_ini_int("AO2XP.ini", "Audio", "Music volume", 100))
|
|
self.sliSoundVolume.setValue(ini.read_ini_int("AO2XP.ini", "Audio", "Sound volume", 100))
|
|
self.sliBlipsVolume.setValue(ini.read_ini_int("AO2XP.ini", "Audio", "Blip volume", 100))
|
|
|
|
self.onImportEvidence(True)
|
|
|
|
self.demoPlayer = demo.DemoPlayer(self)
|
|
self.demoPlayer.MS_Chat.connect(self.netmsgMS)
|
|
self.demoPlayer.newChar.connect(self.onPVPacket)
|
|
self.demoPlayer.newBackground.connect(self.setBackground)
|
|
self.demoPlayer.OOC_Log.connect(self.OOCLog.append)
|
|
self.demoPlayer.IC_Log.connect(self.ICLog.append)
|
|
self.demoPlayer.charSlots.connect(partial(self.charSelect.setCharList, self.charList))
|
|
self.demoPlayer.loadAllEvidence.connect(self.loadAllEvidence)
|
|
self.demoPlayer.updatePlayerList.connect(self.updatePlayerList)
|
|
self.demoPlayer.rainbowColor.connect(self.text.setStyleSheet)
|
|
self.demoPlayer.timerUpdate.connect(self.startPauseOnscreenTimers)
|
|
|
|
self.playerItems.clear()
|
|
self.stopMusic()
|
|
self.ICLog.clear()
|
|
self.OOCLog.clear()
|
|
|
|
self.btnChangeChar.setText('Disconnect')
|
|
self.enableWidgets(True)
|
|
self.demoPlaying = True
|
|
self.demoPlayer.start(fname)
|
|
|
|
def startDemoRecorder(self, bg=None):
|
|
if ini.read_ini_bool("AO2XP.ini", "General", "record demos", False):
|
|
self.demoRecorder = demo.DemoRecorder()
|
|
self.demoRecorder.start()
|
|
self.demoRecorder.record([["SC"] + [char[0] for char in self.charList]], encode=True)
|
|
if bg:
|
|
self.demoRecorder.record([["BN", bg, ""]], encode=True)
|
|
|
|
def enableWidgets(self, demo = False):
|
|
for widget in [
|
|
self.OOCInput, self.btnCallMod,
|
|
self.OOCNameInput, self.OOCLogin, self.tabEvidence,
|
|
self.tabQueue, self.tabIniSwap, self.tabMute,
|
|
self.tabPair, self.tabMisc, self.tabPlayers,
|
|
self.tabMusic, self.boxEmotes, self.boxPositions,
|
|
self.cbFlip, self.cbPreanim, self.cbNoInterrupt,
|
|
self.boxEffects, self.cbSlide, self.cbBench,
|
|
self.cbAdditive, self.areaItems, self.shownameEdit,
|
|
self.boxColors, self.btnDefenseBar.minusbtn, self.btnProsecutionBar.minusbtn,
|
|
self.btnDefenseBar.plusbtn, self.btnProsecutionBar.plusbtn, self.btnWitnessTestimony,
|
|
self.btnCrossExamination, self.btnNotGuilty, self.btnGuilty,
|
|
self.btnRealization, self.btnShake,
|
|
]:
|
|
widget.setEnabled(not demo)
|
|
self.sliDemoSeekbar.setVisible(demo)
|
|
self.ICChatInput.setVisible(not demo)
|
|
|
|
if demo:
|
|
self.btnChangeChar.setText('Disconnect')
|
|
else:
|
|
self.btnChangeChar.setText('Switch &character')
|
|
|
|
def updateAreaList(self):
|
|
try:
|
|
for i in range(self.areasLen):
|
|
areaPlayers = self.areas[0][i]
|
|
areaStatus = self.areas[1][i].title()
|
|
areaCM = self.areas[2][i].decode('utf-8')
|
|
areaLocked = self.areas[3][i].title()
|
|
areaName = self.areas[4][i].decode('utf-8')
|
|
if areaStatus == "Casing":
|
|
self.areaItems.item(i).setText("%s\n%s | %s\n%s users | %s" % (areaName, areaStatus, areaCM, areaPlayers, areaLocked))
|
|
else:
|
|
self.areaItems.item(i).setText("%s\n%s\n%s users | %s" % (areaName, areaStatus, areaPlayers, areaLocked))
|
|
|
|
if areaLocked == "Locked":
|
|
self.areaItems.item(i).setIcon(QtGui.QIcon(AO2XPpath + "icons/" + "lock.png"))
|
|
else:
|
|
self.areaItems.item(i).setIcon(QtGui.QIcon(AO2XPpath + "icons/" + "house.png"))
|
|
except Exception as e:
|
|
print "[debug] Couldn't update areas. Details:"
|
|
print "[debug]", e |