diff --git a/base/themes/default/effects/effects.ini b/base/themes/default/effects/effects.ini new file mode 100644 index 0000000..06caf8e --- /dev/null +++ b/base/themes/default/effects/effects.ini @@ -0,0 +1,4 @@ +realization = sfx-realization +hearts = sfx-squee +reaction = sfx-reactionding +impact = sfx-fan \ No newline at end of file diff --git a/base/themes/default/effects/hearts.png b/base/themes/default/effects/hearts.png new file mode 100644 index 0000000..7f775d1 Binary files /dev/null and b/base/themes/default/effects/hearts.png differ diff --git a/base/themes/default/effects/hearts.webp b/base/themes/default/effects/hearts.webp new file mode 100644 index 0000000..890342e Binary files /dev/null and b/base/themes/default/effects/hearts.webp differ diff --git a/base/themes/default/effects/icons/hearts.png b/base/themes/default/effects/icons/hearts.png new file mode 100644 index 0000000..3c8c6ba Binary files /dev/null and b/base/themes/default/effects/icons/hearts.png differ diff --git a/base/themes/default/effects/icons/impact.png b/base/themes/default/effects/icons/impact.png new file mode 100644 index 0000000..a49d84c Binary files /dev/null and b/base/themes/default/effects/icons/impact.png differ diff --git a/base/themes/default/effects/icons/reaction.png b/base/themes/default/effects/icons/reaction.png new file mode 100644 index 0000000..805c4d9 Binary files /dev/null and b/base/themes/default/effects/icons/reaction.png differ diff --git a/base/themes/default/effects/icons/realization.png b/base/themes/default/effects/icons/realization.png new file mode 100644 index 0000000..8eaf2e8 Binary files /dev/null and b/base/themes/default/effects/icons/realization.png differ diff --git a/base/themes/default/effects/impact.webp b/base/themes/default/effects/impact.webp new file mode 100644 index 0000000..b86c924 Binary files /dev/null and b/base/themes/default/effects/impact.webp differ diff --git a/base/themes/default/effects/reaction.webp b/base/themes/default/effects/reaction.webp new file mode 100644 index 0000000..20688c8 Binary files /dev/null and b/base/themes/default/effects/reaction.webp differ diff --git a/base/themes/default/effects/realization.png b/base/themes/default/effects/realization.png new file mode 100644 index 0000000..776c70d Binary files /dev/null and b/base/themes/default/effects/realization.png differ diff --git a/base/themes/default/effects/realization.webp b/base/themes/default/effects/realization.webp new file mode 100644 index 0000000..647bed3 Binary files /dev/null and b/base/themes/default/effects/realization.webp differ diff --git a/base/themes/default/screenshake.png b/base/themes/default/screenshake.png new file mode 100644 index 0000000..5b71e3b Binary files /dev/null and b/base/themes/default/screenshake.png differ diff --git a/base/themes/default/screenshake_pressed.png b/base/themes/default/screenshake_pressed.png new file mode 100644 index 0000000..43e01c8 Binary files /dev/null and b/base/themes/default/screenshake_pressed.png differ diff --git a/buttons.py b/buttons.py index a924dba..ce6ca85 100644 --- a/buttons.py +++ b/buttons.py @@ -1,32 +1,32 @@ from PyQt4 import QtCore, QtGui import os -AOpath = "base\\" -#AOpath = "I:\\aovanilla1.7.5\\client\\base\\" +AOpath = "base/" +#AOpath = "I:/aovanilla1.7.5/client/base/" -class RealizationButton(QtGui.QLabel): +class AOToggleButton(QtGui.QLabel): pressed = False - def __init__(self, parent, x, y): - super(RealizationButton, self).__init__(parent) + clicked = QtCore.pyqtSignal() + + def __init__(self, parent, x, y, btnname): + super(AOToggleButton, self).__init__(parent) self.parent = parent - self.setGeometry(x, y, 42, 42) - self.notpressed_pix = QtGui.QPixmap(AOpath+"themes\\default\\realization.png") - self.pressed_pix = QtGui.QPixmap(AOpath+"themes\\default\\realization_pressed.png") + self.notpressed_pix = QtGui.QPixmap(AOpath+"themes/default/%s.png" % btnname) + self.pressed_pix = QtGui.QPixmap(AOpath+"themes/default/%s_pressed.png" % btnname) self.setPixmap(self.notpressed_pix) + self.setGeometry(x, y, self.notpressed_pix.size().width(), self.notpressed_pix.size().height()) self.show() def setPressed(self, on): self.pressed = on - if on: - self.setPixmap(self.pressed_pix) - else: - self.setPixmap(self.notpressed_pix) + self.setPixmap(self.pressed_pix if on else self.notpressed_pix) def isPressed(self): return self.pressed def mousePressEvent(self, event): self.setPressed(not self.isPressed()) + self.clicked.emit() class CustomObjection(QtGui.QLabel): pressed = False @@ -34,8 +34,8 @@ class CustomObjection(QtGui.QLabel): super(CustomObjection, self).__init__(parent) self.parent = parent self.setGeometry(x, y, 76, 28) - self.notpressed_pix = QtGui.QPixmap(AOpath+"themes\\default\\custom.png") - self.pressed_pix = QtGui.QPixmap(AOpath+"themes\\default\\custom_selected.png") + self.notpressed_pix = QtGui.QPixmap(AOpath+"themes/default/custom.png") + self.pressed_pix = QtGui.QPixmap(AOpath+"themes/default/custom_selected.png") self.setPixmap(self.notpressed_pix) self.show() @@ -63,14 +63,14 @@ class WTCEbuttons(QtGui.QLabel): super(WTCEbuttons, self).__init__(parent) self.setGeometry(x, y, 85, 42) if type == 0: - self.setPixmap(QtGui.QPixmap(AOpath+"themes\\default\\witnesstestimony.png")) + self.setPixmap(QtGui.QPixmap(AOpath+"themes/default/witnesstestimony.png")) elif type == 1: - self.setPixmap(QtGui.QPixmap(AOpath+"themes\\default\\crossexamination.png")) + self.setPixmap(QtGui.QPixmap(AOpath+"themes/default/crossexamination.png")) elif type == 2: if variant == 0: - self.setPixmap(QtGui.QPixmap(AOpath+"themes\\default\\notguilty.png")) + self.setPixmap(QtGui.QPixmap(AOpath+"themes/default/notguilty.png")) elif variant == 1: - self.setPixmap(QtGui.QPixmap(AOpath+"themes\\default\\guilty.png")) + self.setPixmap(QtGui.QPixmap(AOpath+"themes/default/guilty.png")) self.type = type self.variant = variant @@ -86,14 +86,14 @@ class Objections(QtGui.QLabel): self.type = type self.setGeometry(x, y, 76, 28) if type == 1: - self.notpressed_pix = QtGui.QPixmap(AOpath+"themes\\default\\holdit.png") - self.pressed_pix = QtGui.QPixmap(AOpath+"themes\\default\\holdit_selected.png") + self.notpressed_pix = QtGui.QPixmap(AOpath+"themes/default/holdit.png") + self.pressed_pix = QtGui.QPixmap(AOpath+"themes/default/holdit_selected.png") elif type == 2: - self.notpressed_pix = QtGui.QPixmap(AOpath+"themes\\default\\objection.png") - self.pressed_pix = QtGui.QPixmap(AOpath+"themes\\default\\objection_selected.png") + self.notpressed_pix = QtGui.QPixmap(AOpath+"themes/default/objection.png") + self.pressed_pix = QtGui.QPixmap(AOpath+"themes/default/objection_selected.png") elif type == 3: - self.notpressed_pix = QtGui.QPixmap(AOpath+"themes\\default\\takethat.png") - self.pressed_pix = QtGui.QPixmap(AOpath+"themes\\default\\takethat_selected.png") + self.notpressed_pix = QtGui.QPixmap(AOpath+"themes/default/takethat.png") + self.pressed_pix = QtGui.QPixmap(AOpath+"themes/default/takethat_selected.png") self.setPixmap(self.notpressed_pix) self.show() @@ -162,15 +162,15 @@ class PenaltyBars(QtGui.QLabel): self.resize(84, 14) if type == 1: #defense bar. for i in range(11): - self.penaltybars.append(QtGui.QPixmap(AOpath+"themes\\default\\defensebar"+str(i)+".png")) + self.penaltybars.append(QtGui.QPixmap(AOpath+"themes/default/defensebar"+str(i)+".png")) side = "def" elif type == 2: #prosecution bar for i in range(11): - self.penaltybars.append(QtGui.QPixmap(AOpath+"themes\\default\\prosecutionbar"+str(i)+".png")) + self.penaltybars.append(QtGui.QPixmap(AOpath+"themes/default/prosecutionbar"+str(i)+".png")) side = "pro" self.side = side - self.minusbtn = PixmapButton(parent, QtGui.QPixmap(AOpath+"themes\\default\\"+side+"minus.png")) - self.plusbtn = PixmapButton(parent, QtGui.QPixmap(AOpath+"themes\\default\\"+side+"plus.png")) + self.minusbtn = PixmapButton(parent, QtGui.QPixmap(AOpath+"themes/default/"+side+"minus.png")) + self.plusbtn = PixmapButton(parent, QtGui.QPixmap(AOpath+"themes/default/"+side+"plus.png")) self.minusbtn.clicked.connect(self.minusClick) self.plusbtn.clicked.connect(self.plusClick) self.setPixmap(self.penaltybars[10]) @@ -190,8 +190,8 @@ class PenaltyBars(QtGui.QLabel): self.minusClicked.emit(self.type) def setHealth(self, health): - self.minusbtn.setPixmap(QtGui.QPixmap(AOpath+"themes\\default\\"+self.side+"minus.png")) - self.plusbtn.setPixmap(QtGui.QPixmap(AOpath+"themes\\default\\"+self.side+"plus.png")) + self.minusbtn.setPixmap(QtGui.QPixmap(AOpath+"themes/default/"+self.side+"minus.png")) + self.plusbtn.setPixmap(QtGui.QPixmap(AOpath+"themes/default/"+self.side+"plus.png")) self.setPixmap(self.penaltybars[health]) self.health = health diff --git a/gameview.py b/gameview.py index 08b6a5c..1d6f7eb 100644 --- a/gameview.py +++ b/gameview.py @@ -76,6 +76,13 @@ def get_option(section, value, default=""): tempini.read("base/ao2xp.ini") return ini.read_ini(tempini, section, value, default) +def get_img_suffix(path): + if exists(path): return path + if exists(path+".webp"): return path+".webp" + if exists(path+".apng"): return path+".apng" + if exists(path+".gif"): return path+".gif" + return path+".png" + def get_text_color(textcolor): if textcolor == 0 or textcolor == 6: return QtGui.QColor(255, 255, 255) @@ -278,7 +285,6 @@ class AOCharMovie(QtGui.QLabel): elif self.use_pillow == 1: # apng self.pillow_frames = images.load_apng(apng_path) - #print apng_path, self.pillow_frames[0], int(self.pillow_frames[0][1] * self.pillow_speed) if len(self.pillow_frames[0]) > 1 else 0 if len(self.pillow_frames) > 1: self.pillow_timer.start(int(self.pillow_frames[0][1] * self.pillow_speed)) self.set_pillow_frame() @@ -409,43 +415,63 @@ class AOCharMovie(QtGui.QLabel): class AOMovie(QtGui.QLabel): play_once = True done = QtCore.pyqtSignal() - + use_pillow = 0 + pillow_frames = [] + pillow_frame = 0 + pillow_speed = 1 + def __init__(self, parent): QtGui.QLabel.__init__(self, parent) self.m_movie = QtGui.QMovie() self.setMovie(self.m_movie) self.m_movie.frameChanged.connect(self.frame_change) + + self.pillow_timer = QtCore.QTimer(self) + self.pillow_timer.setSingleShot(True) + self.pillow_timer.timeout.connect(self.pillow_frame_change) def set_play_once(self, once): self.play_once = once - def play(self, p_gif, p_char): - self.m_movie.stop() - - gif_path = "" - - custom_path = "" - if p_gif == "custom": - custom_path = AOpath+"characters/"+p_char+"/"+p_gif+".gif" - else: - custom_path = AOpath+"characters/"+p_char+"/"+p_gif+"_bubble.gif" - - theme_path = AOpath+"themes/default/"+p_gif+".gif" - placeholder_path = AOpath+"themes/default/placeholder.gif" - - if exists(custom_path): - gif_path = custom_path - elif exists(theme_path): - gif_path = theme_path - else: - gif_path = placeholder_path - - self.m_movie.setFileName(gif_path) + def play(self, p_image, p_char=""): + self.stop() + + gif_path = p_image + pillow_modes = {".gif": 0, ".apng": 1, ".webp": 2} + if not exists(gif_path): + pathlist = [ + get_img_suffix("base/characters/"+p_char+"/"+p_image), + get_img_suffix("base/misc/default/"+p_image), + get_img_suffix("base/themes/default/"+p_image), + "base/themes/default/placeholder.gif" + ] + + for f in pathlist: + if exists(f): + gif_path = f + break + + self.use_pillow = pillow_modes[os.path.splitext(gif_path)[1]] + if not self.use_pillow: + self.m_movie.setFileName(gif_path) + self.m_movie.start() + elif self.use_pillow == 1: # apng + self.pillow_frames = images.load_apng(gif_path) + if len(self.pillow_frames) > 1: self.pillow_timer.start(int(self.pillow_frames[0][1] * self.pillow_speed)) + self.set_pillow_frame() + elif self.use_pillow == 2: # webp + self.pillow_loops = 0 + self.pillow_frames, self.webp_loops = images.load_webp(gif_path) + if len(self.pillow_frames) > 1: self.pillow_timer.start(int(self.pillow_frames[0][1] * self.pillow_speed)) + self.set_pillow_frame() + self.show() - self.m_movie.start() def stop(self): + self.pillow_frames = [] + self.pillow_frame = 0 + self.pillow_timer.stop() self.m_movie.stop() self.hide() @@ -456,6 +482,35 @@ class AOMovie(QtGui.QLabel): self.stop() self.done.emit() + @QtCore.pyqtSlot() + def pillow_frame_change(self): + if not self.pillow_frames: return + + if len(self.pillow_frames)-1 == self.pillow_frame: + if self.play_once or (self.use_pillow == 2 and self.pillow_loops+1 == self.webp_loops): + delay(int(self.pillow_frames[self.pillow_frame][1] * self.pillow_speed)) + self.stop() + self.done.emit() + elif len(self.pillow_frames) > 1: # loop + self.pillow_loops += 1 + self.pillow_frame = 0 + self.pillow_timer.start(int(self.pillow_frames[self.pillow_frame][1] * self.pillow_speed)) + elif len(self.pillow_frames) > 1: + self.pillow_frame += 1 + self.pillow_timer.start(int(self.pillow_frames[self.pillow_frame][1] * self.pillow_speed)) + + self.set_pillow_frame() + + def set_pillow_frame(self): + if not self.pillow_frames: return + + f_img = self.pillow_frames[self.pillow_frame][0] + if f_img.size().width() != 256 or f_img.size().height() != 192: + f_img = f_img.scaled(256, 192, transformMode=QtCore.Qt.SmoothTransformation) + + f_pixmap = QtGui.QPixmap.fromImage(f_img) + self.setPixmap(f_pixmap) + class ZoomLines(QtGui.QLabel): def __init__(self, parent): @@ -540,10 +595,11 @@ class gui(QtGui.QWidget): time_mod = 40 blip = "male" blipsnd = None - chatmessage_size = 24 + chatmessage_size = 31 m_chatmessage = [] blank_blip = False chatmessage_is_empty = False + is_additive = False anim_state = 3 text_state = 2 objection_state = 0 @@ -636,10 +692,11 @@ class gui(QtGui.QWidget): self.name.resize(248, self.name.sizeHint().height()) self.wtceview = WTCE_View(self) self.WTCEsignal.connect(self.wtceview.showWTCE) - + self.objectionview = AOMovie(self) self.objectionview.done.connect(self.objection_done) - + self.effectview = AOMovie(self) + self.whiteflashlab = QtGui.QLabel(self) self.whiteflashlab.setPixmap(QtGui.QPixmap(AOpath + 'themes/default/realizationflash.png')) self.whiteflashlab.setGeometry(0, 0, 256, 192) @@ -805,9 +862,19 @@ class gui(QtGui.QWidget): self.nointerruptbtn = QtGui.QCheckBox(self) self.nointerruptbtn.setChecked(False) self.nointerruptbtn.setText('No Interrupt') - self.nointerruptbtn.resize(self.sfxbutton.sizeHint()) + self.nointerruptbtn.resize(self.nointerruptbtn.sizeHint()) self.nointerruptbtn.move(272, 272+8) - + + # AO 2.8 + self.additivebtn = QtGui.QCheckBox(self) + self.additivebtn.setChecked(False) + self.additivebtn.setText('Additive text') + self.additivebtn.resize(self.additivebtn.sizeHint()) + self.additivebtn.move(272+60, 272+28) + + self.effectdropdown = QtGui.QComboBox(self) + self.effectdropdown.setGeometry(272+60, 272+28+18, 88, 20) + self.changechar = QtGui.QPushButton(self) self.changechar.setText('Switch character') self.changechar.setGeometry(10, 344, 121, 23) @@ -844,8 +911,10 @@ class gui(QtGui.QWidget): self.prevemotepage.hide() self.nextemotepage = NextEmoteButton(self, 236, 253) self.nextemotepage.show() - self.realizationbtn = buttons.RealizationButton(self, 265, 192) + self.realizationbtn = buttons.AOToggleButton(self, 265, 192, "realization") + self.realizationbtn.clicked.connect(self.onRealizationButton) self.realizationsnd = BASS_StreamCreateFile(False, AOpath + 'sounds/general/sfx-realization.wav', 0, 0, 0) + self.shakebtn = buttons.AOToggleButton(self, 265+42, 192, "screenshake") # AO 2.8 self.customobject = buttons.CustomObjection(self, 250, 312) self.holditbtn = buttons.Objections(self, 10, 312, 1) self.objectbtn = buttons.Objections(self, 90, 312, 2) @@ -931,7 +1000,13 @@ class gui(QtGui.QWidget): self.setBackground('default') self.charselect = charselect.charselect(self) - + + def onRealizationButton(self): + if self.realizationbtn.isPressed(): + self.effectdropdown.setCurrentIndex(1) # realization + elif self.effectdropdown.currentText() == "realization": + self.effectdropdown.setCurrentIndex(0) + def onOOCLoginBtn(self): password, ok = QtGui.QInputDialog.getText(self, "Login as moderator", "Enter password.") if password and ok: @@ -1091,6 +1166,7 @@ class gui(QtGui.QWidget): def loadCharacter(self, charname): exec open("base/ao2xp_themes/"+get_option("General", "theme", "default")+"/theme.py") + self.effectdropdown.clear() self.emotedropdown.clear() self.msgqueueList.clear() self.msgqueue = [] @@ -1098,6 +1174,12 @@ class gui(QtGui.QWidget): self.selectedemote = 0 self.current_emote_page = 0 + effectslist = ini.get_effects(charname) + self.effectdropdown.setVisible(bool(effectslist)) + if effectslist: + effectslist.insert(0, "No effect") + self.effectdropdown.addItems(effectslist) + self.charname = ini.read_ini(AOpath + 'characters/' + charname + '/char.ini', "options", "name", charname) self.charside = ini.read_ini(AOpath + 'characters/' + charname + '/char.ini', "options", "side", "def") self.posdropdown.setCurrentIndex(self.posdropdown.findText(self.charside)) @@ -1111,10 +1193,12 @@ class gui(QtGui.QWidget): emote = ini.read_ini(AOpath + 'characters/' + charname + '/char.ini', "emotions", str(emoteind), 'normal#(a)normal#normal#0#') sound = ini.read_ini(AOpath + 'characters/' + charname + '/char.ini', "soundn", str(emoteind), '1') soundt = ini.read_ini(AOpath + 'characters/' + charname + '/char.ini', "soundt", str(emoteind), '0') + soundl = ini.read_ini(AOpath + 'characters/' + charname + '/char.ini', "soundl", str(emoteind), '0') # AO 2.8 emotelist = emote.split('#') del emotelist[len(emotelist) - 1] emotelist.append(sound) emotelist.append(soundt) + emotelist.append(soundl) # AO 2.8 self.charemotes.append(emotelist) if emotelist[0]: self.emotedropdown.addItem(emotelist[0]) @@ -1366,7 +1450,8 @@ class gui(QtGui.QWidget): else: modifier = 2 - msg = "MS#chat#" + msg = "MS#" + msg += "1#" # visible desk modifier msg += emote[1]+"#" #pre-anim msg += self.charname+"#" msg += emote[2]+"#" #anim @@ -1393,18 +1478,50 @@ class gui(QtGui.QWidget): if "cccc_ic_support" in self.features: msg += self.showname+"#" # custom showname - msg += (str(self.pairdropdown.currentIndex()) if self.paircheckbox.isChecked() else "-1")+"#" # pair charID - if "effects" in self.features: - msg += "^%d#" % self.pair_order.currentIndex() # pair ordering + if self.paircheckbox.isChecked(): + msg += str(self.pairdropdown.currentIndex())+"#" # pair charID + if "effects" in self.features: + msg += "^%d#" % self.pair_order.currentIndex() # pair ordering + else: + msg += "-1#" + msg += str(self.pairoffset.value())+"#" # send this anyway; AO 2.8 msg += str(int(self.nointerruptbtn.isChecked()))+"#" # NoInterrupt(TM) - + + if "looping_sfx" in self.features: # AO 2.8 + msg += emote[6]+"#" # loop sound? + msg += "%d#" % self.shakebtn.isPressed() # screen shake + emotes_to_check = [emote[1], "(b)"+emote[2], "(a)"+emote[2]] + effects_to_check = ["_FrameScreenshake", "_FrameRealization", "_FrameSFX"] + + for f_effect in effects_to_check: + packet = "" + for f_emote in emotes_to_check: + packet += f_emote + if ini.read_ini_bool(AOpath+"AO2XP.ini", "General", "network frame effects"): + sfx_frames = "|".join(ini.read_ini_tags(AOpath+"characters/"+self.charname+"/char.ini", f_emote + f_effect)) + if sfx_frames: + packet += "|" + sfx_frames + packet += "^" + msg += packet+"#" + + if "additive" in self.features: + msg += "%d#" % self.additivebtn.isChecked() + + if "effects" in self.features: + fx = self.effectdropdown.currentText() if self.effectdropdown.currentIndex() > 0 else "" + fx_sound = ini.get_effect_sound(fx, self.charname) + p_effect = ini.read_ini(AOpath+"characters/"+self.charname+"/char.ini", "options", "effects") + msg += fx + "|" + p_effect + "|" + fx_sound + "#" + self.effectdropdown.setCurrentIndex(0) + msg += "%" self.msgqueueList.addItem(self.icchatinput.text()) self.msgqueue.append(msg) self.icchatinput.clear() self.realizationbtn.setPressed(False) + self.shakebtn.setPressed(False) def setBackground(self, bg): if not exists(AOpath + 'background/' + bg): @@ -1433,7 +1550,7 @@ class gui(QtGui.QWidget): AO2chat = "cccc_ic_support" in self.features for n_string in range(self.chatmessage_size): - if n_string < len(p_contents) and n_string < 16 or AO2chat: + if n_string < len(p_contents) and (n_string < 16 or AO2chat): self.m_chatmessage[n_string] = p_contents[n_string] else: self.m_chatmessage[n_string] = "" @@ -1460,7 +1577,7 @@ class gui(QtGui.QWidget): if self.msgqueue: chatmsgcomp = str(self.msgqueue[0].split('#')[5]).decode('utf-8').replace('', '$').replace('', '%').replace('', '&').replace('', '#') - if f_char_id == self.mychar and self.m_chatmessage[CHATMSG] == chatmsgcomp: + if f_char_id == self.mychar and self.m_chatmessage[CHATMSG] == chatmsgcomp: # our message showed up del self.msgqueue[0] self.msgqueueList.takeItem(0) @@ -1488,8 +1605,17 @@ class gui(QtGui.QWidget): pass self.icLog.append('[%d:%.2d] %s: %s\n%s presented an evidence: %s' % (t[3], t[4], logcharname, self.m_chatmessage[CHATMSG], f_char, eviname)) - - objection_mod = int(self.m_chatmessage[SHOUT_MOD]) + + self.is_additive = (self.m_chatmessage[ADDITIVE] == "1") + + custom_objection = "custom" + try: objection_mod = int(self.m_chatmessage[SHOUT_MOD]) + except: + if "4&" in self.m_chatmessage[SHOUT_MOD]: # custom objection name + objection_mod = 4 + custom_objection = self.m_chatmessage[SHOUT_MOD].split("4&")[1] # get the name + else: # just in case of mindfuckery + objection_mod = 0 if objection_mod <= 4 and objection_mod >= 1: if objection_mod == 1: @@ -1499,7 +1625,10 @@ class gui(QtGui.QWidget): elif objection_mod == 3: self.objectionview.play("takethat", f_char) elif objection_mod == 4: - self.objectionview.play("custom", f_char) + if custom_objection != "custom": + self.objectionview.play("custom_objections/"+custom_objection, f_char) + else: + self.objectionview.play(custom_objection, f_char) self.playObjectionSnd(f_char, objection_mod) emote_mod = int(self.m_chatmessage[EMOTE_MOD]) @@ -1587,6 +1716,7 @@ class gui(QtGui.QWidget): def handle_chatmessage_2(self): self.zoom.setZoom(False) self.char.stop() + self.effectview.stop() if not self.m_chatmessage[SHOWNAME]: self.name.setText(self.m_chatmessage[CHARNAME]) @@ -1841,12 +1971,39 @@ class gui(QtGui.QWidget): if snd: BASS_ChannelPlay(snd, True) break - + + def do_effect(self, fx_name, fx_sound, p_char, p_folder): + effect = ini.get_effect(fx_name, p_char, p_folder) + if not effect: return + + if fx_sound: + self.playSound(fx_sound) + + if "effects" not in self.features: return + + self.effectview.set_play_once(False) + self.effectview.play(effect) + def start_chat_ticking(self): if self.text_state != 0: return - - if self.m_chatmessage[REALIZATION] == "1": + + if self.m_chatmessage[EFFECTS]: + fx_list = self.m_chatmessage[EFFECTS].split("|") + fx = fx_list[0] + fx_sound = "" + fx_folder = "" + + if len(fx_list) > 1: + fx_sound = fx_list[1] + if len(fx_list) > 2: + fx_folder = fx_list[1] + fx_sound = fx_list[2] + + if fx and fx != "-": + self.do_effect(fx, fx_sound, self.m_chatmessage[CHARNAME], fx_folder) + + elif self.m_chatmessage[REALIZATION] == "1": self.setWhiteFlash(True, 1, 125) self.ao2text.clear() @@ -1895,7 +2052,7 @@ class gui(QtGui.QWidget): else: f_character2 = f_message[self.tick_pos] f_character = QtCore.QString(f_character2) - + if f_character == " ": self.text.setText(self.text.text() + " ") self.ao2text.insertPlainText(" ") @@ -1976,6 +2133,14 @@ class gui(QtGui.QWidget): else: self.inline_color_stack.append(INLINE_GREEN) formatting_char = True + + elif f_character == "f" and self.next_character_is_not_special: # flash + self.setWhiteFlash(True, 0, 75) + self.next_character_is_not_special = False + + elif f_character == "n" and self.next_character_is_not_special: # newline + self.text.setText(self.text.text() + "\n") + self.ao2text.insertPlainText("\n") else: self.next_character_is_not_special = False @@ -2004,12 +2169,12 @@ class gui(QtGui.QWidget): self.ao2text.setAlignment(QtCore.Qt.AlignLeft) self.text.setAlignment(QtCore.Qt.AlignLeft) - if f_message[self.tick_pos] != " " or self.blank_blip: - if self.blip_pos % self.blip_rate == 0 and not formatting_char: - self.blip_pos = 0 - BASS_ChannelPlay(self.blipsnd, True) + if f_message[self.tick_pos] != " " or self.blank_blip: + if self.blip_pos % self.blip_rate == 0 and not formatting_char: + self.blip_pos = 0 + BASS_ChannelPlay(self.blipsnd, True) - self.blip_pos += 1 + self.blip_pos += 1 self.tick_pos += 1 diff --git a/images.py b/images.py index aaab320..89259c6 100644 --- a/images.py +++ b/images.py @@ -73,7 +73,7 @@ def load_webp(file): img.load() # strange thing with Pillow and animated webp's is that the img.info dictionary attr doesn't update unless you call a function like this frames.append([img.toqimage(), img.info["duration"]]) - return frames + return frames, img.info["loop"] def get_apng_duration(file): img = APNG.open(file) diff --git a/ini.py b/ini.py index f4ec6a7..7fc005a 100644 --- a/ini.py +++ b/ini.py @@ -1,5 +1,6 @@ from ConfigParser import ConfigParser from PyQt4.QtCore import QString +from os.path import exists def read_ini(file, section, value, default=""): if isinstance(file, str) or isinstance(file, QString): @@ -44,4 +45,86 @@ def read_ini_int(file, section, value, default=0): for c in conf.options(val): if c.lower() == value.lower(): return conf.getint(val, c) - return default \ No newline at end of file + return default + +def read_sectionless_ini(file, search, default=""): + if isinstance(file, QString): file = str(file) + if isinstance(search, QString): search = str(search) + + with open(file) as f: + for keys in f.read().split("\n"): + if not keys or "=" not in keys: continue + + key, value = keys.split("=") + if search.lower() == key.rstrip().lower(): + return value.lstrip() + return default + +# AO 2.8 + +def get_img_suffix(path): + if exists(path): return path + if exists(path+".webp"): return path+".webp" + if exists(path+".apng"): return path+".apng" + if exists(path+".gif"): return path+".gif" + return path+".png" + +def read_ini_tags(file, target_tag): + if isinstance(file, str) or isinstance(file, QString): + conf = ConfigParser() + conf.read(str(file)) + else: + conf = file + + r_values = [] + + if target_tag: + try: keys = conf.options(target_tag) + except: return [] + + for key in keys: + value = conf.get(target_tag, key) + r_values.append(key+"="+value) + + else: + for sect in conf.sections(): + keys = conf.options(sect) + for key in keys: + value = conf.get(target_tag, key) + r_values.append(key+"="+value) + + return r_values + +def get_effect_sound(fx_name, char): + p_effect = read_ini("base/characters/"+char+"/char.ini", "options", "effects") + p_path = "base/misc/"+p_effect+"/effects.ini" + default_path = "base/themes/default/effects/effects.ini" + + if exists(p_path): + return read_sectionless_ini(p_path, fx_name) + return read_sectionless_ini(default_path, fx_name) + +def get_effects(char): + p_effect = read_ini("base/characters/"+char+"/char.ini", "options", "effects") + p_path = "base/misc/"+p_effect+"/effects.ini" + + effects = ["realization", "hearts", "reaction", "impact"] + if not exists(p_path): return effects + + lines = open(p_path).read().split("\n") + for line in lines: + effect = line.split("=")[0].rstrip() + if effect and effect not in effects: + effects.append(effect) + + return effects + +def get_effect(effect, char, folder): + p_effect = folder + if not p_effect: p_effect = read_ini("base/characters/"+char+"/char.ini", "options", "effects") + p_path = get_img_suffix("base/misc/"+p_effect+"/"+effect) + default_path = get_img_suffix("base/themes/default/effects/"+effect) + + if not exists(p_path): + return default_path + return p_path \ No newline at end of file