bombsquad-plugin-manager/plugins/utilities/advanced_party_window.py
2022-09-17 22:06:42 +05:30

2051 lines
97 KiB
Python

# -*- coding: utf-8 -*-
# ba_meta require api 7
# AdvancedPartyWindow by Mr.Smoothy
# build on base of plasma's modifypartywindow
# added many features
# discord mr.smoothy#5824
# https://discord.gg/ucyaesh Join BCS
# Youtube : Hey Smoothy
# added advanced ID revealer
# live ping support for bcs
#Made by Mr.Smoothy - Plasma Boson
version_str = "7"
import os,urllib
import os,sys,re,json,codecs,traceback,base64
import threading
import time,copy,datetime,shutil
from _thread import start_new_thread
import urllib.request
from typing import TYPE_CHECKING, cast
import _ba
import ba
import time
import math
import threading
from dataclasses import dataclass
from bastd.ui.popup import PopupMenuWindow,PopupWindow
from bastd.ui.confirm import ConfirmWindow
from bastd.ui.colorpicker import ColorPickerExact
from typing import List, Sequence, Optional, Dict, Any, Union
import bastd.ui.party as bastd_party
cache_chat=[]
connect=_ba.connect_to_party
disconnect=_ba.disconnect_from_host
unmuted_names=[]
smo_mode=3
f_chat=False
chatlogger=False
screenmsg=True
ip_add="127.0.0.1"
p_port=43210
p_name="local"
current_ping = 0.0
enable_typing = False # this will prevent auto ping to update textwidget when user actually typing chat message
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
def newconnect_to_party(address,port=43210,print_progress=False):
global ip_add
global p_port
dd=_ba.get_connection_to_host_info()
if(dd!={} ):
_ba.disconnect_from_host()
ip_add=address
p_port=port
connect(address,port,print_progress)
else:
ip_add=address
p_port=port
# print(ip_add,p_port)
connect(ip_add,port,print_progress)
DEBUG_SERVER_COMMUNICATION = False
DEBUG_PROCESSING = False
class PingThread(threading.Thread):
"""Thread for sending out game pings."""
def __init__(self):
super().__init__()
def run(self) -> None:
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
ba.app.ping_thread_count += 1
sock: Optional[socket.socket] = None
try:
import socket
from ba.internal import get_ip_address_type
socket_type = get_ip_address_type(ip_add)
sock = socket.socket(socket_type, socket.SOCK_DGRAM)
sock.connect((ip_add,p_port))
accessible = False
starttime = time.time()
# Send a few pings and wait a second for
# a response.
sock.settimeout(1)
for _i in range(3):
sock.send(b'\x0b')
result: Optional[bytes]
try:
# 11: BA_PACKET_SIMPLE_PING
result = sock.recv(10)
except Exception:
result = None
if result == b'\x0c':
# 12: BA_PACKET_SIMPLE_PONG
accessible = True
break
time.sleep(1)
ping = (time.time() - starttime) * 1000.0
global current_ping
current_ping = round(ping,2)
except ConnectionRefusedError:
# Fine, server; sorry we pinged you. Hmph.
pass
except OSError as exc:
import errno
# Ignore harmless errors.
if exc.errno in {
errno.EHOSTUNREACH, errno.ENETUNREACH, errno.EINVAL,
errno.EPERM, errno.EACCES
}:
pass
elif exc.errno == 10022:
# Windows 'invalid argument' error.
pass
elif exc.errno == 10051:
# Windows 'a socket operation was attempted
# to an unreachable network' error.
pass
elif exc.errno == errno.EADDRNOTAVAIL:
if self._port == 0:
# This has happened. Ignore.
pass
elif ba.do_once():
print(f'Got EADDRNOTAVAIL on gather ping'
f' for addr {self._address}'
f' port {self._port}.')
else:
ba.print_exception(
f'Error on gather ping '
f'(errno={exc.errno})', once=True)
except Exception:
ba.print_exception('Error on gather ping', once=True)
finally:
try:
if sock is not None:
sock.close()
except Exception:
ba.print_exception('Error on gather ping cleanup', once=True)
ba.app.ping_thread_count -= 1
_ba.pushcall(update_ping, from_other_thread=True)
time.sleep(4)
self.run()
try:
import OnlineTranslator
tranTypes = [item for item in dir(OnlineTranslator) if item.startswith("Translator_")]
if "item" in globals():del item
except:tranTypes = []#;ba.print_exception()
RecordFilesDir = os.path.join(_ba.env()["python_directory_user"],"Configs" + os.sep)
if not os.path.exists(RecordFilesDir):os.makedirs(RecordFilesDir)
version_str = "3.0.1"
Current_Lang = None
SystemEncode = sys.getfilesystemencoding()
if not isinstance(SystemEncode,str):
SystemEncode = "utf-8"
def update_ping():
try:
_ba.set_ping_widget_value(current_ping)
except:
with _ba.Context('ui'):
if hasattr(_ba,"ping_widget") and _ba.ping_widget.exists():
ba.textwidget(edit=_ba.ping_widget,text="Ping:"+str(current_ping)+" ms")
PingThread().start()
import datetime
try:
from ba._generated.enums import TimeType
except:
from ba._enums import TimeType
class chatloggThread():
"""Thread for sending out game pings."""
def __init__(self):
super().__init__()
self.saved_msg=[]
def run(self) -> None:
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
global chatlogger
self.timerr=ba.Timer(5.0,self.chatlogg,repeat=True,timetype=TimeType.REAL)
def chatlogg(self):
global chatlogger
chats=_ba.get_chat_messages()
for msg in chats:
if msg in self.saved_msg:
pass
else:
self.save(msg)
self.saved_msg.append(msg)
if len(self.saved_msg) > 45:
self.saved_msg.pop(0)
if chatlogger:
pass
else:
self.timerr=None
def save(self,msg):
x=str(datetime.datetime.now())
t=open(os.path.join(_ba.env()["python_directory_user"],"Chat logged.txt"),"a+")
t.write(x+" : "+ msg +"\n")
t.close()
class mututalServerThread():
def run(self):
self.timer=ba.Timer(10,self.checkPlayers,repeat=True,timetype=TimeType.REAL)
def checkPlayers(self):
if _ba.get_connection_to_host_info()!={}:
server_name=_ba.get_connection_to_host_info()["name"]
players=[]
for ros in _ba.get_game_roster():
players.append(ros["display_string"])
start_new_thread(dump_mutual_servers,(players,server_name,))
def dump_mutual_servers(players,server_name):
filePath = os.path.join(RecordFilesDir, "players.json")
data={}
if os.path.isfile(filePath):
f=open(filePath,"r")
data=json.load(f)
for player in players:
if player in data:
if server_name not in data[player]:
data[player].insert(0,server_name)
data[player]=data[player][:3]
else:
data[player]=[server_name]
f=open(filePath,"w")
json.dump(data,f)
mututalServerThread().run()
class customchatThread():
"""."""
def __init__(self):
super().__init__()
global cache_chat
self.saved_msg=[]
chats=_ba.get_chat_messages()
for msg in chats: #fill up old chat , to avoid old msg popup
cache_chat.append(msg)
def run(self) -> None:
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
global chatlogger
self.timerr=ba.Timer(5.0,self.chatcheck,repeat=True,timetype=TimeType.REAL)
def chatcheck(self):
global unmuted_names
global cache_chat
chats=_ba.get_chat_messages()
for msg in chats:
if msg in cache_chat:
pass
else:
if msg.split(":")[0] in unmuted_names:
ba.screenmessage(msg,color=(0.6,0.9,0.6))
cache_chat.append(msg)
if len(self.saved_msg) > 45:
cache_chat.pop(0)
if ba.app.config.resolve('Chat Muted'):
pass
else:
self.timerr=None
def chatloggerstatus():
global chatlogger
if chatlogger:
return "Turn off Chat Logger"
else:
return "Turn on chat logger"
def _getTransText(text , isBaLstr = False , same_fb = False):
global Current_Lang
global chatlogger
if Current_Lang != 'English':
Current_Lang = 'English'
global Language_Texts
Language_Texts = {
"Chinese": {
},
"English": {
"Add_a_Quick_Reply": "Add a Quick Reply",
"Admin_Command_Kick_Confirm": "Are you sure to use admin\
command to kick %s?",
"Ban_For_%d_Seconds": "Ban for %d second(s).",
"Ban_Time_Post": "Enter the time you want to ban(Seconds).",
"Credits_for_This": "Credits for This",
"Custom_Action": "Custom Action",
"Debug_for_Host_Info": "Host Info Debug",
"Game_Record_Saved": "Game replay %s is saved.",
"Kick_ID": "Kick ID:%d",
"Mention_this_guy": "Mention this guy",
"Modify_Main_Color": "Modify Main Color",
"No_valid_player_found": "Can't find a valid player.",
"No_valid_player_id_found": "Can't find a valid player ID.",
"Normal_kick_confirm": "Are you sure to kick %s?",
"Remove_a_Quick_Reply": "Remove a Quick Reply",
"Restart_Game_Record": "Save Recording",
"Restart_Game_Record_Confirm": "Are you sure to restart recording game stream?",
"Send_%d_times": "Send for %d times",
"Something_is_added": "'%s' is added.",
"Something_is_removed": "'%s' is removed.",
"Times": "Times",
"Translator": "Translator",
"chatloggeroff":"Turn off Chat Logger",
"chatloggeron":"Turn on Chat Logger",
"screenmsgoff":"Hide ScreenMessage",
"screenmsgon":"Show ScreenMessage",
"unmutethisguy":"unmute this guy",
"mutethisguy":"mute this guy",
"muteall":"Mute all",
"unmuteall":"Unmute all"
}
}
Language_Texts = Language_Texts.get(Current_Lang)
try:
from Language_Packs import ModifiedPartyWindow_LanguagePack as ext_lan_pack
if isinstance(ext_lan_pack,dict) and isinstance(ext_lan_pack.get(Current_Lang),dict):
complete_Pack = ext_lan_pack.get(Current_Lang)
for key,item in complete_Pack.items():
Language_Texts[key] = item
except:
pass
return(Language_Texts.get(text,"#Unknown Text#" if not same_fb else text) if not isBaLstr else
ba.Lstr(resource = "??Unknown??",fallback_value = Language_Texts.get(text,"#Unknown Text#" if not same_fb else text)))
def _get_popup_window_scale() -> float:
uiscale = ba.app.ui.uiscale
return(2.3 if uiscale is ba.UIScale.SMALL else
1.65 if uiscale is ba.UIScale.MEDIUM else 1.23)
def _creat_Lstr_list(string_list : list = []) -> list:
return([ba.Lstr(resource = "??Unknown??",fallback_value = item) for item in string_list])
customchatThread().run()
class ModifiedPartyWindow(bastd_party.PartyWindow):
def __init__(self, origin: Sequence[float] = (0, 0)):
_ba.set_party_window_open(True)
self._r = 'partyWindow'
self.msg_user_selected=''
self._popup_type: Optional[str] = None
self._popup_party_member_client_id: Optional[int] = None
self._popup_party_member_is_host: Optional[bool] = None
self._width = 500
uiscale = ba.app.ui.uiscale
self._height = (365 if uiscale is ba.UIScale.SMALL else
480 if uiscale is ba.UIScale.MEDIUM else 600)
#Custom color here
self._bg_color = ba.app.config.get("PartyWindow_Main_Color",(0.40, 0.55, 0.20)) if not isinstance(self._getCustomSets().get("Color"),(list,tuple)) else self._getCustomSets().get("Color")
if not isinstance(self._bg_color,(list,tuple)) or not len(self._bg_color) == 3:self._bg_color = (0.40, 0.55, 0.20)
ba.Window.__init__(self,root_widget=ba.containerwidget(
size=(self._width, self._height),
transition='in_scale',
color=self._bg_color,
parent=_ba.get_special_widget('overlay_stack'),
on_outside_click_call=self.close_with_sound,
scale_origin_stack_offset=origin,
scale=(2.0 if uiscale is ba.UIScale.SMALL else
1.35 if uiscale is ba.UIScale.MEDIUM else 1.0),
stack_offset=(0, -10) if uiscale is ba.UIScale.SMALL else (
240, 0) if uiscale is ba.UIScale.MEDIUM else (330, 20)))
self._cancel_button = ba.buttonwidget(parent=self._root_widget,
scale=0.7,
position=(30, self._height - 47),
size=(50, 50),
label='',
on_activate_call=self.close,
autoselect=True,
color=(0.45, 0.63, 0.15),
icon=ba.gettexture('crossOut'),
iconscale=1.2)
self._smoothy_button = ba.buttonwidget(parent=self._root_widget,
scale=0.6,
position=(5, self._height - 47 -40),
size=(50, 50),
label='69',
on_activate_call=self.smoothy_roster_changer,
autoselect=True,
color=(0.45, 0.63, 0.15),
icon=ba.gettexture('replayIcon'),
iconscale=1.2)
ba.containerwidget(edit=self._root_widget,
cancel_button=self._cancel_button)
self._menu_button = ba.buttonwidget(
parent=self._root_widget,
scale=0.7,
position=(self._width - 60, self._height - 47),
size=(50, 50),
label="\xee\x80\x90",
autoselect=True,
button_type='square',
on_activate_call=ba.WeakCall(self._on_menu_button_press),
color=(0.55, 0.73, 0.25),
icon=ba.gettexture('menuButton'),
iconscale=1.2)
info = _ba.get_connection_to_host_info()
if info.get('name', '') != '':
title = info['name']
else:
title = ba.Lstr(resource=self._r + '.titleText')
self._title_text = ba.textwidget(parent=self._root_widget,
scale=0.9,
color=(0.5, 0.7, 0.5),
text=title,
size=(120, 20),
position=(self._width * 0.5-60,
self._height - 29),
on_select_call=self.title_selected,
selectable=True,
maxwidth=self._width * 0.7,
h_align='center',
v_align='center')
self._empty_str = ba.textwidget(parent=self._root_widget,
scale=0.75,
size=(0, 0),
position=(self._width * 0.5,
self._height - 65),
maxwidth=self._width * 0.85,
text="no one",
h_align='center',
v_align='center')
self._scroll_width = self._width - 50
self._scrollwidget = ba.scrollwidget(parent=self._root_widget,
size=(self._scroll_width,
self._height - 200),
position=(30, 80),
color=(0.4, 0.6, 0.3))
self._columnwidget = ba.columnwidget(parent=self._scrollwidget,
border=2,
margin=0)
ba.widget(edit=self._menu_button, down_widget=self._columnwidget)
self._muted_text = ba.textwidget(
parent=self._root_widget,
position=(self._width * 0.5, self._height * 0.5),
size=(0, 0),
h_align='center',
v_align='center',
text="")
self._chat_texts: List[ba.Widget] = []
self._chat_texts_haxx: List[ba.Widget] = []
# add all existing messages if chat is not muted
# print("updates")
if True: #smoothy - always show chat in partywindow
msgs = _ba.get_chat_messages()
for msg in msgs:
self._add_msg(msg)
# print(msg)
# else:
# msgs=_ba.get_chat_messages()
# for msg in msgs:
# print(msg);
# txt = ba.textwidget(parent=self._columnwidget,
# text=msg,
# h_align='left',
# v_align='center',
# size=(0, 13),
# scale=0.55,
# maxwidth=self._scroll_width * 0.94,
# shadow=0.3,
# flatness=1.0)
# self._chat_texts.append(txt)
# if len(self._chat_texts) > 40:
# first = self._chat_texts.pop(0)
# first.delete()
# ba.containerwidget(edit=self._columnwidget, visible_child=txt)
self.ping_widget = txt = ba.textwidget(
parent=self._root_widget,
scale = 0.6,
size=(20, 5),
color=(0.45, 0.63, 0.15),
position=(self._width/2 -20, 50),
text='',
selectable=True,
autoselect=False,
v_align='center')
_ba.ping_widget = self.ping_widget
def enable_chat_mode():
pass
self._text_field = txt = ba.textwidget(
parent=self._root_widget,
editable=True,
size=(530-80, 40),
position=(44+60, 39),
text='',
maxwidth=494,
shadow=0.3,
flatness=1.0,
description=ba.Lstr(resource=self._r + '.chatMessageText'),
autoselect=True,
v_align='center',
corner_scale=0.7)
# for m in _ba.get_chat_messages():
# if m:
# ttchat=ba.textwidget(
# parent=self._columnwidget,
# size=(10,10),
# h_align='left',
# v_align='center',
# text=str(m),
# scale=0.6,
# flatness=0,
# color=(2,2,2),
# shadow=0,
# always_highlight=True
# )
ba.widget(edit=self._scrollwidget,
autoselect=True,
left_widget=self._cancel_button,
up_widget=self._cancel_button,
down_widget=self._text_field)
ba.widget(edit=self._columnwidget,
autoselect=True,
up_widget=self._cancel_button,
down_widget=self._text_field)
ba.containerwidget(edit=self._root_widget, selected_child=txt)
btn = ba.buttonwidget(parent=self._root_widget,
size=(50, 35),
label=ba.Lstr(resource=self._r + '.sendText'),
button_type='square',
autoselect=True,
position=(self._width - 70, 35),
on_activate_call=self._send_chat_message)
def _times_button_on_click():
# self._popup_type = "send_Times_Press"
# allow_range = 100 if _ba.get_foreground_host_session() is not None else 4
# PopupMenuWindow(position=self._times_button.get_screen_space_center(),
# scale=_get_popup_window_scale(),
# choices=[str(index) for index in range(1,allow_range + 1)],
# choices_display=_creat_Lstr_list([_getTransText("Send_%d_times")%int(index) for index in range(1,allow_range + 1)]),
# current_choice="Share_Server_Info",
# delegate=self)
Quickreply = self._get_quick_responds()
if len(Quickreply) > 0:
PopupMenuWindow(position=self._times_button.get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=Quickreply,
choices_display=_creat_Lstr_list(Quickreply),
current_choice=Quickreply[0],
delegate=self)
self._popup_type = "QuickMessageSelect"
self._send_msg_times = 1
self._times_button = ba.buttonwidget(parent=self._root_widget,
size=(50, 35),
label="Quick",
button_type='square',
autoselect=True,
position=(30, 35),
on_activate_call=_times_button_on_click)
ba.textwidget(edit=txt, on_return_press_call=btn.activate)
self._name_widgets: List[ba.Widget] = []
self._roster: Optional[List[Dict[str, Any]]] = None
self.smoothy_mode=1
self.full_chat_mode=False
self._update_timer = ba.Timer(1.0,
ba.WeakCall(self._update),
repeat=True,
timetype=ba.TimeType.REAL)
self._update()
def title_selected(self):
self.full_chat_mode= self.full_chat_mode ==False
self._update()
def smoothy_roster_changer(self):
self.smoothy_mode=(self.smoothy_mode+1)%3
self._update()
def on_chat_message(self, msg: str) -> None:
"""Called when a new chat message comes through."""
# print("on_chat"+msg)
if True:
self._add_msg(msg)
def _on_chat_press(self,msg,widget):
global unmuted_names
if msg.split(":")[0] in unmuted_names:
choices=['mute']
choices_display=[_getTransText("mutethisguy",isBaLstr = True)]
else:
choices=['unmute']
choices_display=[_getTransText("unmutethisguy",isBaLstr = True)]
PopupMenuWindow(position=widget.get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=choices,
choices_display=choices_display,
current_choice="@ this guy",
delegate=self)
self.msg_user_selected=msg.split(":")[0]
self._popup_type = "chatmessagepress"
# _ba.chatmessage("pressed")
def _add_msg(self, msg: str) -> None:
try:
if ba.app.config.resolve('Chat Muted'):
txt = ba.textwidget(parent=self._columnwidget,
text=msg,
h_align='left',
v_align='center',
size=(130, 13),
scale=0.55,
position=(-0.6,0),
selectable=True,
click_activate=True,
maxwidth=self._scroll_width * 0.94,
shadow=0.3,
flatness=1.0)
ba.textwidget(edit=txt,
on_activate_call=ba.Call(
self._on_chat_press,
msg,txt))
else:
txt = ba.textwidget(parent=self._columnwidget,
text=msg,
h_align='left',
v_align='center',
size=(0, 13),
scale=0.55,
maxwidth=self._scroll_width * 0.94,
shadow=0.3,
flatness=1.0)
# btn = ba.buttonwidget(parent=self._columnwidget,
# scale=0.7,
# size=(100,20),
# label="smoothy buttin",
# icon=ba.gettexture('replayIcon'),
# texture=None,
# )
self._chat_texts_haxx.append(txt)
if len(self._chat_texts_haxx) > 40:
first = self._chat_texts_haxx.pop(0)
first.delete()
ba.containerwidget(edit=self._columnwidget, visible_child=txt)
except Exception:
pass
def _add_msg_when_muted(self, msg: str) -> None:
txt = ba.textwidget(parent=self._columnwidget,
text=msg,
h_align='left',
v_align='center',
size=(0, 13),
scale=0.55,
maxwidth=self._scroll_width * 0.94,
shadow=0.3,
flatness=1.0)
self._chat_texts.append(txt)
if len(self._chat_texts) > 40:
first = self._chat_texts.pop(0)
first.delete()
ba.containerwidget(edit=self._columnwidget, visible_child=txt)
def color_picker_closing(self, picker) -> None:
ba._appconfig.commit_app_config()
def color_picker_selected_color(self, picker, color) -> None:
#bs.animateArray(self._root_widget,"color",3,{0:self._bg_color,1500:color})
ba.containerwidget(edit=self._root_widget,color=color)
self._bg_color = color
ba.app.config["PartyWindow_Main_Color"] = color
def _on_nick_rename_press(self,arg) -> None:
ba.containerwidget(edit=self._root_widget, transition='out_scale')
c_width = 600
c_height = 250
uiscale = ba.app.ui.uiscale
self._nick_rename_window = cnt = ba.containerwidget(
scale=(1.8 if uiscale is ba.UIScale.SMALL else
1.55 if uiscale is ba.UIScale.MEDIUM else 1.0),
size=(c_width, c_height),
transition='in_scale')
ba.textwidget(parent=cnt,
size=(0, 0),
h_align='center',
v_align='center',
text='Enter nickname',
maxwidth=c_width * 0.8,
position=(c_width * 0.5, c_height - 60))
id=self._get_nick(arg)
self._player_nick_text = txt89 = ba.textwidget(
parent=cnt,
size=(c_width * 0.8, 40),
h_align='left',
v_align='center',
text=id,
editable=True,
description='Players nick name',
position=(c_width * 0.1, c_height - 140),
autoselect=True,
maxwidth=c_width * 0.7,
max_chars=200)
cbtn = ba.buttonwidget(
parent=cnt,
label=ba.Lstr(resource='cancelText'),
on_activate_call=ba.Call(
lambda c: ba.containerwidget(edit=c, transition='out_scale'),
cnt),
size=(180, 60),
position=(30, 30),
autoselect=True)
okb = ba.buttonwidget(parent=cnt,
label='Rename',
size=(180, 60),
position=(c_width - 230, 30),
on_activate_call=ba.Call(
self._add_nick,arg),
autoselect=True)
ba.widget(edit=cbtn, right_widget=okb)
ba.widget(edit=okb, left_widget=cbtn)
ba.textwidget(edit=txt89, on_return_press_call=okb.activate)
ba.containerwidget(edit=cnt, cancel_button=cbtn, start_button=okb)
def _add_nick(self,arg):
config = ba.app.config
new_name_raw = cast(str, ba.textwidget(query=self._player_nick_text))
if arg:
if not isinstance(config.get('players nick'), dict):
config['players nick'] = {}
config['players nick'][arg] = new_name_raw
config.commit()
ba.containerwidget(edit=self._nick_rename_window,
transition='out_scale')
# ba.containerwidget(edit=self._root_widget,transition='in_scale')
def _get_nick(self,id):
config=ba.app.config
if not isinstance(config.get('players nick'), dict):
return "add nick"
elif id in config['players nick']:
return config['players nick'][id]
else:
return "add nick"
def _reset_game_record(self) -> None:
try:
dir_path = _ba.get_replays_dir();curFilePath = os.path.join(dir_path+os.sep,"__lastReplay.brp").encode(SystemEncode)
newFileName = str(ba.Lstr(resource="replayNameDefaultText").evaluate()+" (%s)"%(datetime.datetime.strftime(datetime.datetime.now(),"%Y_%m_%d_%H_%M_%S"))+".brp")
newFilePath = os.path.join(dir_path+os.sep,newFileName).encode(SystemEncode)
#print(curFilePath, newFilePath)
#os.rename(curFilePath,newFilePath)
shutil.copyfile(curFilePath, newFilePath)
_ba.reset_game_activity_tracking()
ba.screenmessage(_getTransText("Game_Record_Saved")%newFileName,color = (1,1,1))
except:ba.print_exception();ba.screenmessage(ba.Lstr(resource="replayWriteErrorText").evaluate()+"\
"+traceback.format_exc(),color = (1,0,0))
def _on_menu_button_press(self) -> None:
is_muted = ba.app.config.resolve('Chat Muted')
global chatlogger
choices = ["unmute" if is_muted else "mute","screenmsg","addQuickReply","removeQuickReply","chatlogger","credits"]
DisChoices = [_getTransText("unmuteall",isBaLstr = True) if is_muted else _getTransText("muteall",isBaLstr = True),
_getTransText("screenmsgoff",isBaLstr = True) if screenmsg else _getTransText("screenmsgon",isBaLstr = True),
_getTransText("Add_a_Quick_Reply",isBaLstr = True),
_getTransText("Remove_a_Quick_Reply",isBaLstr = True),
_getTransText("chatloggeroff",isBaLstr = True) if chatlogger else _getTransText("chatloggeron",isBaLstr = True),
_getTransText("Credits_for_This",isBaLstr = True)
]
if len(tranTypes) > 0 :
choices.append("translator");DisChoices.append(_getTransText("Translator",isBaLstr = True))
choices.append("resetGameRecord")
DisChoices.append(_getTransText("Restart_Game_Record",isBaLstr = True))
if self._getCustomSets().get("Enable_HostInfo_Debug",False):
choices.append("hostInfo_Debug");DisChoices.append(_getTransText("Debug_for_Host_Info",isBaLstr = True))
PopupMenuWindow(
position=self._menu_button.get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=choices,
choices_display=DisChoices,
current_choice="unmute" if is_muted else "mute", delegate=self)
self._popup_type = "menu"
def _on_party_member_press(self, client_id: int, is_host: bool,
widget: ba.Widget) -> None:
# if we"re the host, pop up "kick" options for all non-host members
if _ba.get_foreground_host_session() is not None:
kick_str = ba.Lstr(resource="kickText")
else:kick_str = ba.Lstr(resource="kickVoteText")
choices = ["kick","@ this guy","info","adminkick"]
choices_display = [kick_str,_getTransText("Mention_this_guy",isBaLstr = True),ba.Lstr(resource="??Unknown??",fallback_value="Info"),
ba.Lstr(resource = "??Unknown??",fallback_value = _getTransText("Kick_ID")%client_id)]
try:
if len(self._getCustomSets().get("partyMemberPress_Custom") if isinstance(self._getCustomSets().get("partyMemberPress_Custom"),dict) else {}) > 0:
choices.append("customAction");choices_display.append(_getTransText("Custom_Action",isBaLstr = True))
except:ba.print_exception()
PopupMenuWindow(position=widget.get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=choices,
choices_display=choices_display,
current_choice="@ this guy",
delegate=self)
self._popup_party_member_client_id = client_id
self._popup_party_member_is_host = is_host
self._popup_type = "partyMemberPress"
def _send_chat_message(self) -> None:
sendtext = ba.textwidget(query=self._text_field)
if sendtext==".ip":
_ba.chatmessage("IP "+ip_add+" PORT "+str(p_port))
ba.textwidget(edit=self._text_field,text="")
return
elif sendtext==".info":
if _ba.get_connection_to_host_info() == {}:
s_build=0
else:
s_build = _ba.get_connection_to_host_info()['build_number']
s_v="0"
if s_build <=14365:
s_v=" 1.4.148 or below"
elif s_build <=14377:
s_v="1.4.148 < x < = 1.4.155 "
elif s_build>=20001 and s_build < 20308:
s_v ="1.5"
elif s_build >= 20308 and s_build < 20591:
s_v="1.6 "
else:
s_v ="1.7 and above "
_ba.chatmessage("script version "+s_v+"- build "+str(s_build))
ba.textwidget(edit=self._text_field,text="")
return
elif sendtext==".ping disabled":
PingThread(ip_add, p_port).start()
ba.textwidget(edit=self._text_field,text="")
return
elif sendtext==".save":
info = _ba.get_connection_to_host_info()
config = ba.app.config
if info.get('name', '') != '':
title = info['name']
if not isinstance(config.get('Saved Servers'), dict):
config['Saved Servers'] = {}
config['Saved Servers'][f'{ip_add}@{p_port}'] = {
'addr': ip_add,
'port': p_port,
'name': title
}
config.commit()
ba.screenmessage("Server saved to manual")
ba.playsound(ba.getsound('gunCocking'))
ba.textwidget(edit=self._text_field,text="")
return
# elif sendtext != "":
# for index in range(getattr(self,"_send_msg_times",1)):
if '\\' in sendtext:
sendtext = sendtext.replace('\\d', ('\ue048'))
sendtext = sendtext.replace('\\c', ('\ue043'))
sendtext = sendtext.replace('\\h', ('\ue049'))
sendtext = sendtext.replace('\\s', ('\ue046'))
sendtext = sendtext.replace('\\n', ('\ue04b'))
sendtext = sendtext.replace('\\f', ('\ue04f'))
sendtext = sendtext.replace('\\g', ('\ue027'))
sendtext = sendtext.replace('\\i', ('\ue03a'))
sendtext = sendtext.replace('\\m', ('\ue04d'))
sendtext = sendtext.replace('\\t', ('\ue01f'))
sendtext = sendtext.replace('\\bs', ('\ue01e'))
sendtext = sendtext.replace('\\j', ('\ue010'))
sendtext = sendtext.replace('\\e', ('\ue045'))
sendtext = sendtext.replace('\\l', ('\ue047'))
sendtext = sendtext.replace('\\a', ('\ue020'))
sendtext = sendtext.replace('\\b', ('\ue00c'))
if sendtext=="":
sendtext=" "
msg=sendtext
msg1=msg.split(" ")
ms2=""
if(len(msg1)>11):
hp=int(len(msg1)/2)
for m in range (0,hp):
ms2=ms2+" "+msg1[m]
_ba.chatmessage(ms2)
ms2=""
for m in range (hp,len(msg1)):
ms2=ms2+" "+msg1[m]
_ba.chatmessage(ms2)
else:
_ba.chatmessage(msg)
ba.textwidget(edit=self._text_field,text="")
# else:
# Quickreply = self._get_quick_responds()
# if len(Quickreply) > 0:
# PopupMenuWindow(position=self._text_field.get_screen_space_center(),
# scale=_get_popup_window_scale(),
# choices=Quickreply,
# choices_display=_creat_Lstr_list(Quickreply),
# current_choice=Quickreply[0],
# delegate=self)
# self._popup_type = "QuickMessageSelect"
# else:
# _ba.chatmessage(sendtext)
# ba.textwidget(edit=self._text_field,text="")
def _get_quick_responds(self):
if not hasattr(self,"_caches") or not isinstance(self._caches,dict):self._caches = {}
try:
filePath = os.path.join(RecordFilesDir,"Quickmessage.txt")
if os.path.exists(RecordFilesDir) is not True:
os.makedirs(RecordFilesDir)
if not os.path.isfile(filePath):
with open(filePath,"wb") as writer:
writer.write(({"Chinese":u"\xe5\x8e\x89\xe5\xae\xb3\xef\xbc\x8c\xe8\xbf\x98\xe6\x9c\x89\xe8\xbf\x99\xe7\xa7\x8d\xe9\xaa\x9a\xe6\x93\x8d\xe4\xbd\x9c!\
\xe4\xbd\xa0\xe2\x84\xa2\xe8\x83\xbd\xe5\x88\xab\xe6\x89\x93\xe9\x98\x9f\xe5\x8f\x8b\xe5\x90\x97\xef\xbc\x9f\
\xe5\x8f\xaf\xe4\xbb\xa5\xe5\x95\x8a\xe5\xb1\x85\xe7\x84\xb6\xe8\x83\xbd\xe8\xbf\x99\xe4\xb9\x88\xe7\x8e\xa9\xef\xbc\x9f"}.get(Current_Lang,"What the hell?\nDude that's amazing!")).encode("UTF-8"))
if os.path.getmtime(filePath) != self._caches.get("Vertify_Quickresponse_Text"):
with open(filePath,"rU", encoding = "UTF-8-sig") as Reader:
Text = Reader.read()
if Text.startswith(str(codecs.BOM_UTF8)):
Text = Text[3:]
self._caches["quickReplys"] = (Text).split("\\n")
self._caches["Vertify_Quickresponse_Text"] = os.path.getmtime(filePath)
return(self._caches.get("quickReplys",[]))
except:ba.print_exception();ba.screenmessage(ba.Lstr(resource="errorText"),(1,0,0));ba.playsound(ba.getsound("error"))
def _write_quick_responds(self,data):
try:
with open(os.path.join(RecordFilesDir,"Quickmessage.txt"),"wb") as writer:
writer.write("\\n".join(data).encode("utf-8"))
except:ba.print_exception();ba.screenmessage(ba.Lstr(resource="errorText"),(1,0,0));ba.playsound(ba.getsound("error"))
def _getCustomSets(self):
try:
if not hasattr(self,"_caches") or not isinstance(self._caches,dict):self._caches = {}
try:
from VirtualHost import MainSettings
if MainSettings.get("Custom_PartyWindow_Sets",{}) != self._caches.get("PartyWindow_Sets",{}):
self._caches["PartyWindow_Sets"] = MainSettings.get("Custom_PartyWindow_Sets",{})
except:
try:
filePath = os.path.join(RecordFilesDir,"Settings.json")
if os.path.isfile(filePath):
if os.path.getmtime(filePath) != self._caches.get("Vertify_MainSettings.json_Text"):
with open(filePath,"rU", encoding = "UTF-8-sig") as Reader:
Text = Reader.read()
if Text.startswith(str(codecs.BOM_UTF8)):
Text = Text[3:]
self._caches["PartyWindow_Sets"] = json.loads(Text.decode("utf-8")).get("Custom_PartyWindow_Sets",{})
self._caches["Vertify_MainSettings.json_Text"] = os.path.getmtime(filePath)
except:ba.print_exception()
return(self._caches.get("PartyWindow_Sets") if isinstance(self._caches.get("PartyWindow_Sets"),dict) else {})
except:ba.print_exception()
def _getObjectByID(self,type = "playerName",ID = None):
if ID is None:ID = self._popup_party_member_client_id
type = type.lower();output = []
for roster in self._roster:
if type.startswith("all"):
if type in ("roster","fullrecord"):output += [roster]
elif type.find("player") != -1 and roster["players"] != []:
if type.find("namefull") != -1:output += [(i["name_full"]) for i in roster["players"]]
elif type.find("name") != -1:output += [(i["name"]) for i in roster["players"]]
elif type.find("playerid") != -1:output += [i["id"] for i in roster["players"]]
elif type.lower() in ("account","displaystring"):output += [(roster["display_string"])]
elif roster["client_id"] == ID and not type.startswith("all"):
try:
if type in ("roster","fullrecord"):return(roster)
elif type.find("player") != -1 and roster["players"] != []:
if len(roster["players"]) == 1 or type.find("singleplayer") != -1:
if type.find("namefull") != -1:return((roster["players"][0]["name_full"]))
elif type.find("name") != -1:return((roster["players"][0]["name"]))
elif type.find("playerid") != -1:return(roster["players"][0]["id"])
else:
if type.find("namefull") != -1:return([(i["name_full"]) for i in roster["players"]])
elif type.find("name") != -1:return([(i["name"]) for i in roster["players"]])
elif type.find("playerid") != -1:return([i["id"] for i in roster["players"]])
elif type.lower() in ("account","displaystring"):return((roster["display_string"]))
except:ba.print_exception()
return(None if len(output) == 0 else output)
def _edit_text_msg_box(self,text,type = "rewrite"):
if not isinstance(type,str) or not isinstance(text,str):return
type = type.lower();text = (text)
if type.find("add") != -1:ba.textwidget(edit=self._text_field,text=ba.textwidget(query=self._text_field)+text)
else:ba.textwidget(edit=self._text_field,text=text)
def _send_admin_kick_command(self):_ba.chatmessage("/kick " + str(self._popup_party_member_client_id))
def new_input_window_callback(self,got_text, flag, code):
if got_text:
if flag.startswith("Host_Kick_Player:"):
try:
result = _ba.disconnect_client(self._popup_party_member_client_id, ban_time=int(code))
if not result:
ba.playsound(ba.getsound('error'))
ba.screenmessage(
ba.Lstr(resource='getTicketsWindow.unavailableText'),
color=(1, 0, 0))
except:
ba.playsound(ba.getsound('error'))
print(traceback.format_exc())
def _kick_selected_player(self):
"""
result = _ba._disconnectClient(self._popup_party_member_client_id,banTime)
if not result:
ba.playsound(ba.getsound("error"))
ba.screenmessage(ba.Lstr(resource="getTicketsWindow.unavailableText"),color=(1,0,0))
"""
if self._popup_party_member_client_id != -1:
if _ba.get_foreground_host_session() is not None:
self._popup_type = "banTimePress"
choices = [0,30,60,120,300,600,900,1800,3600,7200,99999999] if not (isinstance(self._getCustomSets().get("Ban_Time_List"),list)
and all([isinstance(item,int) for item in self._getCustomSets().get("Ban_Time_List")])) else self._getCustomSets().get("Ban_Time_List")
PopupMenuWindow(position=self.get_root_widget().get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=[str(item) for item in choices],
choices_display=_creat_Lstr_list([_getTransText("Ban_For_%d_Seconds")%item for item in choices]),
current_choice="Share_Server_Info",
delegate=self)
"""
NewInputWindow(origin_widget = self.get_root_widget(),
delegate = self,post_text = _getTransText("Ban_Time_Post"),
default_code = "300",flag = "Host_Kick_Player:"+str(self._popup_party_member_client_id))
"""
else:
# kick-votes appeared in build 14248
if (_ba.get_connection_to_host_info().get('build_number', 0) <
14248):
ba.playsound(ba.getsound('error'))
ba.screenmessage(
ba.Lstr(resource='getTicketsWindow.unavailableText'),
color=(1, 0, 0))
else:
# Ban for 5 minutes.
result = _ba.disconnect_client(self._popup_party_member_client_id, ban_time=5 * 60)
if not result:
ba.playsound(ba.getsound('error'))
ba.screenmessage(
ba.Lstr(resource='getTicketsWindow.unavailableText'),
color=(1, 0, 0))
else:
ba.playsound(ba.getsound('error'))
ba.screenmessage(
ba.Lstr(resource='internal.cantKickHostError'),
color=(1, 0, 0))
#NewShareCodeWindow(origin_widget=self.get_root_widget(), delegate=None,code = "300",execText = u"_ba._disconnectClient(%d,{Value})"%self._popup_party_member_client_id)
def joinbombspot(self):
import random
url=['https://discord.gg/CbxhJTrRta','https://discord.gg/ucyaesh']
ba.open_url(url[random.randint(0,1)])
def _update(self) -> None:
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
# pylint: disable=too-many-nested-blocks
# # update muted state
# if ba.app.config.resolve('Chat Muted'):
# ba.textwidget(edit=self._muted_text, color=(1, 1, 1, 0.3))
# # clear any chat texts we're showing
# if self._chat_texts:
# while self._chat_texts:
# first = self._chat_texts.pop()
# first.delete()
# else:
# ba.textwidget(edit=self._muted_text, color=(1, 1, 1, 0.0))
# update roster section
roster = _ba.get_game_roster()
global f_chat
global smo_mode
if roster != self._roster or smo_mode!=self.smoothy_mode or f_chat!=self.full_chat_mode:
self._roster = roster
smo_mode=self.smoothy_mode
f_chat=self.full_chat_mode
# clear out old
for widget in self._name_widgets:
widget.delete()
self._name_widgets = []
if not self._roster :
top_section_height = 60
ba.textwidget(edit=self._empty_str,
text=ba.Lstr(resource=self._r + '.emptyText'))
ba.scrollwidget(edit=self._scrollwidget,
size=(self._width - 50,
self._height - top_section_height - 110),
position=(30, 80))
elif self.full_chat_mode:
top_section_height = 60
ba.scrollwidget(edit=self._scrollwidget,
size=(self._width - 50,
self._height - top_section_height - 75),
position=(30, 80))
else:
columns = 1 if len(
self._roster) == 1 else 2 if len(self._roster) == 2 else 3
rows = int(math.ceil(float(len(self._roster)) / columns))
c_width = (self._width * 0.9) / max(3, columns)
c_width_total = c_width * columns
c_height = 24
c_height_total = c_height * rows
for y in range(rows):
for x in range(columns):
index = y * columns + x
if index < len(self._roster):
t_scale = 0.65
pos = (self._width * 0.53 - c_width_total * 0.5 +
c_width * x - 23,
self._height - 65 - c_height * y - 15)
# if there are players present for this client, use
# their names as a display string instead of the
# client spec-string
try:
if self.smoothy_mode ==1 and self._roster[index]['players']:
# if there's just one, use the full name;
# otherwise combine short names
if len(self._roster[index]
['players']) == 1:
p_str = self._roster[index]['players'][
0]['name_full']
else:
p_str = ('/'.join([
entry['name'] for entry in
self._roster[index]['players']
]))
if len(p_str) > 25:
p_str = p_str[:25] + '...'
elif self.smoothy_mode==0 :
p_str = self._roster[index][
'display_string']
p_str= self._get_nick(p_str)
else:
p_str = self._roster[index][
'display_string']
except Exception:
ba.print_exception(
'Error calcing client name str.')
p_str = '???'
try:
widget = ba.textwidget(parent=self._root_widget,
position=(pos[0], pos[1]),
scale=t_scale,
size=(c_width * 0.85, 30),
maxwidth=c_width * 0.85,
color=(1, 1,
1) if index == 0 else
(1, 1, 1),
selectable=True,
autoselect=True,
click_activate=True,
text=ba.Lstr(value=p_str),
h_align='left',
v_align='center')
self._name_widgets.append(widget)
except Exception:
pass
# in newer versions client_id will be present and
# we can use that to determine who the host is.
# in older versions we assume the first client is
# host
if self._roster[index]['client_id'] is not None:
is_host = self._roster[index][
'client_id'] == -1
else:
is_host = (index == 0)
# FIXME: Should pass client_id to these sort of
# calls; not spec-string (perhaps should wait till
# client_id is more readily available though).
try:
ba.textwidget(edit=widget,
on_activate_call=ba.Call(
self._on_party_member_press,
self._roster[index]['client_id'],
is_host, widget))
except Exception:
pass
pos = (self._width * 0.53 - c_width_total * 0.5 +
c_width * x,
self._height - 65 - c_height * y)
# Make the assumption that the first roster
# entry is the server.
# FIXME: Shouldn't do this.
if is_host:
twd = min(
c_width * 0.85,
_ba.get_string_width(
p_str, suppress_warning=True) *
t_scale)
try:
self._name_widgets.append(
ba.textwidget(
parent=self._root_widget,
position=(pos[0] + twd + 1,
pos[1] - 0.5),
size=(0, 0),
h_align='left',
v_align='center',
maxwidth=c_width * 0.96 - twd,
color=(0.1, 1, 0.1, 0.5),
text=ba.Lstr(resource=self._r +
'.hostText'),
scale=0.4,
shadow=0.1,
flatness=1.0))
except Exception:
pass
try:
ba.textwidget(edit=self._empty_str, text='')
ba.scrollwidget(edit=self._scrollwidget,
size=(self._width - 50,
max(100, self._height - 139 -
c_height_total)),
position=(30, 80))
except Exception:
pass
def hide_screen_msg(self):
file=open('ba_data/data/languages/english.json')
eng=json.loads(file.read())
file.close()
eng['internal']['playerJoinedPartyText']=''
eng['internal']['playerLeftPartyText']=''
eng['internal']['chatBlockedText']=''
eng['kickVoteStartedText']=''
# eng['kickVoteText']=''
eng['kickWithChatText']=''
eng['kickOccurredText']=''
eng['kickVoteFailedText']=''
eng['votesNeededText']=''
eng['playerDelayedJoinText']=''
eng['playerLeftText']=''
eng['kickQuestionText']=''
file=open('ba_data/data/languages/english.json',"w")
json.dump(eng,file)
file.close()
ba.app.lang.setlanguage(None)
def restore_screen_msg(self):
file=open('ba_data/data/languages/english.json')
eng=json.loads(file.read())
file.close()
eng['internal']['playerJoinedPartyText']="${NAME} joined the pawri!"
eng['internal']['playerLeftPartyText']="${NAME} left the pawri."
eng['internal']['chatBlockedText']="${NAME} is chat-blocked for ${TIME} seconds."
eng['kickVoteStartedText']="A kick vote has been started for ${NAME}."
# eng['kickVoteText']=''
eng['kickWithChatText']="Type ${YES} in chat for yes and ${NO} for no."
eng['kickOccurredText']="${NAME} was kicked."
eng['kickVoteFailedText']="Kick-vote failed."
eng['votesNeededText']="${NUMBER} votes needed"
eng['playerDelayedJoinText']="${PLAYER} will enter at the start of the next round."
eng['playerLeftText']="${PLAYER} left the game."
eng['kickQuestionText']="Kick ${NAME}?"
file=open('ba_data/data/languages/english.json',"w")
json.dump(eng,file)
file.close()
ba.app.lang.setlanguage(None)
def popup_menu_selected_choice(self, popup_window: PopupMenuWindow,
choice: str) -> None:
"""Called when a choice is selected in the popup."""
global unmuted_names
if self._popup_type == "banTimePress":
result = _ba.disconnect_client(self._popup_party_member_client_id, ban_time=int(choice))
if not result:
ba.playsound(ba.getsound('error'))
ba.screenmessage(
ba.Lstr(resource='getTicketsWindow.unavailableText'),
color=(1, 0, 0))
elif self._popup_type == "send_Times_Press":
self._send_msg_times = int(choice)
ba.buttonwidget(edit = self._times_button,label="%s:%d"%(_getTransText("Times"),getattr(self,"_send_msg_times",1)))
elif self._popup_type == "chatmessagepress":
if choice=="mute":
unmuted_names.remove(self.msg_user_selected)
if choice=="unmute":
unmuted_names.append(self.msg_user_selected)
elif self._popup_type == "partyMemberPress":
if choice == "kick":
ConfirmWindow(text=_getTransText("Normal_kick_confirm")%self._getObjectByID("account"),
action=self._kick_selected_player, cancel_button=True, cancel_is_selected=True,
color=self._bg_color, text_scale=1.0,
origin_widget=self.get_root_widget())
elif choice=="info":
account=self._getObjectByID("account")
self.loading_widget= ConfirmWindow(text="Searching .....",
color=self._bg_color, text_scale=1.0,cancel_button=False,
origin_widget=self.get_root_widget())
start_new_thread(fetchAccountInfo,(account,self.loading_widget,))
elif choice == "adminkick":
ConfirmWindow(text=_getTransText("Admin_Command_Kick_Confirm")%self._getObjectByID("account"),
action=self._send_admin_kick_command, cancel_button=True, cancel_is_selected=True,
color=self._bg_color, text_scale=1.0,
origin_widget=self.get_root_widget())
elif choice == "@ this guy":
ChoiceDis = [];NewChoices = []
account=self._getObjectByID("account")
ChoiceDis.append(account)
temp = self._getObjectByID("playerNameFull")
if temp is not None:
if isinstance(temp,str) and temp not in ChoiceDis:ChoiceDis.append(temp)
elif isinstance(temp,(list,tuple)):
for item in temp:
if isinstance(item,str) and item not in ChoiceDis:ChoiceDis.append(item)
#print("r\\""
for item in ChoiceDis:
NewChoices.append(u"self._edit_text_msg_box('%s','add')"%(item.replace("'",r"'").replace('"',r'\\"')))
else:
nick=self._get_nick(account)
ChoiceDis.append(nick)
NewChoices.append(u"self._on_nick_rename_press('%s')"%(account))
p = PopupMenuWindow(position=popup_window.root_widget.get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=NewChoices,
choices_display=_creat_Lstr_list(ChoiceDis),
current_choice=NewChoices[0],
delegate=self)
self._popup_type = "Custom_Exec_Choice"
elif choice == "customAction":
customActionSets = self._getCustomSets()
customActionSets = customActionSets.get("partyMemberPress_Custom") if isinstance(customActionSets.get("partyMemberPress_Custom"),dict) else {}
ChoiceDis = [];NewChoices = []
for key,item in customActionSets.items():
ChoiceDis.append(key);NewChoices.append(item)
if len(ChoiceDis) > 0:
p = PopupMenuWindow(position=popup_window.root_widget.get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=NewChoices,
choices_display=_creat_Lstr_list(ChoiceDis),
current_choice=NewChoices[0],
delegate=self)
self._popup_type = "customAction_partyMemberPress"
else:ba.playsound(ba.getsound("error"));ba.screenmessage(ba.Lstr(resource="getTicketsWindow.unavailableText"),color=(1,0,0))
elif self._popup_type == "menu":
if choice in ("mute", "unmute"):
cfg = ba.app.config
cfg['Chat Muted'] = (choice == 'mute')
cfg.apply_and_commit()
if cfg['Chat Muted']:
customchatThread().run()
self._update()
elif choice in ("credits",):
ConfirmWindow(text="AdvancePartyWindow by Mr.Smoothy \n extended version of ModifyPartyWindow(Plasma Boson) \n Version 5.3 \n Dont modify or release the source code \n Discord : \n mr.smoothy#5824 Plasma Boson#4104",
action=self.joinbombspot, width=420,height=200,
cancel_button=False, cancel_is_selected=False,
color=self._bg_color, text_scale=1.0, ok_text="More mods >", cancel_text=None,
origin_widget=self.get_root_widget())
elif choice =="chatlogger":
# ColorPickerExact(parent=self.get_root_widget(), position=self.get_root_widget().get_screen_space_center(),
# initial_color=self._bg_color, delegate=self, tag='')
global chatlogger
if chatlogger:
chatlogger=False
ba.screenmessage("Chat logger turned OFF")
else:
chatlogger=True
chatloggThread().run()
ba.screenmessage("Chat logger turned ON")
elif choice=='screenmsg':
global screenmsg
if screenmsg:
screenmsg=False
self.hide_screen_msg()
else:
screenmsg=True
self.restore_screen_msg()
elif choice == "addQuickReply":
try:
newReply = ba.textwidget(query=self._text_field)
data = self._get_quick_responds()
data.append(newReply)
self._write_quick_responds(data)
ba.screenmessage(_getTransText("Something_is_added")%newReply,color=(0,1,0))
ba.playsound(ba.getsound("dingSmallHigh"))
except:ba.print_exception()
elif choice == "removeQuickReply":
Quickreply = self._get_quick_responds()
PopupMenuWindow(position=self._text_field.get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=Quickreply,
choices_display=_creat_Lstr_list(Quickreply),
current_choice=Quickreply[0],
delegate=self)
self._popup_type = "removeQuickReplySelect"
elif choice in ("hostInfo_Debug",) and isinstance(_ba.get_connection_to_host_info(),dict):
if len(_ba.get_connection_to_host_info()) > 0:
#print(_ba.get_connection_to_host_info(),type(_ba.get_connection_to_host_info()))
ChoiceDis = list(_ba.get_connection_to_host_info().keys())
NewChoices = ["ba.screenmessage(str(_ba.get_connection_to_host_info().get('%s')))"%((str(i)).replace("'",r"'").replace('"',r'\\"')) for i in ChoiceDis]
PopupMenuWindow(position=popup_window.root_widget.get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=NewChoices,
choices_display=_creat_Lstr_list(ChoiceDis),
current_choice=NewChoices[0],
delegate=self)
self._popup_type = "Custom_Exec_Choice"
else:ba.playsound(ba.getsound("error"));ba.screenmessage(ba.Lstr(resource="getTicketsWindow.unavailableText"),color=(1,0,0))
elif choice == "translator":
chats = _ba._getChatMessages()
if len(chats) > 0:
choices = [(item) for item in chats[::-1]]
PopupMenuWindow(position=popup_window.root_widget.get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=choices,
choices_display=_creat_Lstr_list(choices),
current_choice=choices[0],
delegate=self)
self._popup_type = "translator_Press"
else:ba.playsound(ba.getsound("error"));ba.screenmessage(ba.Lstr(resource="getTicketsWindow.unavailableText"),color=(1,0,0))
elif choice == "resetGameRecord":
ConfirmWindow(text=_getTransText("Restart_Game_Record_Confirm"),
action=self._reset_game_record, cancel_button=True, cancel_is_selected=True,
color=self._bg_color, text_scale=1.0,
origin_widget=self.get_root_widget())
elif self._popup_type == "translator_Press":
if len(tranTypes) == 1:
exec("start_new_thread(OnlineTranslator.%s,(u'%s',))"%(tranTypes[0],choice.replace("'",r"'").replace('"',r'\\"')))
elif len(tranTypes) > 1:
choices = ["start_new_thread(OnlineTranslator.%s,(u'%s',))"%(item,choice.replace("'",r"'").replace('"',r'\\"')) for item in tranTypes]
PopupMenuWindow(position=popup_window.root_widget.get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=choices,
choices_display=_creat_Lstr_list(tranTypes),
current_choice=choices[0],
delegate=self)
self._popup_type = "Custom_Exec_Choice"
else:ba.playsound(ba.getsound("error"));ba.screenmessage(ba.Lstr(resource="getTicketsWindow.unavailableText"),color=(1,0,0))
elif self._popup_type == "customAction_partyMemberPress":
try:
keyReplaceValue = (r"{$PlayerNameFull}",r"{$PlayerName}",r"{$PlayerID}",r"{$AccountInfo}",r"{$AllPlayerName}",r"{$AllPlayerNameFull}")
pos = None;curKeyWord = None
for keyWord in keyReplaceValue:
CurPos = choice.find(keyWord)
if CurPos != -1 and (pos is None or CurPos < pos):
pos = CurPos;curKeyWord = keyWord
if isinstance(pos,int) and isinstance(curKeyWord,str):
if curKeyWord in (r"{$PlayerNameFull}",r"{$PlayerName}",r"{$AllPlayerName}",r"{$AllPlayerNameFull}"):
#if choice.count(curKeyWord) != 0:
playerName = self._getObjectByID(curKeyWord.replace("{$","").replace("}",""))
if isinstance(playerName,(list,tuple)):
ChoiceDis = [];NewChoices = []
for i in playerName:
ChoiceDis.append(i)
NewChoices.append(choice.replace(curKeyWord,(i.replace("'",r"'").replace('"',r'\\"')),1))
p = PopupMenuWindow(position=popup_window.root_widget.get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=NewChoices,
choices_display=_creat_Lstr_list(ChoiceDis),
current_choice=NewChoices[0],
delegate=self)
self._popup_type = "customAction_partyMemberPress"
elif isinstance(playerName,str):
self.popup_menu_selected_choice(popup_window,choice.replace(curKeyWord,(playerName.replace("'",r"'").replace('"',r'\\"')),1))
else:ba.screenmessage(_getTransText("No_valid_player_found"),(1,0,0));ba.playsound(ba.getsound("error"))
elif curKeyWord in (r"{$PlayerID}",) != 0:
playerID = self._getObjectByID("PlayerID")
playerName = self._getObjectByID("PlayerName")
#print(playerID,playerName)
if isinstance(playerID,(list,tuple)) and isinstance(playerName,(list,tuple)) and len(playerName) == len(playerID):
ChoiceDis = [];NewChoices = []
for i1,i2 in playerName,playerID:
ChoiceDis.append(i1);NewChoices.append(choice.replace(r"{$PlayerID}",str(i2).replace("'",r"'").replace('"',r'\\"')),1)
p = PopupMenuWindow(position=popup_window.root_widget.get_screen_space_center(),
scale=_get_popup_window_scale(),
choices=NewChoices,
choices_display=_creat_Lstr_list(ChoiceDis),
current_choice=NewChoices[0],
delegate=self)
self._popup_type = "customAction_partyMemberPress"
elif isinstance(playerID,int):
self.popup_menu_selected_choice(popup_window,choice.replace(r"{$PlayerID}",str(playerID).replace("'",r"'").replace('"',r'\\"')))
else:ba.screenmessage(_getTransText("No_valid_player_id_found"),(1,0,0));ba.playsound(ba.getsound("error"))
elif curKeyWord in (r"{$AccountInfo}",) != 0:
self.popup_menu_selected_choice(popup_window,choice.replace(r"{$AccountInfo}",(str(self._getObjectByID("roster"))).replace("'",r"'").replace('"',r'\\"'),1))
else:exec(choice)
except Exception as e:ba.screenmessage(repr(e),(1,0,0))
elif self._popup_type == "QuickMessageSelect":
#ba.textwidget(edit=self._text_field,text=self._get_quick_responds()[index])
self._edit_text_msg_box(choice,"add")
elif self._popup_type == "removeQuickReplySelect":
data = self._get_quick_responds()
if len(data) > 0 and choice in data:
data.remove(choice)
self._write_quick_responds(data)
ba.screenmessage(_getTransText("Something_is_removed")%choice,(1,0,0))
ba.playsound(ba.getsound("shieldDown"))
else:ba.screenmessage(ba.Lstr(resource="errorText"),(1,0,0));ba.playsound(ba.getsound("error"))
elif choice.startswith("custom_Exec_Choice_") or self._popup_type == "Custom_Exec_Choice":
exec(choice[len("custom_Exec_Choice_"):] if choice.startswith("custom_Exec_Choice_") else choice)
else:
print("unhandled popup type: "+str(self._popup_type))
import base64
from ba._general import Call
def fetchAccountInfo(account,loading_widget):
pbid=""
account_data=[]
servers=[]
try:
filePath = os.path.join(RecordFilesDir, "players.json")
fdata = {}
if os.path.isfile(filePath):
f = open(filePath, "r")
fdata = json.load(f)
if account in fdata:
servers=fdata[account]
data = urllib.request.urlopen(f'https://api.bombsquad.ga/player?key={base64.b64encode(account.encode("utf-8")).decode("utf-8")}&base64=true')
account_data=json.loads(data.read().decode('utf-8'))[0]
pbid=account_data["pbid"]
except:
pass
# _ba.pushcall(Call(updateAccountWindow,loading_widget,accounts[0]),from_other_thread=True)
_ba.pushcall(Call(CustomAccountViewerWindow,pbid,account_data,servers,loading_widget),from_other_thread =True)
from bastd.ui.account import viewer
class CustomAccountViewerWindow(viewer.AccountViewerWindow):
def __init__(self,account_id,custom_data,servers,loading_widget):
super().__init__(account_id)
try:
loading_widget._cancel()
except:
pass
self.custom_data=custom_data
self.pb_id=account_id
self.servers=servers
def _copy_pb(self):
ba.clipboard_set_text(self.pb_id)
ba.screenmessage(ba.Lstr(resource='gatherWindow.copyCodeConfirmText'))
def _on_query_response(self,data):
if data is None:
ba.textwidget(edit=self._loading_text,text="")
ba.textwidget(parent=self._scrollwidget,
size=(0, 0),
position=(170, 200),
flatness=1.0,
h_align='center',
v_align='center',
scale=0.5,
color=ba.app.ui.infotextcolor,
text="Mutual servers",
maxwidth=300)
v=200-21
for server in self.servers:
ba.textwidget(parent=self._scrollwidget,
size=(0, 0),
position=(170, v),
h_align='center',
v_align='center',
scale=0.55,
text=server,
maxwidth=300)
v-=23
else:
for account in self.custom_data["accounts"]:
if account not in data["accountDisplayStrings"]:
data["accountDisplayStrings"].append(account)
try:
self._loading_text.delete()
trophystr = ''
try:
trophystr = data['trophies']
num = 10
chunks = [
trophystr[i:i + num]
for i in range(0, len(trophystr), num)
]
trophystr = ('\n\n'.join(chunks))
if trophystr == '':
trophystr = '-'
except Exception:
ba.print_exception('Error displaying trophies.')
account_name_spacing = 15
tscale = 0.65
ts_height = _ba.get_string_height(trophystr,
suppress_warning=True)
sub_width = self._width - 80
sub_height = 500 + ts_height * tscale + \
account_name_spacing * len(data['accountDisplayStrings'])
self._subcontainer = ba.containerwidget(
parent=self._scrollwidget,
size=(sub_width, sub_height),
background=False)
v = sub_height - 20
title_scale = 0.37
center = 0.3
maxwidth_scale = 0.45
showing_character = False
if data['profileDisplayString'] is not None:
tint_color = (1, 1, 1)
try:
if data['profile'] is not None:
profile = data['profile']
character = ba.app.spaz_appearances.get(
profile['character'], None)
if character is not None:
tint_color = (profile['color'] if 'color'
in profile else (1, 1, 1))
tint2_color = (profile['highlight']
if 'highlight' in profile else
(1, 1, 1))
icon_tex = character.icon_texture
tint_tex = character.icon_mask_texture
mask_texture = ba.gettexture(
'characterIconMask')
ba.imagewidget(
parent=self._subcontainer,
position=(sub_width * center - 40, v - 80),
size=(80, 80),
color=(1, 1, 1),
mask_texture=mask_texture,
texture=ba.gettexture(icon_tex),
tint_texture=ba.gettexture(tint_tex),
tint_color=tint_color,
tint2_color=tint2_color)
v -= 95
except Exception:
ba.print_exception('Error displaying character.')
ba.textwidget(
parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=0.9,
color=ba.safecolor(tint_color, 0.7),
shadow=1.0,
text=ba.Lstr(value=data['profileDisplayString']),
maxwidth=sub_width * maxwidth_scale * 0.75)
showing_character = True
v -= 33
center = 0.75 if showing_character else 0.5
maxwidth_scale = 0.45 if showing_character else 0.9
v = sub_height - 20
if len(data['accountDisplayStrings']) <= 1:
account_title = ba.Lstr(
resource='settingsWindow.accountText')
else:
account_title = ba.Lstr(
resource='accountSettingsWindow.accountsText',
fallback_resource='settingsWindow.accountText')
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
flatness=1.0,
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
text=account_title,
maxwidth=sub_width * maxwidth_scale)
draw_small = (showing_character
or len(data['accountDisplayStrings']) > 1)
v -= 14 if draw_small else 20
for account_string in data['accountDisplayStrings']:
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=0.55 if draw_small else 0.8,
text=account_string,
maxwidth=sub_width * maxwidth_scale)
v -= account_name_spacing
v += account_name_spacing
v -= 25 if showing_character else 29
# =======================================================================
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
flatness=1.0,
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
text=str(self.pb_id),
maxwidth=sub_width * maxwidth_scale)
self._copy_btn = ba.buttonwidget(
parent=self._subcontainer,
position=(sub_width * center -120, v -9),
size=(60, 30),
scale=0.5,
label='copy',
color=(0.6, 0.5, 0.6),
on_activate_call=self._copy_pb,
autoselect=True)
v-=24
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
flatness=1.0,
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
text="Name",
maxwidth=sub_width * maxwidth_scale)
v-=26
for name in self.custom_data["names"]:
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=0.51,
text=name,
maxwidth=sub_width * maxwidth_scale)
v-=13
v-=8
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
flatness=1.0,
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
text="Created On",
maxwidth=sub_width * maxwidth_scale)
v-=19
d=self.custom_data["createdOn"]
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=0.55,
text=d[:d.index("T")],
maxwidth=sub_width * maxwidth_scale)
v-=29
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
flatness=1.0,
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
text="Discord",
maxwidth=sub_width * maxwidth_scale)
v -= 19
if len(self.custom_data["discord"]) >0:
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=0.55,
text=self.custom_data["discord"][0]["username"]+ ","+self.custom_data["discord"][0]["id"],
maxwidth=sub_width * maxwidth_scale)
v -= 26
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
flatness=1.0,
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
text="Mutual servers",
maxwidth=sub_width * maxwidth_scale)
v=-19
v=270
for server in self.servers:
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=0.55,
text=server,
maxwidth=sub_width * maxwidth_scale)
v-=13
v-=16
#==================================================================
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
flatness=1.0,
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
text=ba.Lstr(resource='rankText'),
maxwidth=sub_width * maxwidth_scale)
v -= 14
if data['rank'] is None:
rank_str = '-'
suffix_offset = None
else:
str_raw = ba.Lstr(
resource='league.rankInLeagueText').evaluate()
# FIXME: Would be nice to not have to eval this.
rank_str = ba.Lstr(
resource='league.rankInLeagueText',
subs=[('${RANK}', str(data['rank'][2])),
('${NAME}',
ba.Lstr(translate=('leagueNames',
data['rank'][0]))),
('${SUFFIX}', '')]).evaluate()
rank_str_width = min(
sub_width * maxwidth_scale,
_ba.get_string_width(rank_str, suppress_warning=True) *
0.55)
# Only tack our suffix on if its at the end and only for
# non-diamond leagues.
if (str_raw.endswith('${SUFFIX}')
and data['rank'][0] != 'Diamond'):
suffix_offset = rank_str_width * 0.5 + 2
else:
suffix_offset = None
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=0.55,
text=rank_str,
maxwidth=sub_width * maxwidth_scale)
if suffix_offset is not None:
assert data['rank'] is not None
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center + suffix_offset,
v + 3),
h_align='left',
v_align='center',
scale=0.29,
flatness=1.0,
text='[' + str(data['rank'][1]) + ']')
v -= 14
str_raw = ba.Lstr(
resource='league.rankInLeagueText').evaluate()
old_offs = -50
prev_ranks_shown = 0
for prev_rank in data['prevRanks']:
rank_str = ba.Lstr(
value='${S}: ${I}',
subs=[
('${S}',
ba.Lstr(resource='league.seasonText',
subs=[('${NUMBER}', str(prev_rank[0]))])),
('${I}',
ba.Lstr(resource='league.rankInLeagueText',
subs=[('${RANK}', str(prev_rank[3])),
('${NAME}',
ba.Lstr(translate=('leagueNames',
prev_rank[1]))),
('${SUFFIX}', '')]))
]).evaluate()
rank_str_width = min(
sub_width * maxwidth_scale,
_ba.get_string_width(rank_str, suppress_warning=True) *
0.3)
# Only tack our suffix on if its at the end and only for
# non-diamond leagues.
if (str_raw.endswith('${SUFFIX}')
and prev_rank[1] != 'Diamond'):
suffix_offset = rank_str_width + 2
else:
suffix_offset = None
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center + old_offs, v),
h_align='left',
v_align='center',
scale=0.3,
text=rank_str,
flatness=1.0,
maxwidth=sub_width * maxwidth_scale)
if suffix_offset is not None:
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center + old_offs +
suffix_offset, v + 1),
h_align='left',
v_align='center',
scale=0.20,
flatness=1.0,
text='[' + str(prev_rank[2]) + ']')
prev_ranks_shown += 1
v -= 10
v -= 13
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
flatness=1.0,
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
text=ba.Lstr(resource='achievementsText'),
maxwidth=sub_width * maxwidth_scale)
v -= 14
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=0.55,
text=str(data['achievementsCompleted']) + ' / ' +
str(len(ba.app.ach.achievements)),
maxwidth=sub_width * maxwidth_scale)
v -= 25
if prev_ranks_shown == 0 and showing_character:
v -= 20
elif prev_ranks_shown == 1 and showing_character:
v -= 10
center = 0.5
maxwidth_scale = 0.9
ba.textwidget(parent=self._subcontainer,
size=(0, 0),
position=(sub_width * center, v),
h_align='center',
v_align='center',
scale=title_scale,
color=ba.app.ui.infotextcolor,
flatness=1.0,
text=ba.Lstr(resource='trophiesThisSeasonText',
fallback_resource='trophiesText'),
maxwidth=sub_width * maxwidth_scale)
v -= 19
ba.textwidget(parent=self._subcontainer,
size=(0, ts_height),
position=(sub_width * 0.5,
v - ts_height * tscale),
h_align='center',
v_align='top',
corner_scale=tscale,
text=trophystr)
except Exception:
ba.print_exception('Error displaying account info.')
# ba_meta export plugin
class bySmoothy(ba.Plugin):
def __init__(self):
if _ba.env().get("build_number",0) >= 20577:
_ba.connect_to_party=newconnect_to_party
bastd_party.PartyWindow = ModifiedPartyWindow
else:print("AdvancePartyWindow only runs with BombSquad version equal or higher than 1.7")